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