Skip to content

Commit c971fe9

Browse files
Make JSDoc Highlight as Comments (#228)
<!--- IMPORTANT: If this PR addresses multiple unrelated issues, it will be closed until separated. --> ### Description Makes JSDocs highlight as comments. Because they're injected as a different language they don't get captured normally. This is the only language we have to handle this in so it's hard-coded in the TreeSitterClient. Also adds a quick check to prevent potential recursive cursor notifications using SwiftUI. ### Related Issues <!--- REQUIRED: Tag all related issues (e.g. * #123) --> <!--- If this PR resolves the issue please specify (e.g. * closes #123) --> <!--- If this PR addresses multiple issues, these issues must be related to one other --> * [Discord conversation](https://canary.discord.com/channels/951544472238444645/987416899816149024/1201620579590090853) ### Checklist <!--- Add things that are not yet implemented above --> - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots <img width="285" alt="Screenshot 2024-02-02 at 7 37 19 PM" src="https://github.com/CodeEditApp/CodeEditSourceEditor/assets/35942988/f201e5ff-1bf9-4bb5-97fb-0914aa66246a">
1 parent b5a8ca9 commit c971fe9

File tree

4 files changed

+83
-11
lines changed

4 files changed

+83
-11
lines changed

Sources/CodeEditSourceEditor/Controller/TextViewController+Cursor.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extension TextViewController {
1212
/// Sets new cursor positions.
1313
/// - Parameter positions: The positions to set. Lines and columns are 1-indexed.
1414
public func setCursorPositions(_ positions: [CursorPosition]) {
15+
if isPostingCursorNotification { return }
1516
_ = textView.becomeFirstResponder()
1617

1718
var newSelectedRanges: [NSRange] = []
@@ -48,6 +49,9 @@ extension TextViewController {
4849
positions.append(CursorPosition(range: selectedRange.range, line: row, column: column))
4950
}
5051
cursorPositions = positions.sorted(by: { $0.range.location < $1.range.location })
52+
53+
isPostingCursorNotification = true
5154
NotificationCenter.default.post(name: Self.cursorPositionUpdatedNotification, object: nil)
55+
isPostingCursorNotification = false
5256
}
5357
}

Sources/CodeEditSourceEditor/Controller/TextViewController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public class TextViewController: NSViewController {
2929
internal var highlightLayers: [CALayer] = []
3030
internal var systemAppearance: NSAppearance.Name?
3131

32+
package var isPostingCursorNotification: Bool = false
33+
3234
/// The string contents.
3335
public var string: String {
3436
textView.string

Sources/CodeEditSourceEditor/Extensions/Tree+prettyPrint.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,65 @@ extension Tree {
6868
}
6969
}
7070
}
71+
72+
extension MutableTree {
73+
func prettyPrint() {
74+
guard let cursor = self.rootNode?.treeCursor else {
75+
print("NO ROOT NODE")
76+
return
77+
}
78+
guard cursor.currentNode != nil else {
79+
print("NO CURRENT NODE")
80+
return
81+
}
82+
83+
func p(_ cursor: TreeCursor, depth: Int) {
84+
guard let node = cursor.currentNode else {
85+
return
86+
}
87+
88+
let visible = node.isNamed
89+
90+
if visible {
91+
print(String(repeating: " ", count: depth * 2), terminator: "")
92+
if let fieldName = cursor.currentFieldName {
93+
print(fieldName, ": ", separator: "", terminator: "")
94+
}
95+
print("(", node.nodeType ?? "NONE", " ", node.range, " ", separator: "", terminator: "")
96+
}
97+
98+
if cursor.goToFirstChild() {
99+
while true {
100+
if cursor.currentNode != nil && cursor.currentNode!.isNamed {
101+
print("")
102+
}
103+
104+
p(cursor, depth: depth + 1)
105+
106+
if !cursor.gotoNextSibling() {
107+
break
108+
}
109+
}
110+
111+
if !cursor.gotoParent() {
112+
fatalError("Could not go to parent, this tree may be invalid.")
113+
}
114+
}
115+
116+
if visible {
117+
print(")", terminator: "")
118+
}
119+
}
120+
121+
if cursor.currentNode?.childCount == 0 {
122+
if !cursor.currentNode!.isNamed {
123+
print("{\(cursor.currentNode!.nodeType ?? "NONE")}")
124+
} else {
125+
print("\"\(cursor.currentNode!.nodeType ?? "NONE")\"")
126+
}
127+
} else {
128+
p(cursor, depth: 1)
129+
}
130+
}
131+
}
71132
#endif

Sources/CodeEditSourceEditor/TreeSitter/TreeSitterClient+Highlight.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import CodeEditLanguages
1111

1212
extension TreeSitterClient {
1313
func queryHighlightsForRange(range: NSRange) -> [HighlightRange] {
14-
guard let state = self.state, let readCallback else { return [] }
14+
guard let state = self.state else { return [] }
1515

1616
var highlights: [HighlightRange] = []
1717
var injectedSet = IndexSet(integersIn: range)
@@ -22,8 +22,7 @@ extension TreeSitterClient {
2222
if let rangeIntersection = range.intersection(layerRange) {
2323
let queryResult = queryLayerHighlights(
2424
layer: layer,
25-
range: rangeIntersection,
26-
readCallback: readCallback
25+
range: rangeIntersection
2726
)
2827

2928
highlights.append(contentsOf: queryResult)
@@ -36,8 +35,7 @@ extension TreeSitterClient {
3635
for range in injectedSet.rangeView {
3736
let queryResult = queryLayerHighlights(
3837
layer: state.layers[0],
39-
range: NSRange(range),
40-
readCallback: readCallback
38+
range: NSRange(range)
4139
)
4240
highlights.append(contentsOf: queryResult)
4341
}
@@ -52,8 +50,7 @@ extension TreeSitterClient {
5250
/// - Returns: Any ranges to highlight.
5351
internal func queryLayerHighlights(
5452
layer: LanguageLayer,
55-
range: NSRange,
56-
readCallback: SwiftTreeSitter.Predicate.TextProvider
53+
range: NSRange
5754
) -> [HighlightRange] {
5855
guard let tree = layer.tree,
5956
let rootNode = tree.rootNode else {
@@ -68,7 +65,16 @@ extension TreeSitterClient {
6865
queryCursor.setRange(range)
6966
queryCursor.matchLimit = Constants.treeSitterMatchLimit
7067

71-
return highlightsFromCursor(cursor: queryCursor, includedRange: range, readCallback: readCallback)
68+
var highlights: [HighlightRange] = []
69+
70+
// See https://github.com/CodeEditApp/CodeEditSourceEditor/pull/228
71+
if layer.id == .jsdoc {
72+
highlights.append(HighlightRange(range: range, capture: .comment))
73+
}
74+
75+
highlights += highlightsFromCursor(cursor: queryCursor, includedRange: range)
76+
77+
return highlights
7278
}
7379

7480
/// Resolves a query cursor to the highlight ranges it contains.
@@ -79,8 +85,7 @@ extension TreeSitterClient {
7985
/// - Returns: Any highlight ranges contained in the cursor.
8086
internal func highlightsFromCursor(
8187
cursor: QueryCursor,
82-
includedRange: NSRange,
83-
readCallback: SwiftTreeSitter.Predicate.TextProvider
88+
includedRange: NSRange
8489
) -> [HighlightRange] {
8590
return cursor
8691
.flatMap { $0.captures }
@@ -89,7 +94,7 @@ extension TreeSitterClient {
8994
// in the included range
9095
let intersectionRange = $0.range.intersection(includedRange) ?? .zero
9196
// Check that the capture name is one CESE can parse. If not, ignore it completely.
92-
if intersectionRange.length > 0, let captureName = CaptureName.fromString($0.name ?? "") {
97+
if intersectionRange.length > 0, let captureName = CaptureName.fromString($0.name) {
9398
return HighlightRange(range: intersectionRange, capture: captureName)
9499
}
95100
return nil

0 commit comments

Comments
 (0)