mirror of
https://github.com/atom/atom.git
synced 2026-02-13 08:04:56 -05:00
WIP: Use rake to start compiling resources (like require.coffee)
This commit is contained in:
281
src/app/display-buffer.coffee
Normal file
281
src/app/display-buffer.coffee
Normal file
@@ -0,0 +1,281 @@
|
||||
_ = require 'underscore'
|
||||
TokenizedBuffer = require 'tokenized-buffer'
|
||||
LineMap = require 'line-map'
|
||||
Point = require 'point'
|
||||
EventEmitter = require 'event-emitter'
|
||||
Range = require 'range'
|
||||
Fold = require 'fold'
|
||||
ScreenLine = require 'screen-line'
|
||||
Token = require 'token'
|
||||
|
||||
module.exports =
|
||||
class DisplayBuffer
|
||||
@idCounter: 1
|
||||
lineMap: null
|
||||
languageMode: null
|
||||
tokenizedBuffer: null
|
||||
activeFolds: null
|
||||
foldsById: null
|
||||
lastTokenizedBufferChangeEvent: null
|
||||
|
||||
constructor: (@buffer, options={}) ->
|
||||
@id = @constructor.idCounter++
|
||||
options.tabText ?= ' '
|
||||
@languageMode = options.languageMode
|
||||
@tokenizedBuffer = new TokenizedBuffer(@buffer, options)
|
||||
@softWrapColumn = options.softWrapColumn ? Infinity
|
||||
@activeFolds = {}
|
||||
@foldsById = {}
|
||||
@buildLineMap()
|
||||
@tokenizedBuffer.on 'change', (e) => @lastTokenizedBufferChangeEvent = e
|
||||
@buffer.on "change.displayBuffer#{@id}", (e) => @handleBufferChange(e)
|
||||
|
||||
buildLineMap: ->
|
||||
@lineMap = new LineMap
|
||||
@lineMap.insertAtBufferRow 0, @buildLinesForBufferRows(0, @buffer.getLastRow())
|
||||
|
||||
setSoftWrapColumn: (@softWrapColumn) ->
|
||||
oldRange = @rangeForAllLines()
|
||||
@buildLineMap()
|
||||
newRange = @rangeForAllLines()
|
||||
@trigger 'change', { oldRange, newRange, lineNumbersChanged: true }
|
||||
|
||||
lineForRow: (row) ->
|
||||
@lineMap.lineForScreenRow(row)
|
||||
|
||||
linesForRows: (startRow, endRow) ->
|
||||
@lineMap.linesForScreenRows(startRow, endRow)
|
||||
|
||||
getLines: ->
|
||||
@lineMap.linesForScreenRows(0, @lineMap.lastScreenRow())
|
||||
|
||||
bufferRowsForScreenRows: (startRow, endRow) ->
|
||||
@lineMap.bufferRowsForScreenRows(startRow, endRow)
|
||||
|
||||
foldAll: ->
|
||||
for currentRow in [0..@buffer.getLastRow()]
|
||||
[startRow, endRow] = @languageMode.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow?
|
||||
|
||||
@createFold(startRow, endRow)
|
||||
|
||||
unfoldAll: ->
|
||||
for row in [@buffer.getLastRow()..0]
|
||||
@activeFolds[row]?.forEach (fold) => @destroyFold(fold)
|
||||
|
||||
foldBufferRow: (bufferRow) ->
|
||||
for currentRow in [bufferRow..0]
|
||||
[startRow, endRow] = @languageMode.rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow? and startRow <= bufferRow <= endRow
|
||||
fold = @largestFoldStartingAtBufferRow(startRow)
|
||||
continue if fold
|
||||
|
||||
@createFold(startRow, endRow)
|
||||
|
||||
return
|
||||
|
||||
unfoldBufferRow: (bufferRow) ->
|
||||
@largestFoldContainingBufferRow(bufferRow)?.destroy()
|
||||
|
||||
createFold: (startRow, endRow) ->
|
||||
return fold if fold = @foldFor(startRow, endRow)
|
||||
fold = new Fold(this, startRow, endRow)
|
||||
@registerFold(fold)
|
||||
|
||||
unless @isFoldContainedByActiveFold(fold)
|
||||
bufferRange = new Range([startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)])
|
||||
oldScreenRange = @screenLineRangeForBufferRange(bufferRange)
|
||||
|
||||
lines = @buildLineForBufferRow(startRow)
|
||||
@lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines)
|
||||
newScreenRange = @screenLineRangeForBufferRange(bufferRange)
|
||||
|
||||
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange, lineNumbersChanged: true
|
||||
|
||||
fold
|
||||
|
||||
isFoldContainedByActiveFold: (fold) ->
|
||||
for row, folds of @activeFolds
|
||||
for otherFold in folds
|
||||
return otherFold if fold != otherFold and fold.isContainedByFold(otherFold)
|
||||
|
||||
foldFor: (startRow, endRow) ->
|
||||
_.find @activeFolds[startRow] ? [], (fold) ->
|
||||
fold.startRow == startRow and fold.endRow == endRow
|
||||
|
||||
destroyFold: (fold) ->
|
||||
@unregisterFold(fold.startRow, fold)
|
||||
|
||||
unless @isFoldContainedByActiveFold(fold)
|
||||
{ startRow, endRow } = fold
|
||||
bufferRange = new Range([startRow, 0], [endRow, @buffer.lineLengthForRow(endRow)])
|
||||
oldScreenRange = @screenLineRangeForBufferRange(bufferRange)
|
||||
lines = @buildLinesForBufferRows(startRow, endRow)
|
||||
@lineMap.replaceScreenRows(oldScreenRange.start.row, oldScreenRange.end.row, lines)
|
||||
newScreenRange = @screenLineRangeForBufferRange(bufferRange)
|
||||
|
||||
@trigger 'change', oldRange: oldScreenRange, newRange: newScreenRange, lineNumbersChanged: true
|
||||
|
||||
destroyFoldsContainingBufferRow: (bufferRow) ->
|
||||
for row, folds of @activeFolds
|
||||
for fold in new Array(folds...)
|
||||
fold.destroy() if fold.getBufferRange().containsRow(bufferRow)
|
||||
|
||||
registerFold: (fold) ->
|
||||
@activeFolds[fold.startRow] ?= []
|
||||
@activeFolds[fold.startRow].push(fold)
|
||||
@foldsById[fold.id] = fold
|
||||
|
||||
unregisterFold: (bufferRow, fold) ->
|
||||
folds = @activeFolds[bufferRow]
|
||||
_.remove(folds, fold)
|
||||
delete @foldsById[fold.id]
|
||||
delete @activeFolds[bufferRow] if folds.length == 0
|
||||
|
||||
largestFoldStartingAtBufferRow: (bufferRow) ->
|
||||
return unless folds = @activeFolds[bufferRow]
|
||||
(folds.sort (a, b) -> b.endRow - a.endRow)[0]
|
||||
|
||||
largestFoldStartingAtScreenRow: (screenRow) ->
|
||||
@largestFoldStartingAtBufferRow(@bufferRowForScreenRow(screenRow))
|
||||
|
||||
largestFoldContainingBufferRow: (bufferRow) ->
|
||||
largestFold = null
|
||||
for currentBufferRow in [bufferRow..0]
|
||||
if fold = @largestFoldStartingAtBufferRow(currentBufferRow)
|
||||
largestFold = fold if fold.endRow >= bufferRow
|
||||
largestFold
|
||||
|
||||
screenLineRangeForBufferRange: (bufferRange) ->
|
||||
@expandScreenRangeToLineEnds(
|
||||
@lineMap.screenRangeForBufferRange(
|
||||
@expandBufferRangeToLineEnds(bufferRange)))
|
||||
|
||||
screenRowForBufferRow: (bufferRow) ->
|
||||
@lineMap.screenPositionForBufferPosition([bufferRow, 0]).row
|
||||
|
||||
bufferRowForScreenRow: (screenRow) ->
|
||||
@lineMap.bufferPositionForScreenPosition([screenRow, 0]).row
|
||||
|
||||
screenRangeForBufferRange: (bufferRange) ->
|
||||
@lineMap.screenRangeForBufferRange(bufferRange)
|
||||
|
||||
bufferRangeForScreenRange: (screenRange) ->
|
||||
@lineMap.bufferRangeForScreenRange(screenRange)
|
||||
|
||||
lineCount: ->
|
||||
@lineMap.screenLineCount()
|
||||
|
||||
getLastRow: ->
|
||||
@lineCount() - 1
|
||||
|
||||
maxLineLength: ->
|
||||
@lineMap.maxScreenLineLength()
|
||||
|
||||
screenPositionForBufferPosition: (position, options) ->
|
||||
@lineMap.screenPositionForBufferPosition(position, options)
|
||||
|
||||
bufferPositionForScreenPosition: (position, options) ->
|
||||
@lineMap.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
stateForScreenRow: (screenRow) ->
|
||||
@tokenizedBuffer.stackForRow(screenRow)
|
||||
|
||||
clipScreenPosition: (position, options) ->
|
||||
@lineMap.clipScreenPosition(position, options)
|
||||
|
||||
handleBufferChange: (e) ->
|
||||
allFolds = [] # Folds can modify @activeFolds, so first make sure we have a stable array of folds
|
||||
allFolds.push(folds...) for row, folds of @activeFolds
|
||||
fold.handleBufferChange(e) for fold in allFolds
|
||||
|
||||
@handleTokenizedBufferChange(@lastTokenizedBufferChangeEvent)
|
||||
|
||||
handleTokenizedBufferChange: (e) ->
|
||||
newRange = e.newRange.copy()
|
||||
newRange.start.row = @bufferRowForScreenRow(@screenRowForBufferRow(newRange.start.row))
|
||||
|
||||
oldScreenRange = @screenLineRangeForBufferRange(e.oldRange)
|
||||
|
||||
newScreenLines = @buildLinesForBufferRows(newRange.start.row, newRange.end.row)
|
||||
@lineMap.replaceScreenRows oldScreenRange.start.row, oldScreenRange.end.row, newScreenLines
|
||||
newScreenRange = @screenLineRangeForBufferRange(newRange)
|
||||
|
||||
@trigger 'change',
|
||||
oldRange: oldScreenRange
|
||||
newRange: newScreenRange
|
||||
bufferChanged: true
|
||||
lineNumbersChanged: !e.oldRange.coversSameRows(newRange) or !oldScreenRange.coversSameRows(newScreenRange)
|
||||
|
||||
buildLineForBufferRow: (bufferRow) ->
|
||||
@buildLinesForBufferRows(bufferRow, bufferRow)
|
||||
|
||||
buildLinesForBufferRows: (startBufferRow, endBufferRow) ->
|
||||
lineFragments = []
|
||||
startBufferColumn = null
|
||||
currentBufferRow = startBufferRow
|
||||
currentScreenLineLength = 0
|
||||
|
||||
startBufferColumn = 0
|
||||
while currentBufferRow <= endBufferRow
|
||||
screenLine = @tokenizedBuffer.lineForScreenRow(currentBufferRow)
|
||||
screenLine.foldable = @languageMode.doesBufferRowStartFold(currentBufferRow)
|
||||
|
||||
if fold = @largestFoldStartingAtBufferRow(currentBufferRow)
|
||||
screenLine = screenLine.copy()
|
||||
screenLine.fold = fold
|
||||
screenLine.bufferDelta = fold.getBufferDelta()
|
||||
lineFragments.push(screenLine)
|
||||
currentBufferRow = fold.endRow + 1
|
||||
continue
|
||||
|
||||
startBufferColumn ?= 0
|
||||
screenLine = screenLine.splitAt(startBufferColumn)[1] if startBufferColumn > 0
|
||||
wrapScreenColumn = @findWrapColumn(screenLine.text, @softWrapColumn)
|
||||
if wrapScreenColumn?
|
||||
screenLine = screenLine.splitAt(wrapScreenColumn)[0]
|
||||
screenLine.screenDelta = new Point(1, 0)
|
||||
startBufferColumn += wrapScreenColumn
|
||||
else
|
||||
currentBufferRow++
|
||||
startBufferColumn = 0
|
||||
|
||||
lineFragments.push(screenLine)
|
||||
|
||||
lineFragments
|
||||
|
||||
findWrapColumn: (line, softWrapColumn) ->
|
||||
return unless line.length > softWrapColumn
|
||||
|
||||
if /\s/.test(line[softWrapColumn])
|
||||
# search forward for the start of a word past the boundary
|
||||
for column in [softWrapColumn..line.length]
|
||||
return column if /\S/.test(line[column])
|
||||
return line.length
|
||||
else
|
||||
# search backward for the start of the word on the boundary
|
||||
for column in [softWrapColumn..0]
|
||||
return column + 1 if /\s/.test(line[column])
|
||||
return softWrapColumn
|
||||
|
||||
expandScreenRangeToLineEnds: (screenRange) ->
|
||||
screenRange = Range.fromObject(screenRange)
|
||||
{ start, end } = screenRange
|
||||
new Range([start.row, 0], [end.row, @lineMap.lineForScreenRow(end.row).text.length])
|
||||
|
||||
expandBufferRangeToLineEnds: (bufferRange) ->
|
||||
bufferRange = Range.fromObject(bufferRange)
|
||||
{ start, end } = bufferRange
|
||||
new Range([start.row, 0], [end.row, Infinity])
|
||||
|
||||
rangeForAllLines: ->
|
||||
new Range([0, 0], @clipScreenPosition([Infinity, Infinity]))
|
||||
|
||||
destroy: ->
|
||||
@tokenizedBuffer.destroy()
|
||||
@buffer.off ".displayBuffer#{@id}"
|
||||
|
||||
logLines: (start, end) ->
|
||||
@lineMap.logLines(start, end)
|
||||
|
||||
_.extend DisplayBuffer.prototype, EventEmitter
|
||||
Reference in New Issue
Block a user