Add visual mode, node registers, search and the jump list (stage 8c)

The rest of vim, on nodes. Visual mode takes in what lies between where
it started and the cursor: on the canvas, every node the rectangle from
one end's box to the other's touches; in the outline and the Note view,
the nodes in between in reading order. V takes whole branches, Ctrl+click
and Ctrl+drag pick with the mouse, and the operators take exactly what's
selected: d, y, > and <, M, and the pickers on every node at once.

Registers now hold text or nodes, so yy, dd and x keep what they take and
p, P and ]p paste it, layout and links included. "+ is the system
clipboard through arboard: nodes go there as Markdown in the user's
format (## headings, a --- tag list, the body), and come back whole while
the clipboard still holds them. clipboard = "unnamedplus" makes plain
yanks use it, as in vim.

. repeats the last change made with keys, with the text typed after it.
/ opens a Telescope-style search: fuzzy matches over titles, tags and
bodies, best first, with the note of the one you're on beside them; n
and N go on with it, and :find searches straight away. Each pane keeps a
jump list for Ctrl-o and Ctrl-i that crosses documents and reopens closed
ones. Capital marks lead into any document and, like the small ones, are
kept in memory.ron. gd follows a cross-link, and :marks, :jumps and
:registers list what they hold.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
skkeyeandClaude Opus 5.5 committed 2026-10-02 04:12:27 -04:00
1 parent abe3dc1d84
commit 7bec5bf3fc
45 files changed
+3922 -272

No files matched your search

+78 -18
View File
@@ -32,7 +32,8 @@ align · branch out · export (Markdown, plain text) and import (indented lists)
**Later**: peeking into portals and pulling a document back in · branches that
keep themselves tidy · the Links map · backlinks inside a document · picking
single nodes in visual mode · `Alt+hjkl` · opening a body in `$EDITOR` ·
single nodes in visual mode with keys (Ctrl+click does it with the mouse) ·
`Alt+hjkl` · opening a body in `$EDITOR` ·
persistent undo · sessions · operator + motion and macros · a tutorial
document · checkbox, link and date fields · Graphviz and Mermaid export · a
Settings view · vim tab pages.
@@ -122,10 +123,8 @@ lines.
- A title left empty comes back as it was, with a note saying so; a new idea
left empty goes away.
- Registers are shared by the whole app, as in vim: text yanked in one title
pastes into another. `"+` stays inside cumulus until part 8c connects it to
the system clipboard.
- `.` inside the text repeats the last change there, typing included. `.` on
nodes comes with node yank and paste, in part 8c.
pastes into another. `"+` is the system clipboard (part 8c).
- `.` inside the text repeats the last change there, typing included.
- As in vim: `cw` changes to the end of the word, `dw` stops at the end of the
line, `j` and `k` move by lines of text rather than wrapped rows, Esc steps
back onto the last letter typed.
@@ -140,6 +139,62 @@ lines.
editor: Enter starts a new line, Tab types four spaces. `cc`, `C` and `s`
start typing on an empty line too, as in vim (bodies are often empty).
**The rest of vim** (settled in part 8c)
- Visual mode takes in what lies between where it started and the cursor:
on the canvas, every node on show that the rectangle from one end's box to
the other's touches; in the outline and the Note view, the nodes in
between in reading order. `V` takes each with its whole branch; `v` in a
`V` selection (and `V` in a `v` one) switches, and the same key again
stops. Ctrl+click adds or takes out one node; Ctrl+drag on empty canvas
adds every node its rectangle touches. The selection is drawn thick in
the theme's visual color, and the status bar counts it.
- Operators take exactly what's selected (the user's pick): `d` deletes the
selected nodes, and with `v` their unselected children move up, as with
`x`; `y` yanks them with their shape among them; `>` and `<` move nodes
that share a parent; `M` moves them on the canvas (with their branches, or
just them after `M` again); the pickers set every one of them, as one undo
step.
- A register holds text or nodes, and either pastes anywhere: nodes pasted
into a text give their titles, text pasted on nodes becomes nodes. `yy`,
`yab`, `yib` yank nodes; every delete (`dd`, `x`, `dib`, `d` on a
selection) keeps what it took, so `dd` then `p` moves a branch. `p` `P`
`]p` paste after, before, or as the last children, keeping the yanked
layout, where a new node would go; a count pastes copies. Links between
the pasted nodes come along, and so do links to nodes still there when
they go back into the document they came from.
- `"+` is the system clipboard. Nodes go there as Markdown, the user's
format: a heading per node (`##` for the nodes yanked, `###` for their
children, `####` for anything deeper), its tags as a list between two
`---` lines, then its body. While the clipboard still holds what cumulus
put there, `"+p` gives back everything (colors, bodies, links, the
layout); text from elsewhere becomes nodes (Markdown headings, or a node
per line nested by its indent). `clipboard = "unnamedplus"` makes every
yank, delete and paste use the clipboard, as vim's setting does.
- `.` repeats the last change made with keys in normal or visual mode: the
command with its count and register, plus everything typed in the text
edit it started (`aMulch` then Esc adds another Mulch). A value picked in a
picker repeats on the cursor's node. A change made on a selection repeats
on the cursor's node (`d` as `x`, or `dd` after `V`; `>` as `>>`).
- `/` opens a search in the middle of the screen, as Telescope does (the
user's pick): what's typed at the top, the matches below it, fuzzy, best
first (titles before tags before bodies), with the letters that matched
standing out, and the note of the match you're on beside them. Case only
matters once there's a capital (smartcase). Enter goes there, Esc goes
back; `n` and `N` then go through the same matches, going round, and
`:find` searches straight away.
- Each pane keeps a jump list, as vim's windows do: `gg`, `G`, marks, a
search, `gd`, a link followed in the Note view and switching documents are
jumps. `Ctrl-o` goes back (into another document, opened again if it was
closed), `Ctrl-i` or Tab forward. `''` still goes back to before the last
jump.
- Capital marks (`mA`, `'A`) lead into any document, opening it if it isn't
open; they're kept in the data folder's `memory.ron`, and the small ones
(`ma`) are kept there with each document, so both last from one run to
the next.
- `gd` follows the cursor's cross-link; with several, a list of them pops up
beside the node. `:marks`, `:jumps` and `:registers` list them; Enter goes
to the mark or the place, or pastes the register.
**Views, panes and popups** (settled in stage 7)
- The Note view is made of parts: the title, the tags, the body, each link
(out, then in) and each child. `j` and `k` step through them, with a bar
@@ -378,7 +433,7 @@ text).
| **Inside text** | `h j k l w b e 0 ^ $ gg G` (and arrows, Home, End) · `f t F T ; ,` · `d` `c` `y` with any motion and `iw aw i" a" i( a(` · `x X s S D C Y` · `r ~ J p P` · `u Ctrl-r .` · `i a I A` back to typing · `o O` (title: a new sibling; body: a new line) · Esc done |
| **Move mode** | `hjkl` 1 cell · `HJKL` 5 cells · `M` branch ↔ node · Enter or Esc done |
| **Connect mode** | move to the target as usual · Enter link · `d` remove · `>` attach as child · `<Space>` style menu (`s` named, `a` arrows, `l` line, `c` color, `t` label) · Esc cancel |
| **Visual** | `d y > < M C` and `<Space>c` `<Space>s` `<Space>t` act on the whole selection · `o` swaps ends |
| **Visual** | `d` `x` `y` `>` `<` `M` and `<Space>c` `<Space>s` `<Space>B` `<Space>t` act on the whole selection · `o` swaps ends · `v` `V` switch, or stop · Ctrl+click, Ctrl+drag pick with the mouse |
## Files
@@ -471,6 +526,7 @@ max_node_width = 30
glyphs = "unicode" # "ascii" for fonts without box-drawing characters
completion_popup = "as-you-type" # "on-tab": only after Tab, like vim's wildmenu
editor = "" # what :config opens files in ("nvim", "code --wait")
clipboard = "cumulus" # "unnamedplus": plain y, d and p use the system clipboard
# keys.toml: sections motions, normal, canvas, outline, note, visual, move,
# connect, list, card, text (vim keys inside text), insert (typing: one key each,
@@ -518,10 +574,10 @@ question = "question" # a #question node gets the "question" style
```
**Data folder** (`%APPDATA%\cumulus\data`, `~/.local/share/cumulus` on Linux):
`memory.ron` (where you left off in each document, keyed by `uid`, the command
history, recent documents; capital marks come in part 8c), `backups/` and
`locks/`. If a second cumulus opens a document that's already open, it opens
it read-only.
`memory.ron` (where you left off in each document, keyed by `uid`, with its
marks; the capital marks; the command history; recent documents), `backups/`
and `locks/`. If a second cumulus opens a document that's already open, it
opens it read-only.
**Command line**
- `cumulus` opens `./ideas.cum`, or `~/ideas.cum` if the current folder doesn't
@@ -546,7 +602,7 @@ cumulus/
├─ main.rs command-line arguments, then app::run
├─ lib.rs the module list
├─ model/ ids, text, geometry, look, node, portal, link, document, edit, op (edits as
│ data), history, fold
│ data), history, fold, clip (nodes taken out, to paste)
├─ input/ key (vim notation), command (Command enum + names), bindings (default tables),
│ keymap (+ keys.toml), engine (counts, registers, operators), mouse
├─ command/ registry, parse, complete, history
@@ -559,21 +615,25 @@ cumulus/
├─ config/ paths (the config and data folders), settings (config.toml, `:set`), keys_file
│ (keys.toml's template), theme_file (themes laid over the built-in one)
├─ storage/ document_file (RON), write_atomically, names (file names from titles), backup,
│ lock, stamp (noticing changes made outside), memory (memory.ron)
│ lock, stamp (noticing changes made outside), memory (memory.ron), markdown
│ (nodes as Markdown, for the clipboard)
└─ app/ mod.rs (App), input (keys and the mouse, routed), update (carrying actions out),
render, terminal (the main loop, setup, teardown, panic hook), startup (what
`cumulus` opens), documents (the open documents and their files), files
(saving, reloading, locks, where you left off), configure (settings, keys and
themes applied; `:config`), action, mode, buffer, commands
(keys every view shares), calls (`:` commands), text_edit (a node's text being
edited, rapid entry), registers, panes (the split tree), popup (help, menu,
pickers, card, questions); later jumps, portals (cache, backlinks, renames)
edited, rapid entry), registers (text or nodes), clipboard (the system's, for
"+), yank (nodes into registers and back), visual (selections), repeat (`.`),
search (`/`), jumps (each pane's jump list), marks (capital marks, `:marks`),
panes (the split tree), popup (help, menu, pickers, card, questions); later
portals (cache, backlinks, renames)
```
The end-to-end tests are in `app/tests.rs` (with `app/tests/files.rs` and
`app/tests/config.rs`): they copy the sample documents to a temporary folder,
type keys and click on a test terminal, and check the screen, the documents
and the files.
The end-to-end tests are in `app/tests.rs` (with `app/tests/files.rs`,
`app/tests/config.rs` and `app/tests/vim.rs`): they copy the sample
documents to a temporary folder, type keys and click on a test terminal, and
check the screen, the documents and the files.
**Flow:** a terminal event comes in → the key engine turns keys into a typed
`Command` (with its count and register) → the focused view answers it with