From 2f99339dcf93ef50b766263297785a32d9c35250 Mon Sep 17 00:00:00 2001
From: Hrishikesh Barman <geekodour@users.noreply.github.com>
Date: Sat, 30 Sep 2023 00:05:26 +0530
Subject: [PATCH] feat: add transformations for latex in oxhugofm (#510)

ox-hugo currently supports the following syntax for latex equations:
- https://orgmode.org/manual/LaTeX-fragments.html
- https://ox-hugo.scripter.co/doc/equations

This syntax is supported by mathjax as is mentioned in the ox-hugo documentation.

But quartz uses remark-math which has some issues with the \( \) syntax.
See https://github.com/remarkjs/remark-math/issues/39

This change adds few more transformations to the OxHugoFlavouredMarkdown
plugin, which makes a best effort conversion of this syntax into what
the Quartz Latex transformer plugin supports.

With these changes, the generated files show latex formatting with
default quartz configuration.

Sidenote on `\_` escape by ox-hugo:

ox-hugo escapes, _ using \_, we match against it after we transform
equations into what quartz supports($$ and $).

This could be achieved using lookaround like regex as follows
```js
(?<=(\$|\$\$)[\s\S]*) -> Positive lookbehind for $ or $$
\\_ -> Matches \_
(?=[\s\S]*(?:\1)) Positive lookahead for $ or $$ if matched
const escapedUnderscoreRegex = new RegExp(/(?<=(\$|\$\$)[\s\S]*)\\_(?=[\s\S]*(?:\1))/, "g")
````

But since lookahead/behind can slow things down on large files, we just
look up all equations with $ and $$ delimiters and then try replacing \_
---
 docs/features/OxHugo compatibility.md   |  1 +
 quartz/plugins/transformers/oxhugofm.ts | 35 +++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/docs/features/OxHugo compatibility.md b/docs/features/OxHugo compatibility.md
index b25167f..3143eb1 100644
--- a/docs/features/OxHugo compatibility.md	
+++ b/docs/features/OxHugo compatibility.md	
@@ -32,6 +32,7 @@ Quartz by default doesn't understand `org-roam` files as they aren't Markdown. Y
   - `replaceFigureWithMdImg`: Whether to replace `<figure/>` with `![]()`
 - Formatting
   - `removeHugoShortcode`: Whether to remove hugo shortcode syntax (`{{}}`)
+  - `replaceOrgLatex`: Whether to replace org-mode formatting for latex fragments with what `Plugin.Latex` supports.
 
 > [!warning]
 >
diff --git a/quartz/plugins/transformers/oxhugofm.ts b/quartz/plugins/transformers/oxhugofm.ts
index 0d7b919..6e70bb1 100644
--- a/quartz/plugins/transformers/oxhugofm.ts
+++ b/quartz/plugins/transformers/oxhugofm.ts
@@ -9,6 +9,9 @@ export interface Options {
   removeHugoShortcode: boolean
   /** Replace <figure/> with ![]() */
   replaceFigureWithMdImg: boolean
+
+  /** Replace org latex fragments with $ and $$ */
+  replaceOrgLatex: boolean
 }
 
 const defaultOptions: Options = {
@@ -16,12 +19,27 @@ const defaultOptions: Options = {
   removePredefinedAnchor: true,
   removeHugoShortcode: true,
   replaceFigureWithMdImg: true,
+  replaceOrgLatex: true,
 }
 
 const relrefRegex = new RegExp(/\[([^\]]+)\]\(\{\{< relref "([^"]+)" >\}\}\)/, "g")
 const predefinedHeadingIdRegex = new RegExp(/(.*) {#(?:.*)}/, "g")
 const hugoShortcodeRegex = new RegExp(/{{(.*)}}/, "g")
 const figureTagRegex = new RegExp(/< ?figure src="(.*)" ?>/, "g")
+// \\\\\( -> matches \\(
+// (.+?) -> Lazy match for capturing the equation
+// \\\\\) -> matches \\)
+const inlineLatexRegex = new RegExp(/\\\\\((.+?)\\\\\)/, "g")
+// (?:\\begin{equation}|\\\\\(|\\\\\[) -> start of equation
+// ([\s\S]*?) -> Matches the block equation
+// (?:\\\\\]|\\\\\)|\\end{equation}) -> end of equation
+const blockLatexRegex = new RegExp(
+  /(?:\\begin{equation}|\\\\\(|\\\\\[)([\s\S]*?)(?:\\\\\]|\\\\\)|\\end{equation})/,
+  "g",
+)
+// \$\$[\s\S]*?\$\$ -> Matches block equations
+// \$.*?\$ -> Matches inline equations
+const quartzLatexRegex = new RegExp(/\$\$[\s\S]*?\$\$|\$.*?\$/, "g")
 
 /**
  * ox-hugo is an org exporter backend that exports org files to hugo-compatible
@@ -67,6 +85,23 @@ export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options> |
           return `![](${src})`
         })
       }
+
+      if (opts.replaceOrgLatex) {
+        src = src.toString()
+        src = src.replaceAll(inlineLatexRegex, (value, ...capture) => {
+          const [eqn] = capture
+          return `$${eqn}$`
+        })
+        src = src.replaceAll(blockLatexRegex, (value, ...capture) => {
+          const [eqn] = capture
+          return `$$${eqn}$$`
+        })
+
+        // ox-hugo escapes _ as \_
+        src = src.replaceAll(quartzLatexRegex, (value) => {
+          return value.replaceAll("\\_", "_")
+        })
+      }
       return src
     },
   }