From cd2492cc8deae80bc5bb55fafa6a6a45f4a85537 Mon Sep 17 00:00:00 2001 From: Fred Fu Date: Mon, 13 Jul 2026 14:44:27 -0400 Subject: [PATCH 1/3] add `typst watch` to monitor changes to scribble files We added a small even loop to monitor scribble file changes. We used `dynamic-rerequire` to reload related doc modules. --- scribble-lib/scribble/run.rkt | 61 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/scribble-lib/scribble/run.rkt b/scribble-lib/scribble/run.rkt index d239aec932..a7bea3e948 100644 --- a/scribble-lib/scribble/run.rkt +++ b/scribble-lib/scribble/run.rkt @@ -2,6 +2,8 @@ (require "xref.rkt" "render.rkt" racket/cmdline + racket/system + racket/rerequire raco/command-name (prefix-in text: "text-render.rkt") (prefix-in markdown: "markdown-render.rkt") @@ -32,6 +34,7 @@ (define current-directory-depth (make-parameter 0)) (define current-lib-mode (make-parameter #f)) (define current-quiet (make-parameter #f)) +(define use-watcher-for-typst? (make-parameter #f)) (define helper-file-prefix (make-parameter #f)) (define keep-existing-helper-files? (make-parameter #f)) (define doc-command-line-arguments (make-parameter null)) @@ -72,6 +75,10 @@ [("--typst") "generate Typst-format output" (current-html #f) (current-render-mixin typst:render-mixin)] + [("--typst-watch") "watch scribbles and automatically compile them to Typst-format output" + (current-html #f) + (use-watcher-for-typst? #t) + (current-render-mixin typst:render-mixin)] [("--pdf") "generate PDF-format output (via PDFLaTeX)" (current-html #f) (current-render-mixin pdf:render-mixin)] @@ -178,22 +185,44 @@ (make-compilation-manager-load/use-compiled-handler))]) (parameterize ([current-command-line-arguments (list->vector (reverse (doc-command-line-arguments)))]) - (build-docs (map (lambda (file) - (define (go) - (let ([mp (if (current-lib-mode) - `(lib ,file) - `(file ,file))]) - ;; Try `doc' submodule, first: - (if (module-declared? `(submod ,mp ,doc-binding) #t) - (dynamic-require `(submod ,mp ,doc-binding) - doc-binding) - (dynamic-require mp doc-binding)))) - (if maker - (parameterize ([current-load/use-compiled maker]) - (go)) - (go))) - files) - files))))) + (define (mk-doc file) + (define (go) + (let ([mp (if (current-lib-mode) + `(lib ,file) + `(file ,file))]) + (define mp^ + ;; Try `doc' submodule first, but do not loaded in `watch' mode. + (if (module-declared? `(submod ,mp ,doc-binding) (not (use-watcher-for-typst?))) + `(submod ,mp ,doc-binding) + mp)) + (when (use-watcher-for-typst?) + ;; in `watch` mode, use `dynamic-rerequire' to load the module first. + (dynamic-rerequire mp^ #:verbosity 'reload)) + (dynamic-require mp^ doc-binding))) + (if maker + (parameterize ([current-load/use-compiled maker]) + (go)) + (go))) + (cond + [(use-watcher-for-typst?) + (define typst-bin (find-executable-path "typst")) + (unless typst-bin + (raise-user-error 'scribble "typst cannot be found in your $PATH")) + (apply process* + typst-bin + "watch" + (map (lambda (x) + (path-replace-extension x ".typ")) + files)) + (let loop () + (define changed + (apply sync (for/list ([i (in-list files)]) + (wrap-evt (filesystem-change-evt i) + (lambda _ i))))) + (build-docs (list (mk-doc changed)) (list changed)) + (loop))] + [else + (build-docs (map mk-doc files) files)]))))) (define (build-docs docs files) (when (and (current-dest-name) From dacf4c5a1e863e91a0953c2bb0a426bdd1a03be4 Mon Sep 17 00:00:00 2001 From: Fred Fu Date: Mon, 13 Jul 2026 20:07:08 -0400 Subject: [PATCH 2/3] Update scribble-lib/scribble/run.rkt Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- scribble-lib/scribble/run.rkt | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/scribble-lib/scribble/run.rkt b/scribble-lib/scribble/run.rkt index a7bea3e948..bea410badf 100644 --- a/scribble-lib/scribble/run.rkt +++ b/scribble-lib/scribble/run.rkt @@ -187,18 +187,19 @@ (list->vector (reverse (doc-command-line-arguments)))]) (define (mk-doc file) (define (go) - (let ([mp (if (current-lib-mode) - `(lib ,file) - `(file ,file))]) - (define mp^ - ;; Try `doc' submodule first, but do not loaded in `watch' mode. - (if (module-declared? `(submod ,mp ,doc-binding) (not (use-watcher-for-typst?))) - `(submod ,mp ,doc-binding) - mp)) - (when (use-watcher-for-typst?) - ;; in `watch` mode, use `dynamic-rerequire' to load the module first. - (dynamic-rerequire mp^ #:verbosity 'reload)) - (dynamic-require mp^ doc-binding))) + (define mp + (if (current-lib-mode) + `(lib ,file) + `(file ,file))) + (define mp^ + ;; Try `doc' submodule first, but do not loaded in `watch' mode. + (if (module-declared? `(submod ,mp ,doc-binding) (not (use-watcher-for-typst?))) + `(submod ,mp ,doc-binding) + mp)) + (when (use-watcher-for-typst?) + ;; in `watch` mode, use `dynamic-rerequire' to load the module first. + (dynamic-rerequire mp^ #:verbosity 'reload)) + (dynamic-require mp^ doc-binding)) (if maker (parameterize ([current-load/use-compiled maker]) (go)) From e49162a4e0abdfc26d300da76622692610f583b5 Mon Sep 17 00:00:00 2001 From: Fred Fu Date: Mon, 13 Jul 2026 21:47:25 -0400 Subject: [PATCH 3/3] handle errors --- scribble-lib/scribble/run.rkt | 49 +++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/scribble-lib/scribble/run.rkt b/scribble-lib/scribble/run.rkt index bea410badf..408e6824b9 100644 --- a/scribble-lib/scribble/run.rkt +++ b/scribble-lib/scribble/run.rkt @@ -3,6 +3,7 @@ "render.rkt" racket/cmdline racket/system + racket/exn racket/rerequire raco/command-name (prefix-in text: "text-render.rkt") @@ -181,24 +182,23 @@ (dynamic-require 'errortrace #f))] #:args (file . another-file) (let ([files (cons file another-file)] + [declared? (lambda (mp) + ;; Try `doc' submodule first, but do not loaded in `watch' mode. + (module-declared? `(submod ,mp ,doc-binding) (not (use-watcher-for-typst?))))] [maker (and make? (make-compilation-manager-load/use-compiled-handler))]) (parameterize ([current-command-line-arguments (list->vector (reverse (doc-command-line-arguments)))]) - (define (mk-doc file) + (define (mk-doc file [pre-process! #f]) (define (go) - (define mp - (if (current-lib-mode) - `(lib ,file) - `(file ,file))) - (define mp^ - ;; Try `doc' submodule first, but do not loaded in `watch' mode. - (if (module-declared? `(submod ,mp ,doc-binding) (not (use-watcher-for-typst?))) - `(submod ,mp ,doc-binding) - mp)) - (when (use-watcher-for-typst?) - ;; in `watch` mode, use `dynamic-rerequire' to load the module first. - (dynamic-rerequire mp^ #:verbosity 'reload)) + (define mp (if (current-lib-mode) + `(lib ,file) + `(file ,file))) + (define mp^ (if (declared? mp) + `(submod ,mp ,doc-binding) + mp)) + (when pre-process! + (pre-process! mp^)) (dynamic-require mp^ doc-binding)) (if maker (parameterize ([current-load/use-compiled maker]) @@ -208,7 +208,7 @@ [(use-watcher-for-typst?) (define typst-bin (find-executable-path "typst")) (unless typst-bin - (raise-user-error 'scribble "typst cannot be found in your $PATH")) + (raise-user-error 'scribble "cannot find typst in your $PATH")) (apply process* typst-bin "watch" @@ -216,14 +216,23 @@ (path-replace-extension x ".typ")) files)) (let loop () - (define changed - (apply sync (for/list ([i (in-list files)]) - (wrap-evt (filesystem-change-evt i) - (lambda _ i))))) - (build-docs (list (mk-doc changed)) (list changed)) + (let/ec k + (define changed + (apply sync (for/list ([i (in-list files)]) + (wrap-evt (filesystem-change-evt i) + (lambda _ i))))) + (build-docs (list (mk-doc changed (lambda (mp) + (with-handlers ([exn:fail? (lambda (e) + (printf "~a" (exn->string e)) + (k))]) + (dynamic-rerequire mp #:verbosity 'reload))))) + (list changed))) (loop))] [else - (build-docs (map mk-doc files) files)]))))) + (with-handlers ([exn:fail? (lambda (e) + (display "got en error") + (raise e))]) + (build-docs (map mk-doc files) files))]))))) (define (build-docs docs files) (when (and (current-dest-name)