Skip to content

Built-in support for multiple passes - #140

Merged
merv1n34k merged 1 commit into
let-def:mainfrom
merv1n34k:feat/idle-rerun
Jul 22, 2026
Merged

Built-in support for multiple passes#140
merv1n34k merged 1 commit into
let-def:mainfrom
merv1n34k:feat/idle-rerun

Conversation

@merv1n34k

@merv1n34k merv1n34k commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Hey!

Closes #35
Closes #73
Closes #77
Closes #110

This adds an opt-in -rerun flag that makes TeXpresso converge TOC, refs, and other aux-derived state on idle, without ever running the engine twice on input the user can see. Default values are 500ms timeout on idle, 5 reruns cap, configured in src/frontend/main.c.

The idea is the following:

  1. The real time rendering and logic stays the same.
  2. If the main program is idle, that is, no request from stdin, the main loop has a timeout of 500ms, if no response from stdin still, the engine is marked "finishing": the main loop keeps driving engine_step until \end{document} is reached, then captures aux/toc/etc. into edit_data and respawns the engine. The new run reads prior-pass aux via the existing entry_data() edit_data branch. Repeats until aux is byte-stable (cap MAX_RERUNS=5, matches latexmk default).

The finishing step is cooperative — engine_step is interleaved with SDL_PollEvent in the main loop, so the viewer stays interactive while the engine runs to end-of-doc.

  1. When user again modify the document and texpresso receives commands on stdin the aux files are discarded, the rollback gets in place and the logic is as before. Only when the first pass is done and the stdin is idle, we run more passes to converge.

IMO, this is useful because when you actively writing or changing the document - the changes are instant, and no additional passes are needed, but when you want to review the document, additional refs, ToC, etc. are converged and rendered, so you can spectate your text in its whole beauty.

Let me know what you think :)

Implementation notes

  • New vtable methods on txp_engine_class: aux_dirty, is_finishing, start_finishing, finish_convergence. PDF/DVI engines stub them as no-ops.
  • New bool edit_data_from_convergence on fileentry_t so the convergence stash doesn't clobber data the editor pushed via (open ...).
  • clear_convergence_stash is hooked into rollback_processes so a real edit invalidates the stash cleanly.
  • setvbuf(stderr, NULL, _IONBF, 0) at the top of main() so [fatal] lines reach disk before a crash; previously the buffered 4KB block could swallow them.
  • reset_seen_for_respawn walks self->fs before each engine respawn and clears e->seen. Without this, the new engine's first Q_OPRD for a previously-read file skips record_seen(0) (since e->seen >= 0), so the trace gets no first-open entry and rollback_add_change walks past trace[0] into OOB memory on the next edit.

@let-def

let-def commented Jun 7, 2026

Copy link
Copy Markdown
Owner

This is a good idea. Generally, I prefer minimizing a tool's surface area, which is why I originally separated the fixed-point computation from the incremental rendering. However, I agree that combining them is much more convenient and is a reasonable change to make at this stage of development.

I also think it makes sense to control reruns from the editor via specific commands:

  • to toggle/change the rerun state
  • to trigger a single rerun (commit current aux)

I am unsure how broken builds should be handled. Should an aux be committed only if the build succeeds? I believe so, but I'm not sure which error levels to consider (warnings are certainly fine; only failed/unterminated builds should be ignored).

Replace the -rerun CLI flag with two editor commands:
- (rerun t/nil) toggles convergence-on-idle at runtime
- (rerun-once) triggers a single immediate pass

When enabled, texpresso respawns the engine with the prior-pass aux
after an idle window, converging TOC/refs. (rerun-once) fires in the
main loop as soon as the engine's aux is ready, so it also works
during active compilation cycles.

Add test/test-rerun.sh + test/refs.tex to exercise parse/dispatch and
end-to-end on-demand convergence via -stream. Wire the target into
Makefile and CI.
@merv1n34k

merv1n34k commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator Author

Hey @let-def!

Can you check out the updates? Changed -rerun flag to ("rerun t/nil") and ("rerun-once") editor commands. This also includes new CI target and better commit logic for dirty aux files.

I did not implement logic for failed/unterminated builds, as it most likely would require to tamper engine_tex to verify exit status via waitpid. The case when user will encounter issues with bad aux files usually fixed when the source is changed, in case of partial aux (the one which could make engine to crash) a simple TeXpresso restart would be enough. The last case is very unlikely to happen in general.

@merv1n34k merv1n34k mentioned this pull request Jul 5, 2026
@merv1n34k
merv1n34k merged commit 8d7f50c into let-def:main Jul 22, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants