From 09ab3e4a751840099bab6f3a1f1f9aa28a84a029 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Wed, 5 Aug 2015 15:35:43 +0200 Subject: [PATCH 01/48] moveLineUp now loops though all selectedBufferRanges in sorted order so as to return immediately if the first selection is at the start row --- src/text-editor.coffee | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 07fe26588..a10d1c97b 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -818,10 +818,14 @@ class TextEditor extends Model @transact groupingInterval, => fn(selection, index) for selection, index in @getSelectionsOrderedByBufferPosition() - # Move lines intersection the most recent selection up by one row in screen + # Move lines intersection the most recent selection or multiple selections up by one row in screen # coordinates. moveLineUp: -> - selection = @getSelectedBufferRange() + newSelectionBufferRanges = [] + selections = @getSelectedBufferRanges() + selections.sort (a, b) -> + return a.compare(b) + for selection in selections return if selection.start.row is 0 lastRow = @buffer.getLastRow() return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' @@ -869,9 +873,9 @@ class TextEditor extends Model for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() @foldBufferRow(foldedRow) - @setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true, autoscroll: true) + newSelectionBufferRanges.push(selection.translate([-insertDelta])) - # Move lines intersecting the most recent selection down by one row in screen + @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) # coordinates. moveLineDown: -> selection = @getSelectedBufferRange() From 99a2907dea94f8a0ea5aa2c88255fc4c68ff4960 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Wed, 5 Aug 2015 15:36:31 +0200 Subject: [PATCH 02/48] moveLineDown now loops through all selectedBufferRanges in reverse sorted order so none of the lines move if the bottom-most selection is at the last line --- src/text-editor.coffee | 170 +++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 81 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a10d1c97b..02d6fae35 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -826,110 +826,118 @@ class TextEditor extends Model selections.sort (a, b) -> return a.compare(b) for selection in selections - return if selection.start.row is 0 - lastRow = @buffer.getLastRow() - return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' + return if selection.start.row is 0 + lastRow = @buffer.getLastRow() + return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' - @transact => - foldedRows = [] - rows = [selection.start.row..selection.end.row] - if selection.start.row isnt selection.end.row and selection.end.column is 0 - rows.pop() unless @isFoldedAtBufferRow(selection.end.row) + @transact => + foldedRows = [] + rows = [selection.start.row..selection.end.row] + if selection.start.row isnt selection.end.row and selection.end.column is 0 + rows.pop() unless @isFoldedAtBufferRow(selection.end.row) - # Move line around the fold that is directly above the selection - precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1]) - precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row - if fold = @largestFoldContainingBufferRow(precedingBufferRow) - insertDelta = fold.getBufferRange().getRowCount() - else - insertDelta = 1 - - for row in rows - if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) - bufferRange = fold.getBufferRange() - startRow = bufferRange.start.row - endRow = bufferRange.end.row - foldedRows.push(startRow - insertDelta) + # Move line around the fold that is directly above the selection + precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1]) + precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row + if fold = @largestFoldContainingBufferRow(precedingBufferRow) + insertDelta = fold.getBufferRange().getRowCount() else - startRow = row - endRow = row + insertDelta = 1 - insertPosition = Point.fromObject([startRow - insertDelta]) - endPosition = Point.min([endRow + 1], @buffer.getEndPosition()) - lines = @buffer.getTextInRange([[startRow], endPosition]) - if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) - lines = "#{lines}\n" + for row in rows + if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) + bufferRange = fold.getBufferRange() + startRow = bufferRange.start.row + endRow = bufferRange.end.row + foldedRows.push(startRow - insertDelta) + else + startRow = row + endRow = row - @buffer.deleteRows(startRow, endRow) + insertPosition = Point.fromObject([startRow - insertDelta]) + endPosition = Point.min([endRow + 1], @buffer.getEndPosition()) + lines = @buffer.getTextInRange([[startRow], endPosition]) + if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) + lines = "#{lines}\n" - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) - @unfoldBufferRow(insertPosition.row) - foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount()) + @buffer.deleteRows(startRow, endRow) - @buffer.insert(insertPosition, lines) + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) + @unfoldBufferRow(insertPosition.row) + foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount()) - # Restore folds that existed before the lines were moved - for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() - @foldBufferRow(foldedRow) + @buffer.insert(insertPosition, lines) + + # Restore folds that existed before the lines were moved + for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() + @foldBufferRow(foldedRow) newSelectionBufferRanges.push(selection.translate([-insertDelta])) @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) + + # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. moveLineDown: -> - selection = @getSelectedBufferRange() - lastRow = @buffer.getLastRow() - return if selection.end.row is lastRow - return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is '' + newSelectionBufferRanges = [] + selections = @getSelectedBufferRanges() + selections.sort (a, b) -> + return a.compare(b) + for selection in selections.reverse() + lastRow = @buffer.getLastRow() + return if selection.end.row is lastRow + return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is '' - @transact => - foldedRows = [] - rows = [selection.end.row..selection.start.row] - if selection.start.row isnt selection.end.row and selection.end.column is 0 - rows.shift() unless @isFoldedAtBufferRow(selection.end.row) + @transact => + foldedRows = [] + rows = [selection.end.row..selection.start.row] + if selection.start.row isnt selection.end.row and selection.end.column is 0 + rows.shift() unless @isFoldedAtBufferRow(selection.end.row) - # Move line around the fold that is directly below the selection - followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1]) - followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row - if fold = @largestFoldContainingBufferRow(followingBufferRow) - insertDelta = fold.getBufferRange().getRowCount() - else - insertDelta = 1 - - for row in rows - if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) - bufferRange = fold.getBufferRange() - startRow = bufferRange.start.row - endRow = bufferRange.end.row - foldedRows.push(endRow + insertDelta) + # Move line around the fold that is directly below the selection + followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1]) + followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row + if fold = @largestFoldContainingBufferRow(followingBufferRow) + insertDelta = fold.getBufferRange().getRowCount() else - startRow = row - endRow = row + insertDelta = 1 - if endRow + 1 is lastRow - endPosition = [endRow, @buffer.lineLengthForRow(endRow)] - else - endPosition = [endRow + 1] - lines = @buffer.getTextInRange([[startRow], endPosition]) - @buffer.deleteRows(startRow, endRow) + for row in rows + if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) + bufferRange = fold.getBufferRange() + startRow = bufferRange.start.row + endRow = bufferRange.end.row + foldedRows.push(endRow + insertDelta) + else + startRow = row + endRow = row - insertPosition = Point.min([startRow + insertDelta], @buffer.getEndPosition()) - if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0 - lines = "\n#{lines}" + if endRow + 1 is lastRow + endPosition = [endRow, @buffer.lineLengthForRow(endRow)] + else + endPosition = [endRow + 1] + lines = @buffer.getTextInRange([[startRow], endPosition]) + @buffer.deleteRows(startRow, endRow) - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) - @unfoldBufferRow(insertPosition.row) - foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount()) + insertPosition = Point.min([startRow + insertDelta], @buffer.getEndPosition()) + if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0 + lines = "\n#{lines}" - @buffer.insert(insertPosition, lines) + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) + @unfoldBufferRow(insertPosition.row) + foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount()) - # Restore folds that existed before the lines were moved - for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() - @foldBufferRow(foldedRow) + @buffer.insert(insertPosition, lines) - @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true, autoscroll: true) + # Restore folds that existed before the lines were moved + for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() + @foldBufferRow(foldedRow) + + newSelectionBufferRanges.push(selection.translate([insertDelta])) + + @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) # Duplicate the most recent cursor's current line. duplicateLines: -> From 5dd061c0143c30f098e1960aa41f5b6b28e868cf Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Wed, 5 Aug 2015 15:43:26 +0200 Subject: [PATCH 03/48] :white_check_mark: add specs for moveLineUp and moveLine Down --- spec/text-editor-spec.coffee | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 5175f91c4..0ff4368dd 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1912,6 +1912,82 @@ describe "TextEditor", -> expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges() describe "buffer manipulation", -> + describe ".moveLineUp", -> + describe "when there is only one line selected", -> + it "moves the line up by one row", -> + editor.setSelectedBufferRange([[3, 2], [3, 9]]) + expect(editor.getSelectedBufferRange()).toEqual {start: {row: 3, column: 2}, end: {row: 3, column: 9}} + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual {start: {row: 2, column: 2}, end: {row: 2, column: 9}} + + describe "when there is multiple selections", -> + + it "moves the selected lines up by one row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [ + {start: {row: 0, column: 2}, end: {row: 0, column: 9}}, + {start: {row: 2, column: 2}, end: {row: 2, column: 9}}, + {start: {row: 4, column: 2}, end: {row: 4, column: 9}} ] + + describe "when there is multiple lines selected and moved upward until the top-most line is at row 0", -> + it "moves all the lines upward until the top-most is at row 0, then no more lines are moved upward", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] + + editor.moveLineUp() + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [ + {start: {row: 0, column: 2}, end: {row: 0, column: 9}}, + {start: {row: 2, column: 2}, end: {row: 2, column: 9}}, + {start: {row: 4, column: 2}, end: {row: 4, column: 9}} ] + + describe ".moveLineDown", -> + describe "when there is only one line selected", -> + it "moves the line down by one row", -> + editor.setSelectedBufferRange([[3, 2], [3, 9]]) + expect(editor.getSelectedBufferRange()).toEqual {start: {row: 3, column: 2}, end: {row: 3, column: 9}} + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual {start: {row: 4, column: 2}, end: {row: 4, column: 9}} + + describe "when there is multiple selections", -> + it "moves the selected lines down by one row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + + expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] + + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [ + {start: {row: 6, column: 2}, end: {row: 6, column: 9}}, + {start: {row: 4, column: 2}, end: {row: 4, column: 9}}, + {start: {row: 2, column: 2}, end: {row: 2, column: 9}}] + + describe "when there is multiple lines selected and moved downward until the bottom-most line is at the last row", -> + it "moves all the lines downward until the bottom-most is at bottom row, then no lines are moved downward", -> + editor.setSelectedBufferRanges([[[7, 2], [7, 5]], [[8, 2], [8, 9]], [[11, 2], [11, 5]]]) + + expect(editor.getSelectedBufferRanges()).toEqual [ + {start: {row: 7, column: 2}, end: {row: 7, column: 5}}, + {start: {row: 8, column: 2}, end: {row: 8, column: 9}}, + {start: {row: 11, column: 2}, end: {row: 11, column: 5}}] + + editor.moveLineDown() + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [ + {start: {row: 12, column: 2}, end: {row: 12, column: 5}}, + {start: {row: 9, column: 2}, end: {row: 9, column: 9}}, + {start: {row: 8, column: 2}, end: {row: 8, column: 5}}] + describe ".insertText(text)", -> describe "when there is a single selection", -> beforeEach -> From 412ea3242a6a3239b0da3f1096cc862671131b3c Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Thu, 6 Aug 2015 16:18:08 +0200 Subject: [PATCH 04/48] CR: remove return statement --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 02d6fae35..3ca335188 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -824,7 +824,7 @@ class TextEditor extends Model newSelectionBufferRanges = [] selections = @getSelectedBufferRanges() selections.sort (a, b) -> - return a.compare(b) + a.compare(b) for selection in selections return if selection.start.row is 0 lastRow = @buffer.getLastRow() From e889e85e2a9729e2cdf2989f062f80b757cfdedf Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Thu, 6 Aug 2015 16:18:33 +0200 Subject: [PATCH 05/48] CR: use array style syntax for bufferRanges --- spec/text-editor-spec.coffee | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 0ff4368dd..3e72f4e0a 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1916,24 +1916,20 @@ describe "TextEditor", -> describe "when there is only one line selected", -> it "moves the line up by one row", -> editor.setSelectedBufferRange([[3, 2], [3, 9]]) - expect(editor.getSelectedBufferRange()).toEqual {start: {row: 3, column: 2}, end: {row: 3, column: 9}} + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] editor.moveLineUp() - expect(editor.getSelectedBufferRange()).toEqual {start: {row: 2, column: 2}, end: {row: 2, column: 9}} + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 9]] describe "when there is multiple selections", -> - it "moves the selected lines up by one row", -> editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual [ - {start: {row: 0, column: 2}, end: {row: 0, column: 9}}, - {start: {row: 2, column: 2}, end: {row: 2, column: 9}}, - {start: {row: 4, column: 2}, end: {row: 4, column: 9}} ] + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] describe "when there is multiple lines selected and moved upward until the top-most line is at row 0", -> it "moves all the lines upward until the top-most is at row 0, then no more lines are moved upward", -> @@ -1943,20 +1939,17 @@ describe "TextEditor", -> editor.moveLineUp() editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual [ - {start: {row: 0, column: 2}, end: {row: 0, column: 9}}, - {start: {row: 2, column: 2}, end: {row: 2, column: 9}}, - {start: {row: 4, column: 2}, end: {row: 4, column: 9}} ] + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] describe ".moveLineDown", -> describe "when there is only one line selected", -> it "moves the line down by one row", -> editor.setSelectedBufferRange([[3, 2], [3, 9]]) - expect(editor.getSelectedBufferRange()).toEqual {start: {row: 3, column: 2}, end: {row: 3, column: 9}} + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] editor.moveLineDown() - expect(editor.getSelectedBufferRange()).toEqual {start: {row: 4, column: 2}, end: {row: 4, column: 9}} + expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [4, 9]] describe "when there is multiple selections", -> it "moves the selected lines down by one row", -> @@ -1966,27 +1959,18 @@ describe "TextEditor", -> editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [ - {start: {row: 6, column: 2}, end: {row: 6, column: 9}}, - {start: {row: 4, column: 2}, end: {row: 4, column: 9}}, - {start: {row: 2, column: 2}, end: {row: 2, column: 9}}] + expect(editor.getSelectedBufferRanges()).toEqual [[[6, 2], [6, 9]], [[4, 2], [4, 9]], [[2, 2], [2, 9]]] describe "when there is multiple lines selected and moved downward until the bottom-most line is at the last row", -> it "moves all the lines downward until the bottom-most is at bottom row, then no lines are moved downward", -> editor.setSelectedBufferRanges([[[7, 2], [7, 5]], [[8, 2], [8, 9]], [[11, 2], [11, 5]]]) - expect(editor.getSelectedBufferRanges()).toEqual [ - {start: {row: 7, column: 2}, end: {row: 7, column: 5}}, - {start: {row: 8, column: 2}, end: {row: 8, column: 9}}, - {start: {row: 11, column: 2}, end: {row: 11, column: 5}}] + expect(editor.getSelectedBufferRanges()).toEqual [[[7, 2], [7, 5]], [[8, 2], [8, 9]], [[11, 2], [11, 5]]] editor.moveLineDown() editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [ - {start: {row: 12, column: 2}, end: {row: 12, column: 5}}, - {start: {row: 9, column: 2}, end: {row: 9, column: 9}}, - {start: {row: 8, column: 2}, end: {row: 8, column: 5}}] + expect(editor.getSelectedBufferRanges()).toEqual [[[12, 2], [12, 5]], [[9, 2], [9, 9]], [[8, 2], [8, 5]]] describe ".insertText(text)", -> describe "when there is a single selection", -> From 6d2c4b61b4b42afcf8d501c38087f0781a22918b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:11:18 -0600 Subject: [PATCH 06/48] Wrap all line movements in transact --- src/text-editor.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 3ca335188..2fe1aa7f9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -823,14 +823,14 @@ class TextEditor extends Model moveLineUp: -> newSelectionBufferRanges = [] selections = @getSelectedBufferRanges() - selections.sort (a, b) -> - a.compare(b) - for selection in selections - return if selection.start.row is 0 - lastRow = @buffer.getLastRow() - return if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' + selections.sort (a, b) -> a.compare(b) + + @transact => + for selection in selections + continue if selection.start.row is 0 + lastRow = @buffer.getLastRow() + continue if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' - @transact => foldedRows = [] rows = [selection.start.row..selection.end.row] if selection.start.row isnt selection.end.row and selection.end.column is 0 @@ -875,7 +875,7 @@ class TextEditor extends Model newSelectionBufferRanges.push(selection.translate([-insertDelta])) - @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) + @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. From b8d02dedde2aa48cad33fae9a31a7c86bb1ba605 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:15:48 -0600 Subject: [PATCH 07/48] Use row-oriented methods --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 2fe1aa7f9..10cb3a385 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -837,8 +837,8 @@ class TextEditor extends Model rows.pop() unless @isFoldedAtBufferRow(selection.end.row) # Move line around the fold that is directly above the selection - precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1]) - precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row + precedingScreenRow = @screenRowForBufferRow(selection.start.row) - 1 + precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) if fold = @largestFoldContainingBufferRow(precedingBufferRow) insertDelta = fold.getBufferRange().getRowCount() else From de500ce41a1344a9c8d5f2274f834c5b5bbe5ad3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:20:22 -0600 Subject: [PATCH 08/48] Compute insertDelta more simply --- src/text-editor.coffee | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 10cb3a385..080f85df7 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -839,10 +839,7 @@ class TextEditor extends Model # Move line around the fold that is directly above the selection precedingScreenRow = @screenRowForBufferRow(selection.start.row) - 1 precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) - if fold = @largestFoldContainingBufferRow(precedingBufferRow) - insertDelta = fold.getBufferRange().getRowCount() - else - insertDelta = 1 + insertDelta = selection.start.row - precedingBufferRow for row in rows if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) From b14f3d8d9211403db06c2088463f64080e9dfe6d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:31:00 -0600 Subject: [PATCH 09/48] =?UTF-8?q?Don=E2=80=99t=20move=20any=20lines=20if?= =?UTF-8?q?=20some=20lines=20can=E2=80=99t=20move?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/text-editor.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 080f85df7..0c1cc282d 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -825,12 +825,14 @@ class TextEditor extends Model selections = @getSelectedBufferRanges() selections.sort (a, b) -> a.compare(b) + if selections[0].start.row is 0 + return + + if selections[selections.length - 1].start.row is @getLastBufferRow() and @buffer.getLastLine() is '' + return + @transact => for selection in selections - continue if selection.start.row is 0 - lastRow = @buffer.getLastRow() - continue if selection.isEmpty() and selection.start.row is lastRow and @buffer.getLastLine() is '' - foldedRows = [] rows = [selection.start.row..selection.end.row] if selection.start.row isnt selection.end.row and selection.end.column is 0 From 2fc2c074bef7ed014320eadb034d3abae553975e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:31:13 -0600 Subject: [PATCH 10/48] =?UTF-8?q?Append=20line=20ending=20if=20it=20isn?= =?UTF-8?q?=E2=80=99t=20present?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/text-editor.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 0c1cc282d..745f5c23e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -856,8 +856,7 @@ class TextEditor extends Model insertPosition = Point.fromObject([startRow - insertDelta]) endPosition = Point.min([endRow + 1], @buffer.getEndPosition()) lines = @buffer.getTextInRange([[startRow], endPosition]) - if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) - lines = "#{lines}\n" + lines += @buffer.lineEndingForRow(endPosition.row - 1) unless lines[lines.length - 1] is '\n' @buffer.deleteRows(startRow, endRow) From 7d98fc141f064e8ff9adade0f0daee3f9e090c60 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:33:25 -0600 Subject: [PATCH 11/48] Operate on selection objects, not just ranges --- src/text-editor.coffee | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 745f5c23e..2355587e2 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -822,26 +822,27 @@ class TextEditor extends Model # coordinates. moveLineUp: -> newSelectionBufferRanges = [] - selections = @getSelectedBufferRanges() - selections.sort (a, b) -> a.compare(b) + selections = @getSelectionsOrderedByBufferPosition() - if selections[0].start.row is 0 + if selections[0].getBufferRange().start.row is 0 return - if selections[selections.length - 1].start.row is @getLastBufferRow() and @buffer.getLastLine() is '' + if selections[selections.length - 1].getBufferRange().start.row is @getLastBufferRow() and @buffer.getLastLine() is '' return @transact => for selection in selections - foldedRows = [] - rows = [selection.start.row..selection.end.row] - if selection.start.row isnt selection.end.row and selection.end.column is 0 - rows.pop() unless @isFoldedAtBufferRow(selection.end.row) + selectionRange = selection.getBufferRange() - # Move line around the fold that is directly above the selection - precedingScreenRow = @screenRowForBufferRow(selection.start.row) - 1 + foldedRows = [] + rows = [selectionRange.start.row..selectionRange.end.row] + if selectionRange.start.row isnt selectionRange.end.row and selectionRange.end.column is 0 + rows.pop() unless @isFoldedAtBufferRow(selectionRange.end.row) + + # Move line around the fold that is directly above the selectionRange + precedingScreenRow = @screenRowForBufferRow(selectionRange.start.row) - 1 precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) - insertDelta = selection.start.row - precedingBufferRow + insertDelta = selectionRange.start.row - precedingBufferRow for row in rows if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) @@ -871,7 +872,7 @@ class TextEditor extends Model for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() @foldBufferRow(foldedRow) - newSelectionBufferRanges.push(selection.translate([-insertDelta])) + newSelectionBufferRanges.push(selectionRange.translate([-insertDelta])) @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) From 5d1e7469a1335321056cd7e8c2f5f352fd047543 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:34:40 -0600 Subject: [PATCH 12/48] Reassign selection range immediately when moving lines up --- src/text-editor.coffee | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 2355587e2..0400a1b56 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -821,7 +821,6 @@ class TextEditor extends Model # Move lines intersection the most recent selection or multiple selections up by one row in screen # coordinates. moveLineUp: -> - newSelectionBufferRanges = [] selections = @getSelectionsOrderedByBufferPosition() if selections[0].getBufferRange().start.row is 0 @@ -872,9 +871,7 @@ class TextEditor extends Model for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() @foldBufferRow(foldedRow) - newSelectionBufferRanges.push(selectionRange.translate([-insertDelta])) - - @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) + selection.setBufferRange(selectionRange.translate([-insertDelta, 0])) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. From e27ff93f9775d7f27c9a23da4e8bb11ad15efc73 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 15:49:17 -0600 Subject: [PATCH 13/48] Move all lines spanned by selection in a single operation --- src/text-editor.coffee | 50 +++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 0400a1b56..19d501203 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -833,43 +833,37 @@ class TextEditor extends Model for selection in selections selectionRange = selection.getBufferRange() - foldedRows = [] - rows = [selectionRange.start.row..selectionRange.end.row] - if selectionRange.start.row isnt selectionRange.end.row and selectionRange.end.column is 0 - rows.pop() unless @isFoldedAtBufferRow(selectionRange.end.row) - - # Move line around the fold that is directly above the selectionRange + # If selected line range is preceded by a fold, one line above on screen + # could be multiple lines in the buffer. precedingScreenRow = @screenRowForBufferRow(selectionRange.start.row) - 1 precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) insertDelta = selectionRange.start.row - precedingBufferRow - for row in rows - if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) - bufferRange = fold.getBufferRange() - startRow = bufferRange.start.row - endRow = bufferRange.end.row - foldedRows.push(startRow - insertDelta) - else - startRow = row - endRow = row + # Any folds in the text that is moved will need to be re-created. + rangesToRefold = + @outermostFoldsInBufferRowRange(selectionRange.start.row, selectionRange.end.row).map (fold) -> + fold.getBufferRange().translate([-insertDelta, 0]) - insertPosition = Point.fromObject([startRow - insertDelta]) - endPosition = Point.min([endRow + 1], @buffer.getEndPosition()) - lines = @buffer.getTextInRange([[startRow], endPosition]) - lines += @buffer.lineEndingForRow(endPosition.row - 1) unless lines[lines.length - 1] is '\n' + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) + rangesToRefold.push(fold.getBufferRange().translate([selectionRange.getRowCount(), 0])) + fold.destroy() - @buffer.deleteRows(startRow, endRow) + # Don't move the last line of a multi-line selection if the selection ends at column 0 + if selectionRange.end.row > selectionRange.start.row and selectionRange.end.column is 0 + linesRange = [[selectionRange.start.row, 0], selectionRange.end] + else + linesRange = [[selectionRange.start.row, 0], [selectionRange.end.row + 1, 0]] - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) - @unfoldBufferRow(insertPosition.row) - foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount()) - - @buffer.insert(insertPosition, lines) + # Delete lines spanned by selection and insert them on the preceding buffer row + lines = @buffer.getTextInRange(linesRange) + lines += @buffer.lineEndingForRow(selectionRange.end.row - 1) unless lines[lines.length - 1] is '\n' + @buffer.delete(linesRange) + @buffer.insert([precedingBufferRow, 0], lines) # Restore folds that existed before the lines were moved - for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() - @foldBufferRow(foldedRow) + for rangeToRefold in rangesToRefold + @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) selection.setBufferRange(selectionRange.translate([-insertDelta, 0])) From 76174771fe7baefe0a4644dfbff9afa1b1e6b7ba Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 16:54:08 -0600 Subject: [PATCH 14/48] :tulip: spec gardening --- spec/text-editor-spec.coffee | 89 +++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 3e72f4e0a..17b8dfcdf 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1913,33 +1913,78 @@ describe "TextEditor", -> describe "buffer manipulation", -> describe ".moveLineUp", -> - describe "when there is only one line selected", -> - it "moves the line up by one row", -> - editor.setSelectedBufferRange([[3, 2], [3, 9]]) - expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] - - editor.moveLineUp() - - expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 9]] - - describe "when there is multiple selections", -> - it "moves the selected lines up by one row", -> - editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) - expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] - - editor.moveLineUp() - - expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] - - describe "when there is multiple lines selected and moved upward until the top-most line is at row 0", -> - it "moves all the lines upward until the top-most is at row 0, then no more lines are moved upward", -> - editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) - expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] + describe "when there is a single selection", -> + describe "when the selection spans a single line", -> + it "moves the line to the preceding row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + editor.setSelectedBufferRange([[3, 2], [3, 9]]) editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + + describe "when the selection spans multiple lines", -> + it "moves the lines spanned by the selection to the preceding row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[3, 2], [4, 9]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [3, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" + + describe "when the selection spans multiple lines, but ends at column 0", -> + it "does not move the last line of the selection", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[3, 2], [4, 0]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [3, 0]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + describe "when there are multiple selections", -> + describe "when all the selections span different lines", -> + it "moves all lines that are spanned by a selection to the preceding row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) editor.moveLineUp() expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] + expect(editor.lineTextForBufferRow(0)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(1)).toBe "var quicksort = function () {" + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift();" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + describe "when one of the selections spans line 0", -> + it "doesn't move any lines, since line 0 can't move", -> + editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]]) + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] + expect(buffer.isModified()).toBe false + + describe "when one of the selections spans the last line, and it is empty", -> + it "doesn't move any lines, since the last line can't move", -> + buffer.append('\n') + editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]]) + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] describe ".moveLineDown", -> describe "when there is only one line selected", -> From 75f341263a33681c44b0e009b3fdc66486647169 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 11 Aug 2015 18:06:24 -0600 Subject: [PATCH 15/48] Handle multiple selections on the same line --- spec/text-editor-spec.coffee | 8 +++++++ src/text-editor.coffee | 45 +++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 17b8dfcdf..b449e99cc 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1968,6 +1968,14 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift();" expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + describe "when some of the selections span the same lines", -> + it "moves lines that contain multiple selections correctly", -> + editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [2, 9]], [[2, 12], [2, 13]]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + describe "when one of the selections spans line 0", -> it "doesn't move any lines, since line 0 can't move", -> editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]]) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 19d501203..c5ce5b189 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -830,34 +830,48 @@ class TextEditor extends Model return @transact => - for selection in selections - selectionRange = selection.getBufferRange() + newSelectionRanges = [] + + while selections.length > 0 + # Find selections spanning a contiguous set of lines + selection = selections.shift() + lastSelectionRange = selection.getBufferRange() + selectionsToMove = [selection] + rangesOfSelectionsToMove = [lastSelectionRange] + + while lastSelectionRange.end.row is selections[0]?.getBufferRange().start.row + selection = selections.shift() + lastSelectionRange = selection.getBufferRange() + selectionsToMove.push(selection) + rangesOfSelectionsToMove.push(lastSelectionRange) + + # Compute the range spanned by all these selections... + linesRangeStart = [selectionsToMove[0].getBufferRange().start.row, 0] + if lastSelectionRange.end.row > lastSelectionRange.start.row and lastSelectionRange.end.column is 0 + # Don't move the last line of a multi-line selection if the selection ends at column 0 + linesRange = new Range(linesRangeStart, lastSelectionRange.end) + else + linesRange = new Range(linesRangeStart, [lastSelectionRange.end.row + 1, 0]) # If selected line range is preceded by a fold, one line above on screen # could be multiple lines in the buffer. - precedingScreenRow = @screenRowForBufferRow(selectionRange.start.row) - 1 + precedingScreenRow = @screenRowForBufferRow(linesRange.start.row) - 1 precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow) - insertDelta = selectionRange.start.row - precedingBufferRow + insertDelta = linesRange.start.row - precedingBufferRow # Any folds in the text that is moved will need to be re-created. rangesToRefold = - @outermostFoldsInBufferRowRange(selectionRange.start.row, selectionRange.end.row).map (fold) -> + @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> fold.getBufferRange().translate([-insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) - rangesToRefold.push(fold.getBufferRange().translate([selectionRange.getRowCount(), 0])) + rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount(), 0])) fold.destroy() - # Don't move the last line of a multi-line selection if the selection ends at column 0 - if selectionRange.end.row > selectionRange.start.row and selectionRange.end.column is 0 - linesRange = [[selectionRange.start.row, 0], selectionRange.end] - else - linesRange = [[selectionRange.start.row, 0], [selectionRange.end.row + 1, 0]] - # Delete lines spanned by selection and insert them on the preceding buffer row lines = @buffer.getTextInRange(linesRange) - lines += @buffer.lineEndingForRow(selectionRange.end.row - 1) unless lines[lines.length - 1] is '\n' + lines += @buffer.lineEndingForRow(linesRange.end.row - 1) unless lines[lines.length - 1] is '\n' @buffer.delete(linesRange) @buffer.insert([precedingBufferRow, 0], lines) @@ -865,7 +879,10 @@ class TextEditor extends Model for rangeToRefold in rangesToRefold @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) - selection.setBufferRange(selectionRange.translate([-insertDelta, 0])) + for selection, i in selectionsToMove + newSelectionRanges.push(rangesOfSelectionsToMove[i].translate([-insertDelta, 0])) + + @setSelectedBufferRanges(newSelectionRanges) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. From 5f249b8af90549f9c60792908762bed71d4745c6 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Thu, 13 Aug 2015 16:43:14 +0200 Subject: [PATCH 16/48] revert to using selection ranges instead of selection objects --- src/text-editor.coffee | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c5ce5b189..c1cd48b89 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -821,12 +821,13 @@ class TextEditor extends Model # Move lines intersection the most recent selection or multiple selections up by one row in screen # coordinates. moveLineUp: -> - selections = @getSelectionsOrderedByBufferPosition() + selections = @getSelectedBufferRanges() + selections.sort (a, b) -> a.compare(b) - if selections[0].getBufferRange().start.row is 0 + if selections[0].start.row is 0 return - if selections[selections.length - 1].getBufferRange().start.row is @getLastBufferRow() and @buffer.getLastLine() is '' + if selections[selections.length - 1].start.row is @getLastBufferRow() and @buffer.getLastLine() is '' return @transact => @@ -835,23 +836,19 @@ class TextEditor extends Model while selections.length > 0 # Find selections spanning a contiguous set of lines selection = selections.shift() - lastSelectionRange = selection.getBufferRange() selectionsToMove = [selection] - rangesOfSelectionsToMove = [lastSelectionRange] - while lastSelectionRange.end.row is selections[0]?.getBufferRange().start.row + while selection.end.row is selections[0]?.start.row selection = selections.shift() - lastSelectionRange = selection.getBufferRange() selectionsToMove.push(selection) - rangesOfSelectionsToMove.push(lastSelectionRange) # Compute the range spanned by all these selections... - linesRangeStart = [selectionsToMove[0].getBufferRange().start.row, 0] - if lastSelectionRange.end.row > lastSelectionRange.start.row and lastSelectionRange.end.column is 0 + linesRangeStart = [selection.start.row, 0] + if selection.end.row > selection.start.row and selection.end.column is 0 # Don't move the last line of a multi-line selection if the selection ends at column 0 - linesRange = new Range(linesRangeStart, lastSelectionRange.end) + linesRange = new Range(linesRangeStart, selection.end) else - linesRange = new Range(linesRangeStart, [lastSelectionRange.end.row + 1, 0]) + linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) # If selected line range is preceded by a fold, one line above on screen # could be multiple lines in the buffer. @@ -866,7 +863,7 @@ class TextEditor extends Model # Make sure the inserted text doesn't go into an existing fold if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) - rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount(), 0])) + rangesToRefold.push(fold.getBufferRange().translate([linesRange.getRowCount() - 1, 0])) fold.destroy() # Delete lines spanned by selection and insert them on the preceding buffer row @@ -879,8 +876,8 @@ class TextEditor extends Model for rangeToRefold in rangesToRefold @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) - for selection, i in selectionsToMove - newSelectionRanges.push(rangesOfSelectionsToMove[i].translate([-insertDelta, 0])) + for selection in selectionsToMove + newSelectionRanges.push(selection.translate([-insertDelta, 0])) @setSelectedBufferRanges(newSelectionRanges) From f3354f9a335962f0bf790bc173cb20f2cbae0cd3 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Thu, 13 Aug 2015 18:00:51 +0200 Subject: [PATCH 17/48] :white_check_mark: moveLineUp dealing with folds --- spec/text-editor-spec.coffee | 113 +++++++++++++++++++++++++++++------ 1 file changed, 94 insertions(+), 19 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index b449e99cc..ff647d165 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1915,16 +1915,42 @@ describe "TextEditor", -> describe ".moveLineUp", -> describe "when there is a single selection", -> describe "when the selection spans a single line", -> - it "moves the line to the preceding row", -> - expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" - expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + describe "when there is no fold in the preceeding row", -> + it "moves the line to the preceding row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" - editor.setSelectedBufferRange([[3, 2], [3, 9]]) - editor.moveLineUp() + editor.setSelectedBufferRange([[3, 2], [3, 9]]) + editor.moveLineUp() - expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 9]] - expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" - expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + + describe "when the preceding row consists of folded code", -> + it "moves the line above the folded row and preseveres the correct folds", -> + expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(9)).toBe " };" + + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[8, 0], [8,4]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[4,0], [4, 4]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() describe "when the selection spans multiple lines", -> it "moves the lines spanned by the selection to the preceding row", -> @@ -1954,19 +1980,68 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - describe "when there are multiple selections", -> - describe "when all the selections span different lines", -> - it "moves all lines that are spanned by a selection to the preceding row", -> - editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + describe "when there are multiple selections and the preceeding row is a folded row", -> + it "moves the lines spanned by the selection to the preceeding row, but preserves the folded code", -> + expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(9)).toBe " };" + + editor.createFold(4, 7) + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[8, 0], [9,2]]) editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] - expect(editor.lineTextForBufferRow(0)).toBe " var sort = function(items) {" - expect(editor.lineTextForBufferRow(1)).toBe "var quicksort = function () {" - expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" - expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" - expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift();" - expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.getSelectedBufferRange()).toEqual [[4,0], [5, 2]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " };" + expect(editor.lineTextForBufferRow(6)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(5)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(10)).toBeFalsy() + + describe "when there are multiple selections", -> + describe "when all the selections span different lines", -> + describe "when there is no folds", -> + it "moves all lines that are spanned by a selection to the preceding row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]] + expect(editor.lineTextForBufferRow(0)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(1)).toBe "var quicksort = function () {" + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift();" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + describe "when there is a fold", -> + it "moves all lines that spanned by a selection to preceding row, preserving all folds", -> + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRanges([[[8, 0], [8, 3]], [[11, 0], [11, 5]]]) + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual [[[4, 0], [4, 3]], [[10, 0], [10, 5]]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(10)).toBe " return sort(Array.apply(this, arguments));" + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() describe "when some of the selections span the same lines", -> it "moves lines that contain multiple selections correctly", -> From 0bb89e9191fccab95804d4d55f1e2d0bd5dd0383 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sat, 15 Aug 2015 11:18:25 +0200 Subject: [PATCH 18/48] change moveLineDown to handle multiple selections, mimic moveLineUp --- src/text-editor.coffee | 93 +++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c1cd48b89..bdf824993 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -884,64 +884,65 @@ class TextEditor extends Model # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. moveLineDown: -> - newSelectionBufferRanges = [] selections = @getSelectedBufferRanges() - selections.sort (a, b) -> - return a.compare(b) - for selection in selections.reverse() - lastRow = @buffer.getLastRow() - return if selection.end.row is lastRow - return if selection.end.row is lastRow - 1 and @buffer.getLastLine() is '' + selections.sort (a, b) -> a.compare(b) + if selections[selections.length - 1].start.row is @buffer.getLastRow() + return - @transact => - foldedRows = [] - rows = [selection.end.row..selection.start.row] - if selection.start.row isnt selection.end.row and selection.end.column is 0 - rows.shift() unless @isFoldedAtBufferRow(selection.end.row) + if selections[selections.length - 1].start.row is @getLastBufferRow() and @buffer.getLastLine() is '' + return - # Move line around the fold that is directly below the selection - followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1]) - followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row - if fold = @largestFoldContainingBufferRow(followingBufferRow) - insertDelta = fold.getBufferRange().getRowCount() + @transact => + newSelectionRanges = [] + + while selections.length > 0 + # Find selections spanning a contiguous set of lines + selection = selections.shift() + selectionsToMove = [selection] + + while selection.end.row is selections[0]?.start.row + selection = selections.shift() + selectionsToMove.push(selection) + + # Compute the range spanned by all these selections... + linesRangeStart = [selection.start.row, 0] + if selection.end.row > selection.start.row and selection.end.column is 0 + # Don't move the last line of a multi-line selection if the selection ends at column 0 + linesRange = new Range(linesRangeStart, selection.end) else - insertDelta = 1 + linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) - for row in rows - if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) - bufferRange = fold.getBufferRange() - startRow = bufferRange.start.row - endRow = bufferRange.end.row - foldedRows.push(endRow + insertDelta) - else - startRow = row - endRow = row + # If selected line range is preceded by a fold, one line above on screen + # could be multiple lines in the buffer. + followingScreenRow = @screenRowForBufferRow(linesRange.end.row) + 1 + followingBufferRow = @bufferRowForScreenRow(followingScreenRow) + insertDelta = followingBufferRow - linesRange.end.row - if endRow + 1 is lastRow - endPosition = [endRow, @buffer.lineLengthForRow(endRow)] - else - endPosition = [endRow + 1] - lines = @buffer.getTextInRange([[startRow], endPosition]) - @buffer.deleteRows(startRow, endRow) + # Any folds in the text that is moved will need to be re-created. + rangesToRefold = + @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> + fold.getBufferRange().translate([+insertDelta, 0]) - insertPosition = Point.min([startRow + insertDelta], @buffer.getEndPosition()) - if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0 - lines = "\n#{lines}" + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow - insertDelta) + rangesToRefold.push(fold.getBufferRange().translate([-(linesRange.getRowCount() - 1), 0])) + fold.destroy() - # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) - @unfoldBufferRow(insertPosition.row) - foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount()) - - @buffer.insert(insertPosition, lines) + # Delete lines spanned by selection and insert them on the following correct buffer row + insertPosition = [selection.translate([insertDelta, 0]).start.row, 0] + lines = @buffer.getTextInRange(linesRange) + lines += @buffer.lineEndingForRow(linesRange.end.row - 1) unless lines[lines.length - 1] is '\n' + @buffer.delete(linesRange) + @buffer.insert(insertPosition, lines) # Restore folds that existed before the lines were moved - for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() - @foldBufferRow(foldedRow) + for rangeToRefold in rangesToRefold + @displayBuffer.createFold(rangeToRefold.start.row, rangeToRefold.end.row) - newSelectionBufferRanges.push(selection.translate([insertDelta])) + for selection in selectionsToMove + newSelectionRanges.push(selection.translate([insertDelta, 0])) - @setSelectedBufferRanges(newSelectionBufferRanges, preserveFolds: true, autoscroll: true) + @setSelectedBufferRanges(newSelectionRanges) # Duplicate the most recent cursor's current line. duplicateLines: -> From b60e0bec2ee1c6267598bcd21a7ac0ce0749f7f6 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sat, 15 Aug 2015 11:19:26 +0200 Subject: [PATCH 19/48] :sunflower: spec for moveLineDown --- spec/text-editor-spec.coffee | 208 ++++++++++++++++++++++++++++------- 1 file changed, 168 insertions(+), 40 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index ff647d165..5d67081b2 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1929,28 +1929,28 @@ describe "TextEditor", -> describe "when the preceding row consists of folded code", -> it "moves the line above the folded row and preseveres the correct folds", -> - expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" - expect(editor.lineTextForBufferRow(9)).toBe " };" + expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(9)).toBe " };" - editor.createFold(4, 7) + editor.createFold(4, 7) - expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - editor.setSelectedBufferRange([[8, 0], [8,4]]) - editor.moveLineUp() + editor.setSelectedBufferRange([[8, 0], [8, 4]]) + editor.moveLineUp() - expect(editor.getSelectedBufferRange()).toEqual [[4,0], [4, 4]] - expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" - expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" - expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [4, 4]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() describe "when the selection spans multiple lines", -> it "moves the lines spanned by the selection to the preceding row", -> @@ -1983,7 +1983,7 @@ describe "TextEditor", -> describe "when there are multiple selections and the preceeding row is a folded row", -> it "moves the lines spanned by the selection to the preceeding row, but preserves the folded code", -> expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" - expect(editor.lineTextForBufferRow(9)).toBe " };" + expect(editor.lineTextForBufferRow(9)).toBe " };" editor.createFold(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -1992,12 +1992,12 @@ describe "TextEditor", -> expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - editor.setSelectedBufferRange([[8, 0], [9,2]]) + editor.setSelectedBufferRange([[8, 0], [9, 2]]) editor.moveLineUp() - expect(editor.getSelectedBufferRange()).toEqual [[4,0], [5, 2]] + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [5, 2]] expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" - expect(editor.lineTextForBufferRow(5)).toBe " };" + expect(editor.lineTextForBufferRow(5)).toBe " };" expect(editor.lineTextForBufferRow(6)).toBe " while(items.length > 0) {" expect(editor.isFoldedAtBufferRow(5)).toBeFalsy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() @@ -2070,35 +2070,163 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] describe ".moveLineDown", -> - describe "when there is only one line selected", -> - it "moves the line down by one row", -> - editor.setSelectedBufferRange([[3, 2], [3, 9]]) - expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] + describe "when there is a single selection", -> + describe "when the selection spans a single line", -> + describe "when there is no fold in the following row", -> + it "moves the line to the following row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" - editor.moveLineDown() + editor.setSelectedBufferRange([[2, 2], [2, 9]]) + editor.moveLineDown() - expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [4, 9]] + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" - describe "when there is multiple selections", -> - it "moves the selected lines down by one row", -> - editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + describe "when the following row is a folded row", -> + it "moves the line below the folded row and preserves the fold", -> + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]] + editor.createFold(4, 7) - editor.moveLineDown() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - expect(editor.getSelectedBufferRanges()).toEqual [[[6, 2], [6, 9]], [[4, 2], [4, 9]], [[2, 2], [2, 9]]] + editor.setSelectedBufferRange([[3, 0], [3, 4]]) + editor.moveLineDown() - describe "when there is multiple lines selected and moved downward until the bottom-most line is at the last row", -> - it "moves all the lines downward until the bottom-most is at bottom row, then no lines are moved downward", -> - editor.setSelectedBufferRanges([[[7, 2], [7, 5]], [[8, 2], [8, 9]], [[11, 2], [11, 5]]]) + expect(editor.getSelectedBufferRange()).toEqual [[7, 0], [7, 4]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() - expect(editor.getSelectedBufferRanges()).toEqual [[[7, 2], [7, 5]], [[8, 2], [8, 9]], [[11, 2], [11, 5]]] - editor.moveLineDown() + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + describe "when the slecetion spans multiple lines", -> + it "moves the lines spanned by the selection to the following row", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[2, 2], [3, 9]]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [[[12, 2], [12, 5]], [[9, 2], [9, 9]], [[8, 2], [8, 5]]] + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [4, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + describe "when the selection spans multiple lines, but ends at column 0", -> + it "does not move the last line of the selection", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.setSelectedBufferRange([[2, 2], [3, 0]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [4, 0]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + describe "when there are multiple selections and the following row is a folded row", -> + it "moves the lines spanned by the selection to the following row, but preserves the folded code", -> + expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + editor.createFold(4, 7) + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRange([[2, 0], [3, 2]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[6, 0], [7, 2]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(1)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" + + describe "when there are multiple selections", -> + describe "when all the selections span different lines", -> + describe "when there is no folds", -> + it "moves all lines that are spanned by a selection to the following row", -> + editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [2, 9]], [[4, 2], [4, 9]], [[6, 2], [6, 9]]] + expect(editor.lineTextForBufferRow(1)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(5)).toBe " current < pivot ? left.push(current) : right.push(current);" + expect(editor.lineTextForBufferRow(6)).toBe " current = items.shift();" + + describe "when there is a fold", -> + it "moves all lines that spanned by a selection to preceding row, preserving all folds", -> + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRanges([[[1, 2], [1, 6]], [[3, 0], [3, 4]], [[8, 0], [8, 3]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [2, 6]], [[7, 0], [7, 4]], [[9, 0], [9, 3]]] + expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(9)).toBe " return sort(left).concat(pivot).concat(sort(right));" + + describe "when some of the selections span the same lines", -> + it "moves lines that contain multiple selections correctly", -> + editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[4, 2], [4, 9]], [[4, 12], [4, 13]]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + + describe "when one of the selections spans the last buffer row ", -> + it "doesn't move any lines", -> + editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[12, 2], [12, 2]]]) + + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[12, 2], [12, 2]]] + expect(buffer.isModified()).toBe false + + describe "when one of the selections spans the last line, and it is empty", -> + it "doesn't move any lines, since the last line can't move", -> + buffer.append('\n') + editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]]) + + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] describe ".insertText(text)", -> describe "when there is a single selection", -> From 0de26c306a7fea08b4dc971915bfc0ef0e2943bb Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Thu, 20 Aug 2015 11:09:03 +0200 Subject: [PATCH 20/48] fix folds accidentally becoming unfolded --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index bdf824993..bed3c6719 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -924,8 +924,8 @@ class TextEditor extends Model fold.getBufferRange().translate([+insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold - if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow - insertDelta) - rangesToRefold.push(fold.getBufferRange().translate([-(linesRange.getRowCount() - 1), 0])) + if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) + rangesToRefold.push(fold.getBufferRange().translate([insertDelta - 1, 0])) fold.destroy() # Delete lines spanned by selection and insert them on the following correct buffer row From 5b698cfa2b8533b768e9172c49be59d23d879e7b Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sun, 23 Aug 2015 17:50:23 +0200 Subject: [PATCH 21/48] update typo in comment --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index bed3c6719..218992fc7 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -912,7 +912,7 @@ class TextEditor extends Model else linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) - # If selected line range is preceded by a fold, one line above on screen + # If selected line range is followed by a fold, one line below on screen # could be multiple lines in the buffer. followingScreenRow = @screenRowForBufferRow(linesRange.end.row) + 1 followingBufferRow = @bufferRowForScreenRow(followingScreenRow) From e74b8b6ae23db12abc90d51143faf0f1fe888963 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sun, 23 Aug 2015 17:53:21 +0200 Subject: [PATCH 22/48] use Point as insertion and no need to post-fix newline --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 218992fc7..6a6c6d366 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -929,9 +929,9 @@ class TextEditor extends Model fold.destroy() # Delete lines spanned by selection and insert them on the following correct buffer row - insertPosition = [selection.translate([insertDelta, 0]).start.row, 0] + insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) lines = @buffer.getTextInRange(linesRange) - lines += @buffer.lineEndingForRow(linesRange.end.row - 1) unless lines[lines.length - 1] is '\n' + @buffer.delete(linesRange) @buffer.insert(insertPosition, lines) From b29f2ae5a819e0ca1d1c90f42c8349dae60d65d6 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sun, 23 Aug 2015 17:53:35 +0200 Subject: [PATCH 23/48] removed unnecessary + operator --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6a6c6d366..d737a850e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -921,7 +921,7 @@ class TextEditor extends Model # Any folds in the text that is moved will need to be re-created. rangesToRefold = @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> - fold.getBufferRange().translate([+insertDelta, 0]) + fold.getBufferRange().translate([insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) From b394b2c903a341a91fbfa1f556102c3e1a6aa9e7 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sun, 23 Aug 2015 18:35:26 +0200 Subject: [PATCH 24/48] preserve folds when making new selections --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d737a850e..dd423f370 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -942,7 +942,7 @@ class TextEditor extends Model for selection in selectionsToMove newSelectionRanges.push(selection.translate([insertDelta, 0])) - @setSelectedBufferRanges(newSelectionRanges) + @setSelectedBufferRanges(newSelectionRanges, {preserveFolds: true}) # Duplicate the most recent cursor's current line. duplicateLines: -> From 7bb85869052623d42399a5486ae80a953670b4fd Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sun, 23 Aug 2015 19:24:39 +0200 Subject: [PATCH 25/48] now possible to always move lines down at the end of the file --- src/text-editor.coffee | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index dd423f370..20725c1b4 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -886,11 +886,6 @@ class TextEditor extends Model moveLineDown: -> selections = @getSelectedBufferRanges() selections.sort (a, b) -> a.compare(b) - if selections[selections.length - 1].start.row is @buffer.getLastRow() - return - - if selections[selections.length - 1].start.row is @getLastBufferRow() and @buffer.getLastLine() is '' - return @transact => newSelectionRanges = [] @@ -931,6 +926,8 @@ class TextEditor extends Model # Delete lines spanned by selection and insert them on the following correct buffer row insertPosition = new Point(selection.translate([insertDelta, 0]).start.row, 0) lines = @buffer.getTextInRange(linesRange) + if linesRange.end.row is @buffer.getLastRow() + lines = "\n#{lines}" @buffer.delete(linesRange) @buffer.insert(insertPosition, lines) From e838866655ff334cc9d1c00b106416a82afec30a Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Mon, 24 Aug 2015 08:13:39 +0200 Subject: [PATCH 26/48] remove spec pertaining to moving down on last line :sunflower: --- spec/text-editor-spec.coffee | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 5d67081b2..5823afbb6 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2210,24 +2210,6 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[4, 2], [4, 9]], [[4, 12], [4, 13]]] expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" - describe "when one of the selections spans the last buffer row ", -> - it "doesn't move any lines", -> - editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[12, 2], [12, 2]]]) - - editor.moveLineDown() - - expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[12, 2], [12, 2]]] - expect(buffer.isModified()).toBe false - - describe "when one of the selections spans the last line, and it is empty", -> - it "doesn't move any lines, since the last line can't move", -> - buffer.append('\n') - editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]]) - - editor.moveLineDown() - - expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] - describe ".insertText(text)", -> describe "when there is a single selection", -> beforeEach -> From 3a5abef3aff92c71b33b084f0607341da14fca40 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Fri, 28 Aug 2015 10:13:31 +0200 Subject: [PATCH 27/48] bug fix: go through the selections in reverse order --- src/text-editor.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 20725c1b4..5215466fe 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -886,6 +886,7 @@ class TextEditor extends Model moveLineDown: -> selections = @getSelectedBufferRanges() selections.sort (a, b) -> a.compare(b) + selections = selections.reverse() @transact => newSelectionRanges = [] From 2b7d220dbc947d60d5bd84bfc34ae4ea77eca0f9 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Fri, 28 Aug 2015 11:24:57 +0200 Subject: [PATCH 28/48] scroll to top-most/bottom-most buffer position --- src/text-editor.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5215466fe..17101ead7 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -880,6 +880,7 @@ class TextEditor extends Model newSelectionRanges.push(selection.translate([-insertDelta, 0])) @setSelectedBufferRanges(newSelectionRanges) + @scrollToBufferPosition([newSelectionRanges[0].start.row + 2, 0]) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. @@ -941,6 +942,7 @@ class TextEditor extends Model newSelectionRanges.push(selection.translate([insertDelta, 0])) @setSelectedBufferRanges(newSelectionRanges, {preserveFolds: true}) + @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) # Duplicate the most recent cursor's current line. duplicateLines: -> From 5ed6657d47223aac501260af5602de31522435ca Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Fri, 28 Aug 2015 12:06:25 +0200 Subject: [PATCH 29/48] spec garden for reversing the buffer selections :tulip: --- spec/text-editor-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 5823afbb6..7d1330fce 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2171,7 +2171,7 @@ describe "TextEditor", -> editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [2, 9]], [[4, 2], [4, 9]], [[6, 2], [6, 9]]] + expect(editor.getSelectedBufferRanges()).toEqual [[[6, 2], [6, 9]], [[4, 2], [4, 9]], [[2, 2], [2, 9]]] expect(editor.lineTextForBufferRow(1)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" @@ -2192,7 +2192,7 @@ describe "TextEditor", -> editor.setSelectedBufferRanges([[[1, 2], [1, 6]], [[3, 0], [3, 4]], [[8, 0], [8, 3]]]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [2, 6]], [[7, 0], [7, 4]], [[9, 0], [9, 3]]] + expect(editor.getSelectedBufferRanges()).toEqual [[[9, 0], [9, 3]], [[7, 0], [7, 4]], [[2, 2], [2, 6]]] expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2207,7 +2207,7 @@ describe "TextEditor", -> editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [[[4, 2], [4, 9]], [[4, 12], [4, 13]]] + expect(editor.getSelectedBufferRanges()).toEqual [[[4, 12], [4, 13]], [[4, 2], [4, 9]]] expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" describe ".insertText(text)", -> From 57c17f8e562dce85963bcc29c582e2927890e7ce Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Sun, 30 Aug 2015 11:10:24 +0200 Subject: [PATCH 30/48] set autoscroll to false when setting new buffer ranges --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 17101ead7..2dd17185c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -879,7 +879,7 @@ class TextEditor extends Model for selection in selectionsToMove newSelectionRanges.push(selection.translate([-insertDelta, 0])) - @setSelectedBufferRanges(newSelectionRanges) + @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) @scrollToBufferPosition([newSelectionRanges[0].start.row + 2, 0]) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen @@ -941,7 +941,7 @@ class TextEditor extends Model for selection in selectionsToMove newSelectionRanges.push(selection.translate([insertDelta, 0])) - @setSelectedBufferRanges(newSelectionRanges, {preserveFolds: true}) + @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) # Duplicate the most recent cursor's current line. From 9cd2c111c4e52cc036f38829d4d815e87f8e5233 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Tue, 8 Sep 2015 14:27:36 +0200 Subject: [PATCH 31/48] bug that causes moveLineUp to move the wrong selections --- src/text-editor.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5b1735a7f..8d014a320 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -831,10 +831,6 @@ class TextEditor extends Model selection = selections.shift() selectionsToMove = [selection] - while selection.end.row is selections[0]?.start.row - selection = selections.shift() - selectionsToMove.push(selection) - # Compute the range spanned by all these selections... linesRangeStart = [selection.start.row, 0] if selection.end.row > selection.start.row and selection.end.column is 0 From e14b59ab435b61396c5304af034f352c4f9d71f9 Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Tue, 8 Sep 2015 14:59:44 +0200 Subject: [PATCH 32/48] fix for "bug that causes moveLineUp to move the wrong selections" --- src/text-editor.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 8d014a320..280dfdb59 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -831,6 +831,11 @@ class TextEditor extends Model selection = selections.shift() selectionsToMove = [selection] + while selection.end.row is selections[0]?.start.row + selectionsToMove.push(selections[0]) + selection.end.row = selections[0].end.row + selections.shift() + # Compute the range spanned by all these selections... linesRangeStart = [selection.start.row, 0] if selection.end.row > selection.start.row and selection.end.column is 0 From 7fa73d73969da2222d17f27f66d31be04fc8d15b Mon Sep 17 00:00:00 2001 From: Luke Pommersheim Date: Tue, 8 Sep 2015 15:00:05 +0200 Subject: [PATCH 33/48] fix for "make one selection to move down if the start row matches the next's end row" --- src/text-editor.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 280dfdb59..af5e32bbf 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -891,9 +891,11 @@ class TextEditor extends Model selection = selections.shift() selectionsToMove = [selection] - while selection.end.row is selections[0]?.start.row - selection = selections.shift() - selectionsToMove.push(selection) + # if the current selection start row matches the next selections' end row - make them one selection + while selection.start.row is selections[0]?.end.row + selectionsToMove.push(selections[0]) + selection.start.row = selections[0].start.row + selections.shift() # Compute the range spanned by all these selections... linesRangeStart = [selection.start.row, 0] From 09ffa8bec9cb2f547a18596d44f37e52e6578c73 Mon Sep 17 00:00:00 2001 From: abe33 Date: Sun, 11 Oct 2015 18:15:06 +0200 Subject: [PATCH 34/48] :bug: Fix moving multiple selections down locked at wrapped line --- spec/text-editor-spec.coffee | 21 +++++++++++++++++++++ src/text-editor.coffee | 13 ++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 71283d324..2b6514e12 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2075,6 +2075,27 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[4, 12], [4, 13]], [[4, 2], [4, 9]]] expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + describe "when the selections are above a wrapped line", -> + beforeEach -> + editor.setSoftWrapped(true) + editor.setEditorWidthInChars(80) + editor.setText(""" + 1 + 2 + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + 3 + 4 + """) + + it 'moves the lines past the soft wrapped line', -> + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[1, 0], [1, 0]]]) + + editor.moveLineDown() + + expect(editor.lineTextForBufferRow(0)).not.toBe "2" + expect(editor.lineTextForBufferRow(1)).toBe "1" + expect(editor.lineTextForBufferRow(2)).toBe "2" + describe ".insertText(text)", -> describe "when there is a single selection", -> beforeEach -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 952b677f6..83f9dc240 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -885,9 +885,16 @@ class TextEditor extends Model linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) # If selected line range is followed by a fold, one line below on screen - # could be multiple lines in the buffer. - followingScreenRow = @screenRowForBufferRow(linesRange.end.row) + 1 - followingBufferRow = @bufferRowForScreenRow(followingScreenRow) + # could be multiple lines in the buffer. But at the same time, if the + # next buffer row is wrapped, one line in the buffer can represent many + # screen rows. + [nextBufferRowScreenStart, nextBufferRowScreenEnd] = @displayBuffer.rowMap.screenRowRangeForBufferRow(linesRange.end.row) + if nextBufferRowScreenEnd - nextBufferRowScreenStart > 1 + followingScreenRow = nextBufferRowScreenEnd + followingBufferRow = @bufferRowForScreenRow(followingScreenRow) + else + followingScreenRow = @screenRowForBufferRow(linesRange.end.row) + 1 + followingBufferRow = @bufferRowForScreenRow(followingScreenRow) insertDelta = followingBufferRow - linesRange.end.row # Any folds in the text that is moved will need to be re-created. From 9ee89a1f31a524166c3d7e2bc2f3e2e46d5be8cb Mon Sep 17 00:00:00 2001 From: abe33 Date: Sun, 11 Oct 2015 18:15:22 +0200 Subject: [PATCH 35/48] :art: Fix bad indentation --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 83f9dc240..e246f76a9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -923,8 +923,8 @@ class TextEditor extends Model for selection in selectionsToMove newSelectionRanges.push(selection.translate([insertDelta, 0])) - @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) - @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) + @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) + @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) # Duplicate the most recent cursor's current line. duplicateLines: -> From 4d44016eb1b9511b7f6524e9d3c3900a9c700ea5 Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 12 Oct 2015 19:40:23 +0200 Subject: [PATCH 36/48] :art: Remove unnecessary branching --- src/text-editor.coffee | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e246f76a9..9988bc203 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -888,13 +888,8 @@ class TextEditor extends Model # could be multiple lines in the buffer. But at the same time, if the # next buffer row is wrapped, one line in the buffer can represent many # screen rows. - [nextBufferRowScreenStart, nextBufferRowScreenEnd] = @displayBuffer.rowMap.screenRowRangeForBufferRow(linesRange.end.row) - if nextBufferRowScreenEnd - nextBufferRowScreenStart > 1 - followingScreenRow = nextBufferRowScreenEnd - followingBufferRow = @bufferRowForScreenRow(followingScreenRow) - else - followingScreenRow = @screenRowForBufferRow(linesRange.end.row) + 1 - followingBufferRow = @bufferRowForScreenRow(followingScreenRow) + followingScreenRow = @displayBuffer.lastScreenRowForBufferRow(linesRange.end.row) + 1 + followingBufferRow = @bufferRowForScreenRow(followingScreenRow) insertDelta = followingBufferRow - linesRange.end.row # Any folds in the text that is moved will need to be re-created. From d68a0db6d2e9d6847d983e728bfd9a874c8e5ec8 Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 12 Oct 2015 21:16:07 +0200 Subject: [PATCH 37/48] :art: Fix description test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Actually it doesn’t test multiple selections moves. --- spec/text-editor-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 2b6514e12..307d72656 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2004,7 +2004,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - describe "when there are multiple selections and the following row is a folded row", -> + describe "when the following row is a folded row", -> it "moves the lines spanned by the selection to the following row, but preserves the folded code", -> expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" From 45a3dbca4edccf0019bd28a358470e28e716bdf6 Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 12 Oct 2015 22:05:25 +0200 Subject: [PATCH 38/48] :bug: Fix folds not preserved when moving multiple selections down When two or more selections span the rows immediately before a fold, the `did-change` event dispatched on the deletion will trigger a merge of the selections, which in turn trigger an unfold at the buffer position of the new selection, which is now the position of the fold. Consolidating the selections at the begin of the transaction will prevent the merge and will keep the fold untouched. --- spec/text-editor-spec.coffee | 23 +++++++++++++++++++++++ src/text-editor.coffee | 1 + 2 files changed, 24 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 307d72656..105463993 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2067,6 +2067,29 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" expect(editor.lineTextForBufferRow(9)).toBe " return sort(left).concat(pivot).concat(sort(right));" + describe "and the multiple selections spans the two line before it", -> + it "moves all the lines, preserving the fold", -> + editor.createFold(4, 7) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.setSelectedBufferRanges([[[2, 2], [2, 6]], [[3, 0], [3, 4]]]) + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual [[[7, 0], [7, 4]], [[6, 2], [6, 6]]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + describe "when some of the selections span the same lines", -> it "moves lines that contain multiple selections correctly", -> editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9988bc203..a22b79205 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -863,6 +863,7 @@ class TextEditor extends Model selections = selections.reverse() @transact => + @consolidateSelections() newSelectionRanges = [] while selections.length > 0 From d8aba0d295b796004a45d341d515e003747a02c0 Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 12 Oct 2015 22:10:09 +0200 Subject: [PATCH 39/48] :art: Improve test descriptions and fix a typo --- spec/text-editor-spec.coffee | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 105463993..65f6f1273 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1976,7 +1976,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" - describe "when the slecetion spans multiple lines", -> + describe "when the selection spans multiple lines", -> it "moves the lines spanned by the selection to the following row", -> expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" @@ -2044,8 +2044,8 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(5)).toBe " current < pivot ? left.push(current) : right.push(current);" expect(editor.lineTextForBufferRow(6)).toBe " current = items.shift();" - describe "when there is a fold", -> - it "moves all lines that spanned by a selection to preceding row, preserving all folds", -> + describe "when there is a fold below one of the selected row", -> + it "moves all lines spanned by a selection to the following row, preserving the fold", -> editor.createFold(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2067,28 +2067,28 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" expect(editor.lineTextForBufferRow(9)).toBe " return sort(left).concat(pivot).concat(sort(right));" - describe "and the multiple selections spans the two line before it", -> - it "moves all the lines, preserving the fold", -> - editor.createFold(4, 7) + describe "when there is a fold below a group of multiple selections without any lines with no selection in-between", -> + it "moves all the lines below the fold, preserving the fold", -> + editor.createFold(4, 7) - expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - editor.setSelectedBufferRanges([[[2, 2], [2, 6]], [[3, 0], [3, 4]]]) - editor.moveLineDown() + editor.setSelectedBufferRanges([[[2, 2], [2, 6]], [[3, 0], [3, 4]]]) + editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual [[[7, 0], [7, 4]], [[6, 2], [6, 6]]] - expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" - expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() - expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() - expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" - expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.getSelectedBufferRanges()).toEqual [[[7, 0], [7, 4]], [[6, 2], [6, 6]]] + expect(editor.lineTextForBufferRow(2)).toBe " while(items.length > 0) {" + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" describe "when some of the selections span the same lines", -> it "moves lines that contain multiple selections correctly", -> From 9d21c773c184de2e282372cb11615490be9591e0 Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 2 Nov 2015 21:46:02 +0100 Subject: [PATCH 40/48] :bug: Fix moving single selection intersecting a fold --- spec/text-editor-spec.coffee | 166 +++++++++++++++++++++++++++++++++++ src/text-editor.coffee | 18 ++++ 2 files changed, 184 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index cd3f06c96..7ee8d2ee8 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1837,6 +1837,33 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + describe "when the cursor is at the beginning of a fold", -> + it "moves the line to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true) + expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [4, 9]] + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [3, 9]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + + describe "when the preceding row consists of folded code", -> it "moves the line above the folded row and preseveres the correct folds", -> expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" @@ -1876,6 +1903,63 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" + describe "when the selection's end intersects a fold", -> + it "moves the lines to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [3, 9]] + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " if (items.length <= 1) return items;" + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + + describe "when the selection's start intersects a fold", -> + it "moves the lines to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRange()).toEqual [[3, 2], [7, 9]] + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(8)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + describe "when the selection spans multiple lines, but ends at column 0", -> it "does not move the last line of the selection", -> expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" @@ -1994,6 +2078,31 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + describe "when the cursor is at the beginning of a fold", -> + it "moves the line to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[5, 2], [5, 9]] + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + describe "when the following row is a folded row", -> it "moves the line below the folded row and preserves the fold", -> expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];" @@ -2049,6 +2158,63 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + describe "when the selection's end intersects a fold", -> + it "moves the lines to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [5, 9]] + expect(editor.lineTextForBufferRow(3)).toBe " return sort(left).concat(pivot).concat(sort(right));" + expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + describe "when the selection's start intersects a fold", -> + it "moves the lines to the following row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRange()).toEqual [[5, 2], [9, 9]] + expect(editor.lineTextForBufferRow(4)).toBe " };" + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(9)).toBe " return sort(left).concat(pivot).concat(sort(right));" + + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(10)).toBeFalsy() + describe "when the following row is a folded row", -> it "moves the lines spanned by the selection to the following row, but preserves the folded code", -> expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;" diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e77ccb4a8..a1baa06f0 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -899,6 +899,15 @@ class TextEditor extends Model else linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) + # If there's a fold containing either the starting row or the end row + # of the selection then the whole fold needs to be moved. + if fold = @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + newEndRow = fold.getBufferRange().end.row + 1 + linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + else if fold = @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + newEndRow = fold.getBufferRange().end.row + 1 + linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + # If selected line range is preceded by a fold, one line above on screen # could be multiple lines in the buffer. precedingScreenRow = @screenRowForBufferRow(linesRange.start.row) - 1 @@ -961,6 +970,15 @@ class TextEditor extends Model else linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) + # If there's a fold containing either the starting row or the end row + # of the selection then the whole fold needs to be moved. + if fold = @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + newEndRow = fold.getBufferRange().end.row + 1 + linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + else if fold = @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + newEndRow = fold.getBufferRange().end.row + 1 + linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + # If selected line range is followed by a fold, one line below on screen # could be multiple lines in the buffer. But at the same time, if the # next buffer row is wrapped, one line in the buffer can represent many From 7e32dab68f89103a97717cf5d372d0c06bf07fe1 Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 2 Nov 2015 23:37:56 +0100 Subject: [PATCH 41/48] :art: Fix tests descriptions --- spec/text-editor-spec.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 7ee8d2ee8..f6f9cb2ec 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1838,7 +1838,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" describe "when the cursor is at the beginning of a fold", -> - it "moves the line to the following row without breaking the fold", -> + it "moves the line to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" editor.createFold(4, 7) @@ -1904,7 +1904,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(4)).toBe " if (items.length <= 1) return items;" describe "when the selection's end intersects a fold", -> - it "moves the lines to the following row without breaking the fold", -> + it "moves the lines to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" editor.createFold(4, 7) @@ -1932,7 +1932,7 @@ describe "TextEditor", -> expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() describe "when the selection's start intersects a fold", -> - it "moves the lines to the following row without breaking the fold", -> + it "moves the lines to the previous row without breaking the fold", -> expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" editor.createFold(4, 7) @@ -1974,7 +1974,7 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" - describe "when there are multiple selections and the preceeding row is a folded row", -> + describe "when the preceeding row is a folded row", -> it "moves the lines spanned by the selection to the preceeding row, but preserves the folded code", -> expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));" expect(editor.lineTextForBufferRow(9)).toBe " };" From 668a2dd6cf2c2be7e042b2cf59602650aa4b72dc Mon Sep 17 00:00:00 2001 From: abe33 Date: Mon, 2 Nov 2015 23:43:43 +0100 Subject: [PATCH 42/48] :bug: Fix moving a multiple selection with a fold creating new folds --- spec/text-editor-spec.coffee | 82 ++++++++++++++++++++++++++++++++++++ src/text-editor.coffee | 25 +++++++---- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index f6f9cb2ec..668842c67 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2015,6 +2015,46 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift();" expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + describe "when one selection intersects a fold", -> + it "moves the lines to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRanges([ + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineUp() + + expect(editor.getSelectedBufferRanges()).toEqual([ + [[1, 2], [1, 9]], + [[3, 2], [3, 9]] + ]) + + expect(editor.lineTextForBufferRow(1)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(2)).toBe " var sort = function(items) {" + expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + + expect(editor.isFoldedAtBufferRow(1)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + describe "when there is a fold", -> it "moves all lines that spanned by a selection to preceding row, preserving all folds", -> editor.createFold(4, 7) @@ -2301,6 +2341,48 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;" expect(editor.lineTextForBufferRow(7)).toBe " var pivot = items.shift(), current, left = [], right = [];" + describe "when one selection intersects a fold", -> + it "moves the lines to the previous row without breaking the fold", -> + expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {" + + editor.createFold(4, 7) + editor.setSelectedBufferRanges([ + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ], preserveFolds: true) + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + + editor.moveLineDown() + + expect(editor.getSelectedBufferRanges()).toEqual([ + [[5, 2], [5, 9]] + [[3, 2], [3, 9]], + ]) + + expect(editor.lineTextForBufferRow(2)).toBe " var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " if (items.length <= 1) return items;" + expect(editor.lineTextForBufferRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));" + + expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {" + expect(editor.lineTextForBufferRow(9)).toBe " };" + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + describe "when some of the selections span the same lines", -> it "moves lines that contain multiple selections correctly", -> editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a1baa06f0..151ca4528 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -971,13 +971,19 @@ class TextEditor extends Model linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) # If there's a fold containing either the starting row or the end row - # of the selection then the whole fold needs to be moved. - if fold = @displayBuffer.largestFoldContainingBufferRow(selection.start.row) - newEndRow = fold.getBufferRange().end.row + 1 - linesRange.end.row = newEndRow if newEndRow > linesRange.end.row - else if fold = @displayBuffer.largestFoldContainingBufferRow(selection.end.row) - newEndRow = fold.getBufferRange().end.row + 1 + # of the selection then the whole fold needs to be moved and restored. + # The initial fold range is stored and will be translated once the + # insert delta is know. + selectionFoldRanges = [] + foldAtSelectionStart = + @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + foldAtSelectionEnd = + @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + if fold = foldAtSelectionStart ? foldAtSelectionEnd + selectionFoldRanges.push range = fold.getBufferRange() + newEndRow = range.end.row + 1 linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + fold.destroy() # If selected line range is followed by a fold, one line below on screen # could be multiple lines in the buffer. But at the same time, if the @@ -988,9 +994,10 @@ class TextEditor extends Model insertDelta = followingBufferRow - linesRange.end.row # Any folds in the text that is moved will need to be re-created. - rangesToRefold = - @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> - fold.getBufferRange().translate([insertDelta, 0]) + # It includes the folds that were intersecting with the selection. + rangesToRefold = selectionFoldRanges.concat( + @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> fold.getBufferRange() + ).map (range) -> range.translate([insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow) From dacd08badfa45f40a5cd6b0f38466a57339d004d Mon Sep 17 00:00:00 2001 From: abe33 Date: Tue, 3 Nov 2015 00:48:22 +0100 Subject: [PATCH 43/48] :bug: Fix moving multiple selection with folds creates new folds --- spec/fixtures/sample-with-many-folds.js | 12 +++++ spec/text-editor-spec.coffee | 70 ++++++++++++++++++++++++- src/text-editor.coffee | 31 +++++++---- 3 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/sample-with-many-folds.js diff --git a/spec/fixtures/sample-with-many-folds.js b/spec/fixtures/sample-with-many-folds.js new file mode 100644 index 000000000..a3c5b7acc --- /dev/null +++ b/spec/fixtures/sample-with-many-folds.js @@ -0,0 +1,12 @@ +1; +2; +function f3() { + return 4; +}; +6; +7; +function f8() { + return 9; +}; +11; +12; diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 668842c67..bcf4fe7b8 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2077,6 +2077,39 @@ describe "TextEditor", -> expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + describe 'when there are many folds', -> + beforeEach -> + waitsForPromise -> + atom.workspace.open('sample-with-many-folds.js', autoIndent: false).then (o) -> editor = o + + describe 'and many selections intersects folded rows', -> + it 'moves and preserves all the folds', -> + editor.createFold(2, 4) + editor.createFold(7, 9) + + editor.setSelectedBufferRanges([ + [[1, 0], [5, 4]], + [[7, 0], [7, 4]] + ], preserveFolds: true) + + editor.moveLineUp() + + expect(editor.lineTextForBufferRow(1)).toEqual "function f3() {" + expect(editor.lineTextForBufferRow(4)).toEqual "6;" + expect(editor.lineTextForBufferRow(5)).toEqual "1;" + expect(editor.lineTextForBufferRow(6)).toEqual "function f8() {" + expect(editor.lineTextForBufferRow(9)).toEqual "7;" + + expect(editor.isFoldedAtBufferRow(1)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() + + expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() + describe "when some of the selections span the same lines", -> it "moves lines that contain multiple selections correctly", -> editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) @@ -2103,7 +2136,7 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] - describe ".moveLineDown", -> + fdescribe ".moveLineDown", -> describe "when there is a single selection", -> describe "when the selection spans a single line", -> describe "when there is no fold in the following row", -> @@ -2295,6 +2328,41 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(5)).toBe " current < pivot ? left.push(current) : right.push(current);" expect(editor.lineTextForBufferRow(6)).toBe " current = items.shift();" + describe 'when there are many folds', -> + beforeEach -> + waitsForPromise -> + atom.workspace.open('sample-with-many-folds.js', autoIndent: false).then (o) -> editor = o + + describe 'and many selections intersects folded rows', -> + it 'moves and preserves all the folds', -> + editor.createFold(2, 4) + editor.createFold(7, 9) + + editor.setSelectedBufferRanges([ + [[2, 0], [2, 4]], + [[6, 0], [10, 4]] + ], preserveFolds: true) + + editor.moveLineDown() + + expect(editor.lineTextForBufferRow(2)).toEqual "6;" + expect(editor.lineTextForBufferRow(3)).toEqual "function f3() {" + expect(editor.lineTextForBufferRow(6)).toEqual "12;" + expect(editor.lineTextForBufferRow(7)).toEqual "7;" + expect(editor.lineTextForBufferRow(8)).toEqual "function f8() {" + expect(editor.lineTextForBufferRow(11)).toEqual "11;" + + expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() + expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(9)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(10)).toBeTruthy() + expect(editor.isFoldedAtBufferRow(11)).toBeFalsy() + describe "when there is a fold below one of the selected row", -> it "moves all lines spanned by a selection to the following row, preserving the fold", -> editor.createFold(4, 7) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 151ca4528..d64bcb47c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -900,13 +900,19 @@ class TextEditor extends Model linesRange = new Range(linesRangeStart, [selection.end.row + 1, 0]) # If there's a fold containing either the starting row or the end row - # of the selection then the whole fold needs to be moved. - if fold = @displayBuffer.largestFoldContainingBufferRow(selection.start.row) - newEndRow = fold.getBufferRange().end.row + 1 - linesRange.end.row = newEndRow if newEndRow > linesRange.end.row - else if fold = @displayBuffer.largestFoldContainingBufferRow(selection.end.row) - newEndRow = fold.getBufferRange().end.row + 1 + # of the selection then the whole fold needs to be moved and restored. + # The initial fold range is stored and will be translated once the + # insert delta is know. + selectionFoldRanges = [] + foldAtSelectionStart = + @displayBuffer.largestFoldContainingBufferRow(selection.start.row) + foldAtSelectionEnd = + @displayBuffer.largestFoldContainingBufferRow(selection.end.row) + if fold = foldAtSelectionStart ? foldAtSelectionEnd + selectionFoldRanges.push range = fold.getBufferRange() + newEndRow = range.end.row + 1 linesRange.end.row = newEndRow if newEndRow > linesRange.end.row + fold.destroy() # If selected line range is preceded by a fold, one line above on screen # could be multiple lines in the buffer. @@ -915,9 +921,13 @@ class TextEditor extends Model insertDelta = linesRange.start.row - precedingBufferRow # Any folds in the text that is moved will need to be re-created. - rangesToRefold = + # It includes the folds that were intersecting with the selection. + rangesToRefold = selectionFoldRanges.concat( @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> - fold.getBufferRange().translate([-insertDelta, 0]) + range = fold.getBufferRange() + fold.destroy() + range + ).map (range) -> range.translate([-insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold if fold = @displayBuffer.largestFoldStartingAtBufferRow(precedingBufferRow) @@ -996,7 +1006,10 @@ class TextEditor extends Model # Any folds in the text that is moved will need to be re-created. # It includes the folds that were intersecting with the selection. rangesToRefold = selectionFoldRanges.concat( - @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> fold.getBufferRange() + @outermostFoldsInBufferRowRange(linesRange.start.row, linesRange.end.row).map (fold) -> + range = fold.getBufferRange() + fold.destroy() + range ).map (range) -> range.translate([insertDelta, 0]) # Make sure the inserted text doesn't go into an existing fold From 5d8ec8587e523fdf8b0d8b8310dc9f86ebe2af4b Mon Sep 17 00:00:00 2001 From: abe33 Date: Tue, 3 Nov 2015 00:51:01 +0100 Subject: [PATCH 44/48] :fire: Remove test focus --- spec/text-editor-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index bcf4fe7b8..a6ece20fc 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2136,7 +2136,7 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] - fdescribe ".moveLineDown", -> + describe ".moveLineDown", -> describe "when there is a single selection", -> describe "when the selection spans a single line", -> describe "when there is no fold in the following row", -> From 849dd33e8728b9c4bdd3002c0f40944c056b6fe5 Mon Sep 17 00:00:00 2001 From: abe33 Date: Wed, 4 Nov 2015 18:55:52 +0100 Subject: [PATCH 45/48] :art: Place move lines test with auto indent with other move lines tests --- spec/text-editor-spec.coffee | 58 +++++++++++++++++------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 40cfd4e97..552a0ee7c 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2124,6 +2124,20 @@ describe "TextEditor", -> describe "buffer manipulation", -> describe ".moveLineUp", -> + it "moves the line under the cursor up", -> + editor.setCursorBufferPosition([1, 0]) + editor.moveLineUp() + expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe " var sort = function(items) {" + expect(editor.indentationForBufferRow(0)).toBe 1 + expect(editor.indentationForBufferRow(1)).toBe 0 + + it "updates the line's indentation when the editor.autoIndent setting is true", -> + atom.config.set('editor.autoIndent', true) + editor.setCursorBufferPosition([1, 0]) + editor.moveLineUp() + expect(editor.indentationForBufferRow(0)).toBe 0 + expect(editor.indentationForBufferRow(1)).toBe 0 + describe "when there is a single selection", -> describe "when the selection spans a single line", -> describe "when there is no fold in the preceeding row", -> @@ -2438,6 +2452,20 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]] describe ".moveLineDown", -> + it "moves the line under the cursor down", -> + editor.setCursorBufferPosition([0, 0]) + editor.moveLineDown() + expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe "var quicksort = function () {" + expect(editor.indentationForBufferRow(0)).toBe 1 + expect(editor.indentationForBufferRow(1)).toBe 0 + + it "updates the line's indentation when the editor.autoIndent setting is true", -> + atom.config.set('editor.autoIndent', true) + editor.setCursorBufferPosition([0, 0]) + editor.moveLineDown() + expect(editor.indentationForBufferRow(0)).toBe 1 + expect(editor.indentationForBufferRow(1)).toBe 2 + describe "when there is a single selection", -> describe "when the selection spans a single line", -> describe "when there is no fold in the following row", -> @@ -5150,36 +5178,6 @@ describe "TextEditor", -> """ expect(editor.getSelectedBufferRange()).toEqual [[13, 0], [14, 2]] - describe ".moveLineUp()", -> - it "moves the line under the cursor up", -> - editor.setCursorBufferPosition([1, 0]) - editor.moveLineUp() - expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe " var sort = function(items) {" - expect(editor.indentationForBufferRow(0)).toBe 1 - expect(editor.indentationForBufferRow(1)).toBe 0 - - it "updates the line's indentation when the editor.autoIndent setting is true", -> - atom.config.set('editor.autoIndent', true) - editor.setCursorBufferPosition([1, 0]) - editor.moveLineUp() - expect(editor.indentationForBufferRow(0)).toBe 0 - expect(editor.indentationForBufferRow(1)).toBe 0 - - describe ".moveLineDown()", -> - it "moves the line under the cursor down", -> - editor.setCursorBufferPosition([0, 0]) - editor.moveLineDown() - expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe "var quicksort = function () {" - expect(editor.indentationForBufferRow(0)).toBe 1 - expect(editor.indentationForBufferRow(1)).toBe 0 - - it "updates the line's indentation when the editor.autoIndent setting is true", -> - atom.config.set('editor.autoIndent', true) - editor.setCursorBufferPosition([0, 0]) - editor.moveLineDown() - expect(editor.indentationForBufferRow(0)).toBe 1 - expect(editor.indentationForBufferRow(1)).toBe 2 - describe ".shouldPromptToSave()", -> it "returns false when an edit session's buffer is in use by more than one session", -> jasmine.unspy(editor, 'shouldPromptToSave') From aa7f87bd62e930149f3d7181d759a2779b2b45bf Mon Sep 17 00:00:00 2001 From: abe33 Date: Wed, 4 Nov 2015 18:56:13 +0100 Subject: [PATCH 46/48] :bug: Fix auto indent no longer applied when moving lines --- src/text-editor.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d64bcb47c..bdcbbf4e0 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -948,6 +948,7 @@ class TextEditor extends Model newSelectionRanges.push(selection.translate([-insertDelta, 0])) @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) + @autoIndentSelectedRows() if @shouldAutoIndent() @scrollToBufferPosition([newSelectionRanges[0].start.row + 2, 0]) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen @@ -1034,6 +1035,7 @@ class TextEditor extends Model newSelectionRanges.push(selection.translate([insertDelta, 0])) @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) + @autoIndentSelectedRows() if @shouldAutoIndent() @scrollToBufferPosition([newSelectionRanges[0].start.row - 1, 0]) # Duplicate the most recent cursor's current line. From 80553028c15b5662e0f3a9834b3080f6a7876ed1 Mon Sep 17 00:00:00 2001 From: abe33 Date: Wed, 4 Nov 2015 19:14:44 +0100 Subject: [PATCH 47/48] :bug: Fix top of a wrapped line not visible when moving it up --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index bdcbbf4e0..87848807c 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -949,7 +949,7 @@ class TextEditor extends Model @setSelectedBufferRanges(newSelectionRanges, {autoscroll: false, preserveFolds: true}) @autoIndentSelectedRows() if @shouldAutoIndent() - @scrollToBufferPosition([newSelectionRanges[0].start.row + 2, 0]) + @scrollToBufferPosition([newSelectionRanges[0].start.row, 0]) # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen # coordinates. From 5b192ea2955c18d15a4328999928765d0f4419f6 Mon Sep 17 00:00:00 2001 From: abe33 Date: Thu, 5 Nov 2015 20:36:22 +0100 Subject: [PATCH 48/48] :art: Fix typo in moveLineUp comment --- src/text-editor.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 87848807c..36639251b 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -866,8 +866,8 @@ class TextEditor extends Model @transact groupingInterval, => fn(selection, index) for selection, index in @getSelectionsOrderedByBufferPosition() - # Move lines intersection the most recent selection or multiple selections up by one row in screen - # coordinates. + # Move lines intersecting the most recent selection or multiple selections + # up by one row in screen coordinates. moveLineUp: -> selections = @getSelectedBufferRanges() selections.sort (a, b) -> a.compare(b) @@ -951,8 +951,8 @@ class TextEditor extends Model @autoIndentSelectedRows() if @shouldAutoIndent() @scrollToBufferPosition([newSelectionRanges[0].start.row, 0]) - # Move lines intersecting the most recent selection or muiltiple selections down by one row in screen - # coordinates. + # Move lines intersecting the most recent selection or muiltiple selections + # down by one row in screen coordinates. moveLineDown: -> selections = @getSelectedBufferRanges() selections.sort (a, b) -> a.compare(b)