From cf7fb7763a5dabea7356d9bee7d122753db61dfc Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 23 Aug 2019 17:28:55 +0200 Subject: [PATCH 1/3] introduce cursor word accessibilty commands --- .../common/controller/cursorWordOperations.ts | 34 ++++++- .../test/wordOperations.test.ts | 42 ++++++++- .../contrib/wordOperations/wordOperations.ts | 91 +++++++++++++++++++ 3 files changed, 164 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 1939996a24279..b411d54bb4909 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -39,7 +39,8 @@ const enum WordType { export const enum WordNavigationType { WordStart = 0, WordStartFast = 1, - WordEnd = 2 + WordEnd = 2, + WordAcessibility = 3 // Respect chrome defintion of a word } export class WordOperations { @@ -202,6 +203,19 @@ export class WordOperations { return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1); } + if (wordNavigationType === WordNavigationType.WordAcessibility) { + while ( + prevWordOnLine + && prevWordOnLine.wordType === WordType.Separator + && prevWordOnLine.end - prevWordOnLine.start === 1 + ) { + // Skip over a word made up of one single separator + prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1)); + } + + return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1); + } + // We are stopping at the ending of words if (prevWordOnLine && column <= prevWordOnLine.end + 1) { @@ -275,6 +289,22 @@ export class WordOperations { } else { column = model.getLineMaxColumn(lineNumber); } + } else if (wordNavigationType === WordNavigationType.WordAcessibility) { + + while ( + nextWordOnLine + && nextWordOnLine.wordType === WordType.Separator + && nextWordOnLine.end - nextWordOnLine.start === 1 + ) { + // Skip over a word made up of one single separator + nextWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); + } + + if (nextWordOnLine) { + column = nextWordOnLine.start + 1; + } else { + column = model.getLineMaxColumn(lineNumber); + } } else { if (nextWordOnLine && !movedDown && column >= nextWordOnLine.start + 1) { nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); @@ -617,4 +647,4 @@ export class WordPartOperations extends WordOperations { function enforceDefined(arr: Array): T[] { return arr.filter(el => Boolean(el)); -} \ No newline at end of file +} diff --git a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts index 98c22c721efa6..c647e151cfe59 100644 --- a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts +++ b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts @@ -9,7 +9,7 @@ import { EditorCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; -import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight } from 'vs/editor/contrib/wordOperations/wordOperations'; +import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect } from 'vs/editor/contrib/wordOperations/wordOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; suite('WordOperations', () => { @@ -26,6 +26,10 @@ suite('WordOperations', () => { const _cursorWordStartRightSelect = new CursorWordStartRightSelect(); const _cursorWordEndRightSelect = new CursorWordEndRightSelect(); const _cursorWordRightSelect = new CursorWordRightSelect(); + const _cursorWordAccessibilityLeft = new CursorWordAccessibilityLeft(); + const _cursorWordAccessibilityLeftSelect = new CursorWordAccessibilityLeftSelect(); + const _cursorWordAccessibilityRight = new CursorWordAccessibilityRight(); + const _cursorWordAccessibilityRightSelect = new CursorWordAccessibilityRightSelect(); const _deleteWordLeft = new DeleteWordLeft(); const _deleteWordStartLeft = new DeleteWordStartLeft(); const _deleteWordEndLeft = new DeleteWordEndLeft(); @@ -39,6 +43,12 @@ suite('WordOperations', () => { function cursorWordLeft(editor: ICodeEditor, inSelectionMode: boolean = false): void { runEditorCommand(editor, inSelectionMode ? _cursorWordLeftSelect : _cursorWordLeft); } + function cursorWordAccessibilityLeft(editor: ICodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordAccessibilityLeft : _cursorWordAccessibilityLeftSelect); + } + function cursorWordAccessibilityRight(editor: ICodeEditor, inSelectionMode: boolean = false): void { + runEditorCommand(editor, inSelectionMode ? _cursorWordAccessibilityRightSelect : _cursorWordAccessibilityRight); + } function cursorWordStartLeft(editor: ICodeEditor, inSelectionMode: boolean = false): void { runEditorCommand(editor, inSelectionMode ? _cursorWordStartLeftSelect : _cursorWordStartLeft); } @@ -326,6 +336,36 @@ suite('WordOperations', () => { assert.deepEqual(actual, EXPECTED); }); + test('cursorWordAccessibilityLeft', () => { + const EXPECTED = ['| |/* |Just |some |more |text |a|+= |3 |+|5|-|3 |+ |7 |*/| '].join('\n'); + const [text,] = deserializePipePositions(EXPECTED); + const actualStops = testRepeatedActionAndExtractPositions( + text, + new Position(1000, 1000), + ed => cursorWordAccessibilityLeft(ed), + ed => ed.getPosition()!, + ed => ed.getPosition()!.equals(new Position(1, 1)) + ); + const actual = serializePipePositions(text, actualStops); + assert.deepEqual(actual, EXPECTED); + }); + + test('cursorWordAccessibilityRight', () => { + const EXPECTED = [ + 'console|.log|(err|)|', + ].join('\n'); + const [text,] = deserializePipePositions(EXPECTED); + const actualStops = testRepeatedActionAndExtractPositions( + text, + new Position(1, 1), + ed => cursorWordAccessibilityRight(ed), + ed => ed.getPosition()!, + ed => ed.getPosition()!.equals(new Position(1, 17)) + ); + const actual = serializePipePositions(text, actualStops); + assert.deepEqual(actual, EXPECTED); + }); + test('deleteWordLeft for non-empty selection', () => { withTestCodeEditor([ ' \tMy First Line\t ', diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index dc4e111f96261..bd305ba7103b7 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -18,6 +18,8 @@ import { ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export interface MoveWordOptions extends ICommandOptions { inSelectionMode: boolean; @@ -170,6 +172,49 @@ export class CursorWordLeftSelect extends WordLeftCommand { } } +const CHROME_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;",.<>/?'; +export class CursorWordAccessibilityLeft extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordAcessibility, + id: 'cursorWordAccessibilityLeft', + precondition: undefined, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), + primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, + mac: { primary: KeyMod.Alt | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib + 1 + } + }); + } + + protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + } +} + +export class CursorWordAccessibilityLeftSelect extends WordLeftCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordAcessibility, + id: 'cursorWordAccessibilitLeftSelecty', + precondition: undefined, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib + 1 + } + }); + } + + protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + } +} + export class CursorWordStartRight extends WordRightCommand { constructor() { super({ @@ -248,6 +293,48 @@ export class CursorWordRightSelect extends WordRightCommand { } } +export class CursorWordAccessibilityRight extends WordRightCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordAcessibility, + id: 'cursorWordAccessibilityRight', + precondition: undefined, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), + primary: KeyMod.CtrlCmd | KeyCode.RightArrow, + mac: { primary: KeyMod.Alt | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib + 1 + } + }); + } + + protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + } +} + +export class CursorWordAccessibilityRightSelect extends WordRightCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordAcessibility, + id: 'cursorWordAccessibilityRightSelect', + precondition: undefined, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow, + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib + 1 + } + }); + } + + protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + } +} + export interface DeleteWordOptions extends ICommandOptions { whitespaceHeuristics: boolean; wordNavigationType: WordNavigationType; @@ -397,6 +484,10 @@ registerEditorCommand(new CursorWordRight()); registerEditorCommand(new CursorWordStartRightSelect()); registerEditorCommand(new CursorWordEndRightSelect()); registerEditorCommand(new CursorWordRightSelect()); +registerEditorCommand(new CursorWordAccessibilityLeft()); +registerEditorCommand(new CursorWordAccessibilityLeftSelect()); +registerEditorCommand(new CursorWordAccessibilityRight()); +registerEditorCommand(new CursorWordAccessibilityRightSelect()); registerEditorCommand(new DeleteWordStartLeft()); registerEditorCommand(new DeleteWordEndLeft()); registerEditorCommand(new DeleteWordLeft()); From 96af1d44cef908813601c605445f467b0e19ecaf Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 26 Aug 2019 16:48:19 +0200 Subject: [PATCH 2/3] fix accessibility word operatoins and tests --- .../common/controller/cursorWordOperations.ts | 14 ++++++-------- .../wordOperations/test/wordOperations.test.ts | 8 +++----- .../contrib/wordOperations/wordOperations.ts | 8 ++++---- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index b411d54bb4909..300174e613aa8 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -40,7 +40,7 @@ export const enum WordNavigationType { WordStart = 0, WordStartFast = 1, WordEnd = 2, - WordAcessibility = 3 // Respect chrome defintion of a word + WordAccessibility = 3 // Respect chrome defintion of a word } export class WordOperations { @@ -203,13 +203,12 @@ export class WordOperations { return new Position(lineNumber, prevWordOnLine ? prevWordOnLine.start + 1 : 1); } - if (wordNavigationType === WordNavigationType.WordAcessibility) { + if (wordNavigationType === WordNavigationType.WordAccessibility) { while ( prevWordOnLine && prevWordOnLine.wordType === WordType.Separator - && prevWordOnLine.end - prevWordOnLine.start === 1 ) { - // Skip over a word made up of one single separator + // Skip over words made up of only separators prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, prevWordOnLine.start + 1)); } @@ -289,19 +288,18 @@ export class WordOperations { } else { column = model.getLineMaxColumn(lineNumber); } - } else if (wordNavigationType === WordNavigationType.WordAcessibility) { + } else if (wordNavigationType === WordNavigationType.WordAccessibility) { while ( nextWordOnLine && nextWordOnLine.wordType === WordType.Separator - && nextWordOnLine.end - nextWordOnLine.start === 1 ) { // Skip over a word made up of one single separator - nextWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); + nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); } if (nextWordOnLine) { - column = nextWordOnLine.start + 1; + column = nextWordOnLine.end + 1; } else { column = model.getLineMaxColumn(lineNumber); } diff --git a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts index c647e151cfe59..baacaf2b44588 100644 --- a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts +++ b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts @@ -337,7 +337,7 @@ suite('WordOperations', () => { }); test('cursorWordAccessibilityLeft', () => { - const EXPECTED = ['| |/* |Just |some |more |text |a|+= |3 |+|5|-|3 |+ |7 |*/| '].join('\n'); + const EXPECTED = ['| /* |Just |some |more |text |a+= |3 +|5-|3 + |7 */ '].join('\n'); const [text,] = deserializePipePositions(EXPECTED); const actualStops = testRepeatedActionAndExtractPositions( text, @@ -351,16 +351,14 @@ suite('WordOperations', () => { }); test('cursorWordAccessibilityRight', () => { - const EXPECTED = [ - 'console|.log|(err|)|', - ].join('\n'); + const EXPECTED = [' /* Just| some| more| text| a|+= 3| +5|-3| + 7| */ |'].join('\n'); const [text,] = deserializePipePositions(EXPECTED); const actualStops = testRepeatedActionAndExtractPositions( text, new Position(1, 1), ed => cursorWordAccessibilityRight(ed), ed => ed.getPosition()!, - ed => ed.getPosition()!.equals(new Position(1, 17)) + ed => ed.getPosition()!.equals(new Position(1, 50)) ); const actual = serializePipePositions(text, actualStops); assert.deepEqual(actual, EXPECTED); diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index bd305ba7103b7..3f330238e9ea6 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -177,7 +177,7 @@ export class CursorWordAccessibilityLeft extends WordLeftCommand { constructor() { super({ inSelectionMode: false, - wordNavigationType: WordNavigationType.WordAcessibility, + wordNavigationType: WordNavigationType.WordAccessibility, id: 'cursorWordAccessibilityLeft', precondition: undefined, kbOpts: { @@ -198,7 +198,7 @@ export class CursorWordAccessibilityLeftSelect extends WordLeftCommand { constructor() { super({ inSelectionMode: true, - wordNavigationType: WordNavigationType.WordAcessibility, + wordNavigationType: WordNavigationType.WordAccessibility, id: 'cursorWordAccessibilitLeftSelecty', precondition: undefined, kbOpts: { @@ -297,7 +297,7 @@ export class CursorWordAccessibilityRight extends WordRightCommand { constructor() { super({ inSelectionMode: false, - wordNavigationType: WordNavigationType.WordAcessibility, + wordNavigationType: WordNavigationType.WordAccessibility, id: 'cursorWordAccessibilityRight', precondition: undefined, kbOpts: { @@ -318,7 +318,7 @@ export class CursorWordAccessibilityRightSelect extends WordRightCommand { constructor() { super({ inSelectionMode: true, - wordNavigationType: WordNavigationType.WordAcessibility, + wordNavigationType: WordNavigationType.WordAccessibility, id: 'cursorWordAccessibilityRightSelect', precondition: undefined, kbOpts: { From c5c750332c1efe476a9c0ef67283ad39f7e99dab Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 26 Aug 2019 23:34:23 +0200 Subject: [PATCH 3/3] no need to list chrome separtors, default editor ones are just fine --- src/vs/editor/contrib/wordOperations/wordOperations.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index 3f330238e9ea6..e104f52a534d5 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -20,6 +20,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions'; export interface MoveWordOptions extends ICommandOptions { inSelectionMode: boolean; @@ -172,7 +173,6 @@ export class CursorWordLeftSelect extends WordLeftCommand { } } -const CHROME_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;",.<>/?'; export class CursorWordAccessibilityLeft extends WordLeftCommand { constructor() { super({ @@ -190,7 +190,7 @@ export class CursorWordAccessibilityLeft extends WordLeftCommand { } protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + return super._move(getMapForWordSeparators(EDITOR_DEFAULTS.wordSeparators), model, position, wordNavigationType); } } @@ -211,7 +211,7 @@ export class CursorWordAccessibilityLeftSelect extends WordLeftCommand { } protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + return super._move(getMapForWordSeparators(EDITOR_DEFAULTS.wordSeparators), model, position, wordNavigationType); } } @@ -310,7 +310,7 @@ export class CursorWordAccessibilityRight extends WordRightCommand { } protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + return super._move(getMapForWordSeparators(EDITOR_DEFAULTS.wordSeparators), model, position, wordNavigationType); } } @@ -331,7 +331,7 @@ export class CursorWordAccessibilityRightSelect extends WordRightCommand { } protected _move(_: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return super._move(getMapForWordSeparators(CHROME_SEPARATORS), model, position, wordNavigationType); + return super._move(getMapForWordSeparators(EDITOR_DEFAULTS.wordSeparators), model, position, wordNavigationType); } }