Skip to content

Readline/POSIX keybinding gaps in interactive prompts: Ctrl-U missing (CommandPalette/Search), Ctrl-J shadowed (Insert+LSP popup) #360

Description

@cuttlefisch

Summary

Two confirmed, real gaps in MAE's interactive prompt keybindings, found while investigating a user report of "can't cycle downward with things like ctrl-j" in interactive prompts (that specific claim doesn't match current source for Mode::CommandPalette/Mode::FilePicker, where Ctrl-J already correctly moves the selection down — possibly a stale-binary repro; see caveat below — but the investigation surfaced two other real, confirmed gaps worth fixing regardless).

Gap 1 — Ctrl-U (clear query) missing in Mode::CommandPalette and Mode::Search

crates/mae/src/key_handling/command_palette.rs had arms for Ctrl-K (up), Ctrl-J (down), Ctrl-C (close), and plain Char(ch), but no Char('u') arm — it fell through to a no-op _ => {}. This is inconsistent with the immediately analogous Mode::FilePicker, which already binds Ctrl-U → picker.clear_query() (crates/mae/src/key_handling/file_picker.rs). Mode::Search (/-search) had no Ctrl bindings at all.

This affects every command using Mode::CommandPalette: kb-find, switch-buffer, recent-files, project-switch, kb-insert-link, etc.

Gap 2 — Ctrl-J shadowed as "insert newline" in Mode::Insert while the LSP completion popup is open

crates/mae/src/key_handling/insert.rs unconditionally treated Ctrl-J as a newline-insert (and dismissed any open completion popup as a side effect), while Ctrl-N/Ctrl-P were correctly wired to lsp_complete_next/lsp_complete_prev when the popup is open. This is inconsistent with Mode::CommandPalette/Mode::FilePicker, where Ctrl-J already means "move selection down" — when an LSP completion popup is open, Ctrl-J should move the completion selection too, not insert a literal newline.

What was checked and is NOT a gap

  • No terminal/GUI key-decoding collision: crossterm's raw-mode C0-control decoding keeps Ctrl-J and Enter/Return distinct (confirmed via the vendored crossterm source, which explicitly fixes this for issue Add chord-diagram (circular) graph-layout mode, make it the default #371 in raw mode); winit's GUI→crossterm translation (crates/gui/src/input.rscrates/mae/src/key_handling/mod.rs) never merges them either.
  • Mode::ConversationInput (AI chat prompt): Ctrl-U is already a correct readline unix-line-discard (kill-to-cursor, appropriate for a multi-line prompt buffer). This mode has no completion-list-navigation concept at all (free-text buffer editing, not a fuzzy-filtered candidate list), so there's no analogous "Ctrl-J should move a selection" need.

Caveat on the original repro

Ctrl-J already worked correctly in Mode::CommandPalette/Mode::FilePicker in the source at time of investigation — if the original report was specifically about kb-find/switch-buffer, it may have been reproduced against a stale/pre-fix binary rather than current source (multiple locally-running mae --gui instances were separately confirmed running deleted/stale binaries during this same investigation session). Worth a fresh-binary retest if this recurs.

Fix (implemented)

  • Added Ctrl-U to Mode::CommandPalette (mirrors FilePicker's clear_query pattern, routes through kb_find_palette_query_changed() for correct large-KB lazy re-search behavior) and Mode::Search (direct .clear() on search_input).
  • Added a Char('j') if CONTROL && popup_open arm in Mode::Insert, placed before the existing unguarded Ctrl-J-newline arm (required by Rust match-arm evaluation order), calling lsp_complete_next() for parity with the sibling modes. The existing unguarded arm remains as the correct fallback when no popup is open.

Tests

crates/mae/src/key_handling/tests.rs: command_palette_ctrl_u_clears_query, command_palette_ctrl_u_on_kb_find_large_kb_triggers_requery (adversarial — proves the lazy re-search path fires, not just a local clear), search_mode_ctrl_u_clears_search_input, insert_mode_ctrl_j_moves_completion_selection_when_popup_open, insert_mode_ctrl_j_inserts_newline_when_popup_closed (regression guard — existing behavior intact when there's no popup).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions