WIP: Start rendering lines from DisplayLayers

This commit is contained in:
Nathan Sobo
2015-12-14 13:55:29 -07:00
parent 18f2f28ad4
commit 924d880fa8
4 changed files with 113 additions and 63 deletions

View File

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

View File

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

View File

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