mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Remove LineWrapper and LineFolder
They are supplanted by Renderer
This commit is contained in:
@@ -1,378 +0,0 @@
|
||||
Buffer = require 'buffer'
|
||||
Higlighter = require 'highlighter'
|
||||
LineFolder = require 'line-folder'
|
||||
Range = require 'range'
|
||||
|
||||
describe "LineFolder", ->
|
||||
[buffer, folder, changeHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve 'fixtures/sample.js')
|
||||
highlighter = new Higlighter(buffer)
|
||||
folder = new LineFolder(highlighter)
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
folder.on 'change', changeHandler
|
||||
|
||||
describe "when folds are created and removed", ->
|
||||
it "emits 'fold' and 'unfold' events", ->
|
||||
foldHandler = jasmine.createSpy 'foldHandler'
|
||||
unfoldHandler = jasmine.createSpy 'unfoldHandler'
|
||||
folder.on 'fold', foldHandler
|
||||
folder.on 'unfold', unfoldHandler
|
||||
|
||||
foldRange = new Range([4, 29], [7, 4])
|
||||
fold = folder.createFold(foldRange)
|
||||
|
||||
expect(foldHandler).toHaveBeenCalled()
|
||||
[[range]] = foldHandler.argsForCall
|
||||
expect(range).toEqual foldRange
|
||||
|
||||
fold.destroy()
|
||||
expect(unfoldHandler).toHaveBeenCalled()
|
||||
[[range]] = unfoldHandler.argsForCall
|
||||
expect(range).toEqual foldRange
|
||||
|
||||
describe "when there is a single fold spanning multiple lines", ->
|
||||
it "replaces folded lines with a single line containing a placeholder and emits a change event", ->
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
previousLine4Text = line4.text
|
||||
previousLine5Text = line5.text
|
||||
|
||||
fold = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
|
||||
expect(line4.text).toBe ' while(items.length > 0) {...}'
|
||||
expect(line5.text).toBe ' return sort(left).concat(pivot).concat(sort(right));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[4, 0], [7, 5]]
|
||||
expect(event.newRange).toEqual [[4, 0], [4, 33]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold.destroy()
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
expect(line4.text).toBe previousLine4Text
|
||||
expect(line5.text).toBe previousLine5Text
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 33]]
|
||||
expect(event.newRange).toEqual [[4, 0], [7, 5]]
|
||||
|
||||
describe "when there is a single fold contained on a single line", ->
|
||||
it "renders a placeholder for the folded region, but does not skip any lines", ->
|
||||
fold = folder.createFold(new Range([2, 8], [2, 25]))
|
||||
|
||||
[line2, line3] = folder.linesForScreenRows(2, 3)
|
||||
expect(line2.text).toBe ' if (...) return items;'
|
||||
expect(line3.text).toBe ' var pivot = items.shift(), current, left = [], right = [];'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[2, 0], [2, 40]]
|
||||
expect(event.newRange).toEqual [[2, 0], [2, 26]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold.destroy()
|
||||
|
||||
[line2, line3] = folder.linesForScreenRows(2, 3)
|
||||
expect(line2.text).toBe ' if (items.length <= 1) return items;'
|
||||
expect(line3.text).toBe ' var pivot = items.shift(), current, left = [], right = [];'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.newRange).toEqual [[2, 0], [2, 40]]
|
||||
expect(event.oldRange).toEqual [[2, 0], [2, 26]]
|
||||
changeHandler.reset()
|
||||
|
||||
describe "when there is a nested fold", ->
|
||||
it "does not render a placeholder for the nested fold because it is inside of the other fold", ->
|
||||
folder.createFold(new Range([8, 5], [8, 10]))
|
||||
folder.createFold(new Range([4, 29], [8, 36]))
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
|
||||
expect(line4.text).toBe ' while(items.length > 0) {...concat(sort(right));'
|
||||
expect(line5.text).toBe ' };'
|
||||
|
||||
it "renders the contents of the outer fold correctly, including the inner fold's placeholder, when the outer fold is destroyed", ->
|
||||
fold1 = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
fold2 = folder.createFold(new Range([3, 4], [8, 56]))
|
||||
fold2.destroy()
|
||||
expect(folder.lineForScreenRow(5).text).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
|
||||
it "allows the outer fold to start at the same location as the inner fold", ->
|
||||
fold1 = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
fold2 = folder.createFold(new Range([4, 29], [9, 2]))
|
||||
expect(folder.lineForScreenRow(4).text).toBe " while(items.length > 0) {...};"
|
||||
|
||||
describe "when another fold begins on the last line of a fold", ->
|
||||
describe "when the second fold is created before the first fold", ->
|
||||
it "renders a placeholder for both folds on the first line of the first fold", ->
|
||||
fold1 = folder.createFold(new Range([7, 5], [8, 36]))
|
||||
fold2 = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
expect(line4.text).toBe ' while(items.length > 0) {...}...concat(sort(right));'
|
||||
expect(line5.text).toBe ' };'
|
||||
|
||||
expect(changeHandler.callCount).toBe 2
|
||||
[[event1], [event2]] = changeHandler.argsForCall
|
||||
expect(event1.oldRange).toEqual [[7, 0], [8, 56]]
|
||||
expect(event1.newRange).toEqual [[7, 0], [7, 28]]
|
||||
expect(event2.oldRange).toEqual [[4, 0], [7, 28]]
|
||||
expect(event2.newRange).toEqual [[4, 0], [4, 56]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold1.destroy()
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
expect(line4.text).toBe ' while(items.length > 0) {...}'
|
||||
expect(line5.text).toBe ' return sort(left).concat(pivot).concat(sort(right));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 56]]
|
||||
expect(event.newRange).toEqual [[4, 0], [5, 56]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold2.destroy()
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
expect(line4.text).toBe ' while(items.length > 0) {'
|
||||
expect(line5.text).toBe ' current = items.shift();'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 33]]
|
||||
expect(event.newRange).toEqual [[4, 0], [7, 5]]
|
||||
|
||||
describe "when the second fold is created after the first fold", ->
|
||||
it "renders a placeholder for both folds on the first line of the first fold", ->
|
||||
fold1 = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
fold2 = folder.createFold(new Range([7, 5], [8, 36]))
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
expect(line4.text).toBe ' while(items.length > 0) {...}...concat(sort(right));'
|
||||
expect(line5.text).toBe ' };'
|
||||
|
||||
expect(changeHandler.callCount).toBe 2
|
||||
[[event1], [event2]] = changeHandler.argsForCall
|
||||
expect(event1.oldRange).toEqual [[4, 0], [7, 5]]
|
||||
expect(event1.newRange).toEqual [[4, 0], [4, 33]]
|
||||
expect(event2.oldRange).toEqual [[4, 0], [5, 56]]
|
||||
expect(event2.newRange).toEqual [[4, 0], [4, 56]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold1.destroy()
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
[line7] = folder.linesForScreenRows(7, 7)
|
||||
expect(line4.text).toBe ' while(items.length > 0) {'
|
||||
expect(line5.text).toBe ' current = items.shift();'
|
||||
expect(line7.text).toBe ' }...concat(sort(right));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 56]]
|
||||
expect(event.newRange).toEqual [[4, 0], [7, 28]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold2.destroy()
|
||||
[line4, line5] = folder.linesForScreenRows(4, 5)
|
||||
expect(line4.text).toBe ' while(items.length > 0) {'
|
||||
expect(line5.text).toBe ' current = items.shift();'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[7, 0], [7, 28]]
|
||||
expect(event.newRange).toEqual [[7, 0], [8, 56]]
|
||||
|
||||
describe "when creating a fold on the first line of an existing fold", ->
|
||||
it "renders the line correctly", ->
|
||||
folder.createFold(new Range([4, 29], [7, 4]))
|
||||
folder.createFold(new Range([4, 10], [4, 26]))
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(...) {...}'
|
||||
|
||||
describe "when a fold starts at the beginning of a line", ->
|
||||
it "renders a placeholder at the beginning of the line", ->
|
||||
folder.createFold(new Range([4, 0], [7, 4]))
|
||||
expect(folder.lineForScreenRow(4).text).toBe '...}'
|
||||
|
||||
describe "when a fold ends at the beginning of a line", ->
|
||||
it "renders a placeholder at the beginning of the line", ->
|
||||
folder.createFold(new Range([4, 29], [7, 0]))
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(items.length > 0) {... }'
|
||||
|
||||
describe "when a fold starts on the first line of the buffer", ->
|
||||
it "renders the first line correctly when the fold is destroyed", ->
|
||||
fold = folder.createFold(new Range([0, 14], [0, 27]))
|
||||
fold.destroy()
|
||||
expect(folder.lineForScreenRow(0).text).toBe 'var quicksort = function () {'
|
||||
|
||||
it "doesn't raise an error when attempting to fold empty ranges", ->
|
||||
folder.createFold(new Range([1, 1], [1, 1]))
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
[fold1, fold2] = []
|
||||
beforeEach ->
|
||||
fold1 = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
fold2 = folder.createFold(new Range([7, 5], [8, 36]))
|
||||
changeHandler.reset()
|
||||
|
||||
describe "when the old range precedes lines with a fold", ->
|
||||
it "updates the buffer and re-positions subsequent folds", ->
|
||||
buffer.change(new Range([1, 5], [2, 10]), 'abc')
|
||||
|
||||
expect(folder.lineForScreenRow(1).text).toBe ' varabcems.length <= 1) return items;'
|
||||
expect(folder.lineForScreenRow(3).text).toBe ' while(items.length > 0) {...}...concat(sort(right));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[1, 0], [2, 40]]
|
||||
expect(event.newRange).toEqual [[1, 0], [1, 38]]
|
||||
changeHandler.reset()
|
||||
|
||||
fold1.destroy()
|
||||
expect(folder.lineForScreenRow(3).text).toBe ' while(items.length > 0) {'
|
||||
expect(folder.lineForScreenRow(6).text).toBe ' }...concat(sort(right));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[3, 0], [3, 56]]
|
||||
expect(event.newRange).toEqual [[3, 0], [6, 28]]
|
||||
|
||||
describe "when the old range follows lines with a fold", ->
|
||||
it "re-positions the screen ranges for the change event based on the preceding fold", ->
|
||||
buffer.change(new Range([9, 3], [10, 0]), 'abc')
|
||||
|
||||
expect(folder.lineForScreenRow(5).text).toBe ' }abc'
|
||||
expect(folder.lineForScreenRow(6).text).toBe ' return sort(Array.apply(this, arguments));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[5, 0], [6, 0]]
|
||||
expect(event.newRange).toEqual [[5, 0], [5, 6]]
|
||||
|
||||
describe "when the old range contains unfolded text on the first line of a fold, preceding the fold placeholder", ->
|
||||
it "re-renders the line with the placeholder and re-positions the fold", ->
|
||||
buffer.change(new Range([4, 4], [4, 9]), 'slongaz')
|
||||
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' slongaz(items.length > 0) {...}...concat(sort(right));'
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 56]]
|
||||
expect(event.newRange).toEqual [[4, 0], [4, 58]]
|
||||
|
||||
fold1.destroy()
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' slongaz(items.length > 0) {'
|
||||
|
||||
describe "when the old range is contained to a single line in-between two fold placeholders", ->
|
||||
it "re-renders the line with the placeholder and re-positions the second fold", ->
|
||||
buffer.insert([7, 4], 'abc')
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(items.length > 0) {...abc}...concat(sort(right));'
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 56]]
|
||||
expect(event.newRange).toEqual [[4, 0], [4, 59]]
|
||||
|
||||
fold2.destroy()
|
||||
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(items.length > 0) {...abc}'
|
||||
|
||||
describe "when the old range is inside a fold", ->
|
||||
it "does not trigger a change event, but updates the fold and ensures the change is present when the fold is destroyed", ->
|
||||
buffer.change(new Range([4, 29], [6, 0]), 'abc')
|
||||
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(items.length > 0) {...}...concat(sort(right));'
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
|
||||
fold1.destroy()
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(items.length > 0) {abc current < pivot ? left.push(current) : right.push(current);'
|
||||
expect(folder.lineForScreenRow(5).text).toBe ' }...concat(sort(right));'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 56]]
|
||||
expect(event.newRange).toEqual [[4, 0], [5, 28]]
|
||||
|
||||
describe "when the old range surrounds a fold", ->
|
||||
it "removes the fold and replaces the fold placeholder with the new text", ->
|
||||
buffer.change(new Range([4, 29], [7, 4]), 'party()')
|
||||
|
||||
expect(folder.lineForScreenRow(4).text).toBe ' while(items.length > 0) {party()}...concat(sort(right));'
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[[event]] = changeHandler.argsForCall
|
||||
expect(event.oldRange).toEqual [[4, 0], [4, 56]]
|
||||
expect(event.newRange).toEqual [[4, 0], [4, 60]]
|
||||
|
||||
describe "when the old range straddles the start of a fold", ->
|
||||
it "moves the start of the fold to the end of the new range", ->
|
||||
|
||||
describe "when the old region straddles the end of a fold", ->
|
||||
it "moves the start of the fold to the beginning of the new range", ->
|
||||
|
||||
describe "position translation", ->
|
||||
describe "when there is single fold spanning multiple lines", ->
|
||||
it "translates positions to account for folded lines and characters and the placeholder", ->
|
||||
folder.createFold(new Range([4, 29], [7, 4]))
|
||||
|
||||
# preceding fold: identity
|
||||
expect(folder.screenPositionForBufferPosition([3, 0])).toEqual [3, 0]
|
||||
expect(folder.screenPositionForBufferPosition([4, 0])).toEqual [4, 0]
|
||||
expect(folder.screenPositionForBufferPosition([4, 29])).toEqual [4, 29]
|
||||
|
||||
expect(folder.bufferPositionForScreenPosition([3, 0])).toEqual [3, 0]
|
||||
expect(folder.bufferPositionForScreenPosition([4, 0])).toEqual [4, 0]
|
||||
expect(folder.bufferPositionForScreenPosition([4, 29])).toEqual [4, 29]
|
||||
|
||||
# inside of fold: translate to the start of the fold
|
||||
expect(folder.screenPositionForBufferPosition([4, 35])).toEqual [4, 29]
|
||||
expect(folder.screenPositionForBufferPosition([5, 5])).toEqual [4, 29]
|
||||
|
||||
# following fold, on last line of fold
|
||||
expect(folder.screenPositionForBufferPosition([7, 4])).toEqual [4, 32]
|
||||
expect(folder.bufferPositionForScreenPosition([4, 32])).toEqual [7, 4]
|
||||
|
||||
# # following fold, subsequent line
|
||||
expect(folder.screenPositionForBufferPosition([8, 0])).toEqual [5, 0]
|
||||
expect(folder.screenPositionForBufferPosition([11, 13])).toEqual [8, 13]
|
||||
|
||||
expect(folder.bufferPositionForScreenPosition([5, 0])).toEqual [8, 0]
|
||||
expect(folder.bufferPositionForScreenPosition([9, 2])).toEqual [12, 2]
|
||||
|
||||
describe "when there is a single fold spanning a single line", ->
|
||||
it "translates positions to account for folded characters and the placeholder", ->
|
||||
folder.createFold(new Range([4, 10], [4, 15]))
|
||||
|
||||
expect(folder.screenPositionForBufferPosition([4, 5])).toEqual [4, 5]
|
||||
expect(folder.screenPositionForBufferPosition([4, 15])).toEqual [4, 13]
|
||||
expect(folder.screenPositionForBufferPosition([4, 20])).toEqual [4, 18]
|
||||
|
||||
expect(folder.bufferPositionForScreenPosition([4, 5])).toEqual [4, 5]
|
||||
expect(folder.bufferPositionForScreenPosition([4, 13])).toEqual [4, 15]
|
||||
expect(folder.bufferPositionForScreenPosition([4, 18])).toEqual [4, 20]
|
||||
|
||||
describe ".clipScreenPosition(screenPosition)", ->
|
||||
beforeEach ->
|
||||
folder.createFold(new Range([4, 29], [7, 4]))
|
||||
|
||||
it "returns the nearest valid position based on the current screen lines", ->
|
||||
expect(folder.clipScreenPosition([-1, -1])).toEqual [0, 0]
|
||||
expect(folder.clipScreenPosition([0, -1])).toEqual [0, 0]
|
||||
expect(folder.clipScreenPosition([-1, 5])).toEqual [0, 0]
|
||||
expect(folder.clipScreenPosition([1, 10000])).toEqual [1, 30]
|
||||
expect(folder.clipScreenPosition([2, 15])).toEqual [2, 15]
|
||||
expect(folder.clipScreenPosition([4, 32])).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 1000])).toEqual [4, 33]
|
||||
expect(folder.clipScreenPosition([1000, 1000])).toEqual [9, 2]
|
||||
|
||||
describe "when skipAtomicTokens is false (the default)", ->
|
||||
it "clips positions inside a placeholder to the beginning of the placeholder", ->
|
||||
expect(folder.clipScreenPosition([4, 30])).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 31])).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 32])).toEqual [4, 32]
|
||||
|
||||
describe "when skipAtomicTokens is true", ->
|
||||
it "clips positions inside a placeholder to the end of the placeholder", ->
|
||||
expect(folder.clipScreenPosition([4, 29], skipAtomicTokens: true)).toEqual [4, 29]
|
||||
expect(folder.clipScreenPosition([4, 30], skipAtomicTokens: true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 31], skipAtomicTokens: true)).toEqual [4, 32]
|
||||
expect(folder.clipScreenPosition([4, 32], skipAtomicTokens: true)).toEqual [4, 32]
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
LineWrapper = require 'line-wrapper'
|
||||
Buffer = require 'buffer'
|
||||
Highlighter = require 'highlighter'
|
||||
LineFolder = require 'line-folder'
|
||||
Range = require 'range'
|
||||
ScreenLineFragment = require 'screen-line-fragment'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "LineWrapper", ->
|
||||
[wrapper, folder, buffer, changeHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve('fixtures/sample.js'))
|
||||
highlighter = new Highlighter(buffer)
|
||||
folder = new LineFolder(highlighter)
|
||||
wrapper = new LineWrapper(50, folder)
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
wrapper.on 'change', changeHandler
|
||||
|
||||
describe ".lineForScreenRow(row)", ->
|
||||
it "returns tokens for the line fragment corresponding to the given screen row", ->
|
||||
expect(tokensText wrapper.lineForScreenRow(3).tokens).toEqual(' var pivot = items.shift(), current, left = [], ')
|
||||
expect(tokensText wrapper.lineForScreenRow(4).tokens).toEqual('right = [];')
|
||||
expect(tokensText wrapper.lineForScreenRow(5).tokens).toEqual(' while(items.length > 0) {')
|
||||
|
||||
it "does not attempt to wrap in the middle of a fold placeholder", ->
|
||||
buffer.insert([0, 0], '0123456789ABCD\n')
|
||||
wrapper.setMaxLength(10)
|
||||
folder.createFold(new Range([0, 8], [0, 12]))
|
||||
expect(tokensText wrapper.lineForScreenRow(0).tokens).toEqual('01234567 ')
|
||||
expect(tokensText wrapper.lineForScreenRow(1).tokens).toEqual('...CD')
|
||||
|
||||
describe ".lineCount()", ->
|
||||
it "returns the total number of screen lines", ->
|
||||
expect(wrapper.lineCount()).toBe 16
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
describe "when a buffer line is updated", ->
|
||||
describe "when the number of screen lines remains the same for the changed buffer line", ->
|
||||
it "re-wraps the existing lines and emits a change event for all its screen lines", ->
|
||||
buffer.insert([6, 28], '1234567')
|
||||
expect(wrapper.lineForScreenRow(7).text).toBe ' current < pivot ? left1234567.push(current) '
|
||||
expect(wrapper.lineForScreenRow(8).text).toBe ': right.push(current);'
|
||||
expect(wrapper.lineForScreenRow(9).text).toBe ' }'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual([[7, 0], [8, 20]])
|
||||
expect(event.newRange).toEqual([[7, 0], [8, 22]])
|
||||
|
||||
describe "when the number of screen lines increases for the changed buffer line", ->
|
||||
it "re-wraps and adds an additional screen line and emits a change event for all screen lines", ->
|
||||
buffer.insert([6, 28], '1234567890')
|
||||
expect(wrapper.lineForScreenRow(7).text).toBe ' current < pivot ? '
|
||||
expect(wrapper.lineForScreenRow(8).text).toBe 'left1234567890.push(current) : '
|
||||
expect(wrapper.lineForScreenRow(9).text).toBe 'right.push(current);'
|
||||
expect(wrapper.lineForScreenRow(10).text).toBe ' }'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual([[7, 0], [8, 20]])
|
||||
expect(event.newRange).toEqual([[7, 0], [9, 20]])
|
||||
|
||||
describe "when the number of screen lines decreases for the changed buffer line", ->
|
||||
it "re-wraps and removes a screen line and emits a change event for all screen lines", ->
|
||||
buffer.change(new Range([6, 24], [6, 42]), '')
|
||||
expect(wrapper.lineForScreenRow(7).text).toBe ' current < pivot ? : right.push(current);'
|
||||
expect(wrapper.lineForScreenRow(8).text).toBe ' }'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual([[7, 0], [8, 20]])
|
||||
expect(event.newRange).toEqual([[7, 0], [7, 47]])
|
||||
|
||||
describe "when buffer lines are inserted", ->
|
||||
it "re-wraps existing and new screen lines and emits a change event", ->
|
||||
buffer.insert([6, 21], '1234567890 abcdefghij 1234567890\nabcdefghij')
|
||||
expect(wrapper.lineForScreenRow(7).text).toBe ' current < pivot1234567890 abcdefghij '
|
||||
expect(wrapper.lineForScreenRow(8).text).toBe '1234567890'
|
||||
expect(wrapper.lineForScreenRow(9).text).toBe 'abcdefghij ? left.push(current) : '
|
||||
expect(wrapper.lineForScreenRow(10).text).toBe 'right.push(current);'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual([[7, 0], [8, 20]])
|
||||
expect(event.newRange).toEqual([[7, 0], [10, 20]])
|
||||
|
||||
describe "when buffer lines are removed", ->
|
||||
it "removes screen lines and emits a change event", ->
|
||||
buffer.change(new Range([3, 21], [7, 5]), ';')
|
||||
expect(wrapper.lineForScreenRow(3).text).toBe ' var pivot = items;'
|
||||
expect(wrapper.lineForScreenRow(4).text).toBe ' return '
|
||||
expect(wrapper.lineForScreenRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
|
||||
expect(wrapper.lineForScreenRow(6).text).toBe ' };'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual([[3, 0], [11, 45]])
|
||||
expect(event.newRange).toEqual([[3, 0], [5, 45]])
|
||||
|
||||
describe ".setMaxLength(length)", ->
|
||||
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
|
||||
wrapper.setMaxLength(40)
|
||||
expect(tokensText wrapper.lineForScreenRow(4).tokens).toBe 'left = [], right = [];'
|
||||
expect(tokensText wrapper.lineForScreenRow(5).tokens).toBe ' while(items.length > 0) {'
|
||||
expect(tokensText wrapper.lineForScreenRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual([[0, 0], [15, 2]])
|
||||
expect(event.newRange).toEqual([[0, 0], [18, 2]])
|
||||
|
||||
describe ".screenPositionForBufferPosition(point)", ->
|
||||
it "translates the given buffer position to a screen position, accounting for wrapped lines", ->
|
||||
# before any wrapped lines
|
||||
expect(wrapper.screenPositionForBufferPosition([0, 5])).toEqual([0, 5])
|
||||
expect(wrapper.screenPositionForBufferPosition([0, 29])).toEqual([0, 29])
|
||||
|
||||
# on a wrapped line
|
||||
expect(wrapper.screenPositionForBufferPosition([3, 5])).toEqual([3, 5])
|
||||
expect(wrapper.screenPositionForBufferPosition([3, 50])).toEqual([3, 50])
|
||||
expect(wrapper.screenPositionForBufferPosition([3, 62])).toEqual([4, 11])
|
||||
|
||||
# following a wrapped line
|
||||
expect(wrapper.screenPositionForBufferPosition([4, 5])).toEqual([5, 5])
|
||||
|
||||
it "translates a position at the end of a wrapped screen line to the begining of the next screen line", ->
|
||||
expect(wrapper.screenPositionForBufferPosition([3, 51], true)).toEqual([4, 0])
|
||||
|
||||
describe "when the position follows a fold", ->
|
||||
it "adjusts the position to account for the fold", ->
|
||||
fold = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
expect(wrapper.screenPositionForBufferPosition([7, 4])).toEqual [5, 32]
|
||||
expect(wrapper.screenPositionForBufferPosition([8, 12])).toEqual [7, 1]
|
||||
|
||||
describe ".bufferPositionForScreenPosition(point)", ->
|
||||
it "translates the given screen position to a buffer position, account for wrapped lines", ->
|
||||
# before any wrapped lines
|
||||
expect(wrapper.bufferPositionForScreenPosition([0, 5])).toEqual([0, 5])
|
||||
|
||||
# on a wrapped line
|
||||
expect(wrapper.bufferPositionForScreenPosition([3, 5])).toEqual([3, 5])
|
||||
expect(wrapper.bufferPositionForScreenPosition([4, 0])).toEqual([3, 51])
|
||||
expect(wrapper.bufferPositionForScreenPosition([4, 5])).toEqual([3, 56])
|
||||
|
||||
# following a wrapped line
|
||||
expect(wrapper.bufferPositionForScreenPosition([5, 5])).toEqual([4, 5])
|
||||
|
||||
describe "when the position follows a fold placeholder", ->
|
||||
it "adjusts the position to account for the fold", ->
|
||||
fold = folder.createFold(new Range([4, 29], [7, 4]))
|
||||
expect(wrapper.bufferPositionForScreenPosition([5, 32])).toEqual [7, 4]
|
||||
expect(wrapper.bufferPositionForScreenPosition([6, 11])).toEqual [8, 10]
|
||||
|
||||
describe ".wrapScreenLine(screenLine)", ->
|
||||
makeTokens = (tokenValues...) ->
|
||||
tokenValues.map (value) -> { value, type: 'foo' }
|
||||
|
||||
makeScreenLine = (tokenValues...) ->
|
||||
tokens = makeTokens(tokenValues...)
|
||||
text = tokenValues.join('')
|
||||
new ScreenLineFragment(tokens, text, [1, 0], [1, 0])
|
||||
|
||||
beforeEach ->
|
||||
wrapper.setMaxLength(10)
|
||||
|
||||
describe "when the buffer line is shorter than max length", ->
|
||||
it "does not split the line", ->
|
||||
screenLines = wrapper.wrapScreenLine(makeScreenLine 'abc', 'def')
|
||||
expect(screenLines.length).toBe 1
|
||||
expect(screenLines[0].tokens).toEqual(makeTokens 'abc', 'def')
|
||||
|
||||
[line1] = screenLines
|
||||
expect(line1.startColumn).toBe 0
|
||||
expect(line1.endColumn).toBe 6
|
||||
expect(line1.text.length).toBe 6
|
||||
expect(line1.outputDelta).toEqual [1, 0]
|
||||
expect(line1.inputDelta).toEqual [1, 0]
|
||||
|
||||
describe "when the buffer line is empty", ->
|
||||
it "returns a single empty screen line", ->
|
||||
screenLines = wrapper.wrapScreenLine(makeScreenLine())
|
||||
expect(screenLines.length).toBe 1
|
||||
[screenLine] = screenLines
|
||||
expect(screenLine.tokens).toEqual []
|
||||
expect(screenLine.outputDelta).toEqual [1, 0]
|
||||
expect(screenLine.inputDelta).toEqual [1, 0]
|
||||
|
||||
describe "when there is a non-whitespace character at the max-length boundary", ->
|
||||
describe "when there is whitespace before the max-length boundary", ->
|
||||
it "splits the line at the start of the first word before the boundary", ->
|
||||
screenLines = wrapper.wrapScreenLine(makeScreenLine '12 ', '45 ', ' 89A', 'BC')
|
||||
expect(screenLines.length).toBe 2
|
||||
[line1, line2] = screenLines
|
||||
expect(line1.tokens).toEqual(makeTokens '12 ', '45 ', ' ')
|
||||
expect(line2.tokens).toEqual(makeTokens '89A', 'BC')
|
||||
|
||||
expect(line1.startColumn).toBe 0
|
||||
expect(line1.endColumn).toBe 7
|
||||
expect(line1.text.length).toBe 7
|
||||
expect(line1.outputDelta).toEqual [1, 0]
|
||||
expect(line1.inputDelta).toEqual [0, 7]
|
||||
|
||||
expect(line2.startColumn).toBe 7
|
||||
expect(line2.endColumn).toBe 12
|
||||
expect(line2.text.length).toBe 5
|
||||
expect(line2.outputDelta).toEqual [1, 0]
|
||||
expect(line2.inputDelta).toEqual [1, 0]
|
||||
|
||||
describe "when there is no whitespace before the max-length boundary", ->
|
||||
describe "when there is a non-atomic token at the boundary", ->
|
||||
it "splits the line at the boundary, because there's no 'good' place to split it", ->
|
||||
screenLines = wrapper.wrapScreenLine(makeScreenLine '123', '456', '789AB', 'CD')
|
||||
expect(screenLines.length).toBe 2
|
||||
[line1, line2] = screenLines
|
||||
expect(line1.tokens).toEqual(makeTokens '123', '456', '789A')
|
||||
expect(line2.tokens).toEqual(makeTokens 'B', 'CD')
|
||||
|
||||
expect(line1.startColumn).toBe 0
|
||||
expect(line1.endColumn).toBe 10
|
||||
expect(line1.text.length).toBe 10
|
||||
|
||||
expect(line2.startColumn).toBe 10
|
||||
expect(line2.endColumn).toBe 13
|
||||
expect(line2.text.length).toBe 3
|
||||
|
||||
describe "when there is an atomic token at the boundary", ->
|
||||
it "splits the line before the token, because the token itself should not be split", ->
|
||||
screenLine = makeScreenLine '123', '456', '789AB', 'CD'
|
||||
screenLine.tokens[2].isAtomic = true
|
||||
screenLines = wrapper.wrapScreenLine(screenLine)
|
||||
expect(screenLines.length).toBe 2
|
||||
[line1, line2] = screenLines
|
||||
|
||||
expectedLine1Tokens = makeTokens '123', '456', ' '
|
||||
expectedLine1Tokens[2].type = 'text'
|
||||
expect(line1.tokens).toEqual(expectedLine1Tokens)
|
||||
|
||||
expectedLine2Tokens = makeTokens '789AB', 'CD'
|
||||
expectedLine2Tokens[0].isAtomic = true
|
||||
expect(line2.tokens).toEqual expectedLine2Tokens
|
||||
|
||||
expect(line1.startColumn).toBe 0
|
||||
expect(line1.endColumn).toBe 10
|
||||
expect(line1.text).toBe tokensText(expectedLine1Tokens)
|
||||
|
||||
expect(line2.startColumn).toBe 10
|
||||
expect(line2.endColumn).toBe 17
|
||||
expect(line2.text).toBe tokensText(expectedLine2Tokens)
|
||||
|
||||
|
||||
describe "when there is a whitespace character at the max-length boundary", ->
|
||||
it "splits the line at the start of the first word beyond the boundary", ->
|
||||
screenLines = wrapper.wrapScreenLine(makeScreenLine '12 ', '45 ', ' 89 C', 'DE')
|
||||
expect(screenLines.length).toBe 2
|
||||
[line1, line2] = screenLines
|
||||
expect(line1.tokens).toEqual(makeTokens '12 ', '45 ', ' 89 ')
|
||||
expect(line2.tokens).toEqual(makeTokens 'C', 'DE')
|
||||
|
||||
expect(line1.startColumn).toBe 0
|
||||
expect(line1.endColumn).toBe 11
|
||||
expect(line1.text.length).toBe 11
|
||||
|
||||
expect(line2.startColumn).toBe 11
|
||||
expect(line2.endColumn).toBe 14
|
||||
expect(line2.text.length).toBe 3
|
||||
|
||||
describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
it "allows valid positions", ->
|
||||
expect(wrapper.clipScreenPosition([4, 5])).toEqual [4, 5]
|
||||
expect(wrapper.clipScreenPosition([4, 11])).toEqual [4, 11]
|
||||
|
||||
it "disallows negative positions", ->
|
||||
expect(wrapper.clipScreenPosition([-1, -1])).toEqual [0, 0]
|
||||
expect(wrapper.clipScreenPosition([-1, 10])).toEqual [0, 0]
|
||||
expect(wrapper.clipScreenPosition([0, -1])).toEqual [0, 0]
|
||||
|
||||
it "disallows positions beyond the last row", ->
|
||||
expect(wrapper.clipScreenPosition([1000, 0])).toEqual [15, 2]
|
||||
expect(wrapper.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(wrapper.clipScreenPosition([1, 10000])).toEqual [1, 30]
|
||||
expect(wrapper.clipScreenPosition([4, 30])).toEqual [4, 11]
|
||||
expect(wrapper.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(wrapper.clipScreenPosition([0, 29], wrapBeyondNewlines: true)).toEqual [0, 29]
|
||||
expect(wrapper.clipScreenPosition([0, 30], wrapBeyondNewlines: true)).toEqual [1, 0]
|
||||
expect(wrapper.clipScreenPosition([0, 1000], wrapBeyondNewlines: true)).toEqual [1, 0]
|
||||
|
||||
describe "when wrapAtSoftNewlines is false (the default)", ->
|
||||
it "wraps positions at the end of soft-wrapped lines to the character preceding the end of the line", ->
|
||||
expect(wrapper.clipScreenPosition([3, 50])).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 51])).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 58])).toEqual [3, 50]
|
||||
expect(wrapper.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(wrapper.clipScreenPosition([3, 50], wrapAtSoftNewlines: true)).toEqual [3, 50]
|
||||
expect(wrapper.clipScreenPosition([3, 51], wrapAtSoftNewlines: true)).toEqual [4, 0]
|
||||
expect(wrapper.clipScreenPosition([3, 58], wrapAtSoftNewlines: true)).toEqual [4, 0]
|
||||
expect(wrapper.clipScreenPosition([3, 1000], wrapAtSoftNewlines: true)).toEqual [4, 0]
|
||||
|
||||
describe "when skipAtomicTokens is false (the default)", ->
|
||||
it "clips screen positions in the middle of fold placeholders to the to the beginning of fold placeholders", ->
|
||||
folder.createFold(new Range([3, 55], [3, 59]))
|
||||
expect(wrapper.clipScreenPosition([4, 5])).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 6])).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 7])).toEqual [4, 7]
|
||||
|
||||
describe "when skipAtomicTokens is true", ->
|
||||
it "wraps the screen positions in the middle of fold placeholders to the end of the placeholder", ->
|
||||
folder.createFold(new Range([3, 55], [3, 59]))
|
||||
expect(wrapper.clipScreenPosition([4, 4], skipAtomicTokens: true)).toEqual [4, 4]
|
||||
expect(wrapper.clipScreenPosition([4, 5], skipAtomicTokens: true)).toEqual [4, 7]
|
||||
expect(wrapper.clipScreenPosition([4, 6], skipAtomicTokens: true)).toEqual [4, 7]
|
||||
|
||||
describe "interactions with folding", ->
|
||||
it "behaves properly in the presence of folding", ->
|
||||
folder.createFold(new Range([2, 14], [2, 20]))
|
||||
expect(wrapper.lineForScreenRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
Point = require 'point'
|
||||
Range = require 'range'
|
||||
LineMap = require 'line-map'
|
||||
ScreenLineFragment = require 'screen-line-fragment'
|
||||
EventEmitter = require 'event-emitter'
|
||||
|
||||
module.exports =
|
||||
class LineFolder
|
||||
activeFolds: null
|
||||
foldsById: null
|
||||
lineMap: null
|
||||
lastHighlighterChangeEvent: null
|
||||
|
||||
constructor: (@highlighter) ->
|
||||
@activeFolds = {}
|
||||
@foldsById = {}
|
||||
@buildLineMap()
|
||||
@highlighter.buffer.on 'change', (e) => @handleBufferChange(e)
|
||||
@highlighter.on 'change', (e) => @lastHighlighterChangeEvent = e
|
||||
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
@lineMap.insertAtInputRow(0, @highlighter.screenLines)
|
||||
|
||||
logLines: (start=0, end=@lastRow())->
|
||||
@lineMap.logLines(start, end)
|
||||
|
||||
createFold: (bufferRange) ->
|
||||
return if bufferRange.isEmpty()
|
||||
fold = new Fold(this, bufferRange)
|
||||
@registerFold(bufferRange.start.row, fold)
|
||||
oldScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
|
||||
|
||||
lineWithFold = @buildLineForBufferRow(bufferRange.start.row)
|
||||
@lineMap.replaceOutputRows(oldScreenRange.start.row, oldScreenRange.end.row, lineWithFold)
|
||||
|
||||
newScreenRange = oldScreenRange.copy()
|
||||
newScreenRange.end = _.clone(newScreenRange.start)
|
||||
for fragment in lineWithFold
|
||||
newScreenRange.end.column += fragment.text.length
|
||||
|
||||
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange
|
||||
@trigger 'fold', bufferRange
|
||||
fold
|
||||
|
||||
destroyFold: (fold) ->
|
||||
bufferRange = fold.getRange()
|
||||
@unregisterFold(bufferRange.start.row, fold)
|
||||
startScreenRow = @screenRowForBufferRow(bufferRange.start.row)
|
||||
|
||||
oldScreenRange = new Range()
|
||||
oldScreenRange.start.row = startScreenRow
|
||||
oldScreenRange.end.row = startScreenRow
|
||||
oldScreenRange.end.column = @lineMap.lineForOutputRow(startScreenRow).text.length
|
||||
|
||||
@lineMap.replaceOutputRow(startScreenRow, @buildLinesForBufferRows(bufferRange.start.row, bufferRange.end.row))
|
||||
|
||||
newScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
|
||||
|
||||
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange
|
||||
@trigger 'unfold', fold.getRange()
|
||||
|
||||
registerFold: (bufferRow, fold) ->
|
||||
@activeFolds[bufferRow] ?= []
|
||||
@activeFolds[bufferRow].push(fold)
|
||||
@foldsById[fold.id] = fold
|
||||
|
||||
unregisterFold: (bufferRow, fold) ->
|
||||
folds = @activeFolds[bufferRow]
|
||||
folds.splice(folds.indexOf(fold), 1)
|
||||
delete @foldsById[fold.id]
|
||||
|
||||
handleBufferChange: (e) ->
|
||||
for row, folds of @activeFolds
|
||||
fold.handleBufferChange(e) for fold in folds
|
||||
@handleHighlighterChange(@lastHighlighterChangeEvent)
|
||||
|
||||
handleHighlighterChange: (e) ->
|
||||
oldScreenRange = @screenRangeForBufferRange(e.oldRange)
|
||||
expandedOldScreenRange = @expandScreenRangeToLineEnds(oldScreenRange)
|
||||
lines = @buildLinesForBufferRows(e.newRange.start.row, e.newRange.end.row)
|
||||
@lineMap.replaceOutputRows(oldScreenRange.start.row, oldScreenRange.end.row, lines)
|
||||
newScreenRange = @screenRangeForBufferRange(e.newRange)
|
||||
expandedNewScreenRange = @expandScreenRangeToLineEnds(newScreenRange)
|
||||
|
||||
unless oldScreenRange.isEmpty() and newScreenRange.isEmpty()
|
||||
@trigger 'change', oldRange: expandedOldScreenRange, newRange: expandedNewScreenRange
|
||||
|
||||
buildLineForBufferRow: (bufferRow) ->
|
||||
@buildLinesForBufferRows(bufferRow, bufferRow)
|
||||
|
||||
buildLinesForBufferRows: (startRow, endRow) ->
|
||||
@$buildLinesForBufferRows(@foldStartRowForBufferRow(startRow), endRow)
|
||||
|
||||
$buildLinesForBufferRows: (startRow, endRow, startColumn) ->
|
||||
return [] if startRow > endRow and not startColumn?
|
||||
startColumn ?= 0
|
||||
|
||||
screenLine = @highlighter.lineForScreenRow(startRow).splitAt(startColumn)[1]
|
||||
|
||||
for fold in @foldsForBufferRow(startRow)
|
||||
{ start, end } = fold.getRange()
|
||||
if start.column >= startColumn
|
||||
prefix = screenLine.splitAt(start.column - startColumn)[0]
|
||||
suffix = @$buildLinesForBufferRows(end.row, endRow, end.column)
|
||||
return _.compact(_.flatten([prefix, @buildFoldPlaceholder(fold), suffix]))
|
||||
|
||||
[screenLine].concat(@$buildLinesForBufferRows(startRow + 1, endRow))
|
||||
|
||||
foldStartRowForBufferRow: (bufferRow) ->
|
||||
@bufferRowForScreenRow(@screenRowForBufferRow(bufferRow))
|
||||
|
||||
buildFoldPlaceholder: (fold) ->
|
||||
token = {value: '...', type: 'fold-placeholder', fold, isAtomic: true}
|
||||
new ScreenLineFragment([token], '...', [0, 3], fold.getRange().toDelta(), isAtomic: true)
|
||||
|
||||
foldsForBufferRow: (bufferRow) ->
|
||||
folds = @activeFolds[bufferRow] or []
|
||||
folds.sort (a, b) -> a.compare(b)
|
||||
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@lineMap.linesForOutputRows(startRow, endRow)
|
||||
|
||||
lineForScreenRow: (screenRow) ->
|
||||
@lineMap.lineForOutputRow(screenRow)
|
||||
|
||||
getLines: ->
|
||||
@lineMap.linesForOutputRows(0, @lastRow())
|
||||
|
||||
lineCount: ->
|
||||
@lineMap.outputLineCount()
|
||||
|
||||
lastRow: ->
|
||||
@lineCount() - 1
|
||||
|
||||
screenRowForBufferRow: (bufferRow) ->
|
||||
@screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
|
||||
bufferRowForScreenRow: (screenRow) ->
|
||||
@bufferPositionForScreenPosition([screenRow, 0]).row
|
||||
|
||||
screenPositionForBufferPosition: (bufferPosition) ->
|
||||
@lineMap.outputPositionForInputPosition(bufferPosition)
|
||||
|
||||
bufferPositionForScreenPosition: (screenPosition) ->
|
||||
@lineMap.inputPositionForOutputPosition(screenPosition)
|
||||
|
||||
clipScreenPosition: (screenPosition, options={}) ->
|
||||
@lineMap.clipOutputPosition(screenPosition, options)
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.outputRangeForInputRange(bufferRange)
|
||||
|
||||
bufferRangeForScreenRange: (screenRange) ->
|
||||
@lineMap.inputRangeForOutputRange(screenRange)
|
||||
|
||||
expandScreenRangeToLineEnds: (screenRange) ->
|
||||
{ start, end } = screenRange
|
||||
new Range([start.row, 0], [end.row, @lineMap.lineForOutputRow(end.row).text.length])
|
||||
|
||||
_.extend LineFolder.prototype, EventEmitter
|
||||
|
||||
class Fold
|
||||
@idCounter: 1
|
||||
|
||||
constructor: (@lineFolder, {@start, @end}) ->
|
||||
@id = @constructor.idCounter++
|
||||
|
||||
destroy: ->
|
||||
@lineFolder.destroyFold(this)
|
||||
|
||||
getRange: ->
|
||||
new Range(@start, @end)
|
||||
|
||||
handleBufferChange: (event) ->
|
||||
oldStartRow = @start.row
|
||||
|
||||
{ oldRange } = event
|
||||
if oldRange.start.isLessThanOrEqual(@start) and oldRange.end.isGreaterThanOrEqual(@end)
|
||||
@lineFolder.unregisterFold(oldStartRow, this)
|
||||
return
|
||||
|
||||
@start = @updateAnchorPoint(@start, event)
|
||||
@end = @updateAnchorPoint(@end, event, false)
|
||||
|
||||
if @start.row != oldStartRow
|
||||
@lineFolder.unregisterFold(oldStartRow, this)
|
||||
@lineFolder.registerFold(@start.row, this)
|
||||
|
||||
updateAnchorPoint: (point, event, inclusive=true) ->
|
||||
{ newRange, oldRange } = event
|
||||
if inclusive
|
||||
return point if oldRange.end.isGreaterThan(point)
|
||||
else
|
||||
return point if oldRange.end.isGreaterThanOrEqual(point)
|
||||
|
||||
newRange.end.add(point.subtract(oldRange.end))
|
||||
|
||||
compare: (other) ->
|
||||
startComparison = @start.compare(other.start)
|
||||
if startComparison == 0
|
||||
other.end.compare(@end)
|
||||
else
|
||||
startComparison
|
||||
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
EventEmitter = require 'event-emitter'
|
||||
LineMap = require 'line-map'
|
||||
Point = require 'point'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class LineWrapper
|
||||
constructor: (@maxLength, @lineFolder) ->
|
||||
@buildLineMap()
|
||||
@lineFolder.on 'change', (e) => @handleChange(e)
|
||||
|
||||
setMaxLength: (@maxLength) ->
|
||||
oldRange = @rangeForAllLines()
|
||||
@buildLineMap()
|
||||
newRange = @rangeForAllLines()
|
||||
@trigger 'change', { oldRange, newRange }
|
||||
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
@lineMap.insertAtInputRow 0, @buildScreenLinesForBufferRows(0, @lineFolder.lastRow())
|
||||
|
||||
handleChange: (e) ->
|
||||
oldBufferRange = e.oldRange
|
||||
newBufferRange = e.newRange
|
||||
|
||||
oldScreenRange = @lineMap.outputRangeForInputRange(@expandBufferRangeToLineEnds(oldBufferRange))
|
||||
newScreenLines = @buildScreenLinesForBufferRows(newBufferRange.start.row, newBufferRange.end.row)
|
||||
@lineMap.replaceInputRows oldBufferRange.start.row, oldBufferRange.end.row, newScreenLines
|
||||
newScreenRange = @lineMap.outputRangeForInputRange(@expandBufferRangeToLineEnds(newBufferRange))
|
||||
|
||||
@trigger 'change', { oldRange: oldScreenRange, newRange: newScreenRange }
|
||||
|
||||
expandBufferRangeToLineEnds: (bufferRange) ->
|
||||
{ start, end } = bufferRange
|
||||
new Range([start.row, 0], [end.row, @lineMap.lineForInputRow(end.row).text.length])
|
||||
|
||||
rangeForAllLines: ->
|
||||
endRow = @lineCount() - 1
|
||||
endColumn = @lineMap.lineForOutputRow(endRow).text.length
|
||||
new Range([0, 0], [endRow, endColumn])
|
||||
|
||||
buildScreenLinesForBufferRows: (start, end) ->
|
||||
_(@lineFolder
|
||||
.linesForScreenRows(start, end)
|
||||
.map((screenLine) => @wrapScreenLine(screenLine))).flatten()
|
||||
|
||||
wrapScreenLine: (screenLine, startColumn=0) ->
|
||||
screenLines = []
|
||||
splitColumn = @findSplitColumn(screenLine.text)
|
||||
|
||||
if splitColumn == 0 or splitColumn == screenLine.text.length
|
||||
screenLines.push screenLine
|
||||
endColumn = startColumn + screenLine.text.length
|
||||
else
|
||||
[leftHalf, rightHalf] = screenLine.splitAt(splitColumn)
|
||||
leftHalf.outputDelta = new Point(1, 0)
|
||||
screenLines.push leftHalf
|
||||
endColumn = startColumn + leftHalf.text.length
|
||||
screenLines.push @wrapScreenLine(rightHalf, endColumn)...
|
||||
|
||||
_.extend(screenLines[0], {startColumn, endColumn})
|
||||
screenLines
|
||||
|
||||
findSplitColumn: (line) ->
|
||||
return line.length unless line.length > @maxLength
|
||||
|
||||
if /\s/.test(line[@maxLength])
|
||||
# search forward for the start of a word past the boundary
|
||||
for column in [@maxLength..line.length]
|
||||
return column if /\S/.test(line[column])
|
||||
return line.length
|
||||
else
|
||||
# search backward for the start of the word on the boundary
|
||||
for column in [@maxLength..0]
|
||||
return column + 1 if /\s/.test(line[column])
|
||||
return @maxLength
|
||||
|
||||
screenPositionForBufferPosition: (bufferPosition) ->
|
||||
@lineMap.outputPositionForInputPosition(
|
||||
@lineFolder.screenPositionForBufferPosition(bufferPosition))
|
||||
|
||||
bufferPositionForScreenPosition: (screenPosition) ->
|
||||
@lineFolder.bufferPositionForScreenPosition(
|
||||
@lineMap.inputPositionForOutputPosition(screenPosition))
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.outputRangeForInputRange(
|
||||
@lineFolder.screenRangeForBufferRange(bufferRange))
|
||||
|
||||
bufferRangeForScreenRange: (screenRange) ->
|
||||
@lineFolder.bufferRangeForScreenRange(
|
||||
@lineMap.inputRangeForOutputRange(screenRange))
|
||||
|
||||
clipScreenPosition: (screenPosition, options={}) ->
|
||||
@lineMap.outputPositionForInputPosition(
|
||||
@lineFolder.clipScreenPosition(
|
||||
@lineMap.inputPositionForOutputPosition(@lineMap.clipOutputPosition(screenPosition, options)),
|
||||
options
|
||||
)
|
||||
)
|
||||
|
||||
lineForScreenRow: (screenRow) ->
|
||||
@linesForScreenRows(screenRow, screenRow)[0]
|
||||
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@lineMap.linesForOutputRows(startRow, endRow)
|
||||
|
||||
getLines: ->
|
||||
@linesForScreenRows(0, @lastRow())
|
||||
|
||||
lineCount: ->
|
||||
@lineMap.outputLineCount()
|
||||
|
||||
lastRow: ->
|
||||
@lineCount() - 1
|
||||
|
||||
logLines: (start=0, end=@lineCount() - 1)->
|
||||
@lineMap.logLines(start, end)
|
||||
|
||||
_.extend(LineWrapper.prototype, EventEmitter)
|
||||
Reference in New Issue
Block a user