diff --git a/package.json b/package.json index 98a71f29c..dea61b73b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", - "fuzzaldrin": "^1.1", + "fuzzaldrin": "^2.1", "git-utils": "^2.1.4", "grim": "0.12.0", "guid": "0.0.10", @@ -84,7 +84,7 @@ "exception-reporting": "0.20.0", "feedback": "0.33.0", "find-and-replace": "0.134.0", - "fuzzy-finder": "0.57.0", + "fuzzy-finder": "0.58.0", "git-diff": "0.39.0", "go-to-line": "0.25.0", "grammar-selector": "0.29.0", diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 2cdb306e9..2ea61d36c 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1331,9 +1331,19 @@ describe "EditorComponent", -> gutterNode = componentNode.querySelector('.gutter') describe "when the gutter is clicked", -> - it "moves the cursor to the beginning of the clicked row", -> + it "selects the clicked row", -> gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4))) - expect(editor.getCursorScreenPosition()).toEqual [4, 0] + expect(editor.getSelectedScreenRange()).toEqual [[4, 0], [5, 0]] + + describe "when the gutter is meta-clicked", -> + it "creates a new selection for the clicked row", -> + editor.setSelectedScreenRange([[3, 0], [3, 2]]) + + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]]] + + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]], [[6, 0], [7, 0]]] describe "when the gutter is shift-clicked", -> beforeEach -> @@ -1366,6 +1376,40 @@ describe "EditorComponent", -> gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2))) expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]] + describe "when the gutter is meta-clicked and dragged", -> + beforeEach -> + editor.setSelectedScreenRange([[3, 0], [3, 2]]) + + describe "when dragging downward", -> + it "selects the rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]] + + it "merges overlapping selections", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]] + + describe "when dragging upward", -> + it "selects the rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]] + + it "merges overlapping selections", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 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", -> diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 6fdbed35b..46d43ed7d 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -138,7 +138,6 @@ afterEach -> jasmine.unspy(atom, 'saveSync') ensureNoPathSubscriptions() atom.syntax.off() - ensureNoDeprecatedFunctionsCalled() if isCoreSpec waits(0) # yield to ui thread to make screen update more frequently ensureNoPathSubscriptions = -> diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 3446bb6a2..056c29fe7 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -1,3 +1,4 @@ +_ = require 'underscore-plus' React = require 'react-atom-fork' {div, span} = require 'reactionary-atom-fork' {debounce, defaults, isEqualForProperties} = require 'underscore-plus' @@ -644,8 +645,12 @@ EditorComponent = React.createClass onGutterMouseDown: (event) -> return unless event.button is 0 # only handle the left mouse button - if event.shiftKey + {shiftKey, metaKey, ctrlKey} = event + + if shiftKey @onGutterShiftClick(event) + else if metaKey or (ctrlKey and process.platform isnt 'darwin') + @onGutterMetaClick(event) else @onGutterClick(event) @@ -653,14 +658,38 @@ EditorComponent = React.createClass {editor} = @props clickedRow = @screenPositionForMouseEvent(event).row - editor.setCursorScreenPosition([clickedRow, 0]) + editor.setSelectedScreenRange([[clickedRow, 0], [clickedRow + 1, 0]], preserveFolds: true) @handleDragUntilMouseUp event, (screenPosition) -> dragRow = screenPosition.row if dragRow < clickedRow # dragging up - editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]]) + editor.setSelectedScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true) else - editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]]) + editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true) + + onGutterMetaClick: (event) -> + {editor} = @props + clickedRow = @screenPositionForMouseEvent(event).row + + bufferRange = editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]]) + rowSelection = editor.addSelectionForBufferRange(bufferRange, preserveFolds: true) + + @handleDragUntilMouseUp event, (screenPosition) -> + dragRow = screenPosition.row + + if dragRow < clickedRow # dragging up + rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]], preserveFolds: true) + else + rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]], preserveFolds: true) + + # After updating the selected screen range, merge overlapping selections + editor.mergeIntersectingSelections(preserveFolds: true) + + # The merge process will possibly destroy the current selection because + # it will be merged into another one. Therefore, we need to obtain a + # reference to the new selection that contains the originally selected row + rowSelection = _.find editor.getSelections(), (selection) -> + selection.intersectsBufferRange(bufferRange) onGutterShiftClick: (event) -> {editor} = @props @@ -675,9 +704,9 @@ EditorComponent = React.createClass @handleDragUntilMouseUp event, (screenPosition) -> dragRow = screenPosition.row if dragRow < tailPosition.row # dragging up - editor.setSelectedScreenRange([[dragRow, 0], tailPosition]) + editor.setSelectedScreenRange([[dragRow, 0], tailPosition], preserveFolds: true) else - editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]]) + editor.setSelectedScreenRange([tailPosition, [dragRow + 1, 0]], preserveFolds: true) onStylesheetsChanged: (stylesheet) -> return unless @performedInitialMeasurement diff --git a/src/editor.coffee b/src/editor.coffee index 9f9d18da7..4e5be2195 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -2256,7 +2256,7 @@ class Editor extends Model selection = new Selection(_.extend({editor: this, marker, cursor}, options)) @selections.push(selection) selectionBufferRange = selection.getBufferRange() - @mergeIntersectingSelections() + @mergeIntersectingSelections(preserveFolds: marker.getAttributes().preserveFolds) if selection.destroyed for selection in @getSelections() if selection.intersectsBufferRange(selectionBufferRange) diff --git a/src/git.coffee b/src/git.coffee index 2c9cc7550..9bbd25d61 100644 --- a/src/git.coffee +++ b/src/git.coffee @@ -27,12 +27,14 @@ Task = require './task' # # ## Examples # +# ### Logging the URL of the origin remote +# # ```coffee # git = atom.project.getRepo() # console.log git.getOriginUrl() # ``` # -# ## Requiring in packages +# ### Requiring in packages # # ```coffee # {Git} = require 'atom' diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index e9eeddaf9..c6cf62490 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -21,7 +21,7 @@ GutterComponent = React.createClass if gutterBackgroundColor isnt 'rbga(0, 0, 0, 0)' backgroundColor = gutterBackgroundColor - div className: 'gutter', onClick: @onClick, onMouseDown: onMouseDown, + div className: 'gutter', onClick: @onClick, onMouseDown: @onMouseDown, div className: 'line-numbers', ref: 'lineNumbers', style: height: Math.max(scrollHeight, scrollViewHeight) WebkitTransform: @getTransform() @@ -215,6 +215,14 @@ GutterComponent = React.createClass lineNumberNodeForScreenRow: (screenRow) -> @lineNumberNodesById[@lineNumberIdsByScreenRow[screenRow]] + onMouseDown: (event) -> + {editor} = @props + {target} = event + lineNumber = target.parentNode + + unless target.classList.contains('icon-right') and lineNumber.classList.contains('foldable') + @props.onMouseDown(event) + onClick: (event) -> {editor} = @props {target} = event