tlienart / Franklin.jl

(yet another) static site generator. Simple, customisable, fast, maths with KaTeX, code evaluation, optional pre-rendering, in Julia.
https://franklinjl.org
MIT License
940 stars 111 forks source link

Raw versus processed text in environments #659

Closed epatters closed 3 years ago

epatters commented 3 years ago

I'm excited to use the new environments feature. For example, I currently have a command for inserting tikz-cd figures:

\newcommand{\tikzcd}[2]{
```julia:./!#1
#hideall
using TikzCDs
save(SVG(joinpath(@OUTPUT, "!#1.svg")), TikzCD(raw"""!#2"""))

\fig{./!#1} }


This command works well but I'd rather it be an environment. When I try

\newenvironment{tikzcd}[1]{

#hideall
using TikzCDs
save(SVG(joinpath(@OUTPUT, "!#1.svg")),
     TikzCD(raw"""}{
"""))

\fig{./!#1} }


I get errors like 

ERROR: Franklin.LxObjError("Command or environment '\arrow' was used before it was\ndefined.")



suggesting that Franklin is now trying to interpret LaTeX macros as Franklin commands. Is there a better way to implement this environment?
tlienart commented 3 years ago

Oh wow, I like this spirit! ok so here you do something that's a bit too crazy for Franklin because you split a code environment in two, I know it might seem like a small thing to "join them first then parse" but that's currently not allowed in a simple \newenvironment (it could be allowed but it's a bit tricky and might take a while, in short the PRE and POST bracket are parsed separately from the overall content so each needs to be able to be parsed on its own (which I should probably put explicitly in the docs)).

However it's easy to achieve what you want with a little bit of Julia. Put this in your utils.jl:

function env_tikzcd(e, _)
  content = strip(Franklin.content(e)) # what's effectively between the begin and the end
  name = strip(Franklin.content(e.braces[1])) # what's in the "option" brace
  return """
    ```julia:./$name
    #hideall
    using TikzCDs
    save(SVG(joinpath(@OUTPUT, "$name.svg")), TikzCD($content))
\fig{./$name}
"""

end



And this would be called with `\begin{tikzcd}{name}tikz code\end{tikzcd}` 

I haven't tried this, you might have to adjust a little bit for instance to escape the `content` string in case Tikz doesn't like it; but that shouldn't be too hard hopefully. Let me know how it goes!

**Note**: if you meet errors etc, I'll try tomorrow (it's night time here) to come up with a working example. If it does work (modulo small fixes) maybe you can do a PR for the demos? 
epatters commented 3 years ago

Thanks a lot, I didn't know you could define environments in Julia code. This approach works great!

tlienart commented 3 years ago

Cool! I imagine you figured that the #1 needed to be replaced by $name, sorry I had typed my answer a bit in a rush.

Glad it works for you, looking forward to seeing the result 😁

epatters commented 3 years ago

In case it's helpful to anyone else, here's the final code:

function env_tikzcd(e, _)
  content = strip(Franklin.content(e))
  name = strip(Franklin.content(e.braces[1]))
  return """
    ```julia:./$name
    #hideall
    using TikzCDs
    save(SVG(joinpath(@OUTPUT, "$name.svg")),
         TikzCD(raw\"\"\"$content\"\"\"))
\\fig{./$name}

""" end