mirror of
https://github.com/atom/atom.git
synced 2026-01-25 06:48:28 -05:00
Convert DecorationManager to JS
This commit is contained in:
committed by
Antonio Scandurra
parent
94294d1b92
commit
30cd83f7aa
@@ -14,8 +14,7 @@ describe "DecorationManager", ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
afterEach ->
|
||||
decorationManager.destroy()
|
||||
buffer.release()
|
||||
buffer.destroy()
|
||||
|
||||
describe "decorations", ->
|
||||
[layer1Marker, layer2Marker, layer1MarkerDecoration, layer2MarkerDecoration, decorationProperties] = []
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
{Emitter} = require 'event-kit'
|
||||
Model = require './model'
|
||||
Decoration = require './decoration'
|
||||
LayerDecoration = require './layer-decoration'
|
||||
|
||||
module.exports =
|
||||
class DecorationManager extends Model
|
||||
didUpdateDecorationsEventScheduled: false
|
||||
updatedSynchronously: false
|
||||
|
||||
constructor: (@displayLayer) ->
|
||||
super
|
||||
|
||||
@emitter = new Emitter
|
||||
@decorationsById = {}
|
||||
@decorationsByMarkerId = {}
|
||||
@overlayDecorationsById = {}
|
||||
@layerDecorationsByMarkerLayerId = {}
|
||||
@decorationCountsByLayerId = {}
|
||||
@layerUpdateDisposablesByLayerId = {}
|
||||
|
||||
observeDecorations: (callback) ->
|
||||
callback(decoration) for decoration in @getDecorations()
|
||||
@onDidAddDecoration(callback)
|
||||
|
||||
onDidAddDecoration: (callback) ->
|
||||
@emitter.on 'did-add-decoration', callback
|
||||
|
||||
onDidRemoveDecoration: (callback) ->
|
||||
@emitter.on 'did-remove-decoration', callback
|
||||
|
||||
onDidUpdateDecorations: (callback) ->
|
||||
@emitter.on 'did-update-decorations', callback
|
||||
|
||||
setUpdatedSynchronously: (@updatedSynchronously) ->
|
||||
|
||||
decorationForId: (id) ->
|
||||
@decorationsById[id]
|
||||
|
||||
getDecorations: (propertyFilter) ->
|
||||
allDecorations = []
|
||||
for markerId, decorations of @decorationsByMarkerId
|
||||
allDecorations.push(decorations...) if decorations?
|
||||
if propertyFilter?
|
||||
allDecorations = allDecorations.filter (decoration) ->
|
||||
for key, value of propertyFilter
|
||||
return false unless decoration.properties[key] is value
|
||||
true
|
||||
allDecorations
|
||||
|
||||
getLineDecorations: (propertyFilter) ->
|
||||
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line')
|
||||
|
||||
getLineNumberDecorations: (propertyFilter) ->
|
||||
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('line-number')
|
||||
|
||||
getHighlightDecorations: (propertyFilter) ->
|
||||
@getDecorations(propertyFilter).filter (decoration) -> decoration.isType('highlight')
|
||||
|
||||
getOverlayDecorations: (propertyFilter) ->
|
||||
result = []
|
||||
for id, decoration of @overlayDecorationsById
|
||||
result.push(decoration)
|
||||
if propertyFilter?
|
||||
result.filter (decoration) ->
|
||||
for key, value of propertyFilter
|
||||
return false unless decoration.properties[key] is value
|
||||
true
|
||||
else
|
||||
result
|
||||
|
||||
decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsByMarkerId = {}
|
||||
for layerId of @decorationCountsByLayerId
|
||||
layer = @displayLayer.getMarkerLayer(layerId)
|
||||
for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow])
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
decorationsByMarkerId[marker.id] = decorations
|
||||
decorationsByMarkerId
|
||||
|
||||
decorationsStateForScreenRowRange: (startScreenRow, endScreenRow) ->
|
||||
decorationsState = {}
|
||||
|
||||
for layerId of @decorationCountsByLayerId
|
||||
layer = @displayLayer.getMarkerLayer(layerId)
|
||||
|
||||
for marker in layer.findMarkers(intersectsScreenRowRange: [startScreenRow, endScreenRow]) when marker.isValid()
|
||||
screenRange = marker.getScreenRange()
|
||||
bufferRange = marker.getBufferRange()
|
||||
rangeIsReversed = marker.isReversed()
|
||||
|
||||
if decorations = @decorationsByMarkerId[marker.id]
|
||||
for decoration in decorations
|
||||
decorationsState[decoration.id] = {
|
||||
properties: decoration.properties
|
||||
screenRange, bufferRange, rangeIsReversed
|
||||
}
|
||||
|
||||
if layerDecorations = @layerDecorationsByMarkerLayerId[layerId]
|
||||
for layerDecoration in layerDecorations
|
||||
decorationsState["#{layerDecoration.id}-#{marker.id}"] = {
|
||||
properties: layerDecoration.overridePropertiesByMarkerId[marker.id] ? layerDecoration.properties
|
||||
screenRange, bufferRange, rangeIsReversed
|
||||
}
|
||||
|
||||
decorationsState
|
||||
|
||||
decorateMarker: (marker, decorationParams) ->
|
||||
if marker.isDestroyed()
|
||||
error = new Error("Cannot decorate a destroyed marker")
|
||||
error.metadata = {markerLayerIsDestroyed: marker.layer.isDestroyed()}
|
||||
if marker.destroyStackTrace?
|
||||
error.metadata.destroyStackTrace = marker.destroyStackTrace
|
||||
if marker.bufferMarker?.destroyStackTrace?
|
||||
error.metadata.destroyStackTrace = marker.bufferMarker?.destroyStackTrace
|
||||
throw error
|
||||
marker = @displayLayer.getMarkerLayer(marker.layer.id).getMarker(marker.id)
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@overlayDecorationsById[decoration.id] = decoration if decoration.isType('overlay')
|
||||
@decorationsById[decoration.id] = decoration
|
||||
@observeDecoratedLayer(marker.layer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
@emitter.emit 'did-add-decoration', decoration
|
||||
decoration
|
||||
|
||||
decorateMarkerLayer: (markerLayer, decorationParams) ->
|
||||
throw new Error("Cannot decorate a destroyed marker layer") if markerLayer.isDestroyed()
|
||||
decoration = new LayerDecoration(markerLayer, this, decorationParams)
|
||||
@layerDecorationsByMarkerLayerId[markerLayer.id] ?= []
|
||||
@layerDecorationsByMarkerLayerId[markerLayer.id].push(decoration)
|
||||
@observeDecoratedLayer(markerLayer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
decoration
|
||||
|
||||
decorationsForMarkerId: (markerId) ->
|
||||
@decorationsByMarkerId[markerId]
|
||||
|
||||
scheduleUpdateDecorationsEvent: ->
|
||||
if @updatedSynchronously
|
||||
@emitter.emit 'did-update-decorations'
|
||||
return
|
||||
|
||||
unless @didUpdateDecorationsEventScheduled
|
||||
@didUpdateDecorationsEventScheduled = true
|
||||
process.nextTick =>
|
||||
@didUpdateDecorationsEventScheduled = false
|
||||
@emitter.emit 'did-update-decorations'
|
||||
|
||||
decorationDidChangeType: (decoration) ->
|
||||
if decoration.isType('overlay')
|
||||
@overlayDecorationsById[decoration.id] = decoration
|
||||
else
|
||||
delete @overlayDecorationsById[decoration.id]
|
||||
|
||||
didDestroyMarkerDecoration: (decoration) ->
|
||||
{marker} = decoration
|
||||
return unless decorations = @decorationsByMarkerId[marker.id]
|
||||
index = decorations.indexOf(decoration)
|
||||
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @decorationsById[decoration.id]
|
||||
@emitter.emit 'did-remove-decoration', decoration
|
||||
delete @decorationsByMarkerId[marker.id] if decorations.length is 0
|
||||
delete @overlayDecorationsById[decoration.id]
|
||||
@unobserveDecoratedLayer(marker.layer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
|
||||
didDestroyLayerDecoration: (decoration) ->
|
||||
{markerLayer} = decoration
|
||||
return unless decorations = @layerDecorationsByMarkerLayerId[markerLayer.id]
|
||||
index = decorations.indexOf(decoration)
|
||||
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @layerDecorationsByMarkerLayerId[markerLayer.id] if decorations.length is 0
|
||||
@unobserveDecoratedLayer(markerLayer)
|
||||
@scheduleUpdateDecorationsEvent()
|
||||
|
||||
observeDecoratedLayer: (layer) ->
|
||||
@decorationCountsByLayerId[layer.id] ?= 0
|
||||
if ++@decorationCountsByLayerId[layer.id] is 1
|
||||
@layerUpdateDisposablesByLayerId[layer.id] = layer.onDidUpdate(@scheduleUpdateDecorationsEvent.bind(this))
|
||||
|
||||
unobserveDecoratedLayer: (layer) ->
|
||||
if --@decorationCountsByLayerId[layer.id] is 0
|
||||
@layerUpdateDisposablesByLayerId[layer.id].dispose()
|
||||
delete @decorationCountsByLayerId[layer.id]
|
||||
delete @layerUpdateDisposablesByLayerId[layer.id]
|
||||
272
src/decoration-manager.js
Normal file
272
src/decoration-manager.js
Normal file
@@ -0,0 +1,272 @@
|
||||
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.didUpdateDecorationsEventScheduled = false
|
||||
this.updatedSynchronously = false
|
||||
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)
|
||||
}
|
||||
|
||||
setUpdatedSynchronously(updatedSynchronously) {
|
||||
this.updatedSynchronously = updatedSynchronously
|
||||
}
|
||||
|
||||
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.scheduleUpdateDecorationsEvent()
|
||||
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.scheduleUpdateDecorationsEvent()
|
||||
return decoration
|
||||
}
|
||||
|
||||
decorationsForMarkerId(markerId) {
|
||||
return this.decorationsByMarkerId[markerId]
|
||||
}
|
||||
|
||||
scheduleUpdateDecorationsEvent() {
|
||||
if (this.updatedSynchronously) {
|
||||
this.emitter.emit('did-update-decorations')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.didUpdateDecorationsEventScheduled) {
|
||||
this.didUpdateDecorationsEventScheduled = true
|
||||
return process.nextTick(() => {
|
||||
this.didUpdateDecorationsEventScheduled = false
|
||||
return 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.scheduleUpdateDecorationsEvent()
|
||||
}
|
||||
|
||||
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.scheduleUpdateDecorationsEvent()
|
||||
}
|
||||
|
||||
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.scheduleUpdateDecorationsEvent.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]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user