-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathiter.go
355 lines (319 loc) · 10.8 KB
/
iter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
package highlight
import (
"context"
"slices"
"github.com/tree-sitter/go-tree-sitter"
)
type highlightRange struct {
start uint
end uint
depth uint
}
type iterator struct {
Ctx context.Context
Source []byte
LanguageName string
ByteOffset uint
Highlighter *Highlighter
InjectionCallback InjectionCallback
Layers []*iterLayer
NextEvents []Event
LastHighlightRange *highlightRange
LastLayer *iterLayer
}
func (h *iterator) emitEvents(offset uint, events ...Event) (Event, error) {
var result Event
if h.ByteOffset < offset {
result = EventSource{
StartByte: h.ByteOffset,
EndByte: offset,
}
h.ByteOffset = offset
h.NextEvents = append(h.NextEvents, events...)
} else {
if len(events) > 1 {
h.NextEvents = append(h.NextEvents, events[1:]...)
}
result = events[0]
}
h.sortLayers()
return result, nil
}
func (h *iterator) next() (Event, error) {
main:
for {
if len(h.NextEvents) > 0 {
event := h.NextEvents[0]
h.NextEvents = h.NextEvents[1:]
return event, nil
}
// check for cancellation
select {
case <-h.Ctx.Done():
return nil, h.Ctx.Err()
default:
}
// If none of the layers have any more highlight boundaries, terminate.
if len(h.Layers) == 0 {
if h.ByteOffset < uint(len(h.Source)) {
event := EventSource{
StartByte: h.ByteOffset,
EndByte: uint(len(h.Source)),
}
h.ByteOffset = uint(len(h.Source))
return event, nil
}
return nil, nil
}
// Get the next capture from whichever layer has the earliest highlight boundary.
layer := h.Layers[0]
if layer != h.LastLayer {
var events []Event
if h.LastLayer != nil {
events = append(events, EventLayerEnd{})
}
h.LastLayer = layer
return h.emitEvents(h.ByteOffset, append(events, EventLayerStart{
LanguageName: layer.Config.LanguageName,
})...)
}
var nextCaptureRange tree_sitter.Range
if nextMatch, captureIndex, ok := layer.Captures.Peek(); ok {
nextCapture := nextMatch.Captures[captureIndex]
nextCaptureRange = nextCapture.Node.Range()
// If any previous highlight ends before this node starts, then before
// processing this capture, emit the source code up until the end of the
// previous highlight, and an end event for that highlight.
if len(layer.HighlightEndStack) > 0 {
endByte := layer.HighlightEndStack[len(layer.HighlightEndStack)-1]
if endByte <= nextCaptureRange.StartByte {
layer.HighlightEndStack = layer.HighlightEndStack[:len(layer.HighlightEndStack)-1]
return h.emitEvents(endByte, EventCaptureEnd{})
}
}
} else {
// If there are no more captures, then emit any remaining highlight end events.
// And if there are none of those, then just advance to the end of the document.
if len(layer.HighlightEndStack) > 0 {
endByte := layer.HighlightEndStack[len(layer.HighlightEndStack)-1]
layer.HighlightEndStack = layer.HighlightEndStack[:len(layer.HighlightEndStack)-1]
return h.emitEvents(endByte, EventCaptureEnd{})
}
return h.emitEvents(uint(len(h.Source)), nil)
}
match, captureIndex, _ := layer.Captures.Next()
capture := match.Captures[captureIndex]
// If this capture represents an injection, then process the injection.
if match.PatternIndex < layer.Config.LocalsPatternIndex {
languageName, contentNode, includeChildren := injectionForMatch(layer.Config, h.LanguageName, layer.Config.Query, match, h.Source)
// Explicitly remove this match so that none of its other captures will remain
// in the stream of captures.
match.Remove()
// If a language is found with the given name, then add a new language layer
// to the highlighted document.
if languageName != "" && contentNode != nil {
newConfig := h.InjectionCallback(languageName)
if newConfig != nil {
ranges := intersectRanges(layer.Ranges, []tree_sitter.Node{*contentNode}, includeChildren)
if len(ranges) > 0 {
newLayers, err := newIterLayers(h.Ctx, h.Source, h.LanguageName, h.Highlighter, h.InjectionCallback, *newConfig, layer.Depth+1, ranges)
if err != nil {
return nil, err
}
for _, newLayer := range newLayers {
h.insertLayer(newLayer)
}
}
}
}
h.sortLayers()
continue main
}
// Remove from the local scope stack any local scopes that have already ended.
for nextCaptureRange.StartByte > layer.ScopeStack[len(layer.ScopeStack)-1].Range.EndByte {
layer.ScopeStack = layer.ScopeStack[:len(layer.ScopeStack)-1]
}
// If this capture is for tracking local variables, then process the
// local variable info.
var referenceHighlight *Highlight
var definitionHighlight *Highlight
for match.PatternIndex < layer.Config.HighlightsPatternIndex {
// If the node represents a local scope, push a new local scope onto
// the scope stack.
if layer.Config.LocalScopeCaptureIndex != nil && uint(capture.Index) == *layer.Config.LocalScopeCaptureIndex {
definitionHighlight = nil
scope := localScope{
Inherits: true,
Range: nextCaptureRange,
LocalDefs: nil,
}
for _, prop := range layer.Config.Query.PropertySettings(match.PatternIndex) {
if prop.Key == captureLocalScopeInherits {
scope.Inherits = *prop.Value == "true"
}
}
layer.ScopeStack = append(layer.ScopeStack, scope)
} else if layer.Config.LocalDefCaptureIndex != nil && uint(capture.Index) == *layer.Config.LocalDefCaptureIndex {
// If the node represents a definition, add a new definition to the
// local scope at the top of the scope stack.
referenceHighlight = nil
definitionHighlight = nil
scope := layer.ScopeStack[len(layer.ScopeStack)-1]
var valueRange tree_sitter.Range
for _, matchCapture := range match.Captures {
if layer.Config.LocalDefValueCaptureIndex != nil && uint(matchCapture.Index) == *layer.Config.LocalDefValueCaptureIndex {
valueRange = matchCapture.Node.Range()
}
}
if len(h.Source) > int(nextCaptureRange.StartByte) && len(h.Source) > int(valueRange.EndByte) {
name := string(h.Source[nextCaptureRange.StartByte:nextCaptureRange.EndByte])
scope.LocalDefs = append(scope.LocalDefs, localDef{
Name: name,
Range: nextCaptureRange,
Highlight: nil,
})
definitionHighlight = scope.LocalDefs[len(scope.LocalDefs)-1].Highlight
}
} else if layer.Config.LocalRefCaptureIndex != nil && uint(capture.Index) == *layer.Config.LocalRefCaptureIndex && definitionHighlight == nil {
// If the node represents a reference, then try to find the corresponding
// definition in the scope stack.
definitionHighlight = nil
if len(h.Source) > int(nextCaptureRange.StartByte) && len(h.Source) > int(nextCaptureRange.EndByte) {
name := string(h.Source[nextCaptureRange.StartByte:nextCaptureRange.EndByte])
for _, scope := range slices.Backward(layer.ScopeStack) {
var highlight *Highlight
for _, def := range slices.Backward(scope.LocalDefs) {
if def.Name == name && nextCaptureRange.StartByte >= def.Range.EndByte {
highlight = def.Highlight
}
}
if highlight != nil {
referenceHighlight = highlight
break
}
if !scope.Inherits {
break
}
}
}
}
// Continue processing any additional matches for the same node.
if nextMatch, nextCaptureIndex, ok := layer.Captures.Peek(); ok {
nextCapture := nextMatch.Captures[nextCaptureIndex]
if nextCapture.Node.Equals(capture.Node) {
capture = nextCapture
match, _, _ = layer.Captures.Next()
continue
}
}
h.sortLayers()
continue main
}
// Otherwise, this capture must represent a highlight.
// If this exact range has already been highlighted by an earlier pattern, or by
// a different layer, then skip over this one.
if h.LastHighlightRange != nil {
lastRange := *h.LastHighlightRange
if nextCaptureRange.StartByte == lastRange.start && nextCaptureRange.EndByte == lastRange.end && layer.Depth < lastRange.depth {
h.sortLayers()
continue main
}
}
// Once a highlighting pattern is found for the current node, keep iterating over
// any later highlighting patterns that also match this node and set the match to it.
// Captures for a given node are ordered by pattern index, so these subsequent
// captures are guaranteed to be for highlighting, not injections or
// local variables.
for {
nextMatch, nextCaptureIndex, ok := layer.Captures.Peek()
if !ok {
break
}
nextCapture := nextMatch.Captures[nextCaptureIndex]
if nextCapture.Node.Equals(capture.Node) {
followingMatch, _, _ := layer.Captures.Next()
// If the current node was found to be a local variable, then ignore
// the following match if it's a highlighting pattern that is disabled
// for local variables.
if definitionHighlight != nil || referenceHighlight != nil && layer.Config.NonLocalVariablePatterns[followingMatch.PatternIndex] {
continue
}
match.Remove()
capture = nextCapture
match = followingMatch
} else {
break
}
}
currentHighlight := layer.Config.HighlightIndices[uint(capture.Index)]
// If this node represents a local definition, then store the current
// highlight value on the local scope entry representing this node.
if definitionHighlight != nil {
definitionHighlight = currentHighlight
}
// Emit a scope start event and push the node's end position to the stack.
highlight := referenceHighlight
if highlight == nil {
highlight = currentHighlight
}
if highlight != nil {
h.LastHighlightRange = &highlightRange{
start: nextCaptureRange.StartByte,
end: nextCaptureRange.EndByte,
depth: layer.Depth,
}
layer.HighlightEndStack = append(layer.HighlightEndStack, nextCaptureRange.EndByte)
return h.emitEvents(nextCaptureRange.StartByte, EventCaptureStart{
Highlight: *highlight,
})
}
h.sortLayers()
}
}
func (h *iterator) sortLayers() {
for len(h.Layers) > 0 {
key := h.Layers[0].sortKey()
if key != nil {
var i int
for i+1 < len(h.Layers) {
nextOffsetKey := h.Layers[i+1].sortKey()
if nextOffsetKey != nil {
if nextOffsetKey.GreaterThan(*key) {
i += 1
continue
}
}
break
}
if i > 0 {
h.Layers = append(rotateLeft(h.Layers[:i+1], 1), h.Layers[i+1:]...)
}
break
}
layer := h.Layers[0]
h.Layers = h.Layers[1:]
h.Highlighter.pushCursor(layer.Cursor)
}
}
func (h *iterator) insertLayer(layer *iterLayer) {
key := layer.sortKey()
if key != nil {
i := 1
for i < len(h.Layers) {
keyI := h.Layers[i].sortKey()
if keyI != nil {
if keyI.LessThan(*key) {
h.Layers = slices.Insert(h.Layers, i, layer)
return
}
i += 1
} else {
h.Layers = slices.Delete(h.Layers, i, i+1)
}
}
h.Layers = append(h.Layers, layer)
}
}
func rotateLeft[T any](s []T, i int) []T {
return append(s[i:], s[:i]...)
}