mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 10:17:11 +03:00
Splice LineTopIndex when DisplayBuffer changes
We invalidate whole screen lines accordingly to `DisplayBuffer`, so that we can catch if there was any screen-only transformation and move block decorations accordingly.
This commit is contained in:
parent
8710089cb7
commit
5fa9d3bc40
@ -169,38 +169,87 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
it "computes each tile's height and scrollTop based on block decorations' height", ->
|
||||
presenter = buildPresenter(explicitHeight: 120, scrollTop: 0, lineHeight: 10, tileSize: 2)
|
||||
describe "when there are block decorations", ->
|
||||
it "computes each tile's height and scrollTop based on block decorations' height", ->
|
||||
presenter = buildPresenter(explicitHeight: 120, scrollTop: 0, lineHeight: 10, tileSize: 2)
|
||||
|
||||
blockDecoration1 = editor.addBlockDecorationForScreenRow(0)
|
||||
blockDecoration2 = editor.addBlockDecorationForScreenRow(3)
|
||||
blockDecoration3 = editor.addBlockDecorationForScreenRow(5)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration1, 0, 1)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration2, 0, 30)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration3, 0, 40)
|
||||
blockDecoration1 = editor.addBlockDecorationForScreenRow(0)
|
||||
blockDecoration2 = editor.addBlockDecorationForScreenRow(3)
|
||||
blockDecoration3 = editor.addBlockDecorationForScreenRow(5)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration1, 0, 1)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration2, 0, 30)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration3, 0, 40)
|
||||
|
||||
expect(stateFn(presenter).tiles[0].height).toBe(20 + 1)
|
||||
expect(stateFn(presenter).tiles[0].top).toBe(0)
|
||||
expect(stateFn(presenter).tiles[2].height).toBe(20 + 30)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(20 + 1)
|
||||
expect(stateFn(presenter).tiles[4].height).toBe(20 + 40)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(20 + 30 + 20 + 1)
|
||||
expect(stateFn(presenter).tiles[6].height).toBe(20)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe((20 + 40) + (20 + 30) + 20 + 1)
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[0].height).toBe(20 + 1)
|
||||
expect(stateFn(presenter).tiles[0].top).toBe(0)
|
||||
expect(stateFn(presenter).tiles[2].height).toBe(20 + 30)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(20 + 1)
|
||||
expect(stateFn(presenter).tiles[4].height).toBe(20 + 40)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(20 + 30 + 20 + 1)
|
||||
expect(stateFn(presenter).tiles[6].height).toBe(20)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe((20 + 40) + (20 + 30) + 20 + 1)
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
|
||||
presenter.setScrollTop(21)
|
||||
presenter.setScrollTop(21)
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[2].height).toBe(20 + 30)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(0)
|
||||
expect(stateFn(presenter).tiles[4].height).toBe(20 + 40)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(30 + 20)
|
||||
expect(stateFn(presenter).tiles[6].height).toBe(20)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe((20 + 40) + (20 + 30))
|
||||
expect(stateFn(presenter).tiles[8].height).toBe(20)
|
||||
expect(stateFn(presenter).tiles[8].top).toBe((20 + 40) + (20 + 30) + 20)
|
||||
expect(stateFn(presenter).tiles[10]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[0]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[2].height).toBe(20 + 30)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(0)
|
||||
expect(stateFn(presenter).tiles[4].height).toBe(20 + 40)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(30 + 20)
|
||||
expect(stateFn(presenter).tiles[6].height).toBe(20)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe((20 + 40) + (20 + 30))
|
||||
expect(stateFn(presenter).tiles[8].height).toBe(20)
|
||||
expect(stateFn(presenter).tiles[8].top).toBe((20 + 40) + (20 + 30) + 20)
|
||||
expect(stateFn(presenter).tiles[10]).toBeUndefined()
|
||||
|
||||
it "works correctly when soft wrapping is enabled", ->
|
||||
blockDecoration1 = editor.addBlockDecorationForScreenRow(0, null)
|
||||
blockDecoration2 = editor.addBlockDecorationForScreenRow(4, null)
|
||||
blockDecoration3 = editor.addBlockDecorationForScreenRow(8, null)
|
||||
|
||||
presenter = buildPresenter(explicitHeight: 330, lineHeight: 10, tileSize: 2, baseCharacterWidth: 5)
|
||||
|
||||
presenter.setBlockDecorationDimensions(blockDecoration1, 0, 10)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration2, 0, 20)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration3, 0, 30)
|
||||
|
||||
expect(stateFn(presenter).tiles[0].top).toBe(0 * 10)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(2 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(4 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe(6 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[8].top).toBe(8 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[10].top).toBe(10 * 10 + 10 + 20 + 30)
|
||||
expect(stateFn(presenter).tiles[12].top).toBe(12 * 10 + 10 + 20 + 30)
|
||||
|
||||
editor.setSoftWrapped(true)
|
||||
presenter.setContentFrameWidth(5 * 25)
|
||||
|
||||
expect(stateFn(presenter).tiles[0].top).toBe(0 * 10)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(2 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(4 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe(6 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[8].top).toBe(8 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[10].top).toBe(10 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[12].top).toBe(12 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[14].top).toBe(14 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[16].top).toBe(16 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[18].top).toBe(18 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[20].top).toBe(20 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[22].top).toBe(22 * 10 + 10 + 20 + 30)
|
||||
expect(stateFn(presenter).tiles[24].top).toBe(24 * 10 + 10 + 20 + 30)
|
||||
expect(stateFn(presenter).tiles[26].top).toBe(26 * 10 + 10 + 20 + 30)
|
||||
expect(stateFn(presenter).tiles[28].top).toBe(28 * 10 + 10 + 20 + 30)
|
||||
|
||||
editor.setSoftWrapped(false)
|
||||
|
||||
expect(stateFn(presenter).tiles[0].top).toBe(0 * 10)
|
||||
expect(stateFn(presenter).tiles[2].top).toBe(2 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[4].top).toBe(4 * 10 + 10)
|
||||
expect(stateFn(presenter).tiles[6].top).toBe(6 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[8].top).toBe(8 * 10 + 10 + 20)
|
||||
expect(stateFn(presenter).tiles[10].top).toBe(10 * 10 + 10 + 20 + 30)
|
||||
expect(stateFn(presenter).tiles[12].top).toBe(12 * 10 + 10 + 20 + 30)
|
||||
|
||||
it "includes state for all tiles if no external ::explicitHeight is assigned", ->
|
||||
presenter = buildPresenter(explicitHeight: null, tileSize: 2)
|
||||
|
@ -29,8 +29,8 @@ class TextEditorPresenter
|
||||
@lineNumberDecorationsByScreenRow = {}
|
||||
@customGutterDecorationsByGutterName = {}
|
||||
@observedBlockDecorations = new Set()
|
||||
@invalidatedBlockDecorations = new Set()
|
||||
@invalidateAllBlockDecorations = false
|
||||
@invalidatedDimensionsByBlockDecoration = new Set()
|
||||
@invalidateAllBlockDecorationsDimensions = false
|
||||
@screenRowsToMeasure = []
|
||||
@transferMeasurementsToModel()
|
||||
@transferMeasurementsFromModel()
|
||||
@ -130,7 +130,8 @@ class TextEditorPresenter
|
||||
@shouldUpdateDecorations = true
|
||||
|
||||
observeModel: ->
|
||||
@disposables.add @model.onDidChange =>
|
||||
@disposables.add @model.onDidChange ({start, end, screenDelta}) =>
|
||||
@spliceBlockDecorationsInRange(start, end, screenDelta)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@ -139,10 +140,6 @@ class TextEditorPresenter
|
||||
@emitDidUpdateState()
|
||||
|
||||
@disposables.add @model.onDidAddDecoration(@didAddBlockDecoration.bind(this))
|
||||
@disposables.add @model.buffer.onDidChange ({oldRange, newRange}) =>
|
||||
oldExtent = oldRange.getExtent()
|
||||
newExtent = newRange.getExtent()
|
||||
@lineTopIndex.splice(oldRange.start, oldExtent, newExtent)
|
||||
|
||||
for decoration in @model.getDecorations({type: 'block'})
|
||||
this.didAddBlockDecoration(decoration)
|
||||
@ -990,7 +987,7 @@ class TextEditorPresenter
|
||||
@measurementsChanged()
|
||||
|
||||
measurementsChanged: ->
|
||||
@invalidateAllBlockDecorations = true
|
||||
@invalidateAllBlockDecorationsDimensions = true
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@ -1048,16 +1045,16 @@ class TextEditorPresenter
|
||||
@blockDecorationsByScreenRow = {}
|
||||
visibleDecorationsByMarkerId = @model.decorationsForScreenRowRange(@getStartTileRow(), @getEndTileRow() + @tileSize - 1)
|
||||
|
||||
if @invalidateAllBlockDecorations
|
||||
if @invalidateAllBlockDecorationsDimensions
|
||||
for decoration in @model.getDecorations(type: 'block')
|
||||
@invalidatedBlockDecorations.add(decoration)
|
||||
@invalidateAllBlockDecorations = false
|
||||
@invalidatedDimensionsByBlockDecoration.add(decoration)
|
||||
@invalidateAllBlockDecorationsDimensions = false
|
||||
|
||||
for markerId, decorations of visibleDecorationsByMarkerId
|
||||
for decoration in decorations when decoration.isType('block')
|
||||
@updateBlockDecorationState(decoration, true)
|
||||
|
||||
@invalidatedBlockDecorations.forEach (decoration) =>
|
||||
@invalidatedDimensionsByBlockDecoration.forEach (decoration) =>
|
||||
@updateBlockDecorationState(decoration, false)
|
||||
|
||||
for decorationId, decorationState of @state.content.blockDecorations
|
||||
@ -1270,15 +1267,25 @@ class TextEditorPresenter
|
||||
setBlockDecorationDimensions: (decoration, width, height) ->
|
||||
@lineTopIndex.resizeBlock(decoration.getId(), height)
|
||||
|
||||
@invalidatedBlockDecorations.delete(decoration)
|
||||
@invalidatedDimensionsByBlockDecoration.delete(decoration)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
invalidateBlockDecorationDimensions: (decoration) ->
|
||||
@invalidatedBlockDecorations.add(decoration)
|
||||
@invalidatedDimensionsByBlockDecoration.add(decoration)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
spliceBlockDecorationsInRange: (start, end, screenDelta) ->
|
||||
return if screenDelta is 0
|
||||
|
||||
oldExtent = Point(end - start, Infinity)
|
||||
newExtent = Point(end - start + screenDelta, 0)
|
||||
invalidatedBlockDecorationIds = @lineTopIndex.splice(Point(start, 0), oldExtent, newExtent, true)
|
||||
invalidatedBlockDecorationIds?.forEach (blockDecorationId) =>
|
||||
decoration = @model.decorationForId(blockDecorationId)
|
||||
@lineTopIndex.moveBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition())
|
||||
|
||||
didAddBlockDecoration: (decoration) ->
|
||||
return if not decoration.isType('block') or @observedBlockDecorations.has(decoration)
|
||||
|
||||
@ -1292,12 +1299,7 @@ class TextEditorPresenter
|
||||
didDestroyDisposable.dispose()
|
||||
@didDestroyBlockDecoration(decoration)
|
||||
|
||||
@lineTopIndex.insertBlock(
|
||||
decoration.getId(),
|
||||
decoration.getMarker().getHeadBufferPosition(),
|
||||
true,
|
||||
0
|
||||
)
|
||||
@lineTopIndex.insertBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition(), true, 0)
|
||||
|
||||
@observedBlockDecorations.add(decoration)
|
||||
@invalidateBlockDecorationDimensions(decoration)
|
||||
@ -1311,7 +1313,7 @@ class TextEditorPresenter
|
||||
# change.
|
||||
return if markerEvent.textChanged
|
||||
|
||||
@lineTopIndex.moveBlock(decoration.getId(), decoration.getMarker().getHeadBufferPosition())
|
||||
@lineTopIndex.moveBlock(decoration.getId(), decoration.getMarker().getHeadScreenPosition())
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
@ -1320,7 +1322,7 @@ class TextEditorPresenter
|
||||
|
||||
@lineTopIndex.removeBlock(decoration.getId())
|
||||
@observedBlockDecorations.delete(decoration)
|
||||
@invalidatedBlockDecorations.delete(decoration)
|
||||
@invalidatedDimensionsByBlockDecoration.delete(decoration)
|
||||
@shouldUpdateDecorations = true
|
||||
@emitDidUpdateState()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user