diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 2b3a75c960..f1db4276ee 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -7,6 +7,9 @@ - Composition: links pointing to own board are purged [#2203](https://github.com/terrastruct/d2/pull/2203) - Syntax: reserved keywords must be unquoted [#2231](https://github.com/terrastruct/d2/pull/2231) +- Latex: Backslashes in Latex blocks do not escape [#2232](https://github.com/terrastruct/d2/pull/2232) + - This is a breaking change. Previously Latex blocks required escaping the backslash. So + for older D2 versions, you should remove the excess backslashes. #### Bugfixes ⛑️ diff --git a/d2parser/parse.go b/d2parser/parse.go index 3eb500484f..5d385f210d 100644 --- a/d2parser/parse.go +++ b/d2parser/parse.go @@ -1480,6 +1480,12 @@ func (p *parser) parseBlockString() *d2ast.BlockString { } if r != endHint { + if (bs.Tag == "latex" || bs.Tag == "tex") && r == '\\' { + // For LaTeX, where single backslash is common, we escape it so that users don't have to write double the backslashes + sb.WriteRune('\\') + sb.WriteRune('\\') + continue + } sb.WriteRune(r) continue } diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 376545c877..3430d33fcd 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -1381,30 +1381,24 @@ c: |md { name: "latex", script: `a: |latex -\\Huge{\\frac{\\alpha g^2}{\\omega^5} e^{[ -0.74\\bigl\\{\\frac{\\omega U_\\omega 19.5}{g}\\bigr\\}^{\\!-4}\\,]}} +\Huge{\frac{\alpha g^2}{\omega^5} e^{[ -0.74\bigl\{\frac{\omega U_\omega 19.5}{g}\bigr\}^{\!-4}\,]}} | - b: |latex e = mc^2 | - z: |latex -gibberish\\; math:\\sum_{i=0}^\\infty i^2 +gibberish\; math:\sum_{i=0}^\infty i^2 | - z -> a z -> b - a -> c b -> c sugar -> c c: mixed together - c -> solution: we get - Linear program: { formula: |latex - \\min_{ \\mathclap{\\substack{ x \\in \\mathbb{R}^n \\ x \\geq 0 \\ Ax \\leq b }}} c^T x + \min_{ \mathclap{\substack{ x \in \mathbb{R}^n \ x \geq 0 \ Ax \leq b }}} c^T x | } `, diff --git a/e2etests/testdata/files/grid_outside_labels.d2 b/e2etests/testdata/files/grid_outside_labels.d2 index 9f54d9234f..adce4a5543 100644 --- a/e2etests/testdata/files/grid_outside_labels.d2 +++ b/e2etests/testdata/files/grid_outside_labels.d2 @@ -4,61 +4,55 @@ container: ___________________________________container_________________________ amscd plugin: { ex: |tex - \\begin{CD} B @>{\\text{very long label}}>> C S^{{\\mathcal{W}}_\\Lambda}\\otimes T @>j>> T\\\\ @VVV V \\end{CD} + \begin{CD} B @>{\text{very long label}}>> C S^{{\mathcal{W}}_\Lambda}\otimes T @>j>> T\\ @VVV V \end{CD} | } braket plugin: { style.3d: true ex: |tex - \\bra{a}\\ket{b} + \bra{a}\ket{b} | } cancel plugin: { ex: |tex - \\cancel{Culture + 5} + \cancel{Culture + 5} | } - color plugin: { icon: https://icons.terrastruct.com/essentials/profits.svg icon.near: outside-right-center ex: |tex - \\textcolor{red}{y} = \\textcolor{green}{\\sin} x + \textcolor{red}{y} = \textcolor{green}{\sin} x | } - gensymb plugin: { ex: |tex - \\lambda = 10.6\\,\\micro\\mathrm{m} + \lambda = 10.6\,\micro\mathrm{m} | } - mhchem plugin: { style.multiple: true ex: |tex \ce{SO4^2- + Ba^2+ -> BaSO4 v} | } - physics plugin: { ex: |tex - \\var{F[g(x)]} - \\dd(\\cos\\theta) + \var{F[g(x)]} + \dd(\cos\theta) | } - multilines: { ex: |tex - \\displaylines{x = a + b \\\\ y = b + c} - \\sum_{k=1}^{n} h_{k} \\int_{0}^{1} \\bigl(\\partial_{k} f(x_{k-1}+t h_{k} e_{k}) -\\partial_{k} f(a)\\bigr) \\,dt + \displaylines{x = a + b \\ y = b + c} + \sum_{k=1}^{n} h_{k} \int_{0}^{1} \bigl(\partial_{k} f(x_{k-1}+t h_{k} e_{k}) -\partial_{k} f(a)\bigr) \,dt | } - asm: { ex: |latex - \\min_{ \\mathclap{\\substack{ x \\in \\mathbb{R}^n \\ x \\geq 0 \\ Ax \\leq b }}} c^T x + \min_{ \mathclap{\substack{ x \in \mathbb{R}^n \ x \geq 0 \ Ax \leq b }}} c^T x | } } diff --git a/e2etests/testdata/files/grid_with_latex.d2 b/e2etests/testdata/files/grid_with_latex.d2 index 9cb7245edf..e1a0f0fe27 100644 --- a/e2etests/testdata/files/grid_with_latex.d2 +++ b/e2etests/testdata/files/grid_with_latex.d2 @@ -19,7 +19,7 @@ y: { z: { grid-columns: 2 lim: |latex - \\lim_{h \\rightarrow 0 } \\frac{f(x+h)-f(x)}{h} + \lim_{h \rightarrow 0 } \frac{f(x+h)-f(x)}{h} | add: |latex 1 + 1 diff --git a/e2etests/testdata/stable/grid_outside_labels/dagre/board.exp.json b/e2etests/testdata/stable/grid_outside_labels/dagre/board.exp.json index 7140db2379..b04c458535 100644 --- a/e2etests/testdata/stable/grid_outside_labels/dagre/board.exp.json +++ b/e2etests/testdata/stable/grid_outside_labels/dagre/board.exp.json @@ -10,7 +10,7 @@ "x": 0, "y": 0 }, - "width": 1213, + "width": 1184, "height": 524, "opacity": 1, "strokeDash": 0, @@ -294,7 +294,7 @@ "x": 385, "y": 82 }, - "width": 255, + "width": 226, "height": 103, "opacity": 1, "strokeDash": 0, @@ -387,7 +387,7 @@ "x": 385, "y": 241 }, - "width": 324, + "width": 295, "height": 103, "opacity": 1, "strokeDash": 0, @@ -468,7 +468,7 @@ "x": 385, "y": 410 }, - "width": 314, + "width": 285, "height": 93, "opacity": 1, "strokeDash": 0, @@ -509,8 +509,8 @@ "x": 415, "y": 440 }, - "width": 254, - "height": 18, + "width": 225, + "height": 20, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -529,7 +529,7 @@ "fields": null, "methods": null, "columns": null, - "label": "\\ce{SO4^2- + Ba^2+ -> BaSO4 v}", + "label": "\\\\ce{SO4^2- + Ba^2+ -> BaSO4 v}", "fontSize": 16, "fontFamily": "DEFAULT", "language": "latex", @@ -537,8 +537,8 @@ "italic": false, "bold": false, "underline": false, - "labelWidth": 254, - "labelHeight": 18, + "labelWidth": 225, + "labelHeight": 20, "zIndex": 0, "level": 3 }, @@ -546,7 +546,7 @@ "id": "container.physics plugin", "type": "rectangle", "pos": { - "x": 729, + "x": 700, "y": 82 }, "width": 464, @@ -587,7 +587,7 @@ "id": "container.physics plugin.ex", "type": "text", "pos": { - "x": 759, + "x": 730, "y": 112 }, "width": 128, @@ -627,7 +627,7 @@ "id": "container.multilines", "type": "rectangle", "pos": { - "x": 729, + "x": 700, "y": 233 }, "width": 464, @@ -668,7 +668,7 @@ "id": "container.multilines.ex", "type": "text", "pos": { - "x": 759, + "x": 730, "y": 263 }, "width": 404, @@ -708,7 +708,7 @@ "id": "container.asm", "type": "rectangle", "pos": { - "x": 729, + "x": 700, "y": 401 }, "width": 464, @@ -749,7 +749,7 @@ "id": "container.asm.ex", "type": "text", "pos": { - "x": 759, + "x": 730, "y": 431 }, "width": 62, diff --git a/e2etests/testdata/stable/grid_outside_labels/dagre/sketch.exp.svg b/e2etests/testdata/stable/grid_outside_labels/dagre/sketch.exp.svg index 697ea22b1c..d7112f91c1 100644 --- a/e2etests/testdata/stable/grid_outside_labels/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/grid_outside_labels/dagre/sketch.exp.svg @@ -1,9 +1,9 @@ -___________________________________container____________________________________amscd plugin +___________________________________container____________________________________amscd plugin -braket plugincancel plugincolor plugingensymb pluginmhchem pluginphysics pluginmultilinesasmµ - - +braket plugincancel plugincolor plugingensymb pluginmhchem pluginphysics pluginmultilinesasmµ + + - - - - - - + + + + + + - - - - + + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/grid_outside_labels/elk/board.exp.json b/e2etests/testdata/stable/grid_outside_labels/elk/board.exp.json index 21f554c1e8..91fd56b518 100644 --- a/e2etests/testdata/stable/grid_outside_labels/elk/board.exp.json +++ b/e2etests/testdata/stable/grid_outside_labels/elk/board.exp.json @@ -10,7 +10,7 @@ "x": 12, "y": 12 }, - "width": 1323, + "width": 1294, "height": 521, "opacity": 1, "strokeDash": 0, @@ -294,7 +294,7 @@ "x": 437, "y": 58 }, - "width": 285, + "width": 256, "height": 148, "opacity": 1, "strokeDash": 0, @@ -387,7 +387,7 @@ "x": 437, "y": 226 }, - "width": 354, + "width": 325, "height": 133, "opacity": 1, "strokeDash": 0, @@ -468,8 +468,8 @@ "x": 437, "y": 389 }, - "width": 344, - "height": 123, + "width": 315, + "height": 124, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -509,8 +509,8 @@ "x": 482, "y": 434 }, - "width": 254, - "height": 18, + "width": 225, + "height": 20, "opacity": 1, "strokeDash": 0, "strokeWidth": 2, @@ -529,7 +529,7 @@ "fields": null, "methods": null, "columns": null, - "label": "\\ce{SO4^2- + Ba^2+ -> BaSO4 v}", + "label": "\\\\ce{SO4^2- + Ba^2+ -> BaSO4 v}", "fontSize": 16, "fontFamily": "DEFAULT", "language": "latex", @@ -537,8 +537,8 @@ "italic": false, "bold": false, "underline": false, - "labelWidth": 254, - "labelHeight": 18, + "labelWidth": 225, + "labelHeight": 20, "zIndex": 0, "level": 3 }, @@ -546,7 +546,7 @@ "id": "container.physics plugin", "type": "rectangle", "pos": { - "x": 811, + "x": 782, "y": 58 }, "width": 504, @@ -587,7 +587,7 @@ "id": "container.physics plugin.ex", "type": "text", "pos": { - "x": 861, + "x": 832, "y": 108 }, "width": 128, @@ -627,7 +627,7 @@ "id": "container.multilines", "type": "rectangle", "pos": { - "x": 811, + "x": 782, "y": 204 }, "width": 504, @@ -668,7 +668,7 @@ "id": "container.multilines.ex", "type": "text", "pos": { - "x": 861, + "x": 832, "y": 254 }, "width": 404, @@ -708,7 +708,7 @@ "id": "container.asm", "type": "rectangle", "pos": { - "x": 811, + "x": 782, "y": 376 }, "width": 504, @@ -749,7 +749,7 @@ "id": "container.asm.ex", "type": "text", "pos": { - "x": 861, + "x": 832, "y": 426 }, "width": 62, diff --git a/e2etests/testdata/stable/grid_outside_labels/elk/sketch.exp.svg b/e2etests/testdata/stable/grid_outside_labels/elk/sketch.exp.svg index 26c68cea54..480ae334ec 100644 --- a/e2etests/testdata/stable/grid_outside_labels/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/grid_outside_labels/elk/sketch.exp.svg @@ -1,9 +1,9 @@ -___________________________________container____________________________________amscd plugin +___________________________________container____________________________________amscd plugin -braket plugincancel plugincolor plugingensymb pluginmhchem pluginphysics pluginmultilinesasmµ - - +braket plugincancel plugincolor plugingensymb pluginmhchem pluginphysics pluginmultilinesasmµ + + - - - - - - + + + + + + - - - - + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/single-backslash-latex/dagre/board.exp.json b/e2etests/testdata/txtar/single-backslash-latex/dagre/board.exp.json new file mode 100644 index 0000000000..35b4e6fbce --- /dev/null +++ b/e2etests/testdata/txtar/single-backslash-latex/dagre/board.exp.json @@ -0,0 +1,129 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "formula", + "type": "rectangle", + "pos": { + "x": 10, + "y": 20 + }, + "width": 142, + "height": 142, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "formula", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 92, + "labelHeight": 36, + "labelPosition": "OUTSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "formula.equation", + "type": "text", + "pos": { + "x": 40, + "y": 50 + }, + "width": 82, + "height": 82, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "\\\\begin{equation} \\\\label{eq1}\n\\\\begin{split}\nA & = \\\\frac{\\\\\\\\pi r^2}{2} \\\\\\\\\n & = \\\\frac{1}{2} \\\\pi r^2\n\\\\end{split}\n\\\\end{equation}", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "latex", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 82, + "labelHeight": 82, + "zIndex": 0, + "level": 2 + } + ], + "connections": [], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/txtar/single-backslash-latex/dagre/sketch.exp.svg b/e2etests/testdata/txtar/single-backslash-latex/dagre/sketch.exp.svg new file mode 100644 index 0000000000..13b6c3cc8c --- /dev/null +++ b/e2etests/testdata/txtar/single-backslash-latex/dagre/sketch.exp.svg @@ -0,0 +1,832 @@ +formula + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/single-backslash-latex/elk/board.exp.json b/e2etests/testdata/txtar/single-backslash-latex/elk/board.exp.json new file mode 100644 index 0000000000..ff19a34d5a --- /dev/null +++ b/e2etests/testdata/txtar/single-backslash-latex/elk/board.exp.json @@ -0,0 +1,129 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "formula", + "type": "rectangle", + "pos": { + "x": 12, + "y": 12 + }, + "width": 182, + "height": 182, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "formula", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 92, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "formula.equation", + "type": "text", + "pos": { + "x": 62, + "y": 62 + }, + "width": 82, + "height": 82, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "\\\\begin{equation} \\\\label{eq1}\n\\\\begin{split}\nA & = \\\\frac{\\\\\\\\pi r^2}{2} \\\\\\\\\n & = \\\\frac{1}{2} \\\\pi r^2\n\\\\end{split}\n\\\\end{equation}", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "latex", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 82, + "labelHeight": 82, + "zIndex": 0, + "level": 2 + } + ], + "connections": [], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/txtar/single-backslash-latex/elk/sketch.exp.svg b/e2etests/testdata/txtar/single-backslash-latex/elk/sketch.exp.svg new file mode 100644 index 0000000000..7b60f04d64 --- /dev/null +++ b/e2etests/testdata/txtar/single-backslash-latex/elk/sketch.exp.svg @@ -0,0 +1,832 @@ +formula + + + + \ No newline at end of file diff --git a/e2etests/themes_test.go b/e2etests/themes_test.go index 4691ffce9b..989b5b4491 100644 --- a/e2etests/themes_test.go +++ b/e2etests/themes_test.go @@ -111,8 +111,8 @@ func main() { markdown -> code -> ex ex: |tex - \\displaylines{x = a + b \\\\ y = b + c} - \\sum_{k=1}^{n} h_{k} \\int_{0}^{1} \\bigl(\\partial_{k} f(x_{k-1}+t h_{k} e_{k}) -\\partial_{k} f(a)\\bigr) \\,dt + \displaylines{x = a + b \\ y = b + c} + \sum_{k=1}^{n} h_{k} \int_{0}^{1} \bigl(\partial_{k} f(x_{k-1}+t h_{k} e_{k}) -\partial_{k} f(a)\bigr) \,dt | `, }, @@ -218,8 +218,8 @@ func main() { markdown -> code -> ex ex: |tex - \\displaylines{x = a + b \\\\ y = b + c} - \\sum_{k=1}^{n} h_{k} \\int_{0}^{1} \\bigl(\\partial_{k} f(x_{k-1}+t h_{k} e_{k}) -\\partial_{k} f(a)\\bigr) \\,dt + \displaylines{x = a + b \\ y = b + c} + \sum_{k=1}^{n} h_{k} \int_{0}^{1} \bigl(\partial_{k} f(x_{k-1}+t h_{k} e_{k}) -\partial_{k} f(a)\bigr) \,dt | `, }, diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index 38b0ec213f..ad28b43f54 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -657,3 +657,15 @@ my_table: { } x -> my_table."shape" + +-- single-backslash-latex -- +formula: { + equation: |latex + \begin{equation} \label{eq1} + \begin{split} + A & = \frac{\\pi r^2}{2} \\ + & = \frac{1}{2} \pi r^2 + \end{split} + \end{equation} + | +}