Highlighter emits change events.

Changes to the buffer may cause lines beyond the scope of the textual
change to be re-highlighted. If so so, this is reflected in the
pre/post range of Highlighter's change events.
This commit is contained in:
Corey Johnson & Nathan Sobo
2012-02-02 17:45:57 -08:00
parent 803e12a201
commit 84c104b0b9
2 changed files with 82 additions and 4 deletions

View File

@@ -15,9 +15,16 @@ describe "Highlighter", ->
expect(highlighter.tokensForRow(11)[1]).toEqual(type: 'keyword', value: 'return')
describe "when the buffer changes", ->
changeHandler = null
beforeEach ->
changeHandler = jasmine.createSpy('changeHandler')
highlighter.on "change", changeHandler
describe "when lines are updated, but none are added or removed", ->
it "updates tokens for each of the changed lines", ->
buffer.change(new Range([0, 0], [2, 0]), "foo()\nbar()\n")
range = new Range([0, 0], [2, 0])
buffer.change(range, "foo()\nbar()\n")
expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'identifier', value: 'foo')
expect(highlighter.tokensForRow(1)[0]).toEqual(type: 'identifier', value: 'bar')
@@ -25,16 +32,30 @@ describe "Highlighter", ->
# line 2 is unchanged
expect(highlighter.tokensForRow(2)[1]).toEqual(type: 'keyword', value: 'if')
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
expect(event.preRange).toEqual range
expect(event.postRange).toEqual new Range([0, 0], [2,0])
it "updates tokens for lines beyond the changed lines if needed", ->
buffer.insert([5, 30], '/* */')
changeHandler.reset()
buffer.insert([2, 0], '/*')
expect(highlighter.tokensForRow(3)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(4)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(5)[0].type).toBe 'comment'
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
expect(event.preRange).toEqual new Range([2, 0], [6, buffer.getLine(6).length])
expect(event.postRange).toEqual new Range([2, 0], [6, buffer.getLine(6).length])
describe "when lines are both updated and removed", ->
it "updates tokens to reflect the removed lines", ->
buffer.change(new Range([1, 0], [3, 0]), "foo()")
range = new Range([1, 0], [3, 0])
buffer.change(range, "foo()")
# previous line 0 remains
expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var')
@@ -48,9 +69,29 @@ describe "Highlighter", ->
expect(highlighter.tokensForRow(3)[1]).toEqual(type: 'identifier', value: 'current')
expect(highlighter.tokensForRow(4)[3]).toEqual(type: 'keyword.operator', value: '<')
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
expect(event.preRange).toEqual range
expect(event.postRange).toEqual new Range([1, 0], [1, 5])
it "updates tokens for lines beyond the changed lines if needed", ->
buffer.insert([5, 30], '/* */')
changeHandler.reset()
buffer.change(new Range([2, 0], [3, 0]), '/*')
expect(highlighter.tokensForRow(2)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(3)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(4)[0].type).toBe 'comment'
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
expect(event.preRange).toEqual new Range([2, 0], [6, buffer.getLine(5).length])
expect(event.postRange).toEqual new Range([2, 0], [5, buffer.getLine(5).length])
describe "when lines are both updated and inserted", ->
it "updates tokens to reflect the inserted lines", ->
buffer.change(new Range([1, 0], [2, 0]), "foo()\nbar()\nbaz()\nquux()")
range = new Range([1, 0], [2, 0])
buffer.change(range, "foo()\nbar()\nbaz()\nquux()")
# previous line 0 remains
expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var')
@@ -67,3 +108,25 @@ describe "Highlighter", ->
# previous line 3 is pushed down to become line 5
expect(highlighter.tokensForRow(5)[3]).toEqual(type: 'identifier', value: 'pivot')
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
expect(event.preRange).toEqual range
expect(event.postRange).toEqual new Range([1, 0], [4, 6])
it "updates tokens for lines beyond the changed lines if needed", ->
buffer.insert([5, 30], '/* */')
changeHandler.reset()
buffer.insert([2, 0], '/*\nabcde\nabcder')
expect(highlighter.tokensForRow(2)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(3)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(4)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(5)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(6)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(7)[0].type).toBe 'comment'
expect(highlighter.tokensForRow(8)[0].type).not.toBe 'comment'
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
expect(event.preRange).toEqual new Range([2, 0], [6, buffer.getLine(8).length])
expect(event.postRange).toEqual new Range([2, 0], [8, buffer.getLine(8).length])

View File

@@ -16,7 +16,8 @@ class Highlighter
@tokenizer = (new Mode).getTokenizer()
handleBufferChange: (e) ->
{ preRange, postRange } = e
preRange = e.preRange.copy()
postRange = e.postRange.copy()
previousState = @lines[preRange.end.row].state
newLines = @tokenizeRows('start', postRange.start.row, postRange.end.row)
@@ -28,6 +29,13 @@ class Highlighter
previousState = @lines[nextRow].state
@lines[nextRow] = @tokenizeRow(@lines[row].state, nextRow)
preRange.end.row++
preRange.end.column = @buffer.getLine(nextRow).length
postRange.end.row++
postRange.end.column = @buffer.getLine(nextRow).length
@trigger("change", {preRange, postRange})
tokenizeRows: (startState, startRow, endRow) ->
state = startState
for row in [startRow..endRow]
@@ -41,3 +49,10 @@ class Highlighter
tokensForRow: (row) ->
@lines[row].tokens
on: (eventName, handler) ->
@eventHandlers ?= {}
@eventHandlers[eventName] ?= []
@eventHandlers[eventName].push(handler)
trigger: (eventName, event) ->
@eventHandlers?[eventName]?.forEach (handler) -> handler(event)