WIP: Start handling buffer updates w/ LineFolder

Still a ways to go here, but folds are moved correctly when there are
buffer updates. Many unfinished specs.
This commit is contained in:
Nathan Sobo
2012-02-23 16:12:22 -07:00
parent 43c66a02a4
commit 16a2fd0bb3
4 changed files with 115 additions and 11 deletions

View File

@@ -155,6 +155,55 @@ describe "LineFolder", ->
expect(event.oldRange).toEqual [[7, 0], [7, 28]]
expect(event.newRange).toEqual [[7, 0], [8, 56]]
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 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 a fold", ->
it "re-positions the change based on the preceding fold", ->
describe "when the old range is contained to a single line in-between two fold placeholders", ->
describe "when the line is updated", ->
describe "when lines are inserted", ->
describe "when the old range is inside a fold", ->
it "does not trigger a change event, but ensures the change is present when the fold is destroyed", ->
describe "when the old range surrounds a fold", ->
it "removes the fold and replaces the placeholder with the new text", ->
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", ->

View File

@@ -10,15 +10,20 @@ class LineFolder
constructor: (@highlighter) ->
@activeFolds = {}
@buildLineMap()
@highlighter.buffer.on 'change', (e) => @handleBufferChange(e)
@highlighter.on 'change', (e) => @handleHighlighterChange(e)
buildLineMap: ->
@lineMap = new LineMap
@lineMap.insertAtBufferRow(0, @highlighter.screenLines)
logLines: (start=0, end=@lastRow())->
for row in [start..end]
console.log row, @lineForScreenRow(row).text
createFold: (bufferRange) ->
fold = new Fold(this, bufferRange)
@activeFolds[bufferRange.start.row] ?= []
@activeFolds[bufferRange.start.row].push(fold)
@registerFold(bufferRange.start.row, fold)
oldScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
lineWithFold = @buildLine(oldScreenRange.start.row)
@@ -33,11 +38,8 @@ class LineFolder
fold
destroyFold: (fold) ->
bufferRange = fold.range
folds = @activeFolds[bufferRange.start.row]
foldIndex = folds.indexOf(fold)
folds[foldIndex..foldIndex] = []
bufferRange = fold.getRange()
@unregisterFold(bufferRange.start.row, fold)
startScreenRow = @screenRowForBufferRow(bufferRange.start.row)
oldScreenRange = new Range()
@@ -51,6 +53,26 @@ class LineFolder
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange
registerFold: (bufferRow, fold) ->
@activeFolds[bufferRow] ?= []
@activeFolds[bufferRow].push(fold)
unregisterFold: (bufferRow, fold) ->
folds = @activeFolds[bufferRow]
folds.splice(folds.indexOf(fold), 1)
handleBufferChange: (e) ->
for row, folds of @activeFolds
fold.handleBufferChange(e) for fold in folds
handleHighlighterChange: (e) ->
oldScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(e.oldRange))
lines = @buildLinesForBufferRows(e.newRange.start.row, e.newRange.end.row)
@lineMap.replaceScreenRows(e.oldRange.start.row, e.oldRange.end.row, lines)
newScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(e.newRange))
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange
buildLinesForBufferRows: (start, end) ->
lines = [@buildLine(@screenRowForBufferRow(start))]
if end > start
@@ -64,7 +86,7 @@ class LineFolder
buildLineForBufferRow: (bufferRow, startColumn=0) ->
screenLine = @highlighter.lineForScreenRow(bufferRow).splitAt(startColumn)[1]
for fold in @foldsForBufferRow(bufferRow)
{ start, end } = fold.range
{ start, end } = fold.getRange()
if start.column > startColumn
prefix = screenLine.splitAt(start.column - startColumn)[0]
suffix = @buildLineForBufferRow(end.row, end.column)
@@ -72,7 +94,7 @@ class LineFolder
screenLine
buildFoldPlaceholder: (fold) ->
new ScreenLineFragment([{value: '...', type: 'fold-placeholder'}], '...', [0, 3], fold.range.toDelta(), isAtomic: true)
new ScreenLineFragment([{value: '...', type: 'fold-placeholder'}], '...', [0, 3], fold.getRange().toDelta(), isAtomic: true)
foldsForBufferRow: (bufferRow) ->
@activeFolds[bufferRow] or []
@@ -80,6 +102,9 @@ class LineFolder
linesForScreenRows: (startRow, endRow) ->
@lineMap.linesForScreenRows(startRow, endRow)
lineForScreenRow: (screenRow) ->
@lineMap.lineForScreenRow(screenRow)
getLines: ->
@lineMap.getScreenLines()
@@ -111,8 +136,29 @@ class LineFolder
_.extend LineFolder.prototype, EventEmitter
class Fold
constructor: (@lineFolder, @range) ->
constructor: (@lineFolder, {start, end}) ->
@start = new Anchor(start)
@end = new Anchor(end)
destroy: ->
@lineFolder.destroyFold(this)
getRange: ->
new Range(@start.position, @end.position)
handleBufferChange: (event) ->
oldStartRow = @start.position.row
@start.handleBufferChange(event)
@end.handleBufferChange(event)
newStartRow = @start.position.row
if newStartRow != oldStartRow
@lineFolder.unregisterFold(oldStartRow, this)
@lineFolder.registerFold(newStartRow, this)
class Anchor
constructor: (@position) ->
handleBufferChange: (e) ->
@position = e.newRange.end.add(@position.subtract(e.oldRange.end))

View File

@@ -14,7 +14,6 @@ class Point
constructor: (@row=0, @column=0) ->
add: (other) ->
debugger unless other
row = @row + other.row
if other.row == 0
column = @column + other.column
@@ -23,6 +22,15 @@ class Point
new Point(row, column)
subtract: (other) ->
row = @row - other.row
if @row == other.row
column = @column - other.column
else
column = @column
new Point(row, column)
splitAt: (column) ->
if @row == 0
rightColumn = @column - column

View File

@@ -17,6 +17,7 @@ class ScreenLineFragment
leftTokens = []
leftTextLength = 0
while leftTextLength < column
debugger unless rightTokens[0]
if leftTextLength + rightTokens[0].value.length > column
rightTokens[0..0] = @splitTokenAt(rightTokens[0], column - leftTextLength)
nextToken = rightTokens.shift()