pulsar/spec/row-map-spec.coffee
Nathan Sobo 0192c57f46 Fix corner case in RowMap::mapBufferRowRange w/ 0-buffer-row regions
Fixes #688

The DisplayBuffer applies buffer and screen deltas to the row map as
rows are inserted/removed from the buffer/screen. This can leave some
of the regions in a weird state, such as mapping multiple screen rows
to zero buffer rows. But next the DisplayBuffer applies any new mappings
based on the replaced lines over the top of existing regions. These
weirdly shaped regions should be overwritten by newly inserted regions,
so at the end of the operation the row map makes sense again.

This fixes a corner case where regions spanning 0 buffer rows at the
very beginning of the row range were not being included in the set of
regions to replace. This was in turn causing the RowMap to get into a
bad state in certain situations involving soft-wrapped lines.
2013-08-20 19:30:29 -06:00

262 lines
12 KiB
CoffeeScript

RowMap = require 'row-map'
describe "RowMap", ->
map = null
beforeEach ->
map = new RowMap
describe "when no mappings have been recorded", ->
it "maps buffer rows to screen rows 1:1", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(100)).toEqual [100, 101]
describe ".mapBufferRowRange(startBufferRow, endBufferRow, screenRows)", ->
describe "when mapping to a single screen row (like a visible fold)", ->
beforeEach ->
map.mapBufferRowRange(5, 10, 1)
map.mapBufferRowRange(15, 20, 1)
map.mapBufferRowRange(25, 30, 1)
it "accounts for the mapping when translating buffer rows to screen row ranges", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(10)).toEqual [6, 7]
expect(map.screenRowRangeForBufferRow(14)).toEqual [10, 11]
expect(map.screenRowRangeForBufferRow(15)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(19)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(20)).toEqual [12, 13]
expect(map.screenRowRangeForBufferRow(24)).toEqual [16, 17]
expect(map.screenRowRangeForBufferRow(25)).toEqual [17, 18]
expect(map.screenRowRangeForBufferRow(29)).toEqual [17, 18]
expect(map.screenRowRangeForBufferRow(30)).toEqual [18, 19]
it "accounts for the mapping when translating screen rows to buffer row ranges", ->
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
expect(map.bufferRowRangeForScreenRow(6)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(10)).toEqual [14, 15]
expect(map.bufferRowRangeForScreenRow(11)).toEqual [15, 20]
expect(map.bufferRowRangeForScreenRow(12)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(16)).toEqual [24, 25]
expect(map.bufferRowRangeForScreenRow(17)).toEqual [25, 30]
expect(map.bufferRowRangeForScreenRow(18)).toEqual [30, 31]
describe "when mapping to zero screen rows (like an invisible fold)", ->
beforeEach ->
map.mapBufferRowRange(5, 10, 0)
map.mapBufferRowRange(15, 20, 0)
map.mapBufferRowRange(25, 30, 0)
it "accounts for the mapping when translating buffer rows to screen row ranges", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 5]
expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 5]
expect(map.screenRowRangeForBufferRow(10)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(14)).toEqual [9, 10]
expect(map.screenRowRangeForBufferRow(15)).toEqual [10, 10]
expect(map.screenRowRangeForBufferRow(19)).toEqual [10, 10]
expect(map.screenRowRangeForBufferRow(20)).toEqual [10, 11]
expect(map.screenRowRangeForBufferRow(24)).toEqual [14, 15]
expect(map.screenRowRangeForBufferRow(25)).toEqual [15, 15]
expect(map.screenRowRangeForBufferRow(29)).toEqual [15, 15]
expect(map.screenRowRangeForBufferRow(30)).toEqual [15, 16]
it "accounts for the mapping when translating screen rows to buffer row ranges", ->
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(9)).toEqual [14, 15]
expect(map.bufferRowRangeForScreenRow(10)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(14)).toEqual [24, 25]
expect(map.bufferRowRangeForScreenRow(15)).toEqual [30, 31]
describe "when mapping a single buffer row to multiple screen rows (like a wrapped line)", ->
beforeEach ->
map.mapBufferRowRange(5, 6, 3)
map.mapBufferRowRange(10, 11, 2)
map.mapBufferRowRange(20, 21, 5)
it "accounts for the mapping when translating buffer rows to screen row ranges", ->
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 1]
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 8]
expect(map.screenRowRangeForBufferRow(6)).toEqual [8, 9]
expect(map.screenRowRangeForBufferRow(9)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(10)).toEqual [12, 14]
expect(map.screenRowRangeForBufferRow(11)).toEqual [14, 15]
expect(map.screenRowRangeForBufferRow(19)).toEqual [22, 23]
expect(map.screenRowRangeForBufferRow(20)).toEqual [23, 28]
expect(map.screenRowRangeForBufferRow(21)).toEqual [28, 29]
it "accounts for the mapping when translating screen rows to buffer row ranges", ->
expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1]
expect(map.bufferRowRangeForScreenRow(4)).toEqual [4, 5]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 6]
expect(map.bufferRowRangeForScreenRow(7)).toEqual [5, 6]
expect(map.bufferRowRangeForScreenRow(8)).toEqual [6, 7]
expect(map.bufferRowRangeForScreenRow(11)).toEqual [9, 10]
expect(map.bufferRowRangeForScreenRow(12)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(13)).toEqual [10, 11]
expect(map.bufferRowRangeForScreenRow(14)).toEqual [11, 12]
expect(map.bufferRowRangeForScreenRow(22)).toEqual [19, 20]
expect(map.bufferRowRangeForScreenRow(23)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(27)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(28)).toEqual [21, 22]
describe "after re-mapping a row range to a new number of screen rows", ->
beforeEach ->
map.applyScreenDelta(12, 2) # simulate adding 2 more soft wraps
map.mapBufferRowRange(10, 11, 4)
it "updates translation accordingly", ->
expect(map.screenRowRangeForBufferRow(4)).toEqual [4, 5]
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 8]
expect(map.screenRowRangeForBufferRow(6)).toEqual [8, 9]
expect(map.screenRowRangeForBufferRow(9)).toEqual [11, 12]
expect(map.screenRowRangeForBufferRow(10)).toEqual [12, 16]
expect(map.screenRowRangeForBufferRow(11)).toEqual [16, 17]
expect(map.screenRowRangeForBufferRow(19)).toEqual [24, 25]
expect(map.screenRowRangeForBufferRow(20)).toEqual [25, 30]
expect(map.screenRowRangeForBufferRow(21)).toEqual [30, 31]
describe "when the row range is inside an existing 1:1 region", ->
it "preserves the starting screen row of subsequent 1:N regions", ->
map.mapBufferRowRange(5, 10, 1)
map.mapBufferRowRange(25, 30, 1)
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
expect(map.bufferRowRangeForScreenRow(21)).toEqual [25, 30]
map.mapBufferRowRange(15, 20, 1)
expect(map.bufferRowRangeForScreenRow(11)).toEqual [15, 20]
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 10]
expect(map.bufferRowRangeForScreenRow(21)).toEqual [25, 30]
describe "when the row range surrounds existing regions", ->
it "replaces the regions inside the given buffer row range with a single region", ->
map.mapBufferRowRange(5, 10, 1) # inner fold 1
map.mapBufferRowRange(11, 13, 1) # inner fold 2
map.mapBufferRowRange(15, 20, 1) # inner fold 3
map.mapBufferRowRange(22, 27, 1) # following fold
map.mapBufferRowRange(5, 20, 1)
expect(map.bufferRowRangeForScreenRow(5)).toEqual [5, 20]
expect(map.bufferRowRangeForScreenRow(6)).toEqual [20, 21]
expect(map.bufferRowRangeForScreenRow(7)).toEqual [21, 22]
expect(map.bufferRowRangeForScreenRow(8)).toEqual [22, 27]
it "replaces regions that cover 0 buffer rows at the start or end of the buffer row range", ->
map.mapBufferRowRange(0, 0, 1)
map.mapBufferRowRange(0, 1, 1)
map.mapBufferRowRange(1, 1, 1)
map.mapBufferRowRange(0, 1, 3)
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 3]
describe "when the row range straddles existing regions", ->
it "splits the straddled regions and places the new region between them", ->
# filler region 0
map.mapBufferRowRange(4, 7, 1) # region 1
# filler region 2
map.mapBufferRowRange(13, 15, 1) # region 3
# create region straddling region 0 and region 2
map.mapBufferRowRange(2, 10, 1)
expect(map.regions[0]).toEqual(bufferRows: 2, screenRows: 2)
expect(map.regions[1]).toEqual(bufferRows: 8, screenRows: 1)
expect(map.regions[2]).toEqual(bufferRows: 3, screenRows: 8)
expect(map.regions[3]).toEqual(bufferRows: 2, screenRows: 1)
it "merges adjacent isomorphic mappings", ->
map.mapBufferRowRange(2, 4, 1)
map.mapBufferRowRange(4, 5, 2)
map.mapBufferRowRange(1, 4, 3)
expect(map.regions).toEqual [{bufferRows: 5, screenRows: 5}]
describe ".applyBufferDelta(startBufferRow, delta)", ->
describe "when applying a positive delta", ->
it "expands the region containing the given start row by the given delta", ->
map.mapBufferRowRange(4, 8, 1)
map.applyBufferDelta(5, 4)
expect(map.regions[0]).toEqual(bufferRows: 4, screenRows: 4)
expect(map.regions[1]).toEqual(bufferRows: 8, screenRows: 1)
describe "when applying a negative delta", ->
it "shrinks regions starting at the start row until the entire delta is consumed", ->
map.mapBufferRowRange(4, 8, 1)
map.mapBufferRowRange(10, 14, 1)
map.applyBufferDelta(3, -6)
expect(map.regions[0]).toEqual(bufferRows: 3, screenRows: 4)
expect(map.regions[1]).toEqual(bufferRows: 0, screenRows: 1)
expect(map.regions[2]).toEqual(bufferRows: 1, screenRows: 2)
expect(map.regions[3]).toEqual(bufferRows: 4, screenRows: 1)
describe ".applyScreenDelta(startScreenRow, delta)", ->
describe "when applying a positive delta", ->
it "can enlarge the screen side of existing regions", ->
map.mapBufferRowRange(5, 6, 3) # wrapped line
map.applyScreenDelta(5, 2) # wrap it twice more
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 10]
describe "when applying a negative delta", ->
it "can collapse the screen side of multiple regions to 0 until the entire delta has been applied", ->
map.mapBufferRowRange(5, 10, 1) # inner fold 1
map.mapBufferRowRange(11, 13, 1) # inner fold 2
map.mapBufferRowRange(15, 20, 1) # inner fold 3
map.mapBufferRowRange(22, 27, 1) # following fold
map.applyScreenDelta(6, -5)
expect(map.screenRowRangeForBufferRow(5)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(9)).toEqual [5, 6]
expect(map.screenRowRangeForBufferRow(10)).toEqual [6, 6]
expect(map.screenRowRangeForBufferRow(19)).toEqual [6, 6]
expect(map.screenRowRangeForBufferRow(22)).toEqual [8, 9]
expect(map.screenRowRangeForBufferRow(26)).toEqual [8, 9]
it "starts collapsing the first region at the start row, not before", ->
map.mapBufferRowRange(5, 6, 4)
map.mapBufferRowRange(11, 13, 1)
map.applyScreenDelta(7, -5)
expect(map.regions[0]).toEqual(bufferRows: 5, screenRows: 5)
expect(map.regions[1]).toEqual(bufferRows: 1, screenRows: 2)
expect(map.regions[2]).toEqual(bufferRows: 5, screenRows: 2)
it "does not throw an exception when applying a delta beyond the last region", ->
map.mapBufferRowRange(5, 10, 1) # inner fold 1
map.applyScreenDelta(15, 10)