diff --git a/src/selection.coffee b/src/selection.coffee index 6c6609a38..0e565cf33 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -190,7 +190,7 @@ class Selection extends Model # position. # # * `position` An instance of {Point}, with a given `row` and `column`. - selectToScreenPosition: (position) -> + selectToScreenPosition: (position, options) -> position = Point.fromObject(position) @modifySelection => @@ -200,12 +200,12 @@ class Selection extends Model else @marker.setScreenRange([@initialScreenRange.start, position], reversed: false) else - @cursor.setScreenPosition(position) + @cursor.setScreenPosition(position, options) if @linewise - @expandOverLine() + @expandOverLine(options) else if @wordwise - @expandOverWord() + @expandOverWord(options) # Public: Selects the text from the current cursor position to a given buffer # position. @@ -311,28 +311,28 @@ class Selection extends Model # Public: Modifies the selection to encompass the current word. # # Returns a {Range}. - selectWord: -> - options = {} + selectWord: (options={}) -> options.wordRegex = /[\t ]*/ if @cursor.isSurroundedByWhitespace() if @cursor.isBetweenWordAndNonWord() options.includeNonWordCharacters = false - @setBufferRange(@cursor.getCurrentWordBufferRange(options)) + @setBufferRange(@cursor.getCurrentWordBufferRange(options), options) @wordwise = true @initialScreenRange = @getScreenRange() # Public: Expands the newest selection to include the entire word on which # the cursors rests. - expandOverWord: -> + expandOverWord: (options) -> @setBufferRange(@getBufferRange().union(@cursor.getCurrentWordBufferRange()), autoscroll: false) - @cursor.autoscroll() + @cursor.autoscroll() if options?.autoscroll ? true # Public: Selects an entire line in the buffer. # # * `row` The line {Number} to select (default: the row of the cursor). - selectLine: (row=@cursor.getBufferPosition().row) -> + selectLine: (row, options) -> + row ?= @cursor.getBufferPosition().row range = @editor.bufferRangeForBufferRow(row, includeNewline: true) - @setBufferRange(@getBufferRange().union(range), autoscroll: true) + @setBufferRange(@getBufferRange().union(range), options) @linewise = true @wordwise = false @initialScreenRange = @getScreenRange() @@ -341,10 +341,10 @@ class Selection extends Model # the cursor currently rests. # # It also includes the newline character. - expandOverLine: -> + expandOverLine: (options) -> range = @getBufferRange().union(@cursor.getCurrentLineBufferRange(includeNewline: true)) @setBufferRange(range, autoscroll: false) - @cursor.autoscroll() + @cursor.autoscroll() if options?.autoscroll ? true ### Section: Modifying the selected text diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 81f92476f..7382cebcc 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -395,16 +395,16 @@ class TextEditorComponent if cursorAtScreenPosition and @editor.hasMultipleCursors() cursorAtScreenPosition.destroy() else - @editor.addCursorAtScreenPosition(screenPosition) + @editor.addCursorAtScreenPosition(screenPosition, autoscroll: false) else - @editor.setCursorScreenPosition(screenPosition) + @editor.setCursorScreenPosition(screenPosition, autoscroll: false) when 2 - @editor.getLastSelection().selectWord() + @editor.getLastSelection().selectWord(autoscroll: false) when 3 - @editor.getLastSelection().selectLine() + @editor.getLastSelection().selectLine(null, autoscroll: false) @handleDragUntilMouseUp (screenPosition) => - @editor.selectToScreenPosition(screenPosition, true) + @editor.selectToScreenPosition(screenPosition, suppressSelectionMerge: true, autoscroll: false) onLineNumberGutterMouseDown: (event) => return unless event.button is 0 # only handle the left mouse button @@ -422,14 +422,14 @@ class TextEditorComponent clickedScreenRow = @screenPositionForMouseEvent(event).row clickedBufferRow = @editor.bufferRowForScreenRow(clickedScreenRow) initialScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]]) - @editor.setSelectedScreenRange(initialScreenRange, preserveFolds: true) + @editor.setSelectedScreenRange(initialScreenRange, preserveFolds: true, autoscroll: false) @handleGutterDrag(initialScreenRange) onGutterMetaClick: (event) => clickedScreenRow = @screenPositionForMouseEvent(event).row clickedBufferRow = @editor.bufferRowForScreenRow(clickedScreenRow) initialScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]]) - @editor.addSelectionForScreenRange(initialScreenRange, preserveFolds: true) + @editor.addSelectionForScreenRange(initialScreenRange, preserveFolds: true, autoscroll: false) @handleGutterDrag(initialScreenRange) onGutterShiftClick: (event) => @@ -439,9 +439,9 @@ class TextEditorComponent clickedLineScreenRange = @editor.screenRangeForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]]) if clickedScreenRow < tailScreenPosition.row - @editor.selectToScreenPosition(clickedLineScreenRange.start, true) + @editor.selectToScreenPosition(clickedLineScreenRange.start, suppressSelectionMerge: true, autoscroll: false) else - @editor.selectToScreenPosition(clickedLineScreenRange.end, true) + @editor.selectToScreenPosition(clickedLineScreenRange.end, suppressSelectionMerge: true, autoscroll: false) @handleGutterDrag(new Range(tailScreenPosition, tailScreenPosition)) @@ -456,7 +456,6 @@ class TextEditorComponent endPosition = [dragRow + 1, 0] screenRange = new Range(endPosition, endPosition).union(initialRange) @editor.getLastSelection().setScreenRange(screenRange, reversed: false, autoscroll: false, preserveFolds: true) - @editor.getLastCursor().autoscroll() onStylesheetsChanged: (styleElement) => return unless @performedInitialMeasurement @@ -512,7 +511,9 @@ class TextEditorComponent animationLoop = => @requestAnimationFrame => if dragging and @mounted - screenPosition = @screenPositionForMouseEvent(lastMousePosition) + linesClientRect = @linesComponent.getDomNode().getBoundingClientRect() + autoscroll(lastMousePosition, linesClientRect) + screenPosition = @screenPositionForMouseEvent(lastMousePosition, linesClientRect) dragHandler(screenPosition) animationLoop() else if not @mounted @@ -543,6 +544,32 @@ class TextEditorComponent window.removeEventListener('mouseup', onMouseUp) willInsertTextSubscription.dispose() + autoscroll = (mouseClientPosition) => + editorClientRect = @domNode.getBoundingClientRect() + + if mouseClientPosition.clientY < editorClientRect.top + mouseYDelta = editorClientRect.top - mouseClientPosition.clientY + yDirection = -1 + else if mouseClientPosition.clientY > editorClientRect.bottom + mouseYDelta = mouseClientPosition.clientY - editorClientRect.bottom + yDirection = 1 + + if mouseClientPosition.clientX < editorClientRect.left + mouseXDelta = editorClientRect.left - mouseClientPosition.clientX + xDirection = -1 + else if mouseClientPosition.clientX > editorClientRect.right + mouseXDelta = mouseClientPosition.clientX - editorClientRect.right + xDirection = 1 + + if mouseYDelta? + @presenter.setScrollTop(@presenter.getScrollTop() + yDirection * scaleScrollDelta(mouseYDelta)) + + if mouseXDelta? + @presenter.setScrollLeft(@presenter.getScrollLeft() + xDirection * scaleScrollDelta(mouseXDelta)) + + scaleScrollDelta = (scrollDelta) -> + Math.pow(scrollDelta / 2, 3) / 280 + pasteSelectionClipboard = (event) => if event?.which is 2 and process.platform is 'linux' if selection = require('./safe-clipboard').readText('selection') @@ -748,17 +775,20 @@ class TextEditorComponent if scrollSensitivity = parseInt(scrollSensitivity) @scrollSensitivity = Math.abs(scrollSensitivity) / 100 - screenPositionForMouseEvent: (event) -> - pixelPosition = @pixelPositionForMouseEvent(event) + screenPositionForMouseEvent: (event, linesClientRect) -> + pixelPosition = @pixelPositionForMouseEvent(event, linesClientRect) @editor.screenPositionForPixelPosition(pixelPosition) - pixelPositionForMouseEvent: (event) -> + pixelPositionForMouseEvent: (event, linesClientRect) -> {clientX, clientY} = event - linesClientRect = @linesComponent.getDomNode().getBoundingClientRect() + linesClientRect ?= @linesComponent.getDomNode().getBoundingClientRect() top = clientY - linesClientRect.top + @presenter.scrollTop left = clientX - linesClientRect.left + @presenter.scrollLeft - {top, left} + bottom = linesClientRect.top + @presenter.scrollTop + linesClientRect.height - clientY + right = linesClientRect.left + @presenter.scrollLeft + linesClientRect.width - clientX + + {top, left, bottom, right} getModel: -> @editor diff --git a/src/text-editor.coffee b/src/text-editor.coffee index b8c2a76fb..e2e50f5fb 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1943,10 +1943,10 @@ class TextEditor extends Model # This method may merge selections that end up intesecting. # # * `position` An instance of {Point}, with a given `row` and `column`. - selectToScreenPosition: (position, suppressMerge) -> + selectToScreenPosition: (position, options) -> lastSelection = @getLastSelection() - lastSelection.selectToScreenPosition(position) - unless suppressMerge + lastSelection.selectToScreenPosition(position, options) + unless options?.suppressSelectionMerge @mergeIntersectingSelections(reversed: lastSelection.isReversed()) # Essential: Move the cursor of each selection one character upward while