mirror of
https://github.com/atom/atom.git
synced 2026-01-26 23:38:48 -05:00
Rewrite LinesTileComponent in JavaScript
This commit is contained in:
@@ -1,288 +0,0 @@
|
||||
HighlightsComponent = require './highlights-component'
|
||||
ZERO_WIDTH_NBSP = '\ufeff'
|
||||
|
||||
cloneObject = (object) ->
|
||||
clone = {}
|
||||
clone[key] = value for key, value of object
|
||||
clone
|
||||
|
||||
module.exports =
|
||||
class LinesTileComponent
|
||||
constructor: ({@presenter, @id, @domElementPool, @assert}) ->
|
||||
@measuredLines = new Set
|
||||
@lineNodesByLineId = {}
|
||||
@screenRowsByLineId = {}
|
||||
@lineIdsByScreenRow = {}
|
||||
@textNodesByLineId = {}
|
||||
@insertionPointsBeforeLineById = {}
|
||||
@insertionPointsAfterLineById = {}
|
||||
@domNode = @domElementPool.buildElement("div")
|
||||
@domNode.style.position = "absolute"
|
||||
@domNode.style.display = "block"
|
||||
|
||||
@highlightsComponent = new HighlightsComponent(@domElementPool)
|
||||
@domNode.appendChild(@highlightsComponent.getDomNode())
|
||||
|
||||
destroy: ->
|
||||
@domElementPool.freeElementAndDescendants(@domNode)
|
||||
|
||||
getDomNode: ->
|
||||
@domNode
|
||||
|
||||
updateSync: (state) ->
|
||||
@newState = state
|
||||
unless @oldState
|
||||
@oldState = {tiles: {}}
|
||||
@oldState.tiles[@id] = {lines: {}}
|
||||
|
||||
@newTileState = @newState.tiles[@id]
|
||||
@oldTileState = @oldState.tiles[@id]
|
||||
|
||||
if @newState.backgroundColor isnt @oldState.backgroundColor
|
||||
@domNode.style.backgroundColor = @newState.backgroundColor
|
||||
@oldState.backgroundColor = @newState.backgroundColor
|
||||
|
||||
if @newTileState.zIndex isnt @oldTileState.zIndex
|
||||
@domNode.style.zIndex = @newTileState.zIndex
|
||||
@oldTileState.zIndex = @newTileState.zIndex
|
||||
|
||||
if @newTileState.display isnt @oldTileState.display
|
||||
@domNode.style.display = @newTileState.display
|
||||
@oldTileState.display = @newTileState.display
|
||||
|
||||
if @newTileState.height isnt @oldTileState.height
|
||||
@domNode.style.height = @newTileState.height + 'px'
|
||||
@oldTileState.height = @newTileState.height
|
||||
|
||||
if @newState.width isnt @oldState.width
|
||||
@domNode.style.width = @newState.width + 'px'
|
||||
@oldTileState.width = @newTileState.width
|
||||
|
||||
if @newTileState.top isnt @oldTileState.top or @newTileState.left isnt @oldTileState.left
|
||||
@domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)"
|
||||
@oldTileState.top = @newTileState.top
|
||||
@oldTileState.left = @newTileState.left
|
||||
|
||||
@updateLineNodes()
|
||||
|
||||
@highlightsComponent.updateSync(@newTileState)
|
||||
|
||||
removeLineNodes: ->
|
||||
@removeLineNode(id) for id of @oldTileState.lines
|
||||
return
|
||||
|
||||
removeLineNode: (id) ->
|
||||
@domElementPool.freeElementAndDescendants(@lineNodesByLineId[id])
|
||||
@removeBlockDecorationInsertionPointBeforeLine(id)
|
||||
@removeBlockDecorationInsertionPointAfterLine(id)
|
||||
|
||||
delete @lineNodesByLineId[id]
|
||||
delete @textNodesByLineId[id]
|
||||
delete @lineIdsByScreenRow[@screenRowsByLineId[id]]
|
||||
delete @screenRowsByLineId[id]
|
||||
delete @oldTileState.lines[id]
|
||||
|
||||
updateLineNodes: ->
|
||||
for id of @oldTileState.lines
|
||||
unless @newTileState.lines.hasOwnProperty(id)
|
||||
@removeLineNode(id)
|
||||
|
||||
newLineIds = null
|
||||
newLineNodes = null
|
||||
|
||||
for id, lineState of @newTileState.lines
|
||||
if @oldTileState.lines.hasOwnProperty(id)
|
||||
@updateLineNode(id)
|
||||
else
|
||||
newLineIds ?= []
|
||||
newLineNodes ?= []
|
||||
newLineIds.push(id)
|
||||
newLineNodes.push(@buildLineNode(id))
|
||||
@screenRowsByLineId[id] = lineState.screenRow
|
||||
@lineIdsByScreenRow[lineState.screenRow] = id
|
||||
@oldTileState.lines[id] = cloneObject(lineState)
|
||||
|
||||
return unless newLineIds?
|
||||
|
||||
for id, i in newLineIds
|
||||
lineNode = newLineNodes[i]
|
||||
@lineNodesByLineId[id] = lineNode
|
||||
if nextNode = @findNodeNextTo(lineNode)
|
||||
@domNode.insertBefore(lineNode, nextNode)
|
||||
else
|
||||
@domNode.appendChild(lineNode)
|
||||
|
||||
@insertBlockDecorationInsertionPointBeforeLine(id)
|
||||
@insertBlockDecorationInsertionPointAfterLine(id)
|
||||
|
||||
removeBlockDecorationInsertionPointBeforeLine: (id) ->
|
||||
if insertionPoint = @insertionPointsBeforeLineById[id]
|
||||
@domElementPool.freeElementAndDescendants(insertionPoint)
|
||||
delete @insertionPointsBeforeLineById[id]
|
||||
|
||||
insertBlockDecorationInsertionPointBeforeLine: (id) ->
|
||||
{hasPrecedingBlockDecorations, screenRow} = @newTileState.lines[id]
|
||||
|
||||
if hasPrecedingBlockDecorations
|
||||
lineNode = @lineNodesByLineId[id]
|
||||
insertionPoint = @domElementPool.buildElement("content")
|
||||
@domNode.insertBefore(insertionPoint, lineNode)
|
||||
@insertionPointsBeforeLineById[id] = insertionPoint
|
||||
insertionPoint.dataset.screenRow = screenRow
|
||||
@updateBlockDecorationInsertionPointBeforeLine(id)
|
||||
|
||||
updateBlockDecorationInsertionPointBeforeLine: (id) ->
|
||||
oldLineState = @oldTileState.lines[id]
|
||||
newLineState = @newTileState.lines[id]
|
||||
insertionPoint = @insertionPointsBeforeLineById[id]
|
||||
return unless insertionPoint?
|
||||
|
||||
if newLineState.screenRow isnt oldLineState.screenRow
|
||||
insertionPoint.dataset.screenRow = newLineState.screenRow
|
||||
|
||||
precedingBlockDecorationsSelector = newLineState.precedingBlockDecorations.map((d) -> ".atom--block-decoration-#{d.id}").join(',')
|
||||
|
||||
if precedingBlockDecorationsSelector isnt oldLineState.precedingBlockDecorationsSelector
|
||||
insertionPoint.setAttribute("select", precedingBlockDecorationsSelector)
|
||||
oldLineState.precedingBlockDecorationsSelector = precedingBlockDecorationsSelector
|
||||
|
||||
removeBlockDecorationInsertionPointAfterLine: (id) ->
|
||||
if insertionPoint = @insertionPointsAfterLineById[id]
|
||||
@domElementPool.freeElementAndDescendants(insertionPoint)
|
||||
delete @insertionPointsAfterLineById[id]
|
||||
|
||||
insertBlockDecorationInsertionPointAfterLine: (id) ->
|
||||
{hasFollowingBlockDecorations, screenRow} = @newTileState.lines[id]
|
||||
|
||||
if hasFollowingBlockDecorations
|
||||
lineNode = @lineNodesByLineId[id]
|
||||
insertionPoint = @domElementPool.buildElement("content")
|
||||
@domNode.insertBefore(insertionPoint, lineNode.nextSibling)
|
||||
@insertionPointsAfterLineById[id] = insertionPoint
|
||||
insertionPoint.dataset.screenRow = screenRow
|
||||
@updateBlockDecorationInsertionPointAfterLine(id)
|
||||
|
||||
updateBlockDecorationInsertionPointAfterLine: (id) ->
|
||||
oldLineState = @oldTileState.lines[id]
|
||||
newLineState = @newTileState.lines[id]
|
||||
insertionPoint = @insertionPointsAfterLineById[id]
|
||||
return unless insertionPoint?
|
||||
|
||||
if newLineState.screenRow isnt oldLineState.screenRow
|
||||
insertionPoint.dataset.screenRow = newLineState.screenRow
|
||||
|
||||
followingBlockDecorationsSelector = newLineState.followingBlockDecorations.map((d) -> ".atom--block-decoration-#{d.id}").join(',')
|
||||
|
||||
if followingBlockDecorationsSelector isnt oldLineState.followingBlockDecorationsSelector
|
||||
insertionPoint.setAttribute("select", followingBlockDecorationsSelector)
|
||||
oldLineState.followingBlockDecorationsSelector = followingBlockDecorationsSelector
|
||||
|
||||
findNodeNextTo: (node) ->
|
||||
for nextNode, index in @domNode.children
|
||||
continue if index is 0 # skips highlights node
|
||||
return nextNode if @screenRowForNode(node) < @screenRowForNode(nextNode)
|
||||
return
|
||||
|
||||
screenRowForNode: (node) -> parseInt(node.dataset.screenRow)
|
||||
|
||||
buildLineNode: (id) ->
|
||||
{lineText, tagCodes, screenRow, decorationClasses} = @newTileState.lines[id]
|
||||
|
||||
lineNode = @domElementPool.buildElement("div", "line")
|
||||
lineNode.dataset.screenRow = screenRow
|
||||
|
||||
if decorationClasses?
|
||||
for decorationClass in decorationClasses
|
||||
lineNode.classList.add(decorationClass)
|
||||
|
||||
textNodes = []
|
||||
startIndex = 0
|
||||
openScopeNode = lineNode
|
||||
for tagCode in tagCodes when tagCode isnt 0
|
||||
if @presenter.isCloseTagCode(tagCode)
|
||||
openScopeNode = openScopeNode.parentElement
|
||||
else if @presenter.isOpenTagCode(tagCode)
|
||||
scope = @presenter.tagForCode(tagCode)
|
||||
newScopeNode = @domElementPool.buildElement("span", scope.replace(/\.+/g, ' '))
|
||||
openScopeNode.appendChild(newScopeNode)
|
||||
openScopeNode = newScopeNode
|
||||
else
|
||||
textNode = @domElementPool.buildText(lineText.substr(startIndex, tagCode))
|
||||
startIndex += tagCode
|
||||
openScopeNode.appendChild(textNode)
|
||||
textNodes.push(textNode)
|
||||
|
||||
if startIndex is 0
|
||||
textNode = @domElementPool.buildText(' ')
|
||||
lineNode.appendChild(textNode)
|
||||
textNodes.push(textNode)
|
||||
|
||||
if lineText.endsWith(@presenter.displayLayer.foldCharacter)
|
||||
# Insert a zero-width non-breaking whitespace, so that
|
||||
# LinesYardstick can take the fold-marker::after pseudo-element
|
||||
# into account during measurements when such marker is the last
|
||||
# character on the line.
|
||||
textNode = @domElementPool.buildText(ZERO_WIDTH_NBSP)
|
||||
lineNode.appendChild(textNode)
|
||||
textNodes.push(textNode)
|
||||
|
||||
@textNodesByLineId[id] = textNodes
|
||||
lineNode
|
||||
|
||||
updateLineNode: (id) ->
|
||||
oldLineState = @oldTileState.lines[id]
|
||||
newLineState = @newTileState.lines[id]
|
||||
|
||||
lineNode = @lineNodesByLineId[id]
|
||||
|
||||
newDecorationClasses = newLineState.decorationClasses
|
||||
oldDecorationClasses = oldLineState.decorationClasses
|
||||
|
||||
if oldDecorationClasses?
|
||||
for decorationClass in oldDecorationClasses
|
||||
unless newDecorationClasses? and decorationClass in newDecorationClasses
|
||||
lineNode.classList.remove(decorationClass)
|
||||
|
||||
if newDecorationClasses?
|
||||
for decorationClass in newDecorationClasses
|
||||
unless oldDecorationClasses? and decorationClass in oldDecorationClasses
|
||||
lineNode.classList.add(decorationClass)
|
||||
|
||||
oldLineState.decorationClasses = newLineState.decorationClasses
|
||||
|
||||
if not oldLineState.hasPrecedingBlockDecorations and newLineState.hasPrecedingBlockDecorations
|
||||
@insertBlockDecorationInsertionPointBeforeLine(id)
|
||||
else if oldLineState.hasPrecedingBlockDecorations and not newLineState.hasPrecedingBlockDecorations
|
||||
@removeBlockDecorationInsertionPointBeforeLine(id)
|
||||
|
||||
if not oldLineState.hasFollowingBlockDecorations and newLineState.hasFollowingBlockDecorations
|
||||
@insertBlockDecorationInsertionPointAfterLine(id)
|
||||
else if oldLineState.hasFollowingBlockDecorations and not newLineState.hasFollowingBlockDecorations
|
||||
@removeBlockDecorationInsertionPointAfterLine(id)
|
||||
|
||||
if newLineState.screenRow isnt oldLineState.screenRow
|
||||
lineNode.dataset.screenRow = newLineState.screenRow
|
||||
@lineIdsByScreenRow[newLineState.screenRow] = id
|
||||
@screenRowsByLineId[id] = newLineState.screenRow
|
||||
|
||||
@updateBlockDecorationInsertionPointBeforeLine(id)
|
||||
@updateBlockDecorationInsertionPointAfterLine(id)
|
||||
|
||||
oldLineState.screenRow = newLineState.screenRow
|
||||
oldLineState.hasPrecedingBlockDecorations = newLineState.hasPrecedingBlockDecorations
|
||||
oldLineState.hasFollowingBlockDecorations = newLineState.hasFollowingBlockDecorations
|
||||
|
||||
lineNodeForScreenRow: (screenRow) ->
|
||||
@lineNodesByLineId[@lineIdsByScreenRow[screenRow]]
|
||||
|
||||
lineNodeForLineId: (lineId) ->
|
||||
@lineNodesByLineId[lineId]
|
||||
|
||||
textNodesForLineId: (lineId) ->
|
||||
@textNodesByLineId[lineId].slice()
|
||||
|
||||
lineIdForScreenRow: (screenRow) ->
|
||||
@lineIdsByScreenRow[screenRow]
|
||||
|
||||
textNodesForScreenRow: (screenRow) ->
|
||||
@textNodesByLineId[@lineIdsByScreenRow[screenRow]]?.slice()
|
||||
348
src/lines-tile-component.js
Normal file
348
src/lines-tile-component.js
Normal file
@@ -0,0 +1,348 @@
|
||||
const HighlightsComponent = require('./highlights-component')
|
||||
const ZERO_WIDTH_NBSP = '\ufeff'
|
||||
|
||||
module.exports = class LinesTileComponent {
|
||||
constructor ({presenter, id, domElementPool, assert}) {
|
||||
this.presenter = presenter
|
||||
this.id = id
|
||||
this.domElementPool = domElementPool
|
||||
this.assert = assert
|
||||
this.measuredLines = new Set()
|
||||
this.lineNodesByLineId = {}
|
||||
this.screenRowsByLineId = {}
|
||||
this.lineIdsByScreenRow = {}
|
||||
this.textNodesByLineId = {}
|
||||
this.insertionPointsBeforeLineById = {}
|
||||
this.insertionPointsAfterLineById = {}
|
||||
this.domNode = this.domElementPool.buildElement('div')
|
||||
this.domNode.style.position = 'absolute'
|
||||
this.domNode.style.display = 'block'
|
||||
this.highlightsComponent = new HighlightsComponent(this.domElementPool)
|
||||
this.domNode.appendChild(this.highlightsComponent.getDomNode())
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.domElementPool.freeElementAndDescendants(this.domNode)
|
||||
}
|
||||
|
||||
getDomNode () {
|
||||
return this.domNode
|
||||
}
|
||||
|
||||
updateSync (state) {
|
||||
this.newState = state
|
||||
if (this.oldState == null) {
|
||||
this.oldState = {tiles: {}}
|
||||
this.oldState.tiles[this.id] = {lines: {}}
|
||||
}
|
||||
|
||||
this.newTileState = this.newState.tiles[this.id]
|
||||
this.oldTileState = this.oldState.tiles[this.id]
|
||||
|
||||
if (this.newState.backgroundColor !== this.oldState.backgroundColor) {
|
||||
this.domNode.style.backgroundColor = this.newState.backgroundColor
|
||||
this.oldState.backgroundColor = this.newState.backgroundColor
|
||||
}
|
||||
|
||||
if (this.newTileState.zIndex !== this.oldTileState.zIndex) {
|
||||
this.domNode.style.zIndex = this.newTileState.zIndex
|
||||
this.oldTileState.zIndex = this.newTileState.zIndex
|
||||
}
|
||||
|
||||
if (this.newTileState.display !== this.oldTileState.display) {
|
||||
this.domNode.style.display = this.newTileState.display
|
||||
this.oldTileState.display = this.newTileState.display
|
||||
}
|
||||
|
||||
if (this.newTileState.height !== this.oldTileState.height) {
|
||||
this.domNode.style.height = this.newTileState.height + 'px'
|
||||
this.oldTileState.height = this.newTileState.height
|
||||
}
|
||||
|
||||
if (this.newState.width !== this.oldState.width) {
|
||||
this.domNode.style.width = this.newState.width + 'px'
|
||||
this.oldTileState.width = this.newTileState.width
|
||||
}
|
||||
|
||||
if (this.newTileState.top !== this.oldTileState.top || this.newTileState.left !== this.oldTileState.left) {
|
||||
this.domNode.style.transform = `translate3d(${this.newTileState.left}px, ${this.newTileState.top}px, 0px)`
|
||||
this.oldTileState.top = this.newTileState.top
|
||||
this.oldTileState.left = this.newTileState.left
|
||||
}
|
||||
|
||||
this.updateLineNodes()
|
||||
this.highlightsComponent.updateSync(this.newTileState)
|
||||
}
|
||||
|
||||
removeLineNodes () {
|
||||
for (const id of Object.keys(this.oldTileState.lines)) {
|
||||
this.removeLineNode(id)
|
||||
}
|
||||
}
|
||||
|
||||
removeLineNode (id) {
|
||||
this.domElementPool.freeElementAndDescendants(this.lineNodesByLineId[id])
|
||||
this.removeBlockDecorationInsertionPointBeforeLine(id)
|
||||
this.removeBlockDecorationInsertionPointAfterLine(id)
|
||||
delete this.lineNodesByLineId[id]
|
||||
delete this.textNodesByLineId[id]
|
||||
delete this.lineIdsByScreenRow[this.screenRowsByLineId[id]]
|
||||
delete this.screenRowsByLineId[id]
|
||||
delete this.oldTileState.lines[id]
|
||||
}
|
||||
|
||||
updateLineNodes () {
|
||||
for (const id of Object.keys(this.oldTileState.lines)) {
|
||||
if (!this.newTileState.lines.hasOwnProperty(id)) {
|
||||
this.removeLineNode(id)
|
||||
}
|
||||
}
|
||||
|
||||
const newLineIds = []
|
||||
const newLineNodes = []
|
||||
for (const id of Object.keys(this.newTileState.lines)) {
|
||||
const lineState = this.newTileState.lines[id]
|
||||
if (this.oldTileState.lines.hasOwnProperty(id)) {
|
||||
this.updateLineNode(id)
|
||||
} else {
|
||||
newLineIds.push(id)
|
||||
newLineNodes.push(this.buildLineNode(id))
|
||||
this.screenRowsByLineId[id] = lineState.screenRow
|
||||
this.lineIdsByScreenRow[lineState.screenRow] = id
|
||||
this.oldTileState.lines[id] = Object.assign({}, lineState)
|
||||
}
|
||||
}
|
||||
|
||||
while (newLineIds.length > 0) {
|
||||
const id = newLineIds.shift()
|
||||
const lineNode = newLineNodes.shift()
|
||||
this.lineNodesByLineId[id] = lineNode
|
||||
const nextNode = this.findNodeNextTo(lineNode)
|
||||
if (nextNode == null) {
|
||||
this.domNode.appendChild(lineNode)
|
||||
} else {
|
||||
this.domNode.insertBefore(lineNode, nextNode)
|
||||
}
|
||||
this.insertBlockDecorationInsertionPointBeforeLine(id)
|
||||
this.insertBlockDecorationInsertionPointAfterLine(id)
|
||||
}
|
||||
}
|
||||
|
||||
removeBlockDecorationInsertionPointBeforeLine (id) {
|
||||
const insertionPoint = this.insertionPointsBeforeLineById[id]
|
||||
if (insertionPoint != null) {
|
||||
this.domElementPool.freeElementAndDescendants(insertionPoint)
|
||||
delete this.insertionPointsBeforeLineById[id]
|
||||
}
|
||||
}
|
||||
|
||||
insertBlockDecorationInsertionPointBeforeLine (id) {
|
||||
const {hasPrecedingBlockDecorations, screenRow} = this.newTileState.lines[id]
|
||||
if (hasPrecedingBlockDecorations) {
|
||||
const lineNode = this.lineNodesByLineId[id]
|
||||
const insertionPoint = this.domElementPool.buildElement('content')
|
||||
this.domNode.insertBefore(insertionPoint, lineNode)
|
||||
this.insertionPointsBeforeLineById[id] = insertionPoint
|
||||
insertionPoint.dataset.screenRow = screenRow
|
||||
this.updateBlockDecorationInsertionPointBeforeLine(id)
|
||||
}
|
||||
}
|
||||
|
||||
updateBlockDecorationInsertionPointBeforeLine (id) {
|
||||
const oldLineState = this.oldTileState.lines[id]
|
||||
const newLineState = this.newTileState.lines[id]
|
||||
const insertionPoint = this.insertionPointsBeforeLineById[id]
|
||||
if (insertionPoint != null) {
|
||||
if (newLineState.screenRow !== oldLineState.screenRow) {
|
||||
insertionPoint.dataset.screenRow = newLineState.screenRow
|
||||
}
|
||||
|
||||
const precedingBlockDecorationsSelector = newLineState.precedingBlockDecorations
|
||||
.map((d) => `.atom--block-decoration-${d.id}`)
|
||||
.join(',')
|
||||
if (precedingBlockDecorationsSelector !== oldLineState.precedingBlockDecorationsSelector) {
|
||||
insertionPoint.setAttribute('select', precedingBlockDecorationsSelector)
|
||||
oldLineState.precedingBlockDecorationsSelector = precedingBlockDecorationsSelector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeBlockDecorationInsertionPointAfterLine (id) {
|
||||
const insertionPoint = this.insertionPointsAfterLineById[id]
|
||||
if (insertionPoint != null) {
|
||||
this.domElementPool.freeElementAndDescendants(insertionPoint)
|
||||
delete this.insertionPointsAfterLineById[id]
|
||||
}
|
||||
}
|
||||
|
||||
insertBlockDecorationInsertionPointAfterLine (id) {
|
||||
const {hasFollowingBlockDecorations, screenRow} = this.newTileState.lines[id]
|
||||
if (hasFollowingBlockDecorations) {
|
||||
const lineNode = this.lineNodesByLineId[id]
|
||||
const insertionPoint = this.domElementPool.buildElement('content')
|
||||
this.domNode.insertBefore(insertionPoint, lineNode.nextSibling)
|
||||
this.insertionPointsAfterLineById[id] = insertionPoint
|
||||
insertionPoint.dataset.screenRow = screenRow
|
||||
this.updateBlockDecorationInsertionPointAfterLine(id)
|
||||
}
|
||||
}
|
||||
|
||||
updateBlockDecorationInsertionPointAfterLine (id) {
|
||||
const oldLineState = this.oldTileState.lines[id]
|
||||
const newLineState = this.newTileState.lines[id]
|
||||
const insertionPoint = this.insertionPointsAfterLineById[id]
|
||||
|
||||
if (insertionPoint != null) {
|
||||
if (newLineState.screenRow !== oldLineState.screenRow) {
|
||||
insertionPoint.dataset.screenRow = newLineState.screenRow
|
||||
}
|
||||
|
||||
const followingBlockDecorationsSelector = newLineState.followingBlockDecorations
|
||||
.map((d) => `.atom--block-decoration-${d.id}`)
|
||||
.join(',')
|
||||
if (followingBlockDecorationsSelector !== oldLineState.followingBlockDecorationsSelector) {
|
||||
insertionPoint.setAttribute('select', followingBlockDecorationsSelector)
|
||||
oldLineState.followingBlockDecorationsSelector = followingBlockDecorationsSelector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findNodeNextTo (node) {
|
||||
let i = 1 // skip highlights node
|
||||
while (i < this.domNode.children.length) {
|
||||
const nextNode = this.domNode.children[i]
|
||||
if (this.screenRowForNode(node) < this.screenRowForNode(nextNode)) {
|
||||
return nextNode
|
||||
}
|
||||
i++
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
screenRowForNode (node) {
|
||||
return parseInt(node.dataset.screenRow)
|
||||
}
|
||||
|
||||
buildLineNode (id) {
|
||||
const {lineText, tagCodes, screenRow, decorationClasses} = this.newTileState.lines[id]
|
||||
|
||||
const lineNode = this.domElementPool.buildElement('div', 'line')
|
||||
lineNode.dataset.screenRow = screenRow
|
||||
if (decorationClasses != null) {
|
||||
for (const decorationClass of decorationClasses) {
|
||||
lineNode.classList.add(decorationClass)
|
||||
}
|
||||
}
|
||||
|
||||
const textNodes = []
|
||||
let startIndex = 0
|
||||
let openScopeNode = lineNode
|
||||
for (const tagCode of tagCodes) {
|
||||
if (tagCode !== 0) {
|
||||
if (this.presenter.isCloseTagCode(tagCode)) {
|
||||
openScopeNode = openScopeNode.parentElement
|
||||
} else if (this.presenter.isOpenTagCode(tagCode)) {
|
||||
const scope = this.presenter.tagForCode(tagCode)
|
||||
const newScopeNode = this.domElementPool.buildElement('span', scope.replace(/\.+/g, ' '))
|
||||
openScopeNode.appendChild(newScopeNode)
|
||||
openScopeNode = newScopeNode
|
||||
} else {
|
||||
const textNode = this.domElementPool.buildText(lineText.substr(startIndex, tagCode))
|
||||
startIndex += tagCode
|
||||
openScopeNode.appendChild(textNode)
|
||||
textNodes.push(textNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (startIndex === 0) {
|
||||
const textNode = this.domElementPool.buildText(' ')
|
||||
lineNode.appendChild(textNode)
|
||||
textNodes.push(textNode)
|
||||
}
|
||||
|
||||
if (lineText.endsWith(this.presenter.displayLayer.foldCharacter)) {
|
||||
const textNode = this.domElementPool.buildText(ZERO_WIDTH_NBSP)
|
||||
lineNode.appendChild(textNode)
|
||||
textNodes.push(textNode)
|
||||
}
|
||||
|
||||
this.textNodesByLineId[id] = textNodes
|
||||
return lineNode
|
||||
}
|
||||
|
||||
updateLineNode (id) {
|
||||
const oldLineState = this.oldTileState.lines[id]
|
||||
const newLineState = this.newTileState.lines[id]
|
||||
const lineNode = this.lineNodesByLineId[id]
|
||||
const newDecorationClasses = newLineState.decorationClasses
|
||||
const oldDecorationClasses = oldLineState.decorationClasses
|
||||
|
||||
if (oldDecorationClasses != null) {
|
||||
for (const decorationClass of oldDecorationClasses) {
|
||||
if (newDecorationClasses == null || !newDecorationClasses.includes(decorationClass)) {
|
||||
lineNode.classList.remove(decorationClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newDecorationClasses != null) {
|
||||
for (const decorationClass of newDecorationClasses) {
|
||||
if (oldDecorationClasses == null || !oldDecorationClasses.includes(decorationClass)) {
|
||||
lineNode.classList.add(decorationClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldLineState.decorationClasses = newLineState.decorationClasses
|
||||
|
||||
if (!oldLineState.hasPrecedingBlockDecorations && newLineState.hasPrecedingBlockDecorations) {
|
||||
this.insertBlockDecorationInsertionPointBeforeLine(id)
|
||||
} else if (oldLineState.hasPrecedingBlockDecorations && !newLineState.hasPrecedingBlockDecorations) {
|
||||
this.removeBlockDecorationInsertionPointBeforeLine(id)
|
||||
}
|
||||
|
||||
if (!oldLineState.hasFollowingBlockDecorations && newLineState.hasFollowingBlockDecorations) {
|
||||
this.insertBlockDecorationInsertionPointAfterLine(id)
|
||||
} else if (oldLineState.hasFollowingBlockDecorations && !newLineState.hasFollowingBlockDecorations) {
|
||||
this.removeBlockDecorationInsertionPointAfterLine(id)
|
||||
}
|
||||
|
||||
if (newLineState.screenRow !== oldLineState.screenRow) {
|
||||
lineNode.dataset.screenRow = newLineState.screenRow
|
||||
this.lineIdsByScreenRow[newLineState.screenRow] = id
|
||||
this.screenRowsByLineId[id] = newLineState.screenRow
|
||||
}
|
||||
|
||||
this.updateBlockDecorationInsertionPointBeforeLine(id)
|
||||
this.updateBlockDecorationInsertionPointAfterLine(id)
|
||||
oldLineState.screenRow = newLineState.screenRow
|
||||
oldLineState.hasPrecedingBlockDecorations = newLineState.hasPrecedingBlockDecorations
|
||||
oldLineState.hasFollowingBlockDecorations = newLineState.hasFollowingBlockDecorations
|
||||
}
|
||||
|
||||
lineNodeForScreenRow (screenRow) {
|
||||
return this.lineNodesByLineId[this.lineIdsByScreenRow[screenRow]]
|
||||
}
|
||||
|
||||
lineNodeForLineId (lineId) {
|
||||
return this.lineNodesByLineId[lineId]
|
||||
}
|
||||
|
||||
textNodesForLineId (lineId) {
|
||||
return this.textNodesByLineId[lineId].slice()
|
||||
}
|
||||
|
||||
lineIdForScreenRow (screenRow) {
|
||||
return this.lineIdsByScreenRow[screenRow]
|
||||
}
|
||||
|
||||
textNodesForScreenRow (screenRow) {
|
||||
const textNodes = this.textNodesByLineId[this.lineIdsByScreenRow[screenRow]]
|
||||
if (textNodes == null) {
|
||||
return null
|
||||
} else {
|
||||
return textNodes.slice()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user