diff --git a/spec/atom/editor-spec.coffee b/spec/atom/editor-spec.coffee index 3812ed3d4..c84f20dac 100644 --- a/spec/atom/editor-spec.coffee +++ b/spec/atom/editor-spec.coffee @@ -1013,46 +1013,24 @@ describe "Editor", -> expect(cursor3.getBufferPosition()).toEqual [4, 0] describe "selections", -> - it "adds an additional selection upon clicking and dragging with the meta-key held down", -> - editor.attachToDom() - editor.lines.trigger mousedownEvent(editor: editor, point: [4, 10]) - editor.lines.trigger mousemoveEvent(editor: editor, point: [5, 27]) - editor.lines.trigger 'mouseup' + describe "upon clicking and dragging with the meta-key held down", -> + it "adds an additional selection upon clicking and dragging with the meta-key held down", -> + editor.attachToDom() + editor.lines.trigger mousedownEvent(editor: editor, point: [4, 10]) + editor.lines.trigger mousemoveEvent(editor: editor, point: [5, 27]) + editor.lines.trigger 'mouseup' - editor.lines.trigger mousedownEvent(editor: editor, point: [6, 10], metaKey: true) - editor.lines.trigger mousemoveEvent(editor: editor, point: [8, 27], metaKey: true) - editor.lines.trigger 'mouseup' + editor.lines.trigger mousedownEvent(editor: editor, point: [6, 10], metaKey: true) + editor.lines.trigger mousemoveEvent(editor: editor, point: [8, 27], metaKey: true) + editor.lines.trigger 'mouseup' - selections = editor.compositeSelection.getSelections() - expect(selections.length).toBe 2 - [selection1, selection2] = selections - expect(selection1.getScreenRange()).toEqual [[4, 10], [5, 27]] - expect(selection2.getScreenRange()).toEqual [[6, 10], [8, 27]] + selections = editor.compositeSelection.getSelections() + expect(selections.length).toBe 2 + [selection1, selection2] = selections + expect(selection1.getScreenRange()).toEqual [[4, 10], [5, 27]] + expect(selection2.getScreenRange()).toEqual [[6, 10], [8, 27]] - it "adjusts all selections based on keyboard movement", -> - editor.setSelectionBufferRange [[0,9], [0,13]] - editor.addSelectionForBufferRange [[3,16], [3,21]] - [selection1, selection2] = editor.compositeSelection.getSelections() - - editor.selectRight() - expect(selection1.getBufferRange()).toEqual [[0,9], [0,14]] - expect(selection2.getBufferRange()).toEqual [[3,16], [3,22]] - - editor.selectLeft() - editor.selectLeft() - expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]] - expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]] - - editor.selectDown() - expect(selection1.getBufferRange()).toEqual [[0,9], [1,12]] - expect(selection2.getBufferRange()).toEqual [[3,16], [4,20]] - - editor.selectUp() - expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]] - expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]] - - describe "when multiple selctions intersect", -> - it "merges a selection that is completely contained within another", -> + it "merges selections when they intersect", -> editor.attachToDom() editor.lines.trigger mousedownEvent(editor: editor, point: [4, 10]) editor.lines.trigger mousemoveEvent(editor: editor, point: [5, 27]) @@ -1067,6 +1045,71 @@ describe "Editor", -> [selection1] = selections expect(selection1.getScreenRange()).toEqual [[3, 10], [6, 27]] + describe "upon moving the cursor with the arrow keys with the shift key held down", -> + it "resizes all selections", -> + editor.setSelectionBufferRange [[0,9], [0,13]] + editor.addSelectionForBufferRange [[3,16], [3,21]] + [selection1, selection2] = editor.compositeSelection.getSelections() + + editor.selectRight() + expect(selection1.getBufferRange()).toEqual [[0,9], [0,14]] + expect(selection2.getBufferRange()).toEqual [[3,16], [3,22]] + + editor.selectLeft() + editor.selectLeft() + expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]] + expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]] + + editor.selectDown() + expect(selection1.getBufferRange()).toEqual [[0,9], [1,12]] + expect(selection2.getBufferRange()).toEqual [[3,16], [4,20]] + + editor.selectUp() + expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]] + expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]] + + it "merges selections when they intersect when moving down", -> + editor.setSelectionBufferRange [[0,9], [0,13]] + editor.addSelectionForBufferRange [[1,10], [1,20]] + editor.addSelectionForBufferRange [[2,15], [3,25]] + [selection1, selection2, selection3] = editor.compositeSelection.getSelections() + + editor.selectDown() + expect(editor.compositeSelection.getSelections()).toEqual [selection1] + expect(selection1.getScreenRange()).toEqual([[0, 9], [4, 25]]) + expect(selection2.parent()).not.toExist() + expect(selection3.parent()).not.toExist() + + it "merges selections when they intersect when moving up", -> + editor.setSelectionBufferRange [[0,9], [0,13]], reverse: true + editor.addSelectionForBufferRange [[1,10], [1,20]], reverse: true + [selection1, selection2] = editor.compositeSelection.getSelections() + + editor.selectUp() + expect(editor.compositeSelection.getSelections()).toEqual [selection1] + expect(selection1.getScreenRange()).toEqual([[0, 0], [1, 20]]) + expect(selection2.parent()).not.toExist() + + it "merges selections when they intersect when moving left", -> + editor.setSelectionBufferRange [[0,9], [0,13]], reverse: true + editor.addSelectionForBufferRange [[0,14], [1,20]], reverse: true + [selection1, selection2] = editor.compositeSelection.getSelections() + + editor.selectLeft() + expect(editor.compositeSelection.getSelections()).toEqual [selection1] + expect(selection1.getScreenRange()).toEqual([[0, 8], [1, 20]]) + expect(selection2.parent()).not.toExist() + + it "merges selections when they intersect when moving right", -> + editor.setSelectionBufferRange [[0,9], [0,13]] + editor.addSelectionForBufferRange [[0,14], [1,20]] + [selection1, selection2] = editor.compositeSelection.getSelections() + + editor.selectRight() + expect(editor.compositeSelection.getSelections()).toEqual [selection1] + expect(selection1.getScreenRange()).toEqual([[0, 9], [1, 21]]) + expect(selection2.parent()).not.toExist() + describe "cursor merging", -> it "merges cursors when they overlap due to a buffer change", -> editor.setCursorScreenPosition([0, 0]) diff --git a/spec/atom/range-spec.coffee b/spec/atom/range-spec.coffee index 2c9b70667..780962a5f 100644 --- a/spec/atom/range-spec.coffee +++ b/spec/atom/range-spec.coffee @@ -16,11 +16,13 @@ describe "Range", -> expect(new Range(new Point(1, 1), new Point(1, 2)).isEmpty()).toBeFalsy() describe ".intersectsWith(otherRange)", -> - it "returns true if the ranges intersect", -> + it "returns true if the ranges intersect or share an endpoint", -> expect(new Range([1, 1], [2, 10]).intersectsWith(new Range([2, 1], [3, 10]))).toBeTruthy() expect(new Range([2, 1], [3, 10]).intersectsWith(new Range([1, 1], [2, 10]))).toBeTruthy() expect(new Range([2, 1], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeTruthy() expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([2, 1], [3, 10]))).toBeTruthy() + expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([3, 1], [3, 10]))).toBeTruthy() + expect(new Range([3, 1], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeTruthy() expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([3, 2], [3, 10]))).toBeFalsy() expect(new Range([3, 2], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeFalsy() diff --git a/src/atom/composite-selection.coffee b/src/atom/composite-selection.coffee index efb3dcae2..15ce96c5b 100644 --- a/src/atom/composite-selection.coffee +++ b/src/atom/composite-selection.coffee @@ -17,9 +17,9 @@ class CompositeSeleciton @selections.push(selection) @editor.lines.append(selection) - addSelectionForBufferRange: (bufferRange) -> + addSelectionForBufferRange: (bufferRange, options) -> cursor = @editor.compositeCursor.addCursor() - @selectionForCursor(cursor).setBufferRange(bufferRange) + @selectionForCursor(cursor).setBufferRange(bufferRange, options) removeSelectionForCursor: (cursor) -> _.remove(@selections, @selectionForCursor(cursor)) @@ -31,32 +31,36 @@ class CompositeSeleciton selection.handleBufferChange(e) for selection in @getSelections() insertText: (text) -> - @modifySelections (selection) -> + @modifySelectedText (selection) -> selection.insertText(text) backspace: -> - @modifySelections (selection) -> selection.backspace() + @modifySelectedText (selection) -> selection.backspace() delete: -> - @modifySelections (selection) -> selection.delete() + @modifySelectedText (selection) -> selection.delete() selectToScreenPosition: (position) -> @lastSelection().selectToScreenPosition(position) selectRight: -> selection.selectRight() for selection in @getSelections() + @mergeIntersectingSelections() selectLeft: -> selection.selectLeft() for selection in @getSelections() + @mergeIntersectingSelections() selectUp: -> selection.selectUp() for selection in @getSelections() + @mergeIntersectingSelections() selectDown: -> selection.selectDown() for selection in @getSelections() + @mergeIntersectingSelections() - setBufferRange: (bufferRange) -> - @lastSelection().setBufferRange(bufferRange) + setBufferRange: (bufferRange, options) -> + @lastSelection().setBufferRange(bufferRange, options) getBufferRange: (bufferRange) -> @lastSelection().getBufferRange() @@ -77,7 +81,7 @@ class CompositeSeleciton @mergeIntersectingSelections() return - modifySelections: (fn) -> + modifySelectedText: (fn) -> selection.retainSelection = true for selection in @getSelections() for selection in @getSelections() selection.retainSelection = false @@ -85,7 +89,7 @@ class CompositeSeleciton cut: -> maintainPasteboard = false - @modifySelections (selection) -> + @modifySelectedText (selection) -> selection.cut(maintainPasteboard) maintainPasteboard = true diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index dca1cf38e..05b5fc2a4 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -356,8 +356,8 @@ class Editor extends View getSelection: (index) -> @compositeSelection.getSelection(index) getSelectedText: -> @compositeSelection.getSelection().getText() - setSelectionBufferRange: (bufferRange) -> @compositeSelection.setBufferRange(bufferRange) - addSelectionForBufferRange: (bufferRange) -> @compositeSelection.addSelectionForBufferRange(bufferRange) + setSelectionBufferRange: (bufferRange, options) -> @compositeSelection.setBufferRange(bufferRange, options) + addSelectionForBufferRange: (bufferRange, options) -> @compositeSelection.addSelectionForBufferRange(bufferRange, options) selectRight: -> @compositeSelection.selectRight() selectLeft: -> @compositeSelection.selectLeft() selectUp: -> @compositeSelection.selectUp() diff --git a/src/atom/range.coffee b/src/atom/range.coffee index e17e0d973..f16848ff0 100644 --- a/src/atom/range.coffee +++ b/src/atom/range.coffee @@ -42,7 +42,7 @@ class Range intersectsWith: (otherRange) -> if @start.isLessThanOrEqual(otherRange.start) - @end.isGreaterThan(otherRange.start) + @end.isGreaterThanOrEqual(otherRange.start) else otherRange.intersectsWith(this) diff --git a/src/atom/selection.coffee b/src/atom/selection.coffee index 483ef1928..fac11fa19 100644 --- a/src/atom/selection.coffee +++ b/src/atom/selection.coffee @@ -81,16 +81,20 @@ class Selection extends View else new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition()) - setScreenRange: (range) -> - @cursor.setScreenPosition(range.start) + setScreenRange: (range, options={}) -> + { reverse } = options + { start, end } = range + [start, end] = [end, start] if reverse + + @cursor.setScreenPosition(start) @modifySelection => - @cursor.setScreenPosition(range.end) + @cursor.setScreenPosition(end) getBufferRange: -> @editor.bufferRangeForScreenRange(@getScreenRange()) - setBufferRange: (bufferRange) -> - @setScreenRange(@editor.screenRangeForBufferRange(bufferRange)) + setBufferRange: (bufferRange, options) -> + @setScreenRange(@editor.screenRangeForBufferRange(bufferRange), options) getText: -> @editor.buffer.getTextInRange @getBufferRange()