feat(tui): re-read the document when its file changes - #10
Merged
Conversation
A pager left open beside an editor now keeps up with what is being written. The event loop already wakes every 120 ms to look at the termination flag, so watching is one `stat` per tick against the file's modification time and length and needs no new dependency. A change is acted on only once a second look finds it unchanged, so a half-written save is never parsed, and a path that briefly vanishes -- how many editors save -- is waited out rather than treated as an empty document. The reading position survives the way it survives a resize, through the source offset of the topmost visible text. Unlike a resize, that offset has to be carried across the edit first: `remap_offset` compares the two sources from both ends, so text inserted above what the reader is looking at no longer pushes them off it. A live search is re-run against the new source rather than re-projected, the contents pane is rebuilt, and the footnote popup closes because the marker it points at may have moved. Reading and parsing stay in `term`: the state machine touches no file (design spec 13), so it is handed a parsed document. A file that cannot be read is reported in the status bar and leaves the document on screen alone. On by default, with `--no-reload`, `--reload` and `reload = false` to control it. Standard input is watched for nothing, there being no file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A pager left open beside an editor now keeps up with what is being written.
What it does
A document read from a file is re-read whenever that file changes on disk. The
reading position is kept, a live search is re-run against the new text, the
contents pane is rebuilt, and a footnote popup closes because the marker it
points at may have moved. On by default;
--no-reload,--reloadandreload = falsecontrol it. Standard input is watched for nothing, there beingno file.
How
Noticing the change (
src/tui/watch.rs). The event loop already wakes every120 ms to look at the termination flag, so watching is one
statper tickcomparing modification time and length — no new dependency, nothing to port per
platform. A change is remembered when first seen and acted on only when the next
look finds it unchanged, so a half-written save is never parsed; a write that is
still growing moves the stamp again and the wait starts over. A path that
momentarily does not exist — how many editors save — is a save in progress, not
a reason to throw away what is on screen.
Keeping the reader's place. The position survives the way it survives a
resize, through the source offset of the topmost visible text. Unlike a resize,
that offset has to be carried across the edit first:
remap_offsetcompares thetwo sources from both ends to find the region that actually changed, so text
inserted above what the reader is looking at no longer pushes them off it. It
is exact for the single edited region a save produces; several edits at once
collapse into the one region spanning them, which is approximate rather than
wrong.
Where the work happens. Reading and parsing stay in
term, because the statemachine touches no file (design spec §13) —
App::reloadis handed a parseddocument. The render cache is keyed on
Doc::version, so the new documentinvalidates it by construction rather than by anyone remembering to. A file that
cannot be read or is not text is reported in the status bar and leaves the
document on screen alone.
API breaks
mdmost::tui::rungained a second parameter,source: Option<&Path>.Configgained a public field,reload: bool, and a method,math_syntax,which is now the one place
mathandmath_backslashbecome aMathSyntax.Both are recorded in
CHANGES.md.Testing
16 new tests, each written and watched fail before the code existed:
remap_offset— offsets before, after and inside the edit, and identity;App::reload— position kept under an edit above and below it, clamp on ashorter document, contents rebuilt, search re-run, popup closed, notice shown;
reload_tick— a settled change reaching the document, and an unreadable fileleaving it alone;
reloadkey, its default, and its round trip throughS.cargo test(34 binaries),cargo clippy --all-targetsandcargo fmt --checkare clean. Checked end to end in a pseudo terminal as well: the file was
rewritten under a running pager, and the new text and the
reloadednotice bothappear in the painted frame.