forked from atom/atom
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtokenized-buffer-iterator.coffee
126 lines (110 loc) · 3.7 KB
/
tokenized-buffer-iterator.coffee
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
{Point} = require 'text-buffer'
module.exports =
class TokenizedBufferIterator
constructor: (@tokenizedBuffer) ->
@openTags = null
@closeTags = null
@containingTags = null
seek: (position) ->
@openTags = []
@closeTags = []
@tagIndex = null
currentLine = @tokenizedBuffer.tokenizedLineForRow(position.row)
@currentTags = currentLine.tags
@currentLineOpenTags = currentLine.openScopes
@currentLineLength = currentLine.text.length
@containingTags = @currentLineOpenTags.map (id) => @tokenizedBuffer.grammar.scopeForId(id)
currentColumn = 0
for tag, index in @currentTags
if tag >= 0
if currentColumn >= position.column
@tagIndex = index
break
else
currentColumn += tag
@containingTags.pop() while @closeTags.shift()
@containingTags.push(openTag) while openTag = @openTags.shift()
else
scopeName = @tokenizedBuffer.grammar.scopeForId(tag)
if tag % 2 is 0 # close tag
if @openTags.length > 0
if currentColumn >= position.column
@tagIndex = index
break
else
@containingTags.pop() while @closeTags.shift()
@containingTags.push(openTag) while openTag = @openTags.shift()
@closeTags.push(scopeName)
else # open tag
@openTags.push(scopeName)
@tagIndex ?= @currentTags.length
@position = Point(position.row, Math.min(@currentLineLength, currentColumn))
@containingTags.slice()
moveToSuccessor: ->
@containingTags.pop() for tag in @closeTags
@containingTags.push(tag) for tag in @openTags
@openTags = []
@closeTags = []
loop
if @tagIndex is @currentTags.length
if @isAtTagBoundary()
break
else
if @shouldMoveToNextLine
@moveToNextLine()
@openTags = @currentLineOpenTags.map (id) => @tokenizedBuffer.grammar.scopeForId(id)
@shouldMoveToNextLine = false
else if @nextLineHasMismatchedContainingTags()
@closeTags = @containingTags.slice().reverse()
@containingTags = []
@shouldMoveToNextLine = true
else
return false unless @moveToNextLine()
else
tag = @currentTags[@tagIndex]
if tag >= 0
if @isAtTagBoundary()
break
else
@position = Point(@position.row, Math.min(@currentLineLength, @position.column + @currentTags[@tagIndex]))
else
scopeName = @tokenizedBuffer.grammar.scopeForId(tag)
if tag % 2 is 0
if @openTags.length > 0
break
else
@closeTags.push(scopeName)
else
@openTags.push(scopeName)
@tagIndex++
true
getPosition: ->
@position
getCloseTags: ->
@closeTags.slice()
getOpenTags: ->
@openTags.slice()
###
Section: Private Methods
###
nextLineHasMismatchedContainingTags: ->
if line = @tokenizedBuffer.tokenizedLineForRow(@position.row + 1)
return true if line.openScopes.length isnt @containingTags.length
for i in [[email protected]] by 1
if @containingTags[i] isnt @tokenizedBuffer.grammar.scopeForId(line.openScopes[i])
return true
false
else
false
moveToNextLine: ->
@position = Point(@position.row + 1, 0)
if tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(@position.row)
@currentTags = tokenizedLine.tags
@currentLineLength = tokenizedLine.text.length
@currentLineOpenTags = tokenizedLine.openScopes
@tagIndex = 0
true
else
false
isAtTagBoundary: ->
@closeTags.length > 0 or @openTags.length > 0