tex2typst 0.3.29 → 0.4.0
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.d.ts +13 -9
- package/dist/index.js +80 -33
- package/dist/tex2typst.min.js +13 -13
- package/package.json +1 -1
- package/src/convert.ts +51 -23
- package/src/exposed-types.ts +13 -9
- package/src/index.ts +23 -12
- package/src/map.ts +1 -0
- package/src/tex-types.ts +2 -0
- package/src/tex-writer.ts +10 -1
- package/tests/struct-bidirection.yaml +10 -1
- package/tests/struct-tex2typst.yaml +0 -6
- package/tests/tex-to-typst.test.ts +1 -1
- package/tests/typst-to-tex.test.ts +36 -12
package/src/convert.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TexNode, TexToken, TexTokenType, TexFuncCall, TexGroup, TexSupSub,
|
|
2
2
|
TexText, TexBeginEnd, TexLeftRight, TexTerminal} from "./tex-types";
|
|
3
|
-
import type { Tex2TypstOptions } from "./exposed-types";
|
|
3
|
+
import type { Tex2TypstOptions, Typst2TexOptions } from "./exposed-types";
|
|
4
4
|
import { TypstFraction, TypstFuncCall, TypstGroup, TypstLeftright, TypstMarkupFunc, TypstMatrixLike, TypstNode, TypstSupsub, TypstTerminal } from "./typst-types";
|
|
5
5
|
import { TypstNamedParams } from "./typst-types";
|
|
6
6
|
import { TypstSupsubData } from "./typst-types";
|
|
@@ -224,7 +224,7 @@ function appendWithBracketsIfNeeded(node: TypstNode): TypstNode {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2TypstOptions
|
|
227
|
+
export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2TypstOptions): TypstNode {
|
|
228
228
|
switch (abstractNode.type) {
|
|
229
229
|
case 'terminal': {
|
|
230
230
|
const node = abstractNode as TexTerminal;
|
|
@@ -637,7 +637,9 @@ function typst_token_to_tex(token: TypstToken): TexToken {
|
|
|
637
637
|
|
|
638
638
|
const TEX_NODE_COMMA = new TexToken(TexTokenType.ELEMENT, ',').toNode();
|
|
639
639
|
|
|
640
|
-
export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
640
|
+
export function convert_typst_node_to_tex(abstractNode: TypstNode, options: Typst2TexOptions): TexNode {
|
|
641
|
+
const convert_node = (node: TypstNode) => convert_typst_node_to_tex(node, options);
|
|
642
|
+
|
|
641
643
|
switch (abstractNode.type) {
|
|
642
644
|
case 'terminal': {
|
|
643
645
|
const node = abstractNode as TypstTerminal;
|
|
@@ -676,7 +678,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
676
678
|
|
|
677
679
|
case 'group': {
|
|
678
680
|
const node = abstractNode as TypstGroup;
|
|
679
|
-
const args = node.items.map(
|
|
681
|
+
const args = node.items.map(convert_node);
|
|
680
682
|
const alignment_char = new TexToken(TexTokenType.CONTROL, '&').toNode();
|
|
681
683
|
const newline_char = new TexToken(TexTokenType.CONTROL, '\\\\').toNode();
|
|
682
684
|
if (array_includes(args, alignment_char)) {
|
|
@@ -693,7 +695,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
693
695
|
}
|
|
694
696
|
case 'leftright': {
|
|
695
697
|
const node = abstractNode as TypstLeftright;
|
|
696
|
-
const body =
|
|
698
|
+
const body = convert_node(node.body);
|
|
697
699
|
let left = node.left? typst_token_to_tex(node.left) : new TexToken(TexTokenType.ELEMENT, '.');
|
|
698
700
|
let right = node.right? typst_token_to_tex(node.right) : new TexToken(TexTokenType.ELEMENT, '.');
|
|
699
701
|
// const is_over_high = node.isOverHigh();
|
|
@@ -715,7 +717,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
715
717
|
// `\left\| a + \frac{1}{3} \right\|` <- `norm(a + 1/3)`
|
|
716
718
|
case 'norm': {
|
|
717
719
|
const arg0 = node.args[0];
|
|
718
|
-
const body =
|
|
720
|
+
const body = convert_node(arg0);
|
|
719
721
|
if (node.isOverHigh()) {
|
|
720
722
|
return new TexLeftRight({
|
|
721
723
|
body: body,
|
|
@@ -736,7 +738,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
736
738
|
const left = "\\l" + node.head.value;
|
|
737
739
|
const right = "\\r" + node.head.value;
|
|
738
740
|
const arg0 = node.args[0];
|
|
739
|
-
const body =
|
|
741
|
+
const body = convert_node(arg0);
|
|
740
742
|
const left_node = new TexToken(TexTokenType.COMMAND, left);
|
|
741
743
|
const right_node = new TexToken(TexTokenType.COMMAND, right);
|
|
742
744
|
if (node.isOverHigh()) {
|
|
@@ -752,22 +754,22 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
752
754
|
// special hook for root
|
|
753
755
|
case 'root': {
|
|
754
756
|
const [degree, radicand] = node.args;
|
|
755
|
-
const data =
|
|
756
|
-
return new TexFuncCall(new TexToken(TexTokenType.COMMAND, '\\sqrt'), [
|
|
757
|
+
const data = convert_node(degree);
|
|
758
|
+
return new TexFuncCall(new TexToken(TexTokenType.COMMAND, '\\sqrt'), [convert_node(radicand)], data);
|
|
757
759
|
}
|
|
758
760
|
// special hook for overbrace and underbrace
|
|
759
761
|
case 'overbrace':
|
|
760
762
|
case 'underbrace': {
|
|
761
763
|
const [body, label] = node.args;
|
|
762
|
-
const base = new TexFuncCall(typst_token_to_tex(node.head), [
|
|
763
|
-
const script =
|
|
764
|
+
const base = new TexFuncCall(typst_token_to_tex(node.head), [convert_node(body)]);
|
|
765
|
+
const script = convert_node(label);
|
|
764
766
|
const data = node.head.value === 'overbrace' ? { base, sup: script, sub: null } : { base, sub: script, sup: null };
|
|
765
767
|
return new TexSupSub(data);
|
|
766
768
|
}
|
|
767
769
|
// special hook for vec
|
|
768
770
|
// "vec(a, b, c)" -> "\begin{pmatrix}a\\ b\\ c\end{pmatrix}"
|
|
769
771
|
case 'vec': {
|
|
770
|
-
const tex_matrix = node.args.map(
|
|
772
|
+
const tex_matrix = node.args.map(arg => [convert_node(arg)]);
|
|
771
773
|
return new TexBeginEnd(new TexToken(TexTokenType.LITERAL, 'pmatrix'), tex_matrix);
|
|
772
774
|
}
|
|
773
775
|
// special hook for op
|
|
@@ -798,21 +800,47 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
798
800
|
}
|
|
799
801
|
return new TexFuncCall(
|
|
800
802
|
new TexToken(TexTokenType.COMMAND, command),
|
|
801
|
-
[
|
|
803
|
+
[convert_node(node.args[1])]
|
|
802
804
|
);
|
|
803
805
|
}
|
|
806
|
+
// display(...) -> \displaystyle ... \textstyle
|
|
807
|
+
// The postprocessor will remove \textstyle if it is the end of the math code
|
|
808
|
+
case 'display': {
|
|
809
|
+
const arg0 = node.args[0];
|
|
810
|
+
const group = new TexGroup([
|
|
811
|
+
TexToken.COMMAND_DISPLAYSTYLE.toNode(),
|
|
812
|
+
convert_node(arg0),
|
|
813
|
+
]);
|
|
814
|
+
if (!options.blockMathMode) {
|
|
815
|
+
group.items.push(TexToken.COMMAND_TEXTSTYLE.toNode());
|
|
816
|
+
}
|
|
817
|
+
return group;
|
|
818
|
+
}
|
|
819
|
+
// inline(...) -> \textstyle ... \displaystyle
|
|
820
|
+
// The postprocessor will remove \displaystyle if it is the end of the math code
|
|
821
|
+
case 'inline': {
|
|
822
|
+
const arg0 = node.args[0];
|
|
823
|
+
const group = new TexGroup([
|
|
824
|
+
TexToken.COMMAND_TEXTSTYLE.toNode(),
|
|
825
|
+
convert_node(arg0),
|
|
826
|
+
]);
|
|
827
|
+
if (options.blockMathMode) {
|
|
828
|
+
group.items.push(TexToken.COMMAND_DISPLAYSTYLE.toNode());
|
|
829
|
+
}
|
|
830
|
+
return group;
|
|
831
|
+
}
|
|
804
832
|
// general case
|
|
805
833
|
default: {
|
|
806
834
|
const func_name_tex = typst_token_to_tex(node.head);
|
|
807
835
|
const is_known_func = TEX_UNARY_COMMANDS.includes(func_name_tex.value.substring(1))
|
|
808
836
|
|| TEX_BINARY_COMMANDS.includes(func_name_tex.value.substring(1));
|
|
809
837
|
if (func_name_tex.value.length > 0 && is_known_func) {
|
|
810
|
-
return new TexFuncCall(func_name_tex, node.args.map(
|
|
838
|
+
return new TexFuncCall(func_name_tex, node.args.map(convert_node));
|
|
811
839
|
} else {
|
|
812
840
|
return new TexGroup([
|
|
813
841
|
typst_token_to_tex(node.head).toNode(),
|
|
814
842
|
new TexToken(TexTokenType.ELEMENT, '(').toNode(),
|
|
815
|
-
...array_intersperse(node.args.map(
|
|
843
|
+
...array_intersperse(node.args.map(convert_node), TEX_NODE_COMMA),
|
|
816
844
|
new TexToken(TexTokenType.ELEMENT, ')').toNode()
|
|
817
845
|
]);
|
|
818
846
|
}
|
|
@@ -828,7 +856,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
828
856
|
const color = node.options['fill'];
|
|
829
857
|
return new TexFuncCall(
|
|
830
858
|
new TexToken(TexTokenType.COMMAND, '\\textcolor'),
|
|
831
|
-
[
|
|
859
|
+
[convert_node(color), convert_node(node.fragments[0])]
|
|
832
860
|
)
|
|
833
861
|
}
|
|
834
862
|
}
|
|
@@ -840,8 +868,8 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
840
868
|
case 'supsub': {
|
|
841
869
|
const node = abstractNode as TypstSupsub;
|
|
842
870
|
const { base, sup, sub } = node;
|
|
843
|
-
const sup_tex = sup?
|
|
844
|
-
const sub_tex = sub?
|
|
871
|
+
const sup_tex = sup? convert_node(sup) : null;
|
|
872
|
+
const sub_tex = sub? convert_node(sub) : null;
|
|
845
873
|
|
|
846
874
|
// special hook for limits
|
|
847
875
|
// `limits(+)^a` -> `\overset{a}{+}`
|
|
@@ -849,7 +877,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
849
877
|
// `limits(+)_a^b` -> `\overset{b}{\underset{a}{+}}`
|
|
850
878
|
if (base.head.eq(new TypstToken(TypstTokenType.SYMBOL, 'limits'))) {
|
|
851
879
|
const limits = base as TypstFuncCall;
|
|
852
|
-
const body_in_limits =
|
|
880
|
+
const body_in_limits = convert_node(limits.args[0]);
|
|
853
881
|
if (sup_tex !== null && sub_tex === null) {
|
|
854
882
|
return new TexFuncCall(new TexToken(TexTokenType.COMMAND, '\\overset'), [sup_tex, body_in_limits]);
|
|
855
883
|
} else if (sup_tex === null && sub_tex !== null) {
|
|
@@ -860,7 +888,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
860
888
|
}
|
|
861
889
|
}
|
|
862
890
|
|
|
863
|
-
const base_tex =
|
|
891
|
+
const base_tex = convert_node(base);
|
|
864
892
|
|
|
865
893
|
const res = new TexSupSub({
|
|
866
894
|
base: base_tex,
|
|
@@ -871,7 +899,7 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
871
899
|
}
|
|
872
900
|
case 'matrixLike': {
|
|
873
901
|
const node = abstractNode as TypstMatrixLike;
|
|
874
|
-
const tex_matrix = node.matrix.map(row => row.map(
|
|
902
|
+
const tex_matrix = node.matrix.map(row => row.map(convert_node));
|
|
875
903
|
if (node.head.eq(TypstMatrixLike.MAT)) {
|
|
876
904
|
let env_type = 'pmatrix'; // typst mat use delim:"(" by default
|
|
877
905
|
if (node.options) {
|
|
@@ -918,8 +946,8 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
|
|
|
918
946
|
case 'fraction': {
|
|
919
947
|
const node = abstractNode as TypstFraction;
|
|
920
948
|
const [numerator, denominator] = node.args;
|
|
921
|
-
const num_tex =
|
|
922
|
-
const den_tex =
|
|
949
|
+
const num_tex = convert_node(numerator);
|
|
950
|
+
const den_tex = convert_node(denominator);
|
|
923
951
|
return new TexFuncCall(new TexToken(TexTokenType.COMMAND, '\\frac'), [num_tex, den_tex]);
|
|
924
952
|
}
|
|
925
953
|
default:
|
package/src/exposed-types.ts
CHANGED
|
@@ -6,17 +6,21 @@
|
|
|
6
6
|
* Any undocumented options may be not working at present or break in the future!
|
|
7
7
|
*/
|
|
8
8
|
export interface Tex2TypstOptions {
|
|
9
|
-
nonStrict
|
|
10
|
-
preferShorthands
|
|
11
|
-
keepSpaces
|
|
12
|
-
fracToSlash
|
|
13
|
-
inftyToOo
|
|
14
|
-
optimize
|
|
15
|
-
customTexMacros
|
|
9
|
+
nonStrict: boolean; /** default is true */
|
|
10
|
+
preferShorthands: boolean; /** default is true */
|
|
11
|
+
keepSpaces: boolean; /** default is false */
|
|
12
|
+
fracToSlash: boolean; /** default is true */
|
|
13
|
+
inftyToOo: boolean; /** default is false */
|
|
14
|
+
optimize: boolean; /** default is true */
|
|
15
|
+
customTexMacros: { [key: string]: string; };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export
|
|
19
|
-
|
|
18
|
+
export interface Typst2TexOptions {
|
|
19
|
+
blockMathMode: boolean; /** default is true */
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare function tex2typst(tex: string, options?: Partial<Tex2TypstOptions>): string;
|
|
23
|
+
export declare function typst2tex(typst: string, options?: Partial<Typst2TexOptions>): string;
|
|
20
24
|
|
|
21
25
|
export declare const symbolMap: Map<string, string>;
|
|
22
26
|
export declare const shorthandMap: Map<string, string>;
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseTex } from "./tex-parser";
|
|
2
|
-
import type { Tex2TypstOptions } from "./exposed-types";
|
|
2
|
+
import type { Tex2TypstOptions, Typst2TexOptions } from "./exposed-types";
|
|
3
3
|
import { TypstWriter } from "./typst-writer";
|
|
4
4
|
import { type TypstWriterOptions } from "./typst-types";
|
|
5
5
|
import { convert_tex_node_to_typst, convert_typst_node_to_tex } from "./convert";
|
|
@@ -9,7 +9,7 @@ import { TexWriter } from "./tex-writer";
|
|
|
9
9
|
import { shorthandMap } from "./typst-shorthands";
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
export function tex2typst(tex: string, options
|
|
12
|
+
export function tex2typst(tex: string, options: Partial<Tex2TypstOptions> = {}): string {
|
|
13
13
|
const opt: Tex2TypstOptions = {
|
|
14
14
|
nonStrict: true,
|
|
15
15
|
preferShorthands: true,
|
|
@@ -20,14 +20,12 @@ export function tex2typst(tex: string, options?: Tex2TypstOptions): string {
|
|
|
20
20
|
customTexMacros: {}
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
if(options !==
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
opt[key as keyof Tex2TypstOptions] = options[key as keyof Tex2TypstOptions] as any;
|
|
30
|
-
}
|
|
23
|
+
if (typeof options !== 'object') {
|
|
24
|
+
throw new Error("options must be an object");
|
|
25
|
+
}
|
|
26
|
+
for (const key in opt) {
|
|
27
|
+
if (key in options) {
|
|
28
|
+
opt[key as keyof Tex2TypstOptions] = options[key as keyof Tex2TypstOptions] as any;
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
|
|
@@ -38,9 +36,22 @@ export function tex2typst(tex: string, options?: Tex2TypstOptions): string {
|
|
|
38
36
|
return writer.finalize();
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
export function typst2tex(typst: string): string {
|
|
39
|
+
export function typst2tex(typst: string, options: Partial<Typst2TexOptions> = {}): string {
|
|
40
|
+
const opt: Typst2TexOptions = {
|
|
41
|
+
blockMathMode: true,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (typeof options !== 'object') {
|
|
45
|
+
throw new Error("options must be an object");
|
|
46
|
+
}
|
|
47
|
+
for (const key in opt) {
|
|
48
|
+
if (key in options) {
|
|
49
|
+
opt[key as keyof Typst2TexOptions] = options[key as keyof Typst2TexOptions] as any;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
42
53
|
const typstTree = parseTypst(typst);
|
|
43
|
-
const texTree = convert_typst_node_to_tex(typstTree);
|
|
54
|
+
const texTree = convert_typst_node_to_tex(typstTree, opt);
|
|
44
55
|
const writer = new TexWriter();
|
|
45
56
|
writer.append(texTree);
|
|
46
57
|
return writer.finalize();
|
package/src/map.ts
CHANGED
package/src/tex-types.ts
CHANGED
|
@@ -44,6 +44,8 @@ export class TexToken {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
public static readonly EMPTY = new TexToken(TexTokenType.EMPTY, '');
|
|
47
|
+
public static readonly COMMAND_DISPLAYSTYLE = new TexToken(TexTokenType.COMMAND, '\\displaystyle');
|
|
48
|
+
public static readonly COMMAND_TEXTSTYLE = new TexToken(TexTokenType.COMMAND, '\\textstyle');
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
|
package/src/tex-writer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TexNode, TexToken, writeTexTokenBuffer } from "./tex-types";
|
|
1
|
+
import { TexNode, TexToken, TexTokenType, writeTexTokenBuffer } from "./tex-types";
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
@@ -12,6 +12,15 @@ export class TexWriter {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
protected flushQueue() {
|
|
15
|
+
// remove \textstyle or \displaystyle if it is the end of the math code
|
|
16
|
+
while (this.queue.length > 0) {
|
|
17
|
+
const last_token = this.queue[this.queue.length - 1];
|
|
18
|
+
if (last_token.eq(TexToken.COMMAND_DISPLAYSTYLE) || last_token.eq(TexToken.COMMAND_TEXTSTYLE)) {
|
|
19
|
+
this.queue.pop();
|
|
20
|
+
} else {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
15
24
|
for (let i = 0; i < this.queue.length; i++) {
|
|
16
25
|
this.buffer = writeTexTokenBuffer(this.buffer, this.queue[i]);
|
|
17
26
|
}
|
|
@@ -191,4 +191,13 @@ cases:
|
|
|
191
191
|
typst: a class("binary", X) b
|
|
192
192
|
- title: mathop
|
|
193
193
|
tex: a \mathop{X} b
|
|
194
|
-
typst: a class("large", X) b
|
|
194
|
+
typst: a class("large", X) b
|
|
195
|
+
- title: display style
|
|
196
|
+
tex: a = \displaystyle \sum_i x_i
|
|
197
|
+
typst: a = display(sum_i x_i)
|
|
198
|
+
- title: display style, case 3
|
|
199
|
+
tex: \displaystyle \lim_x F(x) = 0, \lim_x F(x) = 1
|
|
200
|
+
typst: display(lim_x F(x) = 0 comma lim_x F(x) = 1)
|
|
201
|
+
- title: display style, case 4
|
|
202
|
+
tex: \lim_x F(x) = 0, \displaystyle \lim_x F(x) = 1
|
|
203
|
+
typst: lim_x F(x) = 0, display(lim_x F(x) = 1)
|
|
@@ -406,12 +406,6 @@ cases:
|
|
|
406
406
|
- title: Backslash space
|
|
407
407
|
tex: a \ b
|
|
408
408
|
typst: a med b
|
|
409
|
-
- title: display style
|
|
410
|
-
tex: \displaystyle \lim_{x \to -\infty} F(x) = 0, \lim_{x \to +\infty} F(x) = 1
|
|
411
|
-
typst: display(lim_(x -> - infinity) F(x) = 0 comma lim_(x -> + infinity) F(x) = 1)
|
|
412
|
-
- title: display style 2
|
|
413
|
-
tex: \lim_{x \to -\infty} F(x) = 0, \displaystyle \lim_{x \to +\infty} F(x) = 1
|
|
414
|
-
typst: lim_(x -> - infinity) F(x) = 0, display(lim_(x -> + infinity) F(x) = 1)
|
|
415
409
|
# this test the typst formatter. Old bad implementation translated it to "C(x y)/z"
|
|
416
410
|
- title: parenthesis in fraction
|
|
417
411
|
tex: C \frac{xy}{z}
|
|
@@ -103,7 +103,7 @@ caseFiles.forEach((ymlFilename) => {
|
|
|
103
103
|
let tex_node: null | TexNode = null;
|
|
104
104
|
let result: null | string = null;
|
|
105
105
|
try {
|
|
106
|
-
const settings: Tex2TypstOptions = {
|
|
106
|
+
const settings: Partial<Tex2TypstOptions> = {
|
|
107
107
|
nonStrict: c.nonStrict? c.nonStrict: false,
|
|
108
108
|
preferShorthands: c.preferShorthands !== undefined? c.preferShorthands: true,
|
|
109
109
|
inftyToOo: c.inftyToOo !== undefined? c.inftyToOo: false,
|
|
@@ -1,48 +1,72 @@
|
|
|
1
1
|
|
|
2
|
-
import { describe,
|
|
2
|
+
import { describe, test, expect } from 'vitest';
|
|
3
3
|
import { parseTypst } from '../src/typst-parser';
|
|
4
4
|
import { TexWriter } from '../src/tex-writer';
|
|
5
5
|
import { convert_typst_node_to_tex } from '../src/convert';
|
|
6
6
|
import { loadTestCases, TestCase } from './test-common';
|
|
7
|
+
import { Typst2TexOptions } from '../src/exposed-types';
|
|
8
|
+
import { typst2tex } from '../src';
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
const options: Typst2TexOptions = {
|
|
11
|
+
blockMathMode: true,
|
|
12
|
+
};
|
|
9
13
|
|
|
10
14
|
describe('examples', () => {
|
|
11
|
-
|
|
15
|
+
test('a + b', function () {
|
|
12
16
|
const typst_node = parseTypst('a + b');
|
|
13
|
-
const tex_node = convert_typst_node_to_tex(typst_node);
|
|
17
|
+
const tex_node = convert_typst_node_to_tex(typst_node, options);
|
|
14
18
|
const writer = new TexWriter();
|
|
15
19
|
writer.append(tex_node);
|
|
16
20
|
const res = writer.finalize();
|
|
17
21
|
expect(res).toEqual('a + b');
|
|
18
22
|
});
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
test('sqrt(x)', function () {
|
|
21
25
|
const typst_node = parseTypst('sqrt(x)');
|
|
22
|
-
const tex_node = convert_typst_node_to_tex(typst_node);
|
|
26
|
+
const tex_node = convert_typst_node_to_tex(typst_node, options);
|
|
23
27
|
const writer = new TexWriter();
|
|
24
28
|
writer.append(tex_node);
|
|
25
29
|
const res = writer.finalize();
|
|
26
30
|
expect(res).toEqual('\\sqrt{x}');
|
|
27
31
|
});
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
test('integral_a^b f(x) dif x', function () {
|
|
30
34
|
const typst_node = parseTypst('integral_a^b f(x) dif x');
|
|
31
|
-
const tex_node = convert_typst_node_to_tex(typst_node);
|
|
35
|
+
const tex_node = convert_typst_node_to_tex(typst_node, options);
|
|
32
36
|
const writer = new TexWriter();
|
|
33
37
|
writer.append(tex_node);
|
|
34
38
|
const res = writer.finalize();
|
|
35
39
|
expect(res).toEqual('\\int_a^b f(x) \\mathrm{d} x');
|
|
36
40
|
});
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
test('lr({a + 1/3))', function () {
|
|
39
43
|
const typst_node = parseTypst('lr({a + 1/3))');
|
|
40
|
-
const tex_node = convert_typst_node_to_tex(typst_node);
|
|
44
|
+
const tex_node = convert_typst_node_to_tex(typst_node, options);
|
|
41
45
|
const writer = new TexWriter();
|
|
42
46
|
writer.append(tex_node);
|
|
43
47
|
const res = writer.finalize();
|
|
44
48
|
expect(res).toEqual('\\left\\{a + \\frac{1}{3} \\right)');
|
|
45
49
|
});
|
|
50
|
+
|
|
51
|
+
test('blockMathMode = false', function () {
|
|
52
|
+
const typst_code_1 = "a = display(sum_i x_i) b";
|
|
53
|
+
const res1 = typst2tex(typst_code_1, { blockMathMode: false });
|
|
54
|
+
expect(res1).toEqual(String.raw`a = \displaystyle \sum_i x_i \textstyle b`);
|
|
55
|
+
|
|
56
|
+
const typst_code_2 = "a = inline(sum_i x_i) b";
|
|
57
|
+
const res2 = typst2tex(typst_code_2, { blockMathMode: false });
|
|
58
|
+
expect(res2).toEqual(String.raw`a = \textstyle \sum_i x_i b`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('blockMathMode = true', function () {
|
|
62
|
+
const typst_code_1 = "a = inline(sum_i x_i) b";
|
|
63
|
+
const res1 = typst2tex(typst_code_1, { blockMathMode: true });
|
|
64
|
+
expect(res1).toEqual(String.raw`a = \textstyle \sum_i x_i \displaystyle b`);
|
|
65
|
+
|
|
66
|
+
const typst_code_2 = "a = display(sum_i x_i) b";
|
|
67
|
+
const res2 = typst2tex(typst_code_2, { blockMathMode: true });
|
|
68
|
+
expect(res2).toEqual(String.raw`a = \displaystyle \sum_i x_i b`);
|
|
69
|
+
});
|
|
46
70
|
});
|
|
47
71
|
|
|
48
72
|
|
|
@@ -52,7 +76,7 @@ describe('struct-typst2tex.yaml', function () {
|
|
|
52
76
|
suite.cases.forEach((c: TestCase) => {
|
|
53
77
|
test(c.title, function () {
|
|
54
78
|
const typst_node = parseTypst(c.typst);
|
|
55
|
-
const tex_node = convert_typst_node_to_tex(typst_node);
|
|
79
|
+
const tex_node = convert_typst_node_to_tex(typst_node, options);
|
|
56
80
|
const writer = new TexWriter();
|
|
57
81
|
writer.append(tex_node);
|
|
58
82
|
const res = writer.finalize();
|
|
@@ -66,7 +90,7 @@ describe('struct-bidirection.yaml', function () {
|
|
|
66
90
|
suite.cases.forEach((c: TestCase) => {
|
|
67
91
|
test(c.title, function () {
|
|
68
92
|
const typst_node = parseTypst(c.typst);
|
|
69
|
-
const tex_node = convert_typst_node_to_tex(typst_node);
|
|
93
|
+
const tex_node = convert_typst_node_to_tex(typst_node, options);
|
|
70
94
|
const writer = new TexWriter();
|
|
71
95
|
writer.append(tex_node);
|
|
72
96
|
const res = writer.finalize();
|