Skip to content

Commit fe9681f

Browse files
committed
arrow symbols
1 parent d13e6ab commit fe9681f

File tree

8 files changed

+94
-42
lines changed

8 files changed

+94
-42
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tex2typst",
3-
"version": "0.2.2",
3+
"version": "0.2.6",
44
"description": "JavaScript library for converting TeX code to Typst",
55
"type": "module",
66
"main": "dist/index.js",

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { parseTex } from "./parser";
22
import { Tex2TypstOptions } from "./types";
33
import { TypstWriter } from "./writer";
4+
import { symbolMap } from "./map";
45

56

67
export function tex2typst(tex: string, options?: Tex2TypstOptions): string {
@@ -26,4 +27,4 @@ export function tex2typst(tex: string, options?: Tex2TypstOptions): string {
2627
return writer.finalize();
2728
}
2829

29-
export { Tex2TypstOptions };
30+
export { symbolMap, Tex2TypstOptions };

src/map.ts

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export const symbolMap = new Map<string, string>([
2-
['gets', 'arrow.l'],
32
['nonumber', ''],
43
['vec', 'arrow'],
54
['neq', 'eq.not'],
@@ -25,14 +24,15 @@ export const symbolMap = new Map<string, string>([
2524
['tfrac', 'frac'],
2625

2726
['boldsymbol', 'bold'],
28-
['mathbf', 'bold'],
2927
['mathbb', 'bb'],
28+
['mathbf', 'bold'],
3029
['mathcal', 'cal'],
30+
['mathit', 'italic'],
3131
['mathfrak', 'frak'],
32+
['mathrm', 'upright'],
3233
['mathsf', 'sans'],
3334
['mathtt', 'mono'],
3435

35-
['mathrm', 'upright'],
3636
['rm', 'upright'],
3737

3838
// TODO: \pmb need special logic to handle but it is not implemented now. See the commented test case.
@@ -58,18 +58,6 @@ export const symbolMap = new Map<string, string>([
5858
['equiv', 'equiv'],
5959
['propto', 'prop'],
6060

61-
/* arrows used in proofs */
62-
// tex: \implies \iff \leftrightarrow \longleftrightarrow \rightrightarrows
63-
// typst: arrow.r.double.long arrow.l.r.double.long arrow.l.r arrow.l.r.long arrows.rr
64-
['implies', 'arrow.r.double.long'],
65-
['Longrightarrow', 'arrow.r.double.long'], // Note: This macro is not supported by KaTeX
66-
['iff', 'arrow.l.r.double.long'],
67-
['Longleftrightarrow', 'arrow.l.r.double.long'], // Note: This macro is not supported by KaTeX
68-
['leftrightarrow', 'arrow.l.r'],
69-
['longleftrightarrow', 'arrow.l.r.long'],
70-
['rightrightarrows', 'arrows.rr'],
71-
72-
7361
/* left and right floor,ceil */
7462
// tex: \lfloor \rfloor \lceil \rceil
7563
// typst: ⌊ ⌋ ⌈ ⌉
@@ -80,19 +68,56 @@ export const symbolMap = new Map<string, string>([
8068
['lceil', '⌈'],
8169
['rceil', '⌉'],
8270

71+
/* arrows */
72+
['gets', 'arrow.l'],
73+
['hookleftarrow', 'arrow.l.hook'],
74+
['leftharpoonup', 'harpoon.lt'],
75+
['leftharpoondown', 'harpoon.lb'],
76+
['rightleftharpoons', 'harpoons.rtlb'],
77+
['longleftarrow', 'arrow.l.long'],
78+
['longrightarrow', 'arrow.r.long'],
79+
['longleftrightarrow', 'arrow.l.r.long'],
80+
['Longleftarrow', 'arrow.l.double.long'],
81+
['Longrightarrow', 'arrow.r.double.long'],
82+
['Longleftrightarrow', 'arrow.l.r.double.long'],
83+
['longmapsto', 'arrow.r.bar'],
84+
['hookrightarrow', 'arrow.r.hook'],
85+
['rightharpoonup', 'harpoon.rt'],
86+
['rightharpoondown', 'harpoon.rb'],
87+
['iff', 'arrow.l.r.double.long'],
88+
['implies', 'arrow.r.double.long'],
89+
['uparrow', 'arrow.t'],
90+
['downarrow', 'arrow.b'],
91+
['updownarrow', 'arrow.t.b'],
92+
['Uparrow', 'arrow.t.double'],
93+
['Downarrow', 'arrow.b.double'],
94+
['Updownarrow', 'arrow.t.b.double'],
95+
['nearrow', 'arrow.tr'],
96+
['searrow', 'arrow.br'],
97+
['swarrow', 'arrow.bl'],
98+
['nwarrow', 'arrow.tl'],
99+
['leadsto', 'arrow.squiggly'],
100+
101+
['leftleftarrows', 'arrows.ll'],
102+
['rightrightarrows', 'arrows.rr'],
103+
83104

84105
['Cap', 'sect.double'],
85106
['Cup', 'union.double'],
86107
['Delta', 'Delta'],
87108
['Gamma', 'Gamma'],
88109
['Join', 'join'],
89110
['Lambda', 'Lambda'],
111+
['Leftarrow', 'arrow.l.double'],
112+
['Leftrightarrow', 'arrow.l.r.double'],
90113
['Longrightarrow', 'arrow.r.double.long'],
91114
['Omega', 'Omega'],
115+
['P', 'pilcrow'],
92116
['Phi', 'Phi'],
93117
['Pi', 'Pi'],
94118
['Psi', 'Psi'],
95-
['Rightarrow', 'arrow.double'],
119+
['Rightarrow', 'arrow.r.double'],
120+
['S', 'section'],
96121
['Sigma', 'Sigma'],
97122
['Theta', 'Theta'],
98123
['aleph', 'alef'],
@@ -126,6 +151,7 @@ export const symbolMap = new Map<string, string>([
126151
['colon', 'colon'],
127152
['cong', 'tilde.equiv'],
128153
['coprod', 'product.co'],
154+
['copyright', 'copyright'],
129155
['cup', 'union'],
130156
['curlyvee', 'or.curly'],
131157
['curlywedge', 'and.curly'],
@@ -179,7 +205,7 @@ export const symbolMap = new Map<string, string>([
179205
['leqslant', 'lt.eq.slant'],
180206
['lhd', 'triangle.l'],
181207
['ll', 'lt.double'],
182-
['longmapsto', 'arrow.long.bar'],
208+
['longmapsto', 'arrow.bar.long'],
183209
['longrightarrow', 'arrow.long'],
184210
['lor', 'or'],
185211
['ltimes', 'times.l'],

src/parser.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,25 @@ const UNARY_COMMANDS = [
55
'sqrt',
66
'text',
77

8-
'arccos',
9-
'arcsin',
10-
'arctan',
11-
'arg',
128
'bar',
139
'bold',
1410
'boldsymbol',
1511
'ddot',
16-
'det',
17-
'dim',
1812
'dot',
19-
'exp',
20-
'gcd',
2113
'hat',
22-
'ker',
2314
'mathbb',
2415
'mathbf',
2516
'mathcal',
17+
'mathfrak',
18+
'mathit',
19+
'mathrm',
2620
'mathscr',
2721
'mathsf',
2822
'mathtt',
29-
'mathrm',
30-
'max',
31-
'min',
32-
'mod',
3323
'operatorname',
3424
'overbrace',
3525
'overline',
3626
'pmb',
37-
'sup',
3827
'rm',
3928
'tilde',
4029
'underbrace',
@@ -295,17 +284,15 @@ function tokenize(latex: string): Token[] {
295284
throw new LatexParserError('Expecting command name after \\');
296285
}
297286
const firstTwoChars = latex.slice(pos, pos + 2);
298-
if (firstTwoChars === '\\\\') {
299-
token = { type: 'control', value: '\\\\' };
300-
pos += 2;
287+
if (['\\\\', '\\,'].includes(firstTwoChars)) {
288+
token = { type: 'control', value: firstTwoChars };
301289
} else if (['\\{','\\}', '\\%', '\\$', '\\&', '\\#', '\\_'].includes(firstTwoChars)) {
302290
token = { type: 'element', value: firstTwoChars };
303-
pos += 2;
304291
} else {
305292
const command = eat_command_name(latex, pos + 1);
306293
token = { type: 'command', value: '\\' + command};
307-
pos += 1 + command.length;
308294
}
295+
pos += token.value.length;
309296
break;
310297
}
311298
default: {
@@ -502,6 +489,8 @@ export class LatexParser {
502489
throw new LatexParserError("Unmatched '}'");
503490
case '\\\\':
504491
return [{ type: 'control', content: '\\\\' }, start + 1];
492+
case '\\,':
493+
return [{ type: 'control', content: '\\,' }, start + 1];
505494
case '_': {
506495
let [sub, pos] = this.parseNextExpr(tokens, start + 1);
507496
let sup: TexNode | undefined = undefined;

src/writer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ export class TypstWriter {
283283
} else if (node.type === 'control') {
284284
if (node.content === '\\\\') {
285285
this.queue.push({ type: 'symbol', content: node.content });
286+
} else if (node.content === '\\,') {
287+
this.queue.push({ type: 'symbol', content: 'thin' });
286288
} else {
287289
throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
288290
}

test/cheat-sheet.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import path from 'path';
22
import toml from 'toml';
33
import fs from 'node:fs';
44
import { describe, it, test, expect } from 'vitest';
5-
import { tex2typst } from '../src';
5+
import { tex2typst, symbolMap } from '../src';
66

77
interface CheatSheet {
88
math_commands: { [key: string]: string };
@@ -38,6 +38,7 @@ describe('cheat sheet', () => {
3838
const expected = value;
3939
const result = tex2typst(input);
4040
expect(result).toBe(expected);
41+
expect(symbolMap.get(key)).toBe(expected);
4142
}
4243
});
4344
});

test/cheat-sheet.toml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ ln = "ln"
3636
log = "log"
3737
mathbb = "bb"
3838
mathcal = "cal"
39+
mathit = "italic"
3940
mathsf = "sans"
41+
mathrm = "upright"
4042
mathtt = "mono"
4143
max = "max"
4244
min = "min"
@@ -61,18 +63,41 @@ vec = "arrow"
6163
widehat = "hat"
6264

6365
[math_symbols]
66+
gets = "arrow.l"
67+
hookleftarrow = "arrow.l.hook"
68+
leftharpoonup = "harpoon.lt"
69+
leftharpoondown = "harpoon.lb"
70+
rightleftharpoons = "harpoons.rtlb"
71+
longleftarrow = "arrow.l.long"
72+
hookrightarrow = "arrow.r.hook"
73+
rightharpoonup = "harpoon.rt"
74+
rightharpoondown = "harpoon.rb"
75+
iff = "arrow.l.r.double.long"
76+
Uparrow = "arrow.t.double"
77+
Downarrow = "arrow.b.double"
78+
Updownarrow = "arrow.t.b.double"
79+
nearrow = "arrow.tr"
80+
searrow = "arrow.br"
81+
82+
6483
Cap = "sect.double"
6584
Cup = "union.double"
6685
Delta = "Delta"
6786
Gamma = "Gamma"
6887
Join = "join"
6988
Lambda = "Lambda"
89+
Leftarrow = "arrow.l.double"
90+
Leftrightarrow = "arrow.l.r.double"
91+
Longleftarrow = "arrow.l.double.long"
92+
Longleftrightarrow = "arrow.l.r.double.long"
7093
Longrightarrow = "arrow.r.double.long"
7194
Omega = "Omega"
95+
P = "pilcrow"
7296
Phi = "Phi"
7397
Pi = "Pi"
7498
Psi = "Psi"
75-
Rightarrow = "arrow.double"
99+
Rightarrow = "arrow.r.double"
100+
S = "section"
76101
Sigma = "Sigma"
77102
Theta = "Theta"
78103
aleph = "alef"
@@ -106,6 +131,7 @@ circ = "circle.small" # 'circle.small' or 'compose'
106131
colon = "colon"
107132
cong = "tilde.equiv"
108133
coprod = "product.co"
134+
copyright = "copyright"
109135
cup = "union"
110136
curlyvee = "or.curly"
111137
curlywedge = "and.curly"
@@ -153,13 +179,14 @@ ldots = "dots.l"
153179
le = "lt.eq"
154180
leadsto = "arrow.squiggly"
155181
leftarrow = "arrow.l"
182+
leftleftarrows = "arrows.ll"
156183
leftthreetimes = "times.three.l"
157184
leftrightarrow = "arrow.l.r"
158185
leq = "lt.eq"
159186
leqslant = "lt.eq.slant"
160187
lhd = "triangle.l"
161188
ll = "lt.double"
162-
longmapsto = "arrow.long.bar"
189+
longmapsto = "arrow.bar.long"
163190
longrightarrow = "arrow.long"
164191
ltimes = "times.l"
165192
mapsto = "arrow.bar"

test/math.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,10 @@ cases:
293293
a + b // co
294294
c + d
295295
e + f // co
296-
g + h
296+
g + h
297+
- title: mod
298+
tex: a^{p-1} \equiv 1 \mod{p}
299+
typst: a^(p - 1) equiv 1 mod p
300+
- title: thin space
301+
tex: a \, b
302+
typst: a thin b

0 commit comments

Comments
 (0)