Skip to content

Commit e3d0b46

Browse files
Implemented drawHashMarksAndLabelsInRect
1 parent 57293fb commit e3d0b46

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

LineNumberTextView/LineNumberTextView/LineNumberGutter.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,95 @@ class LineNumberGutter: NSRulerView {
5151
required init?(coder: NSCoder) {
5252
fatalError("init(coder:) has not been implemented")
5353
}
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+
}
54145
}

0 commit comments

Comments
 (0)