Rename Renderer -> DisplayBuffer

The idea is that the DisplayBuffer serves a similar role to the Buffer, except that it's holding content in an arrangement that's optimized for display.
This commit is contained in:
Nathan Sobo
2012-06-13 21:02:30 -06:00
parent 5135f3e066
commit b59e73edcd
5 changed files with 234 additions and 234 deletions

View File

@@ -1,62 +1,62 @@
Renderer = require 'renderer'
DisplayBuffer = require 'display-buffer'
Buffer = require 'buffer'
describe "Renderer", ->
[renderer, buffer, changeHandler, tabText] = []
describe "DisplayBuffer", ->
[displayBuffer, buffer, changeHandler, tabText] = []
beforeEach ->
tabText = ' '
buffer = new Buffer(require.resolve 'fixtures/sample.js')
renderer = new Renderer(buffer, {tabText})
displayBuffer = new DisplayBuffer(buffer, {tabText})
changeHandler = jasmine.createSpy 'changeHandler'
renderer.on 'change', changeHandler
displayBuffer.on 'change', changeHandler
describe "when the buffer changes", ->
it "renders line numbers correctly", ->
originalLineCount = renderer.lineCount()
originalLineCount = displayBuffer.lineCount()
oneHundredLines = [0..100].join("\n")
buffer.insert([0,0], oneHundredLines)
expect(renderer.lineCount()).toBe 100 + originalLineCount
expect(displayBuffer.lineCount()).toBe 100 + originalLineCount
describe "soft wrapping", ->
beforeEach ->
renderer.setSoftWrapColumn(50)
displayBuffer.setSoftWrapColumn(50)
changeHandler.reset()
describe "rendering of soft-wrapped lines", ->
describe "when the line is shorter than the max line length", ->
it "renders the line unchanged", ->
expect(renderer.lineForRow(0).text).toBe buffer.lineForRow(0)
expect(displayBuffer.lineForRow(0).text).toBe buffer.lineForRow(0)
describe "when the line is empty", ->
it "renders the empty line", ->
expect(renderer.lineForRow(13).text).toBe ''
expect(displayBuffer.lineForRow(13).text).toBe ''
describe "when there is a non-whitespace character at the max length boundary", ->
describe "when there is whitespace before the boundary", ->
it "wraps the line at the end of the first whitespace preceding the boundary", ->
expect(renderer.lineForRow(10).text).toBe ' return '
expect(renderer.lineForRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
expect(displayBuffer.lineForRow(10).text).toBe ' return '
expect(displayBuffer.lineForRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
describe "when there is no whitespace before the boundary", ->
it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", ->
buffer.change([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n')
renderer.setSoftWrapColumn(10)
expect(renderer.lineForRow(0).text).toBe 'abcdefghij'
expect(renderer.lineForRow(1).text).toBe 'klmnopqrst'
expect(renderer.lineForRow(2).text).toBe 'uvwxyz'
displayBuffer.setSoftWrapColumn(10)
expect(displayBuffer.lineForRow(0).text).toBe 'abcdefghij'
expect(displayBuffer.lineForRow(1).text).toBe 'klmnopqrst'
expect(displayBuffer.lineForRow(2).text).toBe 'uvwxyz'
describe "when there is a whitespace character at the max length boundary", ->
it "wraps the line at the first non-whitespace character following the boundary", ->
expect(renderer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
expect(renderer.lineForRow(4).text).toBe 'right = [];'
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
expect(displayBuffer.lineForRow(4).text).toBe 'right = [];'
describe "when the buffer changes", ->
describe "when buffer lines are updated", ->
describe "when the update makes a soft-wrapped line shorter than the max line length", ->
it "rewraps the line and emits a change event", ->
buffer.delete([[6, 24], [6, 42]])
expect(renderer.lineForRow(7).text).toBe ' current < pivot ? : right.push(current);'
expect(renderer.lineForRow(8).text).toBe ' }'
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? : right.push(current);'
expect(displayBuffer.lineForRow(8).text).toBe ' }'
expect(changeHandler).toHaveBeenCalled()
[[event]]= changeHandler.argsForCall
@@ -67,10 +67,10 @@ describe "Renderer", ->
describe "when the update causes a line to softwrap an additional time", ->
it "rewraps the line and emits a change event", ->
buffer.insert([6, 28], '1234567890')
expect(renderer.lineForRow(7).text).toBe ' current < pivot ? '
expect(renderer.lineForRow(8).text).toBe 'left1234567890.push(current) : '
expect(renderer.lineForRow(9).text).toBe 'right.push(current);'
expect(renderer.lineForRow(10).text).toBe ' }'
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? '
expect(displayBuffer.lineForRow(8).text).toBe 'left1234567890.push(current) : '
expect(displayBuffer.lineForRow(9).text).toBe 'right.push(current);'
expect(displayBuffer.lineForRow(10).text).toBe ' }'
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -81,10 +81,10 @@ describe "Renderer", ->
describe "when buffer lines are inserted", ->
it "inserts / updates wrapped lines and emits a change event", ->
buffer.insert([6, 21], '1234567890 abcdefghij 1234567890\nabcdefghij')
expect(renderer.lineForRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
expect(renderer.lineForRow(8).text).toBe '1234567890'
expect(renderer.lineForRow(9).text).toBe 'abcdefghij ? left.push(current) : '
expect(renderer.lineForRow(10).text).toBe 'right.push(current);'
expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
expect(displayBuffer.lineForRow(8).text).toBe '1234567890'
expect(displayBuffer.lineForRow(9).text).toBe 'abcdefghij ? left.push(current) : '
expect(displayBuffer.lineForRow(10).text).toBe 'right.push(current);'
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -95,10 +95,10 @@ describe "Renderer", ->
describe "when buffer lines are removed", ->
it "removes lines and emits a change event", ->
buffer.change([[3, 21], [7, 5]], ';')
expect(renderer.lineForRow(3).text).toBe ' var pivot = items;'
expect(renderer.lineForRow(4).text).toBe ' return '
expect(renderer.lineForRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
expect(renderer.lineForRow(6).text).toBe ' };'
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items;'
expect(displayBuffer.lineForRow(4).text).toBe ' return '
expect(displayBuffer.lineForRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
expect(displayBuffer.lineForRow(6).text).toBe ' };'
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -109,31 +109,31 @@ describe "Renderer", ->
describe "position translation", ->
it "translates positions accounting for wrapped lines", ->
# before any wrapped lines
expect(renderer.screenPositionForBufferPosition([0, 5])).toEqual([0, 5])
expect(renderer.bufferPositionForScreenPosition([0, 5])).toEqual([0, 5])
expect(renderer.screenPositionForBufferPosition([0, 29])).toEqual([0, 29])
expect(renderer.bufferPositionForScreenPosition([0, 29])).toEqual([0, 29])
expect(displayBuffer.screenPositionForBufferPosition([0, 5])).toEqual([0, 5])
expect(displayBuffer.bufferPositionForScreenPosition([0, 5])).toEqual([0, 5])
expect(displayBuffer.screenPositionForBufferPosition([0, 29])).toEqual([0, 29])
expect(displayBuffer.bufferPositionForScreenPosition([0, 29])).toEqual([0, 29])
# on a wrapped line
expect(renderer.screenPositionForBufferPosition([3, 5])).toEqual([3, 5])
expect(renderer.bufferPositionForScreenPosition([3, 5])).toEqual([3, 5])
expect(renderer.screenPositionForBufferPosition([3, 50])).toEqual([3, 50])
expect(renderer.screenPositionForBufferPosition([3, 51])).toEqual([4, 0])
expect(renderer.bufferPositionForScreenPosition([4, 0])).toEqual([3, 51])
expect(renderer.bufferPositionForScreenPosition([3, 50])).toEqual([3, 50])
expect(renderer.screenPositionForBufferPosition([3, 62])).toEqual([4, 11])
expect(renderer.bufferPositionForScreenPosition([4, 11])).toEqual([3, 62])
expect(displayBuffer.screenPositionForBufferPosition([3, 5])).toEqual([3, 5])
expect(displayBuffer.bufferPositionForScreenPosition([3, 5])).toEqual([3, 5])
expect(displayBuffer.screenPositionForBufferPosition([3, 50])).toEqual([3, 50])
expect(displayBuffer.screenPositionForBufferPosition([3, 51])).toEqual([4, 0])
expect(displayBuffer.bufferPositionForScreenPosition([4, 0])).toEqual([3, 51])
expect(displayBuffer.bufferPositionForScreenPosition([3, 50])).toEqual([3, 50])
expect(displayBuffer.screenPositionForBufferPosition([3, 62])).toEqual([4, 11])
expect(displayBuffer.bufferPositionForScreenPosition([4, 11])).toEqual([3, 62])
# following a wrapped line
expect(renderer.screenPositionForBufferPosition([4, 5])).toEqual([5, 5])
expect(renderer.bufferPositionForScreenPosition([5, 5])).toEqual([4, 5])
expect(displayBuffer.screenPositionForBufferPosition([4, 5])).toEqual([5, 5])
expect(displayBuffer.bufferPositionForScreenPosition([5, 5])).toEqual([4, 5])
describe ".setSoftWrapColumn(length)", ->
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
renderer.setSoftWrapColumn(40)
expect(tokensText renderer.lineForRow(4).tokens).toBe 'left = [], right = [];'
expect(tokensText renderer.lineForRow(5).tokens).toBe ' while(items.length > 0) {'
expect(tokensText renderer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
displayBuffer.setSoftWrapColumn(40)
expect(tokensText displayBuffer.lineForRow(4).tokens).toBe 'left = [], right = [];'
expect(tokensText displayBuffer.lineForRow(5).tokens).toBe ' while(items.length > 0) {'
expect(tokensText displayBuffer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
expect(changeHandler).toHaveBeenCalled()
[event] = changeHandler.argsForCall[0]
@@ -144,68 +144,68 @@ describe "Renderer", ->
describe "structural folding", ->
describe "the foldable flag on screen lines", ->
it "sets 'foldable' to true for screen lines that start a foldable region", ->
expect(renderer.lineForRow(0).foldable).toBeTruthy()
expect(renderer.lineForRow(1).foldable).toBeTruthy()
expect(renderer.lineForRow(2).foldable).toBeFalsy()
expect(renderer.lineForRow(3).foldable).toBeFalsy()
expect(displayBuffer.lineForRow(0).foldable).toBeTruthy()
expect(displayBuffer.lineForRow(1).foldable).toBeTruthy()
expect(displayBuffer.lineForRow(2).foldable).toBeFalsy()
expect(displayBuffer.lineForRow(3).foldable).toBeFalsy()
describe "when a foldable line is wrapped", ->
it "only marks the first screen line as foldable", ->
renderer.setSoftWrapColumn(20)
expect(renderer.lineForRow(0).foldable).toBeTruthy()
expect(renderer.lineForRow(1).foldable).toBeFalsy()
expect(renderer.lineForRow(2).foldable).toBeTruthy()
expect(renderer.lineForRow(3).foldable).toBeFalsy()
displayBuffer.setSoftWrapColumn(20)
expect(displayBuffer.lineForRow(0).foldable).toBeTruthy()
expect(displayBuffer.lineForRow(1).foldable).toBeFalsy()
expect(displayBuffer.lineForRow(2).foldable).toBeTruthy()
expect(displayBuffer.lineForRow(3).foldable).toBeFalsy()
describe ".foldAll()", ->
it "folds every foldable line", ->
renderer.foldAll()
fold = renderer.lineForRow(0).fold
displayBuffer.foldAll()
fold = displayBuffer.lineForRow(0).fold
expect(fold).toBeDefined()
expect([fold.startRow, fold.endRow]).toEqual [0,12]
expect(Object.keys(renderer.activeFolds).length).toBe(3)
expect(renderer.activeFolds[1].length).toBe(1)
expect(renderer.activeFolds[4].length).toBe(1)
expect(Object.keys(displayBuffer.activeFolds).length).toBe(3)
expect(displayBuffer.activeFolds[1].length).toBe(1)
expect(displayBuffer.activeFolds[4].length).toBe(1)
it "doesn't fold lines that are already folded", ->
renderer.toggleFoldAtBufferRow(4)
renderer.foldAll()
expect(Object.keys(renderer.activeFolds).length).toBe(3)
expect(renderer.activeFolds[0].length).toBe(1)
expect(renderer.activeFolds[1].length).toBe(1)
expect(renderer.activeFolds[4].length).toBe(1)
displayBuffer.toggleFoldAtBufferRow(4)
displayBuffer.foldAll()
expect(Object.keys(displayBuffer.activeFolds).length).toBe(3)
expect(displayBuffer.activeFolds[0].length).toBe(1)
expect(displayBuffer.activeFolds[1].length).toBe(1)
expect(displayBuffer.activeFolds[4].length).toBe(1)
describe ".toggleFoldAtBufferRow(bufferRow)", ->
describe "when bufferRow can be folded", ->
it "creates/destroys a fold based on the syntactic region starting at the given row", ->
renderer.toggleFoldAtBufferRow(1)
fold = renderer.lineForRow(1).fold
displayBuffer.toggleFoldAtBufferRow(1)
fold = displayBuffer.lineForRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 9
renderer.toggleFoldAtBufferRow(1)
expect(renderer.lineForRow(1).fold).toBeUndefined()
displayBuffer.toggleFoldAtBufferRow(1)
expect(displayBuffer.lineForRow(1).fold).toBeUndefined()
describe "when bufferRow can't be folded", ->
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
renderer.toggleFoldAtBufferRow(8)
fold = renderer.lineForRow(1).fold
displayBuffer.toggleFoldAtBufferRow(8)
fold = displayBuffer.lineForRow(1).fold
expect(fold.startRow).toBe 1
expect(fold.endRow).toBe 9
describe "primitive folding", ->
beforeEach ->
buffer = new Buffer(require.resolve 'fixtures/two-hundred.txt')
renderer = new Renderer(buffer, {tabText})
renderer.on 'change', changeHandler
displayBuffer = new DisplayBuffer(buffer, {tabText})
displayBuffer.on 'change', changeHandler
describe "when folds are created and destroyed", ->
describe "when a fold spans multiple lines", ->
it "replaces the lines spanned by the fold with a placeholder that references the fold object", ->
fold = renderer.createFold(4, 7)
fold = displayBuffer.createFold(4, 7)
[line4, line5] = renderer.linesForRows(4, 5)
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe fold
expect(line4.text).toMatch /^4-+/
expect(line4.bufferDelta).toEqual [4, 0]
@@ -220,7 +220,7 @@ describe "Renderer", ->
changeHandler.reset()
fold.destroy()
[line4, line5] = renderer.linesForRows(4, 5)
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line4.bufferDelta).toEqual [1, 0]
@@ -235,9 +235,9 @@ describe "Renderer", ->
describe "when a fold spans a single line", ->
it "renders a fold placeholder for the folded line but does not skip any lines", ->
fold = renderer.createFold(4, 4)
fold = displayBuffer.createFold(4, 4)
[line4, line5] = renderer.linesForRows(4, 5)
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe fold
expect(line4.text).toMatch /^4-+/
expect(line4.bufferDelta).toEqual [1, 0]
@@ -256,7 +256,7 @@ describe "Renderer", ->
fold.destroy()
[line4, line5] = renderer.linesForRows(4, 5)
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line4.bufferDelta).toEqual [1, 0]
@@ -272,10 +272,10 @@ describe "Renderer", ->
describe "when a fold is nested within another fold", ->
it "does not render the placeholder for the inner fold until the outer fold is destroyed", ->
innerFold = renderer.createFold(6, 7)
outerFold = renderer.createFold(4, 8)
innerFold = displayBuffer.createFold(6, 7)
outerFold = displayBuffer.createFold(4, 8)
[line4, line5] = renderer.linesForRows(4, 5)
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe outerFold
expect(line4.text).toMatch /4-+/
expect(line4.bufferDelta).toEqual [5, 0]
@@ -284,7 +284,7 @@ describe "Renderer", ->
outerFold.destroy()
[line4, line5, line6, line7] = renderer.linesForRows(4, 7)
[line4, line5, line6, line7] = displayBuffer.linesForRows(4, 7)
expect(line4.fold).toBeUndefined()
expect(line4.text).toMatch /^4-+/
expect(line4.bufferDelta).toEqual [1, 0]
@@ -297,10 +297,10 @@ describe "Renderer", ->
expect(line7.text).toBe '8'
it "allows the outer fold to start at the same location as the inner fold", ->
innerFold = renderer.createFold(4, 6)
outerFold = renderer.createFold(4, 8)
innerFold = displayBuffer.createFold(4, 6)
outerFold = displayBuffer.createFold(4, 8)
[line4, line5] = renderer.linesForRows(4, 5)
[line4, line5] = displayBuffer.linesForRows(4, 5)
expect(line4.fold).toBe outerFold
expect(line4.text).toMatch /4-+/
expect(line4.bufferDelta).toEqual [5, 0]
@@ -309,46 +309,46 @@ describe "Renderer", ->
describe "when creating a fold where one already exists", ->
it "returns existing fold and does't create new fold", ->
fold = renderer.createFold(0,10)
expect(renderer.activeFolds[0].length).toBe 1
fold = displayBuffer.createFold(0,10)
expect(displayBuffer.activeFolds[0].length).toBe 1
newFold = renderer.createFold(0,10)
newFold = displayBuffer.createFold(0,10)
expect(newFold).toBe fold
expect(renderer.activeFolds[0].length).toBe 1
expect(displayBuffer.activeFolds[0].length).toBe 1
describe "when a fold is created inside an existing folded region", ->
it "creates/destroys the fold, but does not trigger change event", ->
outerFold = renderer.createFold(0, 10)
outerFold = displayBuffer.createFold(0, 10)
changeHandler.reset()
innerFold = renderer.createFold(2, 5)
innerFold = displayBuffer.createFold(2, 5)
expect(changeHandler).not.toHaveBeenCalled()
[line0, line1] = renderer.linesForRows(0, 1)
[line0, line1] = displayBuffer.linesForRows(0, 1)
expect(line0.fold).toBe outerFold
expect(line1.fold).toBeUndefined()
changeHandler.reset()
innerFold.destroy()
expect(changeHandler).not.toHaveBeenCalled()
[line0, line1] = renderer.linesForRows(0, 1)
[line0, line1] = displayBuffer.linesForRows(0, 1)
expect(line0.fold).toBe outerFold
expect(line1.fold).toBeUndefined()
describe "when the buffer changes", ->
[fold1, fold2] = []
beforeEach ->
fold1 = renderer.createFold(2, 4)
fold2 = renderer.createFold(6, 8)
fold1 = displayBuffer.createFold(2, 4)
fold2 = displayBuffer.createFold(6, 8)
changeHandler.reset()
describe "when the old range surrounds a fold", ->
it "removes the fold and replaces the selection with the new text", ->
buffer.change([[1, 0], [5, 1]], 'party!')
expect(renderer.lineForRow(0).text).toBe "0"
expect(renderer.lineForRow(1).text).toBe "party!"
expect(renderer.lineForRow(2).fold).toBe fold2
expect(renderer.lineForRow(3).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(0).text).toBe "0"
expect(displayBuffer.lineForRow(1).text).toBe "party!"
expect(displayBuffer.lineForRow(2).fold).toBe fold2
expect(displayBuffer.lineForRow(3).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -358,14 +358,14 @@ describe "Renderer", ->
describe "when the old range surrounds two nested folds", ->
it "removes both folds and replaces the selection with the new text", ->
renderer.createFold(2, 9)
displayBuffer.createFold(2, 9)
changeHandler.reset()
buffer.change([[1, 0], [10, 0]], 'goodbye')
expect(renderer.lineForRow(0).text).toBe "0"
expect(renderer.lineForRow(1).text).toBe "goodbye10"
expect(renderer.lineForRow(2).text).toBe "11"
expect(displayBuffer.lineForRow(0).text).toBe "0"
expect(displayBuffer.lineForRow(1).text).toBe "goodbye10"
expect(displayBuffer.lineForRow(2).text).toBe "11"
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -386,11 +386,11 @@ describe "Renderer", ->
it "updates the buffer and re-positions subsequent folds", ->
buffer.change([[0, 0], [1, 1]], 'abc')
expect(renderer.lineForRow(0).text).toBe "abc"
expect(renderer.lineForRow(1).fold).toBe fold1
expect(renderer.lineForRow(2).text).toBe "5"
expect(renderer.lineForRow(3).fold).toBe fold2
expect(renderer.lineForRow(4).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(0).text).toBe "abc"
expect(displayBuffer.lineForRow(1).fold).toBe fold1
expect(displayBuffer.lineForRow(2).text).toBe "5"
expect(displayBuffer.lineForRow(3).fold).toBe fold2
expect(displayBuffer.lineForRow(4).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -400,12 +400,12 @@ describe "Renderer", ->
changeHandler.reset()
fold1.destroy()
expect(renderer.lineForRow(0).text).toBe "abc"
expect(renderer.lineForRow(1).text).toBe "2"
expect(renderer.lineForRow(3).text).toMatch /^4-+/
expect(renderer.lineForRow(4).text).toBe "5"
expect(renderer.lineForRow(5).fold).toBe fold2
expect(renderer.lineForRow(6).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(0).text).toBe "abc"
expect(displayBuffer.lineForRow(1).text).toBe "2"
expect(displayBuffer.lineForRow(3).text).toMatch /^4-+/
expect(displayBuffer.lineForRow(4).text).toBe "5"
expect(displayBuffer.lineForRow(5).fold).toBe fold2
expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -420,19 +420,19 @@ describe "Renderer", ->
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 6
expect(renderer.lineForRow(1).text).toBe '1a'
expect(renderer.lineForRow(2).text).toBe 'b'
expect(renderer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(1).text).toBe '1a'
expect(displayBuffer.lineForRow(2).text).toBe 'b'
expect(displayBuffer.lineForRow(2).fold).toBe fold1
describe "when the old range follows a fold", ->
it "re-positions the screen ranges for the change event based on the preceding fold", ->
buffer.change([[10, 0], [11, 0]], 'abc')
expect(renderer.lineForRow(1).text).toBe "1"
expect(renderer.lineForRow(2).fold).toBe fold1
expect(renderer.lineForRow(3).text).toBe "5"
expect(renderer.lineForRow(4).fold).toBe fold2
expect(renderer.lineForRow(5).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toBe "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -447,13 +447,13 @@ describe "Renderer", ->
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 5
expect(renderer.lineForRow(1).text).toBe "1"
expect(renderer.lineForRow(2).text).toBe "2"
expect(renderer.lineForRow(2).fold).toBe fold1
expect(renderer.lineForRow(2).bufferDelta).toEqual [4, 0]
expect(renderer.lineForRow(3).text).toMatch "5"
expect(renderer.lineForRow(4).fold).toBe fold2
expect(renderer.lineForRow(5).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).text).toBe "2"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(2).bufferDelta).toEqual [4, 0]
expect(displayBuffer.lineForRow(3).text).toMatch "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -467,13 +467,13 @@ describe "Renderer", ->
expect(fold1.startRow).toBe 2
expect(fold1.endRow).toBe 7
expect(renderer.lineForRow(1).text).toBe "1"
expect(renderer.lineForRow(2).text).toBe "2"
expect(renderer.lineForRow(2).fold).toBe fold1
expect(renderer.lineForRow(2).bufferDelta).toEqual [6, 0]
expect(renderer.lineForRow(3).text).toMatch "5"
expect(renderer.lineForRow(4).fold).toBe fold2
expect(renderer.lineForRow(5).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).text).toBe "2"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(2).bufferDelta).toEqual [6, 0]
expect(displayBuffer.lineForRow(3).text).toMatch "5"
expect(displayBuffer.lineForRow(4).fold).toBe fold2
expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -494,12 +494,12 @@ describe "Renderer", ->
it "re-renders the line with the placeholder and re-positions the second fold", ->
buffer.insert([5, 0], 'abc\n')
expect(renderer.lineForRow(1).text).toBe "1"
expect(renderer.lineForRow(2).fold).toBe fold1
expect(renderer.lineForRow(3).text).toMatch "abc"
expect(renderer.lineForRow(4).text).toBe "5"
expect(renderer.lineForRow(5).fold).toBe fold2
expect(renderer.lineForRow(6).text).toMatch /^9-+/
expect(displayBuffer.lineForRow(1).text).toBe "1"
expect(displayBuffer.lineForRow(2).fold).toBe fold1
expect(displayBuffer.lineForRow(3).text).toMatch "abc"
expect(displayBuffer.lineForRow(4).text).toBe "5"
expect(displayBuffer.lineForRow(5).fold).toBe fold2
expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/
expect(changeHandler).toHaveBeenCalled()
[[event]] = changeHandler.argsForCall
@@ -509,98 +509,98 @@ describe "Renderer", ->
describe "position translation", ->
it "translates positions to account for folded lines and characters and the placeholder", ->
renderer.createFold(4, 7)
displayBuffer.createFold(4, 7)
# preceding fold: identity
expect(renderer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0]
expect(renderer.screenPositionForBufferPosition([4, 0])).toEqual [4, 0]
expect(displayBuffer.screenPositionForBufferPosition([3, 0])).toEqual [3, 0]
expect(displayBuffer.screenPositionForBufferPosition([4, 0])).toEqual [4, 0]
expect(renderer.bufferPositionForScreenPosition([3, 0])).toEqual [3, 0]
expect(renderer.bufferPositionForScreenPosition([4, 0])).toEqual [4, 0]
expect(displayBuffer.bufferPositionForScreenPosition([3, 0])).toEqual [3, 0]
expect(displayBuffer.bufferPositionForScreenPosition([4, 0])).toEqual [4, 0]
# inside of fold: translate to the start of the fold
expect(renderer.screenPositionForBufferPosition([4, 35])).toEqual [4, 0]
expect(renderer.screenPositionForBufferPosition([5, 5])).toEqual [4, 0]
expect(displayBuffer.screenPositionForBufferPosition([4, 35])).toEqual [4, 0]
expect(displayBuffer.screenPositionForBufferPosition([5, 5])).toEqual [4, 0]
# following fold
expect(renderer.screenPositionForBufferPosition([8, 0])).toEqual [5, 0]
expect(renderer.screenPositionForBufferPosition([11, 2])).toEqual [8, 2]
expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [5, 0]
expect(displayBuffer.screenPositionForBufferPosition([11, 2])).toEqual [8, 2]
expect(renderer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0]
expect(renderer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2]
expect(displayBuffer.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0]
expect(displayBuffer.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2]
describe ".destroyFoldsContainingBufferRow(row)", ->
describe "when two folds start on the given buffer row", ->
it "destroys both folds", ->
renderer.createFold(2, 4)
renderer.createFold(2, 6)
displayBuffer.createFold(2, 4)
displayBuffer.createFold(2, 6)
expect(renderer.lineForRow(3).text).toBe '7'
renderer.destroyFoldsContainingBufferRow(2)
expect(renderer.lineForRow(3).text).toBe '3'
expect(displayBuffer.lineForRow(3).text).toBe '7'
displayBuffer.destroyFoldsContainingBufferRow(2)
expect(displayBuffer.lineForRow(3).text).toBe '3'
describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
beforeEach ->
renderer.setSoftWrapColumn(50)
displayBuffer.setSoftWrapColumn(50)
it "allows valid positions", ->
expect(renderer.clipScreenPosition([4, 5])).toEqual [4, 5]
expect(renderer.clipScreenPosition([4, 11])).toEqual [4, 11]
expect(displayBuffer.clipScreenPosition([4, 5])).toEqual [4, 5]
expect(displayBuffer.clipScreenPosition([4, 11])).toEqual [4, 11]
it "disallows negative positions", ->
expect(renderer.clipScreenPosition([-1, -1])).toEqual [0, 0]
expect(renderer.clipScreenPosition([-1, 10])).toEqual [0, 0]
expect(renderer.clipScreenPosition([0, -1])).toEqual [0, 0]
expect(displayBuffer.clipScreenPosition([-1, -1])).toEqual [0, 0]
expect(displayBuffer.clipScreenPosition([-1, 10])).toEqual [0, 0]
expect(displayBuffer.clipScreenPosition([0, -1])).toEqual [0, 0]
it "disallows positions beyond the last row", ->
expect(renderer.clipScreenPosition([1000, 0])).toEqual [15, 2]
expect(renderer.clipScreenPosition([1000, 1000])).toEqual [15, 2]
expect(displayBuffer.clipScreenPosition([1000, 0])).toEqual [15, 2]
expect(displayBuffer.clipScreenPosition([1000, 1000])).toEqual [15, 2]
describe "when wrapBeyondNewlines is false (the default)", ->
it "wraps positions beyond the end of hard newlines to the end of the line", ->
expect(renderer.clipScreenPosition([1, 10000])).toEqual [1, 30]
expect(renderer.clipScreenPosition([4, 30])).toEqual [4, 11]
expect(renderer.clipScreenPosition([4, 1000])).toEqual [4, 11]
expect(displayBuffer.clipScreenPosition([1, 10000])).toEqual [1, 30]
expect(displayBuffer.clipScreenPosition([4, 30])).toEqual [4, 11]
expect(displayBuffer.clipScreenPosition([4, 1000])).toEqual [4, 11]
describe "when wrapBeyondNewlines is true", ->
it "wraps positions past the end of hard newlines to the next line", ->
expect(renderer.clipScreenPosition([0, 29], wrapBeyondNewlines: true)).toEqual [0, 29]
expect(renderer.clipScreenPosition([0, 30], wrapBeyondNewlines: true)).toEqual [1, 0]
expect(renderer.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0]
expect(displayBuffer.clipScreenPosition([0, 29], wrapBeyondNewlines: true)).toEqual [0, 29]
expect(displayBuffer.clipScreenPosition([0, 30], wrapBeyondNewlines: true)).toEqual [1, 0]
expect(displayBuffer.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0]
it "wraps positions in the middle of fold lines to the next screen line", ->
renderer.createFold(3, 5)
expect(renderer.clipScreenPosition([3, 5], wrapBeyondNewlines: true)).toEqual [4, 0]
displayBuffer.createFold(3, 5)
expect(displayBuffer.clipScreenPosition([3, 5], wrapBeyondNewlines: true)).toEqual [4, 0]
describe "when wrapAtSoftNewlines is false (the default)", ->
it "clips positions at the end of soft-wrapped lines to the character preceding the end of the line", ->
expect(renderer.clipScreenPosition([3, 50])).toEqual [3, 50]
expect(renderer.clipScreenPosition([3, 51])).toEqual [3, 50]
expect(renderer.clipScreenPosition([3, 58])).toEqual [3, 50]
expect(renderer.clipScreenPosition([3, 1000])).toEqual [3, 50]
expect(displayBuffer.clipScreenPosition([3, 50])).toEqual [3, 50]
expect(displayBuffer.clipScreenPosition([3, 51])).toEqual [3, 50]
expect(displayBuffer.clipScreenPosition([3, 58])).toEqual [3, 50]
expect(displayBuffer.clipScreenPosition([3, 1000])).toEqual [3, 50]
describe "when wrapAtSoftNewlines is true", ->
it "wraps positions at the end of soft-wrapped lines to the next screen line", ->
expect(renderer.clipScreenPosition([3, 50], wrapAtSoftNewlines: true)).toEqual [3, 50]
expect(renderer.clipScreenPosition([3, 51], wrapAtSoftNewlines: true)).toEqual [4, 0]
expect(renderer.clipScreenPosition([3, 58], wrapAtSoftNewlines: true)).toEqual [4, 0]
expect(renderer.clipScreenPosition([3, 1000], wrapAtSoftNewlines: true)).toEqual [4, 0]
expect(displayBuffer.clipScreenPosition([3, 50], wrapAtSoftNewlines: true)).toEqual [3, 50]
expect(displayBuffer.clipScreenPosition([3, 51], wrapAtSoftNewlines: true)).toEqual [4, 0]
expect(displayBuffer.clipScreenPosition([3, 58], wrapAtSoftNewlines: true)).toEqual [4, 0]
expect(displayBuffer.clipScreenPosition([3, 1000], wrapAtSoftNewlines: true)).toEqual [4, 0]
describe "when skipAtomicTokens is false (the default)", ->
it "clips screen positions in the middle of atomic tab characters to the beginning of the character", ->
buffer.insert([0, 0], '\t')
expect(renderer.clipScreenPosition([0, 0])).toEqual [0, 0]
expect(renderer.clipScreenPosition([0, 1])).toEqual [0, 0]
expect(renderer.clipScreenPosition([0, tabText.length])).toEqual [0, tabText.length]
expect(displayBuffer.clipScreenPosition([0, 0])).toEqual [0, 0]
expect(displayBuffer.clipScreenPosition([0, 1])).toEqual [0, 0]
expect(displayBuffer.clipScreenPosition([0, tabText.length])).toEqual [0, tabText.length]
describe "when skipAtomicTokens is true", ->
it "clips screen positions in the middle of atomic tab characters to the end of the character", ->
buffer.insert([0, 0], '\t')
expect(renderer.clipScreenPosition([0, 0], skipAtomicTokens: true)).toEqual [0, 0]
expect(renderer.clipScreenPosition([0, 1], skipAtomicTokens: true)).toEqual [0, tabText.length]
expect(renderer.clipScreenPosition([0, tabText.length], skipAtomicTokens: true)).toEqual [0, tabText.length]
expect(displayBuffer.clipScreenPosition([0, 0], skipAtomicTokens: true)).toEqual [0, 0]
expect(displayBuffer.clipScreenPosition([0, 1], skipAtomicTokens: true)).toEqual [0, tabText.length]
expect(displayBuffer.clipScreenPosition([0, tabText.length], skipAtomicTokens: true)).toEqual [0, tabText.length]
describe ".maxLineLength()", ->
it "returns the length of the longest screen line", ->
expect(renderer.maxLineLength()).toBe 65
expect(displayBuffer.maxLineLength()).toBe 65

View File

@@ -9,7 +9,7 @@ ScreenLine = require 'screen-line'
Token = require 'token'
module.exports =
class Renderer
class DisplayBuffer
@idCounter: 1
lineMap: null
languageMode: null
@@ -25,7 +25,7 @@ class Renderer
@foldsById = {}
@buildLineMap()
@languageMode.on 'change', (e) => @lastLanguageModeChangeEvent = e
@buffer.on "change.renderer#{@id}", (e) => @handleBufferChange(e)
@buffer.on "change.displayBuffer#{@id}", (e) => @handleBufferChange(e)
buildLineMap: ->
@lineMap = new LineMap
@@ -252,9 +252,9 @@ class Renderer
destroy: ->
@languageMode.destroy()
@buffer.off ".renderer#{@id}"
@buffer.off ".displayBuffer#{@id}"
logLines: (start, end) ->
@lineMap.logLines(start, end)
_.extend Renderer.prototype, EventEmitter
_.extend DisplayBuffer.prototype, EventEmitter

View File

@@ -1,6 +1,6 @@
Point = require 'point'
Buffer = require 'buffer'
Renderer = require 'renderer'
DisplayBuffer = require 'display-buffer'
Cursor = require 'cursor'
Selection = require 'selection'
EventEmitter = require 'event-emitter'
@@ -25,7 +25,7 @@ class EditSession
scrollTop: 0
scrollLeft: 0
renderer: null
displayBuffer: null
cursors: null
selections: null
autoIndent: true
@@ -34,8 +34,8 @@ class EditSession
constructor: ({@buffer, @tabText, @autoIndent, @softTabs, @softWrapColumn}) ->
@id = @constructor.idCounter++
@softTabs ?= true
@renderer = new Renderer(@buffer, { @softWrapColumn, @tabText })
@languageMode = @renderer.languageMode
@displayBuffer = new DisplayBuffer(@buffer, { @softWrapColumn, @tabText })
@languageMode = @displayBuffer.languageMode
@cursors = []
@selections = []
@addCursorAtScreenPosition([0, 0])
@@ -44,14 +44,14 @@ class EditSession
for selection in @getSelections()
selection.handleBufferChange(e)
@renderer.on "change.edit-session-#{@id}", (e) =>
@displayBuffer.on "change.edit-session-#{@id}", (e) =>
@trigger 'screen-lines-change', e
@moveCursors (cursor) -> cursor.refreshScreenPosition() unless e.bufferChanged
destroy: ->
@buffer.off ".edit-session-#{@id}"
@renderer.off ".edit-session-#{@id}"
@renderer.destroy()
@displayBuffer.off ".edit-session-#{@id}"
@displayBuffer.destroy()
serialize: ->
buffer: @buffer.serialize()
@@ -72,7 +72,7 @@ class EditSession
setScrollLeft: (@scrollLeft) ->
getScrollLeft: -> @scrollLeft
setSoftWrapColumn: (@softWrapColumn) -> @renderer.setSoftWrapColumn(@softWrapColumn)
setSoftWrapColumn: (@softWrapColumn) -> @displayBuffer.setSoftWrapColumn(@softWrapColumn)
setAutoIndent: (@autoIndent) ->
setSoftTabs: (@softTabs) ->
@@ -91,19 +91,19 @@ class EditSession
scanInRange: (args...) -> @buffer.scanInRange(args...)
backwardsScanInRange: (args...) -> @buffer.backwardsScanInRange(args...)
screenPositionForBufferPosition: (bufferPosition, options) -> @renderer.screenPositionForBufferPosition(bufferPosition, options)
bufferPositionForScreenPosition: (screenPosition, options) -> @renderer.bufferPositionForScreenPosition(screenPosition, options)
screenRangeForBufferRange: (range) -> @renderer.screenRangeForBufferRange(range)
bufferRangeForScreenRange: (range) -> @renderer.bufferRangeForScreenRange(range)
clipScreenPosition: (screenPosition, options) -> @renderer.clipScreenPosition(screenPosition, options)
lineForScreenRow: (row) -> @renderer.lineForRow(row)
linesForScreenRows: (start, end) -> @renderer.linesForRows(start, end)
stateForScreenRow: (screenRow) -> @renderer.stateForScreenRow(screenRow)
screenLineCount: -> @renderer.lineCount()
maxScreenLineLength: -> @renderer.maxLineLength()
getLastScreenRow: -> @renderer.getLastRow()
bufferRowsForScreenRows: (startRow, endRow) -> @renderer.bufferRowsForScreenRows(startRow, endRow)
logScreenLines: (start, end) -> @renderer.logLines(start, end)
screenPositionForBufferPosition: (bufferPosition, options) -> @displayBuffer.screenPositionForBufferPosition(bufferPosition, options)
bufferPositionForScreenPosition: (screenPosition, options) -> @displayBuffer.bufferPositionForScreenPosition(screenPosition, options)
screenRangeForBufferRange: (range) -> @displayBuffer.screenRangeForBufferRange(range)
bufferRangeForScreenRange: (range) -> @displayBuffer.bufferRangeForScreenRange(range)
clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options)
lineForScreenRow: (row) -> @displayBuffer.lineForRow(row)
linesForScreenRows: (start, end) -> @displayBuffer.linesForRows(start, end)
stateForScreenRow: (screenRow) -> @displayBuffer.stateForScreenRow(screenRow)
screenLineCount: -> @displayBuffer.lineCount()
maxScreenLineLength: -> @displayBuffer.maxLineLength()
getLastScreenRow: -> @displayBuffer.getLastRow()
bufferRowsForScreenRows: (startRow, endRow) -> @displayBuffer.bufferRowsForScreenRows(startRow, endRow)
logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
insertText: (text) ->
@mutateSelectedText (selection) -> selection.insertText(text)
@@ -178,26 +178,26 @@ class EditSession
selection.fold() for selection in @getSelections()
foldAll: ->
@renderer.foldAll()
@displayBuffer.foldAll()
toggleFold: ->
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
@toggleFoldAtBufferRow(bufferRow)
toggleFoldAtBufferRow: (bufferRow) ->
@renderer.toggleFoldAtBufferRow(bufferRow)
@displayBuffer.toggleFoldAtBufferRow(bufferRow)
createFold: (startRow, endRow) ->
@renderer.createFold(startRow, endRow)
@displayBuffer.createFold(startRow, endRow)
destroyFoldsContainingBufferRow: (bufferRow) ->
@renderer.destroyFoldsContainingBufferRow(bufferRow)
@displayBuffer.destroyFoldsContainingBufferRow(bufferRow)
unfoldCurrentRow: (row) ->
@renderer.largestFoldForBufferRow(@getLastCursor().getCurrentBufferRow())?.destroy()
@displayBuffer.largestFoldForBufferRow(@getLastCursor().getCurrentBufferRow())?.destroy()
destroyFold: (foldId) ->
fold = @renderer.foldsById[foldId]
fold = @displayBuffer.foldsById[foldId]
fold.destroy()
@setCursorBufferPosition([fold.startRow, 0])

View File

@@ -569,7 +569,7 @@ class Editor extends View
@setScrollPositionFromActiveEditSession()
@renderLines()
@activeEditSession.on 'screen-lines-change', (e) => @handleRendererChange(e)
@activeEditSession.on 'screen-lines-change', (e) => @handleDisplayBufferChange(e)
getCursorView: (index) ->
index ?= @cursorViews.length - 1
@@ -690,7 +690,7 @@ class Editor extends View
getLastVisibleScreenRow: ->
Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1
handleRendererChange: (e) ->
handleDisplayBufferChange: (e) ->
oldScreenRange = e.oldRange
newScreenRange = e.newRange

View File

@@ -5,15 +5,15 @@ module.exports =
class Fold
@idCounter: 1
renderer: null
displayBuffer: null
startRow: null
endRow: null
constructor: (@renderer, @startRow, @endRow) ->
constructor: (@displayBuffer, @startRow, @endRow) ->
@id = @constructor.idCounter++
destroy: ->
@renderer.destroyFold(this)
@displayBuffer.destroyFold(this)
inspect: ->
"Fold(#{@startRow}, #{@endRow})"
@@ -28,15 +28,15 @@ class Fold
oldStartRow = @startRow
if @isContainedByRange(event.oldRange)
@renderer.unregisterFold(@startRow, this)
@displayBuffer.unregisterFold(@startRow, this)
return
@updateStartRow(event)
@updateEndRow(event)
if @startRow != oldStartRow
@renderer.unregisterFold(oldStartRow, this)
@renderer.registerFold(this)
@displayBuffer.unregisterFold(oldStartRow, this)
@displayBuffer.registerFold(this)
isContainedByRange: (range) ->
range.start.row <= @startRow and @endRow <= range.end.row