From c6ff7e893428b603c27181efcc855fdee1aadfbe Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 8 May 2013 11:05:19 -0600 Subject: [PATCH] Inserting a mapping within an existing mapping preserves its shape Inserting a mapping should never change the position of any existing mappings on screen or in the buffer. It's simply a statement about a range of rows in the buffer mapping to a range of existing rows on screen, but shouldn't add or remove any rows. Adding and removing rows on screen or in the buffer is the job of the applyBufferDelta and applyScreenDelta methods. --- spec/app/row-map-spec.coffee | 39 ++++++++++++++------------------ src/app/row-map.coffee | 44 ++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/spec/app/row-map-spec.coffee b/spec/app/row-map-spec.coffee index 51d2392dd..d1b99fdc8 100644 --- a/spec/app/row-map-spec.coffee +++ b/spec/app/row-map-spec.coffee @@ -15,9 +15,8 @@ describe "RowMap", -> describe "when mapping to a single screen row (like a visible fold)", -> beforeEach -> map.mapBufferRowRange(5, 10, 1) - map.mapBufferRowRange(35, 40, 1) - map.mapBufferRowRange(25, 30, 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] @@ -37,11 +36,6 @@ describe "RowMap", -> expect(map.screenRowRangeForBufferRow(29)).toEqual [17, 18] expect(map.screenRowRangeForBufferRow(30)).toEqual [18, 19] - expect(map.screenRowRangeForBufferRow(34)).toEqual [22, 23] - expect(map.screenRowRangeForBufferRow(35)).toEqual [23, 24] - expect(map.screenRowRangeForBufferRow(39)).toEqual [23, 24] - expect(map.screenRowRangeForBufferRow(40)).toEqual [24, 25] - it "accounts for the mapping when translating screen rows to buffer row ranges", -> expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1] @@ -57,16 +51,11 @@ describe "RowMap", -> expect(map.bufferRowRangeForScreenRow(17)).toEqual [25, 30] expect(map.bufferRowRangeForScreenRow(18)).toEqual [30, 31] - expect(map.bufferRowRangeForScreenRow(22)).toEqual [34, 35] - expect(map.bufferRowRangeForScreenRow(23)).toEqual [35, 40] - expect(map.bufferRowRangeForScreenRow(24)).toEqual [40, 41] - describe "when mapping to zero screen rows (like an invisible fold)", -> beforeEach -> map.mapBufferRowRange(5, 10, 0) - map.mapBufferRowRange(35, 40, 0) - map.mapBufferRowRange(25, 30, 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] @@ -86,11 +75,6 @@ describe "RowMap", -> expect(map.screenRowRangeForBufferRow(29)).toEqual [15, 15] expect(map.screenRowRangeForBufferRow(30)).toEqual [15, 16] - expect(map.screenRowRangeForBufferRow(34)).toEqual [19, 20] - expect(map.screenRowRangeForBufferRow(35)).toEqual [20, 20] - expect(map.screenRowRangeForBufferRow(39)).toEqual [20, 20] - expect(map.screenRowRangeForBufferRow(40)).toEqual [20, 21] - it "accounts for the mapping when translating screen rows to buffer row ranges", -> expect(map.bufferRowRangeForScreenRow(0)).toEqual [0, 1] @@ -103,14 +87,11 @@ describe "RowMap", -> expect(map.bufferRowRangeForScreenRow(14)).toEqual [24, 25] expect(map.bufferRowRangeForScreenRow(15)).toEqual [30, 31] - expect(map.bufferRowRangeForScreenRow(19)).toEqual [34, 35] - expect(map.bufferRowRangeForScreenRow(20)).toEqual [40, 41] - describe "when mapping a single buffer row to multiple screen rows (like a wrapped line)", -> beforeEach -> map.mapBufferRowRange(5, 6, 3) - map.mapBufferRowRange(20, 21, 5) 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] @@ -161,3 +142,17 @@ describe "RowMap", -> expect(map.screenRowRangeForBufferRow(19)).toEqual [24, 25] expect(map.screenRowRangeForBufferRow(20)).toEqual [25, 30] expect(map.screenRowRangeForBufferRow(21)).toEqual [30, 31] + + describe "when mapping into an existing 1:1 region", -> + it "preserves the starting screen row of subsequent 1:N mappings", -> + 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] diff --git a/src/app/row-map.coffee b/src/app/row-map.coffee index caa0bfda5..4e8fb8267 100644 --- a/src/app/row-map.coffee +++ b/src/app/row-map.coffee @@ -11,6 +11,13 @@ class RowMap screenRow += targetBufferRow - bufferRow [screenRow, screenRow + 1] + bufferRowRangeForBufferRow: (targetBufferRow) -> + { mapping, screenRow, bufferRow } = @traverseToBufferRow(targetBufferRow) + if mapping and mapping.bufferRows != mapping.screenRows # 1:n mapping + [bufferRow, bufferRow + mapping.bufferRows] + else # 1:1 mapping + [targetBufferRow, targetBufferRow + 1] + bufferRowRangeForScreenRow: (targetScreenRow) -> { mapping, screenRow, bufferRow } = @traverseToScreenRow(targetScreenRow) if mapping and mapping.bufferRows != mapping.screenRows # 1:n mapping @@ -20,24 +27,33 @@ class RowMap [bufferRow, bufferRow + 1] mapBufferRowRange: (startBufferRow, endBufferRow, screenRows) -> - { mapping, index, bufferRow } = @traverseToBufferRow(startBufferRow) - - if mapping and mapping.bufferRows != mapping.screenRows - if bufferRow == startBufferRow and bufferRow + mapping.bufferRows == endBufferRow - mapping.screenRows = screenRows - return - else - throw new Error("Invalid mapping insertion") - - padBefore = startBufferRow - bufferRow - padAfter = (bufferRow + mapping?.bufferRows) - endBufferRow + { mapping, index, bufferRow, screenRow } = @traverseToBufferRow(startBufferRow) newMappings = [] - newMappings.push(bufferRows: padBefore, screenRows: padBefore) if padBefore > 0 - newMappings.push(bufferRows: endBufferRow - startBufferRow, screenRows: screenRows) - newMappings.push(bufferRows: padAfter, screenRows: padAfter) if padAfter > 0 + + preRows = startBufferRow - bufferRow + if preRows > 0 + newMappings.push(bufferRows: preRows, screenRows: preRows) + + bufferRows = endBufferRow - startBufferRow + newMappings.push({bufferRows, screenRows}) + + if mapping + postBufferRows = mapping.bufferRows - preRows - bufferRows + postScreenRows = mapping.screenRows - preRows - screenRows + if postBufferRows > 0 or postScreenRows > 0 + newMappings.push(bufferRows: postBufferRows, screenRows: postScreenRows) + @mappings[index..index] = newMappings + applyBufferDelta: (startBufferRow, delta) -> + { mapping } = @traverseToBufferRow(startBufferRow) + mapping?.bufferRows += delta + + applyScreenDelta: (startBufferRow, delta) -> + { mapping } = @traverseToScreenRow(startBufferRow) + mapping?.screenRows += delta + traverseToBufferRow: (targetBufferRow) -> bufferRow = 0 screenRow = 0