Intro
As I mentioned in the Intro of the Haskell setup post, tooling changes often and becomes outdated quickly. So why write this post at all? Honestly, it is mostly for me, like much of this blog I don’t think I have (m)any readers π. . My math notes are infrequent enough (my last one was six months ago) that I run the risk of forgetting the details of this Rube Goldberg machine of a workflow and then I won’t be able to maintain or fix it.
Workflow
The setup is tailor-made to support math_notes, a fun little vanity project where I write up something related to math (a solution to a problem or a deep dive into some concept). The repo consists of a bunch of subdirectories, each holding an article. The parent directory has a LaTeX file that collects all of them into a “book”. This layout requires a little bit of trickery. I want the articles to stand on their own and be able to be compiled into their own little PDFs. But I also want their content to be included in the book PDF.
To achieve this content duality of standalone article and being contained inside the book, I chose the straightforward approach of breaking up the article into header, content and footer. All three pieces are imported into the article LaTeX file. And then the content piece is also imported into the book LaTeX file. Speaking of importing: I purposefully said import
and not include
. The LaTeX include and input statements have the drawback that the included content cannot refer to its outside sources (like images or listings) relative to itself (aka in the article directory) when used from the book. This conflicts with the dual nature of the content. Luckily there exists LaTeX import package, which does exactly what I want.
The subdirectory (using the latest note as an example) looks like this:
burnside_polya
βββ burnside_polya.tex
βββ burnside_polya_content.tex
βββ burnside_polya_footer.tex
βββ burnside_polya_header.tex
......
βββ ......
The book imports only burnside_polya_content.tex
and the article burnside_polya.tex
includes all three parts in the obvious way (no import package necessary, plain old input
suffices):
\input{burnside_polya_header.tex}
\input{burnside_polya_content.tex}
\input{burnside_polya_footer.tex}
Inside the subdirectory I build the article PDF like so:
latexmk -synctex=1 -interaction=nonstopmode -pdf burnside_polya.tex
In the parent directory I build book.pdf
similarly:
latexmk -synctex=1 -interaction=nonstopmode -pdf book.tex
When I want to write a new article, I invoke templeton to generate an article scaffolding (this saves me the tedium of setting up the four LaTeX files necessary for an article):
templeton --project latex --data Title=foo
with the usual header, footer and input statements (here’s the templeton latex.yaml template used in the invocation).
foo
βββ foo.tex
βββ foo_content.tex
βββ foo_footer.tex
βββ foo_header.tex
Editor and PDF Preview
I use Nova
I have a Nova subscription and also use Nova to compose blog posts on this Hugo blog. See Nova and Hugo setup for how I set that up.
for editing LaTeX. I added the Nova LaTeX Extension and installed the texlab LSP server. The Nova LaTeX Extension
recommends using Skim for the PDF previews because Skim supports SyncTeX.
Everything works great out of the box but I tweaked it a little to support building and previewing my dual use article content.
For the tweaks I wrote a little bit of glue code in the form of a Go command line app called latexnova This is similar to the glue code hugonova for my Hugo Nova blog setup. .
Let’s first tackle the build
aspect. I want to build the standalone article whenever the active editor selection is on any file in the article subdirectory (header, footer, content or wrapping article LateX file, doesn’t matter). The latexnova build
command has some intelligence to figure this out and correctly drive latexmk
. In Nova I defined a custom task that invokes latexnova
and passes in the current file and current line number:
Similarly, when the active file in the editor is anything in the parent directory, then the book gets built.
The Preview
aspect is using this same logic, keying off the active file selection to decide what to preview. latexnova
has a second command: latexnova server
that runs a small web server that serves up the correct PDF according to what is selected in the editor window. This way Nova builtin preview always shows the PDF I am interested in (in the screenshot below latexnova server
runs on localhost:5432
):
What about Skim and SyncTeX? For forward searching (aka “show me the place in the PDF corresponding to what is selected in the editor”) I hitchhike on the previously described build command which doesn’t just drive latexmk
but also opens the PDF in Skim with the right place (page and place in the page) in the PDF selected.
For backward searching (aka “open the place in the editor corresponding to what I command-clicked in the PDF”) I just rely on the SyncTeX metadata and Skim’s support for Nova, which just works (yay π).
Software Requirements
Here again a recap of all the needed software:
In addition to the above straightforward installations, we have these glue code command line apps, which unfortunately you need to compile yourself with a proper Go dev setup.