diff --git a/README.md b/README.md index 2e8290064..b24873cf5 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,10 @@ Vim has a lot of nooks and crannies. VSCodeVim preserves some of the coolest noo * `gd` - jump to definition. _Astoundingly_ useful in any language that VSCode provides definition support for. I use this one probably hundreds of times a day. * `gq` on a visual selection - Reflow and wordwrap blocks of text, preserving commenting style. Great for formatting documentation comments. +* `gc`, which adds another cursor on the next word it finds which is the same as the word under the cursor. +* `af`, a command that I added in visual mode, which selects increasingly large blocks of text. e.g. if you had "blah (foo [bar 'ba|z'])" then it would select 'baz' first. If you pressed az again, it'd then select [bar 'baz'], and if you did it a third time it would select "(foo [bar 'baz'])". + +(The mnemonic: selecting blocks is fast af! :wink:) ## Special Shoutouts to Cool Contributors diff --git a/src/actions/actions.ts b/src/actions/actions.ts index e0bb15ab7..85b1ac3b1 100644 --- a/src/actions/actions.ts +++ b/src/actions/actions.ts @@ -5225,6 +5225,62 @@ class SelectABigWord extends TextObjectMovement { } } +/** + * This is a custom action that I (johnfn) added. It selects procedurally + * larger blocks. e.g. if you had "blah (foo [bar 'ba|z'])" then it would + * select 'baz' first. If you pressed az again, it'd then select [bar 'baz'], + * and if you did it a third time it would select "(foo [bar 'baz'])". + */ +@RegisterAction +class SelectAnExpandingBlock extends TextObjectMovement { + keys = ["a", "f"]; + modes = [ModeName.Visual]; + + public async execAction(position: Position, vimState: VimState): Promise { + const ranges = [ + await new MoveASingleQuotes().execAction(position, vimState), + await new MoveADoubleQuotes().execAction(position, vimState), + await new MoveAClosingCurlyBrace().execAction(position, vimState), + await new MoveAParentheses().execAction(position, vimState), + await new MoveASquareBracket().execAction(position, vimState), + ]; + + let smallestRange: Range | undefined = undefined; + + for (const iMotion of ranges) { + if (iMotion.failed) { continue; } + + const range = Range.FromIMovement(iMotion); + let contender: Range | undefined = undefined; + + if (!smallestRange) { + contender = range; + } else { + if (range.start.isAfter(smallestRange.start) && + range.stop.isBefore(smallestRange.stop)) { + contender = range; + } + } + + if (contender && !contender.equals(new Range(vimState.cursorStartPosition, vimState.cursorPosition))) { + smallestRange = contender; + } + } + + if (!smallestRange) { + return { + start: vimState.cursorStartPosition, + stop: vimState.cursorPosition, + }; + } else { + return { + start: smallestRange.start, + stop: smallestRange.stop, + }; + } + } +} + @RegisterAction class SelectInnerWord extends TextObjectMovement { modes = [ModeName.Normal, ModeName.Visual];