mirror of
https://github.com/atom/atom.git
synced 2026-02-09 06:05:11 -05:00
WIP: Start rendering lines from DisplayLayers
This commit is contained in:
@@ -14,7 +14,6 @@ cloneObject = (object) ->
|
||||
module.exports =
|
||||
class LinesTileComponent
|
||||
constructor: ({@presenter, @id, @domElementPool, @assert, grammars}) ->
|
||||
@tokenIterator = new TokenIterator(grammarRegistry: grammars)
|
||||
@measuredLines = new Set
|
||||
@lineNodesByLineId = {}
|
||||
@screenRowsByLineId = {}
|
||||
@@ -125,8 +124,7 @@ class LinesTileComponent
|
||||
screenRowForNode: (node) -> parseInt(node.dataset.screenRow)
|
||||
|
||||
buildLineNode: (id) ->
|
||||
{width} = @newState
|
||||
{screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newTileState.lines[id]
|
||||
{screenRow, words, decorationClasses} = @newTileState.lines[id]
|
||||
|
||||
lineNode = @domElementPool.buildElement("div", "line")
|
||||
lineNode.dataset.screenRow = screenRow
|
||||
@@ -136,13 +134,13 @@ class LinesTileComponent
|
||||
lineNode.classList.add(decorationClass)
|
||||
|
||||
@currentLineTextNodes = []
|
||||
if text is ""
|
||||
@setEmptyLineInnerNodes(id, lineNode)
|
||||
else
|
||||
@setLineInnerNodes(id, lineNode)
|
||||
# if words.length is 0
|
||||
# @setEmptyLineInnerNodes(id, lineNode)
|
||||
|
||||
@setLineInnerNodes(id, lineNode)
|
||||
@textNodesByLineId[id] = @currentLineTextNodes
|
||||
|
||||
lineNode.appendChild(@domElementPool.buildElement("span", "fold-marker")) if fold
|
||||
# lineNode.appendChild(@domElementPool.buildElement("span", "fold-marker")) if fold
|
||||
lineNode
|
||||
|
||||
setEmptyLineInnerNodes: (id, lineNode) ->
|
||||
@@ -184,48 +182,61 @@ class LinesTileComponent
|
||||
@currentLineTextNodes.push(textNode)
|
||||
|
||||
setLineInnerNodes: (id, lineNode) ->
|
||||
lineState = @newTileState.lines[id]
|
||||
{firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, invisibles} = lineState
|
||||
lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0
|
||||
{words} = @newTileState.lines[id]
|
||||
lineLength = 0
|
||||
for word in words
|
||||
lineLength += word.length
|
||||
textNode = @domElementPool.buildText(word)
|
||||
lineNode.appendChild(textNode)
|
||||
@currentLineTextNodes.push(textNode)
|
||||
|
||||
@tokenIterator.reset(lineState)
|
||||
openScopeNode = lineNode
|
||||
if lineLength is 0
|
||||
textNode = @domElementPool.buildText('\u00a0')
|
||||
lineNode.appendChild(textNode)
|
||||
@currentLineTextNodes.push(textNode)
|
||||
|
||||
while @tokenIterator.next()
|
||||
for scope in @tokenIterator.getScopeEnds()
|
||||
openScopeNode = openScopeNode.parentElement
|
||||
|
||||
for scope in @tokenIterator.getScopeStarts()
|
||||
newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' '))
|
||||
openScopeNode.appendChild(newScopeNode)
|
||||
openScopeNode = newScopeNode
|
||||
|
||||
tokenStart = @tokenIterator.getScreenStart()
|
||||
tokenEnd = @tokenIterator.getScreenEnd()
|
||||
tokenText = @tokenIterator.getText()
|
||||
isHardTab = @tokenIterator.isHardTab()
|
||||
|
||||
if hasLeadingWhitespace = tokenStart < firstNonWhitespaceIndex
|
||||
tokenFirstNonWhitespaceIndex = firstNonWhitespaceIndex - tokenStart
|
||||
else
|
||||
tokenFirstNonWhitespaceIndex = null
|
||||
|
||||
if hasTrailingWhitespace = tokenEnd > firstTrailingWhitespaceIndex
|
||||
tokenFirstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - tokenStart)
|
||||
else
|
||||
tokenFirstTrailingWhitespaceIndex = null
|
||||
|
||||
hasIndentGuide =
|
||||
@newState.indentGuidesVisible and
|
||||
(hasLeadingWhitespace or lineIsWhitespaceOnly)
|
||||
|
||||
hasInvisibleCharacters =
|
||||
(invisibles?.tab and isHardTab) or
|
||||
(invisibles?.space and (hasLeadingWhitespace or hasTrailingWhitespace))
|
||||
|
||||
@appendTokenNodes(tokenText, isHardTab, tokenFirstNonWhitespaceIndex, tokenFirstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, openScopeNode)
|
||||
|
||||
@appendEndOfLineNodes(id, lineNode)
|
||||
# lineState = @newTileState.lines[id]
|
||||
# {firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, invisibles} = lineState
|
||||
# lineIsWhitespaceOnly = firstTrailingWhitespaceIndex is 0
|
||||
#
|
||||
# @tokenIterator.reset(lineState)
|
||||
# openScopeNode = lineNode
|
||||
#
|
||||
# while @tokenIterator.next()
|
||||
# for scope in @tokenIterator.getScopeEnds()
|
||||
# openScopeNode = openScopeNode.parentElement
|
||||
#
|
||||
# for scope in @tokenIterator.getScopeStarts()
|
||||
# newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' '))
|
||||
# openScopeNode.appendChild(newScopeNode)
|
||||
# openScopeNode = newScopeNode
|
||||
#
|
||||
# tokenStart = @tokenIterator.getScreenStart()
|
||||
# tokenEnd = @tokenIterator.getScreenEnd()
|
||||
# tokenText = @tokenIterator.getText()
|
||||
# isHardTab = @tokenIterator.isHardTab()
|
||||
#
|
||||
# if hasLeadingWhitespace = tokenStart < firstNonWhitespaceIndex
|
||||
# tokenFirstNonWhitespaceIndex = firstNonWhitespaceIndex - tokenStart
|
||||
# else
|
||||
# tokenFirstNonWhitespaceIndex = null
|
||||
#
|
||||
# if hasTrailingWhitespace = tokenEnd > firstTrailingWhitespaceIndex
|
||||
# tokenFirstTrailingWhitespaceIndex = Math.max(0, firstTrailingWhitespaceIndex - tokenStart)
|
||||
# else
|
||||
# tokenFirstTrailingWhitespaceIndex = null
|
||||
#
|
||||
# hasIndentGuide =
|
||||
# @newState.indentGuidesVisible and
|
||||
# (hasLeadingWhitespace or lineIsWhitespaceOnly)
|
||||
#
|
||||
# hasInvisibleCharacters =
|
||||
# (invisibles?.tab and isHardTab) or
|
||||
# (invisibles?.space and (hasLeadingWhitespace or hasTrailingWhitespace))
|
||||
#
|
||||
# @appendTokenNodes(tokenText, isHardTab, tokenFirstNonWhitespaceIndex, tokenFirstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, openScopeNode)
|
||||
#
|
||||
# @appendEndOfLineNodes(id, lineNode)
|
||||
|
||||
appendTokenNodes: (tokenText, isHardTab, firstNonWhitespaceIndex, firstTrailingWhitespaceIndex, hasIndentGuide, hasInvisibleCharacters, scopeNode) ->
|
||||
if isHardTab
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{CompositeDisposable, Disposable, Emitter} = require 'event-kit'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
MarkerIndex = require 'marker-index'
|
||||
_ = require 'underscore-plus'
|
||||
Decoration = require './decoration'
|
||||
|
||||
@@ -16,6 +17,7 @@ class TextEditorPresenter
|
||||
{@model, @config} = params
|
||||
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params
|
||||
{@contentFrameWidth} = params
|
||||
@tokenIterator = @model.displayLayer.buildTokenIterator()
|
||||
|
||||
@gutterWidth = 0
|
||||
@tileSize ?= 6
|
||||
@@ -23,6 +25,9 @@ class TextEditorPresenter
|
||||
@realScrollLeft = @scrollLeft
|
||||
@disposables = new CompositeDisposable
|
||||
@emitter = new Emitter
|
||||
@lineIdCounter = 1
|
||||
@linesById = new Map
|
||||
@lineMarkerIndex = new MarkerIndex
|
||||
@visibleHighlights = {}
|
||||
@characterWidthsByScope = {}
|
||||
@lineDecorationsByScreenRow = {}
|
||||
@@ -82,6 +87,8 @@ class TextEditorPresenter
|
||||
@updateCommonGutterState()
|
||||
@updateReflowState()
|
||||
|
||||
@updateLines()
|
||||
|
||||
if @shouldUpdateDecorations
|
||||
@fetchDecorations()
|
||||
@updateLineDecorations()
|
||||
@@ -126,7 +133,8 @@ class TextEditorPresenter
|
||||
@shouldUpdateDecorations = true
|
||||
|
||||
observeModel: ->
|
||||
@disposables.add @model.onDidChange =>
|
||||
@disposables.add @model.displayLayer.onDidChangeTextSync (change) =>
|
||||
@invalidateLines(change)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@@ -375,7 +383,7 @@ class TextEditorPresenter
|
||||
tileState.lines ?= {}
|
||||
visibleLineIds = {}
|
||||
for screenRow in screenRows
|
||||
line = @model.tokenizedLineForScreenRow(screenRow)
|
||||
line = @lineForScreenRow(screenRow)
|
||||
unless line?
|
||||
throw new Error("No line exists for row #{screenRow}. Last screen row: #{@model.getLastScreenRow()}")
|
||||
|
||||
@@ -387,18 +395,7 @@ class TextEditorPresenter
|
||||
else
|
||||
tileState.lines[line.id] =
|
||||
screenRow: screenRow
|
||||
text: line.text
|
||||
openScopes: line.openScopes
|
||||
tags: line.tags
|
||||
specialTokens: line.specialTokens
|
||||
firstNonWhitespaceIndex: line.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line.firstTrailingWhitespaceIndex
|
||||
invisibles: line.invisibles
|
||||
endOfLineInvisibles: line.endOfLineInvisibles
|
||||
isOnlyWhitespace: line.isOnlyWhitespace()
|
||||
indentLevel: line.indentLevel
|
||||
tabLength: line.tabLength
|
||||
fold: line.fold
|
||||
words: line.words
|
||||
decorationClasses: @lineDecorationClassesForRow(screenRow)
|
||||
|
||||
for id, line of tileState.lines
|
||||
@@ -1010,6 +1007,46 @@ class TextEditorPresenter
|
||||
rect.height = Math.round(rect.height)
|
||||
rect
|
||||
|
||||
updateLines: ->
|
||||
visibleLineIds = new Set
|
||||
|
||||
for screenRow in @getScreenRows()
|
||||
screenRowStart = Point(screenRow, 0)
|
||||
lineIds = @lineMarkerIndex.findStartingAt(screenRowStart)
|
||||
if lineIds.size is 0
|
||||
line = @buildLine(screenRow)
|
||||
@lineMarkerIndex.insert(line.id, screenRowStart, Point(screenRow, Infinity))
|
||||
@linesById.set(line.id, line)
|
||||
visibleLineIds.add(line.id)
|
||||
else
|
||||
lineIds.forEach (id) ->
|
||||
visibleLineIds.add(id)
|
||||
|
||||
@linesById.forEach (line, lineId) =>
|
||||
unless visibleLineIds.has(lineId)
|
||||
@lineMarkerIndex.delete(lineId)
|
||||
@linesById.delete(lineId)
|
||||
|
||||
buildLine: (screenRow) ->
|
||||
line = {id: @lineIdCounter++, words: []}
|
||||
@tokenIterator.seekToScreenRow(screenRow)
|
||||
loop
|
||||
line.words.push(@tokenIterator.getText())
|
||||
break unless @tokenIterator.moveToSuccessor()
|
||||
break unless @tokenIterator.getStartScreenPosition().row is screenRow
|
||||
line
|
||||
|
||||
invalidateLines: ({start, replacedExtent, replacementExtent}) ->
|
||||
{touch} = @lineMarkerIndex.splice(start, replacedExtent, replacementExtent)
|
||||
touch.forEach (lineId) =>
|
||||
@lineMarkerIndex.delete(lineId)
|
||||
@linesById.delete(lineId)
|
||||
|
||||
lineForScreenRow: (screenRow) ->
|
||||
lineIds = @lineMarkerIndex.findStartingAt(Point(screenRow, 0))
|
||||
lineId = lineIds.values().next().value
|
||||
@linesById.get(lineId)
|
||||
|
||||
fetchDecorations: ->
|
||||
return unless 0 <= @startRow <= @endRow <= Infinity
|
||||
@decorations = @model.decorationsStateForScreenRowRange(@startRow, @endRow - 1)
|
||||
@@ -1236,7 +1273,7 @@ class TextEditorPresenter
|
||||
startBlinkingCursors: ->
|
||||
unless @isCursorBlinking()
|
||||
@state.content.cursorsVisible = true
|
||||
@toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2)
|
||||
# @toggleCursorBlinkHandle = setInterval(@toggleCursorBlink.bind(this), @getCursorBlinkPeriod() / 2)
|
||||
|
||||
isCursorBlinking: ->
|
||||
@toggleCursorBlinkHandle?
|
||||
|
||||
@@ -117,6 +117,7 @@ class TextEditor extends Model
|
||||
@config, @assert, @grammarRegistry, @packageManager
|
||||
})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@displayLayer = buffer.addDisplayLayer({tabLength})
|
||||
@selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true)
|
||||
|
||||
for marker in @selectionsMarkerLayer.getMarkers()
|
||||
|
||||
Reference in New Issue
Block a user