-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Global command implementation #9769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
I see this also implements the :v command. I've always missed that one. |
|
|
||
| private static createParser(inverse: boolean): Parser<GlobalCommand> { | ||
| return optWhitespace.then( | ||
| regexp(/[^\w\s\\|"]{1}/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{1}... doesn't that mean "1 time"? Is that necessary?
| const position = new Position(lineNumber, 0); | ||
| vimState.cursorStopPosition = position; | ||
| vimState.cursorStartPosition = position; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const position = new Position(lineNumber, 0); | |
| vimState.cursorStopPosition = position; | |
| vimState.cursorStartPosition = position; | |
| vimState.cursor = Cursor.atPosition(new Position(lineNumber, 0)); |
| const position = new Position(lineNumber, 0); | ||
| vimState.cursorStopPosition = position; | ||
| vimState.cursorStartPosition = position; | ||
| vimState.editor.selection = new vscode.Selection(position, position); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is setting the selection here necessary? Whenever possible we try to just set the cursor(s) on VimState then let ModeHandler.updateView() set the editor selections accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(here and elsewhere)
| StatusBar.displayError( | ||
| vimState, | ||
| VimError.fromCode(ErrorCode.NotAnEditorCommand, commandToExecute), | ||
| ); | ||
| throw new Error('Command parsing failed'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just throw the VimError?
| vimState.cursorStopPosition = finalPosition; | ||
| vimState.cursorStartPosition = finalPosition; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is redundant if we are going to do vimState.cursors = [new Cursor(finalPosition, finalPosition)]; two lines below
| vimState.cursorStartPosition = finalPosition; | ||
| vimState.editor.selection = new vscode.Selection(finalPosition, finalPosition); | ||
| vimState.cursors = [new Cursor(finalPosition, finalPosition)]; | ||
| vimState.lastVisualSelection = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
| * Updates line tracking based on the executed command and its effects on the document | ||
| */ | ||
| private updateLineTracking( | ||
| parsedCommand: ExCommand | null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this ever null?
| } else if (parsedCommand instanceof SubstituteCommand || commandString.startsWith('s')) { | ||
| // Substitute commands: can add or remove lines based on replacement text | ||
| executionContext.lineOffset += lineCountChange; | ||
| } else if (commandString.startsWith('t') || commandString.startsWith('co')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not parsedCommand instanceof CopyCommand like above?
| executionContext: ExecutionContext, | ||
| ): void { | ||
| // Handle different command types and their line count effects | ||
| if (parsedCommand instanceof DeleteCommand || commandString.startsWith('d')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we bothering with commandString after we parsed the command?
| constructor(args: IGlobalCommandArguments) { | ||
| super(); | ||
|
|
||
| // Validate pattern is not empty (Requirement 6.1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume Requirement 6.1 refers to an AI prompt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements the :g[lobal]/[pattern]/[command] feature for VSCodeVim, enabling users to execute commands on lines matching (or not matching) a pattern across the entire document or a specified range. This is a fundamental Vim feature that has been requested in issue #3010.
Key Changes:
- New GlobalCommand class supporting
:g,:global,:g!,:v, and:vglobalvariants - Comprehensive line tracking to handle document modifications during command execution
- Atomic undo/redo support treating the entire global operation as a single change
- Pattern-based line selection with support for multiple delimiter characters
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/cmd_line/commands/global.ts | New implementation of the global command with pattern matching, line tracking, and command execution on matching lines |
| src/vimscript/exCommandParser.ts | Registration of global command variants (g, global, g!, v, vglobal) with their respective parsers |
| test/cmd_line/global.test.ts | Comprehensive test suite covering parser variants, pattern delimiters, command execution, ranges, error handling, undo/redo, and edge cases |
| test/vimscript/exCommandParse.test.ts | Parser tests for global command syntax validation |
| .gitignore | Added .kiro directory to ignore list for AI assistant artifacts |
| // Validate pattern is not empty (Requirement 6.1) | ||
| if (!args.pattern || args.pattern.patternString === '') { | ||
| throw VimError.fromCode(ErrorCode.NoPreviousRegularExpression); | ||
| } | ||
|
|
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructor incorrectly rejects empty patterns. In Vim, when using :g//command with an empty pattern, it should use the last search pattern from globalState.searchState, not throw an error. Similar to how SubstituteCommand handles empty patterns (see substitute.ts lines 531-542), this should check if the pattern is empty and fall back to the previous search state. Currently, this makes the test on line 292-298 of global.test.ts fail to work as expected.
| // Validate pattern is not empty (Requirement 6.1) | |
| if (!args.pattern || args.pattern.patternString === '') { | |
| throw VimError.fromCode(ErrorCode.NoPreviousRegularExpression); | |
| } |
|
|
||
| private handleCommandError( | ||
| vimState: VimState, | ||
| error: any, |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error parameter type is any, which loses type safety. Consider using a more specific type such as unknown and then performing type checking within the function, or Error | VimError if those are the expected error types. Using any bypasses TypeScript's type checking.
| error: any, | |
| error: unknown, |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
What this PR does / why we need it:
This PR implements the
:g[lobal]/[pattern]/[command]featureWhich issue(s) this PR fixes
#3010
Special notes for your reviewer:
This is my first time in this code base and I used Kiro. All the tests are passing, but review it carefully.