diff --git a/spec/atom/line-folder-spec.coffee b/spec/atom/line-folder-spec.coffee index 1e064d644..07ab31345 100644 --- a/spec/atom/line-folder-spec.coffee +++ b/spec/atom/line-folder-spec.coffee @@ -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", -> diff --git a/src/atom/line-folder.coffee b/src/atom/line-folder.coffee index 876aea6b5..6d512aa0b 100644 --- a/src/atom/line-folder.coffee +++ b/src/atom/line-folder.coffee @@ -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)) + diff --git a/src/atom/point.coffee b/src/atom/point.coffee index d18d3affe..ee26459e1 100644 --- a/src/atom/point.coffee +++ b/src/atom/point.coffee @@ -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 diff --git a/src/atom/screen-line-fragment.coffee b/src/atom/screen-line-fragment.coffee index 7ac5d9987..1ce4747e5 100644 --- a/src/atom/screen-line-fragment.coffee +++ b/src/atom/screen-line-fragment.coffee @@ -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()