mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Render line and line number decorations
This commit is contained in:
committed by
Antonio Scandurra
parent
e15e7e3c96
commit
ff325c0151
@@ -28,7 +28,6 @@ describe "DecorationManager", ->
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
expect(layer1MarkerDecoration).toBeDefined()
|
||||
expect(layer1MarkerDecoration.getProperties()).toBe decorationProperties
|
||||
expect(decorationManager.decorationForId(layer1MarkerDecoration.id)).toBe layer1MarkerDecoration
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)).toEqual {
|
||||
"#{layer1Marker.id}": [layer1MarkerDecoration],
|
||||
"#{layer2Marker.id}": [layer2MarkerDecoration]
|
||||
@@ -36,15 +35,12 @@ describe "DecorationManager", ->
|
||||
|
||||
layer1MarkerDecoration.destroy()
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[layer1Marker.id]).not.toBeDefined()
|
||||
expect(decorationManager.decorationForId(layer1MarkerDecoration.id)).not.toBeDefined()
|
||||
layer2MarkerDecoration.destroy()
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[layer2Marker.id]).not.toBeDefined()
|
||||
expect(decorationManager.decorationForId(layer2MarkerDecoration.id)).not.toBeDefined()
|
||||
|
||||
it "will not fail if the decoration is removed twice", ->
|
||||
layer1MarkerDecoration.destroy()
|
||||
layer1MarkerDecoration.destroy()
|
||||
expect(decorationManager.decorationForId(layer1MarkerDecoration.id)).not.toBeDefined()
|
||||
|
||||
it "does not allow destroyed markers to be decorated", ->
|
||||
layer1Marker.destroy()
|
||||
|
||||
@@ -175,8 +175,6 @@ describe('TextEditorComponent', () => {
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
expect(getBaseCharacterWidth(component)).toBe(55)
|
||||
|
||||
console.log(element.offsetWidth);
|
||||
expect(lineNodeForScreenRow(component, 3).textContent).toBe(
|
||||
' var pivot = items.shift(), current, left = [], '
|
||||
)
|
||||
@@ -344,6 +342,74 @@ describe('TextEditorComponent', () => {
|
||||
expect(scroller.scrollLeft).toBe(expectedScrollLeft)
|
||||
})
|
||||
})
|
||||
|
||||
describe('line and line number decorations', () => {
|
||||
it('adds decoration classes on screen lines spanned by decorated markers', async () => {
|
||||
const {component, element, editor} = buildComponent({width: 435, attach: false})
|
||||
editor.setSoftWrapped(true)
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
expect(lineNodeForScreenRow(component, 3).textContent).toBe(
|
||||
' var pivot = items.shift(), current, left = [], '
|
||||
)
|
||||
expect(lineNodeForScreenRow(component, 4).textContent).toBe(
|
||||
' right = [];'
|
||||
)
|
||||
|
||||
const marker1 = editor.markScreenRange([[1, 10], [3, 10]])
|
||||
const layer = editor.addMarkerLayer()
|
||||
const marker2 = layer.markScreenPosition([5, 0])
|
||||
const marker3 = layer.markScreenPosition([8, 0])
|
||||
const marker4 = layer.markScreenPosition([10, 0])
|
||||
const markerDecoration = editor.decorateMarker(marker1, {type: ['line', 'line-number'], class: 'a'})
|
||||
const layerDecoration = editor.decorateMarkerLayer(layer, {type: ['line', 'line-number'], class: 'b'})
|
||||
layerDecoration.setPropertiesForMarker(marker4, {type: 'line', class: 'c'})
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 4).classList.contains('a')).toBe(false)
|
||||
expect(lineNodeForScreenRow(component, 5).classList.contains('b')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 8).classList.contains('b')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 10).classList.contains('b')).toBe(false)
|
||||
expect(lineNodeForScreenRow(component, 10).classList.contains('c')).toBe(true)
|
||||
|
||||
expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 2).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 3).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 4).classList.contains('a')).toBe(false)
|
||||
expect(lineNumberNodeForScreenRow(component, 5).classList.contains('b')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 8).classList.contains('b')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 10).classList.contains('b')).toBe(false)
|
||||
expect(lineNumberNodeForScreenRow(component, 10).classList.contains('c')).toBe(false)
|
||||
|
||||
marker1.setScreenRange([[5, 0], [8, 0]])
|
||||
await component.getNextUpdatePromise()
|
||||
|
||||
expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(false)
|
||||
expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(false)
|
||||
expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe(false)
|
||||
expect(lineNodeForScreenRow(component, 4).classList.contains('a')).toBe(false)
|
||||
expect(lineNodeForScreenRow(component, 5).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 5).classList.contains('b')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 6).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 7).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 8).classList.contains('a')).toBe(true)
|
||||
expect(lineNodeForScreenRow(component, 8).classList.contains('b')).toBe(true)
|
||||
|
||||
expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(false)
|
||||
expect(lineNumberNodeForScreenRow(component, 2).classList.contains('a')).toBe(false)
|
||||
expect(lineNumberNodeForScreenRow(component, 3).classList.contains('a')).toBe(false)
|
||||
expect(lineNumberNodeForScreenRow(component, 4).classList.contains('a')).toBe(false)
|
||||
expect(lineNumberNodeForScreenRow(component, 5).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 5).classList.contains('b')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 6).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 7).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 8).classList.contains('a')).toBe(true)
|
||||
expect(lineNumberNodeForScreenRow(component, 8).classList.contains('b')).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function buildComponent (params = {}) {
|
||||
@@ -401,6 +467,15 @@ function clientLeftForCharacter (component, row, column) {
|
||||
}
|
||||
}
|
||||
|
||||
function lineNumberNodeForScreenRow (component, row) {
|
||||
const gutterElement = component.refs.lineNumberGutter.element
|
||||
const endRow = Math.min(component.getRenderedEndRow(), component.getModel().getApproximateScreenLineCount())
|
||||
const visibleTileCount = Math.ceil((endRow - component.getRenderedStartRow()) / component.getRowsPerTile())
|
||||
const tileStartRow = component.getTileStartRow(row)
|
||||
const tileIndex = (tileStartRow / component.getRowsPerTile()) % visibleTileCount
|
||||
return gutterElement.children[tileIndex].children[row - tileStartRow]
|
||||
}
|
||||
|
||||
function lineNodeForScreenRow (component, row) {
|
||||
const screenLine = component.getModel().screenLineForScreenRow(row)
|
||||
return component.lineNodesByScreenLineId.get(screenLine.id)
|
||||
|
||||
@@ -9,6 +9,7 @@ class DecorationManager {
|
||||
|
||||
this.emitter = new Emitter()
|
||||
this.decorationCountsByLayer = new Map()
|
||||
this.markerDecorationCountsByLayer = new Map()
|
||||
this.decorationsByMarker = new Map()
|
||||
this.layerDecorationsByMarkerLayer = new Map()
|
||||
this.overlayDecorations = new Set()
|
||||
@@ -80,6 +81,40 @@ class DecorationManager {
|
||||
}
|
||||
}
|
||||
|
||||
decorationPropertiesByMarkerForScreenRowRange (startScreenRow, endScreenRow) {
|
||||
const decorationPropertiesByMarker = new Map()
|
||||
|
||||
this.decorationCountsByLayer.forEach((count, markerLayer) => {
|
||||
const markers = markerLayer.findMarkers({intersectsScreenRowRange: [startScreenRow, endScreenRow - 1]})
|
||||
const layerDecorations = this.layerDecorationsByMarkerLayer.get(markerLayer)
|
||||
const hasMarkerDecorations = this.markerDecorationCountsByLayer.get(markerLayer) > 0
|
||||
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
const marker = markers[i]
|
||||
|
||||
let decorationPropertiesForMarker = decorationPropertiesByMarker.get(marker)
|
||||
if (decorationPropertiesForMarker == null) {
|
||||
decorationPropertiesForMarker = []
|
||||
decorationPropertiesByMarker.set(marker, decorationPropertiesForMarker)
|
||||
}
|
||||
|
||||
if (layerDecorations) {
|
||||
layerDecorations.forEach((layerDecoration) => {
|
||||
decorationPropertiesForMarker.push(layerDecoration.getPropertiesForMarker(marker) || layerDecoration.getProperties())
|
||||
})
|
||||
}
|
||||
|
||||
if (hasMarkerDecorations) {
|
||||
this.decorationsByMarker.get(marker).forEach((decoration) => {
|
||||
decorationPropertiesForMarker.push(decoration.getProperties())
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return decorationPropertiesByMarker
|
||||
}
|
||||
|
||||
decorationsForScreenRowRange (startScreenRow, endScreenRow) {
|
||||
const decorationsByMarkerId = {}
|
||||
for (const layer of this.decorationCountsByLayer.keys()) {
|
||||
@@ -118,7 +153,7 @@ class DecorationManager {
|
||||
const layerDecorations = this.layerDecorationsByMarkerLayer.get(layer)
|
||||
if (layerDecorations) {
|
||||
layerDecorations.forEach((layerDecoration) => {
|
||||
const properties = layerDecoration.overridePropertiesByMarkerId[marker.id] != null ? layerDecoration.overridePropertiesByMarkerId[marker.id] : layerDecoration.properties
|
||||
const properties = layerDecoration.getPropertiesForMarker(marker) || layerDecoration.getProperties()
|
||||
decorationsState[`${layerDecoration.id}-${marker.id}`] = {
|
||||
properties,
|
||||
screenRange,
|
||||
@@ -155,7 +190,7 @@ class DecorationManager {
|
||||
}
|
||||
decorationsForMarker.add(decoration)
|
||||
if (decoration.isType('overlay')) this.overlayDecorations.add(decoration)
|
||||
this.observeDecoratedLayer(marker.layer)
|
||||
this.observeDecoratedLayer(marker.layer, true)
|
||||
this.emitDidUpdateDecorations()
|
||||
this.emitter.emit('did-add-decoration', decoration)
|
||||
return decoration
|
||||
@@ -172,7 +207,7 @@ class DecorationManager {
|
||||
this.layerDecorationsByMarkerLayer.set(markerLayer, layerDecorations)
|
||||
}
|
||||
layerDecorations.add(decoration)
|
||||
this.observeDecoratedLayer(markerLayer)
|
||||
this.observeDecoratedLayer(markerLayer, false)
|
||||
this.emitDidUpdateDecorations()
|
||||
return decoration
|
||||
}
|
||||
@@ -196,7 +231,7 @@ class DecorationManager {
|
||||
decorations.delete(decoration)
|
||||
if (decorations.size === 0) this.decorationsByMarker.delete(marker)
|
||||
this.overlayDecorations.delete(decoration)
|
||||
this.unobserveDecoratedLayer(marker.layer)
|
||||
this.unobserveDecoratedLayer(marker.layer, true)
|
||||
this.emitter.emit('did-remove-decoration', decoration)
|
||||
this.emitDidUpdateDecorations()
|
||||
}
|
||||
@@ -211,20 +246,23 @@ class DecorationManager {
|
||||
if (decorations.size === 0) {
|
||||
this.layerDecorationsByMarkerLayer.delete(markerLayer)
|
||||
}
|
||||
this.unobserveDecoratedLayer(markerLayer)
|
||||
this.unobserveDecoratedLayer(markerLayer, true)
|
||||
this.emitDidUpdateDecorations()
|
||||
}
|
||||
}
|
||||
|
||||
observeDecoratedLayer (layer) {
|
||||
observeDecoratedLayer (layer, isMarkerDecoration) {
|
||||
const newCount = (this.decorationCountsByLayer.get(layer) || 0) + 1
|
||||
this.decorationCountsByLayer.set(layer, newCount)
|
||||
if (newCount === 1) {
|
||||
this.layerUpdateDisposablesByLayer.set(layer, layer.onDidUpdate(this.emitDidUpdateDecorations.bind(this)))
|
||||
}
|
||||
if (isMarkerDecoration) {
|
||||
this.markerDecorationCountsByLayer.set(layer, (this.markerDecorationCountsByLayer.get(layer) || 0) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
unobserveDecoratedLayer (layer) {
|
||||
unobserveDecoratedLayer (layer, isMarkerDecoration) {
|
||||
const newCount = this.decorationCountsByLayer.get(layer) - 1
|
||||
if (newCount === 0) {
|
||||
this.layerUpdateDisposablesByLayer.get(layer).dispose()
|
||||
@@ -232,5 +270,8 @@ class DecorationManager {
|
||||
} else {
|
||||
this.decorationCountsByLayer.set(layer, newCount)
|
||||
}
|
||||
if (isMarkerDecoration) {
|
||||
this.markerDecorationCountsByLayer.set(this.markerDecorationCountsByLayer.get(layer) - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class LayerDecoration
|
||||
@id = nextId()
|
||||
@destroyed = false
|
||||
@markerLayerDestroyedDisposable = @markerLayer.onDidDestroy => @destroy()
|
||||
@overridePropertiesByMarkerId = {}
|
||||
@overridePropertiesByMarker = null
|
||||
|
||||
# Essential: Destroys the decoration.
|
||||
destroy: ->
|
||||
@@ -42,7 +42,7 @@ class LayerDecoration
|
||||
setProperties: (newProperties) ->
|
||||
return if @destroyed
|
||||
@properties = newProperties
|
||||
@decorationManager.scheduleUpdateDecorationsEvent()
|
||||
@decorationManager.emitDidUpdateDecorations()
|
||||
|
||||
# Essential: Override the decoration properties for a specific marker.
|
||||
#
|
||||
@@ -52,8 +52,12 @@ class LayerDecoration
|
||||
# Pass `null` to clear the override.
|
||||
setPropertiesForMarker: (marker, properties) ->
|
||||
return if @destroyed
|
||||
@overridePropertiesByMarker ?= new Map()
|
||||
if properties?
|
||||
@overridePropertiesByMarkerId[marker.id] = properties
|
||||
@overridePropertiesByMarker.set(marker, properties)
|
||||
else
|
||||
delete @overridePropertiesByMarkerId[marker.id]
|
||||
@decorationManager.scheduleUpdateDecorationsEvent()
|
||||
@overridePropertiesByMarker.delete(marker.id)
|
||||
@decorationManager.emitDidUpdateDecorations()
|
||||
|
||||
getPropertiesForMarker: (marker) ->
|
||||
@overridePropertiesByMarker?.get(marker)
|
||||
|
||||
@@ -38,6 +38,10 @@ class TextEditorComponent {
|
||||
this.lastKeydownBeforeKeypress = null
|
||||
this.openedAccentedCharacterMenu = false
|
||||
this.cursorsToRender = []
|
||||
this.decorationsToRender = {
|
||||
lineNumbers: new Map(),
|
||||
lines: new Map()
|
||||
}
|
||||
|
||||
if (this.props.model) this.observeModel()
|
||||
resizeDetector.listenTo(this.element, this.didResize.bind(this))
|
||||
@@ -74,6 +78,7 @@ class TextEditorComponent {
|
||||
if (this.pendingAutoscroll) this.initiateAutoscroll()
|
||||
this.populateVisibleRowRange()
|
||||
const longestLineToMeasure = this.checkForNewLongestLine()
|
||||
this.queryDecorationsToRender()
|
||||
this.queryCursorsToRender()
|
||||
|
||||
etch.updateSync(this)
|
||||
@@ -166,9 +171,11 @@ class TextEditorComponent {
|
||||
if (this.measurements) {
|
||||
const startRow = this.getRenderedStartRow()
|
||||
const endRow = Math.min(model.getApproximateScreenLineCount(), this.getRenderedEndRow())
|
||||
const bufferRows = new Array(endRow - startRow)
|
||||
const foldableFlags = new Array(endRow - startRow)
|
||||
const softWrappedFlags = new Array(endRow - startRow)
|
||||
const visibleRowCount = endRow - startRow
|
||||
const bufferRows = new Array(visibleRowCount)
|
||||
const foldableFlags = new Array(visibleRowCount)
|
||||
const softWrappedFlags = new Array(visibleRowCount)
|
||||
const lineNumberDecorations = new Array(visibleRowCount)
|
||||
|
||||
let previousBufferRow = (startRow > 0) ? model.bufferRowForScreenRow(startRow - 1) : -1
|
||||
for (let row = startRow; row < endRow; row++) {
|
||||
@@ -177,17 +184,20 @@ class TextEditorComponent {
|
||||
bufferRows[i] = bufferRow
|
||||
softWrappedFlags[i] = bufferRow === previousBufferRow
|
||||
foldableFlags[i] = model.isFoldableAtBufferRow(bufferRow)
|
||||
lineNumberDecorations[i] = this.decorationsToRender.lineNumbers.get(row)
|
||||
previousBufferRow = bufferRow
|
||||
}
|
||||
|
||||
const rowsPerTile = this.getRowsPerTile()
|
||||
|
||||
this.currentFrameLineNumberGutterProps = {
|
||||
ref: 'lineNumberGutter',
|
||||
height: this.getScrollHeight(),
|
||||
width: this.measurements.lineNumberGutterWidth,
|
||||
lineHeight: this.measurements.lineHeight,
|
||||
startRow, endRow, rowsPerTile, maxLineNumberDigits,
|
||||
bufferRows, softWrappedFlags, foldableFlags
|
||||
bufferRows, lineNumberDecorations, softWrappedFlags,
|
||||
foldableFlags
|
||||
}
|
||||
|
||||
return $(LineNumberGutterComponent, this.currentFrameLineNumberGutterProps)
|
||||
@@ -265,12 +275,18 @@ class TextEditorComponent {
|
||||
const tileHeight = rowsPerTile * this.measurements.lineHeight
|
||||
const tileIndex = (tileStartRow / rowsPerTile) % visibleTileCount
|
||||
|
||||
const lineDecorations = new Array(rowsPerTile)
|
||||
for (let row = tileStartRow; row < tileEndRow; row++) {
|
||||
lineDecorations[row - tileStartRow] = this.decorationsToRender.lines.get(row)
|
||||
}
|
||||
|
||||
tileNodes[tileIndex] = $(LinesTileComponent, {
|
||||
key: tileIndex,
|
||||
height: tileHeight,
|
||||
width: tileWidth,
|
||||
top: this.topPixelPositionForRow(tileStartRow),
|
||||
screenLines: screenLines.slice(tileStartRow - startRow, tileEndRow - startRow),
|
||||
lineDecorations,
|
||||
displayLayer,
|
||||
lineNodesByScreenLineId,
|
||||
textNodesByScreenLineId
|
||||
@@ -393,6 +409,52 @@ class TextEditorComponent {
|
||||
}
|
||||
}
|
||||
|
||||
queryDecorationsToRender () {
|
||||
this.decorationsToRender.lineNumbers.clear()
|
||||
this.decorationsToRender.lines.clear()
|
||||
|
||||
const decorationsByMarker =
|
||||
this.getModel().decorationManager.decorationPropertiesByMarkerForScreenRowRange(
|
||||
this.getRenderedStartRow(),
|
||||
this.getRenderedEndRow()
|
||||
)
|
||||
|
||||
decorationsByMarker.forEach((decorations, marker) => {
|
||||
const screenRange = marker.getScreenRange()
|
||||
const reversed = marker.isReversed()
|
||||
for (let i = 0, length = decorations.length; i < decorations.length; i++) {
|
||||
const decoration = decorations[i]
|
||||
this.addToDecorationsToRender(decoration.type, decoration, screenRange, reversed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addToDecorationsToRender (type, decoration, screenRange, reversed) {
|
||||
if (Array.isArray(type)) {
|
||||
for (let i = 0, length = type.length; i < length; i++) {
|
||||
this.addToDecorationsToRender(type[i], decoration, screenRange, reversed)
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case 'line-number':
|
||||
for (let row = screenRange.start.row; row <= screenRange.end.row; row++) {
|
||||
const currentClassName = this.decorationsToRender.lineNumbers.get(row)
|
||||
const newClassName = currentClassName ? currentClassName + ' ' + decoration.class : decoration.class
|
||||
this.decorationsToRender.lineNumbers.set(row, newClassName)
|
||||
}
|
||||
break
|
||||
case 'line':
|
||||
for (let row = screenRange.start.row; row <= screenRange.end.row; row++) {
|
||||
const currentClassName = this.decorationsToRender.lines.get(row)
|
||||
const newClassName = currentClassName ? currentClassName + ' ' + decoration.class : decoration.class
|
||||
this.decorationsToRender.lines.set(row, newClassName)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
positionCursorsToRender () {
|
||||
const height = this.measurements.lineHeight + 'px'
|
||||
for (let i = 0; i < this.cursorsToRender.length; i++) {
|
||||
@@ -878,6 +940,7 @@ class TextEditorComponent {
|
||||
const scheduleUpdate = this.scheduleUpdate.bind(this)
|
||||
this.disposables.add(model.selectionsMarkerLayer.onDidUpdate(scheduleUpdate))
|
||||
this.disposables.add(model.displayLayer.onDidChangeSync(scheduleUpdate))
|
||||
this.disposables.add(model.onDidUpdateDecorations(scheduleUpdate))
|
||||
this.disposables.add(model.onDidRequestAutoscroll(this.didRequestAutoscroll.bind(this)))
|
||||
}
|
||||
|
||||
@@ -1017,7 +1080,8 @@ class LineNumberGutterComponent {
|
||||
render () {
|
||||
const {
|
||||
height, width, lineHeight, startRow, endRow, rowsPerTile,
|
||||
maxLineNumberDigits, bufferRows, softWrappedFlags, foldableFlags
|
||||
maxLineNumberDigits, bufferRows, softWrappedFlags, foldableFlags,
|
||||
lineNumberDecorations
|
||||
} = this.props
|
||||
|
||||
const visibleTileCount = Math.ceil((endRow - startRow) / rowsPerTile)
|
||||
@@ -1046,6 +1110,10 @@ class LineNumberGutterComponent {
|
||||
lineNumber = (bufferRow + 1).toString()
|
||||
if (foldable) className += ' foldable'
|
||||
}
|
||||
|
||||
const lineNumberDecoration = lineNumberDecorations[i]
|
||||
if (lineNumberDecoration != null) className += ' ' + lineNumberDecoration
|
||||
|
||||
lineNumber = NBSP_CHARACTER.repeat(maxLineNumberDigits - lineNumber.length) + lineNumber
|
||||
|
||||
tileChildren[row - tileStartRow] = $.div({key, className},
|
||||
@@ -1100,6 +1168,7 @@ class LineNumberGutterComponent {
|
||||
if (!arraysEqual(oldProps.bufferRows, newProps.bufferRows)) return true
|
||||
if (!arraysEqual(oldProps.softWrappedFlags, newProps.softWrappedFlags)) return true
|
||||
if (!arraysEqual(oldProps.foldableFlags, newProps.foldableFlags)) return true
|
||||
if (!arraysEqual(oldProps.lineNumberDecorations, newProps.lineNumberDecorations)) return true
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1120,8 +1189,8 @@ class LinesTileComponent {
|
||||
render () {
|
||||
const {
|
||||
height, width, top,
|
||||
screenLines, displayLayer,
|
||||
lineNodesByScreenLineId, textNodesByScreenLineId
|
||||
screenLines, lineDecorations, displayLayer,
|
||||
lineNodesByScreenLineId, textNodesByScreenLineId,
|
||||
} = this.props
|
||||
|
||||
const children = new Array(screenLines.length)
|
||||
@@ -1134,6 +1203,7 @@ class LinesTileComponent {
|
||||
children[i] = $(LineComponent, {
|
||||
key: screenLine.id,
|
||||
screenLine,
|
||||
lineDecoration: lineDecorations[i],
|
||||
displayLayer,
|
||||
lineNodesByScreenLineId,
|
||||
textNodesByScreenLineId
|
||||
@@ -1159,16 +1229,17 @@ class LinesTileComponent {
|
||||
if (oldProps.height !== newProps.height) return true
|
||||
if (oldProps.width !== newProps.width) return true
|
||||
if (!arraysEqual(oldProps.screenLines, newProps.screenLines)) return true
|
||||
if (!arraysEqual(oldProps.lineDecorations, newProps.lineDecorations)) return true
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class LineComponent {
|
||||
constructor (props) {
|
||||
const {displayLayer, screenLine, lineNodesByScreenLineId, textNodesByScreenLineId} = props
|
||||
const {displayLayer, screenLine, lineDecoration, lineNodesByScreenLineId, textNodesByScreenLineId} = props
|
||||
this.props = props
|
||||
this.element = document.createElement('div')
|
||||
this.element.classList.add('line')
|
||||
this.element.className = this.buildClassName()
|
||||
lineNodesByScreenLineId.set(screenLine.id, this.element)
|
||||
|
||||
const textNodes = []
|
||||
@@ -1214,7 +1285,12 @@ class LineComponent {
|
||||
}
|
||||
}
|
||||
|
||||
update () {}
|
||||
update (newProps) {
|
||||
if (this.props.lineDecoration !== newProps.lineDecoration) {
|
||||
this.props = newProps
|
||||
this.element.className = this.buildClassName()
|
||||
}
|
||||
}
|
||||
|
||||
destroy () {
|
||||
const {lineNodesByScreenLineId, textNodesByScreenLineId, screenLine} = this.props
|
||||
@@ -1223,6 +1299,13 @@ class LineComponent {
|
||||
textNodesByScreenLineId.delete(screenLine.id)
|
||||
}
|
||||
}
|
||||
|
||||
buildClassName () {
|
||||
const {lineDecoration} = this.props
|
||||
let className = 'line'
|
||||
if (lineDecoration != null) className += ' ' + lineDecoration
|
||||
return className
|
||||
}
|
||||
}
|
||||
|
||||
const classNamesByScopeName = new Map()
|
||||
|
||||
@@ -1849,9 +1849,6 @@ class TextEditor extends Model
|
||||
getOverlayDecorations: (propertyFilter) ->
|
||||
@decorationManager.getOverlayDecorations(propertyFilter)
|
||||
|
||||
decorationForId: (id) ->
|
||||
@decorationManager.decorationForId(id)
|
||||
|
||||
###
|
||||
Section: Markers
|
||||
###
|
||||
|
||||
Reference in New Issue
Block a user