Add with_persistent_menus option#1118
Conversation
When a menu is active, two hardcoded paths in the engine dismiss it while the user is erasing input: with quick completions on, any backspace deactivates it; and regardless of quick completions, any edit that leaves the line buffer empty deactivates it. There was no way to configure either. User reasoning: erasing the filter characters should not dismiss the menu — activating a menu on an empty buffer is already valid and shows all values, so erasing back to empty should be able to behave the same. The flag covers both paths so it also works when quick completions are enabled (the default in downstream nushell). Add `with_persistent_menus(bool)` (default `false`, preserving current behavior). Naming: a first draft was `with_deactivate_menu_on_empty_buffer(true)` — rejected because enabling the feature read as a double negative, and once the quick-completions path was included, "empty buffer" no longer described the only trigger. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
I'd like to see how this work in an example so I can try it out before landing it. |
|
Code looks good, not much to say since it plugs into the existing behavior. |
|
@fdncred @kronberger-droid below is the video demo. And nushell/nushell#18573 is the next change that is needed to use the reedline feature in nushell Screen.Recording.2026-07-11.at.16.33.55.mov |
|
@maxim-uvarov This is fine but I'd like to see an example in the examples folder that demonstrates this reedline feature without nushell. I want to be able to run it myself without nushell to see how it works. Just look at the other examples as a guide on how to create one. |
Requested by review: a runnable demonstration of `with_persistent_menus` that doesn't need the downstream nushell PR. Uses a small inline prefix completer instead of `DefaultCompleter` because the latter returns nothing for empty input, which would make the kept-open menu invisible exactly at the empty-line moment the example wants to show. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The quick-completions arm also dismisses on BackspaceWord and non-selecting MoveToLineStart; the existing matrix only exercised Backspace. All three share one guard today, but the tests pin the contract in case the arm is ever split. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@fdncred, this?
Type he, press Tab, then erase with Backspace past the start — the menu stays open and Note: the example uses a small inline prefix completer instead of DefaultCompleter, Also extended the test matrix to cover the other two commands in the quick-dismissal arm |
|
yup, that's what i meant. very cool. thanks! |
|
I'll let @kronberger-droid try it before we land it incase he sees something I missed. |
Disclosure: the code was written by an AI agentunder my direction; I can't review Rust line-by-line myself. What was verified: the full reedline test suite plus a new 4-case test matrix (quick × persistent), cargo fmt --check / clippy with CI flags, and manual behavior checks in a live nushell build with both flag values. I've wanted this behavior for a long time and drove the design decisions. Please review accordingly.
Summary
When a menu is active and the user erases the characters they typed to filter it, the engine dismisses the menu. Two hardcoded paths in
engine.rsdo this: with quick completions on, anyBackspace/BackspaceWord/MoveToLineStartdeactivates the menu; and regardless of quick completions, any edit that leaves the line buffer empty deactivates it. Neither was configurable.Erasing the filter is a natural part of browsing completions — narrowing too far and backing up shouldn't throw the menu away, especially since activating a menu on an empty buffer is already valid and shows all values.
Public API change: a new builder option,
Reedline::with_persistent_menus(bool), defaultfalse. With the default, observable behavior is unchanged. Withtrue, edits refilter an active menu instead of dismissing it; the menu still closes onEsc,Ctrl-C, or when a value is accepted.Before
With a menu open, erasing input dismisses it:
After
Default (
with_persistent_menus(false)): same as before.with_persistent_menus(true): erasing characters (or emptying the line) sendsMenuEvent::Editlike any other edit, so the menu stays open and refilters — down to the full unfiltered list on an empty buffer.Additional notes
A follow-up nushell PR wires this as
$env.config.completions.persistent_menusonce a reedline release includes it.Covered by a new
rstestcase matrix (quick × persistent) insrc/engine.rs.