Use LineMap in LineFolder. All specs pass.

This commit is contained in:
Nathan Sobo
2012-02-17 16:52:12 -07:00
parent d1d48f8fca
commit 312bb34c0b
10 changed files with 266 additions and 50 deletions

View File

@@ -1,3 +1,5 @@
Point = require 'point'
module.exports =
class Delta
@fromObject: (object) ->
@@ -9,6 +11,7 @@ class Delta
constructor: (@rows=0, @columns=0) ->
add: (other) ->
debugger unless other
rows = @rows + other.rows
if other.rows == 0
columns = @columns + other.columns
@@ -25,6 +28,12 @@ class Delta
[new Delta(0, column), new Delta(@rows, rightColumns)]
inspect: ->
"(#{@rows}, #{@columns})"
isEqual: (other) ->
other = Delta.fromObject(other)
@rows == other.rows and @columns == other.columns
toPoint: ->
new Point(@rows, @columns)

View File

@@ -64,6 +64,9 @@ class Highlighter
screenLineForRow: (row) ->
@screenLines[row]
lineFragments: ->
@lineFragmentsForRows(0, @buffer.lastRow())
lineFragmentsForRows: (startRow, endRow) ->
for row in [startRow..endRow]
@lineFragmentForRow(row)

View File

@@ -1,5 +1,7 @@
Point = require 'point'
LineMap = require 'line-map'
ScreenLineFragment = require 'screen-line-fragment'
_ = require 'underscore'
module.exports =
class LineFolder
@@ -9,33 +11,61 @@ class LineFolder
buildLineMap: ->
@lineMap = new LineMap
@lineMap.insertAtBufferRow(0, @highlighter.screenLines)
@lineMap.insertAtBufferRow(0, @highlighter.lineFragments())
fold: (range) ->
{ start, end } = range
@activeFolds[start.row] ?= []
@activeFolds[start.row].push(new Fold(this, range))
fold: (bufferRange) ->
@activeFolds[bufferRange.start.row] ?= []
@activeFolds[bufferRange.start.row].push(new Fold(this, bufferRange))
screenRange = @screenRangeForBufferRange(bufferRange)
@lineMap.replaceScreenRows(screenRange.start.row, screenRange.end.row, @renderScreenLine(screenRange.start.row))
screenRow = @screenRowForBufferRow(start.row)
@lineMap.replaceBufferRows(start.row, end.row, @buildScreenLineForRow(screenRow))
renderScreenLine: (screenRow) ->
@renderScreenLineForBufferRow(@bufferRowForScreenRow(screenRow))
buildScreenLineForBufferRow: (bufferRow, startColumn) ->
screenLine = @highlighter.screenLineForRow(bufferRow).splitAt(startColumn)[1]
renderScreenLineForBufferRow: (bufferRow, startColumn=0) ->
screenLine = @highlighter.lineFragmentForRow(bufferRow).splitAt(startColumn)[1]
for fold in @foldsForBufferRow(bufferRow)
if fold.start.column > startColumn
prefix = screenLine.splitAt(fold.start.column - startColumn)[0]
suffix = @buildScreenLineForBufferRow(fold.end.row, fold.end.column)
return [prefix, @foldPlaceholder(fold), suffix]
{ start, end } = fold.range
if start.column > startColumn
prefix = screenLine.splitAt(start.column - startColumn)[0]
suffix = @buildScreenLineForBufferRow(end.row, end.column)
return _.flatten([prefix, @buildFoldPlaceholder(fold), suffix])
screenLine
screenRowForBufferRow: (screenRow) ->
@lineMap.screenPositionForBufferPosition([screenRow, 0]).row
buildScreenLineForBufferRow: (bufferRow, startColumn=0) ->
screenLine = @highlighter.lineFragmentForRow(bufferRow).splitAt(startColumn)[1]
for fold in @foldsForBufferRow(bufferRow)
{ start, end } = fold.range
if start.column > startColumn
prefix = screenLine.splitAt(start.column - startColumn)[0]
suffix = @buildScreenLineForBufferRow(end.row, end.column)
screenLine = _.flatten([prefix, @buildFoldPlaceholder(fold), suffix])
return screenLine
screenLine
lineFragmentsForScreenRows: (startRow, endRow) ->
@lineMap.lineFragmentsForScreenRows(startRow, endRow)
buildFoldPlaceholder: (fold) ->
new ScreenLineFragment([{value: '...', type: 'fold-placeholder'}], '...', [0, 3], fold.range.toDelta())
foldsForBufferRow: (bufferRow) ->
@activeFolds[bufferRow] or []
linesForScreenRows: (startRow, endRow) ->
@lineMap.linesForScreenRows(startRow, endRow)
screenRowForBufferRow: (bufferRow) ->
@screenPositionForBufferPosition([bufferRow, 0]).row
bufferRowForScreenRow: (screenRow) ->
@bufferPositionForScreenPosition([screenRow, 0]).row
screenPositionForBufferPosition: (bufferPosition) ->
@lineMap.screenPositionForBufferPosition(bufferPosition)
bufferPositionForScreenPosition: (screenPosition) ->
@lineMap.bufferPositionForScreenPosition(screenPosition)
screenRangeForBufferRange: (bufferRange) ->
@lineMap.screenRangeForBufferRange(bufferRange)
class Fold
constructor: (@lineFolder, @range) ->

View File

@@ -1,5 +1,7 @@
_ = require 'underscore'
Delta = require 'delta'
Point = require 'point'
Range = require 'range'
module.exports =
class LineMap
@@ -20,20 +22,32 @@ class LineMap
@lineFragments[insertIndex...insertIndex] = lineFragments
spliceAtBufferRow: (startRow, rowCount, lineFragments) ->
@spliceByDelta('bufferDelta', startRow, rowCount, lineFragments)
spliceAtScreenRow: (startRow, rowCount, lineFragments) ->
@spliceByDelta('screenDelta', startRow, rowCount, lineFragments)
spliceByDelta: (deltaType, startRow, rowCount, lineFragments) ->
stopRow = startRow + rowCount
startIndex = undefined
stopIndex = 0
delta = new Delta
for lineFragment, i in @lineFragments
startIndex ?= i if delta.rows == startRow
nextDelta = delta.add(lineFragment.bufferDelta)
startIndex = i if delta.rows == startRow and not startIndex
nextDelta = delta.add(lineFragment[deltaType])
break if nextDelta.rows > stopRow
delta = nextDelta
stopIndex++
@lineFragments[startIndex...stopIndex] = lineFragments
replaceBufferRows: (start, end, lineFragments) ->
@spliceAtBufferRow(start, end - start + 1, lineFragments)
replaceScreenRows: (start, end, lineFragments) ->
@spliceAtScreenRow(start, end - start + 1, lineFragments)
lineFragmentsForScreenRow: (screenRow) ->
@lineFragmentsForScreenRows(screenRow, screenRow)
@@ -48,8 +62,60 @@ class LineMap
lineFragments
linesForScreenRows: (startRow, endRow) ->
lastLine = null
lines = []
delta = new Delta
for fragment in @lineFragments
break if delta.rows > endRow
if delta.rows >= startRow
if pendingFragment
pendingFragment = pendingFragment.concat(fragment)
else
pendingFragment = fragment
if pendingFragment.screenDelta.rows > 0
lines.push pendingFragment
pendingFragment = null
delta = delta.add(fragment.screenDelta)
lines
bufferLineCount: ->
delta = new Delta
for lineFragment in @lineFragments
delta = delta.add(lineFragment.bufferDelta)
delta.rows
screenPositionForBufferPosition: (bufferPosition) ->
bufferPosition = Point.fromObject(bufferPosition)
bufferDelta = new Delta
screenDelta = new Delta
for lineFragment in @lineFragments
nextDelta = bufferDelta.add(lineFragment.bufferDelta)
break if nextDelta.toPoint().greaterThan(bufferPosition)
bufferDelta = nextDelta
screenDelta = screenDelta.add(lineFragment.screenDelta)
columns = screenDelta.columns + (bufferPosition.column - bufferDelta.columns)
new Point(screenDelta.rows, columns)
bufferPositionForScreenPosition: (screenPosition) ->
screenPosition = Point.fromObject(screenPosition)
bufferDelta = new Delta
screenDelta = new Delta
for lineFragment in @lineFragments
nextDelta = screenDelta.add(lineFragment.screenDelta)
break if nextDelta.toPoint().greaterThan(screenPosition)
screenDelta = nextDelta
bufferDelta = bufferDelta.add(lineFragment.bufferDelta)
columns = bufferDelta.columns + (screenPosition.column - screenDelta.columns)
new Point(bufferDelta.rows, columns)
screenRangeForBufferRange: (bufferRange) ->
start = @screenPositionForBufferPosition(bufferRange.start)
end = @screenPositionForBufferPosition(bufferRange.end)
new Range(start, end)

View File

@@ -34,3 +34,6 @@ class Point
-1
else
0
greaterThan: (other) ->
@compare(other) > 0

View File

@@ -1,6 +1,8 @@
Point = require 'point'
Delta = require 'delta'
_ = require 'underscore'
module.exports =
class Range
constructor: (pointA = new Point(0, 0), pointB = new Point(0, 0)) ->
@@ -29,3 +31,11 @@ class Range
isEmpty: ->
@start.isEqual(@end)
toDelta: ->
rows = @end.row - @start.row
if rows == 0
columns = @end.column - @start.column
else
columns = @end.column
new Delta(rows, columns)

View File

@@ -7,10 +7,8 @@ class ScreenLineFragment
@screenDelta = Delta.fromObject(screenDelta)
@bufferDelta = Delta.fromObject(bufferDelta)
splitAt: (column) ->
return [undefined, this] if column == 0
return [this, undefined] if column >= @text.length
rightTokens = _.clone(@tokens)
leftTokens = []
@@ -37,3 +35,13 @@ class ScreenLineFragment
value1 = value.substring(0, splitIndex)
value2 = value.substring(splitIndex)
[{value: value1, type }, {value: value2, type}]
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)
isEqual: (other) ->
_.isEqual(@tokens, other.tokens) and @screenDelta.isEqual(other.screenDelta) and @bufferDelta.isEqual(other.bufferDelta)