From 3d91fd3c482742c150f98e076d089b91f7f8567a Mon Sep 17 00:00:00 2001 From: johnfn Date: Wed, 15 Jun 2016 02:17:20 -0700 Subject: [PATCH 1/4] Begin adding star --- src/actions/actions.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index 513109a973b..f5d47e838f1 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -370,6 +370,25 @@ class CommandNextSearchMatch extends BaseMovement { } } +// TODO - bah, movements need to be more powerful. +@RegisterAction +class CommandStar extends BaseMovement { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ["*"]; + + public async execAction(position: Position, vimState: VimState): Promise { + const start = position.getWordLeft(true); + const end = position.getCurrentWordEnd(true).getRight(); + const currentWord = TextEditor.getText(new vscode.Range(start, end)); + + vimState.searchString = currentWord; + vimState.searchDirection = 1; + vimState.cursorPosition = CommandInsertInSearchMode.GetNextSearchMatch(vimState.cursorPosition, currentWord); + + return position; + } +} + @RegisterAction class CommandPreviousSearchMatch extends BaseMovement { @@ -440,7 +459,6 @@ export class CommandSearchForwards extends BaseCommand { vimState.searchCursorStartPosition = position; vimState.nextSearchMatchPosition = undefined; vimState.currentMode = ModeName.SearchInProgressMode; - // vimState.actionState.actionKeys = []; // ??? return vimState; } @@ -458,7 +476,6 @@ export class CommandSearchBackwards extends BaseCommand { vimState.searchCursorStartPosition = position; vimState.nextSearchMatchPosition = undefined; vimState.currentMode = ModeName.SearchInProgressMode; - // vimState.actionState.actionKeys = []; // ??? return vimState; } From 82a48f432a03d61cb8d5edbde2ea9e35b6bfdf4c Mon Sep 17 00:00:00 2001 From: johnfn Date: Tue, 21 Jun 2016 11:27:20 -0700 Subject: [PATCH 2/4] Add * --- src/actions/actions.ts | 25 +++++++++++++++++++------ src/mode/modeHandler.ts | 5 +---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index 70013faae52..44ebec8066c 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -67,6 +67,12 @@ export function isIMovement(o: IMovement | Position): o is IMovement { } export class BaseAction { + /** + * Can this action be paired with an operator (is it like w in dw)? All + * BaseMovements can be, and some more sophisticated commands also can be. + */ + isMotion = false; + /** * Modes that this action can be run in. */ @@ -119,6 +125,8 @@ export class BaseAction { * A movement is something like 'h', 'k', 'w', 'b', 'gg', etc. */ export abstract class BaseMovement extends BaseAction { + isMotion = true; + /** * Whether we should change desiredColumn in VimState. */ @@ -466,26 +474,29 @@ class CommandNextSearchMatch extends BaseMovement { } } -// TODO - bah, movements need to be more powerful. @RegisterAction -class CommandStar extends BaseMovement { +class CommandStar extends BaseCommand { modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; keys = ["*"]; + isMotion = true; - public async execAction(position: Position, vimState: VimState): Promise { + public async exec(position: Position, vimState: VimState): Promise { const start = position.getWordLeft(true); const end = position.getCurrentWordEnd(true).getRight(); const currentWord = TextEditor.getText(new vscode.Range(start, end)); vimState.searchString = currentWord; vimState.searchDirection = 1; - vimState.cursorPosition = CommandInsertInSearchMode.GetNextSearchMatch(vimState.cursorPosition, currentWord); + const result = CommandInsertInSearchMode.GetNextSearchMatch(vimState.cursorPosition, currentWord); - return position; + if (result !== undefined) { + vimState.cursorPosition = result; + } + + return vimState; } } - @RegisterAction class CommandPreviousSearchMatch extends BaseMovement { modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; @@ -546,6 +557,7 @@ class CommandInsertInInsertMode extends BaseCommand { export class CommandSearchForwards extends BaseCommand { modes = [ModeName.Normal]; keys = ["/"]; + isMotion = true; public async exec(position: Position, vimState: VimState): Promise { vimState.searchString = ""; @@ -563,6 +575,7 @@ export class CommandSearchForwards extends BaseCommand { export class CommandSearchBackwards extends BaseCommand { modes = [ModeName.Normal]; keys = ["?"]; + isMotion = true; public async exec(position: Position, vimState: VimState): Promise { vimState.searchString = ""; diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index 80bb92a0a67..22ef155a3cd 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -181,10 +181,7 @@ export class RecordedState { } public get hasRunAMovement(): boolean { - return _.filter(this.actionsRun, a => - a instanceof BaseMovement || - a instanceof CommandSearchForwards || - a instanceof CommandSearchBackwards).length > 0; + return _.filter(this.actionsRun, a => a.isMotion).length > 0; } /** From 09555d4945fe1dd50f88a2bddc1a98ff49633f37 Mon Sep 17 00:00:00 2001 From: johnfn Date: Tue, 21 Jun 2016 11:40:00 -0700 Subject: [PATCH 3/4] Fix implementation, add tests. --- src/actions/actions.ts | 65 +++++++++++++++++++++-- test/mode/normalModeTests/motions.test.ts | 36 +++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index 44ebec8066c..69854faf988 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -480,17 +480,74 @@ class CommandStar extends BaseCommand { keys = ["*"]; isMotion = true; - public async exec(position: Position, vimState: VimState): Promise { + public static GetWordAtPosition(position: Position): string { const start = position.getWordLeft(true); const end = position.getCurrentWordEnd(true).getRight(); - const currentWord = TextEditor.getText(new vscode.Range(start, end)); + + return TextEditor.getText(new vscode.Range(start, end)); + } + + public async exec(position: Position, vimState: VimState): Promise { + const currentWord = CommandStar.GetWordAtPosition(position); vimState.searchString = currentWord; vimState.searchDirection = 1; - const result = CommandInsertInSearchMode.GetNextSearchMatch(vimState.cursorPosition, currentWord); - if (result !== undefined) { + let result: Position; + + while (true) { + result = CommandInsertInSearchMode.GetNextSearchMatch(vimState.cursorPosition, currentWord); + + if (result === undefined) { + break; + } + + vimState.cursorPosition = result; + + if (CommandStar.GetWordAtPosition(result) === currentWord) { + break; + } + } + + return vimState; + } +} + +// TODO - merge both * and # implementations, use regex in GetNextSearchMatch +// rather than this approach +@RegisterAction +class CommandHash extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; + keys = ["#"]; + isMotion = true; + + public static GetWordAtPosition(position: Position): string { + const start = position.getWordLeft(true); + const end = position.getCurrentWordEnd(true).getRight(); + + return TextEditor.getText(new vscode.Range(start, end)); + } + + public async exec(position: Position, vimState: VimState): Promise { + const currentWord = CommandStar.GetWordAtPosition(position); + + vimState.searchString = currentWord; + vimState.searchDirection = -1; + + let result: Position; + + while (true) { + result = CommandInsertInSearchMode.GetPreviousSearchMatch(vimState.cursorPosition, currentWord); + + if (result === undefined) { + break; + } + vimState.cursorPosition = result; + + if (CommandStar.GetWordAtPosition(result) === currentWord) { + break; + } } return vimState; diff --git a/test/mode/normalModeTests/motions.test.ts b/test/mode/normalModeTests/motions.test.ts index 7c64c4580b1..e69a293a9cc 100644 --- a/test/mode/normalModeTests/motions.test.ts +++ b/test/mode/normalModeTests/motions.test.ts @@ -255,4 +255,40 @@ suite("Motions in Normal Mode", () => { keysPressed: '10l', end: ['blah blah |blah'] }); + + newTest({ + title: "Can handle *", + start: ['|blah duh blah duh blah'], + keysPressed: '*', + end: ['blah duh |blah duh blah'] + }); + + newTest({ + title: "Can handle tricky *", + start: ['|blah blahblah duh blah'], + keysPressed: '*', + end: ['blah blahblah duh |blah'] + }); + + newTest({ + title: "Can handle **", + start: ['|blah duh blah duh blah'], + keysPressed: '**', + end: ['blah duh blah duh |blah'] + }); + + newTest({ + title: "Can handle #", + start: ['blah duh |blah duh blah'], + keysPressed: '#', + end: ['|blah duh blah duh blah'] + }); + + newTest({ + title: "Can handle ##", + start: ['blah duh blah duh |blah'], + keysPressed: '##', + end: ['|blah duh blah duh blah'] + }); + }); \ No newline at end of file From d157f24038b569325f59497732d77886d25b77c1 Mon Sep 17 00:00:00 2001 From: johnfn Date: Tue, 21 Jun 2016 11:43:28 -0700 Subject: [PATCH 4/4] Allow * and # to be repeated with number prefixes. --- src/actions/actions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/actions/actions.ts b/src/actions/actions.ts index 69854faf988..4eb076df1a8 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -479,6 +479,7 @@ class CommandStar extends BaseCommand { modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; keys = ["*"]; isMotion = true; + canBeRepeated = true; public static GetWordAtPosition(position: Position): string { const start = position.getWordLeft(true); @@ -520,6 +521,7 @@ class CommandHash extends BaseCommand { modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine]; keys = ["#"]; isMotion = true; + canBeRepeated = true; public static GetWordAtPosition(position: Position): string { const start = position.getWordLeft(true);