From 35753c3a8de275b8b97ae5dda0f5be4a713cbc30 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 9 Mar 2017 20:41:42 -0700 Subject: [PATCH] Add specs for single-, triple-, and cmd-clicking --- spec/text-editor-component-spec.js | 126 +++++++++++++++++++++++++++-- src/text-editor-component.js | 20 ++++- src/text-editor.coffee | 12 ++- 3 files changed, 148 insertions(+), 10 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 1c2286c9e..f39e595ca 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -646,9 +646,7 @@ describe('TextEditorComponent', () => { it('selects words on double-click', () => { const {component, editor} = buildComponent() - const clientX = clientLeftForCharacter(component, 1, 16) - const clientY = clientTopForLine(component, 1) - + const {clientX, clientY} = clientPositionForCharacter(component, 1, 16) component.didMouseDownOnLines({detail: 1, clientX, clientY}) component.didMouseDownOnLines({detail: 2, clientX, clientY}) expect(editor.getSelectedScreenRange()).toEqual([[1, 13], [1, 21]]) @@ -656,13 +654,122 @@ describe('TextEditorComponent', () => { it('selects lines on triple-click', () => { const {component, editor} = buildComponent() - const clientX = clientLeftForCharacter(component, 1, 16) - const clientY = clientTopForLine(component, 1) - + const {clientX, clientY} = clientPositionForCharacter(component, 1, 16) component.didMouseDownOnLines({detail: 1, clientX, clientY}) component.didMouseDownOnLines({detail: 2, clientX, clientY}) + component.didMouseDownOnLines({detail: 3, clientX, clientY}) expect(editor.getSelectedScreenRange()).toEqual([[1, 0], [2, 0]]) }) + + it('adds or removes cursors when holding cmd or ctrl when single-clicking', () => { + const {component, editor} = buildComponent() + spyOn(component, 'getPlatform').andCallFake(() => mockedPlatform) + + let mockedPlatform = 'darwin' + expect(editor.getCursorScreenPositions()).toEqual([[0, 0]]) + + // add cursor at 1, 16 + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 1, 16), { + detail: 1, + metaKey: true + }) + ) + expect(editor.getCursorScreenPositions()).toEqual([[0, 0], [1, 16]]) + + // remove cursor at 0, 0 + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 0, 0), { + detail: 1, + metaKey: true + }) + ) + expect(editor.getCursorScreenPositions()).toEqual([[1, 16]]) + + // cmd-click cursor at 1, 16 but don't remove it because it's the last one + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 1, 16), { + detail: 1, + metaKey: true + }) + ) + expect(editor.getCursorScreenPositions()).toEqual([[1, 16]]) + + // cmd-clicking within a selection destroys it + editor.addSelectionForScreenRange([[2, 10], [2, 15]]) + expect(editor.getSelectedScreenRanges()).toEqual([ + [[1, 16], [1, 16]], + [[2, 10], [2, 15]] + ]) + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 2, 13), { + detail: 1, + metaKey: true + }) + ) + expect(editor.getSelectedScreenRanges()).toEqual([ + [[1, 16], [1, 16]] + ]) + + // ctrl-click does not add cursors on macOS + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 1, 4), { + detail: 1, + ctrlKey: true + }) + ) + expect(editor.getCursorScreenPositions()).toEqual([[1, 4]]) + + mockedPlatform = 'win32' + + // ctrl-click adds cursors on platforms *other* than macOS + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 1, 16), { + detail: 1, + ctrlKey: true + }) + ) + expect(editor.getCursorScreenPositions()).toEqual([[1, 4], [1, 16]]) + }) + + it('adds word selections when holding cmd or ctrl when double-clicking', () => { + const {component, editor} = buildComponent() + editor.addCursorAtScreenPosition([1, 16]) + expect(editor.getCursorScreenPositions()).toEqual([[0, 0], [1, 16]]) + + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 1, 16), { + detail: 1, + metaKey: true + }) + ) + component.didMouseDownOnLines( + Object.assign(clientPositionForCharacter(component, 1, 16), { + detail: 2, + metaKey: true + }) + ) + expect(editor.getSelectedScreenRanges()).toEqual([ + [[0, 0], [0, 0]], + [[1, 13], [1, 21]] + ]) + }) + + it('adds line selections when holding cmd or ctrl when triple-clicking', () => { + const {component, editor} = buildComponent() + editor.addCursorAtScreenPosition([1, 16]) + expect(editor.getCursorScreenPositions()).toEqual([[0, 0], [1, 16]]) + + const {clientX, clientY} = clientPositionForCharacter(component, 1, 16) + component.didMouseDownOnLines({detail: 1, metaKey: true, clientX, clientY}) + component.didMouseDownOnLines({detail: 2, metaKey: true, clientX, clientY}) + component.didMouseDownOnLines({detail: 3, metaKey: true, clientX, clientY}) + + expect(editor.getSelectedScreenRanges()).toEqual([ + [[0, 0], [0, 0]], + [[1, 0], [2, 0]] + ]) + }) }) }) @@ -726,6 +833,13 @@ function clientLeftForCharacter (component, row, column) { } } +function clientPositionForCharacter (component, row, column) { + return { + clientX: clientLeftForCharacter(component, row, column), + clientY: clientTopForLine(component, row) + } +} + function lineNumberNodeForScreenRow (component, row) { const gutterElement = component.refs.lineNumberGutter.element const tileStartRow = component.tileStartRowForRow(row) diff --git a/src/text-editor-component.js b/src/text-editor-component.js index be3857b00..e8ea3c64c 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -393,6 +393,11 @@ class TextEditorComponent { }) } + // This is easier to mock + getPlatform () { + return process.platform + } + queryDecorationsToRender () { this.decorationsToRender.lineNumbers.clear() this.decorationsToRender.lines.clear() @@ -739,14 +744,27 @@ class TextEditorComponent { const {model} = this.props const screenPosition = this.screenPositionForMouseEvent(event) + const addOrRemoveSelection = event.metaKey || (event.ctrlKey && this.getPlatform() !== 'darwin') + switch (event.detail) { case 1: - model.setCursorScreenPosition(screenPosition) + if (addOrRemoveSelection) { + const existingSelection = model.getSelectionAtScreenPosition(screenPosition) + if (existingSelection) { + if (model.hasMultipleCursors()) existingSelection.destroy() + } else { + model.addCursorAtScreenPosition(screenPosition) + } + } else { + model.setCursorScreenPosition(screenPosition) + } break case 2: + if (addOrRemoveSelection) model.addCursorAtScreenPosition(screenPosition) model.getLastSelection().selectWord({autoscroll: false}) break case 3: + if (addOrRemoveSelection) model.addCursorAtScreenPosition(screenPosition) model.getLastSelection().selectLine(null, {autoscroll: false}) break } diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 4b1a510e2..c54ad0138 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2096,9 +2096,9 @@ class TextEditor extends Model # # Returns the first matched {Cursor} or undefined getCursorAtScreenPosition: (position) -> - for cursor in @cursors - return cursor if cursor.getScreenPosition().isEqual(position) - undefined + if selection = @getSelectionAtScreenPosition(position) + if selection.getHeadScreenPosition().isEqual(position) + selection.cursor # Essential: Get the position of the most recently added cursor in screen # coordinates. @@ -2647,6 +2647,12 @@ class TextEditor extends Model @createLastSelectionIfNeeded() _.last(@selections) + getSelectionAtScreenPosition: (position) -> + debugger if global.debug + markers = @selectionsMarkerLayer.findMarkers(containsScreenPosition: position) + if markers.length > 0 + @cursorsByMarkerId.get(markers[0].id).selection + # Extended: Get current {Selection}s. # # Returns: An {Array} of {Selection}s.