Refer to "buffer" and "screen" coordinate spaces as "input" and "output"

Since we compose the line wrapper and the line folder together, the
line map is not always translating between screen and buffer coordinate
spaces. It's translating one step in the chain, with output closer to
the screen and input closer to the buffer.
This commit is contained in:
Nathan Sobo
2012-02-29 18:39:45 -07:00
parent 6c8a48b928
commit 32a6a2cd7f
7 changed files with 213 additions and 214 deletions

View File

@@ -1,6 +1,6 @@
LineMap = require 'line-map'
ScreenLineFragment = require 'screen-line-fragment'
Buffer = require 'buffer'
Input = require 'buffer'
Highlighter = require 'highlighter'
Point = require 'point'
@@ -9,190 +9,189 @@ describe "LineMap", ->
[line0, line1, line2, line3, line4] = []
beforeEach ->
buffer = new Buffer(require.resolve 'fixtures/sample.js')
buffer = new Input(require.resolve 'fixtures/sample.js')
highlighter = new Highlighter(buffer)
map = new LineMap
[line0, line1, line2, line3, line4] = highlighter.linesForScreenRows(0, 4)
describe ".insertAtBufferRow(row, screenLine(s))", ->
describe ".insertAtInputRow(row, lineFragments)", ->
it "inserts the given line fragments before the specified buffer row", ->
map.insertAtBufferRow(0, [line2, line3])
map.insertAtBufferRow(0, [line0, line1])
map.insertAtBufferRow(4, [line4])
map.insertAtInputRow(0, [line2, line3])
map.insertAtInputRow(0, [line0, line1])
map.insertAtInputRow(4, [line4])
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line1
expect(map.lineForScreenRow(2)).toEqual line2
expect(map.lineForScreenRow(3)).toEqual line3
expect(map.lineForScreenRow(4)).toEqual line4
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line1
expect(map.lineForOutputRow(2)).toEqual line2
expect(map.lineForOutputRow(3)).toEqual line3
expect(map.lineForOutputRow(4)).toEqual line4
it "allows for partial line fragments on the row following the insertion", ->
[line0a, line0b] = line0.splitAt(10)
map.insertAtBufferRow(0, [line0a, line0b])
map.insertAtBufferRow(0, [line1])
map.insertAtInputRow(0, [line0a, line0b])
map.insertAtInputRow(0, [line1])
expect(map.lineForScreenRow(0)).toEqual line1
expect(map.lineForScreenRow(1)).toEqual line0a.concat(line0b)
expect(map.lineForOutputRow(0)).toEqual line1
expect(map.lineForOutputRow(1)).toEqual line0a.concat(line0b)
describe ".spliceAtBufferRow(bufferRow, rowCount, screenLines)", ->
describe ".spliceAtInputRow(bufferRow, rowCount, lineFragments)", ->
describe "when called with a row count of 0", ->
it "inserts the given line fragments before the specified buffer row", ->
map.insertAtBufferRow(0, [line0, line1])
map.spliceAtBufferRow(1, 0, [line3, line4])
map.insertAtInputRow(0, [line0, line1])
map.spliceAtInputRow(1, 0, [line3, line4])
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.lineForScreenRow(3)).toEqual line1
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3
expect(map.lineForOutputRow(2)).toEqual line4
expect(map.lineForOutputRow(3)).toEqual line1
map.spliceAtBufferRow(0, 0, [line2])
expect(map.lineForScreenRow(0)).toEqual line2
map.spliceAtInputRow(0, 0, [line2])
expect(map.lineForOutputRow(0)).toEqual line2
describe "when called with a row count of 1", ->
describe "when the specified buffer row is spanned by a single line fragment", ->
it "replaces the spanning line fragment with the given line fragments", ->
map.insertAtBufferRow(0, [line0, line1, line2])
map.spliceAtBufferRow(1, 1, [line3, line4])
map.insertAtInputRow(0, [line0, line1, line2])
map.spliceAtInputRow(1, 1, [line3, line4])
expect(map.bufferLineCount()).toBe 4
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.lineForScreenRow(3)).toEqual line2
expect(map.inputLineCount()).toBe 4
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3
expect(map.lineForOutputRow(2)).toEqual line4
expect(map.lineForOutputRow(3)).toEqual line2
describe "when the specified buffer row is spanned by multiple line fragments", ->
it "replaces all spanning line fragments with the given line fragments", ->
[line1a, line1b] = line1.splitAt(10)
[line3a, line3b] = line3.splitAt(10)
map.insertAtBufferRow(0, [line0, line1a, line1b, line2])
map.spliceAtBufferRow(1, 1, [line3a, line3b, line4])
map.insertAtInputRow(0, [line0, line1a, line1b, line2])
map.spliceAtInputRow(1, 1, [line3a, line3b, line4])
expect(map.bufferLineCount()).toBe 4
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3a.concat(line3b)
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.lineForScreenRow(3)).toEqual line2
expect(map.inputLineCount()).toBe 4
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3a.concat(line3b)
expect(map.lineForOutputRow(2)).toEqual line4
expect(map.lineForOutputRow(3)).toEqual line2
describe "when called with a row count greater than 1", ->
it "replaces all line fragments spanning the multiple buffer rows with the given line fragments", ->
[line1a, line1b] = line1.splitAt(10)
[line3a, line3b] = line3.splitAt(10)
map.insertAtBufferRow(0, [line0, line1a, line1b, line2])
map.spliceAtBufferRow(1, 2, [line3a, line3b, line4])
map.insertAtInputRow(0, [line0, line1a, line1b, line2])
map.spliceAtInputRow(1, 2, [line3a, line3b, line4])
expect(map.bufferLineCount()).toBe 3
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3a.concat(line3b)
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.inputLineCount()).toBe 3
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3a.concat(line3b)
expect(map.lineForOutputRow(2)).toEqual line4
describe ".spliceAtScreenRow(startRow, rowCount, lineFragemnts)", ->
describe ".spliceAtOutputRow(startRow, rowCount, lineFragemnts)", ->
describe "when called with a row count of 0", ->
it "inserts the given line fragments before the specified buffer row", ->
map.insertAtBufferRow(0, [line0, line1, line2])
map.spliceAtScreenRow(1, 0, [line3, line4])
map.insertAtInputRow(0, [line0, line1, line2])
map.spliceAtOutputRow(1, 0, [line3, line4])
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.lineForScreenRow(3)).toEqual line1
expect(map.lineForScreenRow(4)).toEqual line2
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3
expect(map.lineForOutputRow(2)).toEqual line4
expect(map.lineForOutputRow(3)).toEqual line1
expect(map.lineForOutputRow(4)).toEqual line2
describe "when called with a row count of 1", ->
describe "when the specified screen row is spanned by a single line fragment", ->
describe "when the specified output row is spanned by a single line fragment", ->
it "replaces the spanning line fragment with the given line fragments", ->
map.insertAtBufferRow(0, [line0, line1, line2])
map.spliceAtScreenRow(1, 1, [line3, line4])
map.insertAtInputRow(0, [line0, line1, line2])
map.spliceAtOutputRow(1, 1, [line3, line4])
expect(map.bufferLineCount()).toBe 4
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.lineForScreenRow(3)).toEqual line2
expect(map.inputLineCount()).toBe 4
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3
expect(map.lineForOutputRow(2)).toEqual line4
expect(map.lineForOutputRow(3)).toEqual line2
describe "when the specified screen row is spanned by multiple line fragments", ->
describe "when the specified output row is spanned by multiple line fragments", ->
it "replaces all spanning line fragments with the given line fragments", ->
[line0a, line0b] = line0.splitAt(10)
[line3a, line3b] = line3.splitAt(10)
map.insertAtBufferRow(0, [line0a, line0b, line1, line2])
map.spliceAtScreenRow(0, 1, [line3a, line3b, line4])
map.insertAtInputRow(0, [line0a, line0b, line1, line2])
map.spliceAtOutputRow(0, 1, [line3a, line3b, line4])
expect(map.bufferLineCount()).toBe 4
expect(map.lineForScreenRow(0)).toEqual line3a.concat(line3b)
expect(map.lineForScreenRow(1)).toEqual line4
expect(map.lineForScreenRow(2)).toEqual line1
expect(map.lineForScreenRow(3)).toEqual line2
expect(map.inputLineCount()).toBe 4
expect(map.lineForOutputRow(0)).toEqual line3a.concat(line3b)
expect(map.lineForOutputRow(1)).toEqual line4
expect(map.lineForOutputRow(2)).toEqual line1
expect(map.lineForOutputRow(3)).toEqual line2
describe "when called with a row count greater than 1", ->
it "replaces all line fragments spanning the multiple buffer rows with the given line fragments", ->
[line1a, line1b] = line1.splitAt(10)
[line3a, line3b] = line3.splitAt(10)
map.insertAtBufferRow(0, [line0, line1a, line1b, line2])
map.spliceAtScreenRow(1, 2, [line3a, line3b, line4])
map.insertAtInputRow(0, [line0, line1a, line1b, line2])
map.spliceAtOutputRow(1, 2, [line3a, line3b, line4])
expect(map.bufferLineCount()).toBe 3
expect(map.lineForScreenRow(0)).toEqual line0
expect(map.lineForScreenRow(1)).toEqual line3a.concat(line3b)
expect(map.lineForScreenRow(2)).toEqual line4
expect(map.inputLineCount()).toBe 3
expect(map.lineForOutputRow(0)).toEqual line0
expect(map.lineForOutputRow(1)).toEqual line3a.concat(line3b)
expect(map.lineForOutputRow(2)).toEqual line4
describe ".linesForScreenRows(startRow, endRow)", ->
it "returns lines for the given row range, concatenating fragments that belong on a single screen line", ->
describe ".linesForOutputRows(startRow, endRow)", ->
it "returns lines for the given row range, concatenating fragments that belong on a single output line", ->
[line1a, line1b] = line1.splitAt(11)
[line3a, line3b] = line3.splitAt(16)
map.insertAtBufferRow(0, [line0, line1a, line1b, line2, line3a, line3b, line4])
expect(map.linesForScreenRows(1, 3)).toEqual [line1, line2, line3]
map.insertAtInputRow(0, [line0, line1a, line1b, line2, line3a, line3b, line4])
expect(map.linesForOutputRows(1, 3)).toEqual [line1, line2, line3]
# repeating assertion to cover a regression where this method mutated lines
expect(map.linesForScreenRows(1, 3)).toEqual [line1, line2, line3]
expect(map.linesForOutputRows(1, 3)).toEqual [line1, line2, line3]
describe ".lineForBufferRow(bufferRow)", ->
it "returns the concatenated screen line fragments that comprise the given buffer row", ->
describe ".lineForInputRow(bufferRow)", ->
it "returns the concatenated output line fragments that comprise the given buffer row", ->
line1Text = line1.text
[line1a, line1b] = line1.splitAt(11)
line1a.screenDelta = new Point(1, 0)
line1a.outputDelta = new Point(1, 0)
map.insertAtBufferRow(0, [line0, line1a, line1b, line2])
map.insertAtInputRow(0, [line0, line1a, line1b, line2])
expect(map.lineForBufferRow(0).text).toBe line0.text
expect(map.lineForBufferRow(1).text).toBe line1Text
expect(map.lineForInputRow(0).text).toBe line0.text
expect(map.lineForInputRow(1).text).toBe line1Text
describe ".screenPositionForBufferPosition(bufferPosition)", ->
describe ".outputPositionForInputPosition(bufferPosition)", ->
beforeEach ->
# line1a-line3b describes a fold
[line1a, line1b] = line1.splitAt(10)
[line3a, line3b] = line3.splitAt(20)
line1a.bufferDelta.row = 2
line1a.bufferDelta.column = 20
line1a.inputDelta.row = 2
line1a.inputDelta.column = 20
# line4a-line4b describes a wrapped line
[line4a, line4b] = line4.splitAt(20)
line4a.screenDelta = new Point(1, 0)
line4a.outputDelta = new Point(1, 0)
map.insertAtBufferRow(0, [line0, line1a, line3b, line4a, line4b])
map.insertAtInputRow(0, [line0, line1a, line3b, line4a, line4b])
it "translates the given buffer position based on buffer and screen deltas of the line fragments in the map", ->
expect(map.screenPositionForBufferPosition([0, 0])).toEqual [0, 0]
expect(map.screenPositionForBufferPosition([0, 5])).toEqual [0, 5]
expect(map.screenPositionForBufferPosition([1, 5])).toEqual [1, 5]
expect(map.screenPositionForBufferPosition([3, 20])).toEqual [1, 10]
expect(map.screenPositionForBufferPosition([3, 30])).toEqual [1, 20]
expect(map.screenPositionForBufferPosition([4, 5])).toEqual [2, 5]
it "translates the given buffer position based on buffer and output deltas of the line fragments in the map", ->
expect(map.outputPositionForInputPosition([0, 0])).toEqual [0, 0]
expect(map.outputPositionForInputPosition([0, 5])).toEqual [0, 5]
expect(map.outputPositionForInputPosition([1, 5])).toEqual [1, 5]
expect(map.outputPositionForInputPosition([3, 20])).toEqual [1, 10]
expect(map.outputPositionForInputPosition([3, 30])).toEqual [1, 20]
expect(map.outputPositionForInputPosition([4, 5])).toEqual [2, 5]
it "wraps buffer positions at the end of a screen line to the end end of the next screen line", ->
expect(map.screenPositionForBufferPosition([4, 20])).toEqual [3, 0]
it "wraps buffer positions at the end of a output line to the end end of the next output line", ->
expect(map.outputPositionForInputPosition([4, 20])).toEqual [3, 0]
describe ".screenLineCount()", ->
it "returns the total of all inserted screen row deltas", ->
describe ".outputLineCount()", ->
it "returns the total of all inserted output row deltas", ->
[line1a, line1b] = line1.splitAt(10)
[line3a, line3b] = line3.splitAt(10)
line1a.screenDelta = new Point(1, 0)
line3a.screenDelta = new Point(1, 0)
line1a.outputDelta = new Point(1, 0)
line3a.outputDelta = new Point(1, 0)
map.insertAtBufferRow(0, [line0, line1a, line1b, line2])
expect(map.screenLineCount()).toBe 4
map.insertAtInputRow(0, [line0, line1a, line1b, line2])
expect(map.outputLineCount()).toBe 4

View File

@@ -167,8 +167,8 @@ describe "LineWrapper", ->
expect(line1.startColumn).toBe 0
expect(line1.endColumn).toBe 6
expect(line1.text.length).toBe 6
expect(line1.screenDelta).toEqual [1, 0]
expect(line1.bufferDelta).toEqual [1, 0]
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", ->
@@ -176,8 +176,8 @@ describe "LineWrapper", ->
expect(screenLines.length).toBe 1
[screenLine] = screenLines
expect(screenLine.tokens).toEqual []
expect(screenLine.screenDelta).toEqual [1, 0]
expect(screenLine.bufferDelta).toEqual [1, 0]
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", ->
@@ -191,14 +191,14 @@ describe "LineWrapper", ->
expect(line1.startColumn).toBe 0
expect(line1.endColumn).toBe 7
expect(line1.text.length).toBe 7
expect(line1.screenDelta).toEqual [1, 0]
expect(line1.bufferDelta).toEqual [0, 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.screenDelta).toEqual [1, 0]
expect(line2.bufferDelta).toEqual [1, 0]
expect(line2.outputDelta).toEqual [1, 0]
expect(line2.inputDelta).toEqual [1, 0]
describe "when there is no whitespace before the max-length boundary", ->
it "splits the line at the boundary, because there's no 'good' place to split it", ->

View File

@@ -31,18 +31,18 @@ describe "screenLineFragment", ->
it "ensures the returned fragments cover the span of the original line", ->
[left, right] = screenLine.splitAt(15)
expect(left.bufferDelta).toEqual [0, 15]
expect(left.screenDelta).toEqual [0, 15]
expect(left.inputDelta).toEqual [0, 15]
expect(left.outputDelta).toEqual [0, 15]
expect(right.bufferDelta).toEqual [1, 0]
expect(right.screenDelta).toEqual [1, 0]
expect(right.inputDelta).toEqual [1, 0]
expect(right.outputDelta).toEqual [1, 0]
[left2, right2] = left.splitAt(5)
expect(left2.bufferDelta).toEqual [0, 5]
expect(left2.screenDelta).toEqual [0, 5]
expect(left2.inputDelta).toEqual [0, 5]
expect(left2.outputDelta).toEqual [0, 5]
expect(right2.bufferDelta).toEqual [0, 10]
expect(right2.screenDelta).toEqual [0, 10]
expect(right2.inputDelta).toEqual [0, 10]
expect(right2.outputDelta).toEqual [0, 10]
describe "if splitting at 0", ->
it "returns undefined for the left half", ->
@@ -53,12 +53,12 @@ describe "screenLineFragment", ->
[left, right] = screenLine.splitAt(screenLine.text.length)
expect(left.text).toBe screenLine.text
expect(left.screenDelta).toEqual [0, screenLine.text.length]
expect(left.bufferDelta).toEqual [0, screenLine.text.length]
expect(left.outputDelta).toEqual [0, screenLine.text.length]
expect(left.inputDelta).toEqual [0, screenLine.text.length]
expect(right.text).toBe ''
expect(right.screenDelta).toEqual [1, 0]
expect(right.bufferDelta).toEqual [1, 0]
expect(right.outputDelta).toEqual [1, 0]
expect(right.inputDelta).toEqual [1, 0]
describe ".concat(otherFragment)", ->
it "returns the concatenation of the receiver and the given fragment", ->
@@ -68,8 +68,8 @@ describe "screenLineFragment", ->
concatenated = screenLine.concat(highlighter.lineForScreenRow(4))
expect(concatenated.text).toBe ' var pivot = items.shift(), current, left = [], right = []; while(items.length > 0) {'
expect(tokensText concatenated.tokens).toBe concatenated.text
expect(concatenated.screenDelta).toEqual [2, 0]
expect(concatenated.bufferDelta).toEqual [2, 0]
expect(concatenated.outputDelta).toEqual [2, 0]
expect(concatenated.inputDelta).toEqual [2, 0]

View File

@@ -21,7 +21,7 @@ class LineFolder
buildLineMap: ->
@lineMap = new LineMap
@lineMap.insertAtBufferRow(0, @highlighter.screenLines)
@lineMap.insertAtInputRow(0, @highlighter.screenLines)
logLines: (start=0, end=@lastRow())->
@lineMap.logLines(start, end)
@@ -32,7 +32,7 @@ class LineFolder
oldScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
lineWithFold = @buildLineForBufferRow(bufferRange.start.row)
@lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lineWithFold)
@lineMap.replaceOutputRows(oldScreenRange.start.row, oldScreenRange.end.row, lineWithFold)
newScreenRange = oldScreenRange.copy()
newScreenRange.end = _.clone(newScreenRange.start)
@@ -51,9 +51,9 @@ class LineFolder
oldScreenRange = new Range()
oldScreenRange.start.row = startScreenRow
oldScreenRange.end.row = startScreenRow
oldScreenRange.end.column = @lineMap.lineForScreenRow(startScreenRow).text.length
oldScreenRange.end.column = @lineMap.lineForOutputRow(startScreenRow).text.length
@lineMap.replaceScreenRow(startScreenRow, @buildLinesForBufferRows(bufferRange.start.row, bufferRange.end.row))
@lineMap.replaceOutputRow(startScreenRow, @buildLinesForBufferRows(bufferRange.start.row, bufferRange.end.row))
newScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
@@ -79,7 +79,7 @@ class LineFolder
oldScreenRange = @screenRangeForBufferRange(e.oldRange)
expandedOldScreenRange = @expandScreenRangeToLineEnds(oldScreenRange)
lines = @buildLinesForBufferRows(e.newRange.start.row, e.newRange.end.row)
@lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines)
@lineMap.replaceOutputRows(oldScreenRange.start.row, oldScreenRange.end.row, lines)
newScreenRange = @screenRangeForBufferRange(e.newRange)
expandedNewScreenRange = @expandScreenRangeToLineEnds(newScreenRange)
@@ -118,16 +118,16 @@ class LineFolder
folds.sort (a, b) -> a.compare(b)
linesForScreenRows: (startRow, endRow) ->
@lineMap.linesForScreenRows(startRow, endRow)
@lineMap.linesForOutputRows(startRow, endRow)
lineForScreenRow: (screenRow) ->
@lineMap.lineForScreenRow(screenRow)
@lineMap.lineForOutputRow(screenRow)
getLines: ->
@lineMap.screenLinesForRows(0, @lastRow())
@lineMap.linesForOutputRows(0, @lastRow())
lineCount: ->
@lineMap.screenLineCount()
@lineMap.outputLineCount()
lastRow: ->
@lineCount() - 1
@@ -139,23 +139,23 @@ class LineFolder
@bufferPositionForScreenPosition([screenRow, 0]).row
screenPositionForBufferPosition: (bufferPosition) ->
@lineMap.screenPositionForBufferPosition(bufferPosition)
@lineMap.outputPositionForInputPosition(bufferPosition)
bufferPositionForScreenPosition: (screenPosition) ->
@lineMap.bufferPositionForScreenPosition(screenPosition)
@lineMap.inputPositionForOutputPosition(screenPosition)
clipScreenPosition: (screenPosition, options={}) ->
@lineMap.clipScreenPosition(screenPosition, options)
@lineMap.clipOutputPosition(screenPosition, options)
screenRangeForBufferRange: (bufferRange) ->
@lineMap.screenRangeForBufferRange(bufferRange)
@lineMap.outputRangeForInputRange(bufferRange)
bufferRangeForScreenRange: (screenRange) ->
@lineMap.bufferRangeForScreenRange(screenRange)
@lineMap.inputRangeForOutputRange(screenRange)
expandScreenRangeToLineEnds: (screenRange) ->
{ start, end } = screenRange
new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length])
new Range([start.row, 0], [end.row, @lineMap.lineForOutputRow(end.row).text.length])
_.extend LineFolder.prototype, EventEmitter

View File

@@ -7,66 +7,66 @@ class LineMap
constructor: ->
@lineFragments = []
insertAtBufferRow: (bufferRow, lineFragments) ->
@spliceAtBufferRow(bufferRow, 0, lineFragments)
insertAtInputRow: (inputRow, lineFragments) ->
@spliceAtInputRow(inputRow, 0, lineFragments)
spliceAtBufferRow: (startRow, rowCount, lineFragments) ->
@spliceByDelta('bufferDelta', startRow, rowCount, lineFragments)
spliceAtInputRow: (startRow, rowCount, lineFragments) ->
@spliceByDelta('inputDelta', startRow, rowCount, lineFragments)
spliceAtScreenRow: (startRow, rowCount, lineFragments) ->
@spliceByDelta('screenDelta', startRow, rowCount, lineFragments)
spliceAtOutputRow: (startRow, rowCount, lineFragments) ->
@spliceByDelta('outputDelta', startRow, rowCount, lineFragments)
replaceBufferRows: (start, end, lineFragments) ->
@spliceAtBufferRow(start, end - start + 1, lineFragments)
replaceInputRows: (start, end, lineFragments) ->
@spliceAtInputRow(start, end - start + 1, lineFragments)
replaceScreenRow: (row, lineFragments) ->
@replaceScreenRows(row, row, lineFragments)
replaceOutputRow: (row, lineFragments) ->
@replaceOutputRows(row, row, lineFragments)
replaceScreenRows: (start, end, lineFragments) ->
@spliceAtScreenRow(start, end - start + 1, lineFragments)
replaceOutputRows: (start, end, lineFragments) ->
@spliceAtOutputRow(start, end - start + 1, lineFragments)
lineForScreenRow: (row) ->
@linesForScreenRows(row, row)[0]
lineForOutputRow: (row) ->
@linesForOutputRows(row, row)[0]
linesForScreenRows: (startRow, endRow) ->
@linesByDelta('screenDelta', startRow, endRow)
linesForOutputRows: (startRow, endRow) ->
@linesByDelta('outputDelta', startRow, endRow)
lineForBufferRow: (row) ->
@linesForBufferRows(row, row)[0]
lineForInputRow: (row) ->
@linesForInputRows(row, row)[0]
linesForBufferRows: (startRow, endRow) ->
@linesByDelta('bufferDelta', startRow, endRow)
linesForInputRows: (startRow, endRow) ->
@linesByDelta('inputDelta', startRow, endRow)
bufferLineCount: ->
@lineCountByDelta('bufferDelta')
inputLineCount: ->
@lineCountByDelta('inputDelta')
screenLineCount: ->
@lineCountByDelta('screenDelta')
outputLineCount: ->
@lineCountByDelta('outputDelta')
lineCountByDelta: (deltaType) ->
@traverseByDelta(deltaType, new Point(Infinity, 0))[deltaType].row
lastScreenRow: ->
@screenLineCount() - 1
lastOutputRow: ->
@outputLineCount() - 1
screenPositionForBufferPosition: (bufferPosition) ->
@translatePosition('bufferDelta', 'screenDelta', bufferPosition)
outputPositionForInputPosition: (inputPosition) ->
@translatePosition('inputDelta', 'outputDelta', inputPosition)
bufferPositionForScreenPosition: (screenPosition) ->
@translatePosition('screenDelta', 'bufferDelta', screenPosition)
inputPositionForOutputPosition: (outputPosition) ->
@translatePosition('outputDelta', 'inputDelta', outputPosition)
screenRangeForBufferRange: (bufferRange) ->
start = @screenPositionForBufferPosition(bufferRange.start)
end = @screenPositionForBufferPosition(bufferRange.end)
outputRangeForInputRange: (inputRange) ->
start = @outputPositionForInputPosition(inputRange.start)
end = @outputPositionForInputPosition(inputRange.end)
new Range(start, end)
bufferRangeForScreenRange: (screenRange) ->
start = @bufferPositionForScreenPosition(screenRange.start)
end = @bufferPositionForScreenPosition(screenRange.end)
inputRangeForOutputRange: (outputRange) ->
start = @inputPositionForOutputPosition(outputRange.start)
end = @inputPositionForOutputPosition(outputRange.end)
new Range(start, end)
clipScreenPosition: (screenPosition, options) ->
@translatePosition('screenDelta', 'screenDelta', screenPosition, options)
clipOutputPosition: (outputPosition, options) ->
@translatePosition('outputDelta', 'outputDelta', outputPosition, options)
spliceByDelta: (deltaType, startRow, rowCount, lineFragments) ->
stopRow = startRow + rowCount
@@ -93,7 +93,7 @@ class LineMap
else
pendingFragment = _.clone(lineFragment)
if pendingFragment[deltaType].row > 0
pendingFragment.bufferDelta = new Point(1, 0)
pendingFragment.inputDelta = new Point(1, 0)
lines.push pendingFragment
pendingFragment = null
lines
@@ -145,20 +145,20 @@ class LineMap
traverseByDelta: (deltaType, startPosition, endPosition=startPosition, iterator=null) ->
traversalDelta = new Point
screenDelta = new Point
bufferDelta = new Point
outputDelta = new Point
inputDelta = new Point
for lineFragment in @lineFragments
iterator(lineFragment) if traversalDelta.isGreaterThanOrEqual(startPosition) and iterator?
traversalDelta = traversalDelta.add(lineFragment[deltaType])
break if traversalDelta.isGreaterThan(endPosition)
screenDelta = screenDelta.add(lineFragment.screenDelta)
bufferDelta = bufferDelta.add(lineFragment.bufferDelta)
outputDelta = outputDelta.add(lineFragment.outputDelta)
inputDelta = inputDelta.add(lineFragment.inputDelta)
{ screenDelta, bufferDelta, lastLineFragment: lineFragment }
{ outputDelta, inputDelta, lastLineFragment: lineFragment }
logLines: (start=0, end=@screenLineCount() - 1)->
logLines: (start=0, end=@outputLineCount() - 1)->
for row in [start..end]
line = @lineForScreenRow(row).text
line = @lineForOutputRow(row).text
console.log row, line, line.length

View File

@@ -18,26 +18,26 @@ class LineWrapper
buildLineMap: ->
@lineMap = new LineMap
@lineMap.insertAtBufferRow 0, @buildScreenLinesForBufferRows(0, @lineFolder.lastRow())
@lineMap.insertAtInputRow 0, @buildScreenLinesForBufferRows(0, @lineFolder.lastRow())
handleChange: (e) ->
oldBufferRange = e.oldRange
newBufferRange = e.newRange
oldScreenRange = @lineMap.screenRangeForBufferRange(@expandBufferRangeToLineEnds(oldBufferRange))
oldScreenRange = @lineMap.outputRangeForInputRange(@expandBufferRangeToLineEnds(oldBufferRange))
newScreenLines = @buildScreenLinesForBufferRows(newBufferRange.start.row, newBufferRange.end.row)
@lineMap.replaceBufferRows oldBufferRange.start.row, oldBufferRange.end.row, newScreenLines
newScreenRange = @lineMap.screenRangeForBufferRange(@expandBufferRangeToLineEnds(newBufferRange))
@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.lineForBufferRow(end.row).text.length])
new Range([start.row, 0], [end.row, @lineMap.lineForInputRow(end.row).text.length])
rangeForAllLines: ->
endRow = @lineCount() - 1
endColumn = @lineMap.lineForScreenRow(endRow).text.length
endColumn = @lineMap.lineForOutputRow(endRow).text.length
new Range([0, 0], [endRow, endColumn])
buildScreenLinesForBufferRows: (start, end) ->
@@ -54,7 +54,7 @@ class LineWrapper
endColumn = startColumn + screenLine.text.length
else
[leftHalf, rightHalf] = screenLine.splitAt(splitColumn)
leftHalf.screenDelta = new Point(1, 0)
leftHalf.outputDelta = new Point(1, 0)
screenLines.push leftHalf
endColumn = startColumn + leftHalf.text.length
screenLines.push @wrapScreenLine(rightHalf, endColumn)...
@@ -77,25 +77,25 @@ class LineWrapper
return @maxLength
screenPositionForBufferPosition: (bufferPosition) ->
@lineMap.screenPositionForBufferPosition(
@lineMap.outputPositionForInputPosition(
@lineFolder.screenPositionForBufferPosition(bufferPosition))
bufferPositionForScreenPosition: (screenPosition) ->
@lineFolder.bufferPositionForScreenPosition(
@lineMap.bufferPositionForScreenPosition(screenPosition))
@lineMap.inputPositionForOutputPosition(screenPosition))
screenRangeForBufferRange: (bufferRange) ->
@lineMap.screenRangeForBufferRange(
@lineMap.outputRangeForInputRange(
@lineFolder.screenRangeForBufferRange(bufferRange))
bufferRangeForScreenRange: (screenRange) ->
@lineFolder.bufferRangeForScreenRange(
@lineMap.bufferRangeForScreenRange(screenRange))
@lineMap.inputRangeForOutputRange(screenRange))
clipScreenPosition: (screenPosition, options={}) ->
@lineMap.screenPositionForBufferPosition(
@lineMap.outputPositionForInputPosition(
@lineFolder.clipScreenPosition(
@lineMap.bufferPositionForScreenPosition(@lineMap.clipScreenPosition(screenPosition, options)),
@lineMap.inputPositionForOutputPosition(@lineMap.clipOutputPosition(screenPosition, options)),
options
)
)
@@ -104,13 +104,13 @@ class LineWrapper
@linesForScreenRows(screenRow, screenRow)[0]
linesForScreenRows: (startRow, endRow) ->
@lineMap.linesForScreenRows(startRow, endRow)
@lineMap.linesForOutputRows(startRow, endRow)
getLines: ->
@linesForScreenRows(0, @lastRow())
lineCount: ->
@lineMap.screenLineCount()
@lineMap.outputLineCount()
lastRow: ->
@lineCount() - 1

View File

@@ -5,9 +5,9 @@ module.exports =
class ScreenLineFragment
isAtomic: false
constructor: (@tokens, @text, screenDelta, bufferDelta, extraFields) ->
@screenDelta = Point.fromObject(screenDelta)
@bufferDelta = Point.fromObject(bufferDelta)
constructor: (@tokens, @text, outputDelta, inputDelta, extraFields) ->
@outputDelta = Point.fromObject(outputDelta)
@inputDelta = Point.fromObject(inputDelta)
_.extend(this, extraFields)
splitAt: (column) ->
@@ -26,8 +26,8 @@ class ScreenLineFragment
leftText = @text.substring(0, column)
rightText = @text.substring(column)
[leftScreenDelta, rightScreenDelta] = @screenDelta.splitAt(column)
[leftBufferDelta, rightBufferDelta] = @bufferDelta.splitAt(column)
[leftScreenDelta, rightScreenDelta] = @outputDelta.splitAt(column)
[leftBufferDelta, rightBufferDelta] = @inputDelta.splitAt(column)
leftFragment = new ScreenLineFragment(leftTokens, leftText, leftScreenDelta, leftBufferDelta)
rightFragment = new ScreenLineFragment(rightTokens, rightText, rightScreenDelta, rightBufferDelta)
@@ -42,9 +42,9 @@ class ScreenLineFragment
concat: (other) ->
tokens = @tokens.concat(other.tokens)
text = @text + other.text
screenDelta = @screenDelta.add(other.screenDelta)
bufferDelta = @bufferDelta.add(other.bufferDelta)
new ScreenLineFragment(tokens, text, screenDelta, bufferDelta)
outputDelta = @outputDelta.add(other.outputDelta)
inputDelta = @inputDelta.add(other.inputDelta)
new ScreenLineFragment(tokens, text, outputDelta, inputDelta)
lengthForClipping: ->
if @isAtomic
@@ -53,7 +53,7 @@ class ScreenLineFragment
@text.length
isSoftWrapped: ->
@screenDelta.row == 1 and @bufferDelta.row == 0
@outputDelta.row == 1 and @inputDelta.row == 0
isEqual: (other) ->
_.isEqual(@tokens, other.tokens) and @screenDelta.isEqual(other.screenDelta) and @bufferDelta.isEqual(other.bufferDelta)
_.isEqual(@tokens, other.tokens) and @outputDelta.isEqual(other.outputDelta) and @inputDelta.isEqual(other.inputDelta)