|
7 | 7 |
|
8 | 8 | import AppKit |
9 | 9 | import SwiftUI |
| 10 | +import Combine |
10 | 11 | import STTextView |
11 | 12 | import SwiftTreeSitter |
12 | 13 |
|
@@ -47,13 +48,20 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt |
47 | 48 |
|
48 | 49 | // MARK: Init |
49 | 50 |
|
50 | | - public init(text: Binding<String>, language: CodeLanguage, font: NSFont, theme: EditorTheme, tabWidth: Int) { |
| 51 | + public init( |
| 52 | + text: Binding<String>, |
| 53 | + language: CodeLanguage, |
| 54 | + font: NSFont, |
| 55 | + theme: EditorTheme, |
| 56 | + tabWidth: Int, |
| 57 | + cursorPosition: Published<(Int, Int)>.Publisher? = nil |
| 58 | + ) { |
51 | 59 | self.text = text |
52 | 60 | self.language = language |
53 | 61 | self.font = font |
54 | 62 | self.theme = theme |
55 | 63 | self.tabWidth = tabWidth |
56 | | - |
| 64 | + self.cursorPosition = cursorPosition |
57 | 65 | super.init(nibName: nil, bundle: nil) |
58 | 66 | } |
59 | 67 |
|
@@ -124,6 +132,10 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt |
124 | 132 | } |
125 | 133 |
|
126 | 134 | setUpHighlighting() |
| 135 | + |
| 136 | + self.cursorPositionCancellable = self.cursorPosition?.sink(receiveValue: { value in |
| 137 | + self.setCursorPosition(value) |
| 138 | + }) |
127 | 139 | } |
128 | 140 |
|
129 | 141 | internal func setUpHighlighting() { |
@@ -223,6 +235,35 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt |
223 | 235 | keyIsDown = false |
224 | 236 | } |
225 | 237 |
|
| 238 | + // MARK: Cursor Position |
| 239 | + |
| 240 | + private var cursorPosition: Published<(Int, Int)>.Publisher? |
| 241 | + private var cursorPositionCancellable: AnyCancellable? |
| 242 | + |
| 243 | + private func setCursorPosition(_ position: (Int, Int)) { |
| 244 | + guard let provider = textView.textLayoutManager.textContentManager else { |
| 245 | + return |
| 246 | + } |
| 247 | + |
| 248 | + var (line, column) = position |
| 249 | + let string = textView.string |
| 250 | + if line > 0 { |
| 251 | + string.enumerateSubstrings(in: string.startIndex..<string.endIndex) { _, lineRange, _, done in |
| 252 | + line -= 1 |
| 253 | + if line < 1 { |
| 254 | + // If `column` exceeds the line length, set cursor to the end of the line. |
| 255 | + let index = min(lineRange.upperBound, string.index(lineRange.lowerBound, offsetBy: column - 1)) |
| 256 | + if let newRange = NSTextRange(NSRange(index..<index, in: string), provider: provider) { |
| 257 | + self.textView.setSelectedRange(newRange) |
| 258 | + } |
| 259 | + done = true |
| 260 | + } else { |
| 261 | + done = false |
| 262 | + } |
| 263 | + } |
| 264 | + } |
| 265 | + } |
| 266 | + |
226 | 267 | deinit { |
227 | 268 | textView = nil |
228 | 269 | highlighter = nil |
|
0 commit comments