prettier-plugin-wolfram 0.7.5 → 0.7.7
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/package.json +1 -1
- package/src/parser/adapter.js +45 -11
- package/src/translator/nodes/binary.js +2 -0
package/package.json
CHANGED
package/src/parser/adapter.js
CHANGED
|
@@ -24,12 +24,14 @@ export function adapt(tree, source, preprocessedSource, map) {
|
|
|
24
24
|
const src = nodeSource(root, errLineIndex);
|
|
25
25
|
return { type: "ContainerNode", kind: "String", children: [{ type: "Unknown", kind: "SyntaxErrorNode[]", source: src }], source: src };
|
|
26
26
|
}
|
|
27
|
-
// Hoist top-level semicolon chains
|
|
28
|
-
//
|
|
29
|
-
//
|
|
27
|
+
// Hoist top-level semicolon chains into separate ContainerNode children,
|
|
28
|
+
// matching the CodeParser output structure. This must happen for multiline
|
|
29
|
+
// chains even when the final expression is not followed by a trailing ";",
|
|
30
|
+
// otherwise the printer sees one giant CompoundExpression and cannot apply
|
|
31
|
+
// top-level definition spacing between adjacent definitions.
|
|
30
32
|
const children = [];
|
|
31
33
|
for (const c of namedChildren(root)) {
|
|
32
|
-
if (
|
|
34
|
+
if (shouldHoistTopLevelSemicolonChain(c, ctx)) {
|
|
33
35
|
hoistSemicolonChildren(c, ctx, children);
|
|
34
36
|
} else {
|
|
35
37
|
children.push(adaptNode(c, ctx));
|
|
@@ -43,12 +45,22 @@ export function adapt(tree, source, preprocessedSource, map) {
|
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
function shouldHoistTopLevelSemicolonChain(node, ctx) {
|
|
49
|
+
if (node.type !== "infix" || operatorLiteral(node, ctx) !== ";") return false;
|
|
50
|
+
return hasTrailingSemicolon(node) || spansMultipleLines(node, ctx);
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
// Returns true if the infix node ends with a MISSING node (i.e. has a trailing ";").
|
|
47
54
|
function hasTrailingSemicolon(node) {
|
|
48
55
|
const last = node.child(node.childCount - 1);
|
|
49
56
|
return last !== null && last.isMissing;
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
function spansMultipleLines(node, ctx) {
|
|
60
|
+
const source = nodeSource(node, ctx.lineIndex);
|
|
61
|
+
return source?.[0]?.[0] !== source?.[1]?.[0];
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
// Collect all leaf statements from a semicolon infix chain (possibly left-recursive),
|
|
53
65
|
// flattening the left-associative structure into a linear list of segments.
|
|
54
66
|
// Each segment is { nodes: TSNode[], semiToken: TSNode|null } where semiToken is the ";" that follows.
|
|
@@ -56,7 +68,8 @@ function collectSemicolonSegments(node, ctx, segments) {
|
|
|
56
68
|
// A semicolon infix has children: [lhs, ";", rhs] or with comments interspersed.
|
|
57
69
|
// The lhs may itself be a semicolon infix (left-assoc chaining).
|
|
58
70
|
let lhs = null, semiToken = null, rhs = null;
|
|
59
|
-
const
|
|
71
|
+
const leadingComments = [];
|
|
72
|
+
const boundaryComments = [];
|
|
60
73
|
|
|
61
74
|
for (let i = 0; i < node.childCount; i++) {
|
|
62
75
|
const c = node.child(i);
|
|
@@ -66,7 +79,8 @@ function collectSemicolonSegments(node, ctx, segments) {
|
|
|
66
79
|
} else if (c.isMissing) {
|
|
67
80
|
// trailing implicit null — rhs is nothing (trailing semicolon)
|
|
68
81
|
} else if (c.type === "comment") {
|
|
69
|
-
|
|
82
|
+
if (lhs === null) leadingComments.push(c);
|
|
83
|
+
else boundaryComments.push(c);
|
|
70
84
|
} else if (lhs === null) {
|
|
71
85
|
lhs = c;
|
|
72
86
|
} else {
|
|
@@ -74,31 +88,50 @@ function collectSemicolonSegments(node, ctx, segments) {
|
|
|
74
88
|
}
|
|
75
89
|
}
|
|
76
90
|
|
|
91
|
+
const trailingComments = [];
|
|
92
|
+
const rhsLeadingComments = [];
|
|
93
|
+
for (const c of boundaryComments) {
|
|
94
|
+
if (rhs !== null && isLeadingCommentForNextSegment(c, semiToken, ctx)) {
|
|
95
|
+
rhsLeadingComments.push(c);
|
|
96
|
+
} else {
|
|
97
|
+
trailingComments.push(c);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
77
101
|
if (lhs !== null && lhs.type === "infix" && operatorLiteral(lhs, ctx) === ";") {
|
|
102
|
+
const segmentStart = segments.length;
|
|
78
103
|
// Flatten the left subtree
|
|
79
104
|
collectSemicolonSegments(lhs, ctx, segments);
|
|
105
|
+
if (segments.length > segmentStart) {
|
|
106
|
+
segments[segmentStart].leadingComments.unshift(...leadingComments);
|
|
107
|
+
}
|
|
80
108
|
// The last segment from lhs should get the semiToken we found
|
|
81
109
|
if (segments.length > 0 && semiToken !== null) {
|
|
82
110
|
segments[segments.length - 1].semiToken = semiToken;
|
|
83
111
|
}
|
|
84
|
-
|
|
85
|
-
for (const c of pendingComments) {
|
|
112
|
+
for (const c of trailingComments) {
|
|
86
113
|
if (segments.length > 0) segments[segments.length - 1].trailingComments.push(c);
|
|
87
114
|
}
|
|
88
115
|
} else {
|
|
89
116
|
// Push lhs as a new segment
|
|
90
117
|
const nodes = [];
|
|
91
118
|
if (lhs !== null) nodes.push(lhs);
|
|
92
|
-
segments.push({ nodes, semiToken, trailingComments
|
|
93
|
-
for (const c of pendingComments) segments[segments.length - 1].trailingComments.push(c);
|
|
119
|
+
segments.push({ nodes, semiToken, leadingComments, trailingComments });
|
|
94
120
|
}
|
|
95
121
|
|
|
96
122
|
// Push rhs as the next segment (no semiToken yet — will be set by caller if needed)
|
|
97
123
|
if (rhs !== null) {
|
|
98
|
-
segments.push({ nodes: [rhs], semiToken: null, trailingComments: [] });
|
|
124
|
+
segments.push({ nodes: [rhs], semiToken: null, leadingComments: rhsLeadingComments, trailingComments: [] });
|
|
99
125
|
}
|
|
100
126
|
}
|
|
101
127
|
|
|
128
|
+
function isLeadingCommentForNextSegment(comment, semiToken, ctx) {
|
|
129
|
+
if (!semiToken) return false;
|
|
130
|
+
const semiEndLine = nodeSource(semiToken, ctx.lineIndex)?.[1]?.[0];
|
|
131
|
+
const commentStartLine = nodeSource(comment, ctx.lineIndex)?.[0]?.[0];
|
|
132
|
+
return Number.isFinite(semiEndLine) && Number.isFinite(commentStartLine) && commentStartLine > semiEndLine;
|
|
133
|
+
}
|
|
134
|
+
|
|
102
135
|
// Hoist top-level semicolon-separated statements into separate ContainerNode children.
|
|
103
136
|
// For `a; b; c;`, produces [CompoundExpr(a;Null), CompoundExpr(b;Null), CompoundExpr(c;Null)].
|
|
104
137
|
// For `a; b`, produces [CompoundExpr(a;Null), b].
|
|
@@ -107,6 +140,7 @@ function hoistSemicolonChildren(node, ctx, out) {
|
|
|
107
140
|
collectSemicolonSegments(node, ctx, segments);
|
|
108
141
|
|
|
109
142
|
for (const seg of segments) {
|
|
143
|
+
for (const c of seg.leadingComments ?? []) out.push(leaf(c, ctx));
|
|
110
144
|
if (seg.nodes.length === 0) continue;
|
|
111
145
|
|
|
112
146
|
const adaptedNodes = seg.nodes.map(n => adaptNode(n, ctx));
|
|
@@ -16,6 +16,7 @@ const OP_DISPLAY = {
|
|
|
16
16
|
SetDelayed: ":=",
|
|
17
17
|
Power: "^",
|
|
18
18
|
ReplaceAll: "/.",
|
|
19
|
+
ReplaceRepeated: "//.",
|
|
19
20
|
Divide: "/",
|
|
20
21
|
Map: "/@",
|
|
21
22
|
Apply: "@@",
|
|
@@ -193,6 +194,7 @@ export function printBinary(node, options, print) {
|
|
|
193
194
|
"Power",
|
|
194
195
|
"Divide",
|
|
195
196
|
"ReplaceAll",
|
|
197
|
+
"ReplaceRepeated",
|
|
196
198
|
"Rule",
|
|
197
199
|
"RuleDelayed",
|
|
198
200
|
"Map",
|