feat: add floating and docked searchable file picker
This commit is contained in:
@@ -20,6 +20,7 @@ Without a file, vamp opens an unnamed buffer. A nonexistent file opens an empty
|
||||
| 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 |
|
||||
| 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 |
|
||||
| Normal | `i` insert before each caret; `a` insert after each caret; `o`/`O` open lines below/above |
|
||||
| Normal, Visual | `I` (Shift+i) / `A` (Shift+a) insert at first nonblank / line end; Visual splits each touched row into a caret first |
|
||||
| Normal | `h`/`j`/`k`/`l` or arrows move every caret; `0`/`$` or Home/End |
|
||||
@@ -89,6 +90,27 @@ IDs start at 1 and are never renumbered or reused. Closing the last buffer creat
|
||||
|
||||
Switching from Insert commits its pending undo group and parks that buffer in Normal. Explicit buffer switching from Visual retains that buffer's directed ranges and Visual mode; switching back restores them. Command/search prompts, palette queries, and pending motion counts are transient, not restored per buffer. The new commands appear in the command palette and can be bound by exact command name in Normal mode. When several buffers are open, status shows the active ID and total count.
|
||||
|
||||
### File picker and explorer
|
||||
|
||||
Space o opens a floating picker; Space e opens or focuses the docked explorer. Both use the same searchable project-relative file list. There is one instance: invoking the other action changes its presentation while retaining the query and selection. These actions are available as `config.core.file_picker` and `config.core.file_explorer` in Normal/Visual and in the command palette.
|
||||
|
||||
The dock defaults to the right of the editor workspace. Set `config.file_explorer_side = "left"` (or `"right"`, `"top"`, `"bottom"`) to choose another side. Status and command/search prompts remain full-width; editor text, gutter, highlights, and cursors use the remaining rectangle. Focused panes prioritize their query on tiny terminals; unfocused docks shrink or hide to preserve editing space.
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
| Ctrl+w | Close either presentation |
|
||||
| Ctrl+r | Rescan the project while retaining the query |
|
||||
| Ctrl+p | Open the command palette, closing floating search or blurring a dock |
|
||||
|
||||
Opening a file from a dock leaves it visible and returns focus to editing. Space e refocuses it. Query input never edits the underlying buffer; bracketed paste is ignored while file search is focused. Cancellation preserves buffer mode, selections, and pending Insert history. Invalid, binary, unreadable, or disappeared selections report an error and keep focus without changing the active buffer.
|
||||
|
||||
The project root is the working directory captured at startup. Scanning uses the `ignore` crate for nested `.gitignore` rules and negation; parent/global Git ignore rules are disabled. Hidden regular files are included unless ignored, `.git`/`.jj` metadata is always excluded, and symlink files/directories are not followed. Files open using their original OS paths, not sanitized display labels. Root scan failures report an error; descendant/ignore errors retain reachable files with an explicit incomplete-scan warning. A failed refresh keeps the prior list. There is no background watcher: refresh explicitly after external file changes. The explorer is a searchable path list, not an expandable directory tree.
|
||||
|
||||
### Command palette
|
||||
|
||||
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.
|
||||
@@ -111,9 +133,10 @@ Leader bindings require Normal/Visual and the target's existing mode eligibility
|
||||
|
||||
## Module seams
|
||||
|
||||
One crate, four modules, no workspace or frontend-specific types in the editing backend:
|
||||
One crate, five modules, no workspace or terminal-coordinate types in the editing backend:
|
||||
|
||||
- `editor`: owns buffer lifecycle and per-buffer text, saved state, primary/selection set, modes, search, and undo/redo; session-wide clipboard fragments, action metadata and command-palette state/filtering, key notation, leader/count prefixes and binding resolution, modal paste policy, saving, command ownership, capabilities, and validated edit effects. Its low-level interface returns an `Invocation` when an external command is selected.
|
||||
- `editor`: owns buffer lifecycle and per-buffer text, saved state, primary/selection set, modes, search, and undo/redo; session-wide clipboard fragments, action metadata, command-palette and file-picker focus/dispatch, key notation, leader/count prefixes and binding resolution, modal paste policy, saving, command ownership, capabilities, and validated edit effects. Its low-level interface returns an `Invocation` when an external command is selected.
|
||||
- `files`: owns the gitignore-aware catalog, authoritative OS paths, query filtering, navigation, and scan/error state shared by floating and docked presentations.
|
||||
- `session`: `EditorSession` owns the editor and its Ruby hosts together. It loads configuration once, registers eligible plugins before installing shared bindings, executes selected commands against the active buffer, and retires both host and command ownership on invocation or effect-validation failure.
|
||||
- `tui`: translates terminal events to session input, renders read-only active-buffer state, keeps frontend-owned viewports by stable buffer ID, and advertises `text` and `styled_text` capabilities. It has no plugin lifecycle callback, host storage, or paste acceptance policy.
|
||||
- `ruby`: evaluates configuration and runs isolated plugin hosts. Protocol, deadlines, and process cleanup stay inside this module.
|
||||
@@ -125,6 +148,7 @@ Frontends inspect `palette()` for the optional read-only query, filtered command
|
||||
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.
|
||||
Frontends inspect `file_picker()` for optional query/results, presentation, focus, and errors, and `file_explorer_side()` for logical docking preference. They allocate rectangles and render; file filtering, focus transitions, and selected-file opening remain backend-owned.
|
||||
|
||||
The terminal adapter owns raw mode, alternate-screen state, bracketed paste, and its panic hook for one session lifetime. Entry failure, normal return, I/O errors, and unwinding panics restore the caller's hook; active-terminal cleanup runs before caller panic diagnostics. Panics retain their original payload and propagate rather than being swallowed. Terminal state and panic hooks are process-global: run one TUI at a time and do not replace its hook while that session is active.
|
||||
|
||||
@@ -143,6 +167,7 @@ By default, vamp loads `$XDG_CONFIG_HOME/vamp/config.rb`, or `~/.config/vamp/con
|
||||
```ruby
|
||||
Vamp.configure do |config|
|
||||
config.leader = "space"
|
||||
config.file_explorer_side = "right"
|
||||
config.plugin "plugins/greeting.rb"
|
||||
config.bind "g", "greet"
|
||||
config.bind "leader g", "greet"
|
||||
@@ -159,6 +184,7 @@ 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.
|
||||
`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.
|
||||
|
||||
@@ -177,6 +203,7 @@ Terminals share `ctrl+h` with `backspace`, `ctrl+i` with `tab`, and `ctrl+j`/`ct
|
||||
| Normal | `join_lines`, `undo`, `redo`, `command` |
|
||||
| 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 | `word_next`, `word_previous`, `word_end`, `big_word_next`, `big_word_previous`, `big_word_end` |
|
||||
| Normal, Insert | `delete` |
|
||||
|
||||
Reference in New Issue
Block a user