diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/config.md | 17 | ||||
| -rw-r--r-- | docs/design.typ | 2 | ||||
| -rw-r--r-- | docs/effects.md | 11 | ||||
| -rw-r--r-- | docs/fs.md | 117 | ||||
| -rw-r--r-- | docs/mvzr-group-backtrack.md | 64 | ||||
| -rw-r--r-- | docs/tags.md | 33 |
6 files changed, 205 insertions, 39 deletions
diff --git a/docs/config.md b/docs/config.md index a9db1467..40d0fb24 100644 --- a/docs/config.md +++ b/docs/config.md @@ -120,7 +120,13 @@ document in a column of its own. See [where new panes go](tags.md#where-new-pane into the left column beside it, whatever it holds; `BootShell replace` closes such a shell there when it is the column's only one and nobody has typed into it (no scrollback, cursor still on the first prompt line), the boot's -placeholder giving its rows to the document. Bare `BootShell` flips it. +placeholder giving its rows to the document. Bare `BootShell` flips it, as +bare `Placement` does: a setting that chooses among words (`on`/`off` +switches, `Placement`, `BootShell`, `Crt`, `Lift`, `Motion`, +`ShaderAnimation`) steps to its +next value when written bare, as its word clicked in a tag does, and a value +it does not take is refused with the values it takes, which `/commands` also +lists. `Verbose` toggles the message-row announcement every builtin makes of its own name before it runs; it is on by default, and the builtins that own the message row themselves (`Msg`) never announce. Turning it off leaves the row to the @@ -545,6 +551,8 @@ Lift auto a shadow on a light page; on a dark one the others recede Lift off InactiveDim 30 the unfocused panes' text fades 30% toward its ground Motion smooth off, crisp, smooth (the default), bouncy or playful +CursorBlink on the cursor blinks, solid while typing and half a second + after, eased at each edge, solid after 10 idle seconds GripWidth 150 the grip's button and the scrollbar under it, percent of the theme's rail_px (12px at a 17px tagline; 50 to 300) ``` @@ -631,6 +639,13 @@ uses the argument as a path as before. Absolute paths and argument-free Restore retain their existing behavior. `Dump` still honors `$PARDES_DUMP` and otherwise writes a timestamped file in the default directory. +A dump keeps each text pane's dot (its selection, or the caret), as acme's +keeps a window's q0 and q1, and a Restore puts it back where the kept view +shows it. It keeps the settings that differ from a fresh session's (`Placement +pardes`, `Verbose off`, a shader), as the root ctl reads them, and a Restore +sets them again; the theme is kept with the layout, and the font stays the +frontend's. REPL bindings are not kept. + A restored terminal comes back live: its recorded output (the last MiB of it) is replayed as history, a dim `── restored history ──` line marks where it ends, and a new shell starts below it in the directory the old one was in, diff --git a/docs/design.typ b/docs/design.typ index a6bf56a3..639884a9 100644 --- a/docs/design.typ +++ b/docs/design.typ @@ -1960,7 +1960,7 @@ p/l/a`). Topbar: `Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Kill` — editable by left click, with keyboard command navigation and Exec/Look gestures. An additional editable tag in each column supplies local New, Tty, Find, -Grep and Joincol commands. ColumnTags hides that row when space is tight; +Grep and Joincol commands; it is always shown, as acme's column tag is. Colors and Crt left it for their leader paths. *Panes.* Terminal: ghostty-vt, 16 MiB scrollback, OSC 133 prompt semantics, diff --git a/docs/effects.md b/docs/effects.md index 3f375b7d..08fd3d88 100644 --- a/docs/effects.md +++ b/docs/effects.md @@ -23,6 +23,7 @@ why. The verdict is the user's. | Crt (bundled post pass) | `Crt 0..3` | kept, rewritten (no barrel) | fx-compare stills, live window | kept at the user's live look | | Ripple, Glitch | — | removed | live window | dropped by the user after a live look | | G1 lift | `Lift shadow\|rim\|auto` (off), `InactiveDim <percent>` | opt-in until the focus-lift default is decided | lift-shots-2 (off, shadow, rim, auto on acme, dusk, forge); frame series + mp4 per Motion flavour | shadow on acme: keep as is. Round 1 dropped glow and surface (surface lowered the focused text's contrast) and made rim a hairline just above the focused tag (light on a dark page, shade on a light one). auto on a dark page recedes the others (InactiveDim 30) | +| G3 cursor | follows `Motion`; `CursorBlink on\|off` (on) | glide on by default through the default flavour (smooth); blink on (decision 6) | .scratch/render/cursor/cursor-flavours-grid.mp4 (and quarter speed), raw/<flavour>/<motion>/ | pending | ## Motion flavours @@ -64,9 +65,13 @@ Which principles each motion uses: spring over the move's length; anticipation (playful). A pane past its mark never takes a neighbour's click, a rising one stays in its box, and a notice past its row stays in its pane's body. -- **Cursor** (G3, next): designed around the same set: glide on the spring, - stretch along the path, an arc on long jumps, the trailing corners as the - secondary action. +- **Cursor** (G3): timing (the flavour's spring, 2.4x its pace: it follows + input), follow-through (bouncy and playful pass the target), squash and + stretch and secondary action (the trailing corners lag by the flavour's + follow, so a jump smears and collapses), arcs (a jump over 8 cells bows by + the flavour's arc), anticipation (playful's wind-up). A step of a cell or + less, or any move in insert mode, lands at once, so typing never trails; + a scroll carries the cursor with the text. Only the focused cursor moves. Notes on G1: the lift and the dim run on one focus spring per pane, at the @@ -92,7 +92,7 @@ Existing Plan9port/v9fs clients need a userspace bridge for QUIC. ``` /README this guide, also src/fs-help.txt -/index one line per pane: serial, kind (text|term|pdf|image), dirty flag, name +/index one line per pane: serial, kind (text|term|pdf|image), dirty flag, name, column serial /status pid, version and pane count /look write a line: a right click on it at the active pane; read: the serials the last look, exec or ctl write touched (made, else acted at) @@ -102,16 +102,44 @@ Existing Plan9port/v9fs clients need a userspace bridge for QUIC. write follow to that open to wait for more /screen rendered screen JSON; frozen per open handle /listeners the session's dial addresses -/focus the serial of the pane with the keyboard; write a serial to give it the keyboard +/focus the serial of the pane with the keyboard; write a serial to give it the keyboard, + which makes its column the active one as a click there would /ctl the settings, one a line as a write takes them; write a setting or a session builtin -/commands every builtin: word, `arg` if it takes one, and `root` or `pane`, the ctl that takes it -/pane/new open it to make a pane; the read answers that pane's serial +/commands every builtin: word, `arg` if it takes one, `root` or `pane` (the ctl that takes it), a setting's values +/layout one line per column, left to right: serial index x width current|notcurrent + empty|full pane-serials...; then active <serial>: the active column, where + pane/new and a look place a pane next (- when there is none) +/tag the workspace tag; > replaces it, >> appends, one line +/col/<n>/tag the tag of the column with serial n, the same way +/col/<n>/ctl write Delcol, Joincol, New or Tty: each acts on that column, as from its tag +/col/<n>/exec write a word: a middle click on it in that column's tag; rmdir col/<n> closes + an empty column (a column with panes is refused, ENOTEMPTY) +/pane/new open it to make a pane; the read answers that pane's serial. A session holds + 64 panes (16 on the board); at that, every route that would open one -- this + open, look, exec, New, Tty -- fails with `no pane slots: 64 max` and an err + record, and look reads back empty /pane/<n>/ name body tag ctl addr dot limit data xdata sel dirty mark scroll errors event look exec, plus pty/{ctl,status,data} on terminals /os/ the host filesystem /src/ the editor's embedded sources, only when built with -Dembed-sources=true ``` +`/layout`, `/tag` and `/col` go past acme, which serves no column files -- +its columns are only where a window sits. They are here so a script can see +where the panes are (`/index`'s last word is each one's column serial) and +edit the tags a person clicks in: a column tag is one line, a newline +written into it a space, and a truncating write (`echo Make > col/3/tag`) +clears it and drops the newline that ends it, as a pane tag's does. A +column is named by its serial, as a pane is: it stays while the column +lives, whatever opens or closes beside it, and is never reused; /layout +gives each column's serial and its index left to right, and the log says +`newcol <serial>` and `delcol <serial>` as columns come and go, and after a +Restore `restoredcol <old> <new>` for each column as `restored` does for +panes. Joincol folds a column into the one on its right, which keeps its own +serial and tag; the joined one's serial is gone (`delcol`). The root ctl +takes no column word: its `Delcol` is refused, pointing at +`col/<serial>/ctl`. + Control messages are split by what they act on, as acme keeps window verbs on a window's ctl and webfs and upas/fs keep session settings on a root ctl. Each builtin declares its scope in src/builtins.zig (`scope = .session`; @@ -119,9 +147,11 @@ every setting is one, the rest act on a pane). `/ctl` takes the session's builtins, one a line, at whichever pane has the keyboard as each runs -- `Newcol`, `Dump`, `Mount name dial`, `Theme ink`, `Verbose off`; `Exit`, which quits the editor as acme's does (it refuses once, naming each pane with -unsaved text, `<name>: Modified (Exit again to discard)`, and a second -`Exit` with nothing edited since quits, throwing that text away; a scratch -under 100 bytes is not asked about; `Restore`, which replaces every pane, +unsaved text, a `+New` scratch of 100 bytes or more too, in one line, +`<name>, <name>: Modified (Exit again to discard)`, and a second `Exit` +with nothing edited since quits, throwing that text away; a scratch or a +command's output under 100 bytes is not asked about, as acme's winclean +asks about no small unnamed window; `Restore`, which replaces every pane, asks the same first -- `Dump` writes `pardes-<date>-<time>.zon` (UTC) in `DumpDir` (default `$XDG_DATA_HOME/pardes`, else `~/.local/share/pardes`) and logs `dump <path>`, and `Restore` with no path takes the last one; a @@ -151,14 +181,18 @@ in a shell running without job control (`set +m`) the job shares the shell's group, so there is none to signal: Kill says `Kill: no job to signal`, and a write of it to `ctl` fails with that; with nothing running it says `Kill: nothing running`; and, of a typed line, only the -foreground job, so of `sleep 30; echo done` typed at a prompt the `echo` -still runs once the `sleep` is stopped -- +foreground job, after which what the rest of the line does is the shell's +affair: of `sleep 30; echo done` typed at a prompt, fish goes on and runs +the `echo`, and bash abandons the line, as each does when a job it waits +on is killed -- and reads every setting there is, one a line, in the words a write of it -takes (`Verbose on`, `WindowOpacity 70`, `PanelSlide off`, `DumpDir` bare -for the default directory, `LocationsConfig ...`), so writing what it reads -back changes nothing; platform and startup facts are `/status`'s and the +takes (`Verbose on`, `WindowOpacity 70`, `PanelSlide off`, `DumpDir +<the directory in effect>`, `LocationsConfig ...`), so writing what it reads +back changes nothing; a setting the frontend cannot show (`Lift`, +`GripWidth` on a terminal) is refused as `Lift is GUI-only`; platform and startup facts are `/status`'s and the Config window's, not settings. A pane's `ctl` takes the builtins that act on -a pane (`Del`, `Save f`, `Collapse`, which folds that pane, `Undo` and +a pane (`Del`, or `Del k`/`Del j` to give its rows to the pane above or +below, `Save f`, `Collapse`, which folds that pane, `Undo` and `Redo`, which step its body through its edits, `Find pat`) beside acme's `get`, `lock` and `unlock`. The column words are pane words too, acting on the column that pane is in: `Delcol`, `DelAbove`, `DelBelow` @@ -193,8 +227,13 @@ performed what it asked for (a save written, a shell started). A click on the same word, or the word written to `exec`, still opens its prompt. `/commands` lists every builtin the registry holds, in registry order, one -a line: its word, `arg` when it takes one, and `root` or `pane` for the ctl -that takes it, e.g. `Newcol root`, `Save arg pane`, `Verbose arg root`. It is +a line: its word, `arg` when it takes one, `root` or `pane` for the ctl +that takes it, and for a setting that chooses among words those words, +comma-joined, e.g. `Newcol root`, `Save arg pane`, `Verbose arg root on,off`, +`Placement arg root acme,pardes`. Such a setting written bare steps to its +next value, so a two-valued one flips (`Crt`, `Placement`, `BootShell`), as +its word clicked in a tag does; a value it does not take is refused with +`bad value in control message; takes ...` naming those it does. It is generated from the registry, so it is always this build's own list. `/focus` reads the serial of the pane with the keyboard, and a serial written @@ -307,13 +346,17 @@ terminal's while a program other than its shell holds the tty -- after Ctrl-D the shell would run the text, so nothing is sent and the pane says so. Still commands, whatever is bound: a word in a tag (so the tag is how to run `make` from that file), `Exec <text>` run by name -(typed, a 2-1 chord onto `Exec`, a `ctl` line) and a command word @`cmd` in +(typed, a 2-1 chord onto `Exec`, a `ctl` line) -- in a `.py` pane bound to +a REPL, `Exec print(1)` runs `print(1)` as a command, never in the REPL -- +and a command word @`cmd` in the text, looked at or clicked (`# @`pytest -x`` in a script). A 9P `exec` is no gesture and is never sent: a script writes to the REPL pane's -`pty/data`, multi-line code as a bracketed paste (`\e[200~<code>\e[201~` -then `\r`, and a second `\r` when its last line is indented: one Enter -leaves a block open, pasted or typed; a middle click sends that second one -itself), since line by line a blank line ends a Python block and Python +`pty/data`, multi-line code as a bracketed paste (`\e[200~<code>\e[201~`, +then `\r` in a write of its own once the REPL has echoed the paste -- +Python 3.13's REPL takes an Enter read with the paste as part of it, even a +one-line one -- and a second `\r` when its last line is indented: one Enter +leaves a block open, pasted or typed; a middle click does all of this +itself, holding the Enter until the REPL answers the paste), since line by line a blank line ends a Python block and Python 3.14's REPL auto-indents each line typed into it. The REPL gets the text wherever it is -- at a `pdb` or `input()` prompt too. Bindings are not dumped, so a Restore leaves none. @@ -344,7 +387,11 @@ selection, and leaves `look` reading back empty. `/pane/<n>/name` reads the pane's file name (a terminal's directory) and writing it renames the buffer; a relative name resolves against the pane's -directory. `body` appends on write and replaces on truncating open. `sel` +directory. `body` appends on write and replaces on truncating open. A +terminal's `body` is its history as plain text, frozen per open, in logical +lines: a row the terminal wrapped is joined back to the row before it (the +wrap is ghostty's, as `pty/run`'s output unwraps), and the last line ends +with a newline. `sel` reads the selected text and writing it replaces the selection. `errors` appends to the directory's `+Errors` pane. Holding `event` open redirects the pane's Look and Exec clicks to that client, and so does a line written to the @@ -372,7 +419,9 @@ for the question the pane asks on its notice band, which the log names as `ask <serial> <what> <choices>` -- `ask 4 del k j` for Del's side from the keyboard (`k` the pane above takes the rows, `j` the one below), `ask 4 repl a b` for which bound REPL takes an exec (a REPL's letter) -- `answer -` -taking it back as Esc does (with no question standing, `answer` is refused), +taking it back as Esc does (a choice the question does not offer is refused, +naming those it does, and the question stands; with no question standing, +`answer` is refused), and acme's `lock` and `unlock` (editors/acme/xfid.c:603-611), for an edit of several writes to `addr` and `data` that another client must not land in the middle of. As in acme the lock binds only the clients that take @@ -496,9 +545,13 @@ then the text you may edit. A write appends to that text, newlines included, and a tag with more than one line takes a row per line on screen; truncating `tag` clears it, as acme's `cleartag` does -- the default words (`Del`, `Put` and the rest) with it, since they are that text until -you edit it, so `echo Make > tag` leaves only `Make`; append with `>>` to -keep them, with `printf ' Make' >> tag`: `echo` ends its word with a -newline, which starts a new line of the tag. The clearing is an edit of the tag like a typed one and its undo +you edit it, so `echo Make > tag` leaves only `Make` -- a truncating write +drops the one newline that ends what it wrote, which would draw an empty +row, and keeps any other; append with `>>` to +keep them, with `printf ' Make' >> tag`. The leading blank is needed: the +tag reads back with no blank after its last word, so `printf Make >> tag` +glues `Make` onto it; and `echo ' Make' >> tag` ends with a newline, which +starts a new line of the tag. The clearing is an edit of the tag like a typed one and its undo history is kept: `u` in the tag brings back the text it cleared, words included. @@ -515,16 +568,21 @@ entries carry no sizes, and neither does `/screen`, which has no length until an open renders its frame; stat the entry. `/log` is one ring (64 KiB) that records whether or not anyone reads it: -`new`, `del`, `rename` (a terminal's too, as its shell changes directory, +`new`, `del`, `rename` (a terminal's too, as its shell changes directory -- +a new terminal is `new N /` until its shell says where it is, then renamed; +it runs `ls` once at its first prompt, a greeting that shows the directory -- since a terminal is named by its directory), `exit <serial> <N>` before the `del` of a terminal whose shell exited by itself, `ask <serial> <what> <choices>` when a pane asks a question (answered by `answer` on its ctl), and `save <serial> <name>`, `dump <path>` when a Dump is written and `restore <path>` in a Restore's new log after its panes' `new`s, then `restored <old> <new>` for each pane, -mapping the serial it had to the one it has now, and `msg <serial|-> <text>` +mapping the serial it had to the one it has now, and `restoredcol <old> +<new>` for each column, and `msg <serial|-> <text>` for every line the editor says, repeats included (with `verbose` on, that -includes each builtin announcing itself as it runs), and `err <serial|-> +includes each builtin announcing itself as it runs, on purpose: the log says +which ran; its serial is the pane it ran at, `-` when the keyboard was on a +column or workspace tag), and `err <serial|-> <file>: <why>` for every write or truncation the tree refused or that failed -- through a mount a shell sees only the errno its kernel mapped the reply to, usually `Invalid argument`, and this is the reason (`err 3 addr: @@ -547,7 +605,8 @@ Control characters in a record become spaces, so a record is one line. (An `event` record is not: acme's `<origin><action><q0> <q1> <flag> <n> <text>\n`, whose text may hold newlines. The origin is `E` (a 9P write to body or tag), `F` (other files, the editor's own lines), `K` (the keyboard) or `M` (the mouse); the -action's case says where: `x`/`l` a click executed or looked at in the tag, +action's case says where: `x`/`l` a click executed or looked at in the tag +(its offsets count the tag's whole text, path included, as `tag` reads), `X`/`L` in the body, `I`/`D` text put in or taken out of the body, `i`/`d` of the tag. Read `n` bytes of the text, not up to a newline -- bytes here, where acme counts runes. Every offset and count pardes serves is in bytes, `#n` and `q0`/`q1` too; the event count diff --git a/docs/mvzr-group-backtrack.md b/docs/mvzr-group-backtrack.md new file mode 100644 index 00000000..d074af62 --- /dev/null +++ b/docs/mvzr-group-backtrack.md @@ -0,0 +1,64 @@ +# mvzr: a repeat inside a group never gives back to what follows the group + +A report for upstream (https://github.com/mnemnion/mvzr), against trunk +3efcfe3 (0.3.9). pardes patches its fetched copy at build time (build.zig) +until it is fixed there. + +## What happens + +```zig +const mvzr = @import("mvzr"); +// a+ab matches aab; the same with the repeat in a group does not +std.debug.assert(mvzr.compile("a+ab").?.match("aab") != null); +std.debug.assert(mvzr.compile("(a+)ab").?.match("aab") == null); // expected a match +``` + +The same for `(a*)ab` on `aab`, `(.+)_area` and `([a-z_]+)_area` on +`the_total_area`. `\w+_area` and `(\w)+_area` match, as does +`([a-z]+)_area` (whose class cannot eat the `_`, so no giving back is +needed). + +## Why + +`hasAlt` returns true when its scan reaches the group's own closing +`.right` at depth 0: + +```zig +.right => { + if (pump == 0) + return true // the group's own `)`, not an alternative + else + pump -= 1; +}, +``` + +so every group with more pattern after it goes down `matchGroup`'s +alternatives branch. That branch takes each alternative's single match (a +greedy repeat's longest) and, when the pattern after the group fails from +there, moves on to the next alternative; it never tries a shorter match of +the same alternative. The alt-free branch has the same shape. + +## A fix + +Where the pattern after the group fails from an alternative's match, try +that alternative's shorter matches, longest first, before the next +alternative. pardes's patch, in `matchGroup`'s loop: + +```zig +} else { // Try our next pattern, if any. + var e = m1.i; + while (e > i) { + e -= 1; + const shorter = matchAlt(remaining_patt, sets, haystack[0..e], i) orelse continue; + if (shorter.i != e) continue; + if (matchPattern(next_patt, sets, haystack, e)) |whole| return whole; + } + remaining_patt = m1.j; +} +``` + +Matching the alternative against `haystack[0..e]` makes `$` and `\b` see a +string end at `e`, so a group ending in either can accept a cut the full +string would not; a continuation-passing matcher (each repeat trying the +rest of the whole pattern) would be the full fix. `hasAlt` returning false +at the group's own `)` is worth fixing too. diff --git a/docs/tags.md b/docs/tags.md index aa680c48..bfca4f0e 100644 --- a/docs/tags.md +++ b/docs/tags.md @@ -30,6 +30,15 @@ edge will land, beside the rule as a border drag's rail does. Only columns between the old and new positions shift. Each column keeps its width, panes and command text. +Two panes in a column are resized as acme's windows are, by the grip: drag +a pane's grip up or down its own column and its top follows, the pane above +taking or giving the rows, either one down to its tag alone (acme's +coldragwin keeps a window its tag line); drop it in another column and the +pane moves there. In the GUI the 2 px rule between two panes also drags +their seam. No row of text is a handle: a pane's last row takes a click like +any other, and a terminal, which draws no rule, resizes by the grip. A +column's right edge still drags its width. + When Look opens the first document and its source column is too narrow for a new column, it splits below the originating pane, just like `Tty`, instead of inserting at the top of the leftmost column. The originating terminal is @@ -53,14 +62,23 @@ if every command ran; an error says why in acme's words and changes nothing. `p` and `=` print to the directory's `+Errors`. Left out: the file commands `b B D e r w f X Y`, the pipes `< | >`, and `\1`-`\9` in `s`, since mvzr keeps no submatches; acme applies changes that come out of -sequence with a warning, pardes refuses the Edit. +sequence with a warning, pardes refuses the Edit. In `s`, `&` in the +replacement is the matched text (`\&` a plain `&`); in `c`, `a` and `i` an +`&` is only an `&`, as in sam. A pattern that finds nothing says so with the +pattern (`no match for regexp /nomatch/`), and an `s` on an empty dot says +`no substitution: dot is empty`. `Undo` and `Redo` are acme's: typed or clicked in a pane's tag, or written to its `ctl`, they step the body back and forward through its edits, as the `u` and `U` keys do. They are not in the default tags. `Del` closes a pane and gives its rows to one neighbor; the rest of the -column keeps its heights. `Del k` (or `DelAbove`) gives them to the nearest +column keeps its heights. A pane with unsaved text is refused once, as +acme's Del warns (exec.c del, wind.c winclean): `<name>: Modified (Del again +to discard)`, and the same `Del` again, nothing edited since, closes it and +throws the text away; `Delcol` refuses a column holding such a pane the same +way (exec.c delcol). Each word warns on its own, from a key, a tag, `exec` +or a ctl, where the refusal fails the write with EIO. `Del k` (or `DelAbove`) gives them to the nearest expanded pane above, `Del j` (or `DelBelow`) to the one below, each falling back to the other side. Bare `Del` from the keyboard (`SPC d`, or Enter on the word) on a pane with expanded panes both above and below asks on the @@ -74,8 +92,10 @@ A tag is a text like a pane's body: pane tags, column tags and the workspace tag all edit with the body's own normal and insert modes, undo included. A pane's tag may hold more than one line, and a line wider than the pane wraps onto more rows, as acme's tag does: it takes a row per shown -line, up to eight, and leaves its body at least one row. A word the wrap -breaks across rows is still one word to a click. A collapsed pane shows +line, up to eight, and leaves its body at least one row. A line exactly as +wide as the pane stays one row, its caret at the tag's edge past the last +character, as acme's tick is (in a terminal, on the last cell). A word the +wrap breaks across rows is still one word to a click. A collapsed pane shows only the first row. A pane tag starts expanded, showing all its rows, and can be collapsed to @@ -183,7 +203,10 @@ the shared bold, underline, and strikethrough attributes, including diagnostic underlines and the optional `SyntaxBold` keyword weight. Unsaved text shows on the pane's grip button, as acme's modbutton does, -not in the tag: the tag names the file and nothing more. The `dirty` file +not in the tag: the tag names the file and nothing more. In a terminal the +grip is two cells: the second is `*` while the pane holds unsaved text, and +the first is the pane's mode: blank in normal mode, `^` in insert, `$` when +the keyboard goes to a terminal's program (tty mode). The `dirty` file and `index`'s flag say the same to a script. ## Empty columns |
