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 @@
-
\ 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}
+ |
+}