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

@@ -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)