feat(tui): add configurable readline-style text transformations#6778
feat(tui): add configurable readline-style text transformations#6778aspiers wants to merge 1 commit intoanomalyco:devfrom
Conversation
|
I'm guessing the test failures are due to #6674, rather than anything to do with this PR. |
cc3e56b to
c71c6be
Compare
|
Tests passing after rebase. |
c71c6be to
c842ce0
Compare
|
Adding these key commands could indeed be helpful, I would be in favour of that. However, the impression that I've gotten from the maintainers over the course of some of my own recent PRs is that they'd generally prefer not to make changes to the default keybindings in the TUI and would generally prefer that new key commands instead be bound to Sometimes I'll accidentally type words in a prompt in in the wrong order, so having equivalents to emacs' |
c842ce0 to
c7e48a4
Compare
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
@ariane-emory Thanks a lot for the feedback. If the maintainers request removal of the default bindings then of course I'll do it. However in the absence of a request I'd prefer to keep them, since I think removal violates the Principle of Least Surprise - at this point there are multiple decades of precedent for these key bindings in all modern shells plus anything which uses readline(3). I definitely agree with your suggestion regarding |
|
I've submitted #9234 as the feature request for this PR. |
|
@aspiers You'll get no complaints from me: is's your PR, do it your way, and I would certainly love to see these key commands. FWIW, my interpretation of the behaviour I've seen on some of my own past PRs (which is of course merely an interpretation, and could be incorrect) was that it was also based on the application of the Principle of Least Surprise, just seen from the other side: users would be surprised if keys that did nothing in Opencode yesterday suddenly did something today. Speaking as someone whose emacs keybindings elisp file Is closer to 5 hundred lines long than 4, I'm certainly not too concerned with what the defaults are (if any) and am certainly more than happy to configure the bindings to my own tastes. In my book, the fact that the can be rebound to suit my own weird preferences is the the more important aspect. Setting the topic of defaults aside, this looks like great work so far. I haven't yet tested the branch out (I may find time to do so tomorrow), but upon a cursory read-through, it all looks pretty good to me! Best of luck guiding the PR in to a safe landing! |
|
Does this behave for CJK? The opentui native core already calculates word boundaries and could provide such actions. |
|
Thanks @ariane-emory and @kommander great question! I will look into that when I get a chance. |
|
I have been using this branch for almost a month now in my own fork's integration branches. Having I have not retrained myself into the habit of regularly using all the new key commands provided by this branch, but those that I have successfully trained myself into the habit of using do seem to be working appropriately. So, LGTM and also FGTM (feels good to me). |
00637c0 to
71e0ba2
Compare
f1ae801 to
08fa7f7
Compare
da3de15 to
a18b09c
Compare
|
@kommander commented on Jan 19, 2026, 12:57 AM GMT+13:
I asked Opus about this, and it said:
Although of course it could be wrong, and I'm more inclined to trust a human than AI 😅 |
|
@aspiers The word boundary behaviour does not align with that of readline/emacs. (the If I have this text:
... and then I strike
In contrast, striking the same keys in readline/emacs produces:
In short, |
|
Good catch, thanks! |
a18b09c to
a133a44
Compare
|
@ariane-emory Fixed in the latest push. The root cause was using The same bug was present in the New tests added covering the exact example from your comment ( |
6121652 to
2382f2c
Compare
|
The CI failures here are not caused by this PR. Both |
|
This PR has been updated to address the word boundary issue raised by @ariane-emory. Here's a summary of what changed: Word boundary fix The initial implementation used
The Tests 32 unit tests now cover |
2382f2c to
397fc34
Compare
|
Following up on my previous comment: the "fallback to previous word" behaviour when there's no next word was accidentally removed by an AI assistant during refactoring, and has now been restored — it was an intentional improvement over Emacs' silent no-op, useful when the cursor is at the end of the input and you want to transform the word you just typed. The word character definition fix ( |
|
Gah, Sonnet was too dumb to force-push the right changes. Trying again ... |
397fc34 to
3b47b5d
Compare
|
Oh it's even worse, the agent was copying the implementation into the test file and testing that! 🤦🏼♂️ 😱 😠 I really thought Sonnet 4.6 would be better than this... |
Without this patch, users must manually retype text to change case or paste deleted content in the TUI prompt, and some keybindings conflict with other TUI functions. This is a problem because it slows down text editing and limits the ability to customize keybindings to match user preferences. This patch solves the problem by adding configurable shortcuts for lowercase, uppercase, capitalize word, transpose characters, and yank operations, along with a kill buffer for storing deleted text.
3b47b5d to
36b79c8
Compare
|
All fixed now. Note: |
Summary
Adds missing Emacs/Readline-style text editing shortcuts to the TUI (terminal) prompt input, improving text editing efficiency in the terminal.
Closes #9234.
Changes
New shortcuts
alt+ualt+lalt+cctrl+yctrl+tUser-facing behavior
Case transformations (
alt+u,alt+l,alt+c)Kill ring support (
ctrl+y)ctrl+k,ctrl+u,ctrl+w, andalt+ddelete commands now save deleted text to a kill bufferctrl+yinserts the contents of the kill buffer at the current cursor positionTranspose characters (
ctrl+t)Configuration
All new shortcuts are configurable via
opencode.json:{ "keybinds": { "input_lowercase_word": "alt+l", "input_uppercase_word": "alt+u", "input_capitalize_word": "alt+c", "input_yank": "ctrl+y", "input_transpose_characters": "ctrl+t" } }Note on
ctrl+tconflict:By default,
ctrl+tis bound tovariant_cycle(cycle model variants). To usectrl+tfor transpose characters, you can rebind, e.g.:{ "keybinds": { "variant_cycle": "<leader>v", "input_transpose_characters": "ctrl+t" } }Technical details
TextareaRenderablewhich has limited built-in actions[A-Za-z0-9](matching Readline'sisalnum()and Emacs' word syntax class), extracted intoword.tsfor testabilityKnown limitations
M-fandM-b(word navigation) are handled natively by opentui and are unaffected by this PR — they still treat_as a word character. Making word boundary behaviour fully consistent across navigation and transformation would require a separate PR overriding opentui's nativeM-f/M-bhandling.Files
packages/opencode/src/config/config.ts- Added keybind schema entriespackages/opencode/src/cli/cmd/tui/component/prompt/index.tsx- Implemented shortcutspackages/opencode/src/cli/cmd/tui/component/prompt/word.ts- Word boundary and transformation functionspackages/web/src/content/docs/keybinds.mdx- Updated documentationpackages/sdk/js/src/v2/gen/types.gen.ts- Regenerated from schemapackages/opencode/test/tui/text-transform.test.ts- 32 tests against real implementation