tex2typst 0.3.24 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,10 +33,9 @@ var TexToken = class _TexToken {
33
33
  }
34
34
  };
35
35
  var TexNode = class {
36
- constructor(type, head, args) {
36
+ constructor(type, head) {
37
37
  this.type = type;
38
38
  this.head = head ? head : TexToken.EMPTY;
39
- this.args = args;
40
39
  }
41
40
  // Note that this is only shallow equality.
42
41
  eq(other) {
@@ -91,16 +90,17 @@ var TexText = class extends TexNode {
91
90
  }
92
91
  };
93
92
  var TexGroup = class extends TexNode {
94
- constructor(args) {
95
- super("ordgroup", TexToken.EMPTY, args);
93
+ constructor(items) {
94
+ super("ordgroup", TexToken.EMPTY);
95
+ this.items = items;
96
96
  }
97
97
  serialize() {
98
- return this.args.map((n) => n.serialize()).flat();
98
+ return this.items.map((n) => n.serialize()).flat();
99
99
  }
100
100
  };
101
101
  var TexSupSub = class extends TexNode {
102
102
  constructor(data) {
103
- super("supsub", TexToken.EMPTY, []);
103
+ super("supsub", TexToken.EMPTY);
104
104
  this.base = data.base;
105
105
  this.sup = data.sup;
106
106
  this.sub = data.sub;
@@ -143,7 +143,8 @@ var TexSupSub = class extends TexNode {
143
143
  };
144
144
  var TexFuncCall = class extends TexNode {
145
145
  constructor(head, args, data = null) {
146
- super("funcCall", head, args);
146
+ super("funcCall", head);
147
+ this.args = args;
147
148
  this.data = data;
148
149
  }
149
150
  serialize() {
@@ -163,8 +164,9 @@ var TexFuncCall = class extends TexNode {
163
164
  }
164
165
  };
165
166
  var TexLeftRight = class extends TexNode {
166
- constructor(args, data) {
167
- super("leftright", TexToken.EMPTY, args);
167
+ constructor(data) {
168
+ super("leftright", TexToken.EMPTY);
169
+ this.body = data.body;
168
170
  this.left = data.left;
169
171
  this.right = data.right;
170
172
  }
@@ -172,17 +174,18 @@ var TexLeftRight = class extends TexNode {
172
174
  let tokens = [];
173
175
  tokens.push(new TexToken(2 /* COMMAND */, "\\left"));
174
176
  tokens.push(new TexToken(1 /* ELEMENT */, this.left ? this.left.value : "."));
175
- tokens = tokens.concat(this.args.map((n) => n.serialize()).flat());
177
+ tokens = tokens.concat(this.body.serialize());
176
178
  tokens.push(new TexToken(2 /* COMMAND */, "\\right"));
177
179
  tokens.push(new TexToken(1 /* ELEMENT */, this.right ? this.right.value : "."));
178
180
  return tokens;
179
181
  }
180
182
  };
181
183
  var TexBeginEnd = class extends TexNode {
182
- constructor(head, args, data) {
184
+ constructor(head, matrix, data = null) {
183
185
  assert(head.type === 3 /* LITERAL */);
184
- super("beginend", head, args);
185
- this.matrix = data;
186
+ super("beginend", head);
187
+ this.matrix = matrix;
188
+ this.data = data;
186
189
  }
187
190
  serialize() {
188
191
  let tokens = [];
@@ -230,6 +233,7 @@ function writeTexTokenBuffer(buffer, token) {
230
233
  no_need_space ||= buffer === "";
231
234
  no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(buffer) || buffer === "-" || buffer === "+";
232
235
  no_need_space ||= buffer.endsWith("&") && str === "=";
236
+ no_need_space ||= /\d$/.test(buffer) && /^[a-zA-Z]$/.test(str);
233
237
  }
234
238
  if (!no_need_space) {
235
239
  buffer += " ";
@@ -549,7 +553,8 @@ var TEX_BINARY_COMMANDS = [
549
553
  "dfrac",
550
554
  "tbinom",
551
555
  "overset",
552
- "underset"
556
+ "underset",
557
+ "textcolor"
553
558
  ];
554
559
  function unescape(str) {
555
560
  const chars = ["{", "}", "\\", "$", "&", "#", "_", "%"];
@@ -559,7 +564,7 @@ function unescape(str) {
559
564
  return str;
560
565
  }
561
566
  var rules_map = /* @__PURE__ */ new Map([
562
- // math `\begin{array}{cc}`
567
+ // match `\begin{array}{cc}`
563
568
  [
564
569
  String.raw`\\begin{(array|subarry)}{(.+?)}`,
565
570
  (s) => {
@@ -576,7 +581,7 @@ var rules_map = /* @__PURE__ */ new Map([
576
581
  }
577
582
  ],
578
583
  [
579
- String.raw`\\(text|operatorname|begin|end|hspace|array){(.+?)}`,
584
+ String.raw`\\(text|operatorname|textcolor|begin|end|hspace|array){(.+?)}`,
580
585
  (s) => {
581
586
  const match = s.reMatchArray();
582
587
  return [
@@ -809,16 +814,14 @@ var LatexParser = class {
809
814
  res.sub = sub;
810
815
  }
811
816
  if (num_prime > 0) {
812
- res.sup = new TexGroup([]);
817
+ const items = [];
813
818
  for (let i = 0; i < num_prime; i++) {
814
- res.sup.args.push(new TexToken(1 /* ELEMENT */, "'").toNode());
819
+ items.push(new TexToken(1 /* ELEMENT */, "'").toNode());
815
820
  }
816
821
  if (sup) {
817
- res.sup.args.push(sup);
818
- }
819
- if (res.sup.args.length === 1) {
820
- res.sup = res.sup.args[0];
822
+ items.push(sup);
821
823
  }
824
+ res.sup = items.length === 1 ? items[0] : new TexGroup(items);
822
825
  } else if (sup) {
823
826
  res.sup = sup;
824
827
  }
@@ -981,10 +984,9 @@ var LatexParser = class {
981
984
  }
982
985
  pos++;
983
986
  const [body, _] = this.parseGroup(tokens, exprInsideStart, exprInsideEnd);
984
- const args = [body];
985
987
  const left = leftDelimiter.value === "." ? null : leftDelimiter;
986
988
  const right = rightDelimiter.value === "." ? null : rightDelimiter;
987
- const res = new TexLeftRight(args, { left, right });
989
+ const res = new TexLeftRight({ body, left, right });
988
990
  return [res, pos];
989
991
  }
990
992
  parseBeginEndExpr(tokens, start) {
@@ -995,12 +997,10 @@ var LatexParser = class {
995
997
  assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
996
998
  const envName = tokens[pos + 1].value;
997
999
  pos += 3;
998
- const args = [];
1000
+ let data = null;
999
1001
  if (["array", "subarray"].includes(envName)) {
1000
1002
  pos += eat_whitespaces(tokens, pos).length;
1001
- const [arg, newPos] = this.parseNextArg(tokens, pos);
1002
- args.push(arg);
1003
- pos = newPos;
1003
+ [data, pos] = this.parseNextArg(tokens, pos);
1004
1004
  }
1005
1005
  pos += eat_whitespaces(tokens, pos).length;
1006
1006
  const exprInsideStart = pos;
@@ -1022,7 +1022,7 @@ var LatexParser = class {
1022
1022
  exprInside.pop();
1023
1023
  }
1024
1024
  const body = this.parseAligned(exprInside);
1025
- const res = new TexBeginEnd(new TexToken(3 /* LITERAL */, envName), args, body);
1025
+ const res = new TexBeginEnd(new TexToken(3 /* LITERAL */, envName), body, data);
1026
1026
  return [res, pos];
1027
1027
  }
1028
1028
  parseAligned(tokens) {
@@ -1052,7 +1052,7 @@ var LatexParser = class {
1052
1052
  group = new TexGroup([]);
1053
1053
  row.push(group);
1054
1054
  } else {
1055
- group.args.push(res);
1055
+ group.items.push(res);
1056
1056
  }
1057
1057
  }
1058
1058
  return allRows;
@@ -1122,10 +1122,9 @@ var TypstToken = class _TypstToken {
1122
1122
  }
1123
1123
  };
1124
1124
  var TypstNode = class {
1125
- constructor(type, head, args) {
1125
+ constructor(type, head) {
1126
1126
  this.type = type;
1127
1127
  this.head = head ? head : TypstToken.NONE;
1128
- this.args = args;
1129
1128
  }
1130
1129
  setOptions(options) {
1131
1130
  this.options = options;
@@ -1150,16 +1149,17 @@ var TypstTerminal = class extends TypstNode {
1150
1149
  }
1151
1150
  };
1152
1151
  var TypstGroup = class extends TypstNode {
1153
- constructor(args) {
1154
- super("group", TypstToken.NONE, args);
1152
+ constructor(items) {
1153
+ super("group", TypstToken.NONE);
1154
+ this.items = items;
1155
1155
  }
1156
1156
  isOverHigh() {
1157
- return this.args.some((n) => n.isOverHigh());
1157
+ return this.items.some((n) => n.isOverHigh());
1158
1158
  }
1159
1159
  };
1160
1160
  var TypstSupsub = class extends TypstNode {
1161
1161
  constructor(data) {
1162
- super("supsub", TypstToken.NONE, []);
1162
+ super("supsub", TypstToken.NONE);
1163
1163
  this.base = data.base;
1164
1164
  this.sup = data.sup;
1165
1165
  this.sub = data.sub;
@@ -1170,7 +1170,8 @@ var TypstSupsub = class extends TypstNode {
1170
1170
  };
1171
1171
  var TypstFuncCall = class extends TypstNode {
1172
1172
  constructor(head, args) {
1173
- super("funcCall", head, args);
1173
+ super("funcCall", head);
1174
+ this.args = args;
1174
1175
  }
1175
1176
  isOverHigh() {
1176
1177
  if (this.head.value === "frac") {
@@ -1181,47 +1182,48 @@ var TypstFuncCall = class extends TypstNode {
1181
1182
  };
1182
1183
  var TypstFraction = class extends TypstNode {
1183
1184
  constructor(args) {
1184
- super("fraction", TypstToken.NONE, args);
1185
+ super("fraction", TypstToken.NONE);
1186
+ this.args = args;
1185
1187
  }
1186
1188
  isOverHigh() {
1187
1189
  return true;
1188
1190
  }
1189
1191
  };
1190
1192
  var TypstLeftright = class extends TypstNode {
1191
- constructor(head, args, data) {
1192
- super("leftright", head, args);
1193
+ // head is either null or 'lr'
1194
+ constructor(head, data) {
1195
+ super("leftright", head);
1196
+ this.body = data.body;
1193
1197
  this.left = data.left;
1194
1198
  this.right = data.right;
1195
1199
  }
1196
1200
  isOverHigh() {
1197
- return this.args.some((n) => n.isOverHigh());
1201
+ return this.body.isOverHigh();
1198
1202
  }
1199
1203
  };
1200
- var TypstAlign = class extends TypstNode {
1201
- constructor(data) {
1202
- super("align", TypstToken.NONE, []);
1204
+ var TypstMatrixLike = class extends TypstNode {
1205
+ // head is 'mat', 'cases' or null
1206
+ constructor(head, data) {
1207
+ super("matrixLike", head);
1203
1208
  this.matrix = data;
1204
1209
  }
1205
1210
  isOverHigh() {
1206
1211
  return true;
1207
1212
  }
1208
- };
1209
- var TypstMatrix = class extends TypstNode {
1210
- constructor(data) {
1211
- super("matrix", TypstToken.NONE, []);
1212
- this.matrix = data;
1213
+ static {
1214
+ this.MAT = new TypstToken(1 /* SYMBOL */, "mat");
1213
1215
  }
1214
- isOverHigh() {
1215
- return true;
1216
+ static {
1217
+ this.CASES = new TypstToken(1 /* SYMBOL */, "cases");
1216
1218
  }
1217
1219
  };
1218
- var TypstCases = class extends TypstNode {
1219
- constructor(data) {
1220
- super("cases", TypstToken.NONE, []);
1221
- this.matrix = data;
1220
+ var TypstMarkupFunc = class extends TypstNode {
1221
+ constructor(head, fragments) {
1222
+ super("markupFunc", head);
1223
+ this.fragments = fragments;
1222
1224
  }
1223
1225
  isOverHigh() {
1224
- return true;
1226
+ return this.fragments.some((n) => n.isOverHigh());
1225
1227
  }
1226
1228
  };
1227
1229
 
@@ -1300,7 +1302,7 @@ var TypstWriter = class {
1300
1302
  this.inftyToOo = options.inftyToOo;
1301
1303
  this.optimize = options.optimize;
1302
1304
  }
1303
- writeBuffer(token) {
1305
+ writeBuffer(previousToken, token) {
1304
1306
  const str = token.toString();
1305
1307
  if (str === "") {
1306
1308
  return;
@@ -1317,7 +1319,11 @@ var TypstWriter = class {
1317
1319
  no_need_space ||= /^\s/.test(str);
1318
1320
  no_need_space ||= this.buffer.endsWith("&") && str === "=";
1319
1321
  no_need_space ||= this.buffer.endsWith("/") || str === "/";
1322
+ no_need_space ||= token.type === 3 /* LITERAL */;
1320
1323
  no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
1324
+ if (previousToken !== null) {
1325
+ no_need_space ||= previousToken.type === 3 /* LITERAL */;
1326
+ }
1321
1327
  if (!no_need_space) {
1322
1328
  this.buffer += " ";
1323
1329
  }
@@ -1367,7 +1373,7 @@ var TypstWriter = class {
1367
1373
  }
1368
1374
  case "group": {
1369
1375
  const node = abstractNode;
1370
- for (const item of node.args) {
1376
+ for (const item of node.items) {
1371
1377
  this.serialize(item);
1372
1378
  }
1373
1379
  break;
@@ -1383,9 +1389,7 @@ var TypstWriter = class {
1383
1389
  if (left) {
1384
1390
  this.queue.push(left);
1385
1391
  }
1386
- for (const item of node.args) {
1387
- this.serialize(item);
1388
- }
1392
+ this.serialize(node.body);
1389
1393
  if (right) {
1390
1394
  this.queue.push(right);
1391
1395
  }
@@ -1451,74 +1455,71 @@ var TypstWriter = class {
1451
1455
  this.appendWithBracketsIfNeeded(denominator);
1452
1456
  break;
1453
1457
  }
1454
- case "align": {
1458
+ case "matrixLike": {
1455
1459
  const node = abstractNode;
1456
1460
  const matrix = node.matrix;
1457
- matrix.forEach((row, i) => {
1458
- row.forEach((cell, j) => {
1459
- if (j > 0) {
1460
- this.queue.push(new TypstToken(2 /* ELEMENT */, "&"));
1461
+ let cell_sep;
1462
+ let row_sep;
1463
+ if (node.head.eq(TypstMatrixLike.MAT)) {
1464
+ cell_sep = new TypstToken(2 /* ELEMENT */, ",");
1465
+ row_sep = new TypstToken(2 /* ELEMENT */, ";");
1466
+ } else if (node.head.eq(TypstMatrixLike.CASES)) {
1467
+ cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1468
+ row_sep = new TypstToken(2 /* ELEMENT */, ",");
1469
+ } else if (node.head.eq(TypstToken.NONE)) {
1470
+ cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1471
+ row_sep = new TypstToken(1 /* SYMBOL */, "\\");
1472
+ }
1473
+ if (!node.head.eq(TypstToken.NONE)) {
1474
+ this.queue.push(node.head);
1475
+ this.insideFunctionDepth++;
1476
+ this.queue.push(TYPST_LEFT_PARENTHESIS);
1477
+ if (node.options) {
1478
+ for (const [key, value] of Object.entries(node.options)) {
1479
+ this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1461
1480
  }
1462
- this.serialize(cell);
1463
- });
1464
- if (i < matrix.length - 1) {
1465
- this.queue.push(new TypstToken(1 /* SYMBOL */, "\\"));
1466
- }
1467
- });
1468
- break;
1469
- }
1470
- case "matrix": {
1471
- const node = abstractNode;
1472
- const matrix = node.matrix;
1473
- this.queue.push(new TypstToken(1 /* SYMBOL */, "mat"));
1474
- this.insideFunctionDepth++;
1475
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1476
- if (node.options) {
1477
- for (const [key, value] of Object.entries(node.options)) {
1478
- this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1479
1481
  }
1480
1482
  }
1481
1483
  matrix.forEach((row, i) => {
1482
1484
  row.forEach((cell, j) => {
1483
1485
  this.serialize(cell);
1484
1486
  if (j < row.length - 1) {
1485
- this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1487
+ this.queue.push(cell_sep);
1486
1488
  } else {
1487
1489
  if (i < matrix.length - 1) {
1488
- this.queue.push(new TypstToken(2 /* ELEMENT */, ";"));
1490
+ this.queue.push(row_sep);
1489
1491
  }
1490
1492
  }
1491
1493
  });
1492
1494
  });
1493
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1494
- this.insideFunctionDepth--;
1495
+ if (!node.head.eq(TypstToken.NONE)) {
1496
+ this.queue.push(TYPST_RIGHT_PARENTHESIS);
1497
+ this.insideFunctionDepth--;
1498
+ }
1495
1499
  break;
1496
1500
  }
1497
- case "cases": {
1501
+ case "markupFunc": {
1498
1502
  const node = abstractNode;
1499
- const cases = node.matrix;
1500
- this.queue.push(new TypstToken(1 /* SYMBOL */, "cases"));
1501
- this.insideFunctionDepth++;
1503
+ this.queue.push(node.head);
1502
1504
  this.queue.push(TYPST_LEFT_PARENTHESIS);
1503
1505
  if (node.options) {
1504
- for (const [key, value] of Object.entries(node.options)) {
1505
- this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1506
+ const entries = Object.entries(node.options);
1507
+ for (let i = 0; i < entries.length; i++) {
1508
+ const [key, value] = entries[i];
1509
+ this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}`));
1510
+ if (i < entries.length - 1) {
1511
+ this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1512
+ }
1506
1513
  }
1507
1514
  }
1508
- cases.forEach((row, i) => {
1509
- row.forEach((cell, j) => {
1510
- this.serialize(cell);
1511
- if (j < row.length - 1) {
1512
- this.queue.push(new TypstToken(2 /* ELEMENT */, "&"));
1513
- } else {
1514
- if (i < cases.length - 1) {
1515
- this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1516
- }
1517
- }
1518
- });
1519
- });
1520
1515
  this.queue.push(TYPST_RIGHT_PARENTHESIS);
1521
- this.insideFunctionDepth--;
1516
+ this.queue.push(new TypstToken(3 /* LITERAL */, "["));
1517
+ for (const frag of node.fragments) {
1518
+ this.queue.push(new TypstToken(3 /* LITERAL */, "$"));
1519
+ this.serialize(frag);
1520
+ this.queue.push(new TypstToken(3 /* LITERAL */, "$"));
1521
+ }
1522
+ this.queue.push(new TypstToken(3 /* LITERAL */, "]"));
1522
1523
  break;
1523
1524
  }
1524
1525
  default:
@@ -1526,13 +1527,14 @@ var TypstWriter = class {
1526
1527
  }
1527
1528
  }
1528
1529
  appendWithBracketsIfNeeded(node) {
1529
- let need_to_wrap = ["group", "supsub", "align", "fraction", "empty"].includes(node.type);
1530
+ let need_to_wrap = ["group", "supsub", "matrixLike", "fraction", "empty"].includes(node.type);
1530
1531
  if (node.type === "group") {
1531
- if (node.args.length === 0) {
1532
+ const group = node;
1533
+ if (group.items.length === 0) {
1532
1534
  need_to_wrap = true;
1533
1535
  } else {
1534
- const first = node.args[0];
1535
- const last = node.args[node.args.length - 1];
1536
+ const first = group.items[0];
1537
+ const last = group.items[group.items.length - 1];
1536
1538
  if (is_delimiter(first) && is_delimiter(last)) {
1537
1539
  need_to_wrap = false;
1538
1540
  }
@@ -1559,9 +1561,11 @@ var TypstWriter = class {
1559
1561
  }
1560
1562
  }
1561
1563
  this.queue = this.queue.filter((token) => !token.eq(dummy_token));
1562
- this.queue.forEach((token) => {
1563
- this.writeBuffer(token);
1564
- });
1564
+ for (let i = 0; i < this.queue.length; i++) {
1565
+ let token = this.queue[i];
1566
+ let previous_token = i === 0 ? null : this.queue[i - 1];
1567
+ this.writeBuffer(previous_token, token);
1568
+ }
1565
1569
  this.queue = [];
1566
1570
  }
1567
1571
  finalize() {
@@ -2836,7 +2840,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2836
2840
  case "ordgroup":
2837
2841
  const node = abstractNode;
2838
2842
  return new TypstGroup(
2839
- node.args.map((n) => convert_tex_node_to_typst(n, options))
2843
+ node.items.map((n) => convert_tex_node_to_typst(n, options))
2840
2844
  );
2841
2845
  case "supsub": {
2842
2846
  const node2 = abstractNode;
@@ -2862,8 +2866,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2862
2866
  case "leftright": {
2863
2867
  const node2 = abstractNode;
2864
2868
  const { left, right } = node2;
2865
- const [_body] = node2.args;
2866
- const typ_body = convert_tex_node_to_typst(_body, options);
2869
+ const typ_body = convert_tex_node_to_typst(node2.body, options);
2867
2870
  if (options.optimize) {
2868
2871
  if (left !== null && right !== null) {
2869
2872
  const typ_left2 = tex_token_to_typst(left, options);
@@ -2900,8 +2903,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2900
2903
  }
2901
2904
  return new TypstLeftright(
2902
2905
  new TypstToken(1 /* SYMBOL */, "lr"),
2903
- [typ_body],
2904
- { left: typ_left, right: typ_right }
2906
+ { body: typ_body, left: typ_left, right: typ_right }
2905
2907
  );
2906
2908
  }
2907
2909
  case "funcCall": {
@@ -2944,6 +2946,14 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2944
2946
  }
2945
2947
  return new TypstFuncCall(new TypstToken(1 /* SYMBOL */, "op"), [new TypstToken(4 /* TEXT */, arg0.head.value).toNode()]);
2946
2948
  }
2949
+ if (node2.head.value === "\\textcolor") {
2950
+ const res = new TypstMarkupFunc(
2951
+ new TypstToken(1 /* SYMBOL */, `#text`),
2952
+ [convert_tex_node_to_typst(node2.args[1], options)]
2953
+ );
2954
+ res.setOptions({ fill: arg0 });
2955
+ return res;
2956
+ }
2947
2957
  if (node2.head.value === "\\substack") {
2948
2958
  return arg0;
2949
2959
  }
@@ -2973,38 +2983,40 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2973
2983
  }
2974
2984
  case "beginend": {
2975
2985
  const node2 = abstractNode;
2976
- const data = node2.matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
2986
+ const matrix = node2.matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
2977
2987
  if (node2.head.value.startsWith("align")) {
2978
- return new TypstAlign(data);
2988
+ return new TypstMatrixLike(null, matrix);
2979
2989
  }
2980
2990
  if (node2.head.value === "cases") {
2981
- return new TypstCases(data);
2991
+ return new TypstMatrixLike(TypstMatrixLike.CASES, matrix);
2982
2992
  }
2983
2993
  if (node2.head.value === "subarray") {
2984
- const align_node = node2.args[0];
2985
- switch (align_node.head.value) {
2986
- case "r":
2987
- data.forEach((row) => row[0].args.push(new TypstToken(7 /* CONTROL */, "&").toNode()));
2988
- break;
2989
- case "l":
2990
- data.forEach((row) => row[0].args.unshift(new TypstToken(7 /* CONTROL */, "&").toNode()));
2991
- break;
2992
- default:
2993
- break;
2994
+ if (node2.data) {
2995
+ const align_node = node2.data;
2996
+ switch (align_node.head.value) {
2997
+ case "r":
2998
+ matrix.forEach((row) => row[0].items.push(new TypstToken(7 /* CONTROL */, "&").toNode()));
2999
+ break;
3000
+ case "l":
3001
+ matrix.forEach((row) => row[0].items.unshift(new TypstToken(7 /* CONTROL */, "&").toNode()));
3002
+ break;
3003
+ default:
3004
+ break;
3005
+ }
2994
3006
  }
2995
- return new TypstAlign(data);
3007
+ return new TypstMatrixLike(null, matrix);
2996
3008
  }
2997
3009
  if (node2.head.value === "array") {
2998
3010
  const np = { "delim": TYPST_NONE };
2999
- assert(node2.args.length > 0 && node2.args[0].head.type === 3 /* LITERAL */);
3000
- const np_new = convert_tex_array_align_literal(node2.args[0].head.value);
3011
+ assert(node2.data !== null && node2.head.type === 3 /* LITERAL */);
3012
+ const np_new = convert_tex_array_align_literal(node2.data.head.value);
3001
3013
  Object.assign(np, np_new);
3002
- const res = new TypstMatrix(data);
3014
+ const res = new TypstMatrixLike(TypstMatrixLike.MAT, matrix);
3003
3015
  res.setOptions(np);
3004
3016
  return res;
3005
3017
  }
3006
3018
  if (node2.head.value.endsWith("matrix")) {
3007
- const res = new TypstMatrix(data);
3019
+ const res = new TypstMatrixLike(TypstMatrixLike.MAT, matrix);
3008
3020
  let delim;
3009
3021
  switch (node2.head.value) {
3010
3022
  case "matrix":
@@ -3123,32 +3135,30 @@ function convert_typst_node_to_tex(abstractNode) {
3123
3135
  }
3124
3136
  case "group": {
3125
3137
  const node = abstractNode;
3126
- const args = node.args.map(convert_typst_node_to_tex);
3138
+ const args = node.items.map(convert_typst_node_to_tex);
3127
3139
  const alignment_char = new TexToken(7 /* CONTROL */, "&").toNode();
3128
3140
  const newline_char = new TexToken(7 /* CONTROL */, "\\\\").toNode();
3129
3141
  if (array_includes(args, alignment_char)) {
3130
3142
  const rows = array_split(args, newline_char);
3131
- const data = [];
3143
+ const matrix = [];
3132
3144
  for (const row of rows) {
3133
3145
  const cells = array_split(row, alignment_char);
3134
- data.push(cells.map((cell) => new TexGroup(cell)));
3146
+ matrix.push(cells.map((cell) => new TexGroup(cell)));
3135
3147
  }
3136
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, "aligned"), [], data);
3148
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, "aligned"), matrix);
3137
3149
  }
3138
3150
  return new TexGroup(args);
3139
3151
  }
3140
3152
  case "leftright": {
3141
3153
  const node = abstractNode;
3142
- const args = node.args.map(convert_typst_node_to_tex);
3154
+ const body = convert_typst_node_to_tex(node.body);
3143
3155
  let left = node.left ? typst_token_to_tex(node.left) : new TexToken(1 /* ELEMENT */, ".");
3144
3156
  let right = node.right ? typst_token_to_tex(node.right) : new TexToken(1 /* ELEMENT */, ".");
3145
3157
  if (node.isOverHigh()) {
3146
3158
  left.value = "\\left" + left.value;
3147
3159
  right.value = "\\right" + right.value;
3148
3160
  }
3149
- args.unshift(left.toNode());
3150
- args.push(right.toNode());
3151
- return new TexGroup(args);
3161
+ return new TexGroup([left.toNode(), body, right.toNode()]);
3152
3162
  }
3153
3163
  case "funcCall": {
3154
3164
  const node = abstractNode;
@@ -3158,14 +3168,15 @@ function convert_typst_node_to_tex(abstractNode) {
3158
3168
  // `\left\| a + \frac{1}{3} \right\|` <- `norm(a + 1/3)`
3159
3169
  case "norm": {
3160
3170
  const arg0 = node.args[0];
3161
- const args = [convert_typst_node_to_tex(arg0)];
3171
+ const body = convert_typst_node_to_tex(arg0);
3162
3172
  if (node.isOverHigh()) {
3163
- return new TexLeftRight(args, {
3173
+ return new TexLeftRight({
3174
+ body,
3164
3175
  left: new TexToken(2 /* COMMAND */, "\\|"),
3165
3176
  right: new TexToken(2 /* COMMAND */, "\\|")
3166
3177
  });
3167
3178
  } else {
3168
- return new TexGroup(args);
3179
+ return body;
3169
3180
  }
3170
3181
  }
3171
3182
  // special hook for floor, ceil
@@ -3178,16 +3189,17 @@ function convert_typst_node_to_tex(abstractNode) {
3178
3189
  const left = "\\l" + node.head.value;
3179
3190
  const right = "\\r" + node.head.value;
3180
3191
  const arg0 = node.args[0];
3181
- const typ_arg0 = convert_typst_node_to_tex(arg0);
3192
+ const body = convert_typst_node_to_tex(arg0);
3182
3193
  const left_node = new TexToken(2 /* COMMAND */, left);
3183
3194
  const right_node = new TexToken(2 /* COMMAND */, right);
3184
3195
  if (node.isOverHigh()) {
3185
- return new TexLeftRight([typ_arg0], {
3196
+ return new TexLeftRight({
3197
+ body,
3186
3198
  left: left_node,
3187
3199
  right: right_node
3188
3200
  });
3189
3201
  } else {
3190
- return new TexGroup([left_node.toNode(), typ_arg0, right_node.toNode()]);
3202
+ return new TexGroup([left_node.toNode(), body, right_node.toNode()]);
3191
3203
  }
3192
3204
  }
3193
3205
  // special hook for root
@@ -3208,8 +3220,8 @@ function convert_typst_node_to_tex(abstractNode) {
3208
3220
  // special hook for vec
3209
3221
  // "vec(a, b, c)" -> "\begin{pmatrix}a\\ b\\ c\end{pmatrix}"
3210
3222
  case "vec": {
3211
- const tex_data = node.args.map(convert_typst_node_to_tex).map((n) => [n]);
3212
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), [], tex_data);
3223
+ const tex_matrix = node.args.map(convert_typst_node_to_tex).map((n) => [n]);
3224
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), tex_matrix);
3213
3225
  }
3214
3226
  // special hook for op
3215
3227
  case "op": {
@@ -3234,13 +3246,31 @@ function convert_typst_node_to_tex(abstractNode) {
3234
3246
  }
3235
3247
  }
3236
3248
  }
3249
+ case "markupFunc": {
3250
+ const node = abstractNode;
3251
+ switch (node.head.value) {
3252
+ case "#text": {
3253
+ if (node.options && node.options["fill"]) {
3254
+ const color = node.options["fill"];
3255
+ return new TexFuncCall(
3256
+ new TexToken(2 /* COMMAND */, "\\textcolor"),
3257
+ [convert_typst_node_to_tex(color), convert_typst_node_to_tex(node.fragments[0])]
3258
+ );
3259
+ }
3260
+ }
3261
+ case "#heading":
3262
+ default:
3263
+ throw new Error(`Unimplemented markup function: ${node.head.value}`);
3264
+ }
3265
+ }
3237
3266
  case "supsub": {
3238
3267
  const node = abstractNode;
3239
3268
  const { base, sup, sub } = node;
3240
3269
  const sup_tex = sup ? convert_typst_node_to_tex(sup) : null;
3241
3270
  const sub_tex = sub ? convert_typst_node_to_tex(sub) : null;
3242
3271
  if (base.head.eq(new TypstToken(1 /* SYMBOL */, "limits"))) {
3243
- const body_in_limits = convert_typst_node_to_tex(base.args[0]);
3272
+ const limits = base;
3273
+ const body_in_limits = convert_typst_node_to_tex(limits.args[0]);
3244
3274
  if (sup_tex !== null && sub_tex === null) {
3245
3275
  return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, body_in_limits]);
3246
3276
  } else if (sup_tex === null && sub_tex !== null) {
@@ -3258,50 +3288,51 @@ function convert_typst_node_to_tex(abstractNode) {
3258
3288
  });
3259
3289
  return res;
3260
3290
  }
3261
- case "matrix": {
3291
+ case "matrixLike": {
3262
3292
  const node = abstractNode;
3263
- const tex_data = node.matrix.map((row) => row.map(convert_typst_node_to_tex));
3264
- let env_type = "pmatrix";
3265
- if (node.options) {
3266
- if ("delim" in node.options) {
3267
- const delim = node.options.delim;
3268
- switch (delim.head.value) {
3269
- case "#none":
3270
- env_type = "matrix";
3271
- break;
3272
- case "[":
3273
- case "]":
3274
- env_type = "bmatrix";
3275
- break;
3276
- case "(":
3277
- case ")":
3278
- env_type = "pmatrix";
3279
- break;
3280
- case "{":
3281
- case "}":
3282
- env_type = "Bmatrix";
3283
- break;
3284
- case "|":
3285
- env_type = "vmatrix";
3286
- break;
3287
- case "bar":
3288
- case "bar.v":
3289
- env_type = "vmatrix";
3290
- break;
3291
- case "bar.v.double":
3292
- env_type = "Vmatrix";
3293
- break;
3294
- default:
3295
- throw new Error(`Unexpected delimiter ${delim.head}`);
3293
+ const tex_matrix = node.matrix.map((row) => row.map(convert_typst_node_to_tex));
3294
+ if (node.head.eq(TypstMatrixLike.MAT)) {
3295
+ let env_type = "pmatrix";
3296
+ if (node.options) {
3297
+ if ("delim" in node.options) {
3298
+ const delim = node.options.delim;
3299
+ switch (delim.head.value) {
3300
+ case "#none":
3301
+ env_type = "matrix";
3302
+ break;
3303
+ case "[":
3304
+ case "]":
3305
+ env_type = "bmatrix";
3306
+ break;
3307
+ case "(":
3308
+ case ")":
3309
+ env_type = "pmatrix";
3310
+ break;
3311
+ case "{":
3312
+ case "}":
3313
+ env_type = "Bmatrix";
3314
+ break;
3315
+ case "|":
3316
+ env_type = "vmatrix";
3317
+ break;
3318
+ case "bar":
3319
+ case "bar.v":
3320
+ env_type = "vmatrix";
3321
+ break;
3322
+ case "bar.v.double":
3323
+ env_type = "Vmatrix";
3324
+ break;
3325
+ default:
3326
+ throw new Error(`Unexpected delimiter ${delim.head}`);
3327
+ }
3296
3328
  }
3297
3329
  }
3330
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, env_type), tex_matrix);
3331
+ } else if (node.head.eq(TypstMatrixLike.CASES)) {
3332
+ return new TexBeginEnd(new TexToken(3 /* LITERAL */, "cases"), tex_matrix);
3333
+ } else {
3334
+ throw new Error(`Unexpected matrix type ${node.head}`);
3298
3335
  }
3299
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, env_type), [], tex_data);
3300
- }
3301
- case "cases": {
3302
- const node = abstractNode;
3303
- const tex_data = node.matrix.map((row) => row.map(convert_typst_node_to_tex));
3304
- return new TexBeginEnd(new TexToken(3 /* LITERAL */, "cases"), [], tex_data);
3305
3336
  }
3306
3337
  case "fraction": {
3307
3338
  const node = abstractNode;
@@ -3376,10 +3407,10 @@ var rules_map2 = /* @__PURE__ */ new Map([
3376
3407
  new TypstToken(2 /* ELEMENT */, ")")
3377
3408
  ];
3378
3409
  }],
3379
- [String.raw`[a-zA-Z\.]+`, (s) => {
3410
+ [String.raw`#none`, (s) => new TypstToken(0 /* NONE */, s.text())],
3411
+ [String.raw`#?[a-zA-Z\.]+`, (s) => {
3380
3412
  return new TypstToken(s.text().length === 1 ? 2 /* ELEMENT */ : 1 /* SYMBOL */, s.text());
3381
3413
  }],
3382
- [String.raw`#none`, (s) => new TypstToken(0 /* NONE */, s.text())],
3383
3414
  [String.raw`.`, (s) => new TypstToken(2 /* ELEMENT */, s.text())]
3384
3415
  ]);
3385
3416
  var spec2 = {
@@ -3518,10 +3549,10 @@ function process_operators(nodes, parenthesis = false) {
3518
3549
  }
3519
3550
  let numerator = args.pop();
3520
3551
  if (denominator.type === "leftright") {
3521
- denominator = new TypstGroup(denominator.args);
3552
+ denominator = denominator.body;
3522
3553
  }
3523
3554
  if (numerator.type === "leftright") {
3524
- numerator = new TypstGroup(numerator.args);
3555
+ numerator = numerator.body;
3525
3556
  }
3526
3557
  args.push(new TypstFraction([numerator, denominator]));
3527
3558
  stack.pop();
@@ -3530,15 +3561,22 @@ function process_operators(nodes, parenthesis = false) {
3530
3561
  }
3531
3562
  }
3532
3563
  }
3564
+ const body = args.length === 1 ? args[0] : new TypstGroup(args);
3533
3565
  if (parenthesis) {
3534
- return new TypstLeftright(null, args, { left: LEFT_PARENTHESES, right: RIGHT_PARENTHESES });
3566
+ return new TypstLeftright(null, { body, left: LEFT_PARENTHESES, right: RIGHT_PARENTHESES });
3535
3567
  } else {
3536
- if (args.length === 1) {
3537
- return args[0];
3538
- } else {
3539
- return new TypstGroup(args);
3540
- }
3568
+ return body;
3569
+ }
3570
+ }
3571
+ function parse_named_params(groups) {
3572
+ const COLON = new TypstToken(2 /* ELEMENT */, ":").toNode();
3573
+ const np = {};
3574
+ for (const group of groups) {
3575
+ assert(group.items.length == 3);
3576
+ assert(group.items[1].eq(COLON));
3577
+ np[group.items[0].toString()] = new TypstTerminal(new TypstToken(3 /* LITERAL */, group.items[2].toString()));
3541
3578
  }
3579
+ return np;
3542
3580
  }
3543
3581
  var TypstParserError = class extends Error {
3544
3582
  constructor(message) {
@@ -3653,19 +3691,31 @@ var TypstParser = class {
3653
3691
  if (start + 1 < tokens.length && tokens[start + 1].eq(LEFT_PARENTHESES)) {
3654
3692
  if (firstToken.value === "mat") {
3655
3693
  const [matrix, named_params, newPos2] = this.parseMatrix(tokens, start + 1, SEMICOLON, COMMA);
3656
- const mat = new TypstMatrix(matrix);
3694
+ const mat = new TypstMatrixLike(firstToken, matrix);
3657
3695
  mat.setOptions(named_params);
3658
3696
  return [mat, newPos2];
3659
3697
  }
3660
3698
  if (firstToken.value === "cases") {
3661
3699
  const [cases, named_params, newPos2] = this.parseMatrix(tokens, start + 1, COMMA, CONTROL_AND);
3662
- const casesNode = new TypstCases(cases);
3700
+ const casesNode = new TypstMatrixLike(firstToken, cases);
3663
3701
  casesNode.setOptions(named_params);
3664
3702
  return [casesNode, newPos2];
3665
3703
  }
3666
3704
  if (firstToken.value === "lr") {
3667
3705
  return this.parseLrArguments(tokens, start + 1);
3668
3706
  }
3707
+ if (["#heading", "#text"].includes(firstToken.value)) {
3708
+ const [args2, newPos2] = this.parseArguments(tokens, start + 1);
3709
+ const named_params = parse_named_params(args2);
3710
+ assert(tokens[newPos2].eq(LEFT_BRACKET));
3711
+ const DOLLAR = new TypstToken(2 /* ELEMENT */, "$");
3712
+ const end = _find_closing_match(tokens, newPos2 + 1, [DOLLAR], [DOLLAR]);
3713
+ const [group, _] = this.parseGroup(tokens, newPos2 + 2, end);
3714
+ assert(tokens[end + 1].eq(RIGHT_BRACKET));
3715
+ const markup_func = new TypstMarkupFunc(firstToken, [group]);
3716
+ markup_func.setOptions(named_params);
3717
+ return [markup_func, end + 2];
3718
+ }
3669
3719
  const [args, newPos] = this.parseArguments(tokens, start + 1);
3670
3720
  const func_call = new TypstFuncCall(firstToken, args);
3671
3721
  return [func_call, newPos];
@@ -3687,13 +3737,13 @@ var TypstParser = class {
3687
3737
  const inner_end = find_closing_delim(tokens, inner_start);
3688
3738
  const inner_args = this.parseArgumentsWithSeparator(tokens, inner_start + 1, inner_end, COMMA);
3689
3739
  return [
3690
- new TypstLeftright(lr_token, inner_args, { left: tokens[inner_start], right: tokens[inner_end] }),
3740
+ new TypstLeftright(lr_token, { body: new TypstGroup(inner_args), left: tokens[inner_start], right: tokens[inner_end] }),
3691
3741
  end + 1
3692
3742
  ];
3693
3743
  } else {
3694
3744
  const [args, end] = this.parseArguments(tokens, start);
3695
3745
  return [
3696
- new TypstLeftright(lr_token, args, { left: null, right: null }),
3746
+ new TypstLeftright(lr_token, { body: new TypstGroup(args), left: null, right: null }),
3697
3747
  end
3698
3748
  ];
3699
3749
  }
@@ -3716,17 +3766,17 @@ var TypstParser = class {
3716
3766
  continue;
3717
3767
  }
3718
3768
  const g = arr[i];
3719
- const pos_colon = array_find(g.args, COLON);
3769
+ const pos_colon = array_find(g.items, COLON);
3720
3770
  if (pos_colon === -1 || pos_colon === 0) {
3721
3771
  continue;
3722
3772
  }
3723
3773
  to_delete.push(i);
3724
- const param_name = g.args[pos_colon - 1];
3774
+ const param_name = g.items[pos_colon - 1];
3725
3775
  if (param_name.eq(new TypstToken(1 /* SYMBOL */, "delim").toNode())) {
3726
- if (g.args.length !== 3) {
3776
+ if (g.items.length !== 3) {
3727
3777
  throw new TypstParserError("Invalid number of arguments for delim");
3728
3778
  }
3729
- np2["delim"] = g.args[pos_colon + 1];
3779
+ np2["delim"] = g.items[pos_colon + 1];
3730
3780
  } else {
3731
3781
  throw new TypstParserError("Not implemented for other named parameters");
3732
3782
  }