diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 86aab0856..548c88105 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -1845,6 +1845,135 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(editor.getSelectedScreenRange()).toEqual [[3, 4], [9, 0]] + describe "when soft wrap is enabled", -> + beforeEach -> + gutterNode = componentNode.querySelector('.gutter') + + editor.setSoftWrapped(true) + nextAnimationFrame() + componentNode.style.width = 21 * charWidth + editor.getVerticalScrollbarWidth() + 'px' + component.measureHeightAndWidth() + nextAnimationFrame() + + describe "when the gutter is clicked", -> + it "selects the clicked buffer row", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1))) + expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [2, 0]] + + describe "when the gutter is meta-clicked", -> + it "creates a new selection for the clicked buffer row", -> + editor.setSelectedScreenRange([[1, 0], [1, 2]]) + + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[1, 0], [1, 2]], [[2, 0], [5, 0]]] + + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(7), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[1, 0], [1, 2]], [[2, 0], [5, 0]], [[5, 0], [10, 0]]] + + describe "when the gutter is shift-clicked", -> + beforeEach -> + editor.setSelectedScreenRange([[7, 4], [7, 6]]) + + describe "when the clicked row is before the current selection's tail", -> + it "selects to the beginning of the clicked buffer row", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), shiftKey: true)) + expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [7, 4]] + + describe "when the clicked row is after the current selection's tail", -> + it "selects to the beginning of the buffer row following the clicked buffer row", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(11), shiftKey: true)) + expect(editor.getSelectedScreenRange()).toEqual [[7, 4], [16, 0]] + + describe "when the gutter is clicked and dragged", -> + describe "when dragging downward", -> + it "selects the buffer rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1))) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6))) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6))) + expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [10, 0]] + + describe "when dragging upward", -> + it "selects the buffer rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6))) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(1))) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(1))) + expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [10, 0]] + + describe "when the gutter is meta-clicked and dragged", -> + beforeEach -> + editor.setSelectedScreenRange([[7, 4], [7, 6]]) + + describe "when dragging downward", -> + it "selects the buffer rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(3), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(3), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[7, 4], [7, 6]], [[0, 0], [5, 0]]] + + it "merges overlapping selections", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(7), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(7), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[0, 0], [10, 0]]] + + describe "when dragging upward", -> + it "selects the buffer rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(17), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(11), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[7, 4], [7, 6]], [[10, 0], [20, 0]]] + + it "merges overlapping selections", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(17), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(9), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(9), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[5, 0], [20, 0]]] + + describe "when the gutter is shift-clicked and dragged", -> + describe "when the shift-click is below the existing selection's tail", -> + describe "when dragging downward", -> + it "selects the buffer rows between the existing selection's tail and the end of the drag", -> + editor.setSelectedScreenRange([[1, 4], [1, 7]]) + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(7), shiftKey: true)) + + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11))) + nextAnimationFrame() + expect(editor.getSelectedScreenRange()).toEqual [[1, 4], [16, 0]] + + describe "when dragging upward", -> + it "selects the buffer rows between the end of the drag and the tail of the existing selection", -> + editor.setSelectedScreenRange([[1, 4], [1, 7]]) + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(11), shiftKey: true)) + + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(7))) + nextAnimationFrame() + expect(editor.getSelectedScreenRange()).toEqual [[1, 4], [10, 0]] + + describe "when the shift-click is above the existing selection's tail", -> + describe "when dragging upward", -> + it "selects the buffer rows between the end of the drag and the tail of the existing selection", -> + editor.setSelectedScreenRange([[7, 4], [7, 6]]) + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(3), shiftKey: true)) + + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(1))) + nextAnimationFrame() + expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [7, 4]] + + describe "when dragging downward", -> + it "selects the buffer rows between the existing selection's tail and the end of the drag", -> + editor.setSelectedScreenRange([[7, 4], [7, 6]]) + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(1), shiftKey: true)) + + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(3))) + nextAnimationFrame() + expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 4]] + describe "focus handling", -> inputNode = null diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 9536bcaad..75a9a5136 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -404,29 +404,33 @@ class TextEditorComponent onGutterClick: (event) => clickedRow = @screenPositionForMouseEvent(event).row + clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow) - @editor.setSelectedScreenRange([[clickedRow, 0], [clickedRow + 1, 0]], preserveFolds: true) + @editor.setSelectedBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true) @handleDragUntilMouseUp event, (screenPosition) => dragRow = screenPosition.row - if dragRow < clickedRow # dragging up - @editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true) + dragBufferRow = @editor.bufferRowForScreenRow(dragRow) + if dragBufferRow < clickedBufferRow # dragging up + @editor.setSelectedBufferRange([[dragBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true) else - @editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true) + @editor.setSelectedBufferRange([[clickedBufferRow, 0], [dragBufferRow + 1, 0]], preserveFolds: true) onGutterMetaClick: (event) => clickedRow = @screenPositionForMouseEvent(event).row + clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow) - bufferRange = @editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]]) + bufferRange = new Range([clickedBufferRow, 0], [clickedBufferRow + 1, 0]) rowSelection = @editor.addSelectionForBufferRange(bufferRange, preserveFolds: true) @handleDragUntilMouseUp event, (screenPosition) => dragRow = screenPosition.row + dragBufferRow = @editor.bufferRowForScreenRow(dragRow) - if dragRow < clickedRow # dragging up - rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true) + if dragBufferRow < clickedBufferRow # dragging up + rowSelection.setBufferRange([[dragBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true) else - rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true) + rowSelection.setBufferRange([[clickedBufferRow, 0], [dragBufferRow + 1, 0]], preserveFolds: true) # After updating the selected screen range, merge overlapping selections @editor.mergeIntersectingSelections(preserveFolds: true) @@ -439,19 +443,23 @@ class TextEditorComponent onGutterShiftClick: (event) => clickedRow = @screenPositionForMouseEvent(event).row + clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow) tailPosition = @editor.getLastSelection().getTailScreenPosition() + tailBufferPosition = @editor.bufferPositionForScreenPosition(tailPosition) if clickedRow < tailPosition.row - @editor.selectToScreenPosition([clickedRow, 0]) + @editor.selectToBufferPosition([clickedBufferRow, 0]) else - @editor.selectToScreenPosition([clickedRow + 1, 0]) + @editor.selectToBufferPosition([clickedBufferRow + 1, 0]) @handleDragUntilMouseUp event, (screenPosition) => dragRow = screenPosition.row + dragBufferRow = @editor.bufferRowForScreenRow(dragRow) if dragRow < tailPosition.row # dragging up - @editor.setSelectedScreenRange([[dragRow, 0], tailPosition], preserveFolds: true) + @editor.setSelectedBufferRange([[dragBufferRow, 0], tailBufferPosition], preserveFolds: true) else - @editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true) + @editor.setSelectedBufferRange([tailBufferPosition, [dragBufferRow + 1, 0]], preserveFolds: true) + onStylesheetsChanged: (styleElement) => return unless @performedInitialMeasurement