mirror of
https://github.com/atom/atom.git
synced 2026-02-09 06:05:11 -05:00
Merge branch 'master' into chrome
This commit is contained in:
@@ -13,11 +13,11 @@ class Cursor extends View
|
||||
@one 'attach', => @updateAppearance()
|
||||
|
||||
bufferChanged: (e) ->
|
||||
@setPosition(e.newRange.end)
|
||||
@setScreenPosition(e.newRange.end)
|
||||
|
||||
setPosition: (point) ->
|
||||
setScreenPosition: (point) ->
|
||||
point = Point.fromObject(point)
|
||||
@point = @editor.clipPosition(point)
|
||||
@$position = @editor.clipPosition(point)
|
||||
@goalColumn = null
|
||||
@updateAppearance()
|
||||
@trigger 'cursor:position-changed'
|
||||
@@ -26,67 +26,67 @@ class Cursor extends View
|
||||
window.clearTimeout(@idleTimeout) if @idleTimeout
|
||||
@idleTimeout = window.setTimeout (=> @addClass 'idle'), 200
|
||||
|
||||
getPosition: -> _.clone(@point)
|
||||
getScreenPosition: -> _.clone(@$position)
|
||||
|
||||
getColumn: ->
|
||||
@getPosition().column
|
||||
@getScreenPosition().column
|
||||
|
||||
setColumn: (column) ->
|
||||
{ row } = @getPosition()
|
||||
@setPosition {row, column}
|
||||
{ row } = @getScreenPosition()
|
||||
@setScreenPosition {row, column}
|
||||
|
||||
getRow: ->
|
||||
@getPosition().row
|
||||
@getScreenPosition().row
|
||||
|
||||
isOnEOL: ->
|
||||
@getColumn() == @editor.getCurrentLine().length
|
||||
|
||||
moveUp: ->
|
||||
{ row, column } = @getPosition()
|
||||
{ row, column } = @getScreenPosition()
|
||||
column = @goalColumn if @goalColumn?
|
||||
if row > 0
|
||||
@setPosition({row: row - 1, column: column})
|
||||
@setScreenPosition({row: row - 1, column: column})
|
||||
else
|
||||
@moveToLineStart()
|
||||
|
||||
@goalColumn = column
|
||||
|
||||
moveDown: ->
|
||||
{ row, column } = @getPosition()
|
||||
{ row, column } = @getScreenPosition()
|
||||
column = @goalColumn if @goalColumn?
|
||||
if row < @editor.buffer.numLines() - 1
|
||||
@setPosition({row: row + 1, column: column})
|
||||
@setScreenPosition({row: row + 1, column: column})
|
||||
else
|
||||
@moveToLineEnd()
|
||||
|
||||
@goalColumn = column
|
||||
|
||||
moveToLineEnd: ->
|
||||
{ row } = @getPosition()
|
||||
@setPosition({ row, column: @editor.buffer.getLine(row).length })
|
||||
{ row } = @getScreenPosition()
|
||||
@setScreenPosition({ row, column: @editor.buffer.getLine(row).length })
|
||||
|
||||
moveToLineStart: ->
|
||||
{ row } = @getPosition()
|
||||
@setPosition({ row, column: 0 })
|
||||
{ row } = @getScreenPosition()
|
||||
@setScreenPosition({ row, column: 0 })
|
||||
|
||||
moveRight: ->
|
||||
{ row, column } = @getPosition()
|
||||
{ row, column } = @getScreenPosition()
|
||||
if column < @editor.buffer.getLine(row).length
|
||||
column++
|
||||
else if row < @editor.buffer.numLines() - 1
|
||||
row++
|
||||
column = 0
|
||||
@setPosition({row, column})
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
moveLeft: ->
|
||||
{ row, column } = @getPosition()
|
||||
{ row, column } = @getScreenPosition()
|
||||
if column > 0
|
||||
column--
|
||||
else if row > 0
|
||||
row--
|
||||
column = @editor.buffer.getLine(row).length
|
||||
|
||||
@setPosition({row, column})
|
||||
@setScreenPosition({row, column})
|
||||
|
||||
moveLeftUntilMatch: (regex) ->
|
||||
row = @getRow()
|
||||
@@ -108,10 +108,10 @@ class Cursor extends View
|
||||
|
||||
offset = match and -match[0].length or 0
|
||||
|
||||
@setPosition [row, column + offset]
|
||||
@setScreenPosition [row, column + offset]
|
||||
|
||||
updateAppearance: ->
|
||||
position = @editor.pixelPositionFromPoint(@point)
|
||||
position = @editor.pixelPositionFromPoint(@getScreenPosition())
|
||||
@css(position)
|
||||
@autoScrollVertically(position)
|
||||
@autoScrollHorizontally(position)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
Point = require 'point'
|
||||
|
||||
module.exports =
|
||||
class Delta
|
||||
@fromObject: (object) ->
|
||||
if object instanceof Delta
|
||||
object
|
||||
else
|
||||
new Delta(object[0], object[1])
|
||||
|
||||
constructor: (@rows=0, @columns=0) ->
|
||||
|
||||
add: (other) ->
|
||||
debugger unless other
|
||||
rows = @rows + other.rows
|
||||
if other.rows == 0
|
||||
columns = @columns + other.columns
|
||||
else
|
||||
columns = other.columns
|
||||
|
||||
new Delta(rows, columns)
|
||||
|
||||
splitAt: (column) ->
|
||||
if @rows == 0
|
||||
rightColumns = @columns - column
|
||||
else
|
||||
rightColumns = @columns
|
||||
|
||||
[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)
|
||||
@@ -4,6 +4,7 @@ Point = require 'point'
|
||||
Cursor = require 'cursor'
|
||||
Selection = require 'selection'
|
||||
Highlighter = require 'highlighter'
|
||||
LineFolder = require 'line-folder'
|
||||
LineWrapper = require 'line-wrapper'
|
||||
UndoManager = require 'undo-manager'
|
||||
Range = require 'range'
|
||||
@@ -57,6 +58,7 @@ class Editor extends View
|
||||
'meta-z': 'undo'
|
||||
'meta-Z': 'redo'
|
||||
'alt-meta-w': 'toggle-soft-wrap'
|
||||
'alt-meta-f': 'fold-selection'
|
||||
|
||||
@on 'move-right', => @moveCursorRight()
|
||||
@on 'move-left', => @moveCursorLeft()
|
||||
@@ -75,6 +77,7 @@ class Editor extends View
|
||||
@on 'undo', => @undo()
|
||||
@on 'redo', => @redo()
|
||||
@on 'toggle-soft-wrap', => @toggleSoftWrap()
|
||||
@on 'fold-selection', => @foldSelection()
|
||||
|
||||
buildCursorAndSelection: ->
|
||||
@cursor = new Cursor(this)
|
||||
@@ -92,7 +95,7 @@ class Editor extends View
|
||||
clickCount = e.originalEvent.detail
|
||||
|
||||
if clickCount == 1
|
||||
@setCursorPosition @pointFromMouseEvent(e)
|
||||
@setCursorScreenPosition @pointFromMouseEvent(e)
|
||||
else if clickCount == 2
|
||||
@selection.selectWord()
|
||||
else if clickCount >= 3
|
||||
@@ -104,7 +107,7 @@ class Editor extends View
|
||||
@insertText(e.originalEvent.data)
|
||||
|
||||
@on 'cursor:position-changed', =>
|
||||
@hiddenInput.css(@pixelPositionFromPoint(@cursor.getPosition()))
|
||||
@hiddenInput.css(@pixelPositionFromPoint(@cursor.getScreenPosition()))
|
||||
|
||||
@one 'attach', =>
|
||||
@calculateDimensions()
|
||||
@@ -129,22 +132,23 @@ class Editor extends View
|
||||
|
||||
renderLines: ->
|
||||
@lines.empty()
|
||||
for screenLine in @lineWrapper.screenLines()
|
||||
for screenLine in @lineWrapper.getLines()
|
||||
@lines.append @buildLineElement(screenLine)
|
||||
|
||||
setBuffer: (@buffer) ->
|
||||
@highlighter = new Highlighter(@buffer)
|
||||
@lineWrapper = new LineWrapper(Infinity, @highlighter)
|
||||
@lineFolder = new LineFolder(@highlighter)
|
||||
@lineWrapper = new LineWrapper(Infinity, @lineFolder)
|
||||
@undoManager = new UndoManager(@buffer)
|
||||
@renderLines()
|
||||
@setCursorPosition(row: 0, column: 0)
|
||||
@setCursorScreenPosition(row: 0, column: 0)
|
||||
|
||||
@buffer.on 'change', (e) =>
|
||||
@cursor.bufferChanged(e)
|
||||
|
||||
@lineWrapper.on 'change', (e) =>
|
||||
{ oldRange, newRange } = e
|
||||
screenLines = @lineWrapper.screenLinesForRows(newRange.start.row, newRange.end.row)
|
||||
screenLines = @lineWrapper.linesForScreenRows(newRange.start.row, newRange.end.row)
|
||||
if newRange.end.row > oldRange.end.row
|
||||
# update, then insert elements
|
||||
for row in [newRange.start.row..newRange.end.row]
|
||||
@@ -250,8 +254,8 @@ class Editor extends View
|
||||
moveCursorDown: -> @cursor.moveDown()
|
||||
moveCursorRight: -> @cursor.moveRight()
|
||||
moveCursorLeft: -> @cursor.moveLeft()
|
||||
setCursorPosition: (point) -> @cursor.setPosition(point)
|
||||
getCursorPosition: -> @cursor.getPosition()
|
||||
setCursorScreenPosition: (point) -> @cursor.setScreenPosition(point)
|
||||
getCursorScreenPosition: -> @cursor.getScreenPosition()
|
||||
setCursorRow: (row) -> @cursor.setRow(row)
|
||||
getCursorRow: -> @cursor.getRow()
|
||||
setCursorColumn: (column) -> @cursor.setColumn(column)
|
||||
@@ -271,6 +275,8 @@ class Editor extends View
|
||||
copySelection: -> @selection.copy()
|
||||
paste: -> @selection.insertText(atom.native.readFromPasteboard())
|
||||
|
||||
foldSelection: -> @selection.fold()
|
||||
|
||||
backspace: ->
|
||||
@selectLeft() if @selection.isEmpty()
|
||||
@selection.delete()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
_ = require 'underscore'
|
||||
ScreenLineFragment = require 'screen-line-fragment'
|
||||
ScreenLine = require 'screen-line'
|
||||
EventEmitter = require 'event-emitter'
|
||||
|
||||
module.exports =
|
||||
@@ -11,7 +10,7 @@ class Highlighter
|
||||
|
||||
constructor: (@buffer) ->
|
||||
@buildTokenizer()
|
||||
@screenLines = @buildScreenLinesForRows('start', 0, @buffer.lastRow())
|
||||
@screenLines = @buildLinesForScreenRows('start', 0, @buffer.lastRow())
|
||||
@buffer.on 'change', (e) => @handleBufferChange(e)
|
||||
|
||||
buildTokenizer: ->
|
||||
@@ -25,7 +24,7 @@ class Highlighter
|
||||
|
||||
startState = @screenLines[newRange.start.row - 1]?.state or 'start'
|
||||
@screenLines[oldRange.start.row..oldRange.end.row] =
|
||||
@buildScreenLinesForRows(startState, newRange.start.row, newRange.end.row)
|
||||
@buildLinesForScreenRows(startState, newRange.start.row, newRange.end.row)
|
||||
|
||||
# spill detection
|
||||
# compare scanner state of last re-highlighted line with its previous state.
|
||||
@@ -36,7 +35,7 @@ class Highlighter
|
||||
break if @screenLines[row].state == previousState
|
||||
nextRow = row + 1
|
||||
previousState = @screenLines[nextRow].state
|
||||
@screenLines[nextRow] = @buildScreenLineForRow(@screenLines[row].state, nextRow)
|
||||
@screenLines[nextRow] = @buildLineForScreenRow(@screenLines[row].state, nextRow)
|
||||
|
||||
# if highlighting spilled beyond the bounds of the textual change, update
|
||||
# the pre and post range to reflect area of highlight changes
|
||||
@@ -49,30 +48,25 @@ class Highlighter
|
||||
|
||||
@trigger("change", {oldRange, newRange})
|
||||
|
||||
buildScreenLinesForRows: (startState, startRow, endRow) ->
|
||||
buildLinesForScreenRows: (startState, startRow, endRow) ->
|
||||
state = startState
|
||||
for row in [startRow..endRow]
|
||||
screenLine = @buildScreenLineForRow(state, row)
|
||||
screenLine = @buildLineForScreenRow(state, row)
|
||||
state = screenLine.state
|
||||
screenLine
|
||||
|
||||
buildScreenLineForRow: (state, row) ->
|
||||
buildLineForScreenRow: (state, row) ->
|
||||
line = @buffer.getLine(row)
|
||||
{tokens, state} = @tokenizer.getLineTokens(line, state)
|
||||
new ScreenLine(tokens, line, state)
|
||||
new ScreenLineFragment(tokens, line, [1, 0], [1, 0], { state })
|
||||
|
||||
screenLineForRow: (row) ->
|
||||
lineForScreenRow: (row) ->
|
||||
@screenLines[row]
|
||||
|
||||
lineFragments: ->
|
||||
@lineFragmentsForRows(0, @buffer.lastRow())
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@screenLines[startRow..endRow]
|
||||
|
||||
lineFragmentsForRows: (startRow, endRow) ->
|
||||
for row in [startRow..endRow]
|
||||
@lineFragmentForRow(row)
|
||||
|
||||
lineFragmentForRow: (row) ->
|
||||
{ tokens, text } = @screenLines[row]
|
||||
new ScreenLineFragment(tokens, text, [1, 0], [1, 0])
|
||||
lastRow: ->
|
||||
@screenLines.length - 1
|
||||
|
||||
_.extend(Highlighter.prototype, EventEmitter)
|
||||
|
||||
@@ -1,50 +1,110 @@
|
||||
_ = require 'underscore'
|
||||
Point = require 'point'
|
||||
Range = require 'range'
|
||||
LineMap = require 'line-map'
|
||||
ScreenLineFragment = require 'screen-line-fragment'
|
||||
_ = require 'underscore'
|
||||
EventEmitter = require 'event-emitter'
|
||||
|
||||
module.exports =
|
||||
class LineFolder
|
||||
lineMap: null
|
||||
lastHighlighterChangeEvent: null
|
||||
|
||||
constructor: (@highlighter) ->
|
||||
@activeFolds = {}
|
||||
@buildLineMap()
|
||||
@highlighter.buffer.on 'change', (e) => @handleBufferChange(e)
|
||||
@highlighter.on 'change', (e) => @lastHighlighterChangeEvent = e
|
||||
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
@lineMap.insertAtBufferRow(0, @highlighter.lineFragments())
|
||||
@lineMap.insertAtBufferRow(0, @highlighter.screenLines)
|
||||
|
||||
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))
|
||||
logLines: (start=0, end=@lastRow())->
|
||||
for row in [start..end]
|
||||
line = @lineForScreenRow(row).text
|
||||
console.log row, line, line.length
|
||||
|
||||
renderScreenLine: (screenRow) ->
|
||||
@renderScreenLineForBufferRow(@bufferRowForScreenRow(screenRow))
|
||||
createFold: (bufferRange) ->
|
||||
fold = new Fold(this, bufferRange)
|
||||
@registerFold(bufferRange.start.row, fold)
|
||||
oldScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
|
||||
|
||||
renderScreenLineForBufferRow: (bufferRow, startColumn=0) ->
|
||||
screenLine = @highlighter.lineFragmentForRow(bufferRow).splitAt(startColumn)[1]
|
||||
lineWithFold = @buildLine(oldScreenRange.start.row)
|
||||
@lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lineWithFold)
|
||||
|
||||
newScreenRange = oldScreenRange.copy()
|
||||
newScreenRange.end = _.clone(newScreenRange.start)
|
||||
for fragment in lineWithFold
|
||||
newScreenRange.end.column += fragment.text.length
|
||||
|
||||
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange
|
||||
@trigger 'fold', bufferRange
|
||||
fold
|
||||
|
||||
destroyFold: (fold) ->
|
||||
bufferRange = fold.getRange()
|
||||
@unregisterFold(bufferRange.start.row, fold)
|
||||
startScreenRow = @screenRowForBufferRow(bufferRange.start.row)
|
||||
|
||||
oldScreenRange = new Range()
|
||||
oldScreenRange.start.row = startScreenRow
|
||||
oldScreenRange.end.row = startScreenRow
|
||||
oldScreenRange.end.column = @lineMap.lineForScreenRow(startScreenRow).text.length
|
||||
|
||||
@lineMap.replaceScreenRow(startScreenRow, @buildLinesForBufferRows(bufferRange.start.row, bufferRange.end.row))
|
||||
|
||||
newScreenRange = @expandScreenRangeToLineEnds(@screenRangeForBufferRange(bufferRange))
|
||||
|
||||
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange
|
||||
@trigger 'unfold', fold.getRange()
|
||||
|
||||
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(@lastHighlighterChangeEvent)
|
||||
|
||||
handleHighlighterChange: (e) ->
|
||||
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)
|
||||
newScreenRange = @screenRangeForBufferRange(e.newRange)
|
||||
expandedNewScreenRange = @expandScreenRangeToLineEnds(newScreenRange)
|
||||
|
||||
unless oldScreenRange.isEmpty() and newScreenRange.isEmpty()
|
||||
@trigger 'change', oldRange: expandedOldScreenRange, newRange: expandedNewScreenRange
|
||||
|
||||
buildLinesForBufferRows: (start, end) ->
|
||||
lines = [@buildLine(@screenRowForBufferRow(start))]
|
||||
if end > start
|
||||
for row in [start + 1..end]
|
||||
lines.push @buildLineForBufferRow(row)
|
||||
_.flatten(lines)
|
||||
|
||||
buildLine: (screenRow) ->
|
||||
@buildLineForBufferRow(@bufferRowForScreenRow(screenRow))
|
||||
|
||||
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 = @buildScreenLineForBufferRow(end.row, end.column)
|
||||
suffix = @buildLineForBufferRow(end.row, end.column)
|
||||
return _.flatten([prefix, @buildFoldPlaceholder(fold), suffix])
|
||||
screenLine
|
||||
|
||||
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
|
||||
|
||||
buildFoldPlaceholder: (fold) ->
|
||||
new ScreenLineFragment([{value: '...', type: 'fold-placeholder'}], '...', [0, 3], fold.range.toDelta())
|
||||
new ScreenLineFragment([{value: '...', type: 'fold-placeholder'}], '...', [0, 3], fold.getRange().toDelta(), isAtomic: true)
|
||||
|
||||
foldsForBufferRow: (bufferRow) ->
|
||||
@activeFolds[bufferRow] or []
|
||||
@@ -52,6 +112,18 @@ class LineFolder
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@lineMap.linesForScreenRows(startRow, endRow)
|
||||
|
||||
lineForScreenRow: (screenRow) ->
|
||||
@lineMap.lineForScreenRow(screenRow)
|
||||
|
||||
getLines: ->
|
||||
@lineMap.getScreenLines()
|
||||
|
||||
lineCount: ->
|
||||
@lineMap.screenLineCount()
|
||||
|
||||
lastRow: ->
|
||||
@lineCount() - 1
|
||||
|
||||
screenRowForBufferRow: (bufferRow) ->
|
||||
@screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
|
||||
@@ -64,8 +136,48 @@ class LineFolder
|
||||
bufferPositionForScreenPosition: (screenPosition) ->
|
||||
@lineMap.bufferPositionForScreenPosition(screenPosition)
|
||||
|
||||
clipScreenPosition: (screenPosition) ->
|
||||
@lineMap.clipScreenPosition(screenPosition)
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.screenRangeForBufferRange(bufferRange)
|
||||
|
||||
expandScreenRangeToLineEnds: (screenRange) ->
|
||||
{ start, end } = screenRange
|
||||
new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length])
|
||||
|
||||
_.extend LineFolder.prototype, EventEmitter
|
||||
|
||||
class Fold
|
||||
constructor: (@lineFolder, @range) ->
|
||||
constructor: (@lineFolder, {@start, @end}) ->
|
||||
|
||||
destroy: ->
|
||||
@lineFolder.destroyFold(this)
|
||||
|
||||
getRange: ->
|
||||
new Range(@start, @end)
|
||||
|
||||
handleBufferChange: (event) ->
|
||||
oldStartRow = @start.row
|
||||
|
||||
{ oldRange } = event
|
||||
if oldRange.start.isLessThanOrEqual(@start) and oldRange.end.isGreaterThanOrEqual(@end)
|
||||
@lineFolder.unregisterFold(oldStartRow, this)
|
||||
return
|
||||
|
||||
@start = @updateAnchorPoint(@start, event)
|
||||
@end = @updateAnchorPoint(@end, event, false)
|
||||
|
||||
if @start.row != oldStartRow
|
||||
@lineFolder.unregisterFold(oldStartRow, this)
|
||||
@lineFolder.registerFold(@start.row, this)
|
||||
|
||||
updateAnchorPoint: (point, event, inclusive=true) ->
|
||||
{ newRange, oldRange } = event
|
||||
if inclusive
|
||||
return point if oldRange.end.isGreaterThan(point)
|
||||
else
|
||||
return point if oldRange.end.isGreaterThanOrEqual(point)
|
||||
|
||||
newRange.end.add(point.subtract(oldRange.end))
|
||||
|
||||
|
||||
@@ -1,129 +1,152 @@
|
||||
_ = require 'underscore'
|
||||
Delta = require 'delta'
|
||||
Point = require 'point'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class LineMap
|
||||
constructor: ->
|
||||
@lineFragments = []
|
||||
@screenLines = []
|
||||
|
||||
insertAtBufferRow: (bufferRow, lineFragments) ->
|
||||
lineFragments = [lineFragments] unless _.isArray(lineFragments)
|
||||
delta = new Delta
|
||||
insertAtBufferRow: (bufferRow, screenLines) ->
|
||||
screenLines = [screenLines] unless _.isArray(screenLines)
|
||||
delta = new Point
|
||||
insertIndex = 0
|
||||
|
||||
for lineFragment in @lineFragments
|
||||
nextDelta = delta.add(lineFragment.bufferDelta)
|
||||
break if nextDelta.rows > bufferRow
|
||||
for screenLine in @screenLines
|
||||
nextDelta = delta.add(screenLine.bufferDelta)
|
||||
break if nextDelta.row > bufferRow
|
||||
delta = nextDelta
|
||||
insertIndex++
|
||||
|
||||
@lineFragments[insertIndex...insertIndex] = lineFragments
|
||||
@screenLines[insertIndex...insertIndex] = screenLines
|
||||
|
||||
spliceAtBufferRow: (startRow, rowCount, lineFragments) ->
|
||||
@spliceByDelta('bufferDelta', startRow, rowCount, lineFragments)
|
||||
spliceAtBufferRow: (startRow, rowCount, screenLines) ->
|
||||
@spliceByDelta('bufferDelta', startRow, rowCount, screenLines)
|
||||
|
||||
spliceAtScreenRow: (startRow, rowCount, lineFragments) ->
|
||||
@spliceByDelta('screenDelta', startRow, rowCount, lineFragments)
|
||||
spliceAtScreenRow: (startRow, rowCount, screenLines) ->
|
||||
@spliceByDelta('screenDelta', startRow, rowCount, screenLines)
|
||||
|
||||
spliceByDelta: (deltaType, startRow, rowCount, lineFragments) ->
|
||||
spliceByDelta: (deltaType, startRow, rowCount, screenLines) ->
|
||||
stopRow = startRow + rowCount
|
||||
startIndex = undefined
|
||||
stopIndex = 0
|
||||
delta = new Delta
|
||||
delta = new Point
|
||||
|
||||
for lineFragment, i in @lineFragments
|
||||
startIndex = i if delta.rows == startRow and not startIndex
|
||||
nextDelta = delta.add(lineFragment[deltaType])
|
||||
break if nextDelta.rows > stopRow
|
||||
for screenLine, i in @screenLines
|
||||
startIndex = i if delta.row == startRow and not startIndex
|
||||
nextDelta = delta.add(screenLine[deltaType])
|
||||
break if nextDelta.row > stopRow
|
||||
delta = nextDelta
|
||||
stopIndex++
|
||||
|
||||
@lineFragments[startIndex...stopIndex] = lineFragments
|
||||
@screenLines[startIndex...stopIndex] = screenLines
|
||||
|
||||
replaceBufferRows: (start, end, lineFragments) ->
|
||||
@spliceAtBufferRow(start, end - start + 1, lineFragments)
|
||||
replaceBufferRows: (start, end, screenLines) ->
|
||||
@spliceAtBufferRow(start, end - start + 1, screenLines)
|
||||
|
||||
replaceScreenRows: (start, end, lineFragments) ->
|
||||
@spliceAtScreenRow(start, end - start + 1, lineFragments)
|
||||
replaceScreenRow: (row, screenLines) ->
|
||||
@replaceScreenRows(row, row, screenLines)
|
||||
|
||||
lineFragmentsForScreenRow: (screenRow) ->
|
||||
@lineFragmentsForScreenRows(screenRow, screenRow)
|
||||
replaceScreenRows: (start, end, screenLines) ->
|
||||
@spliceAtScreenRow(start, end - start + 1, screenLines)
|
||||
|
||||
lineFragmentsForScreenRows: (startRow, endRow) ->
|
||||
lineFragments = []
|
||||
delta = new Delta
|
||||
getScreenLines: ->
|
||||
return @screenLines
|
||||
|
||||
for lineFragment in @lineFragments
|
||||
break if delta.rows > endRow
|
||||
lineFragments.push(lineFragment) if delta.rows >= startRow
|
||||
delta = delta.add(lineFragment.screenDelta)
|
||||
|
||||
lineFragments
|
||||
lineForScreenRow: (row) ->
|
||||
@linesForScreenRows(row, row)[0]
|
||||
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
lastLine = null
|
||||
lines = []
|
||||
delta = new Delta
|
||||
delta = new Point
|
||||
|
||||
for fragment in @lineFragments
|
||||
break if delta.rows > endRow
|
||||
if delta.rows >= startRow
|
||||
for fragment in @screenLines
|
||||
break if delta.row > endRow
|
||||
if delta.row >= startRow
|
||||
if pendingFragment
|
||||
pendingFragment = pendingFragment.concat(fragment)
|
||||
else
|
||||
pendingFragment = fragment
|
||||
if pendingFragment.screenDelta.rows > 0
|
||||
if pendingFragment.screenDelta.row > 0
|
||||
lines.push pendingFragment
|
||||
pendingFragment = null
|
||||
delta = delta.add(fragment.screenDelta)
|
||||
lines
|
||||
|
||||
lineForBufferRow: (row) ->
|
||||
line = null
|
||||
delta = new Point
|
||||
for fragment in @screenLines
|
||||
break if delta.row > row
|
||||
if delta.row == row
|
||||
if line
|
||||
line = line.concat(fragment)
|
||||
else
|
||||
line = fragment
|
||||
delta = delta.add(fragment.bufferDelta)
|
||||
line
|
||||
|
||||
bufferLineCount: ->
|
||||
delta = new Delta
|
||||
for lineFragment in @lineFragments
|
||||
delta = delta.add(lineFragment.bufferDelta)
|
||||
delta.rows
|
||||
delta = new Point
|
||||
for screenLine in @screenLines
|
||||
delta = delta.add(screenLine.bufferDelta)
|
||||
delta.row
|
||||
|
||||
screenLineCount: ->
|
||||
delta = new Delta
|
||||
for lineFragment in @lineFragments
|
||||
delta = delta.add(lineFragment.screenDelta)
|
||||
delta.rows
|
||||
delta = new Point
|
||||
for screenLine in @screenLines
|
||||
delta = delta.add(screenLine.screenDelta)
|
||||
delta.row
|
||||
|
||||
screenPositionForBufferPosition: (bufferPosition, eagerWrap=true) ->
|
||||
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)
|
||||
break if nextDelta.toPoint().isEqual(bufferPosition) and not eagerWrap
|
||||
bufferDelta = new Point
|
||||
screenDelta = new Point
|
||||
|
||||
for screenLine in @screenLines
|
||||
nextDelta = bufferDelta.add(screenLine.bufferDelta)
|
||||
break if nextDelta.isGreaterThan(bufferPosition)
|
||||
break if nextDelta.isEqual(bufferPosition) and not eagerWrap
|
||||
bufferDelta = nextDelta
|
||||
screenDelta = screenDelta.add(lineFragment.screenDelta)
|
||||
screenDelta = screenDelta.add(screenLine.screenDelta)
|
||||
|
||||
columns = screenDelta.columns + (bufferPosition.column - bufferDelta.columns)
|
||||
new Point(screenDelta.rows, columns)
|
||||
remainingBufferColumn = bufferPosition.column - bufferDelta.column
|
||||
additionalScreenColumn = Math.max(0, Math.min(remainingBufferColumn, screenLine.lengthForClipping()))
|
||||
|
||||
new Point(screenDelta.row, screenDelta.column + additionalScreenColumn)
|
||||
|
||||
bufferPositionForScreenPosition: (screenPosition) ->
|
||||
screenPosition = Point.fromObject(screenPosition)
|
||||
bufferDelta = new Delta
|
||||
screenDelta = new Delta
|
||||
bufferDelta = new Point
|
||||
screenDelta = new Point
|
||||
|
||||
for lineFragment in @lineFragments
|
||||
nextDelta = screenDelta.add(lineFragment.screenDelta)
|
||||
break if nextDelta.toPoint().greaterThan(screenPosition)
|
||||
for screenLine in @screenLines
|
||||
nextDelta = screenDelta.add(screenLine.screenDelta)
|
||||
break if nextDelta.isGreaterThan(screenPosition)
|
||||
screenDelta = nextDelta
|
||||
bufferDelta = bufferDelta.add(lineFragment.bufferDelta)
|
||||
bufferDelta = bufferDelta.add(screenLine.bufferDelta)
|
||||
|
||||
columns = bufferDelta.columns + (screenPosition.column - screenDelta.columns)
|
||||
new Point(bufferDelta.rows, columns)
|
||||
column = bufferDelta.column + (screenPosition.column - screenDelta.column)
|
||||
new Point(bufferDelta.row, column)
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
start = @screenPositionForBufferPosition(bufferRange.start)
|
||||
end = @screenPositionForBufferPosition(bufferRange.end)
|
||||
new Range(start, end)
|
||||
|
||||
clipScreenPosition: (screenPosition) ->
|
||||
screenPosition = Point.fromObject(screenPosition)
|
||||
screenPosition = new Point(Math.max(0, screenPosition.row), Math.max(0, screenPosition.column))
|
||||
|
||||
screenDelta = new Point
|
||||
for screenLine in @screenLines
|
||||
nextDelta = screenDelta.add(screenLine.screenDelta)
|
||||
break if nextDelta.isGreaterThan(screenPosition)
|
||||
screenDelta = nextDelta
|
||||
|
||||
maxColumn = screenDelta.column + screenLine.lengthForClipping()
|
||||
screenDelta.column = Math.min(maxColumn, screenPosition.column)
|
||||
|
||||
screenDelta
|
||||
|
||||
|
||||
@@ -1,73 +1,49 @@
|
||||
_ = require 'underscore'
|
||||
EventEmitter = require 'event-emitter'
|
||||
SpanIndex = require 'span-index'
|
||||
LineMap = require 'line-map'
|
||||
Point = require 'point'
|
||||
Range = require 'range'
|
||||
Delta = require 'delta'
|
||||
|
||||
module.exports =
|
||||
class LineWrapper
|
||||
constructor: (@maxLength, @highlighter) ->
|
||||
@buffer = @highlighter.buffer
|
||||
@buildWrappedLines()
|
||||
@highlighter.on 'change', (e) => @handleChange(e)
|
||||
constructor: (@maxLength, @lineFolder) ->
|
||||
@buildLineMap()
|
||||
@lineFolder.on 'change', (e) => @handleChange(e)
|
||||
|
||||
setMaxLength: (@maxLength) ->
|
||||
oldRange = new Range
|
||||
oldRange.end.row = @screenLineCount() - 1
|
||||
oldRange.end.column = _.last(@index.last().screenLines).text.length
|
||||
@buildWrappedLines()
|
||||
newRange = new Range
|
||||
newRange.end.row = @screenLineCount() - 1
|
||||
newRange.end.column = _.last(@index.last().screenLines).text.length
|
||||
oldRange = @rangeForAllLines()
|
||||
@buildLineMap()
|
||||
newRange = @rangeForAllLines()
|
||||
@trigger 'change', { oldRange, newRange }
|
||||
|
||||
getSpans: (wrappedLines) ->
|
||||
wrappedLines.map (line) -> line.screenLines.length
|
||||
|
||||
unpackWrappedLines: (wrappedLines) ->
|
||||
_.flatten(_.pluck(wrappedLines, 'screenLines'))
|
||||
|
||||
buildWrappedLines: ->
|
||||
@index = new SpanIndex
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
wrappedLines = @buildWrappedLinesForBufferRows(0, @buffer.lastRow())
|
||||
@index.insert 0, @getSpans(wrappedLines), wrappedLines
|
||||
@lineMap.insertAtBufferRow 0, @unpackWrappedLines(wrappedLines)
|
||||
@lineMap.insertAtBufferRow 0, @buildScreenLinesForBufferRows(0, @lineFolder.lastRow())
|
||||
|
||||
handleChange: (e) ->
|
||||
oldRange = new Range
|
||||
oldBufferRange = e.oldRange
|
||||
newBufferRange = e.newRange
|
||||
|
||||
bufferRow = e.oldRange.start.row
|
||||
oldRange.start.row = @firstScreenRowForBufferRow(e.oldRange.start.row)
|
||||
oldRange.end.row = @lastScreenRowForBufferRow(e.oldRange.end.row)
|
||||
oldRange.end.column = _.last(@index.at(e.oldRange.end.row).screenLines).text.length
|
||||
oldScreenRange = @lineMap.screenRangeForBufferRange(@expandBufferRangeToLineEnds(oldBufferRange))
|
||||
newScreenLines = @buildScreenLinesForBufferRows(newBufferRange.start.row, newBufferRange.end.row)
|
||||
@lineMap.replaceBufferRows oldBufferRange.start.row, oldBufferRange.end.row, newScreenLines
|
||||
newScreenRange = @lineMap.screenRangeForBufferRange(@expandBufferRangeToLineEnds(newBufferRange))
|
||||
|
||||
{ start, end } = e.oldRange
|
||||
wrappedLines = @buildWrappedLinesForBufferRows(e.newRange.start.row, e.newRange.end.row)
|
||||
@index.splice start.row, end.row, @getSpans(wrappedLines), wrappedLines
|
||||
@lineMap.replaceBufferRows start.row, end.row, @unpackWrappedLines(wrappedLines)
|
||||
@trigger 'change', { oldRange: oldScreenRange, newRange: newScreenRange }
|
||||
|
||||
newRange = oldRange.copy()
|
||||
newRange.end.row = @lastScreenRowForBufferRow(e.newRange.end.row)
|
||||
newRange.end.column = _.last(@index.at(e.newRange.end.row).screenLines).text.length
|
||||
expandBufferRangeToLineEnds: (bufferRange) ->
|
||||
{ start, end } = bufferRange
|
||||
new Range([start.row, 0], [end.row, @lineMap.lineForBufferRow(end.row).text.length])
|
||||
|
||||
@trigger 'change', { oldRange, newRange }
|
||||
rangeForAllLines: ->
|
||||
endRow = @lineCount() - 1
|
||||
endColumn = @lineMap.lineForScreenRow(endRow).text.length
|
||||
new Range([0, 0], [endRow, endColumn])
|
||||
|
||||
firstScreenRowForBufferRow: (bufferRow) ->
|
||||
@screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
|
||||
lastScreenRowForBufferRow: (bufferRow) ->
|
||||
startRow = @screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
startRow + (@index.at(bufferRow).screenLines.length - 1)
|
||||
|
||||
buildWrappedLinesForBufferRows: (start, end) ->
|
||||
for row in [start..end]
|
||||
@buildWrappedLineForBufferRow(row)
|
||||
|
||||
buildWrappedLineForBufferRow: (bufferRow) ->
|
||||
{ screenLines: @wrapScreenLine(@highlighter.lineFragmentForRow(bufferRow)) }
|
||||
buildScreenLinesForBufferRows: (start, end) ->
|
||||
_(@lineFolder
|
||||
.linesForScreenRows(start, end)
|
||||
.map((screenLine) => @wrapScreenLine(screenLine))).flatten()
|
||||
|
||||
wrapScreenLine: (screenLine, startColumn=0) ->
|
||||
screenLines = []
|
||||
@@ -78,7 +54,7 @@ class LineWrapper
|
||||
endColumn = startColumn + screenLine.text.length
|
||||
else
|
||||
[leftHalf, rightHalf] = screenLine.splitAt(splitColumn)
|
||||
leftHalf.screenDelta = new Delta(1, 0)
|
||||
leftHalf.screenDelta = new Point(1, 0)
|
||||
screenLines.push leftHalf
|
||||
endColumn = startColumn + leftHalf.text.length
|
||||
screenLines.push @wrapScreenLine(rightHalf, endColumn)...
|
||||
@@ -100,35 +76,28 @@ class LineWrapper
|
||||
return column + 1 if /\s/.test(line[column])
|
||||
return @maxLength
|
||||
|
||||
screenRangeFromBufferRange: (bufferRange) ->
|
||||
start = @screenPositionForBufferPosition(bufferRange.start, false)
|
||||
end = @screenPositionForBufferPosition(bufferRange.end, false)
|
||||
new Range(start,end)
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.screenRangeForBufferRange(bufferRange)
|
||||
|
||||
screenPositionForBufferPosition: (bufferPosition, eagerWrap=true) ->
|
||||
@lineMap.screenPositionForBufferPosition(bufferPosition, eagerWrap)
|
||||
@lineMap.screenPositionForBufferPosition(
|
||||
@lineFolder.screenPositionForBufferPosition(bufferPosition),
|
||||
eagerWrap)
|
||||
|
||||
bufferPositionForScreenPosition: (screenPosition) ->
|
||||
@lineMap.bufferPositionForScreenPosition(screenPosition)
|
||||
@lineFolder.bufferPositionForScreenPosition(
|
||||
@lineMap.bufferPositionForScreenPosition(screenPosition))
|
||||
|
||||
screenLineForRow: (screenRow) ->
|
||||
@screenLinesForRows(screenRow, screenRow)[0]
|
||||
lineForScreenRow: (screenRow) ->
|
||||
@linesForScreenRows(screenRow, screenRow)[0]
|
||||
|
||||
screenLinesForRows: (startRow, endRow) ->
|
||||
screenLines = []
|
||||
linesForScreenRows: (startRow, endRow) ->
|
||||
@lineMap.linesForScreenRows(startRow, endRow)
|
||||
|
||||
{ values, startOffset, endOffset } = @index.sliceBySpan(startRow, endRow)
|
||||
getLines: ->
|
||||
@linesForScreenRows(0, @lineCount() - 1)
|
||||
|
||||
screenLines.push(values[0].screenLines[startOffset..-1]...)
|
||||
for wrappedLine in values[1...-1]
|
||||
screenLines.push(wrappedLine.screenLines...)
|
||||
screenLines.push(_.last(values).screenLines[0..endOffset]...)
|
||||
screenLines
|
||||
|
||||
screenLines: ->
|
||||
@screenLinesForRows(0, @screenLineCount() - 1)
|
||||
|
||||
screenLineCount: ->
|
||||
lineCount: ->
|
||||
@lineMap.screenLineCount()
|
||||
|
||||
_.extend(LineWrapper.prototype, EventEmitter)
|
||||
|
||||
@@ -11,16 +11,33 @@ class Point
|
||||
|
||||
new Point(row, column)
|
||||
|
||||
constructor: (@row, @column) ->
|
||||
constructor: (@row=0, @column=0) ->
|
||||
|
||||
isEqual: (other) ->
|
||||
if other instanceof Array
|
||||
@row == other[0] and @column == other[1]
|
||||
add: (other) ->
|
||||
row = @row + other.row
|
||||
if other.row == 0
|
||||
column = @column + other.column
|
||||
else
|
||||
@row == other.row and @column == other.column
|
||||
column = other.column
|
||||
|
||||
inspect: ->
|
||||
"(#{@row}, #{@column})"
|
||||
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
|
||||
else
|
||||
rightColumn = @column
|
||||
|
||||
[new Point(0, column), new Point(@row, rightColumn)]
|
||||
|
||||
compare: (other) ->
|
||||
if @row > other.row
|
||||
@@ -35,5 +52,22 @@ class Point
|
||||
else
|
||||
0
|
||||
|
||||
greaterThan: (other) ->
|
||||
isEqual: (other) ->
|
||||
other = Point.fromObject(other)
|
||||
@compare(other) == 0
|
||||
|
||||
isLessThan: (other) ->
|
||||
@compare(other) < 0
|
||||
|
||||
isLessThanOrEqual: (other) ->
|
||||
@compare(other) <= 0
|
||||
|
||||
isGreaterThan: (other) ->
|
||||
@compare(other) > 0
|
||||
|
||||
isGreaterThanOrEqual: (other) ->
|
||||
@compare(other) >= 0
|
||||
|
||||
inspect: ->
|
||||
"(#{@row}, #{@column})"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
Point = require 'point'
|
||||
Delta = require 'delta'
|
||||
_ = require 'underscore'
|
||||
|
||||
|
||||
@@ -37,5 +36,5 @@ class Range
|
||||
columns = @end.column - @start.column
|
||||
else
|
||||
columns = @end.column
|
||||
new Delta(rows, columns)
|
||||
new Point(rows, columns)
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
_ = require 'underscore'
|
||||
Delta = require 'delta'
|
||||
Point = require 'point'
|
||||
|
||||
module.exports =
|
||||
class ScreenLineFragment
|
||||
constructor: (@tokens, @text, screenDelta, bufferDelta) ->
|
||||
@screenDelta = Delta.fromObject(screenDelta)
|
||||
@bufferDelta = Delta.fromObject(bufferDelta)
|
||||
isAtomic: false
|
||||
|
||||
constructor: (@tokens, @text, screenDelta, bufferDelta, extraFields) ->
|
||||
@screenDelta = Point.fromObject(screenDelta)
|
||||
@bufferDelta = Point.fromObject(bufferDelta)
|
||||
_.extend(this, extraFields)
|
||||
|
||||
splitAt: (column) ->
|
||||
return [undefined, this] if column == 0
|
||||
@@ -43,5 +46,11 @@ class ScreenLineFragment
|
||||
bufferDelta = @bufferDelta.add(other.bufferDelta)
|
||||
new ScreenLineFragment(tokens, text, screenDelta, bufferDelta)
|
||||
|
||||
lengthForClipping: ->
|
||||
if @isAtomic
|
||||
0
|
||||
else
|
||||
@text.length
|
||||
|
||||
isEqual: (other) ->
|
||||
_.isEqual(@tokens, other.tokens) and @screenDelta.isEqual(other.screenDelta) and @bufferDelta.isEqual(other.bufferDelta)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class ScreenLine
|
||||
tokens: null
|
||||
text: null
|
||||
state: null
|
||||
|
||||
constructor: (@tokens, @text, @state) ->
|
||||
|
||||
pushToken: (token) ->
|
||||
@tokens.push(token)
|
||||
@text += token.value
|
||||
|
||||
concat: (otherLine) ->
|
||||
new ScreenLine(@tokens.concat(otherLine.tokens), @text + otherLine.text)
|
||||
|
||||
splitAt: (column) ->
|
||||
return [this] if column == 0 or column >= @text.length
|
||||
|
||||
rightTokens = _.clone(@tokens)
|
||||
leftTokens = []
|
||||
leftTextLength = 0
|
||||
while leftTextLength < column
|
||||
if leftTextLength + rightTokens[0].value.length > column
|
||||
rightTokens[0..0] = @splitTokenAt(rightTokens[0], column - leftTextLength)
|
||||
nextToken = rightTokens.shift()
|
||||
leftTextLength += nextToken.value.length
|
||||
leftTokens.push nextToken
|
||||
|
||||
leftLine = new ScreenLine(leftTokens, @text.substring(0, column))
|
||||
rightLine = new ScreenLine(rightTokens, @text.substring(column))
|
||||
[leftLine, rightLine]
|
||||
|
||||
splitTokenAt: (token, splitIndex) ->
|
||||
{ type, value } = token
|
||||
value1 = value.substring(0, splitIndex)
|
||||
value2 = value.substring(splitIndex)
|
||||
[{value: value1, type }, {value: value2, type}]
|
||||
@@ -1,4 +1,5 @@
|
||||
Cursor = require 'cursor'
|
||||
|
||||
Range = require 'range'
|
||||
{View, $$} = require 'space-pen'
|
||||
|
||||
@@ -61,17 +62,17 @@ class Selection extends View
|
||||
|
||||
getRange: ->
|
||||
if @anchor
|
||||
new Range(@anchor.getPosition(), @cursor.getPosition())
|
||||
new Range(@anchor.getScreenPosition(), @cursor.getScreenPosition())
|
||||
else
|
||||
new Range(@cursor.getPosition(), @cursor.getPosition())
|
||||
new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition())
|
||||
|
||||
setRange: (range) ->
|
||||
@cursor.setPosition(range.start)
|
||||
@cursor.setScreenPosition(range.start)
|
||||
@modifySelection =>
|
||||
@cursor.setPosition(range.end)
|
||||
@cursor.setScreenPosition(range.end)
|
||||
|
||||
getScreenRange: ->
|
||||
@editor.lineWrapper.screenRangeFromBufferRange(@getRange())
|
||||
@editor.lineWrapper.screenRangeForBufferRange(@getRange())
|
||||
|
||||
getText: ->
|
||||
@editor.buffer.getTextInRange @getRange()
|
||||
@@ -97,8 +98,8 @@ class Selection extends View
|
||||
|
||||
placeAnchor: ->
|
||||
return if @anchor
|
||||
cursorPosition = @cursor.getPosition()
|
||||
@anchor = { getPosition: -> cursorPosition }
|
||||
cursorPosition = @cursor.getScreenPosition()
|
||||
@anchor = { getScreenPosition: -> cursorPosition }
|
||||
|
||||
selectWord: ->
|
||||
row = @cursor.getRow()
|
||||
@@ -141,7 +142,7 @@ class Selection extends View
|
||||
|
||||
selectToPosition: (position) ->
|
||||
@modifySelection =>
|
||||
@cursor.setPosition(position)
|
||||
@cursor.setScreenPosition(position)
|
||||
|
||||
moveCursorToLineEnd: ->
|
||||
@cursor.moveToLineEnd()
|
||||
@@ -157,3 +158,6 @@ class Selection extends View
|
||||
return if @isEmpty()
|
||||
text = @editor.buffer.getTextInRange @getRange()
|
||||
atom.native.writeToPasteboard text
|
||||
|
||||
fold: ->
|
||||
@editor.lineFolder.createFold(@getRange())
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class SpanIndex
|
||||
constructor: ->
|
||||
@entries = []
|
||||
|
||||
insert: (index, spans, values) ->
|
||||
@entries[index..index] = @buildIndexEntries(spans, values)
|
||||
|
||||
replace: (index, span, value) ->
|
||||
@splice(index, index, span, [value])
|
||||
|
||||
splice: (start, end, spans, values) ->
|
||||
@entries[start..end] = @buildIndexEntries(spans, values)
|
||||
|
||||
updateSpans: (start, end, span) ->
|
||||
for i in [start..end]
|
||||
@entries[i].span = span
|
||||
|
||||
at: (index) ->
|
||||
@entries[index].value
|
||||
|
||||
last: ->
|
||||
_.last(@entries).value
|
||||
|
||||
clear: ->
|
||||
@entries = []
|
||||
|
||||
lengthBySpan: ->
|
||||
length = 0
|
||||
for entry in @entries
|
||||
length += entry.span
|
||||
length
|
||||
|
||||
sliceBySpan: (start, end) ->
|
||||
currentSpan = 0
|
||||
values = []
|
||||
|
||||
for entry in @entries
|
||||
continue if entry.span is 0
|
||||
nextSpan = currentSpan + entry.span
|
||||
if nextSpan > start
|
||||
startOffset = start - currentSpan if currentSpan <= start
|
||||
if currentSpan <= end
|
||||
values.push entry.value
|
||||
endOffset = end - currentSpan if nextSpan >= end
|
||||
else
|
||||
break
|
||||
currentSpan = nextSpan
|
||||
|
||||
{ values, startOffset, endOffset }
|
||||
|
||||
|
||||
indexForSpan: (targetSpan) ->
|
||||
currentSpan = 0
|
||||
index = 0
|
||||
offset = 0
|
||||
for entry in @entries
|
||||
nextSpan = currentSpan + entry.span
|
||||
if nextSpan > targetSpan
|
||||
offset = targetSpan - currentSpan
|
||||
return { index, offset}
|
||||
currentSpan = nextSpan
|
||||
index++
|
||||
|
||||
spanForIndex: (index) ->
|
||||
span = 0
|
||||
for i in [0..index]
|
||||
span += @entries[i].span
|
||||
span
|
||||
|
||||
buildIndexEntries: (spans, values) ->
|
||||
if _.isArray(spans)
|
||||
_.zip(spans, values).map ([span, value]) -> new SpanIndexEntry(span, value)
|
||||
else
|
||||
values.map (value) -> new SpanIndexEntry(spans, value)
|
||||
|
||||
class SpanIndexEntry
|
||||
constructor: (@span, @value) ->
|
||||
|
||||
@@ -7,27 +7,27 @@ class Motion
|
||||
|
||||
class MoveLeft extends Motion
|
||||
execute: ->
|
||||
{column, row} = @editor.getCursorPosition()
|
||||
{column, row} = @editor.getCursorScreenPosition()
|
||||
@editor.moveCursorLeft() if column > 0
|
||||
|
||||
select: ->
|
||||
position = @editor.getCursorPosition()
|
||||
position = @editor.getCursorScreenPosition()
|
||||
position.column-- if position.column > 0
|
||||
@editor.selectToPosition position
|
||||
|
||||
class MoveRight extends Motion
|
||||
execute: ->
|
||||
{column, row} = @editor.getCursorPosition()
|
||||
{column, row} = @editor.getCursorScreenPosition()
|
||||
@editor.moveCursorRight()
|
||||
|
||||
class MoveUp extends Motion
|
||||
execute: ->
|
||||
{column, row} = @editor.getCursorPosition()
|
||||
{column, row} = @editor.getCursorScreenPosition()
|
||||
@editor.moveCursorUp() if row > 0
|
||||
|
||||
class MoveDown extends Motion
|
||||
execute: ->
|
||||
{column, row} = @editor.getCursorPosition()
|
||||
{column, row} = @editor.getCursorScreenPosition()
|
||||
@editor.moveCursorDown() if row < (@editor.buffer.numLines() - 1)
|
||||
|
||||
class MoveToPreviousWord extends Motion
|
||||
@@ -39,14 +39,14 @@ class MoveToPreviousWord extends Motion
|
||||
|
||||
class MoveToNextWord extends Motion
|
||||
execute: ->
|
||||
@editor.setCursorPosition(@nextWordPosition())
|
||||
@editor.setCursorScreenPosition(@nextWordPosition())
|
||||
|
||||
select: ->
|
||||
@editor.selectToPosition(@nextWordPosition())
|
||||
|
||||
nextWordPosition: ->
|
||||
regex = getWordRegex()
|
||||
{ row, column } = @editor.getCursorPosition()
|
||||
{ row, column } = @editor.getCursorScreenPosition()
|
||||
rightOfCursor = @editor.buffer.getLine(row).substring(column)
|
||||
|
||||
match = regex.exec(rightOfCursor)
|
||||
@@ -64,7 +64,7 @@ class MoveToNextWord extends Motion
|
||||
|
||||
class MoveToNextParagraph extends Motion
|
||||
execute: ->
|
||||
@editor.setCursorPosition(@nextPosition())
|
||||
@editor.setCursorScreenPosition(@nextPosition())
|
||||
|
||||
select: ->
|
||||
@editor.selectToPosition(@nextPosition())
|
||||
|
||||
@@ -44,7 +44,7 @@ class Delete
|
||||
@editor.getSelection().delete()
|
||||
else
|
||||
@editor.buffer.deleteRow(@editor.getCursorRow())
|
||||
@editor.setCursorPosition([@editor.getCursorRow(), 0])
|
||||
@editor.setCursorScreenPosition([@editor.getCursorRow(), 0])
|
||||
|
||||
compose: (motion) ->
|
||||
if not motion.select
|
||||
|
||||
Reference in New Issue
Block a user