Skip to content

Commit a89f6b1

Browse files
committed
add enumerate and itemize environments for jupyter notebook markdown rendering
1 parent 0ec2a12 commit a89f6b1

File tree

2 files changed

+182
-85
lines changed

2 files changed

+182
-85
lines changed

src/packages/jupyter/redux/actions.ts

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
import { SyncDB } from "@cocalc/sync/editor/db/sync";
4141
import type { Client } from "@cocalc/sync/client/types";
4242
import { once } from "@cocalc/util/async-utils";
43+
import latexEnvs from "@cocalc/util/latex-envs";
4344

4445
const { close, required, defaults } = misc;
4546

@@ -2729,6 +2730,8 @@ export abstract class JupyterActions extends Actions<JupyterStoreState> {
27292730
};
27302731

27312732
processRenderedMarkdown = ({ value, id }: { value: string; id: string }) => {
2733+
value = latexEnvs(value);
2734+
27322735
const labelRegExp = /\s*\\label\{.*?\}\s*/g;
27332736
if (this.labels == null) {
27342737
const labels = (this.labels = {});
@@ -2778,94 +2781,10 @@ export abstract class JupyterActions extends Actions<JupyterStoreState> {
27782781
return `[${tag}](#id=${id})`;
27792782
});
27802783

2781-
const figures = transformFigures(noRefs);
2782-
2783-
return figures;
2784+
return noRefs;
27842785
};
27852786
}
27862787

2787-
/*
2788-
Turn this:
2789-
2790-
---
2791-
2792-
...
2793-
2794-
\begin{figure}
2795-
\centering
2796-
\centerline{\includegraphics[width=WIDTH]{URL}}
2797-
\caption{CAPTION}
2798-
\end{figure}
2799-
2800-
...
2801-
2802-
---
2803-
2804-
into this:
2805-
2806-
---
2807-
2808-
...
2809-
2810-
<div style="text-align:center"><img src="URL" style="width:WIDTH"/></div>
2811-
\begin{equation}
2812-
\text{CAPTION}
2813-
\end{equation}
2814-
2815-
...
2816-
2817-
---
2818-
2819-
There can be lots of figures.
2820-
2821-
*/
2822-
2823-
function transformFigures(content: string): string {
2824-
while (true) {
2825-
const i = content.indexOf("\\begin{figure}");
2826-
if (i == -1) {
2827-
return content;
2828-
}
2829-
const j = content.indexOf("\\end{figure}");
2830-
if (j == -1) {
2831-
return content;
2832-
}
2833-
const k = content.indexOf("\\includegraphics");
2834-
if (k == -1) {
2835-
return content;
2836-
}
2837-
const c = content.indexOf("\\caption{");
2838-
if (c == -1) {
2839-
return content;
2840-
}
2841-
const c2 = content.lastIndexOf("}", j);
2842-
if (c2 == -1) {
2843-
return content;
2844-
}
2845-
2846-
const w = content.indexOf("width=");
2847-
const w2 = content.indexOf("{", k);
2848-
const w3 = content.indexOf("}", k);
2849-
if (w2 == -1 || w3 == -1) {
2850-
return content;
2851-
}
2852-
let style = "";
2853-
if (w != -1) {
2854-
style = `width:${content.slice(w + "width=".length, w2 - 1)}`;
2855-
}
2856-
const url = content.slice(w2 + 1, w3);
2857-
const caption = content.slice(c + "\\caption{".length, c2);
2858-
2859-
const md = `\n\n<div style="text-align:center"><img src="${url}" style="${style}"/></div>\n\n
2860-
\\begin{equation}
2861-
\\text{${caption}}
2862-
\\end{equation}\n\n`;
2863-
2864-
content =
2865-
content.slice(0, i) + md + content.slice(j + "\\end{figure}".length);
2866-
}
2867-
}
2868-
28692788
function extractLabel(content: string): string {
28702789
const i = content.indexOf("{");
28712790
const j = content.lastIndexOf("}");

src/packages/util/latex-envs.ts

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
Limited support for some latex environments, only for rendering (not editing).
3+
*/
4+
5+
export default function latexEnvs(value: string): string {
6+
value = transformFigures(value);
7+
value = transformItemEnvironments(value);
8+
return value;
9+
}
10+
11+
/*
12+
transformFigures -- dumb parser to turn this:
13+
14+
---
15+
16+
...
17+
18+
\begin{figure}
19+
\centering
20+
\centerline{\includegraphics[width=WIDTH]{URL}}
21+
\caption{CAPTION}
22+
\end{figure}
23+
24+
...
25+
26+
---
27+
28+
into this:
29+
30+
---
31+
32+
...
33+
34+
<div style="text-align:center"><img src="URL" style="width:WIDTH"/></div>
35+
\begin{equation}
36+
\text{CAPTION}
37+
\end{equation}
38+
39+
...
40+
41+
---
42+
43+
There can be lots of figures.
44+
*/
45+
46+
function transformFigures(content: string): string {
47+
while (true) {
48+
const i = content.indexOf("\\begin{figure}");
49+
if (i == -1) {
50+
return content;
51+
}
52+
const j = content.indexOf("\\end{figure}");
53+
if (j == -1) {
54+
return content;
55+
}
56+
const k = content.indexOf("\\includegraphics");
57+
if (k == -1) {
58+
return content;
59+
}
60+
const c = content.indexOf("\\caption{");
61+
if (c == -1) {
62+
return content;
63+
}
64+
const c2 = content.lastIndexOf("}", j);
65+
if (c2 == -1) {
66+
return content;
67+
}
68+
69+
const w = content.indexOf("width=");
70+
const w2 = content.indexOf("{", k);
71+
const w3 = content.indexOf("}", k);
72+
if (w2 == -1 || w3 == -1) {
73+
return content;
74+
}
75+
let style = "";
76+
if (w != -1) {
77+
style = `width:${content.slice(w + "width=".length, w2 - 1)}`;
78+
}
79+
const url = content.slice(w2 + 1, w3);
80+
const caption = content.slice(c + "\\caption{".length, c2);
81+
82+
const md = `\n\n<div style="text-align:center"><img src="${url}" style="${style}"/></div>\n\n
83+
\\begin{equation}
84+
\\text{${caption}}
85+
\\end{equation}\n\n`;
86+
87+
content =
88+
content.slice(0, i) + md + content.slice(j + "\\end{figure}".length);
89+
}
90+
}
91+
92+
/*
93+
transformEnumerate -- dumb parser to turn this:
94+
95+
---
96+
97+
...
98+
99+
\begin{enumerate}
100+
\item ITEM1
101+
\item ITEM2
102+
...
103+
\end{enumerate}
104+
105+
...
106+
107+
---
108+
109+
into this:
110+
111+
---
112+
113+
...
114+
115+
1. ITEM1
116+
117+
1. ITEM2
118+
119+
...
120+
121+
---
122+
123+
and
124+
125+
---
126+
127+
\begin{itemize}
128+
\item ITEM1
129+
\item ITEM2
130+
...
131+
\end{itemize}
132+
133+
into
134+
135+
- ITEM1
136+
137+
- ITEM2
138+
139+
*/
140+
141+
function transformItemEnvironments(content: string) {
142+
for (const type of ["itemize", "enumerate"]) {
143+
content = transformItemsType(content, type as "itemize" | "enumerate");
144+
}
145+
return content;
146+
}
147+
148+
function transformItemsType(
149+
content: string,
150+
type: "itemize" | "enumerate",
151+
): string {
152+
while (true) {
153+
const BEGIN = `\\begin{${type}}`;
154+
const i = content.indexOf(BEGIN);
155+
if (i == -1) {
156+
return content;
157+
}
158+
const END = `\\end{${type}}`;
159+
const j = content.indexOf(END);
160+
if (j == -1) {
161+
return content;
162+
}
163+
164+
const body = content.slice(i + BEGIN.length + 1, j);
165+
const items = body
166+
.split("\\item")
167+
.filter((x) => x.trim())
168+
.map((x) => (type == "itemize" ? "- " : "1. ") + x)
169+
.join("\n\n");
170+
content =
171+
content.slice(0, i) +
172+
"\n\n" +
173+
items +
174+
"\n\n" +
175+
content.slice(j + END.length + 1);
176+
}
177+
return content;
178+
}

0 commit comments

Comments
 (0)