prettier-plugin-wolfram 0.7.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/LICENSE +7 -0
- package/README.md +290 -0
- package/bin/prettier-wolfram.js +55 -0
- package/package.json +58 -0
- package/src/index.js +80 -0
- package/src/options.js +206 -0
- package/src/parser/adapter.js +690 -0
- package/src/parser/cstEqual.js +18 -0
- package/src/parser/index.js +29 -0
- package/src/parser/operators.js +35 -0
- package/src/parser/position.js +62 -0
- package/src/parser/tree-sitter-wolfram.wasm +0 -0
- package/src/range.js +98 -0
- package/src/rules/index.js +57 -0
- package/src/rules/line-width.js +129 -0
- package/src/rules/newlines-between-definitions.js +103 -0
- package/src/rules/no-bare-symbol-set.js +19 -0
- package/src/rules/no-dynamic-module-leak.js +74 -0
- package/src/rules/no-general-infix-function.js +52 -0
- package/src/rules/no-shadowed-pattern.js +71 -0
- package/src/rules/no-unused-module-var.js +84 -0
- package/src/rules/prefer-rule-delayed.js +59 -0
- package/src/rules/spacing-commas.js +64 -0
- package/src/rules/spacing-operators.js +87 -0
- package/src/translator/commentSpacing.js +51 -0
- package/src/translator/docComments.js +89 -0
- package/src/translator/index.js +98 -0
- package/src/translator/nodes/binary.js +205 -0
- package/src/translator/nodes/call.js +254 -0
- package/src/translator/nodes/compound.js +117 -0
- package/src/translator/nodes/container.js +194 -0
- package/src/translator/nodes/group.js +159 -0
- package/src/translator/nodes/infix.js +408 -0
- package/src/translator/nodes/leaf.js +605 -0
- package/src/translator/nodes/postfix.js +29 -0
- package/src/translator/nodes/prefix.js +27 -0
- package/src/translator/nodes/ternary.js +82 -0
- package/src/translator/ruleAlignment.js +133 -0
- package/src/translator/sourceLines.js +49 -0
- package/src/translator/sourcePreservation.js +22 -0
- package/src/translator/specialForms.js +665 -0
- package/src/utils/codeSpacing.js +420 -0
- package/src/utils/cstErrors.js +36 -0
- package/src/utils/offsets.js +132 -0
- package/src/utils/operatorSpacing.js +49 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/translator/nodes/container.js
|
|
2
|
+
import { doc } from "prettier";
|
|
3
|
+
const { builders } = doc;
|
|
4
|
+
const { hardline } = builders;
|
|
5
|
+
import {
|
|
6
|
+
documentationCommentColumn,
|
|
7
|
+
joinCommentDocs,
|
|
8
|
+
withAlignedTrailingComment,
|
|
9
|
+
} from "../docComments.js";
|
|
10
|
+
import { commentBoundarySeparator } from "../commentSpacing.js";
|
|
11
|
+
import {
|
|
12
|
+
blankLinesForCodeGap,
|
|
13
|
+
observedBlankLinesBetween,
|
|
14
|
+
} from "../../utils/codeSpacing.js";
|
|
15
|
+
import { nodeEndLine, nodeStartLine } from "../sourceLines.js";
|
|
16
|
+
|
|
17
|
+
function isTrivia(node) {
|
|
18
|
+
return (
|
|
19
|
+
node.type === "LeafNode" &&
|
|
20
|
+
["Token`Whitespace", "Whitespace", "Token`Newline", "Newline"].includes(
|
|
21
|
+
node.kind,
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isComment(node) {
|
|
27
|
+
return node.type === "LeafNode" && node.kind === "Token`Comment";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function trailingDocumentationCommentColumns(entries, options) {
|
|
31
|
+
const columns = new Map();
|
|
32
|
+
let block = [];
|
|
33
|
+
let previousEntry = null;
|
|
34
|
+
|
|
35
|
+
const flushBlock = () => {
|
|
36
|
+
if (block.length === 0) return;
|
|
37
|
+
const column = documentationCommentColumn(block, options);
|
|
38
|
+
for (const entry of block) {
|
|
39
|
+
columns.set(entry, column);
|
|
40
|
+
}
|
|
41
|
+
block = [];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const observedBlankLines = previousEntry
|
|
46
|
+
? observedBlankLinesBetween(previousEntry.endLine, entry.startLine)
|
|
47
|
+
: 0;
|
|
48
|
+
|
|
49
|
+
if (
|
|
50
|
+
!entry.trailingCommentDoc ||
|
|
51
|
+
entry.leadingComments.length > 0 ||
|
|
52
|
+
observedBlankLines > 0
|
|
53
|
+
) {
|
|
54
|
+
flushBlock();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (entry.trailingCommentDoc) {
|
|
58
|
+
block.push(entry);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
previousEntry = entry;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
flushBlock();
|
|
65
|
+
return columns;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function containerCommentSeparator(leftNode, rightNode, options) {
|
|
69
|
+
return commentBoundarySeparator(leftNode, rightNode, options, hardline);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function appendLeadingComments(docs, comments, nextNode, options) {
|
|
73
|
+
for (let i = 0; i < comments.length; i++) {
|
|
74
|
+
const comment = comments[i];
|
|
75
|
+
const followingNode = comments[i + 1]?.node ?? nextNode;
|
|
76
|
+
docs.push(comment.doc);
|
|
77
|
+
if (followingNode) {
|
|
78
|
+
docs.push(
|
|
79
|
+
containerCommentSeparator(comment.node, followingNode, options),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Print a ContainerNode[File, ...] with declaration-aware top-level spacing. */
|
|
86
|
+
export function printContainer(node, options, print) {
|
|
87
|
+
const children = node.children.filter((c) => !isTrivia(c));
|
|
88
|
+
|
|
89
|
+
if (children.length === 0) return "";
|
|
90
|
+
|
|
91
|
+
const entries = [];
|
|
92
|
+
let leadingComments = [];
|
|
93
|
+
let leadingCommentStartLine = null;
|
|
94
|
+
let leadingCommentEndLine = null;
|
|
95
|
+
|
|
96
|
+
for (const child of children) {
|
|
97
|
+
if (isComment(child) && entries.length > 0) {
|
|
98
|
+
const prev = entries[entries.length - 1];
|
|
99
|
+
const prevEndLine = nodeEndLine(prev.node, options);
|
|
100
|
+
const commentStartLine = nodeStartLine(child, options);
|
|
101
|
+
if (
|
|
102
|
+
prevEndLine &&
|
|
103
|
+
commentStartLine &&
|
|
104
|
+
prevEndLine === commentStartLine
|
|
105
|
+
) {
|
|
106
|
+
prev.trailingComments.push({ node: child, doc: print(child) });
|
|
107
|
+
prev.endLine = Math.max(
|
|
108
|
+
prev.endLine,
|
|
109
|
+
nodeEndLine(child, options) ?? prev.endLine,
|
|
110
|
+
);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (isComment(child)) {
|
|
116
|
+
leadingComments.push({ node: child, doc: print(child) });
|
|
117
|
+
leadingCommentStartLine ??= nodeStartLine(child, options);
|
|
118
|
+
leadingCommentEndLine =
|
|
119
|
+
nodeEndLine(child, options) ?? leadingCommentEndLine;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const childStartLine = nodeStartLine(child, options);
|
|
124
|
+
const childEndLine = nodeEndLine(child, options) ?? childStartLine ?? 0;
|
|
125
|
+
|
|
126
|
+
entries.push({
|
|
127
|
+
node: child,
|
|
128
|
+
doc: print(child),
|
|
129
|
+
leadingComments,
|
|
130
|
+
trailingComments: [],
|
|
131
|
+
startLine: leadingCommentStartLine ?? childStartLine ?? 0,
|
|
132
|
+
endLine: Math.max(
|
|
133
|
+
childEndLine,
|
|
134
|
+
leadingCommentEndLine ?? childEndLine,
|
|
135
|
+
),
|
|
136
|
+
});
|
|
137
|
+
leadingComments = [];
|
|
138
|
+
leadingCommentStartLine = null;
|
|
139
|
+
leadingCommentEndLine = null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const entry of entries) {
|
|
143
|
+
entry.trailingCommentDoc = joinCommentDocs(
|
|
144
|
+
entry.trailingComments,
|
|
145
|
+
options,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const trailingCommentColumns = trailingDocumentationCommentColumns(
|
|
150
|
+
entries,
|
|
151
|
+
options,
|
|
152
|
+
);
|
|
153
|
+
const docs = [];
|
|
154
|
+
let previousNode = null;
|
|
155
|
+
|
|
156
|
+
for (const entry of entries) {
|
|
157
|
+
if (docs.length > 0) {
|
|
158
|
+
const observedBlankLines = observedBlankLinesBetween(
|
|
159
|
+
previousNode.endLine,
|
|
160
|
+
entry.startLine,
|
|
161
|
+
);
|
|
162
|
+
const blankLines = blankLinesForCodeGap(
|
|
163
|
+
previousNode.node,
|
|
164
|
+
entry.node,
|
|
165
|
+
observedBlankLines,
|
|
166
|
+
options,
|
|
167
|
+
{ topLevel: true },
|
|
168
|
+
);
|
|
169
|
+
docs.push(hardline, ...Array(blankLines).fill(hardline));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (entry.leadingComments.length > 0) {
|
|
173
|
+
appendLeadingComments(
|
|
174
|
+
docs,
|
|
175
|
+
entry.leadingComments,
|
|
176
|
+
entry.node,
|
|
177
|
+
options,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const column =
|
|
182
|
+
trailingCommentColumns.get(entry) ??
|
|
183
|
+
documentationCommentColumn([entry], options);
|
|
184
|
+
docs.push(withAlignedTrailingComment(entry, options, column));
|
|
185
|
+
previousNode = entry;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (leadingComments.length > 0) {
|
|
189
|
+
if (docs.length > 0) docs.push(hardline);
|
|
190
|
+
appendLeadingComments(docs, leadingComments, null, options);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return docs;
|
|
194
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// src/translator/nodes/group.js
|
|
2
|
+
import { doc } from "prettier";
|
|
3
|
+
const { builders } = doc;
|
|
4
|
+
import { isComment, isTrivia } from "./leaf.js";
|
|
5
|
+
import { alignedRuleDoc, withAlignedRuleValues } from "../ruleAlignment.js";
|
|
6
|
+
import { commentBoundarySeparator } from "../commentSpacing.js";
|
|
7
|
+
import { normalizeWolframOptions } from "../../options.js";
|
|
8
|
+
const { group, indent, softline, line } = builders;
|
|
9
|
+
|
|
10
|
+
const GROUP_DELIMITERS = {
|
|
11
|
+
GroupSquare: ["[", "]"],
|
|
12
|
+
GroupCurly: ["{", "}"],
|
|
13
|
+
GroupParen: ["(", ")"],
|
|
14
|
+
GroupDoubleBracket: ["[[", "]]"],
|
|
15
|
+
List: ["{", "}"],
|
|
16
|
+
Association: ["<|", "|>"],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const BRACKET_KINDS = new Set([
|
|
20
|
+
"Token`OpenCurly",
|
|
21
|
+
"Token`CloseCurly",
|
|
22
|
+
"Token`OpenSquare",
|
|
23
|
+
"Token`CloseSquare",
|
|
24
|
+
"Token`OpenParen",
|
|
25
|
+
"Token`CloseParen",
|
|
26
|
+
"Token`LessBar",
|
|
27
|
+
"Token`BarGreater",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
function isBracketToken(node) {
|
|
31
|
+
return node.type === "LeafNode" && BRACKET_KINDS.has(node.kind);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isCommaToken(node) {
|
|
35
|
+
return node.type === "LeafNode" && node.kind === "Token`Comma";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function nextContentEntry(entries, startIndex) {
|
|
39
|
+
for (let i = startIndex + 1; i < entries.length; i++) {
|
|
40
|
+
if (!isCommaToken(entries[i].node)) return entries[i];
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function previousContentEntry(entries, startIndex) {
|
|
46
|
+
for (let i = startIndex - 1; i >= 0; i--) {
|
|
47
|
+
if (!isCommaToken(entries[i].node)) return entries[i];
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function hasCommentBoundary(leftEntry, rightEntry) {
|
|
53
|
+
return isComment(leftEntry?.node) || isComment(rightEntry?.node);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function commentBoundary(leftEntry, rightEntry, options, fallback = line) {
|
|
57
|
+
if (!leftEntry || !rightEntry) return fallback;
|
|
58
|
+
return commentBoundarySeparator(
|
|
59
|
+
leftEntry.node,
|
|
60
|
+
rightEntry.node,
|
|
61
|
+
options,
|
|
62
|
+
fallback,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sequenceEntries(path, print, node) {
|
|
67
|
+
const contents = node.children.filter(
|
|
68
|
+
(child) => !isTrivia(child) && !isBracketToken(child),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
contents.length === 1 &&
|
|
73
|
+
contents[0].type === "InfixNode" &&
|
|
74
|
+
contents[0].op === "Comma"
|
|
75
|
+
) {
|
|
76
|
+
const wrapperIdx = node.children.indexOf(contents[0]);
|
|
77
|
+
return contents[0].children.reduce((entries, child, idx) => {
|
|
78
|
+
if (isTrivia(child)) return entries;
|
|
79
|
+
entries.push({
|
|
80
|
+
node: child,
|
|
81
|
+
doc: path.call(print, "children", wrapperIdx, "children", idx),
|
|
82
|
+
path: ["children", wrapperIdx, "children", idx],
|
|
83
|
+
});
|
|
84
|
+
return entries;
|
|
85
|
+
}, []);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return node.children.reduce((entries, child, idx) => {
|
|
89
|
+
if (isTrivia(child) || isBracketToken(child)) return entries;
|
|
90
|
+
entries.push({
|
|
91
|
+
node: child,
|
|
92
|
+
doc: path.call(print, "children", idx),
|
|
93
|
+
path: ["children", idx],
|
|
94
|
+
});
|
|
95
|
+
return entries;
|
|
96
|
+
}, []);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function printGroup(path, options, print, node) {
|
|
100
|
+
options = normalizeWolframOptions(options);
|
|
101
|
+
const [open, close] = GROUP_DELIMITERS[node.kind] ?? ["{", "}"];
|
|
102
|
+
const entries = withAlignedRuleValues(
|
|
103
|
+
sequenceEntries(path, print, node),
|
|
104
|
+
path,
|
|
105
|
+
options,
|
|
106
|
+
print,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (entries.length === 0) return `${open}${close}`;
|
|
110
|
+
|
|
111
|
+
const docs = [];
|
|
112
|
+
const commaGap = options.wolframSpaceAfterComma ? line : softline;
|
|
113
|
+
const alignmentGroupId = entries.some((entry) => entry.alignedRuleDoc)
|
|
114
|
+
? Symbol("wolfram-align-rule-values")
|
|
115
|
+
: null;
|
|
116
|
+
let previousKind = null;
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < entries.length; i++) {
|
|
119
|
+
const entry = entries[i];
|
|
120
|
+
if (isCommaToken(entry.node)) {
|
|
121
|
+
if (previousKind === null || previousKind === "comma") continue;
|
|
122
|
+
const previousEntry = previousContentEntry(entries, i);
|
|
123
|
+
const followingEntry = nextContentEntry(entries, i);
|
|
124
|
+
const separator =
|
|
125
|
+
followingEntry &&
|
|
126
|
+
hasCommentBoundary(previousEntry, followingEntry)
|
|
127
|
+
? commentBoundary(
|
|
128
|
+
previousEntry,
|
|
129
|
+
followingEntry,
|
|
130
|
+
options,
|
|
131
|
+
commaGap,
|
|
132
|
+
)
|
|
133
|
+
: commaGap;
|
|
134
|
+
docs.push(",", separator);
|
|
135
|
+
previousKind = "comma";
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (previousKind !== null && previousKind !== "comma") {
|
|
140
|
+
const previousEntry = previousContentEntry(entries, i);
|
|
141
|
+
docs.push(
|
|
142
|
+
hasCommentBoundary(previousEntry, entry)
|
|
143
|
+
? commentBoundary(previousEntry, entry, options, line)
|
|
144
|
+
: line,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
docs.push(alignedRuleDoc(entry, alignmentGroupId));
|
|
149
|
+
previousKind = isComment(entry.node) ? "comment" : "item";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (docs.length === 0) return `${open}${close}`;
|
|
153
|
+
|
|
154
|
+
const contents = [open, indent([softline, ...docs]), softline, close];
|
|
155
|
+
|
|
156
|
+
return alignmentGroupId
|
|
157
|
+
? group(contents, { id: alignmentGroupId })
|
|
158
|
+
: group(contents);
|
|
159
|
+
}
|