Add TokenizedBuffer.bufferRangeForScopeAtPosition(selector, position)

You can call this method with a selector and a position and get the range
of any matching scope containing the given position, or a falsy value
if the scope does not match at that position.
This commit is contained in:
Nathan Sobo
2013-08-06 17:35:03 -06:00
parent 39d15d6087
commit 5f323cc67c
4 changed files with 77 additions and 4 deletions

View File

@@ -396,3 +396,21 @@ describe "TokenizedBuffer", ->
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1,1]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1,2]).scopes).toEqual ["source.js", "storage.modifier.js"]
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
beforeEach ->
buffer = project.bufferForPath('sample.js')
tokenizedBuffer = new TokenizedBuffer(buffer)
fullyTokenize(tokenizedBuffer)
describe "when the selector does not match the token at the position", ->
it "returns a falsy value", ->
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeFalsy()
describe "when the selector matches a single token at the position", ->
it "returns the range covered by the token", ->
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.storage.modifier.js', [0, 1])).toEqual [[0, 0], [0, 3]]
describe "when the selector matches a run of multiple tokens at the position", ->
it "returns the range covered by all contigous tokens (within a single line)", ->
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual [[1, 6], [1, 28]]

View File

@@ -109,6 +109,12 @@ class Token
isOnlyWhitespace: ->
not /\S/.test(@value)
matchesScopeSelector: (selector) ->
targetClasses = selector.replace(/^\.?/, '').split('.')
_.any @scopes, (scope) ->
scopeClasses = scope.split('.')
_.isSubset(targetClasses, scopeClasses)
getValueAsHtml: ({invisibles, hasLeadingWhitespace, hasTrailingWhitespace, hasIndentGuide})->
invisibles ?= {}
html = @value

View File

@@ -200,8 +200,34 @@ class TokenizedBuffer
@tokenForPosition(position).scopes
tokenForPosition: (position) ->
{row, column} = Point.fromObject(position)
@tokenizedLines[row].tokenAtBufferColumn(column)
tokenStartPositionForPosition: (position) ->
{row, column} = Point.fromObject(position)
column = @tokenizedLines[row].tokenStartColumnForBufferColumn(column)
new Point(row, column)
bufferRangeForScopeAtPosition: (selector, position) ->
position = Point.fromObject(position)
@tokenizedLines[position.row].tokenAtBufferColumn(position.column)
tokenizedLine = @tokenizedLines[position.row]
startIndex = tokenizedLine.tokenIndexAtBufferColumn(position.column)
for index in [startIndex..0]
token = tokenizedLine.tokenAtIndex(index)
break unless token.matchesScopeSelector(selector)
firstToken = token
for index in [startIndex...tokenizedLine.getTokenCount()]
token = tokenizedLine.tokenAtIndex(index)
break unless token.matchesScopeSelector(selector)
lastToken = token
return unless firstToken? and lastToken?
startColumn = tokenizedLine.bufferColumnForToken(firstToken)
endColumn = tokenizedLine.bufferColumnForToken(lastToken) + lastToken.bufferDelta
new Range([position.row, startColumn], [position.row, endColumn])
destroy: ->
@unsubscribe()

View File

@@ -90,11 +90,22 @@ class TokenizedLine
@lineEnding is null
tokenAtBufferColumn: (bufferColumn) ->
@tokens[@tokenIndexAtBufferColumn(bufferColumn)]
tokenIndexAtBufferColumn: (bufferColumn) ->
delta = 0
for token, index in @tokens
delta += token.bufferDelta
return index if delta > bufferColumn
index - 1
tokenStartColumnForBufferColumn: (bufferColumn) ->
delta = 0
for token in @tokens
delta += token.bufferDelta
return token if delta > bufferColumn
token
nextDelta = delta + token.bufferDelta
break if nextDelta > bufferColumn
delta = nextDelta
delta
breakOutAtomicTokens: (inputTokens, tabLength) ->
outputTokens = []
@@ -112,3 +123,15 @@ class TokenizedLine
return true if _.contains(scope.split('.'), 'comment')
break
false
tokenAtIndex: (index) ->
@tokens[index]
getTokenCount: ->
@tokens.length
bufferColumnForToken: (targetToken) ->
column = 0
for token in @tokens
return column if token is targetToken
column += token.bufferDelta