Skip to content

feat: add W, B, and E WORD motions - #92

Open
paulluis-roehl wants to merge 3 commits into
oribarilan:mainfrom
paulluis-roehl:feat/word-motions
Open

paulluis-roehl wants to merge 3 commits into
oribarilan:mainfrom
paulluis-roehl:feat/word-motions

Conversation

@paulluis-roehl

Copy link
Copy Markdown

Summary

  • Add W, B, and E motions that keep punctuation and URLs together.
  • Support counts, visual selections, and delete/change/yank operators.
  • Mirror existing e test cases and assertion style.
  • Update keybinding documentation.

Closes #88

Validation

  • All 512 tests passed.
  • Lint passes.

@paulluis-roehl
paulluis-roehl marked this pull request as ready for review October 18, 2026 10:31
@oribarilan

Copy link
Copy Markdown
Owner

Thanks for picking this up, @paulluis-roehl! The tests fit right in with the existing ones.

I tried a few edge cases on the branch and found some spots where it doesn't match Vim:

  1. dW on the last word of a line joins it with the next line. With foo bar\n baz and the cursor on bar, dW gives foo baz. W treats the newline and the indent as normal spaces and skips them. Vim stops at the end of the line, so you'd get foo \n baz.

  2. dE at the very end of the text deletes the last character. With foo and the cursor after the last o (offset 3), dE deletes the o. wordMotion returns len - 1, which is smaller than the cursor, so Math.min(offset, target) flips the range. Vim does nothing here. Same for yE.

  3. cW should work like cE. With foo.bar baz and the cursor at 0, cW also deletes the space after foo.bar, so typing x gives xbaz. Vim keeps the space and gives x baz. The test "cW deletes from cursor to start of next WORD" would need to change too.

  4. Vim treats an empty line as a WORD. With foo\n\nbar, W from the start jumps to bar, but Vim stops on the empty line first. So 2W goes one too far for each blank line.

  5. Visual B sends the range backwards, e.g. selectRange { start: 20, end: 12 }. I'm not sure setSelectionInclusive handles start > end, and I haven't tried it in OpenCode yet. Sorting the two with Math.min/Math.max first would be safe either way.

A couple of smaller things:

  • The early returns (dW at the end of the text, dB at the start) and counts like 2dW / d2B don't have tests yet.
  • The E case is almost a copy of endOfWord (the TODO says so). Not a blocker, but sharing one loop would mean less to keep in sync.

Happy to help with any of these if you get stuck.

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.

Add support for E and B keybindings

2 participants