From c9fde20123c8c505727c2bfc35b5784ba9243f63 Mon Sep 17 00:00:00 2001 From: fetiew <feitew@umich.edu> Date: Mon, 16 Apr 2018 23:51:40 -0400 Subject: [PATCH 1/5] Implement word part move and delete --- src/vs/base/common/strings.ts | 24 ++ .../common/controller/cursorWordOperations.ts | 128 ++++++++++- .../test/wordPartOperations.test.ts | 207 ++++++++++++++++++ .../wordPartOperations/wordPartOperations.ts | 113 ++++++++++ src/vs/editor/editor.all.ts | 1 + 5 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts create mode 100644 src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index deeb3ae1f999d..f78b437eb5355 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -323,6 +323,30 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len return -1; } +export function lastWordPartEnd(str: string, startIndex: number = str.length-1): number{ + for (let i = startIndex; i >= 0; i--) { + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { + return i-1; + } + } + return -1; +} + +export function nextWordPartBegin(str: string, startIndex: number = str.length-1): number{ + const checkLowerCase = str.charCodeAt(startIndex-1) === CharCode.Space; // does a lc char count as a part start? + for (let i = startIndex; i < str.length; ++i){ + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || (checkLowerCase && isLowerAsciiLetter(chCode))) { + return i+1; + } + if (chCode === CharCode.Underline){ + return i+2; + } + } + return -1; +} + export function compare(a: string, b: string): number { if (a < b) { return -1; diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 24bbcac19c3d9..a0410aaf74c28 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -229,7 +229,7 @@ export class WordOperations { return new Position(lineNumber, column); } - private static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range { + protected static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range { const lineContent = model.getLineContent(position.lineNumber); const startIndex = position.column - 2; const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex); @@ -304,7 +304,7 @@ export class WordOperations { return len; } - private static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range { + protected static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range { const lineContent = model.getLineContent(position.lineNumber); const startIndex = position.column - 1; const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex); @@ -454,3 +454,127 @@ export class WordOperations { return cursor.move(true, lineNumber, column, 0); } } + +export class WordPartOperations extends WordOperations { + public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let lineNumber = position.lineNumber; + let column = position.column; + + if (lineNumber === 1 && column === 1) { + // Ignore deleting at beginning of file + return null; + } + + if (whitespaceHeuristics) { + let r = WordOperations._deleteWordLeftWhitespace(model, position); + if (r) { + return r; + } + } + + const lineContent = model.getLineContent(position.lineNumber); + const startIndex = position.column - 2; + const lastWordPartEnd = strings.lastWordPartEnd(lineContent, startIndex); + const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + + if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)){ + return wordRange; + } + else { + const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd+2); + return range; + } + + } + + public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let lineNumber = position.lineNumber; + let column = position.column; + + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineNumber); + if (lineNumber === lineCount && column === maxColumn) { + // Ignore deleting at end of file + return null; + } + + if (whitespaceHeuristics) { + let r = WordOperations._deleteWordRightWhitespace(model, position); + if (r) { + return r; + } + } + + const lineContent = model.getLineContent(position.lineNumber); + const startIndex = position.column; + const nextWordPartBegin = strings.nextWordPartBegin(lineContent, startIndex); + const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + + if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)){ + return wordRange; + } + else { + return new Range(lineNumber, column, lineNumber, nextWordPartBegin); + } + } + + public static moveWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { + const startIndex = position.column - 2; + const lineNumber = position.lineNumber; + const column = position.column; + const wordPartCol = strings.lastWordPartEnd(model.getLineContent(lineNumber), startIndex); + const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); + + if (column === 1) { + if (lineNumber > 1) { + return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber-1)); + } + return null; + } + + if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)){ + return wordPos; + } + else{ + return new Position(lineNumber, wordPartCol+2); + } + } + + public static moveWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { + const startIndex = position.column; + const lineNumber = position.lineNumber; + const column = position.column; + const wordPartCol = strings.nextWordPartBegin(model.getLineContent(lineNumber), startIndex); + const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); + + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineNumber); + if (column === maxColumn) { + if (lineNumber < lineCount) { + return new Position(lineNumber + 1, 1); + } + return null; + } + + + if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)){ + return wordPos; + } + else { + return new Position(lineNumber, wordPartCol); + } + } + +} diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts new file mode 100644 index 0000000000000..ac44bfe79bb06 --- /dev/null +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { + DeleteWordPartLeft, DeleteWordPartRight, + CursorWordPartLeft, CursorWordPartRight +} from 'vs/editor/contrib/wordPartOperations/wordPartOperations'; +import { EditorCommand } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; + +suite('WordPartOperations', () => { + const _deleteWordPartLeft = new DeleteWordPartLeft(); + const _deleteWordPartRight = new DeleteWordPartRight(); + const _cursorWordPartLeft = new CursorWordPartLeft(); + const _cursorWordPartRight = new CursorWordPartRight(); + + function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { + command.runEditorCommand(null, editor, null); + } + function moveWordPartLeft(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartLeft); + } + function moveWordPartRight(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartRight); + } + function deleteWordPartLeft(editor: ICodeEditor): void{ + runEditorCommand(editor, _deleteWordPartLeft); + } + function deleteWordPartRight(editor: ICodeEditor): void{ + runEditorCommand(editor, _deleteWordPartRight); + } + + test('move word part left basic', () => { + withTestCodeEditor([ + 'start line', + 'thisIsACamelCaseVar this_is_a_snake_case_var', + 'end line' + ], {}, (editor, _) => { + editor.setPosition(new Position(3, 8)); + const expectedStops = [ + [3, 5], + [3, 4], + [3, 1], + [2, 46], + [2, 42], + [2, 37], + [2, 31], + [2, 29], + [2, 26], + [2, 22], + [2, 21], + [2, 20], + [2, 17], + [2, 13], + [2, 8], + [2, 7], + [2, 5], + [2, 1], + [1, 11], + [1, 7], + [1, 6], + [1, 1] + ]; + + let actualStops: number[][] = []; + for (let i = 0; i < expectedStops.length; i++) { + moveWordPartLeft(editor); + const pos = editor.getPosition(); + actualStops.push([pos.lineNumber, pos.column]); + } + + assert.deepEqual(actualStops, expectedStops); + }); + }); + + test('move word part right basic', () => { + withTestCodeEditor([ + 'start line', + 'thisIsACamelCaseVar this_is_a_snake_case_var', + 'end line' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + const expectedStops = [ + [1, 6], + [1, 7], + [1, 11], + [2, 1], + [2, 5], + [2, 7], + [2, 8], + [2, 13], + [2, 17], + [2, 20], + [2, 21], + [2, 22], + [2, 27], + [2, 30], + [2, 32], + [2, 38], + [2, 43], + [2, 46], + [3, 1], + [3, 4], + [3, 5], + [3, 9] + ]; + + let actualStops: number[][] = []; + for (let i = 0; i < expectedStops.length; i++) { + moveWordPartRight(editor); + const pos = editor.getPosition(); + actualStops.push([pos.lineNumber, pos.column]); + } + + assert.deepEqual(actualStops, expectedStops); + }); + }); + + test('delete word part left basic', () => { + withTestCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 84)); + + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case', '001'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake', '002'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a', '003'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is', '004'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this', '005'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar ', '006'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar', '007'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCase', '008'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamel', '009'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsA', '010'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIs', '011'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ this', '012'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ ', '013'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */', '014'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 ', '015'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-', '016'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '017'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '018'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '019'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '020'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '021'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '022'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text ', '023'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text', '024'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some ', '025'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some', '026'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just ', '027'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just', '028'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* ', '029'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /*', '030'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' ', '031'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), '', '032'); + }); + }); + + test('delete word part right basic', () => { + withTestCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 1)); + + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '001'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '002'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '003'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '004'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '005'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '006'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '007'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '008'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '009'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '010'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '011'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '012'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '013'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '014'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '015'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' */ thisIsACamelCaseVar this_is_a_snake_case_var', '016'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' thisIsACamelCaseVar this_is_a_snake_case_var', '017'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'thisIsACamelCaseVar this_is_a_snake_case_var', '018'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'IsACamelCaseVar this_is_a_snake_case_var', '019'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'ACamelCaseVar this_is_a_snake_case_var', '020'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CamelCaseVar this_is_a_snake_case_var', '021'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CaseVar this_is_a_snake_case_var', '022'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Var this_is_a_snake_case_var', '023'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' this_is_a_snake_case_var', '024'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'this_is_a_snake_case_var', '025'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'is_a_snake_case_var', '026'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a_snake_case_var', '027'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'snake_case_var', '028'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'case_var', '029'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'var', '030'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '', '031'); + }); + }); +}); diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts new file mode 100644 index 0000000000000..781d1ed588e8b --- /dev/null +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ITextModel } from 'vs/editor/common/model'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Selection } from 'vs/editor/common/core/selection'; +import { registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { Range } from 'vs/editor/common/core/range'; +import { WordNavigationType, WordPartOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { DeleteWordCommand, MoveWordCommand } from '../wordOperations/wordOperations'; +import { Position } from 'vs/editor/common/core/position'; + +export class DeleteWordPartLeft extends DeleteWordCommand { + constructor() { + super({ + whitespaceHeuristics: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'deleteWordPartLeft', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.Backspace, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } + } + }); + } + + protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + let r = WordPartOperations.deleteWordPartLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + if (r) { + return r; + } + return new Range(1, 1, 1, 1); + } +} + +export class DeleteWordPartRight extends DeleteWordCommand { + constructor() { + super({ + whitespaceHeuristics: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'deleteWordPartRight', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.Delete, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Delete } + } + }); + } + + protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + let r = WordPartOperations.deleteWordPartRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + if (r) { + return r; + } + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineCount); + return new Range(lineCount, maxColumn, lineCount, maxColumn); + } +} + +export class CursorWordPartLeft extends MoveWordCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordPartStartLeft', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.LeftArrow, + mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } + } + }); + } + + protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordPartOperations.moveWordPartLeft(wordSeparators, model, position, wordNavigationType); + } +} + +export class CursorWordPartRight extends MoveWordCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordPartRight', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.RightArrow, + mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } + } + }); + } + + protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordPartOperations.moveWordPartRight(wordSeparators, model, position, wordNavigationType); + } +} + + +registerEditorCommand(new DeleteWordPartLeft()); +registerEditorCommand(new DeleteWordPartRight()); +registerEditorCommand(new CursorWordPartLeft()); +registerEditorCommand(new CursorWordPartRight()); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 07cbf332285eb..edcf0f3b6d64c 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -41,3 +41,4 @@ import 'vs/editor/contrib/suggest/suggestController'; import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; +import 'vs/editor/contrib/wordPartOperations/wordPartOperations'; From 7415670044d2a0128c6d232a19192b2edcd3a422 Mon Sep 17 00:00:00 2001 From: fetiew <feitew@umich.edu> Date: Tue, 17 Apr 2018 17:12:50 -0400 Subject: [PATCH 2/5] Fixed formatting issues --- src/vs/base/common/strings.ts | 16 ++++++++-------- .../common/controller/cursorWordOperations.ts | 16 ++++++++-------- .../test/wordPartOperations.test.ts | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index f78b437eb5355..d423e60460932 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -323,25 +323,25 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len return -1; } -export function lastWordPartEnd(str: string, startIndex: number = str.length-1): number{ +export function lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { for (let i = startIndex; i >= 0; i--) { let chCode = str.charCodeAt(i); if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { - return i-1; + return i - 1; } } return -1; } -export function nextWordPartBegin(str: string, startIndex: number = str.length-1): number{ - const checkLowerCase = str.charCodeAt(startIndex-1) === CharCode.Space; // does a lc char count as a part start? - for (let i = startIndex; i < str.length; ++i){ +export function nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { + const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? + for (let i = startIndex; i < str.length; ++i) { let chCode = str.charCodeAt(i); if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || (checkLowerCase && isLowerAsciiLetter(chCode))) { - return i+1; + return i + 1; } - if (chCode === CharCode.Underline){ - return i+2; + if (chCode === CharCode.Underline) { + return i + 2; } } return -1; diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index a0410aaf74c28..1f620a7637873 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -483,11 +483,11 @@ export class WordPartOperations extends WordOperations { const lastWordPartEnd = strings.lastWordPartEnd(lineContent, startIndex); const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); - if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)){ + if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)) { return wordRange; } else { - const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd+2); + const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2); return range; } @@ -522,7 +522,7 @@ export class WordPartOperations extends WordOperations { const nextWordPartBegin = strings.nextWordPartBegin(lineContent, startIndex); const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); - if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)){ + if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)) { return wordRange; } else { @@ -539,16 +539,16 @@ export class WordPartOperations extends WordOperations { if (column === 1) { if (lineNumber > 1) { - return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber-1)); + return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); } return null; } - if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)){ + if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)) { return wordPos; } - else{ - return new Position(lineNumber, wordPartCol+2); + else { + return new Position(lineNumber, wordPartCol + 2); } } @@ -569,7 +569,7 @@ export class WordPartOperations extends WordOperations { } - if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)){ + if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)) { return wordPos; } else { diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts index ac44bfe79bb06..e5fa29f253536 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -23,16 +23,16 @@ suite('WordPartOperations', () => { function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { command.runEditorCommand(null, editor, null); } - function moveWordPartLeft(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + function moveWordPartLeft(editor: ICodeEditor, inSelectionmode: boolean = false): void { runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartLeft); } - function moveWordPartRight(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + function moveWordPartRight(editor: ICodeEditor, inSelectionmode: boolean = false): void { runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartRight); } - function deleteWordPartLeft(editor: ICodeEditor): void{ + function deleteWordPartLeft(editor: ICodeEditor): void { runEditorCommand(editor, _deleteWordPartLeft); } - function deleteWordPartRight(editor: ICodeEditor): void{ + function deleteWordPartRight(editor: ICodeEditor): void { runEditorCommand(editor, _deleteWordPartRight); } From f41ee9c0f6a0fdd9e94e385af02d2e9e4d5fb51a Mon Sep 17 00:00:00 2001 From: Alex Dima <alexdima@microsoft.com> Date: Wed, 20 Jun 2018 17:03:31 +0200 Subject: [PATCH 3/5] Stylistic edits --- src/vs/base/common/strings.ts | 28 +----- .../common/controller/cursorWordOperations.ts | 98 ++++++++++--------- .../test/wordPartOperations.test.ts | 2 + 3 files changed, 54 insertions(+), 74 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index c8072e04a3e27..31dff819977ba 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -280,30 +280,6 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len return -1; } -export function lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { - for (let i = startIndex; i >= 0; i--) { - let chCode = str.charCodeAt(i); - if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { - return i - 1; - } - } - return -1; -} - -export function nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { - const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? - for (let i = startIndex; i < str.length; ++i) { - let chCode = str.charCodeAt(i); - if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || (checkLowerCase && isLowerAsciiLetter(chCode))) { - return i + 1; - } - if (chCode === CharCode.Underline) { - return i + 2; - } - } - return -1; -} - export function compare(a: string, b: string): number { if (a < b) { return -1; @@ -357,11 +333,11 @@ export function compareIgnoreCase(a: string, b: string): number { } } -function isLowerAsciiLetter(code: number): boolean { +export function isLowerAsciiLetter(code: number): boolean { return code >= CharCode.a && code <= CharCode.z; } -function isUpperAsciiLetter(code: number): boolean { +export function isUpperAsciiLetter(code: number): boolean { return code >= CharCode.A && code <= CharCode.Z; } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 05afe78875e9b..b69d9799b5300 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -10,6 +10,7 @@ import { WordCharacterClassifier, WordCharacterClass, getMapForWordSeparators } import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { CharCode } from 'vs/base/common/charCode'; interface IFindWordResult { /** @@ -464,6 +465,30 @@ export class WordOperations { } } +export function _lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { + for (let i = startIndex; i >= 0; i--) { + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || strings.isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { + return i - 1; + } + } + return -1; +} + +export function _nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { + const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? + for (let i = startIndex; i < str.length; ++i) { + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || strings.isUpperAsciiLetter(chCode) || (checkLowerCase && strings.isLowerAsciiLetter(chCode))) { + return i + 1; + } + if (chCode === CharCode.Underline) { + return i + 2; + } + } + return str.length + 1; +} + export class WordPartOperations extends WordOperations { public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { if (!selection.isEmpty()) { @@ -471,9 +496,8 @@ export class WordPartOperations extends WordOperations { } const position = new Position(selection.positionLineNumber, selection.positionColumn); - - let lineNumber = position.lineNumber; - let column = position.column; + const lineNumber = position.lineNumber; + const column = position.column; if (lineNumber === 1 && column === 1) { // Ignore deleting at beginning of file @@ -487,19 +511,14 @@ export class WordPartOperations extends WordOperations { } } - const lineContent = model.getLineContent(position.lineNumber); - const startIndex = position.column - 2; - const lastWordPartEnd = strings.lastWordPartEnd(lineContent, startIndex); const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + const lastWordPartEnd = _lastWordPartEnd(model.getLineContent(position.lineNumber), position.column - 2); + const wordPartRange = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2); - if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)) { + if (wordPartRange.getStartPosition().isBeforeOrEqual(wordRange.getStartPosition())) { return wordRange; } - else { - const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2); - return range; - } - + return wordPartRange; } public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { @@ -508,9 +527,8 @@ export class WordPartOperations extends WordOperations { } const position = new Position(selection.positionLineNumber, selection.positionColumn); - - let lineNumber = position.lineNumber; - let column = position.column; + const lineNumber = position.lineNumber; + const column = position.column; const lineCount = model.getLineCount(); const maxColumn = model.getLineMaxColumn(lineNumber); @@ -526,64 +544,48 @@ export class WordPartOperations extends WordOperations { } } - const lineContent = model.getLineContent(position.lineNumber); - const startIndex = position.column; - const nextWordPartBegin = strings.nextWordPartBegin(lineContent, startIndex); const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + const nextWordPartBegin = _nextWordPartBegin(model.getLineContent(position.lineNumber), position.column); + const wordPartRange = new Range(lineNumber, column, lineNumber, nextWordPartBegin); - if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)) { + if (wordRange.getEndPosition().isBeforeOrEqual(wordPartRange.getEndPosition())) { return wordRange; } - else { - return new Range(lineNumber, column, lineNumber, nextWordPartBegin); - } + return wordPartRange; } public static moveWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { - const startIndex = position.column - 2; const lineNumber = position.lineNumber; const column = position.column; - const wordPartCol = strings.lastWordPartEnd(model.getLineContent(lineNumber), startIndex); - const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); - if (column === 1) { - if (lineNumber > 1) { - return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); - } - return null; + return (lineNumber > 1 ? new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)) : position); } - if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)) { + const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); + const lastWordPartEnd = _lastWordPartEnd(model.getLineContent(lineNumber), column - 2); + const wordPartPos = new Position(lineNumber, lastWordPartEnd + 2); + + if (wordPartPos.isBeforeOrEqual(wordPos)) { return wordPos; } - else { - return new Position(lineNumber, wordPartCol + 2); - } + return wordPartPos; } public static moveWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { - const startIndex = position.column; const lineNumber = position.lineNumber; const column = position.column; - const wordPartCol = strings.nextWordPartBegin(model.getLineContent(lineNumber), startIndex); - const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); - - const lineCount = model.getLineCount(); const maxColumn = model.getLineMaxColumn(lineNumber); if (column === maxColumn) { - if (lineNumber < lineCount) { - return new Position(lineNumber + 1, 1); - } - return null; + return (lineNumber < model.getLineCount() ? new Position(lineNumber + 1, 1) : position); } + const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); + const nextWordPartBegin = _nextWordPartBegin(model.getLineContent(lineNumber), column); + const wordPartPos = new Position(lineNumber, nextWordPartBegin); - if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)) { + if (wordPos.isBeforeOrEqual(wordPartPos)) { return wordPos; } - else { - return new Position(lineNumber, wordPartCol); - } + return wordPartPos; } - } diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts index e5fa29f253536..2074dd9cfc691 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -144,10 +144,12 @@ suite('WordPartOperations', () => { deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ ', '013'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */', '014'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 ', '015'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3', '015bis'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-', '016'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '017'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '018'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '019'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3', '019bis'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '020'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '021'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '022'); From f22e404cfe78bd1cda97b15678cbd459692d501b Mon Sep 17 00:00:00 2001 From: Alex Dima <alexdima@microsoft.com> Date: Wed, 20 Jun 2018 17:17:05 +0200 Subject: [PATCH 4/5] Add word part selection commands & tweak mac keybindings --- .../wordPartOperations/wordPartOperations.ts | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 781d1ed588e8b..8ad25d28fa860 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -26,7 +26,7 @@ export class DeleteWordPartLeft extends DeleteWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.Backspace, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace } } }); } @@ -50,7 +50,7 @@ export class DeleteWordPartRight extends DeleteWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.Delete, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Delete } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete } } }); } @@ -66,7 +66,12 @@ export class DeleteWordPartRight extends DeleteWordCommand { } } -export class CursorWordPartLeft extends MoveWordCommand { +export class WordPartLeftCommand extends MoveWordCommand { + protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordPartOperations.moveWordPartLeft(wordSeparators, model, position, wordNavigationType); + } +} +export class CursorWordPartLeft extends WordPartLeftCommand { constructor() { super({ inSelectionMode: false, @@ -76,17 +81,33 @@ export class CursorWordPartLeft extends MoveWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.LeftArrow, - mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow } } }); } +} +export class CursorWordPartLeftSelect extends WordPartLeftCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordPartStartLeftSelect', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } + } + }); + } +} +export class WordPartRightCommand extends MoveWordCommand { protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return WordPartOperations.moveWordPartLeft(wordSeparators, model, position, wordNavigationType); + return WordPartOperations.moveWordPartRight(wordSeparators, model, position, wordNavigationType); } } - -export class CursorWordPartRight extends MoveWordCommand { +export class CursorWordPartRight extends WordPartRightCommand { constructor() { super({ inSelectionMode: false, @@ -96,13 +117,24 @@ export class CursorWordPartRight extends MoveWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.RightArrow, - mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow } } }); } - - protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return WordPartOperations.moveWordPartRight(wordSeparators, model, position, wordNavigationType); +} +export class CursorWordPartRightSelect extends WordPartRightCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordPartRightSelect', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } + } + }); } } @@ -110,4 +142,6 @@ export class CursorWordPartRight extends MoveWordCommand { registerEditorCommand(new DeleteWordPartLeft()); registerEditorCommand(new DeleteWordPartRight()); registerEditorCommand(new CursorWordPartLeft()); +registerEditorCommand(new CursorWordPartLeftSelect()); registerEditorCommand(new CursorWordPartRight()); +registerEditorCommand(new CursorWordPartRightSelect()); From 66eb17581b3ade75da683c02f936b2355ab89627 Mon Sep 17 00:00:00 2001 From: Alex Dima <alexdima@microsoft.com> Date: Wed, 20 Jun 2018 17:42:32 +0200 Subject: [PATCH 5/5] Tweak keybindings on Windows --- .../contrib/wordPartOperations/wordPartOperations.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 8ad25d28fa860..28829ba7b317e 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -25,7 +25,7 @@ export class DeleteWordPartLeft extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.Backspace, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace } } }); @@ -49,7 +49,7 @@ export class DeleteWordPartRight extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.Delete, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Delete, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete } } }); @@ -80,7 +80,7 @@ export class CursorWordPartLeft extends WordPartLeftCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.LeftArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow } } }); @@ -95,7 +95,7 @@ export class CursorWordPartLeftSelect extends WordPartLeftCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } } }); @@ -116,7 +116,7 @@ export class CursorWordPartRight extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.RightArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow } } }); @@ -131,7 +131,7 @@ export class CursorWordPartRightSelect extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } } });