@@ -51,4 +51,95 @@ class LineNumberGutter: NSRulerView {
51
51
required init ? ( coder: NSCoder ) {
52
52
fatalError ( " init(coder:) has not been implemented " )
53
53
}
54
+
55
+ /// Draws the line numbers.
56
+ ///
57
+ /// - parameter rect: NSRect to draw the gutter view in.
58
+ override func drawHashMarksAndLabelsInRect( rect: NSRect ) {
59
+ // Unwrap the clientView, the layoutManager and the textContainer, since we'll
60
+ // them sooner or later.
61
+ guard let textView = self . clientView as? NSTextView ,
62
+ layoutManager = textView. layoutManager,
63
+ textContainer = textView. textContainer,
64
+ content = textView. string else {
65
+ return
66
+ }
67
+
68
+ // Get the range of the currently visible glyphs.
69
+ let visibleGlyphsRange = layoutManager. glyphRangeForBoundingRect ( textView. visibleRect, inTextContainer: textContainer)
70
+
71
+ // Check how many lines are out of the current bounding rect.
72
+ var lineNumber : Int = 1
73
+ do {
74
+ // Define a regular expression to find line breaks.
75
+ let newlineRegex = try NSRegularExpression ( pattern: " \n " , options: [ ] )
76
+ // Check how many lines are out of view; From the glyph at index 0
77
+ // to the first glyph in the visible rect.
78
+ lineNumber += newlineRegex. numberOfMatchesInString ( content, options: [ ] , range: NSMakeRange ( 0 , visibleGlyphsRange. location) )
79
+ } catch {
80
+ return
81
+ }
82
+
83
+ // Get the index of the first glyph in the visible rect, as starting point...
84
+ var firstGlyphOfLineIndex = visibleGlyphsRange. location
85
+
86
+ // ...then loop through all visible glyphs, line by line.
87
+ while firstGlyphOfLineIndex < NSMaxRange ( visibleGlyphsRange) {
88
+ // Get the character range of the line we're currently in.
89
+ let charRangeOfLine = ( content as NSString ) . lineRangeForRange ( NSRange ( location: layoutManager. characterIndexForGlyphAtIndex ( firstGlyphOfLineIndex) , length: 0 ) )
90
+ // Get the glyph range of the line we're currently in.
91
+ let glyphRangeOfLine = layoutManager. glyphRangeForCharacterRange ( charRangeOfLine, actualCharacterRange: nil )
92
+
93
+ var firstGlyphOfRowIndex = firstGlyphOfLineIndex
94
+ var lineWrapCount = 0
95
+
96
+ // Loop through all rows (soft wraps) of the current line.
97
+ while firstGlyphOfRowIndex < NSMaxRange ( glyphRangeOfLine) {
98
+ // The effective range of glyphs within the current line.
99
+ var effectiveRange = NSRange ( location: 0 , length: 0 )
100
+ // Get the rect for the current line fragment.
101
+ let lineRect = layoutManager. lineFragmentRectForGlyphAtIndex ( firstGlyphOfRowIndex, effectiveRange: & effectiveRange, withoutAdditionalLayout: true )
102
+
103
+ // Draw the current line number;
104
+ // When lineWrapCount > 0 the current line spans multiple rows.
105
+ if lineWrapCount == 0 {
106
+ self . drawLineNumber ( lineNumber, atYPosition: lineRect. minY)
107
+ } else {
108
+ break
109
+ }
110
+
111
+ // Move to the next row.
112
+ firstGlyphOfRowIndex = NSMaxRange ( effectiveRange)
113
+ lineWrapCount++
114
+ }
115
+
116
+ // Move to the next line.
117
+ firstGlyphOfLineIndex = NSMaxRange ( glyphRangeOfLine)
118
+ lineNumber++
119
+ }
120
+
121
+ // Draw another line number for the extra line fragment.
122
+ if let _ = layoutManager. extraLineFragmentTextContainer {
123
+ self . drawLineNumber ( lineNumber, atYPosition: layoutManager. extraLineFragmentRect. minY)
124
+ }
125
+ }
126
+
127
+
128
+ func drawLineNumber( num: Int , atYPosition yPos: CGFloat ) {
129
+ // Unwrap the text view.
130
+ guard let textView = self . clientView as? NSTextView ,
131
+ font = textView. font else {
132
+ return
133
+ }
134
+ // Define attributes for the attributed string.
135
+ let attrs = [ NSFontAttributeName: font]
136
+ // Define the attributed string.
137
+ let attributedString = NSAttributedString ( string: " \( num) " , attributes: attrs)
138
+ // Get the NSZeroPoint from the text view.
139
+ let relativePoint = self . convertPoint ( NSZeroPoint, fromView: textView)
140
+ // Calculate the x position, within the gutter.
141
+ let xPosition = GUTTER_WIDTH - ( attributedString. size ( ) . width + 5 )
142
+ // Draw the attributed string to the calculated point.
143
+ attributedString. drawAtPoint ( NSPoint ( x: xPosition, y: relativePoint. y + yPos) )
144
+ }
54
145
}
0 commit comments