Skip to content

Conversation

@rafaelbdb
Copy link

What this PR does / why we need it:

Previously, movement commands with counts (e.g., 20j, 50k) would loop through each individual movement, causing visible lag and poor performance. This made the extension feel sluggish compared to native Vim, especially with large count values.

This PR optimizes the four basic movement commands (j, k, h, l) by overriding execActionWithCount to calculate the target position directly instead of looping:

  • MoveDown (j): Calculates target line as current + count
  • MoveUp (k): Calculates target line as current - count
  • MoveLeft (h): Calculates target character with surrogate pair handling
  • MoveRight (l): Calculates target character with surrogate pair handling

All special cases are preserved:

  • Interactive window history navigation
  • Foldfix mode compatibility
  • Operator context (d20j, y10k work correctly)
  • Visual mode selections
  • Column preservation for vertical movements
  • Boundary checks (don't exceed document limits)

Which issue(s) this PR fixes

Fixes #8557

Special notes for your reviewer:

Additionally fixed build issues encountered during development:

  • Updated test/index.ts to use new glob API (async/await pattern)
  • Added skipLibCheck to tsconfig.json for node_modules compatibility
  • Added ignoreDeprecations: "6.0" to silence baseUrl warning

The optimization provides instant cursor movement for any count value, matching native Vim's performance. Tested on Windows 10 and Ubuntu 22.04 with various count values (1-200) in Normal, Visual, and Operator-pending modes.

Note:

Those changes were made by GitHub Copilot, using Claude Sonnet 4.5 agent

Previously, movement commands with counts (e.g., 20j, 50k) would loop
through each individual movement, causing visible lag and poor performance.

This commit optimizes the four basic movement commands (j, k, h, l) by
overriding execActionWithCount to calculate the target position directly
instead of looping:

- MoveDown (j): Calculates target line as current + count
- MoveUp (k): Calculates target line as current - count
- MoveLeft (h): Calculates target character with surrogate pair handling
- MoveRight (l): Calculates target character with surrogate pair handling

All special cases are preserved:
- Interactive window history navigation
- Foldfix mode compatibility
- Operator context (d20j, y10k work correctly)
- Visual mode selections
- Column preservation for vertical movements
- Boundary checks (don't exceed document limits)

Additionally fixed build issues:
- Updated test/index.ts to use new glob API (async/await)
- Added skipLibCheck to tsconfig.json for node_modules compatibility
- Added ignoreDeprecations: "6.0" to silence baseUrl warning

Fixes VSCodeVim#8557
test/index.ts Outdated
Comment on lines 56 to 58
} catch (err) {
throw err;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any point to catching an error just to throw it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! You're absolutely right. The outer try-catch was just re-throwing the error without adding any value. I've removed it - the inner try-catch around mocha.run() is sufficient to handle errors and propagate them to the Promise.

return position.getDown();
}

public override async execActionWithCount(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For each of these, can't we make the implementation of execAction simply execActionWithCount(..., 1)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent suggestion! I've refactored execAction to simply delegate to execActionWithCount(position, vimState, 1). This eliminates code duplication and centralizes the logic in one place. The method now returns Promise<Position> (with a cast) since execActionWithCount with count=1 always returns a Position in practice, never an IMovement.

"strict": true,
"useUnknownInCatchVariables": false,
"experimentalDecorators": true,
"ignoreDeprecations": "6.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added to suppress a TypeScript compilation error. The baseUrl option (line 16) is deprecated in TypeScript 6.0+ and will be removed in TypeScript 7.0. Without ignoreDeprecations, we get a build error.

The baseUrl is currently needed for the paths mapping ("platform/*": ["src/platform/node/*"]). Ideally, we should migrate away from baseUrl/paths to the new recommended approach, but that would be a larger refactoring outside the scope of this performance optimization.

Should I remove this and open a separate issue to track the TypeScript 7.0 migration, or is it OK to keep it for now?

- Remove unnecessary outer try-catch in test/index.ts
- Simplify MoveDown.execAction by delegating to execActionWithCount
- Keep ignoreDeprecations in tsconfig.json (required for baseUrl deprecation warning)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slow line movement

2 participants