diff --git a/spec/app/tokenized-buffer-spec.coffee b/spec/app/tokenized-buffer-spec.coffee index 78af67309..8648931e1 100644 --- a/spec/app/tokenized-buffer-spec.coffee +++ b/spec/app/tokenized-buffer-spec.coffee @@ -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]] diff --git a/src/app/token.coffee b/src/app/token.coffee index 8fae0930f..0d011ffc4 100644 --- a/src/app/token.coffee +++ b/src/app/token.coffee @@ -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 diff --git a/src/app/tokenized-buffer.coffee b/src/app/tokenized-buffer.coffee index 98f1d2cc5..18affef78 100644 --- a/src/app/tokenized-buffer.coffee +++ b/src/app/tokenized-buffer.coffee @@ -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() diff --git a/src/app/tokenized-line.coffee b/src/app/tokenized-line.coffee index 9b66606b8..18bbecbcc 100644 --- a/src/app/tokenized-line.coffee +++ b/src/app/tokenized-line.coffee @@ -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