feat: add configurable Ctrl+j/k list navigation
This commit is contained in:
@@ -17,7 +17,7 @@ Without a file, vamp opens an unnamed buffer. A nonexistent file opens an empty
|
||||
|
||||
| Mode | Keys |
|
||||
| --- | --- |
|
||||
| All modes | Ctrl+p opens the command palette; type to filter, Up/Down or Ctrl+k/Ctrl+n select, Enter accepts, Esc/Ctrl+p cancels |
|
||||
| All modes | Ctrl+p opens the command palette; type to filter, Ctrl+j/k or arrows select, Enter accepts, Esc/Ctrl+p cancels |
|
||||
| Normal, Visual | Space Space opens the palette through the default leader |
|
||||
| Normal | Space w saves; Space b n/p/l/d switches next/previous, lists, or closes buffers |
|
||||
| Normal, Visual | Space o opens floating file search; Space e opens/focuses the docked explorer |
|
||||
@@ -30,7 +30,7 @@ Without a file, vamp opens an unnamed buffer. A nonexistent file opens an empty
|
||||
| Normal | `x`/Delete delete; `u` undo; `U` (Shift+u) redo; `:` enter command mode |
|
||||
| Normal | `/` opens search; `n` finds the next match; `N` (Shift+n) finds the previous match |
|
||||
| Normal | `v` starts Visual selections; `p`/`P` paste internal clipboard after/before each caret; Esc keeps only the primary caret |
|
||||
| Normal, Visual | Ctrl+k clones above; Ctrl+e clones below (Ctrl+j aliases Enter in terminals) |
|
||||
| Normal, Visual | Ctrl+k clones above; Ctrl+e clones below |
|
||||
| Visual | Cursor/word motions and `n`/`N` extend every selection; `y` copies; `d`/`x`/Delete cuts; `c` changes selections; `v`/Esc cancels highlighting |
|
||||
| Visual | Ctrl+l splits touched rows into Normal carets at first nonblank; Ctrl+n adds the next occurrence; Ctrl+s skips the primary occurrence; Ctrl+a selects all occurrences |
|
||||
| Insert | Type, Enter, Tab, Backspace/Delete, arrows, Home/End at every caret; Esc returns to Normal, retaining carets |
|
||||
@@ -99,7 +99,7 @@ The dock defaults to the right of the editor workspace. Set `config.file_explore
|
||||
| File-search input | Behavior |
|
||||
| --- | --- |
|
||||
| Type / Backspace | Case-insensitive whitespace-token filtering / remove one whole grapheme |
|
||||
| Up/Down or Ctrl+k/Ctrl+n | Wrap through matching files |
|
||||
| Ctrl+j/Down/Ctrl+n or Ctrl+k/Up | Wrap forward or backward through matching files |
|
||||
| Enter | Open the selected file, reusing an existing buffer without reloading hidden edits |
|
||||
| Esc | Close floating search; return to editor focus while retaining a dock |
|
||||
| Tab | Return to editor focus from a dock |
|
||||
@@ -115,11 +115,25 @@ The project root is the working directory captured at startup. Scanning uses the
|
||||
|
||||
Ctrl+p opens a searchable overlay without leaving the current mode. It lists core actions and registered Ex/plugin commands with their owner, effective bindings in the current mode, and eligible modes. Disabled or overridden bindings are not advertised; actions with no key remain available through the palette. Mode-ineligible entries are dimmed and cannot execute. Retired plugin commands disappear.
|
||||
|
||||
Search is case-insensitive: every whitespace-separated query token must occur in the command name, human-readable label, or owner. Results have a stable label/name order. Up/Down or Ctrl+k/Ctrl+n wrap through results; Backspace removes a whole grapheme. Enter with no match or an ineligible selection leaves the palette open. Bracketed paste is ignored while the palette is open.
|
||||
Search is case-insensitive: every whitespace-separated query token must occur in the command name, human-readable label, or owner. Results have a stable label/name order. Ctrl+j/Down/Ctrl+n select the next result; Ctrl+k/Up select the previous, with wraparound. Backspace removes a whole grapheme. Enter with no match or an ineligible selection leaves the palette open. Bracketed paste is ignored while the palette is open.
|
||||
|
||||
Accepting a core action executes it in the underlying mode, including Visual edits and Insert-mode actions. Accepting an Ex/plugin command is available only in Normal mode and opens a prefilled `:` prompt; add arguments if needed, then Enter executes through the usual session dispatch. Choosing `q!` therefore does not immediately discard changes.
|
||||
|
||||
Esc or Ctrl+p cancels without changing text, selections, the previous command/search prompt, or pending Insert history. The opening key can be remapped with `config.core.palette`; navigation and cancellation inside the palette are fixed. The TUI uses a constraint-based modal panel, keeps the selected result visible, and prioritizes the query on tiny terminals.
|
||||
Esc or Ctrl+p cancels without changing text, selections, the previous command/search prompt, or pending Insert history. The opening key can be remapped with `config.core.palette`; list navigation uses the shared configurable `selection` context. Other popup controls keep their defaults unless explicitly intercepted by a selection binding. The TUI uses a constraint-based modal panel, keeps the selected result visible, and prioritizes the query on tiny terminals.
|
||||
|
||||
### Selection-list bindings
|
||||
|
||||
The palette, floating file picker, and focused docked explorer share `config.modes.selection`. It is a binding context, not a buffer editing mode. `config.core.down` moves to the next list item; `config.core.up` moves to the previous. Defaults are Ctrl+j/Down/Ctrl+n for next and Ctrl+k/Up for previous. Remap or disable any single key:
|
||||
|
||||
```ruby
|
||||
config.bind "ctrl+f", config.core.down, mode: config.modes.selection
|
||||
config.bind "ctrl+b", config.core.up, mode: config.modes.selection
|
||||
config.bind "ctrl+j", nil, mode: config.modes.selection
|
||||
```
|
||||
|
||||
Selection bindings take precedence only while a list is focused. Unbound printable keys still filter; a disabled key is consumed without filtering or moving. Only `up`, `down`, and `nil` are accepted—no leader sequences, edits, or plugin commands. Invalid declarations leave existing routes unchanged. Navigation does not change text, carets, history, or the underlying mode; Enter remains accept by default. Both list footers show the effective navigation keys, including remaps and disabled directions.
|
||||
|
||||
Ctrl+j is distinct from Enter: in the TUI's raw mode, LF is Ctrl+j and CR is Enter; CSI-u control events also work. No enhanced-terminal negotiation is required. Ctrl+m remains an Enter alias. Outside lists, existing editing bindings are unchanged: Ctrl+k still clones above in Normal/Visual, and explicit Ctrl+j defaults insert a newline in Insert or submit Command/Search prompts. Enter and Ctrl+j can now be rebound independently.
|
||||
|
||||
### Leader key
|
||||
|
||||
@@ -145,6 +159,7 @@ One crate, five modules, no workspace or terminal-coordinate types in the editin
|
||||
Frontends drive `EditorSession::handle` or `execute` and inspect `editor()`; there is no mutable editor getter that could independently alter plugin registration. Pasted text uses `Input::Paste(String)` through the same input seam as keys. Plugin effects are applied internally so a rejected result also retires its owner; there is no generic frontend effects entry point.
|
||||
Frontends inspect `selections()` for document-ordered, end-exclusive Visual ranges of grapheme coordinates (empty outside Visual), `carets()` for all active cursors in document order, `cursor()` for the primary, and `selection_count()` for the total target count. A range ending at the next row's column 0 includes the previous row's newline; an empty final-row range contains no text. Selection normalization, clipboard contents, edit planning, and history remain inside the editor module.
|
||||
Frontends inspect `palette()` for the optional read-only query, filtered command entries, selected index, effective bindings, and eligibility. Palette input handling and acceptance stay in the editor; registered command execution stays in the session.
|
||||
Frontends use the cached `selection_navigation_hint()` to advertise the current shared list bindings without reconstructing input policy.
|
||||
Frontends inspect `count()` for the optional pending motion prefix; parsing and counted motion dispatch remain editor-owned.
|
||||
Frontends inspect `leader_pending()` for the cached display of a pending leader sequence; prefix recognition, cancellation, and dispatch remain editor-owned.
|
||||
Frontends inspect `buffer_id()`, `buffer_count()`, `has_buffer(id)`, and the ascending-ID `buffers()` iterator of borrowed path/dirty/active metadata. Existing text, cursor, selection, search, and mode methods describe only the active buffer.
|
||||
@@ -183,7 +198,7 @@ Vamp.configure do |config|
|
||||
end
|
||||
```
|
||||
|
||||
Plugin paths resolve relative to the config file, not the shell's working directory. `config.bind key, target, mode: config.modes.normal` overrides one key or leader-prefixed sequence; available modes are `config.modes.normal`, `config.modes.insert`, `config.modes.command`, `config.modes.search`, and `config.modes.visual`. Omitting `mode` selects Normal. The leader is session-wide, defaults to `"space"`, and is applied before configured bindings.
|
||||
Plugin paths resolve relative to the config file, not the shell's working directory. `config.bind key, target, mode: config.modes.normal` overrides one key or leader-prefixed sequence; editing modes are `config.modes.normal`, `config.modes.insert`, `config.modes.command`, `config.modes.search`, and `config.modes.visual`. `config.modes.selection` configures focused list navigation. Omitting `mode` selects Normal. The leader is session-wide, defaults to `"space"`, and is applied before configured bindings.
|
||||
`config.file_explorer_side` is session-wide and defaults to `"right"`; the other choices are `"left"`, `"top"`, and `"bottom"`. An invalid side reports a notice and retains the default, while valid bindings still install. Non-string/empty values disable the whole configuration like other Ruby type errors.
|
||||
|
||||
`config.core` exposes the built-in actions below as immutable `Action` values; `config.modes` exposes immutable `Mode` values. Misspelled methods fail during configuration evaluation, and `bind` rejects the wrong value type. Built-in action strings and mode strings are not accepted. Registered Ex/plugin commands still use exact string names (`"w"`, `"greet"`, etc., without `:` or arguments) and can only be bound in Normal mode. Ruby provides runtime checks, not compile-time checking; the editor still validates action/mode compatibility.
|
||||
@@ -194,7 +209,7 @@ Key notation follows [Herdr's readable style](https://herdr.dev/docs/configurati
|
||||
- Modifiers: `ctrl+a` through `ctrl+z`, and `shift+a` through `shift+z`. `shift+j` and `J` address the same key.
|
||||
- Named punctuation: `minus`, `comma`, `ampersand`, `plus`, `backtick`. Each addresses the same binding as its literal character.
|
||||
|
||||
Terminals share `ctrl+h` with `backspace`, `ctrl+i` with `tab`, and `ctrl+j`/`ctrl+m` with `enter`; these names address the same binding, including inside leader sequences. Only a single modifier on an ASCII letter is supported. Arbitrary non-leader key sequences, Alt/Cmd/Super, combined modifiers, modified special keys, and Vim-style angle-bracket notation are rejected rather than silently treated as another key.
|
||||
Terminals share `ctrl+h` with `backspace`, `ctrl+i` with `tab`, and `ctrl+m` with `enter`; these names address the same binding, including inside leader sequences. Ctrl+j is a distinct control key, not an Enter alias. Only a single modifier on an ASCII letter is supported. Arbitrary non-leader key sequences, Alt/Cmd/Super, combined modifiers, modified special keys, and Vim-style angle-bracket notation are rejected rather than silently treated as another key.
|
||||
|
||||
| Available modes | Methods on `config.core` |
|
||||
| --- | --- |
|
||||
@@ -204,7 +219,8 @@ Terminals share `ctrl+h` with `backspace`, `ctrl+i` with `tab`, and `ctrl+j`/`ct
|
||||
| Normal | `search`, `paste_before`, `paste_after` |
|
||||
| Normal, Visual | `select`, `search_next`, `search_previous` |
|
||||
| Normal, Visual | `file_picker`, `file_explorer` |
|
||||
| Normal, Insert, Visual | `left`, `down`, `up`, `right`, `line_start`, `line_end` |
|
||||
| Normal, Insert, Visual | `left`, `right`, `line_start`, `line_end` |
|
||||
| Normal, Insert, Visual, Selection | `down`, `up` |
|
||||
| Normal, Insert, Visual | `word_next`, `word_previous`, `word_end`, `big_word_next`, `big_word_previous`, `big_word_end` |
|
||||
| Normal, Insert | `delete` |
|
||||
| Visual | `copy`, `cut`, `change`, `select_lines`, `select_next`, `skip_occurrence`, `select_all` |
|
||||
|
||||
Reference in New Issue
Block a user