From ea75a5c5e973dcf03b698d25612085d572bee268 Mon Sep 17 00:00:00 2001 From: thomasjm Date: Tue, 10 Nov 2026 20:01:00 -0800 Subject: [PATCH 1/6] Exporters: render through programs, not server backends --- modules/exporters/latex/default.nix | 10 ++-- .../latex/language_server_export/default.nix | 1 - modules/exporters/latex/latex-render.nix | 48 +++++++++++++++++++ .../lsp-export-server-version.nix | 2 +- modules/exporters/pandoc/default.nix | 11 +++-- .../pandoc/language_server_export/default.nix | 1 - modules/exporters/pandoc/pandoc-render.nix | 48 +++++++++++++++++++ 7 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 modules/exporters/latex/latex-render.nix create mode 100644 modules/exporters/pandoc/pandoc-render.nix diff --git a/modules/exporters/latex/default.nix b/modules/exporters/latex/default.nix index a2eb62f5..a8beaa27 100644 --- a/modules/exporters/latex/default.nix +++ b/modules/exporters/latex/default.nix @@ -24,13 +24,15 @@ let exportServer = callPackage ../lsp-export-server/lsp-export-server.nix {}; exportServerVersion = import ../lsp-export-server/lsp-export-server-version.nix; + latexRender = callPackage ./latex-render.nix {}; - # The export server with the TeX Live engines and codedown.sty in reach. `serve` is the language - # server behind live exports; `render` compiles a file once (the exporter test uses it). + # The export server with the TeX Live engines and codedown.sty in reach, rendering through + # latex-render. `serve` is the language server behind live exports; `render` compiles a file once + # (the exporter test uses it). wrapper = common.writeShellScriptBinWithAttrs {} "latex-export-server" '' export PATH="${texliveToUse}/bin''${PATH:+:''${PATH}}" export TEXINPUTS="${texInputs}''${TEXINPUTS:-}" - exec ${exportServer}/bin/lsp-export-server "$@" + exec ${exportServer}/bin/lsp-export-server "$@" --render ${latexRender}/bin/latex-render --settle ${latexRender}/bin/latex-render ''; languageServers = [ @@ -59,7 +61,7 @@ let icon_monochrome = iconMonochrome; # Live exports run on the latex-export-server language server; these args are the one-shot # form of the same render, and carry the task's options (-o key=value) - args = ["${wrapper}/bin/latex-export-server" "render" "--backend" "latex" "-o" "engine=${settings.engine}"]; + args = ["${wrapper}/bin/latex-export-server" "render" "-o" "engine=${settings.engine}"]; input_extensions = ["tex"]; kind = "render_server"; render_server = "latex-export-server"; diff --git a/modules/exporters/latex/language_server_export/default.nix b/modules/exporters/latex/language_server_export/default.nix index a5b80782..a79a4268 100644 --- a/modules/exporters/latex/language_server_export/default.nix +++ b/modules/exporters/latex/language_server_export/default.nix @@ -33,7 +33,6 @@ common.writeTextDirWithMetaAndPassthru { description = "Live LaTeX rendering for args = [ "${wrapper}/bin/latex-export-server" "serve" - "--backend" "latex" "-o" "engine=${engine}" ]; }]) diff --git a/modules/exporters/latex/latex-render.nix b/modules/exporters/latex/latex-render.nix new file mode 100644 index 00000000..0d3a9242 --- /dev/null +++ b/modules/exporters/latex/latex-render.nix @@ -0,0 +1,48 @@ +{ callPackage +, gawk +, gnugrep +}: + +let + common = callPackage ../../kernels/common.nix {}; + +in + +# The render program behind the latex-export-server language server (see the RENDER_* variables in +# codedownio/lsp-export-server): one engine pass for the quick phase, latexmk for the settle phase. +# The unsaved buffers come first on TEXINPUTS so they shadow their disk copies, the epoch pins the +# PDF's timestamps, and the recorder's INPUT lines become the dependency list. LATEX_RENDER_INPUT +# lets another program (pandoc-render) hand over a .tex it generated. +common.writeShellScriptBinWithAttrs {} "latex-render" '' + set -euo pipefail + + tex="''${LATEX_RENDER_INPUT:-$RENDER_ENTRY}" + engine="''${RENDER_OPT_engine:-pdflatex}" + export TEXINPUTS="$RENDER_OVERLAY//:''${TEXINPUTS:-}" + export LC_ALL=C SOURCE_DATE_EPOCH="$RENDER_EPOCH" FORCE_SOURCE_DATE=1 + + common=(-interaction=nonstopmode -halt-on-error -file-line-error -recorder "-jobname=$RENDER_JOB") + case "$RENDER_PHASE" in + settle) + case "$engine" in + xelatex) flag=-pdfxe ;; + lualatex) flag=-pdflua ;; + *) flag=-pdf ;; + esac + cmd=(latexmk "$flag" "-outdir=$RENDER_OUT" "''${common[@]}" "$tex") + ;; + *) + cmd=("$engine" "-output-directory=$RENDER_OUT" "''${common[@]}" "$tex") + ;; + esac + + echo "''${cmd[*]}" + if ! "''${cmd[@]}"; then + # The engine's own account of what went wrong: the ! lines and the file:line: errors + log="$RENDER_OUT/$RENDER_JOB.log" + [ -f "$log" ] && ${gnugrep}/bin/grep -E -B1 '^!|:error:|\.tex:[0-9]+:' "$log" >&2 || true + exit 1 + fi + + ${gawk}/bin/awk '/^INPUT /{print $2}' "$RENDER_OUT/$RENDER_JOB.fls" > "$RENDER_DEPS" +'' diff --git a/modules/exporters/lsp-export-server/lsp-export-server-version.nix b/modules/exporters/lsp-export-server/lsp-export-server-version.nix index 61fc9a9d..c45b79dc 100644 --- a/modules/exporters/lsp-export-server/lsp-export-server-version.nix +++ b/modules/exporters/lsp-export-server/lsp-export-server-version.nix @@ -1 +1 @@ -"0.4.1.2" +"0.5.0.0" diff --git a/modules/exporters/pandoc/default.nix b/modules/exporters/pandoc/default.nix index e8752f49..f2740f96 100644 --- a/modules/exporters/pandoc/default.nix +++ b/modules/exporters/pandoc/default.nix @@ -20,12 +20,15 @@ let exportServer = callPackage ../lsp-export-server/lsp-export-server.nix {}; exportServerVersion = import ../lsp-export-server/lsp-export-server-version.nix; + latexRender = callPackage ../latex/latex-render.nix {}; + pandocRender = callPackage ./pandoc-render.nix { inherit latexRender; }; - # The export server with pandoc and TeX Live in reach. `serve` is the language server behind - # live exports; `render` converts a file once (the exporter test and notebook exports use it). + # The export server with pandoc and TeX Live in reach, rendering through pandoc-render. `serve` + # is the language server behind live exports; `render` converts a file once (the exporter test + # and notebook exports use it). wrapper = common.writeShellScriptBinWithAttrs {} "pandoc-export-server" '' export PATH="${pandoc}/bin:${texliveScheme}/bin''${PATH:+:''${PATH}}" - exec ${exportServer}/bin/lsp-export-server "$@" + exec ${exportServer}/bin/lsp-export-server "$@" --render ${pandocRender}/bin/pandoc-render --settle ${pandocRender}/bin/pandoc-render ''; languageServers = [ @@ -60,7 +63,7 @@ let inherit icon; icon_monochrome = iconMonochrome; args = [ - "${wrapper}/bin/pandoc-export-server" "render" "--backend" "pandoc" + "${wrapper}/bin/pandoc-export-server" "render" "-o" "to=${to}" "-o" "engine=pdflatex" ] ++ (if pandocArgs == [] then [] else ["-o" "pandoc_args=${builtins.concatStringsSep " " pandocArgs}"]); diff --git a/modules/exporters/pandoc/language_server_export/default.nix b/modules/exporters/pandoc/language_server_export/default.nix index f2960c78..ae7f8305 100644 --- a/modules/exporters/pandoc/language_server_export/default.nix +++ b/modules/exporters/pandoc/language_server_export/default.nix @@ -31,7 +31,6 @@ common.writeTextDirWithMetaAndPassthru { description = "Live pandoc rendering fo args = [ "${wrapper}/bin/pandoc-export-server" "serve" - "--backend" "pandoc" "-o" "engine=pdflatex" ]; }]) diff --git a/modules/exporters/pandoc/pandoc-render.nix b/modules/exporters/pandoc/pandoc-render.nix new file mode 100644 index 00000000..c06ebc04 --- /dev/null +++ b/modules/exporters/pandoc/pandoc-render.nix @@ -0,0 +1,48 @@ +{ callPackage +, pandoc + +, latexRender +}: + +let + common = callPackage ../../kernels/common.nix {}; + +in + +# The render program behind the pandoc-export-server language server (see the RENDER_* variables in +# codedownio/lsp-export-server). Options: to (the writer; pdf means latex), from (the reader for +# Markdown input), pandoc_args (extra arguments, space-separated), and engine for the LaTeX-based +# writers, which hand the generated .tex to latex-render for both phases. The other writers have +# nothing to settle. +common.writeShellScriptBinWithAttrs {} "pandoc-render" '' + set -euo pipefail + + to="''${RENDER_OPT_to:-html}" + writer="$to" + [ "$to" = pdf ] && writer=latex + case "$writer" in + latex|beamer) via_latex=1; out="$RENDER_SRC/$RENDER_JOB.tex" ;; + *) via_latex=0; out="$RENDER_OUTPUT" ;; + esac + if [ "$RENDER_PHASE" = settle ] && [ "$via_latex" = 0 ] && [ -f "$RENDER_OUTPUT" ]; then + exit 0 + fi + + reader=() + case "$RENDER_ENTRY" in + *.md) reader=(-f "''${RENDER_OPT_from:-markdown+tex_math_dollars+tex_math_single_backslash+raw_html+smart}") ;; + esac + extra=() + if [ -n "''${RENDER_OPT_pandoc_args:-}" ]; then + read -r -a extra <<< "$RENDER_OPT_pandoc_args" + fi + + export LC_ALL=C + cmd=(${pandoc}/bin/pandoc "''${reader[@]}" -t "$writer" --standalone "''${extra[@]}" "$RENDER_ENTRY" -o "$out") + echo "''${cmd[*]}" + "''${cmd[@]}" + + if [ "$via_latex" = 1 ]; then + LATEX_RENDER_INPUT="$out" exec ${latexRender}/bin/latex-render + fi +'' From 1a7c7575bba0b71b1b387a9f72888bf6e5b9248f Mon Sep 17 00:00:00 2001 From: thomasjm Date: Tue, 10 Nov 2026 20:01:00 -0800 Subject: [PATCH 2/6] Typst: live export through the export server --- modules/exporters/typst/default.nix | 36 ++++++++++++------- .../typst/language_server_export/default.nix | 36 +++++++++++++++++++ modules/exporters/typst/typst-render.nix | 35 ++++++++++++++++++ 3 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 modules/exporters/typst/language_server_export/default.nix create mode 100644 modules/exporters/typst/typst-render.nix diff --git a/modules/exporters/typst/default.nix b/modules/exporters/typst/default.nix index f3f81af2..2ba1db5e 100644 --- a/modules/exporters/typst/default.nix +++ b/modules/exporters/typst/default.nix @@ -29,18 +29,24 @@ let cp ${./codedown-lib/lib.typ} "$dir/lib.typ" ''; - # Also writes a dependency file at ".deps" (typst's native JSON - # {"inputs": [...]}, paths relative to cwd) so the runner can watch imported - # files and re-render when any of them changes. See exporterInfoDeps below. - script = common.writeShellScriptBinWithAttrs {} "typst-export" '' - echo_and_run() { echo "$*" ; "$@" ; } - echo_and_run export PATH="''${PATH:+''${PATH}:}" - echo_and_run ${typst}/bin/typst compile --package-path ${codedownPackagePath} --deps "$2.deps" "$1" "$2" - ''; - typstToUse = typst.withPackages (ps: (map (x: ps.${x}) packages)); - languageServers = lib.optionals settings.lsp.tinymist.enable + exportServer = callPackage ../lsp-export-server/lsp-export-server.nix {}; + exportServerVersion = import ../lsp-export-server/lsp-export-server-version.nix; + typstRender = callPackage ./typst-render.nix { inherit typstToUse codedownPackagePath; }; + + # The export server rendering through typst-render. `serve` is the language server behind live + # exports; `render` compiles a file once (the exporter test uses it). Typst has no settle pass. + wrapper = common.writeShellScriptBinWithAttrs {} "typst-export-server" '' + exec ${exportServer}/bin/lsp-export-server "$@" --render ${typstRender}/bin/typst-render + ''; + + languageServers = [ + (callPackage ./language_server_export { + inherit wrapper; + version = exportServerVersion; + }) + ] ++ lib.optionals settings.lsp.tinymist.enable [(callPackage ./language_server_tinymist { inherit kernelName typstToUse codedownPackagePath; settings = settings.lsp.tinymist; @@ -52,6 +58,8 @@ let icon = ./typst.png; iconMonochrome = ./typst.svg; + # Live exports run on the typst-export-server language server; the args are the one-shot form of + # the same render mkTypstExporter = display_name: extension: { name = "codedown-exporter-typst"; inherit display_name; @@ -59,10 +67,11 @@ let inherit extension; inherit icon; icon_monochrome = iconMonochrome; - args = [(script + "/bin/typst-export")]; + args = ["${wrapper}/bin/typst-export-server" "render"]; input_extensions = ["typ"]; pandoc = "${pandoc}/bin/pandoc"; - deps = "typst_json"; + kind = "render_server"; + render_server = "typst-export-server"; }; # Live preview exporter. Instead of running a command, the runner attaches a tinymist @@ -87,7 +96,7 @@ symlinkJoin { name = "codedown-exporter-typst"; paths = [ (callPackage ./kernel.nix { inherit attrs extensions typstToUse; }) - script + wrapper ] ++ languageServers ; @@ -118,6 +127,7 @@ symlinkJoin { versions = { typst = typst.version; + lsp-export-server = exportServerVersion; }; inherit packageOptions packageSearch; diff --git a/modules/exporters/typst/language_server_export/default.nix b/modules/exporters/typst/language_server_export/default.nix new file mode 100644 index 00000000..3260535e --- /dev/null +++ b/modules/exporters/typst/language_server_export/default.nix @@ -0,0 +1,36 @@ +{ callPackage +, lib + +, wrapper +, version +}: + +let + common = callPackage ../../../kernels/common.nix {}; + + languageServerName = "typst-export-server"; + + passthru = { + inherit languageServerName; + }; + +in + +# The export server compiles the in-memory document for live exports (see the codedown.render +# protocol in codedownio/lsp-export-server). It only handles its own commands, so tinymist keeps the +# editing features. +common.writeTextDirWithMetaAndPassthru { description = "Live Typst rendering for codedown exports"; } passthru "lib/codedown/language-servers/typst-export-server.yaml" (lib.generators.toYAML {} [{ + name = languageServerName; + inherit version; + icon = ../typst.png; + icon_monochrome = ../typst.svg; + extensions = ["typ"]; + notebook_suffix = ".typ"; + attrs = ["typst"]; + type = "stream"; + primary = false; + args = [ + "${wrapper}/bin/typst-export-server" + "serve" + ]; +}]) diff --git a/modules/exporters/typst/typst-render.nix b/modules/exporters/typst/typst-render.nix new file mode 100644 index 00000000..549aa805 --- /dev/null +++ b/modules/exporters/typst/typst-render.nix @@ -0,0 +1,35 @@ +{ callPackage +, coreutils +, gnused + +, typstToUse +, codedownPackagePath +}: + +let + common = callPackage ../../kernels/common.nix {}; + +in + +# The render program behind the typst-export-server language server (see the RENDER_* variables in +# codedownio/lsp-export-server). The buffer goes in on stdin, so imports resolve from the document's +# directory (the working directory) rather than the work directory's copy; the epoch pins the PDF's +# timestamps. Image formats render the first page only, since a single output file can't hold more. +common.writeShellScriptBinWithAttrs {} "typst-render" '' + set -euo pipefail + + export SOURCE_DATE_EPOCH="$RENDER_EPOCH" + extra=() + case "$RENDER_FORMAT" in + png|svg) extra=(--pages 1) ;; + html) extra=(--features html) ;; + esac + deps="$RENDER_WORK/deps.mk" + + cmd=(${typstToUse}/bin/typst compile --package-path ${codedownPackagePath} --format "$RENDER_FORMAT" "''${extra[@]}" --deps "$deps" --deps-format make - "$RENDER_OUTPUT") + echo "''${cmd[*]} < $RENDER_ENTRY" + "''${cmd[@]}" < "$RENDER_ENTRY" + + # The make-format line is "output: dep dep " + ${gnused}/bin/sed -e 's/^[^:]*: //' -e 's/ *//' "$deps" | ${coreutils}/bin/tr ' ' '\n' > "$RENDER_DEPS" +'' From 6b585b3d6e0085c8050a8011109c28e16e49243c Mon Sep 17 00:00:00 2001 From: thomasjm Date: Tue, 10 Nov 2026 20:14:22 -0800 Subject: [PATCH 3/6] Tests: a serve-mode smoke test per export server noci --- tests/app/Spec/Tests/Exporters.hs | 10 +++++++ tests/src/TestLib/Exporters.hs | 45 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/tests/app/Spec/Tests/Exporters.hs b/tests/app/Spec/Tests/Exporters.hs index c01f4edd..298672e7 100644 --- a/tests/app/Spec/Tests/Exporters.hs +++ b/tests/app/Spec/Tests/Exporters.hs @@ -7,6 +7,7 @@ module Spec.Tests.Exporters (tests) where import Data.String.Interpolate import Data.Text (Text) +import Language.LSP.Protocol.Types (LanguageKind(..)) import Test.Sandwich as Sandwich import TestLib.Exporters import TestLib.JupyterRunnerContext (introduceJustBubblewrap) @@ -30,12 +31,21 @@ tests = describe "Exporters" $ introduceJustBubblewrap $ do it "codedown-exporter-pandoc-beamer" $ testExportMd "codedown-exporter-pandoc-beamer" "html" it "codedown-exporter-pandoc-pdf" $ testExportMd "codedown-exporter-pandoc-pdf" "pdf" + it "pandoc-export-server renders live" $ + testExportServe "pandoc-export-server" "input.md" LanguageKind_Markdown sampleMdFile "html" [("to", "slidy")] [] + introduceNixEnvironment [] [latexConfig] [i|Exporters (latex)|] $ do it "codedown-exporter-latex" $ testExportLatex "codedown-exporter-latex" "pdf" + it "latex-export-server renders live" $ + testExportServe "latex-export-server" "input.tex" LanguageKind_LaTeX sampleLatexFile "pdf" [] [("out.txt", "line one\n")] + introduceNixEnvironment [] [typstConfig] [i|Exporters (typst)|] $ do it "codedown-exporter-typst" $ testExportTypst "codedown-exporter-typst" "pdf" + it "typst-export-server renders live" $ + testExportServe "typst-export-server" "input.typ" (LanguageKind_Custom "typst") sampleTypstFile "pdf" [] [] + testTexliveScheme :: Text -> LanguageSpec testTexliveScheme scheme = do diff --git a/tests/src/TestLib/Exporters.hs b/tests/src/TestLib/Exporters.hs index 528c3892..ec7f603d 100644 --- a/tests/src/TestLib/Exporters.hs +++ b/tests/src/TestLib/Exporters.hs @@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE TemplateHaskell #-} module TestLib.Exporters where @@ -9,13 +10,23 @@ import Control.Monad.IO.Class import Data.Aeson as A import Data.Aeson.QQ.Simple import Data.Aeson.TH as A +import qualified Data.Aeson.KeyMap as AKM +import qualified Data.ByteString as B +import qualified Data.Map as M import Data.String.Interpolate import Data.Text as T import qualified Data.Text.IO as T +import GHC.TypeLits (symbolVal) +import Language.LSP.Protocol.Message +import Language.LSP.Protocol.Types +import Language.LSP.Test +import Language.LSP.Test.Helpers (LspContext, LspSessionInfo(..), LspSessionOptions(..)) +import qualified Language.LSP.Test.Helpers as Helpers import Safe import System.FilePath import Test.Sandwich as Sandwich import TestLib.Aeson +import TestLib.LSP.Session (lspSessionOptionsFor) import TestLib.Types import UnliftIO.Directory import UnliftIO.Process @@ -256,3 +267,37 @@ readExporterInfoByName exportersYaml desired = do case headMay [x | x@(ExporterInfo {..}) <- exporterInfos, exporterInfoName == desired] of Nothing -> expectationFailure [i|Couldn't find exporter info named '#{desired}'. (Had: #{fmap exporterInfoName exporterInfos})|] Just x -> pure x + +-- | Start a live render on the export server behind an exporter and wait for its first output: +-- the serve-mode counterpart of 'testExport'. A wrapper that forgets to write the output, or +-- names a program that isn't on the server's PATH, fails here. +testExportServe :: ( + LspContext ctx m, HasNixEnvironment ctx + ) => Text -> FilePath -> LanguageKind -> Text -> Text -> [(Text, Text)] -> [(FilePath, B.ByteString)] -> ExampleT ctx m () +testExportServe serverName filename languageKind code format options extraFiles = do + sessionOptions <- lspSessionOptionsFor serverName filename languageKind code + Helpers.withLspSession (sessionOptions { lspSessionOptionsExtraFiles = extraFiles }) $ \(LspSessionInfo {..}) -> do + let uri = filePathToUri (lspSessionInfoHomeDir lspSessionInfoFileName) + let startParams = A.object [ + "taskId" A..= ("test" :: Text) + , "uri" A..= getUri uri + , "format" A..= format + , "options" A..= M.fromList options + , "debounceMs" A..= (50 :: Int) + ] + void $ request SMethod_WorkspaceExecuteCommand (ExecuteCommandParams Nothing "codedown.render.start" (Just [startParams])) + + nextRenderOutput >>= \case + A.Object (AKM.lookup "files" -> Just (A.Array files)) | not (Prelude.null files) -> return () + x -> expectationFailure [i|Expected a render output with files, got: #{A.encode x}|] + +-- | The params of the next render output notification, skipping the log and status ones. +nextRenderOutput :: LspContext ctx m => Session (ExampleT ctx m) A.Value +nextRenderOutput = anyMessage >>= \case + FromServerMess method msg -> case splitServerMethod method of + IsServerEither -> case msg of + NotMess (TNotificationMessage _ (SMethod_CustomMethod p) params) + | T.pack (symbolVal p) == "codedown/render/output" -> return params + _ -> nextRenderOutput + _ -> nextRenderOutput + _ -> nextRenderOutput From 6b8215f3355c64136d059785505c5490ca3c679b Mon Sep 17 00:00:00 2001 From: thomasjm Date: Wed, 11 Nov 2026 01:10:43 -0800 Subject: [PATCH 4/6] Fetch lsp-export-server 0.1.0.0 noci --- .../lsp-export-server-version.nix | 2 +- .../lsp-export-server/lsp-export-server.nix | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/exporters/lsp-export-server/lsp-export-server-version.nix b/modules/exporters/lsp-export-server/lsp-export-server-version.nix index c45b79dc..94cad586 100644 --- a/modules/exporters/lsp-export-server/lsp-export-server-version.nix +++ b/modules/exporters/lsp-export-server/lsp-export-server-version.nix @@ -1 +1 @@ -"0.5.0.0" +"0.1.0.0" diff --git a/modules/exporters/lsp-export-server/lsp-export-server.nix b/modules/exporters/lsp-export-server/lsp-export-server.nix index 5a39b764..f28dfbe8 100644 --- a/modules/exporters/lsp-export-server/lsp-export-server.nix +++ b/modules/exporters/lsp-export-server/lsp-export-server.nix @@ -7,28 +7,28 @@ { # HASHES_START "aarch64-linux" = fetchzip { - name = "lsp-export-server-0.4.1.2"; + name = "lsp-export-server-0.1.0.0"; stripRoot = false; - url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.4.1.2/lsp-export-server-0.4.1.2-aarch64-linux.tar.gz"; - hash = "sha256-Xl1gjqwwMmL66sKSuUFyoH8B3bTZgerdr7Dr7KKTNF4="; + url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.1.0.0/lsp-export-server-0.1.0.0-aarch64-linux.tar.gz"; + hash = "sha256-7LQTzqAYOHyr8IeohByxgV9BvhT8eNqO/OjpVlLCeQo="; }; "x86_64-linux" = fetchzip { - name = "lsp-export-server-0.4.1.2"; + name = "lsp-export-server-0.1.0.0"; stripRoot = false; - url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.4.1.2/lsp-export-server-0.4.1.2-x86_64-linux.tar.gz"; - hash = "sha256-USAxpR5pSmrR3Kw7Huncv4nHFNTuVjzWxIr+Kfw/CTA="; + url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.1.0.0/lsp-export-server-0.1.0.0-x86_64-linux.tar.gz"; + hash = "sha256-Hh5sWvw3+UO6tbH5GGxhKAW+IGerRX12SBdrs30uSyY="; }; "x86_64-darwin" = fetchzip { - name = "lsp-export-server-0.4.1.2"; + name = "lsp-export-server-0.1.0.0"; stripRoot = false; - url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.4.1.2/lsp-export-server-0.4.1.2-x86_64-darwin.tar.gz"; - hash = "sha256-h7eLA3ibe1LGQwV8FliiykkeulG2iZjCyoVjtHV+07U="; + url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.1.0.0/lsp-export-server-0.1.0.0-x86_64-darwin.tar.gz"; + hash = "sha256-1btJfUmtHIaqD4bd+NPo4LSZ//cYjtPhU2aQi3hrUuM="; }; "aarch64-darwin" = fetchzip { - name = "lsp-export-server-0.4.1.2"; + name = "lsp-export-server-0.1.0.0"; stripRoot = false; - url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.4.1.2/lsp-export-server-0.4.1.2-aarch64-darwin.tar.gz"; - hash = "sha256-P160ZrABtp+DD4uR+GhFsKnhjZoRe2BZiXwTdQSqAxo="; + url = "https://github.com/codedownio/lsp-export-server/releases/download/v0.1.0.0/lsp-export-server-0.1.0.0-aarch64-darwin.tar.gz"; + hash = "sha256-lBfC748fmgH5TfeIC1q/luRpgCGCAqd5UgR+2BM5m/A="; }; # HASHES_END }.${stdenv.hostPlatform.system} From df018c2d4fa8639704e6184b30304e6635b63914 Mon Sep 17 00:00:00 2001 From: thomasjm Date: Wed, 11 Nov 2026 01:19:01 -0800 Subject: [PATCH 5/6] Tests: bind the store when a closure is too big for bwrap noci --- tests/src/TestLib/LSP/Session.hs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/src/TestLib/LSP/Session.hs b/tests/src/TestLib/LSP/Session.hs index b7deb75a..32b0ff69 100644 --- a/tests/src/TestLib/LSP/Session.hs +++ b/tests/src/TestLib/LSP/Session.hs @@ -88,4 +88,13 @@ getPathAndNixEnvironmentClosure = do ) ) "" - return (pathToUse, closure) + case length closure > maxReadOnlyBinds of + False -> return (pathToUse, closure) + True -> do + info [i|Closure of #{length closure} paths is too big to bind one at a time; binding /nix|] + return (pathToUse, ["/nix"]) + +-- | bwrap takes at most 9000 arguments and a bind is three of them, so an environment with a few +-- thousand paths in its closure (anything carrying TeX Live) has to settle for the whole store. +maxReadOnlyBinds :: Int +maxReadOnlyBinds = 2500 From 20ff526d33f85f2c8dc3c0d793bc532e878a9e41 Mon Sep 17 00:00:00 2001 From: thomasjm Date: Wed, 11 Nov 2026 01:33:14 -0800 Subject: [PATCH 6/6] Exporters: nicer package display names noci --- modules/exporters/latex/default.nix | 1 + modules/exporters/nbconvert/default.nix | 1 + modules/exporters/pandoc/default.nix | 1 + modules/exporters/typst/default.nix | 1 + 4 files changed, 4 insertions(+) diff --git a/modules/exporters/latex/default.nix b/modules/exporters/latex/default.nix index a8beaa27..aa086bb0 100644 --- a/modules/exporters/latex/default.nix +++ b/modules/exporters/latex/default.nix @@ -76,6 +76,7 @@ symlinkJoin { passthru = { meta = { name = "codedown-exporter-latex"; + displayName = "LaTeX Exporter (TeX Live ${texliveYear})"; description = "CodeDown exporter rendering LaTeX notebooks to PDF as you type."; # To separate these out in search results diff --git a/modules/exporters/nbconvert/default.nix b/modules/exporters/nbconvert/default.nix index 362de16e..406bacee 100644 --- a/modules/exporters/nbconvert/default.nix +++ b/modules/exporters/nbconvert/default.nix @@ -55,6 +55,7 @@ symlinkJoin { passthru = { meta = { name = "nbconvert-exporters"; + displayName = "nbconvert Exporter ${nbconvert.version}"; description = "CodeDown exporters for PDF, HTML, LaTeX, slides, etc."; # To separate these out in search results diff --git a/modules/exporters/pandoc/default.nix b/modules/exporters/pandoc/default.nix index f2740f96..322dff4d 100644 --- a/modules/exporters/pandoc/default.nix +++ b/modules/exporters/pandoc/default.nix @@ -115,6 +115,7 @@ symlinkJoin { passthru = { meta = { name = "codedown-exporter-pandoc"; + displayName = "Pandoc Exporter ${pandoc.version}"; description = "CodeDown exporters for PDF, HTML, LaTeX, slides, etc."; # To separate these out in search results diff --git a/modules/exporters/typst/default.nix b/modules/exporters/typst/default.nix index 2ba1db5e..4a2ae88a 100644 --- a/modules/exporters/typst/default.nix +++ b/modules/exporters/typst/default.nix @@ -104,6 +104,7 @@ symlinkJoin { passthru = { meta = { name = "codedown-exporter-typst"; + displayName = "Typst Exporter ${typst.version}"; description = "CodeDown exporter using Typst."; # To separate these out in search results