|
|
|
|
@@ -1,3 +1,4 @@
|
|
|
|
|
NullGrammar = require '../src/null-grammar'
|
|
|
|
|
TokenizedBuffer = require '../src/tokenized-buffer'
|
|
|
|
|
{Point} = TextBuffer = require 'text-buffer'
|
|
|
|
|
_ = require 'underscore-plus'
|
|
|
|
|
@@ -32,15 +33,8 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
atom.packages.activatePackage('language-coffee-script')
|
|
|
|
|
|
|
|
|
|
it "deserializes it searching among the buffers in the current project", ->
|
|
|
|
|
tokenizedBufferA = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBufferB = TokenizedBuffer.deserialize(
|
|
|
|
|
JSON.parse(JSON.stringify(tokenizedBufferA.serialize())),
|
|
|
|
|
atom
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tokenizedBufferA = new TokenizedBuffer({buffer, tabLength: 2})
|
|
|
|
|
tokenizedBufferB = TokenizedBuffer.deserialize(JSON.parse(JSON.stringify(tokenizedBufferA.serialize())), atom)
|
|
|
|
|
expect(tokenizedBufferB.buffer).toBe(tokenizedBufferA.buffer)
|
|
|
|
|
|
|
|
|
|
describe "when the underlying buffer has no path", ->
|
|
|
|
|
@@ -48,25 +42,14 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync(null)
|
|
|
|
|
|
|
|
|
|
it "deserializes it searching among the buffers in the current project", ->
|
|
|
|
|
tokenizedBufferA = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBufferB = TokenizedBuffer.deserialize(
|
|
|
|
|
JSON.parse(JSON.stringify(tokenizedBufferA.serialize())),
|
|
|
|
|
atom
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tokenizedBufferA = new TokenizedBuffer({buffer, tabLength: 2})
|
|
|
|
|
tokenizedBufferB = TokenizedBuffer.deserialize(JSON.parse(JSON.stringify(tokenizedBufferA.serialize())), atom)
|
|
|
|
|
expect(tokenizedBufferB.buffer).toBe(tokenizedBufferA.buffer)
|
|
|
|
|
|
|
|
|
|
describe "when the buffer is destroyed", ->
|
|
|
|
|
beforeEach ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
|
|
|
|
startTokenizing(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
it "stops tokenization", ->
|
|
|
|
|
@@ -78,11 +61,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
describe "when the buffer contains soft-tabs", ->
|
|
|
|
|
beforeEach ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
|
|
|
|
startTokenizing(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
afterEach ->
|
|
|
|
|
@@ -90,32 +69,29 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
buffer.release()
|
|
|
|
|
|
|
|
|
|
describe "on construction", ->
|
|
|
|
|
it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", ->
|
|
|
|
|
line0 = tokenizedBuffer.tokenizedLineForRow(0)
|
|
|
|
|
expect(line0.tokens).toEqual([value: line0.text, scopes: ['source.js']])
|
|
|
|
|
it "tokenizes lines chunk at a time in the background", ->
|
|
|
|
|
line0 = tokenizedBuffer.tokenizedLines[0]
|
|
|
|
|
expect(line0).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
line11 = tokenizedBuffer.tokenizedLineForRow(11)
|
|
|
|
|
expect(line11.tokens).toEqual([value: " return sort(Array.apply(this, arguments));", scopes: ['source.js']])
|
|
|
|
|
|
|
|
|
|
# background tokenization has not begun
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined()
|
|
|
|
|
line11 = tokenizedBuffer.tokenizedLines[11]
|
|
|
|
|
expect(line11).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
# tokenize chunk 1
|
|
|
|
|
advanceClock()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5]).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
# tokenize chunk 2
|
|
|
|
|
advanceClock()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(9).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeFalsy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[9].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[10]).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
# tokenize last chunk
|
|
|
|
|
advanceClock()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(12).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[10].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[12].ruleStack?).toBeTruthy()
|
|
|
|
|
|
|
|
|
|
describe "when the buffer is partially tokenized", ->
|
|
|
|
|
beforeEach ->
|
|
|
|
|
@@ -152,8 +128,8 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
it "does not attempt to tokenize the lines in the change, and preserves the existing invalid row", ->
|
|
|
|
|
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
|
|
|
|
buffer.setTextInRange([[6, 0], [7, 0]], "\n\n\n")
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeFalsy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(7).ruleStack?).toBeFalsy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[6]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[7]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
|
|
|
|
|
|
|
|
|
describe "when the buffer is fully tokenized", ->
|
|
|
|
|
@@ -165,101 +141,101 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
it "updates tokens to reflect the change", ->
|
|
|
|
|
buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n")
|
|
|
|
|
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.decimal.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0].tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1].tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.decimal.js'])
|
|
|
|
|
# line 2 is unchanged
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2].tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
|
|
|
|
|
|
|
|
|
describe "when the change invalidates the tokenization of subsequent lines", ->
|
|
|
|
|
it "schedules the invalidated lines to be tokenized in the background", ->
|
|
|
|
|
buffer.insert([5, 30], '/* */')
|
|
|
|
|
buffer.insert([2, 0], '/*')
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js']
|
|
|
|
|
|
|
|
|
|
advanceClock()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
|
|
|
|
|
it "resumes highlighting with the state of the previous line", ->
|
|
|
|
|
buffer.insert([0, 0], '/*')
|
|
|
|
|
buffer.insert([5, 0], '*/')
|
|
|
|
|
|
|
|
|
|
buffer.insert([1, 0], 'var ')
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
|
|
|
|
|
describe "when lines are both updated and removed", ->
|
|
|
|
|
it "updates tokens to reflect the change", ->
|
|
|
|
|
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
|
|
|
|
|
|
|
|
|
|
# previous line 0 remains
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0].tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
|
|
|
|
|
|
|
|
|
# previous line 3 should be combined with input to form line 1
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1].tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1].tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
|
|
|
|
|
|
|
|
|
# lines below deleted regions should be shifted upward
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2].tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js'])
|
|
|
|
|
|
|
|
|
|
describe "when the change invalidates the tokenization of subsequent lines", ->
|
|
|
|
|
it "schedules the invalidated lines to be tokenized in the background", ->
|
|
|
|
|
buffer.insert([5, 30], '/* */')
|
|
|
|
|
buffer.setTextInRange([[2, 0], [3, 0]], '/*')
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2].tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js']
|
|
|
|
|
|
|
|
|
|
advanceClock()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
|
|
|
|
|
describe "when lines are both updated and inserted", ->
|
|
|
|
|
it "updates tokens to reflect the change", ->
|
|
|
|
|
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
|
|
|
|
|
|
|
|
|
# previous line 0 remains
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0].tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.type.var.js'])
|
|
|
|
|
|
|
|
|
|
# 3 new lines inserted
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1].tokens[0]).toEqual(value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2].tokens[0]).toEqual(value: 'bar', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[0]).toEqual(value: 'baz', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
|
|
|
|
|
# previous line 2 is joined with quux() on line 4
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].tokens[0]).toEqual(value: 'quux', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
|
|
|
|
|
|
|
|
|
# previous line 3 is pushed down to become line 5
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5].tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
|
|
|
|
|
|
|
|
|
describe "when the change invalidates the tokenization of subsequent lines", ->
|
|
|
|
|
it "schedules the invalidated lines to be tokenized in the background", ->
|
|
|
|
|
buffer.insert([5, 30], '/* */')
|
|
|
|
|
buffer.insert([2, 0], '/*\nabcde\nabcder')
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2].tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[3].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5].tokens[0].scopes).toEqual ['source.js']
|
|
|
|
|
|
|
|
|
|
advanceClock() # tokenize invalidated lines in background
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[6].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[7].tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[8].tokens[0].scopes).not.toBe ['source.js', 'comment.block.js']
|
|
|
|
|
|
|
|
|
|
describe "when there is an insertion that is larger than the chunk size", ->
|
|
|
|
|
it "tokenizes the initial chunk synchronously, then tokenizes the remaining lines in the background", ->
|
|
|
|
|
commentBlock = _.multiplyString("// a comment\n", tokenizedBuffer.chunkSize + 2)
|
|
|
|
|
buffer.insert([0, 0], commentBlock)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[4].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5]).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
advanceClock()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[5].ruleStack?).toBeTruthy()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[6].ruleStack?).toBeTruthy()
|
|
|
|
|
|
|
|
|
|
it "does not break out soft tabs across a scope boundary", ->
|
|
|
|
|
waitsForPromise ->
|
|
|
|
|
@@ -284,11 +260,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
|
|
|
|
|
runs ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.coffee'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.coffee'), tabLength: 2})
|
|
|
|
|
startTokenizing(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
afterEach ->
|
|
|
|
|
@@ -352,7 +324,6 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
expect(tokenizedHandler.callCount).toBe(1)
|
|
|
|
|
|
|
|
|
|
it "retokenizes the buffer", ->
|
|
|
|
|
|
|
|
|
|
waitsForPromise ->
|
|
|
|
|
atom.packages.activatePackage('language-ruby-on-rails')
|
|
|
|
|
|
|
|
|
|
@@ -362,14 +333,10 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
runs ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync()
|
|
|
|
|
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar('test.erb'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.selectGrammar('test.erb'), tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
|
|
|
|
{tokens} = tokenizedBuffer.tokenizedLines[0]
|
|
|
|
|
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
|
|
|
|
|
|
|
|
|
waitsForPromise ->
|
|
|
|
|
@@ -377,7 +344,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
|
|
|
|
|
runs ->
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
|
|
|
|
{tokens} = tokenizedBuffer.tokenizedLines[0]
|
|
|
|
|
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby", "meta.tag.block.any.html", "punctuation.definition.tag.begin.html"]
|
|
|
|
|
|
|
|
|
|
describe ".tokenForPosition(position)", ->
|
|
|
|
|
@@ -387,11 +354,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
|
|
|
|
|
it "returns the correct token (regression)", ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"]
|
|
|
|
|
expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"]
|
|
|
|
|
@@ -400,16 +363,12 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
|
|
|
|
beforeEach ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
|
|
|
|
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()
|
|
|
|
|
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
describe "when the selector matches a single token at the position", ->
|
|
|
|
|
it "returns the range covered by the token", ->
|
|
|
|
|
@@ -423,11 +382,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
describe ".indentLevelForRow(row)", ->
|
|
|
|
|
beforeEach ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
describe "when the line is non-empty", ->
|
|
|
|
|
@@ -469,7 +424,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
|
|
|
|
|
buffer.insert([12, 0], ' ')
|
|
|
|
|
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[14]).not.toBeDefined()
|
|
|
|
|
|
|
|
|
|
it "updates the indentLevel of empty lines surrounding a change that inserts lines", ->
|
|
|
|
|
buffer.insert([7, 0], '\n\n')
|
|
|
|
|
@@ -503,11 +458,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
buffer.insert [10, 0], " // multi-line\n // comment\n // block\n"
|
|
|
|
|
buffer.insert [0, 0], "// multi-line\n// comment\n// block\n"
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName('source.js'), tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
it "includes the first line of multi-line comments", ->
|
|
|
|
|
@@ -570,40 +521,69 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false
|
|
|
|
|
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
|
|
|
|
|
|
|
|
|
describe "::tokenizedLineForRow(row)", ->
|
|
|
|
|
it "returns the tokenized line for a row, or a placeholder line if it hasn't been tokenized yet", ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
grammar = atom.grammars.grammarForScopeName('source.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
|
|
|
|
|
line0 = buffer.lineForRow(0)
|
|
|
|
|
|
|
|
|
|
jsScopeStartId = grammar.startIdForScope(grammar.scopeName)
|
|
|
|
|
jsScopeEndId = grammar.endIdForScope(grammar.scopeName)
|
|
|
|
|
startTokenizing(tokenizedBuffer)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([jsScopeStartId, line0.length, jsScopeEndId])
|
|
|
|
|
advanceClock(1)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0]).not.toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).not.toEqual([jsScopeStartId, line0.length, jsScopeEndId])
|
|
|
|
|
|
|
|
|
|
nullScopeStartId = NullGrammar.startIdForScope(NullGrammar.scopeName)
|
|
|
|
|
nullScopeEndId = NullGrammar.endIdForScope(NullGrammar.scopeName)
|
|
|
|
|
tokenizedBuffer.setGrammar(NullGrammar)
|
|
|
|
|
startTokenizing(tokenizedBuffer)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([nullScopeStartId, line0.length, nullScopeEndId])
|
|
|
|
|
advanceClock(1)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe(line0)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tags).toEqual([nullScopeStartId, line0.length, nullScopeEndId])
|
|
|
|
|
|
|
|
|
|
it "returns undefined if the requested row is outside the buffer range", ->
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.js')
|
|
|
|
|
grammar = atom.grammars.grammarForScopeName('source.js')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(999)).toBeUndefined()
|
|
|
|
|
|
|
|
|
|
describe "when the buffer is configured with the null grammar", ->
|
|
|
|
|
it "uses the placeholder tokens and does not actually tokenize using the grammar", ->
|
|
|
|
|
spyOn(atom.grammars.nullGrammar, 'tokenizeLine').andCallThrough()
|
|
|
|
|
it "does not actually tokenize using the grammar", ->
|
|
|
|
|
spyOn(NullGrammar, 'tokenizeLine').andCallThrough()
|
|
|
|
|
buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar')
|
|
|
|
|
buffer.setText('a\nb\nc')
|
|
|
|
|
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, tabLength: 2})
|
|
|
|
|
tokenizeCallback = jasmine.createSpy('onDidTokenize')
|
|
|
|
|
tokenizedBuffer.onDidTokenize(tokenizeCallback)
|
|
|
|
|
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2]).toBeUndefined()
|
|
|
|
|
expect(tokenizeCallback.callCount).toBe(0)
|
|
|
|
|
expect(NullGrammar.tokenizeLine).not.toHaveBeenCalled()
|
|
|
|
|
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
expect(tokenizeCallback.callCount).toBe 1
|
|
|
|
|
expect(atom.grammars.nullGrammar.tokenizeLine.callCount).toBe 0
|
|
|
|
|
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens.length).toBe 1
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].value).toBe 'a'
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens.length).toBe 1
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].value).toBe 'b'
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens.length).toBe 1
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].value).toBe 'c'
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[0]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[1]).toBeUndefined()
|
|
|
|
|
expect(tokenizedBuffer.tokenizedLines[2]).toBeUndefined()
|
|
|
|
|
expect(tokenizeCallback.callCount).toBe(0)
|
|
|
|
|
expect(NullGrammar.tokenizeLine).not.toHaveBeenCalled()
|
|
|
|
|
|
|
|
|
|
describe "text decoration layer API", ->
|
|
|
|
|
describe "iterator", ->
|
|
|
|
|
it "iterates over the syntactic scope boundaries", ->
|
|
|
|
|
buffer = new TextBuffer(text: "var foo = 1 /*\nhello*/var bar = 2\n")
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".js"))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName("source.js"), tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
iterator = tokenizedBuffer.buildIterator()
|
|
|
|
|
@@ -655,11 +635,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
|
|
|
|
|
runs ->
|
|
|
|
|
buffer = new TextBuffer(text: "# hello\n# world")
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".coffee"))
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar: atom.grammars.grammarForScopeName("source.coffee"), tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
iterator = tokenizedBuffer.buildIterator()
|
|
|
|
|
@@ -688,11 +664,7 @@ describe "TokenizedBuffer", ->
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
buffer = new TextBuffer(text: 'start x\nend x\nx')
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({
|
|
|
|
|
buffer, grammarRegistry: atom.grammars, packageManager: atom.packages,
|
|
|
|
|
assert: atom.assert, tabLength: 2,
|
|
|
|
|
})
|
|
|
|
|
tokenizedBuffer.setGrammar(grammar)
|
|
|
|
|
tokenizedBuffer = new TokenizedBuffer({buffer, grammar, tabLength: 2})
|
|
|
|
|
fullyTokenize(tokenizedBuffer)
|
|
|
|
|
|
|
|
|
|
iterator = tokenizedBuffer.buildIterator()
|
|
|
|
|
|