| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
| |
Problem:
Redundant code led to bugs like 2323746bfe4af534979249b051e7ada11c63b389
Solution:
Append the payload in atom_from_frame().
|
| |
|
|
|
|
|
|
|
|
| |
Problem:
A Visual-mode mapping that reads input (`getchar()`) does not include
that input when cascading to other cursors (they only see ESC). OTOH,
Normal-mode mappings do replay the input.
Solution:
Append the payload to the Visual session's Lua-mapping subatom, and to the
session-ending command (`:<C-u>…` mapping).
|
| |
|
|
|
|
|
|
|
|
|
| |
- `{Visual}Q` / `{Visual}zqj`: for linewise motion ("j"), every line
gets a cursor.
- `zq`: handle 3-key motions (`g'a`), don't scroll the view, preserve
the search direction, update existing cursors `gv` target.
- Disallow `zq` in macros.
- Abort `zq` on non-motion ("zqx").
- Classify `g'x` classify like `'x`.
- Classify `'(` `'{` etc. as `type=motion` (they are cursor-relative).
- Perf: skip save/restore of marks when cascading motions ('follow').
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`nvim_mcursor` is not fully baked, and not strongly motivated,
currently. The main programmatic interface for cursors is the
"nvim.multicursor" extmarks namespace.
Solution:
Drop `nvim_mcursor`. Add an internal hook so that multicursor can react
when extmarks are added to the "nvim.multicursor" namespace.
TODO:
If extmarks/namespaces are clumsy to work with, we should _fix that_
rather than adding wrappers.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- `[count]Q` and `{Visual}Q` are implemented as ad hoc utils, because
`Q` is not an "operator".
- `Q` always "toggles", it is not "idempotent".
- Various *cursor-relative* marks (`'.`, `'[`) are not saved/restored
per-cursor, so they don't work during cascade.
- Insert cascade clobbers the primary's `'^` and changelist.
Solution:
- Introduce the `zq{motion}` operator, which places cursors at every
reachable `{motion}`.
- `{Visual}Q` is now an alias to `{Visual}zqj`.
- Introduce `1Q` and `2Q` for idempotent cursor placement.
- Drop `[count]Q`, it's redundant with `zqgn`.
- Classify cursor-relative marks as `type=motion` instead of
`type=jump`. Save/restore per-cursor during cascade.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- Multicursor does not cascade a `gv`-entered insert-session (`gvc`).
- CmdAtom of a `gv`-entered insert-session (e.g. `gvcX<Esc>`) has
`keys="1vcX<Esc>"` (redo's fallback), not the typed keys. And is not
emitted at all if multicursor is inactive.
Analysis:
Redo can't contain "gv", so it does the "1v" fallback, and the
insert-cascade replays redo.
Solution:
Replay "gv" + op at each cursor: store the typed selection in
`InsSession.vsel`; it supplants redo's "1v" in the emitted atom and in
the insert-cascade replay. A cursor without a Visual area skips
a "gv"-based replay (else its keys would run as Normal-mode: "gvrx" =>
"rx").
Redo behavior is unchanged ("1v" fallback): "gv" is absolute, "." acts
at the cursor.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
gcc and Coverity report potential `size + len `overflow kv_concat_len().
CID 656305: Null pointer dereferences (FORWARD_NULL)
/src/nvim/input_cmdatom.c: 474 in atom_push_raw()
468 atom_captures++;
469 }
470 if (collect) {
471 composite.queued = true;
472 }
473 kv_push(vatom.atoms, *atom);
>>> CID 656305: Null pointer dereferences (FORWARD_NULL)
>>> Dereferencing null pointer "cur_frame".
474 vatom.frame = cur_frame->id;
475 return;
476 }
477 atom_captures++;
478 if (atom->type == kAVisual && kv_size(atom->atoms) > 0) {
479 // The completing operator is the only subatom that could have edited.
CID 656304: Memory - illegal accesses (OVERRUN)
/src/nvim/input.c: 718 in sb_add_spec()
712 /// bytes. Unlike sb_add_lit(), no CTRL-V: for text read raw (<Cmd>), not typed into a cmdline.
713 void sb_add_spec(StringBuilder *buf, const char *s)
714 {
715 while (*s != NUL) {
716 if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
717 // Insert special key literally.
>>> CID 656304: Memory - illegal accesses (OVERRUN)
>>> Overrunning dynamic array "buf->items" at offset corresponding to index variable "buf->size" through dereference in call to "memcpy".
718 kv_concat_len(*buf, s, 3);
719 s += 3;
720 } else {
721 sb_add_char(buf, mb_cptr2char_adv(&s));
722 }
723 }
Solution:
Both are false positives; assert or check to avoid the warnings.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- The primary-overlapping cursor is synced to primary only after
cascade, so it may display at a shifted position (e.g. after "rx")
during cascade.
- LHS-replay edits apply twice if there is a primary-overlapping cursor.
Solution:
- Sync the primary-overlapping cursor _before_ replay. This ensures the
mark is correct _during_ the cascade.
- Drop the post-cascade sync; this is safe because the overlapping
mark and the primary-tracking mark are both right-gravity, so they
get shifted the same way.
- Set `origin` on LHS-replay atoms, so the cascade can compare it.
Bonus: a buffer-switching mapping no longer LHS-replays if it ends up in
a different buffer.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Insert-mode `<c-r>x` stuffs the literal text, so multicursor cascade
puts the same (primary) text at each cursor.
Note: `<c-r>-` happens to work because `insert_reg()` special-cases it.
Solution:
Update `insert_reg` so that it preps redo with `<c-r>x` instead of the
literal text. Update `atom_key_class` so that CTRL-R flushes the
insert-span.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Jumps (intentionally) don't cascade, thus they don't queue atoms. But in
follow-mode, a mapping that moves the cursor but queues no atoms,
LHS-cascades (this is a general fallback for the case where `keys`
cannot be resolved). Thus `nnoremap <Down> ]C` replays `]C` at each
cursor, which causes overlapping cursors, which are then merged/deduped.
Solution:
If a composite contains a "jump", set `composite.queued` to disable
LHS-replay.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A primary-overlapping cursor is deduped before an edit. But if
a Visual/Insert action moves the primary first, the cursor is not
skipped (e.g. `vipQva)c` replays at the primary-overlapping cursor).
Solution:
Skip _replay_ of a primary-overlapping cursor, but don't _dedupe_ it.
Keep its position synced with the primary.
Sample the primary-overlapping cursor (`CmdOrigin.mcursor`) at the
action start (first root frame, or Visual session-start), instead of
current frame start.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Incoherent classification of motions vs jumps.
Solution:
Define "motion" as cursor-relative, and "jump" as "absolute".
- Classify G/gg/H/L/M/go/N% as `CmdAtom.type=jump`.
- Classify `*`/`#` as motions.
Behavior changes:
- `*` in follow-mode now moves each cursor to the next occurrence of its
own word.
- Note: the "last search pattern" is not stored per-cursor, so `n`
still searches for the primary's search pattern.
- G/gg/H/M/L in follow-mode do not cascade (would converge/dedupe all cursors).
- If you want to force cursors to converge for some reason, use `9999j`.
- Also affects mappings that mix an absolute move with an edit. E.g.
mapping `nnoremap X ggdd` only replays `dd` per-cursor now. This is
how marks already behaved.
|
| |
|
|
|
|
|
|
|
|
| |
Problem:
In follow-mode, a mapping that moves the cursor but doesn't queue an
atom, cascades LHS. Undo moves the cursor and (intentionally) does not
queue an atom, so `nnoremap u u` replays undo per-cursor.
Solution:
- Set `composite.queued`, to skip LHS-cascade.
- Also treat "U" (line-undo) as a "global op".
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
A cursor overlapping the primary is deduped by comparing positions at
the clock edge, i.e. after the primary edit executed. A "replace" at the
cursor cell ("rx", "~", "g~iw") shifts the overlapping extmark, so it no
longer matches and gets its own replay. E.g. "vipQg~~" applies twice to
the 2nd line, which inverts the effect.
Solution:
Decide "overlaps the primary" at cmd start, before the edit.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- A mapping that edits the buffer during a Visual selection applies only
at the primary cursor. The edit "voids" the pending Visual-session,
because `mc_vsel_refresh` only previews the selection per-cursor and
must not edit.
- A cursor overlapping the primary applies an edit twice, if the
command moves the primary before cascade ("yiwp").
Solution:
Cascade a Visual session's edits span-by-span, like an insert-session.
- Each span is prefixed with "gv" so per-cursor reselect works.
- Void only if no subatom was captured.
- Dedupe cursors against the primary's anchor at cmd start, not its
position after.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A command's `:norm` children are captured as Visual subatoms:
`v<Cmd>normal! e<CR>d` is "decomposed" to `ved`. That is a cosmetic,
nice-to-have feature, which greatly complicates the cmdatom logic:
it must detect "lossy" cases (API moves, "gv" on marks the command
set, a session ended and restarted).
Solution:
Drop the nice-to-have.
- A command is captured as itself, never its `:norm` children.
- While a child frame runs, a pending Visual session is kept open until
the enclosing frame resolves.
- `:`/`<Cmd>` no longer decline to be a Visual subatom.
- Stuffed keys ("." replay, "x" => "dl") and K_EVENT children (RPC,
timer), are still captured as their translation.
- `mc_add()` discards any pending Visual session, so `{Visual}Q` does
not cascade recursively to the cursors it just created.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- A captured Lua mapping (`K_LUA`+id) whose id ends in "0" replays the
wrong mapping: `atom_from_cmdline()` escapes the id like a typed
cmdline (trailing "0" => CTRL-V "048"), but the id is read raw. E.g.
treesitter `an` (id 60) replays `gc` (id 6), so `van` does not
preview/cascade at mcursors.
- Same for `<Cmd>…` ending in "0" or "^" (`<Cmd>normal! 0<CR>`).
- `c` + Lua/":" textobj (`canX`) does not insert-cascade.
Solution:
- Only CTRL-V-escape ":", "/" cmdline; append `<Cmd>` and Lua
mapping-ids "raw" (`sb_add_spec()`).
- `kVInsMotion`: an insert entered by Ex/Lua motion re-executes the
motion, unless it read input.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A Visual selection produced by a Lua mapping is captured as the keys it
fed (`:norm!`), so non-primary cursors get the wrong selection.
Examples:
- API cursor-moves around a fed "o" are lost.
- `setpos("'<")` + "gv" reselects the primary's marks (treesitter `an`).
- A mapping that feeds an absolute selection (`:norm! <Esc>`, then
`norm! 3GV6G`).
Solution:
Classify fed keys as `lossy` if their produced selection differs from
the selection at mapping-end, or fed-key-start: the mapping moved it by
other means (API). Same idea as `composite.lossy` LHS-replay.
Now the captured Visual session contains the mapping's own cmd
(`<Cmd>…`, `:…`, or a Lua mapping-id) as one subatom, instead of the
keys it fed. E.g. `v…<K_LUA id>` instead of `v…V…G`.
Also affects dot-repeat (".").
A `:norm!` that ends a typed selection keeps the Visual session
(`vatom`) open (instead of the usual `atom_visual_reset()` plus
emit/stage), so the enclosing user frame (the mapping's `<Cmd>`/Lua, or
a typed `:norm!`) can complete or replace it.
- A child frame re-entering Visual does not restart it, so a later
`norm! V` continues that session instead of a new `kVatomFed` one.
- At end of the user (toplevel) frame, the fed subatoms are dropped in
favor of the mapping's own atom: as a subatom if Visual is active,
else as the session's closing suffix.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`nvim_feedkeys('gj', 'm')` (remap, but not "t"=typed) emits a CmdAtom
for the mapping's insert session. Same for `feedkeys('ihello<Esc>', '')`.
Keys fed without "t" land in `tb_maplen` ("mapped prefix"), just like
a mapping's expansion, and `atom_is_user_input()` treats that as user
input.
Solution:
Narrow the `atom_is_user_input` condition. A mapping resolved from typed
keys always has a composite by the time its commands run, so
`KeyTyped || (user cmd && composite active)` is sufficient.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- `gv` reselects only primary's region, it doesn't have per-cursor
behavior. '< '> are buffer-global and the sandbox restores the
primary's marks. The Visual atom containing `gv` was voided anyway, so
it never cascaded.
- Same for change-marks (`'[ '] '.`): they are stale after edits from
other cursors, not managed per-cursor.
- `McSandbox` saves "editor state", which should be owned by the
`Context` module.
Solution:
Lift more state from `McSandbox` into `Context`. `kCtxCursor` (position,
curswant) and `kCtxMarks` (Visual area, change-marks), are owned by
`Context` now, as an "in-memory" special-case (TODO: support it for the
"serialized" case?).
- Classify a `gv` selection as replayable. Manage its state per-cursor.
Dot-repeat "." after `gv` behaves like Vim (the "1v" fallback).
- Save/restore change-marks per-cursor. The primary's change-marks are
shifted by edits from other cursors during the cascade.
`McSandbox` still contains (for now) the view and replay isolation
(typeahead/mode, redobuff, search patterns, the active selection).
Bonus:
- `visualmode()` is per cursor.
- A cursor's `curswant` follows its column when other cursors' edits
shift it, instead of the column captured after its own replay.
|
| |
|
|
|
|
|
|
|
| |
Problem:
An insert-session (`mc_ins_cascade_start`) saves a preliminary
"covering" undo-entry, to cover all cursors. If the insert-session exits
without changes, that no-op entry remains, requiring an extra "u".
Solution:
Drop the entry at insert-session end if there were no changes.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A mapping that toggles follow-mode before/after an API cursor-move
cascades the move if follow-mode was on from the start.
Why: Follow-mode is sampled only at command start, and the API move has
no CmdFrame of its own.
Solution:
Record follow-mode at the time of the move, not the start of the frame.
Update `CmdFrame.follow` on all "undecided" parent frames. A frame that
already moved keeps its old value (not retroactive).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`inoremap <Esc> <Esc>l` starts in insert-mode and ends in normal-mode
("l"). Its normal-mode "l" is not captured as a CmdAtom (and thus
doesn't cascade to multicursors in follow-mode).
Analysis:
CmdFrames only delineate in Normal/Visual/Op-pending (i.e. the
`MODE_NORMAL` modes), so a mapping resolved in Insert, Cmdline or
Terminal mode does not start a `composite`, then the mapping "tail" is
lost.
Solution:
Start a "deferred" `composite` if state is not `MODE_NORMAL`, instead of
skipping it entirely.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
- `curcmd` has the lifetime of the root CmdFrame, but lives in its own
container with a reset (atom_redo_reset) that must mirror "toplevel
user command".
- `curcmd.follow` is also the wrong scope: the clock-edge is deferred
across a mapping's keys, each a toplevel command, so
`nnoremap <F1> 1q=j2q=` runs "j" in follow-mode, yet the edge reads
the trailing "2q=" and does not cascade the queued motion.
Solution:
- Eliminate `curcmd`. Use the root `CmdFrame` as the source of truth.
- Record follow-at-first-motion on `composite`, which spans exactly the
frames the deferred clock-edge closes.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
The clock-edge that decides whether queued motions cascade, reads the
global `mc_follow_motion` at the end, which may differ since the command
started/executed.
Examples:
- A mapping like `2q=gg1q=` (off, move, on) cascades the "gg" and merges
the cursors, because follow=on when the clock-edge resolves stuff.
- `{Visual}Q` after a mapping enables follow at the end, and the
clock-edge mistakenly cascades the mapping's keys.
Solution:
Save `CmdFrame.follow` in `curcmd`, and pass it to `mc_clock_edge()`.
Innermost-frame wins, so `q=` performed by a subatom/subcmd wins.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
In follow-mode, a motion that fails at the primary cursor (e.g. "j" at
EOB) still cascades to other cursors, so they move while the primary
does not, which causes them to be deduplicated...
Also affects Visual sessions: "Vjd" at EOB deletes 1 line at the primary
but (potentially) 2 lines at every multicursor.
Solution:
If a motion "beeps", don't replay it.
"3w" near the end still replays (it moved), as does "0" at column
0 (does not beep). This feels like a usable and intuitive compromise.
|
| |
|
|
|
|
|
|
|
| |
Problem:
A Visual-mode mapping that creates cursors (`xmap I Q0i`) replays its
commands only if cursors already existed when it started.
Solution:
Always collect `composite`. Consumers are still decided per frame
(`CmdFrame.consumers`) and at emit/cascade.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A Visual-mode mapping that leaves Visual mode ("xmap I Q0i") does not
replay its motions/edits at the extra cursors, though an insert session
it starts does.
atom_map_start() skips while Visual is active, so the mapping has no
`composite`, and atom_capturable() then rejects its commands.
Solution:
Open the `composite` for Visual-mode mappings too.
Bonus: the mapping atom is now reported as:
before: type=visual, keys="viwcFOO<Esc>"
after: type=mapping, keys="viwc<Esc>iFOO<Esc>", lhs=",c"
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
A Visual selection opened programmatically (`:normal! viw`) is not
previewed at the other cursors, and a user-typed operator completing it
does not cascade.
":normal" keys run as child frames, which are never user input, so the
session they build was classified "fed" once and never revisited.
Solution:
Recalculate the session kind (`vatom.state`) on every frame. Record
`vatom.frame` (the last frame to add to the session) so an enclosing
frame does not reset or recapture it.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
Input (`getchar()`, `input()`, …) consumed by an expr mapping EVALUATION
can leak into `CmdAtom.keys`, depending on count placement ("2dse" vs
"d2se").
Solution:
Skip payload capture while `expr_map_lock` is active.
- Input read during expr EVALUATION is recorded in `CmdAtom.lhs`.
- Only input read by mapping EXECUTION is appended to `CmdAtom.keys`.
|
| |
|
|
|
|
|
|
| |
Problem:
A non-edit mapping that moves the cursor by API (Lua,
`:call nvim_win_set_cursor()`, …) is not cascaded.
Solution:
Fallback to LHS-replay if the cursor moved and follow-mode is enabled.
|
| |
|
|
|
|
|
|
|
|
| |
Problem:
"v{motion}<Esc>" moves the primary cursor to the selection end, but the
other cursors snap back to their anchors unless follow-mode ("q=") is
on. The selection preview showed them at the selection end.
Solution:
Replay the abandoned selection at every cursor, not only in follow-mode.
An abandoned typed selection now always emits a "visual" CmdAtom.
|
| |
|
|
|
|
|
|
|
|
| |
Problem:
Insert entered by a Visual-mode operator mapping (`xnoremap c c`) does
not live-mirror at cursors.
Solution:
Decide `typed` from the Visual session's own provenance (`vatom`, which
spans the selection and knows whether it was user input) instead of only
negating it.
|
| |
|
|
|
|
|
|
|
| |
Problem:
With follow-mode ("q="), an 'operatorfunc' that only moves the cursor
does not cascade.
Solution:
Update the `effect` condition to include cursor-moves if follow-mode is
active.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
An atom queued in buffer A cascades on B's cursors if the mapping ends
in B ("nnoremap X x:bnext<CR>").
Solution:
Check the atom's origin buffer (`CmdAtom.origin.buf`).
Note: This does not preclude mappings etc from doing work in temporary
throwaway buffers, as long as they return to the origin buffer.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Other (squashed) commits:
fix(tui): emit ui_send output atomically with the frame
Problem:
tui_ui_send() writes directly to the TTY, bypassing the output buffer.
Sequences sent via nvim_ui_send() (e.g. kitty multiple-cursors, or
visual-dot-repeat) always arrive in a separate TTY write from the frame
they were computed for. This manifests as "tearing", or e.g. in the case
of multicursor the terminal renders text with stale cursor overlays.
Solution:
- tui_ui_send(): while a frame is being assembled (pending invalid
regions or buffered output), buffer instead of writing directly.
- Out-of-frame sends (tty queries, clear-on-disable) still write
immediately.
- mcursor.lua: emit the terminal-cursor update at the end of the redraw
cycle (`on_end`, when screen positions are final) instead of
vim.schedule().
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
Replaying a deleted Lua mapping, may call an arbitrary function.
RHS of a Lua mapping embeds its LuaRef (`<K_LUA><ref><CR>`). The raw
keys may outlive the ref (redobuff ".", CmdAtom `keys`). If the mapping
is deleted, replaying it either (1) dereferences a freed registry slot,
or (2) calls whatever callback reused the slot (autocmd, timer, other
mapping).
Solution:
Assign a monotonic (never recycled) id to Lua mappings and encode the
mapping keys as `<K_LUA><id><CR>`.
Note: in the case of Vimscript, a deleted function raises E117, but if
the function is redefined with the same name, the mapping will find it.
Alternatives?:
- Globally ensure `LuaRef` ids are not recycled.
- Problem: could exhaust `int` in a long-lived Nvim session? Also,
difficult to impl bc the "recycling" is done by `luaL_ref` itself.
ref: 5ac2e47acc999472042df4f10f8f7b5ffa72ba3e
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
An operator completed by a Lua `:omap` textobject emits
`CmdAtom.type="mapping"` (lhs-only, no keys) instead of `type="operator"`.
Analysis:
`atom_redo_set()` declined K_LUA, though the prepped redo
("op" + K_LUA + id + CR) is exactly what "." replays. A no-edit "g@"
emits nothing at all.
Solution:
- `atom_redo_set`: don't decline K_LUA; the redo route now captures the
operator atom.
- `atom_capture_cmd`: don't early-return if the frame has prepped redo.
- op_function(): save/restore redobuff when invoking 'operatorfunc',
like `call_user_func()` does for Vimscript. (Else the Lua callback
may clobber the prepped "g@" redo / dot-repeat.)
fix #41482
TODO:
- async Lua (timer/vim.schedule) can still clobber the pending dot-repeat...
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
The Vim "stuff" concept breaks the ability to reason about the call
stack and thus the boundaries of a `CmdAtom`: a stuffed translation ("x"
=> "dl") defers to the main loop. This "continuation" must be modeled in
`CmdAtom`, by checking global flags at undefined times, during undefined
circumstances.
Solution:
- After a stuffed "translation", eagerly execute the stuff buffer
(`exec_stuffed()`).
- Delete the CmdAtom "continuation" junk.
Note:
- op_colon runs its cmdline "nested", but that's fine because operators
already nest interactive sessions there (op_change runs edit()), and
the cmdline is frameless so the operator's frame can own/capture it.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|