mirror of
https://github.com/atom/atom.git
synced 2026-02-11 15:14:59 -05:00
The rendering layer can be asynchronous instead, plus layer decorations should remove the need to emit lots of individual events.
255 lines
8.4 KiB
JavaScript
255 lines
8.4 KiB
JavaScript
const {Emitter} = require('event-kit')
|
|
const Decoration = require('./decoration')
|
|
const LayerDecoration = require('./layer-decoration')
|
|
|
|
module.exports =
|
|
class DecorationManager {
|
|
constructor(displayLayer) {
|
|
this.displayLayer = displayLayer
|
|
|
|
this.emitter = new Emitter
|
|
this.decorationsById = {}
|
|
this.decorationsByMarkerId = {}
|
|
this.overlayDecorationsById = {}
|
|
this.layerDecorationsByMarkerLayerId = {}
|
|
this.decorationCountsByLayerId = {}
|
|
this.layerUpdateDisposablesByLayerId = {}
|
|
}
|
|
|
|
observeDecorations(callback) {
|
|
for (let decoration of this.getDecorations()) { callback(decoration); }
|
|
return this.onDidAddDecoration(callback)
|
|
}
|
|
|
|
onDidAddDecoration(callback) {
|
|
return this.emitter.on('did-add-decoration', callback)
|
|
}
|
|
|
|
onDidRemoveDecoration(callback) {
|
|
return this.emitter.on('did-remove-decoration', callback)
|
|
}
|
|
|
|
onDidUpdateDecorations(callback) {
|
|
return this.emitter.on('did-update-decorations', callback)
|
|
}
|
|
|
|
decorationForId(id) {
|
|
return this.decorationsById[id]
|
|
}
|
|
|
|
getDecorations(propertyFilter) {
|
|
let allDecorations = []
|
|
for (let markerId in this.decorationsByMarkerId) {
|
|
const decorations = this.decorationsByMarkerId[markerId]
|
|
if (decorations != null) {
|
|
allDecorations.push(...decorations)
|
|
}
|
|
}
|
|
if (propertyFilter != null) {
|
|
allDecorations = allDecorations.filter(function(decoration) {
|
|
for (let key in propertyFilter) {
|
|
const value = propertyFilter[key]
|
|
if (decoration.properties[key] !== value) return false
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
return allDecorations
|
|
}
|
|
|
|
getLineDecorations(propertyFilter) {
|
|
return this.getDecorations(propertyFilter).filter(decoration => decoration.isType('line'))
|
|
}
|
|
|
|
getLineNumberDecorations(propertyFilter) {
|
|
return this.getDecorations(propertyFilter).filter(decoration => decoration.isType('line-number'))
|
|
}
|
|
|
|
getHighlightDecorations(propertyFilter) {
|
|
return this.getDecorations(propertyFilter).filter(decoration => decoration.isType('highlight'))
|
|
}
|
|
|
|
getOverlayDecorations(propertyFilter) {
|
|
const result = []
|
|
for (let id in this.overlayDecorationsById) {
|
|
const decoration = this.overlayDecorationsById[id]
|
|
result.push(decoration)
|
|
}
|
|
if (propertyFilter != null) {
|
|
return result.filter(function(decoration) {
|
|
for (let key in propertyFilter) {
|
|
const value = propertyFilter[key]
|
|
if (decoration.properties[key] !== value) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
} else {
|
|
return result
|
|
}
|
|
}
|
|
|
|
decorationsForScreenRowRange(startScreenRow, endScreenRow) {
|
|
const decorationsByMarkerId = {}
|
|
for (let layerId in this.decorationCountsByLayerId) {
|
|
const layer = this.displayLayer.getMarkerLayer(layerId)
|
|
for (let marker of layer.findMarkers({intersectsScreenRowRange: [startScreenRow, endScreenRow]})) {
|
|
const decorations = this.decorationsByMarkerId[marker.id]
|
|
if (decorations) {
|
|
decorationsByMarkerId[marker.id] = decorations
|
|
}
|
|
}
|
|
}
|
|
return decorationsByMarkerId
|
|
}
|
|
|
|
decorationsStateForScreenRowRange(startScreenRow, endScreenRow) {
|
|
const decorationsState = {}
|
|
|
|
for (let layerId in this.decorationCountsByLayerId) {
|
|
const layer = this.displayLayer.getMarkerLayer(layerId)
|
|
|
|
for (let marker of layer.findMarkers({intersectsScreenRowRange: [startScreenRow, endScreenRow]})) {
|
|
if (marker.isValid()) {
|
|
const screenRange = marker.getScreenRange()
|
|
const bufferRange = marker.getBufferRange()
|
|
const rangeIsReversed = marker.isReversed()
|
|
|
|
const decorations = this.decorationsByMarkerId[marker.id]
|
|
if (decorations) {
|
|
for (let decoration of decorations) {
|
|
decorationsState[decoration.id] = {
|
|
properties: decoration.properties,
|
|
screenRange, bufferRange, rangeIsReversed
|
|
}
|
|
}
|
|
}
|
|
|
|
const layerDecorations = this.layerDecorationsByMarkerLayerId[layerId]
|
|
if (layerDecorations) {
|
|
for (let layerDecoration of layerDecorations) {
|
|
decorationsState[`${layerDecoration.id}-${marker.id}`] = {
|
|
properties: layerDecoration.overridePropertiesByMarkerId[marker.id] != null ? layerDecoration.overridePropertiesByMarkerId[marker.id] : layerDecoration.properties,
|
|
screenRange, bufferRange, rangeIsReversed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return decorationsState
|
|
}
|
|
|
|
decorateMarker(marker, decorationParams) {
|
|
if (marker.isDestroyed()) {
|
|
const error = new Error("Cannot decorate a destroyed marker")
|
|
error.metadata = {markerLayerIsDestroyed: marker.layer.isDestroyed()}
|
|
if (marker.destroyStackTrace != null) {
|
|
error.metadata.destroyStackTrace = marker.destroyStackTrace
|
|
}
|
|
if (marker.bufferMarker != null && marker.bufferMarker.destroyStackTrace != null) {
|
|
error.metadata.destroyStackTrace = marker.bufferMarker.destroyStackTrace
|
|
}
|
|
throw error
|
|
}
|
|
marker = this.displayLayer.getMarkerLayer(marker.layer.id).getMarker(marker.id)
|
|
const decoration = new Decoration(marker, this, decorationParams)
|
|
if (this.decorationsByMarkerId[marker.id] == null) {
|
|
this.decorationsByMarkerId[marker.id] = []
|
|
}
|
|
this.decorationsByMarkerId[marker.id].push(decoration)
|
|
if (decoration.isType('overlay')) {
|
|
this.overlayDecorationsById[decoration.id] = decoration
|
|
}
|
|
this.decorationsById[decoration.id] = decoration
|
|
this.observeDecoratedLayer(marker.layer)
|
|
this.emitDidUpdateDecorations()
|
|
this.emitter.emit('did-add-decoration', decoration)
|
|
return decoration
|
|
}
|
|
|
|
decorateMarkerLayer(markerLayer, decorationParams) {
|
|
if (markerLayer.isDestroyed()) {
|
|
throw new Error("Cannot decorate a destroyed marker layer")
|
|
}
|
|
const decoration = new LayerDecoration(markerLayer, this, decorationParams)
|
|
if (this.layerDecorationsByMarkerLayerId[markerLayer.id] == null) {
|
|
this.layerDecorationsByMarkerLayerId[markerLayer.id] = []
|
|
}
|
|
this.layerDecorationsByMarkerLayerId[markerLayer.id].push(decoration)
|
|
this.observeDecoratedLayer(markerLayer)
|
|
this.emitDidUpdateDecorations()
|
|
return decoration
|
|
}
|
|
|
|
decorationsForMarkerId(markerId) {
|
|
return this.decorationsByMarkerId[markerId]
|
|
}
|
|
|
|
emitDidUpdateDecorations() {
|
|
this.emitter.emit('did-update-decorations')
|
|
}
|
|
|
|
decorationDidChangeType(decoration) {
|
|
if (decoration.isType('overlay')) {
|
|
return this.overlayDecorationsById[decoration.id] = decoration
|
|
} else {
|
|
return delete this.overlayDecorationsById[decoration.id]
|
|
}
|
|
}
|
|
|
|
didDestroyMarkerDecoration(decoration) {
|
|
let decorations
|
|
const {marker} = decoration
|
|
if (!(decorations = this.decorationsByMarkerId[marker.id])) return
|
|
const index = decorations.indexOf(decoration)
|
|
|
|
if (index > -1) {
|
|
decorations.splice(index, 1)
|
|
delete this.decorationsById[decoration.id]
|
|
this.emitter.emit('did-remove-decoration', decoration)
|
|
if (decorations.length === 0) {
|
|
delete this.decorationsByMarkerId[marker.id]
|
|
}
|
|
delete this.overlayDecorationsById[decoration.id]
|
|
this.unobserveDecoratedLayer(marker.layer)
|
|
}
|
|
return this.emitDidUpdateDecorations()
|
|
}
|
|
|
|
didDestroyLayerDecoration(decoration) {
|
|
let decorations
|
|
const {markerLayer} = decoration
|
|
if (!(decorations = this.layerDecorationsByMarkerLayerId[markerLayer.id])) return
|
|
const index = decorations.indexOf(decoration)
|
|
|
|
if (index > -1) {
|
|
decorations.splice(index, 1)
|
|
if (decorations.length === 0) {
|
|
delete this.layerDecorationsByMarkerLayerId[markerLayer.id]
|
|
}
|
|
this.unobserveDecoratedLayer(markerLayer)
|
|
}
|
|
return this.emitDidUpdateDecorations()
|
|
}
|
|
|
|
observeDecoratedLayer(layer) {
|
|
if (this.decorationCountsByLayerId[layer.id] == null) {
|
|
this.decorationCountsByLayerId[layer.id] = 0
|
|
}
|
|
if (++this.decorationCountsByLayerId[layer.id] === 1) {
|
|
this.layerUpdateDisposablesByLayerId[layer.id] = layer.onDidUpdate(this.emitDidUpdateDecorations.bind(this))
|
|
}
|
|
}
|
|
|
|
unobserveDecoratedLayer(layer) {
|
|
if (--this.decorationCountsByLayerId[layer.id] === 0) {
|
|
this.layerUpdateDisposablesByLayerId[layer.id].dispose()
|
|
delete this.decorationCountsByLayerId[layer.id]
|
|
delete this.layerUpdateDisposablesByLayerId[layer.id]
|
|
}
|
|
}
|
|
}
|