⏺ :edit is wired — clippy -D warnings, 10 tests, fmt, and full workspace all #12
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "edit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
green.
How it works:
and runs edit_session: writes the buffer (session.source()) to a temp
plgr-session-*.pl, suspends the TUI (leave raw mode + alternate screen, show
cursor), runs $EDITOR (default vi, parsed with shlex so EDITOR="code --wait"
and quoted paths work) on that file, then resumes the TUI and reloads.
load_source (validate-whole → split → ordered entries) on a scratch session
first; only on success does it replace the live buffer, report edited. (N
clause(s)), and recompile. A syntactically broken edit is rejected with edit
rejected (buffer unchanged): … and the old buffer + binary stay intact.
app.note, not by corrupting the restored TUI.
This is your redefine/delete path: :edit, change or remove clauses (e.g. drop
the stray test. fact, fix a rule), save and quit, and it recompiles the new
buffer. :edit is now advertised in :help.
One caveat: the suspend/resume and editor handoff are inherently interactive,
so they're not in the automated tests (the reload logic underneath is
load_source, which is). Worth a hands-on spin: type a couple of clauses, :edit,
modify one, :wq, then :list and a query to confirm the recompile took — and
try saving a deliberately broken clause to see the buffer survive.
Review —
:edit($EDITOR handoff)The architecture is right.
Appsets a flag, the loop owner (main) does the terminal-suspend dance because it owns theTerminal.apply_editbuilds a scratchSession::default(), runsload_sourceon it, and only replacesself.session = nexton success — that's exactly how to get atomic-on-failure without duplicating the load logic. The.plsuffix for ftplugin and the shlex parse forEDITOR="code --wait"are the right small touches.Issues below, ordered by impact.
Real bugs
1. Editor exit status is ignored.
main.rs:130-135always reads the temp file and callsapply_editwhenever the editor process spawned, regardless of how it exited. So:q!in vim,:cq, Ctrl-C, or any abnormal exit still reloads the (unchanged) file and printsedited. (N clause(s))— misleading at best. If a future editor wrapper exits non-zero after partially corrupting the file, this silently reloads garbage. Fix: checkstatus.success()and skipapply_editon non-success, loggingedit aborted (editor exited N)instead.2. Reload always recompiles, even when nothing changed. A user who runs
:edit, scrolls around, and saves without changing anything still triggers a recompile and getsedited. (N clause(s)). Easy guard: stash the originalapp.session.source()before writing the temp file and compare against the read-back content; if equal, skipapply_editentirely (or call it with ano_change_okpath that doesn't recompile). This composes with #1 — both want to short-circuit before the recompile.Small observations
3.
$VISUALis not honored. Convention is$VISUAL→$EDITOR→vi. A user withEDITOR=vibutVISUAL=nvim(extremely common) would get vi here. One line inmain.rs:118:4.
apply_edithas no unit tests. The PR description correctly notes the suspend/resume is inherently interactive, butapply_edititself is pure:&mut App+&str→ state change. Two tests pin the contract this PR is built around:apply_edit("garbage(")→ buffer unchanged, "edit rejected" logged.apply_edit("foo. bar.")→ buffer replaced with 2 clauses, "edited" logged.These would prevent a future refactor from quietly breaking the atomic-reject guarantee, which is the whole point of the scratch-session pattern.
5.
App::noteisApp::log.app.rs:55-58— the comment justifies the rename ("on behalf of the host"), but the body isself.log(msg)verbatim. If the intent is purely documentary (host vs. self), fine, but it reads as cruft. Either inline the call site (app.log(...)) or havenoteactually do something different (timestamped? prefixed?).6. Empty-buffer edits recompile an empty program. A user who
:%dand saves getsapply_edit("\n")→load_sourcereturnsOk(0)→session.clausesis empty →recompile()fires with empty source. Whether the engine accepts an empty program is engine-dependent; if it does, you've replaced a useful session with an empty binary. Worth either rejecting withedit rejected: no clausesor, more permissively, accepting it as:reset(probably surprising). Minor — the cure (revert via:editagain) is in the user's hands.7. Suspend uses
let _ =for errors, resume uses?.main.rs:111-113vs123-126. The asymmetry is actually correct (suspend errors are non-fatal; resume errors must abort), but it's worth a one-line comment to keep a future reader from "fixing" the inconsistency.What's good
Appdoesn't know what aTerminalis.should_editis the boundary;mainowns the rest. Mirrors how:loadand:savedon't touch the terminal either.Session. Reusingload_source's validate-whole-then-append semantics instead of re-implementing them is exactly the right move. The PR #10 unification keeps paying off.shlexfor$EDITOR. HandlesEDITOR="code --wait", quoted paths, the lot. Filter-on-Optioncorrectly falls through tovifor empty/whitespace$EDITOR..plsuffix on the temp file. Lights up the user's Prolog ftplugin without ceremony.disable_raw_modeandenable_raw_mode, a panic restores the terminal — verified by inspection ofrun()'s hook (re-disables raw + leaves alt screen, idempotently).:editshows in:help. Surface now matches behavior.Suggested order
Items 1 and 2 are silent-data-loss-class for the user's mental model ("did my edit take or not?"). Worth doing before merge — both are <10 lines. 3 and 4 are quick wins. 5, 6, 7 are commentary.