Skip to content

Commit 098a76c

Browse files
committed
Remove Unnecessary LineFragment Variables
1 parent a1a2c9e commit 098a76c

File tree

10 files changed

+47
-40
lines changed

10 files changed

+47
-40
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// NSRange+translate.swift
3+
// CodeEditTextView
4+
//
5+
// Created by Khan Winter on 7/21/25.
6+
//
7+
8+
import Foundation
9+
10+
extension NSRange {
11+
func translate(location: Int) -> NSRange {
12+
NSRange(location: self.location + location, length: length)
13+
}
14+
}

Sources/CodeEditTextView/TextLayoutManager/TextLayoutManager+Layout.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,13 @@ extension TextLayoutManager {
263263
// ) {
264264
for lineFragmentPosition in line.lineFragments {
265265
let lineFragment = lineFragmentPosition.data
266+
lineFragment.documentRange = lineFragmentPosition.range.translate(location: position.range.location)
266267

267-
layoutFragmentView(for: lineFragmentPosition, at: position.yPos + lineFragmentPosition.yPos)
268+
layoutFragmentView(
269+
inLine: position,
270+
for: lineFragmentPosition,
271+
at: position.yPos + lineFragmentPosition.yPos
272+
)
268273

269274
width = max(width, lineFragment.width)
270275
height += lineFragment.scaledHeight
@@ -281,14 +286,16 @@ extension TextLayoutManager {
281286
/// - lineFragment: The line fragment position to lay out a view for.
282287
/// - yPos: The y value at which the line should begin.
283288
private func layoutFragmentView(
289+
inLine line: TextLineStorage<TextLine>.TextLinePosition,
284290
for lineFragment: TextLineStorage<LineFragment>.TextLinePosition,
285291
at yPos: CGFloat
286292
) {
293+
let fragmentRange = lineFragment.range.translate(location: line.range.location)
287294
let view = viewReuseQueue.getOrCreateView(forKey: lineFragment.data.id) {
288295
renderDelegate?.lineFragmentView(for: lineFragment.data) ?? LineFragmentView()
289296
}
290297
view.translatesAutoresizingMaskIntoConstraints = true // Small optimization for lots of subviews
291-
view.setLineFragment(lineFragment.data, renderer: lineFragmentRenderer)
298+
view.setLineFragment(lineFragment.data, fragmentRange: fragmentRange, renderer: lineFragmentRenderer)
292299
view.frame.origin = CGPoint(x: edgeInsets.left, y: yPos)
293300
layoutView?.addSubview(view, positioned: .below, relativeTo: nil)
294301
view.needsDisplay = true
@@ -300,7 +307,9 @@ extension TextLayoutManager {
300307
guard let view = viewReuseQueue.getView(forKey: lineFragmentPosition.data.id) else {
301308
return true
302309
}
303-
310+
lineFragmentPosition.data.documentRange = lineFragmentPosition.range.translate(
311+
location: position.range.location
312+
)
304313
view.frame.origin = CGPoint(x: edgeInsets.left, y: position.yPos + lineFragmentPosition.yPos)
305314
}
306315
return false

Sources/CodeEditTextView/TextLayoutManager/TextLayoutManager+Public.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,13 @@ extension TextLayoutManager {
112112
fragmentPosition: TextLineStorage<LineFragment>.TextLinePosition,
113113
in linePosition: TextLineStorage<TextLine>.TextLinePosition
114114
) -> Int? {
115-
let endPosition = fragmentPosition.data.documentRange.max
115+
let fragmentRange = fragmentPosition.range.translate(location: linePosition.range.location)
116+
let endPosition = fragmentRange.max
116117

117118
// If the endPosition is at the end of the line, and the line ends with a line ending character
118119
// return the index before the eol.
119120
if fragmentPosition.index == linePosition.data.lineFragments.count - 1,
120-
let lineEnding = LineEnding(line: textStorage?.substring(from: fragmentPosition.data.documentRange) ?? "") {
121+
let lineEnding = LineEnding(line: textStorage?.substring(from: fragmentRange) ?? "") {
121122
return endPosition - lineEnding.length
122123
} else if fragmentPosition.index != linePosition.data.lineFragments.count - 1 {
123124
// If this isn't the last fragment, we want to place the cursor at the offset right before the break
@@ -175,7 +176,7 @@ extension TextLayoutManager {
175176
guard let fragmentPosition = linePosition.data.typesetter.lineFragments.getLine(
176177
atOffset: offset - linePosition.range.location
177178
) else {
178-
return nil
179+
return CGRect(x: edgeInsets.left, y: linePosition.yPos, width: 0, height: linePosition.height)
179180
}
180181

181182
// Get the *real* length of the character at the offset. If this is a surrogate pair it'll return the correct
@@ -190,11 +191,11 @@ extension TextLayoutManager {
190191

191192
let minXPos = characterXPosition(
192193
in: fragmentPosition.data,
193-
for: realRange.location - fragmentPosition.data.documentRange.location
194+
for: realRange.location - linePosition.range.location - fragmentPosition.range.location
194195
)
195196
let maxXPos = characterXPosition(
196197
in: fragmentPosition.data,
197-
for: realRange.max - fragmentPosition.data.documentRange.location
198+
for: realRange.max - linePosition.range.location - fragmentPosition.range.location
198199
)
199200

200201
return CGRect(

Sources/CodeEditTextView/TextLine/LineFragment.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ public final class LineFragment: Identifiable, Equatable {
4747
}
4848

4949
public let id = UUID()
50-
public let lineRange: NSRange
51-
public let documentRange: NSRange
50+
public var documentRange: NSRange = .notFound
5251
public var contents: [FragmentContent]
5352
public var width: CGFloat
5453
public var height: CGFloat
@@ -61,16 +60,12 @@ public final class LineFragment: Identifiable, Equatable {
6160
}
6261

6362
init(
64-
lineRange: NSRange,
65-
documentRange: NSRange,
6663
contents: [FragmentContent],
6764
width: CGFloat,
6865
height: CGFloat,
6966
descent: CGFloat,
7067
lineHeightMultiplier: CGFloat
7168
) {
72-
self.lineRange = lineRange
73-
self.documentRange = documentRange
7469
self.contents = contents
7570
self.width = width
7671
self.height = height

Sources/CodeEditTextView/TextLine/LineFragmentRenderer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public final class LineFragmentRenderer {
122122
context: context
123123
)
124124

125-
let range = createTextRange(for: drawingContext)
125+
let range = createTextRange(for: drawingContext).clamped(to: (textStorage.string as NSString).length)
126126
let string = (textStorage.string as NSString).substring(with: range)
127127

128128
processInvisibleCharacters(
@@ -177,7 +177,7 @@ public final class LineFragmentRenderer {
177177
guard let style = delegate.invisibleStyle(
178178
for: character,
179179
at: NSRange(start: range.location + index, end: range.max),
180-
lineRange: drawingContext.lineFragment.lineRange
180+
lineRange: drawingContext.lineFragment.documentRange
181181
) else {
182182
return
183183
}

Sources/CodeEditTextView/TextLine/LineFragmentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ open class LineFragmentView: NSView {
7979

8080
/// Set a new line fragment for this view, updating view size.
8181
/// - Parameter newFragment: The new fragment to use.
82-
open func setLineFragment(_ newFragment: LineFragment, renderer: LineFragmentRenderer) {
82+
open func setLineFragment(_ newFragment: LineFragment, fragmentRange: NSRange, renderer: LineFragmentRenderer) {
8383
self.lineFragment = newFragment
8484
self.renderer = renderer
8585
self.frame.size = CGSize(width: newFragment.width, height: newFragment.scaledHeight)

Sources/CodeEditTextView/TextLine/TextLine.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ public final class TextLine: Identifiable, Equatable {
6262
markedRanges: markedRanges,
6363
attachments: attachments
6464
)
65-
// self.maxWidth = min(maxWidth, displayData.maxWidth)
6665
self.maxWidth = displayData.maxWidth
6766
needsLayout = false
6867
}

Sources/CodeEditTextView/TextLine/Typesetter/TypesetContext.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ struct TypesetContext {
1616
/// Accumulated generated line fragments.
1717
var lines: [TextLineStorage<LineFragment>.BuildItem] = []
1818
var maxHeight: CGFloat = 0
19-
var maxWidth: CGFloat = 0
2019
/// The current fragment typesetting context.
2120
var fragmentContext = LineFragmentTypesetContext(start: 0, width: 0.0, height: 0.0, descent: 0.0)
2221

@@ -62,11 +61,6 @@ struct TypesetContext {
6261
/// Pop the current fragment state into a new line fragment, and reset the fragment state.
6362
mutating func popCurrentData() {
6463
let fragment = LineFragment(
65-
lineRange: documentRange,
66-
documentRange: NSRange(
67-
location: fragmentContext.start + documentRange.location,
68-
length: currentPosition - fragmentContext.start
69-
),
7064
contents: fragmentContext.contents,
7165
width: fragmentContext.width,
7266
height: fragmentContext.height,
@@ -77,7 +71,6 @@ struct TypesetContext {
7771
.init(data: fragment, length: currentPosition - fragmentContext.start, height: fragment.scaledHeight)
7872
)
7973
maxHeight = max(maxHeight, fragment.scaledHeight)
80-
maxWidth = max(maxWidth, fragment.width)
8174

8275
fragmentContext.clear()
8376
fragmentContext.start = currentPosition

Sources/CodeEditTextView/TextLine/Typesetter/Typesetter.swift

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,28 @@ final public class Typesetter {
3333

3434
public init() { }
3535

36-
/// Performs the typesetting operation, returning the maximum width required for the current layout.
37-
/// - Returns: The maximum width the typeset lines require.
3836
public func typeset(
3937
_ string: NSAttributedString,
4038
documentRange: NSRange,
4139
displayData: TextLine.DisplayData,
4240
markedRanges: MarkedRanges?,
4341
attachments: [AnyTextAttachment] = []
44-
) -> CGFloat {
42+
) {
4543
let string = makeString(string: string, markedRanges: markedRanges)
4644
lineFragments.removeAll()
4745

4846
// Fast path
4947
if string.length == 0 || displayData.maxWidth <= 0 {
5048
typesetEmptyLine(displayData: displayData, string: string)
51-
return 0.0
49+
return
5250
}
53-
let (lines, maxSize) = typesetLineFragments(
51+
let (lines, maxHeight) = typesetLineFragments(
5452
string: string,
5553
documentRange: documentRange,
5654
displayData: displayData,
5755
attachments: attachments
5856
)
59-
lineFragments.build(from: lines, estimatedLineHeight: maxSize.height)
60-
return maxSize.width
57+
lineFragments.build(from: lines, estimatedLineHeight: maxHeight)
6158
}
6259

6360
private func makeString(string: NSAttributedString, markedRanges: MarkedRanges?) -> NSAttributedString {
@@ -135,7 +132,7 @@ final public class Typesetter {
135132
documentRange: NSRange,
136133
displayData: TextLine.DisplayData,
137134
attachments: [AnyTextAttachment]
138-
) -> (lines: [TextLineStorage<LineFragment>.BuildItem], maxSize: CGSize) {
135+
) -> (lines: [TextLineStorage<LineFragment>.BuildItem], maxHeight: CGFloat) {
139136
let contentRuns = createContentRuns(string: string, documentRange: documentRange, attachments: attachments)
140137
var context = TypesetContext(documentRange: documentRange, displayData: displayData)
141138

@@ -158,7 +155,7 @@ final public class Typesetter {
158155
context.popCurrentData()
159156
}
160157

161-
return (context.lines, CGSize(width: context.maxWidth, height: context.maxHeight))
158+
return (context.lines, context.maxHeight)
162159
}
163160

164161
// MARK: - Layout Text Fragments
@@ -233,8 +230,6 @@ final public class Typesetter {
233230
// Insert an empty fragment
234231
let ctLine = CTTypesetterCreateLine(typesetter, CFRangeMake(0, 0))
235232
let fragment = LineFragment(
236-
lineRange: documentRange ?? .zero,
237-
documentRange: NSRange(location: (documentRange ?? .notFound).location, length: 0),
238233
contents: [.init(data: .text(line: ctLine), width: 0.0)],
239234
width: 0,
240235
height: displayData.estimatedLineHeight / displayData.lineHeightMultiplier,

Sources/CodeEditTextView/TextView/TextView+ScrollToVisible.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ extension TextView {
2929
layoutManager.layoutLines()
3030
selectionManager.updateSelectionViews()
3131
selectionManager.drawSelections(in: visibleRect)
32-
}
33-
if lastFrame != .zero {
34-
scrollView.contentView.scrollToVisible(lastFrame)
35-
scrollView.reflectScrolledClipView(scrollView.contentView)
32+
33+
if lastFrame != .zero {
34+
scrollView.contentView.scrollToVisible(lastFrame)
35+
scrollView.reflectScrolledClipView(scrollView.contentView)
36+
}
3637
}
3738
}
3839

0 commit comments

Comments
 (0)