Skip to content

Commit c6c9e52

Browse files
Merge branch 'main' into feat/find-replace
2 parents a9f134e + 334506c commit c6c9e52

File tree

10 files changed

+61
-47
lines changed

10 files changed

+61
-47
lines changed

.github/workflows/swiftlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ jobs:
88
steps:
99
- uses: actions/checkout@v3
1010
- name: GitHub Action for SwiftLint with --strict
11-
run: swiftlint --strict
11+
run: swiftlint --reporter github-actions-logging --strict

Sources/CodeEditSourceEditor/Controller/TextViewController+Highlighter.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extension TextViewController {
1717

1818
let highlighter = Highlighter(
1919
textView: textView,
20+
minimapView: minimapView,
2021
providers: highlightProviders,
2122
attributeProvider: self,
2223
language: language

Sources/CodeEditSourceEditor/Controller/TextViewController+LoadView.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ extension TextViewController {
2323
delegate: self
2424
)
2525
gutterView.updateWidthIfNeeded()
26-
scrollView.addFloatingSubview(
27-
gutterView,
28-
for: .horizontal
29-
)
26+
scrollView.addFloatingSubview(gutterView, for: .horizontal)
3027

3128
minimapView = MinimapView(textView: textView, theme: theme)
3229
scrollView.addFloatingSubview(minimapView, for: .vertical)
@@ -89,7 +86,7 @@ extension TextViewController {
8986
minimapView.bottomAnchor.constraint(equalTo: scrollView.contentView.bottomAnchor),
9087
minimapXConstraint,
9188
maxWidthConstraint,
92-
relativeWidthConstraint,
89+
relativeWidthConstraint
9390
])
9491
}
9592

@@ -124,7 +121,11 @@ extension TextViewController {
124121
queue: .main
125122
) { [weak self] _ in
126123
self?.gutterView.frame.size.height = (self?.textView.frame.height ?? 0) + 10
124+
self?.gutterView.frame.origin.y = (self?.textView.frame.origin.y ?? 0.0)
125+
- (self?.scrollView.contentInsets.top ?? 0)
126+
127127
self?.gutterView.needsDisplay = true
128+
self?.scrollView.needsLayout = true
128129
}
129130

130131
NotificationCenter.default.addObserver(
@@ -146,7 +147,7 @@ extension TextViewController {
146147

147148
// Reset content insets and gutter position when appearance changes
148149
self.styleScrollView()
149-
self.gutterView.frame.origin.y = -self.scrollView.contentInsets.top
150+
self.gutterView.frame.origin.y = self.textView.frame.origin.y - self.scrollView.contentInsets.top
150151
}
151152
}
152153
.store(in: &cancellables)

Sources/CodeEditSourceEditor/Controller/TextViewController+StyleViews.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ extension TextViewController {
106106

107107
findViewController?.topPadding = contentInsets?.top
108108

109-
gutterView.frame.origin.y = -scrollView.contentInsets.top
109+
gutterView.frame.origin.y = textView.frame.origin.y - scrollView.contentInsets.top
110110

111111
// Update scrollview tiling
112112
scrollView.reflectScrolledClipView(scrollView.contentView)

Sources/CodeEditSourceEditor/Gutter/GutterView.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ public class GutterView: NSView {
4545
var textColor: NSColor = .secondaryLabelColor
4646

4747
@Invalidating(.display)
48-
var font: NSFont = .systemFont(ofSize: 13)
48+
var font: NSFont = .systemFont(ofSize: 13) {
49+
didSet {
50+
updateFontLineHeight()
51+
}
52+
}
4953

5054
@Invalidating(.display)
5155
var edgeInsets: EdgeInsets = EdgeInsets(leading: 20, trailing: 12)
@@ -74,6 +78,19 @@ public class GutterView: NSView {
7478
/// The maximum number of digits found for a line number.
7579
private var maxLineLength: Int = 0
7680

81+
private var fontLineHeight = 1.0
82+
83+
private func updateFontLineHeight() {
84+
let string = NSAttributedString(string: "0", attributes: [.font: font])
85+
let typesetter = CTTypesetterCreateWithAttributedString(string)
86+
let ctLine = CTTypesetterCreateLine(typesetter, CFRangeMake(0, 1))
87+
var ascent: CGFloat = 0
88+
var descent: CGFloat = 0
89+
var leading: CGFloat = 0
90+
CTLineGetTypographicBounds(ctLine, &ascent, &descent, &leading)
91+
fontLineHeight = (ascent + descent + leading)
92+
}
93+
7794
override public var isFlipped: Bool {
7895
true
7996
}
@@ -181,7 +198,7 @@ public class GutterView: NSView {
181198
y: line.yPos,
182199
width: width,
183200
height: line.height
184-
)
201+
).pixelAligned
185202
)
186203
}
187204

@@ -217,12 +234,16 @@ public class GutterView: NSView {
217234
let fragment: LineFragment? = linePosition.data.lineFragments.first?.data
218235
var ascent: CGFloat = 0
219236
let lineNumberWidth = CTLineGetTypographicBounds(ctLine, &ascent, nil, nil)
237+
let fontHeightDifference = ((fragment?.height ?? 0) - fontLineHeight) / 4
220238

221-
let yPos = linePosition.yPos + ascent + (fragment?.heightDifference ?? 0)/2
239+
let yPos = linePosition.yPos + ascent + (fragment?.heightDifference ?? 0)/2 + fontHeightDifference
222240
// Leading padding + (width - linewidth)
223241
let xPos = edgeInsets.leading + (maxWidth - lineNumberWidth)
224242

225-
context.textPosition = CGPoint(x: xPos, y: yPos).pixelAligned
243+
ContextSetHiddenSmoothingStyle(context, 16)
244+
245+
context.textPosition = CGPoint(x: xPos, y: yPos)
246+
226247
CTLineDraw(ctLine, context)
227248
}
228249
context.restoreGState()

Sources/CodeEditSourceEditor/Highlighting/Highlighter.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class Highlighter: NSObject {
8585

8686
init(
8787
textView: TextView,
88+
minimapView: MinimapView?,
8889
providers: [HighlightProviding],
8990
attributeProvider: ThemeAttributesProviding,
9091
language: CodeLanguage
@@ -93,7 +94,7 @@ class Highlighter: NSObject {
9394
self.textView = textView
9495
self.attributeProvider = attributeProvider
9596

96-
self.visibleRangeProvider = VisibleRangeProvider(textView: textView)
97+
self.visibleRangeProvider = VisibleRangeProvider(textView: textView, minimapView: minimapView)
9798

9899
let providerIds = providers.indices.map({ $0 })
99100
self.styleContainer = StyledRangeContainer(documentLength: textView.length, providers: providerIds)
@@ -244,7 +245,7 @@ extension Highlighter: NSTextStorageDelegate {
244245
visibleRangeProvider.visibleSet.insert(range: editedRange)
245246
}
246247

247-
visibleRangeProvider.updateVisibleSet(textView: textView)
248+
visibleRangeProvider.visibleTextChanged()
248249

249250
let providerRange = NSRange(location: editedRange.location, length: editedRange.length - delta)
250251
highlightProviders.forEach { $0.storageDidUpdate(range: providerRange, delta: delta) }

Sources/CodeEditSourceEditor/Highlighting/VisibleRangeProvider.swift

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ protocol VisibleRangeProviderDelegate: AnyObject {
1818
@MainActor
1919
class VisibleRangeProvider {
2020
private weak var textView: TextView?
21+
private weak var minimapView: MinimapView?
2122
weak var delegate: VisibleRangeProviderDelegate?
2223

2324
var documentRange: NSRange {
@@ -29,56 +30,44 @@ class VisibleRangeProvider {
2930
return IndexSet(integersIn: textView?.visibleTextRange ?? NSRange())
3031
}()
3132

32-
init(textView: TextView) {
33+
init(textView: TextView, minimapView: MinimapView?) {
3334
self.textView = textView
35+
self.minimapView = minimapView
3436

3537
if let scrollView = textView.enclosingScrollView {
3638
NotificationCenter.default.addObserver(
3739
self,
38-
selector: #selector(visibleTextChanged(_:)),
40+
selector: #selector(visibleTextChanged),
3941
name: NSView.frameDidChangeNotification,
4042
object: scrollView
4143
)
4244

4345
NotificationCenter.default.addObserver(
4446
self,
45-
selector: #selector(visibleTextChanged(_:)),
47+
selector: #selector(visibleTextChanged),
4648
name: NSView.boundsDidChangeNotification,
4749
object: scrollView.contentView
4850
)
49-
} else {
50-
NotificationCenter.default.addObserver(
51-
self,
52-
selector: #selector(visibleTextChanged(_:)),
53-
name: NSView.frameDidChangeNotification,
54-
object: textView
55-
)
5651
}
57-
}
5852

59-
func updateVisibleSet(textView: TextView) {
60-
if let newVisibleRange = textView.visibleTextRange {
61-
visibleSet = IndexSet(integersIn: newVisibleRange)
62-
}
53+
NotificationCenter.default.addObserver(
54+
self,
55+
selector: #selector(visibleTextChanged),
56+
name: NSView.frameDidChangeNotification,
57+
object: textView
58+
)
6359
}
6460

6561
/// Updates the view to highlight newly visible text when the textview is scrolled or bounds change.
66-
@objc func visibleTextChanged(_ notification: Notification) {
67-
let textView: TextView
68-
if let clipView = notification.object as? NSClipView,
69-
let documentView = clipView.enclosingScrollView?.documentView as? TextView {
70-
textView = documentView
71-
} else if let scrollView = notification.object as? NSScrollView,
72-
let documentView = scrollView.documentView as? TextView {
73-
textView = documentView
74-
} else if let documentView = notification.object as? TextView {
75-
textView = documentView
76-
} else {
62+
@objc func visibleTextChanged() {
63+
guard let textViewVisibleRange = textView?.visibleTextRange else {
7764
return
7865
}
79-
80-
updateVisibleSet(textView: textView)
81-
66+
var visibleSet = IndexSet(integersIn: textViewVisibleRange)
67+
if !(minimapView?.isHidden ?? true), let minimapVisibleRange = minimapView?.visibleTextRange {
68+
visibleSet.formUnion(IndexSet(integersIn: minimapVisibleRange))
69+
}
70+
self.visibleSet = visibleSet
8271
delegate?.visibleSetDidUpdate(visibleSet)
8372
}
8473

Tests/CodeEditSourceEditorTests/Highlighting/HighlightProviderStateTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ final class HighlightProviderStateTest: XCTestCase {
3030
override func setUp() async throws {
3131
try await super.setUp()
3232
textView = Mock.textView()
33-
rangeProvider = MockVisibleRangeProvider(textView: textView)
33+
rangeProvider = MockVisibleRangeProvider(textView: textView, minimapView: nil)
3434
delegate = EmptyHighlightProviderStateDelegate()
3535
}
3636

Tests/CodeEditSourceEditorTests/Highlighting/VisibleRangeProviderTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ final class VisibleRangeProviderTests: XCTestCase {
88
textView.string = Array(repeating: "\n", count: 400).joined()
99
textView.layout()
1010

11-
let rangeProvider = VisibleRangeProvider(textView: textView)
11+
let rangeProvider = VisibleRangeProvider(textView: textView, minimapView: nil)
1212
let originalSet = rangeProvider.visibleSet
1313

1414
scrollView.contentView.scroll(to: NSPoint(x: 0, y: 250))
@@ -25,7 +25,7 @@ final class VisibleRangeProviderTests: XCTestCase {
2525
textView.string = Array(repeating: "\n", count: 400).joined()
2626
textView.layout()
2727

28-
let rangeProvider = VisibleRangeProvider(textView: textView)
28+
let rangeProvider = VisibleRangeProvider(textView: textView, minimapView: nil)
2929
let originalSet = rangeProvider.visibleSet
3030

3131
scrollView.setFrameSize(NSSize(width: 250, height: 450))
@@ -46,7 +46,7 @@ final class VisibleRangeProviderTests: XCTestCase {
4646
textView.string = Array(repeating: "\n", count: 400).joined()
4747
textView.layout()
4848

49-
let rangeProvider = VisibleRangeProvider(textView: textView)
49+
let rangeProvider = VisibleRangeProvider(textView: textView, minimapView: nil)
5050
let originalSet = rangeProvider.visibleSet
5151

5252
textView.setFrameSize(NSSize(width: 350, height: 450))

Tests/CodeEditSourceEditorTests/Mock.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ enum Mock {
129129
) -> Highlighter {
130130
Highlighter(
131131
textView: textView,
132+
minimapView: nil,
132133
providers: highlightProviders,
133134
attributeProvider: attributeProvider,
134135
language: language

0 commit comments

Comments
 (0)