Fix rendering of block decorations for invalid markers

Previously, when a marker became invalid we would delete the
corresponding block decoration from the DOM, without however removing it
from the `lineTopIndex`. Also, we had a similar issue when decorating a
marker that was already invalid: we would account for it in the index
without adding it to the DOM.

This commit addresses the above problems by ensuring that block
decorations are never added for invalid markers. At the same time, the
editor component will still keep track of changes on the marker that was
decorated, so that it can detect when its validity changes and render it
appropriately.
This commit is contained in:
Antonio Scandurra
2017-08-30 12:38:44 +02:00
parent d70dcaf819
commit 9bf47f31cb
3 changed files with 137 additions and 26 deletions

View File

@@ -2460,37 +2460,61 @@ class TextEditorComponent {
const {model} = this.props
const decorations = model.getDecorations({type: 'block'})
for (let i = 0; i < decorations.length; i++) {
this.didAddBlockDecoration(decorations[i])
this.addBlockDecoration(decorations[i])
}
}
didAddBlockDecoration (decoration) {
addBlockDecoration (decoration, subscribeToChanges = true) {
const marker = decoration.getMarker()
const {item, position} = decoration.getProperties()
const element = TextEditor.viewForItem(item)
const row = marker.getHeadScreenPosition().row
this.lineTopIndex.insertBlock(decoration, row, 0, position === 'after')
this.blockDecorationsToMeasure.add(decoration)
this.blockDecorationsByElement.set(element, decoration)
this.blockDecorationResizeObserver.observe(element)
if (marker.isValid()) {
const row = marker.getHeadScreenPosition().row
this.lineTopIndex.insertBlock(decoration, row, 0, position === 'after')
this.blockDecorationsToMeasure.add(decoration)
this.blockDecorationsByElement.set(element, decoration)
this.blockDecorationResizeObserver.observe(element)
const didUpdateDisposable = marker.bufferMarker.onDidChange((e) => {
if (!e.textChanged) {
this.lineTopIndex.moveBlock(decoration, marker.getHeadScreenPosition().row)
this.scheduleUpdate()
}
})
const didDestroyDisposable = decoration.onDidDestroy(() => {
this.blockDecorationsToMeasure.delete(decoration)
this.heightsByBlockDecoration.delete(decoration)
this.blockDecorationsByElement.delete(element)
this.blockDecorationResizeObserver.unobserve(element)
this.lineTopIndex.removeBlock(decoration)
didUpdateDisposable.dispose()
didDestroyDisposable.dispose()
this.scheduleUpdate()
})
}
if (subscribeToChanges) {
let wasValid = marker.isValid()
const didUpdateDisposable = marker.bufferMarker.onDidChange(({textChanged}) => {
const isValid = marker.isValid()
if (wasValid && !isValid) {
wasValid = false
this.blockDecorationsToMeasure.delete(decoration)
this.heightsByBlockDecoration.delete(decoration)
this.blockDecorationsByElement.delete(element)
this.blockDecorationResizeObserver.unobserve(element)
this.lineTopIndex.removeBlock(decoration)
this.scheduleUpdate()
} else if (!wasValid && isValid) {
wasValid = true
this.addBlockDecoration(decoration, false)
} else if (isValid && !textChanged) {
this.lineTopIndex.moveBlock(decoration, marker.getHeadScreenPosition().row)
this.scheduleUpdate()
}
})
const didDestroyDisposable = decoration.onDidDestroy(() => {
didUpdateDisposable.dispose()
didDestroyDisposable.dispose()
if (marker.isValid()) {
this.blockDecorationsToMeasure.delete(decoration)
this.heightsByBlockDecoration.delete(decoration)
this.blockDecorationsByElement.delete(element)
this.blockDecorationResizeObserver.unobserve(element)
this.lineTopIndex.removeBlock(decoration)
this.scheduleUpdate()
}
})
}
}
didResizeBlockDecorations (entries) {