@haneullabs/prettier-plugin-move 0.3.3
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/CHANGELOG.md +67 -0
- package/CONTRIBUTING.md +31 -0
- package/README.md +96 -0
- package/bin/prettier-move.js +29 -0
- package/out/cst/annotation.js +64 -0
- package/out/cst/annotation.js.map +1 -0
- package/out/cst/common.js +376 -0
- package/out/cst/common.js.map +1 -0
- package/out/cst/constant.js +92 -0
- package/out/cst/constant.js.map +1 -0
- package/out/cst/enum_definition.js +69 -0
- package/out/cst/enum_definition.js.map +1 -0
- package/out/cst/expression/abort_expression.js +32 -0
- package/out/cst/expression/abort_expression.js.map +1 -0
- package/out/cst/expression/annotation_expression.js +35 -0
- package/out/cst/expression/annotation_expression.js.map +1 -0
- package/out/cst/expression/assign_expression.js +51 -0
- package/out/cst/expression/assign_expression.js.map +1 -0
- package/out/cst/expression/binary_expression.js +70 -0
- package/out/cst/expression/binary_expression.js.map +1 -0
- package/out/cst/expression/block.js +58 -0
- package/out/cst/expression/block.js.map +1 -0
- package/out/cst/expression/block_item.js +25 -0
- package/out/cst/expression/block_item.js.map +1 -0
- package/out/cst/expression/borrow_expression.js +26 -0
- package/out/cst/expression/borrow_expression.js.map +1 -0
- package/out/cst/expression/break_expression.js +27 -0
- package/out/cst/expression/break_expression.js.map +1 -0
- package/out/cst/expression/call_expression.js +25 -0
- package/out/cst/expression/call_expression.js.map +1 -0
- package/out/cst/expression/cast_expression.js +31 -0
- package/out/cst/expression/cast_expression.js.map +1 -0
- package/out/cst/expression/continue_expression.js +26 -0
- package/out/cst/expression/continue_expression.js.map +1 -0
- package/out/cst/expression/dereference_expression.js +27 -0
- package/out/cst/expression/dereference_expression.js.map +1 -0
- package/out/cst/expression/dot_expression.js +66 -0
- package/out/cst/expression/dot_expression.js.map +1 -0
- package/out/cst/expression/expression_list.js +26 -0
- package/out/cst/expression/expression_list.js.map +1 -0
- package/out/cst/expression/identified_expression.js +28 -0
- package/out/cst/expression/identified_expression.js.map +1 -0
- package/out/cst/expression/if_expression.js +133 -0
- package/out/cst/expression/if_expression.js.map +1 -0
- package/out/cst/expression/index.js +74 -0
- package/out/cst/expression/index.js.map +1 -0
- package/out/cst/expression/index_expression.js +28 -0
- package/out/cst/expression/index_expression.js.map +1 -0
- package/out/cst/expression/lambda_expression.js +72 -0
- package/out/cst/expression/lambda_expression.js.map +1 -0
- package/out/cst/expression/let_statement.js +59 -0
- package/out/cst/expression/let_statement.js.map +1 -0
- package/out/cst/expression/loop_expression.js +27 -0
- package/out/cst/expression/loop_expression.js.map +1 -0
- package/out/cst/expression/macro_call_expression.js +66 -0
- package/out/cst/expression/macro_call_expression.js.map +1 -0
- package/out/cst/expression/match_expression.js +86 -0
- package/out/cst/expression/match_expression.js.map +1 -0
- package/out/cst/expression/move_or_copy_expression.js +27 -0
- package/out/cst/expression/move_or_copy_expression.js.map +1 -0
- package/out/cst/expression/name_expression.js +26 -0
- package/out/cst/expression/name_expression.js.map +1 -0
- package/out/cst/expression/pack_expression.js +27 -0
- package/out/cst/expression/pack_expression.js.map +1 -0
- package/out/cst/expression/return_expression.js +44 -0
- package/out/cst/expression/return_expression.js.map +1 -0
- package/out/cst/expression/unary_expression.js +26 -0
- package/out/cst/expression/unary_expression.js.map +1 -0
- package/out/cst/expression/unit_expression.js +17 -0
- package/out/cst/expression/unit_expression.js.map +1 -0
- package/out/cst/expression/vector_expression.js +80 -0
- package/out/cst/expression/vector_expression.js.map +1 -0
- package/out/cst/expression/while_expression.js +42 -0
- package/out/cst/expression/while_expression.js.map +1 -0
- package/out/cst/formatting.js +100 -0
- package/out/cst/formatting.js.map +1 -0
- package/out/cst/function_definition.js +248 -0
- package/out/cst/function_definition.js.map +1 -0
- package/out/cst/literal.js +68 -0
- package/out/cst/literal.js.map +1 -0
- package/out/cst/module.js +158 -0
- package/out/cst/module.js.map +1 -0
- package/out/cst/source_file.js +38 -0
- package/out/cst/source_file.js.map +1 -0
- package/out/cst/struct_definition.js +209 -0
- package/out/cst/struct_definition.js.map +1 -0
- package/out/cst/use_declaration.js +212 -0
- package/out/cst/use_declaration.js.map +1 -0
- package/out/imports-grouping.js +259 -0
- package/out/imports-grouping.js.map +1 -0
- package/out/index.js +97 -0
- package/out/index.js.map +1 -0
- package/out/printer.js +69 -0
- package/out/printer.js.map +1 -0
- package/out/tree.js +371 -0
- package/out/tree.js.map +1 -0
- package/out/utilities.js +251 -0
- package/out/utilities.js.map +1 -0
- package/package.json +34 -0
- package/prettier.config.js +12 -0
- package/src/cst/annotation.ts +71 -0
- package/src/cst/common.ts +430 -0
- package/src/cst/constant.ts +110 -0
- package/src/cst/enum_definition.ts +73 -0
- package/src/cst/expression/abort_expression.ts +35 -0
- package/src/cst/expression/annotation_expression.ts +38 -0
- package/src/cst/expression/assign_expression.ts +66 -0
- package/src/cst/expression/binary_expression.ts +75 -0
- package/src/cst/expression/block.ts +72 -0
- package/src/cst/expression/block_item.ts +29 -0
- package/src/cst/expression/borrow_expression.ts +28 -0
- package/src/cst/expression/break_expression.ts +33 -0
- package/src/cst/expression/call_expression.ts +28 -0
- package/src/cst/expression/cast_expression.ts +35 -0
- package/src/cst/expression/continue_expression.ts +29 -0
- package/src/cst/expression/dereference_expression.ts +33 -0
- package/src/cst/expression/dot_expression.ts +89 -0
- package/src/cst/expression/expression_list.ts +28 -0
- package/src/cst/expression/identified_expression.ts +30 -0
- package/src/cst/expression/if_expression.ts +177 -0
- package/src/cst/expression/index.ts +85 -0
- package/src/cst/expression/index_expression.ts +37 -0
- package/src/cst/expression/lambda_expression.ts +84 -0
- package/src/cst/expression/let_statement.ts +73 -0
- package/src/cst/expression/loop_expression.ts +29 -0
- package/src/cst/expression/macro_call_expression.ts +79 -0
- package/src/cst/expression/match_expression.ts +102 -0
- package/src/cst/expression/move_or_copy_expression.ts +29 -0
- package/src/cst/expression/name_expression.ts +28 -0
- package/src/cst/expression/pack_expression.ts +29 -0
- package/src/cst/expression/return_expression.ts +50 -0
- package/src/cst/expression/unary_expression.ts +28 -0
- package/src/cst/expression/unit_expression.ts +18 -0
- package/src/cst/expression/vector_expression.ts +97 -0
- package/src/cst/expression/while_expression.ts +45 -0
- package/src/cst/formatting.ts +100 -0
- package/src/cst/function_definition.ts +300 -0
- package/src/cst/literal.ts +69 -0
- package/src/cst/module.ts +191 -0
- package/src/cst/source_file.ts +38 -0
- package/src/cst/struct_definition.ts +267 -0
- package/src/cst/use_declaration.ts +238 -0
- package/src/imports-grouping.ts +300 -0
- package/src/index.ts +119 -0
- package/src/printer.ts +93 -0
- package/src/tree.ts +438 -0
- package/src/utilities.ts +387 -0
- package/tree-sitter-move.wasm +0 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// Copyright (c) The Move Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This module contains the logic for grouping imports in a file.
|
|
6
|
+
*
|
|
7
|
+
* @module imports-grouping
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Doc, doc } from 'prettier';
|
|
11
|
+
import { Node } from './';
|
|
12
|
+
import { UseDeclaration } from './cst/use_declaration';
|
|
13
|
+
const { join, softline, indent, line, group } = doc.builders;
|
|
14
|
+
|
|
15
|
+
// === Import Grouping ===
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A simple type to represent grouped imports.
|
|
19
|
+
*/
|
|
20
|
+
export type GroupedImports = Map<string, Map<string, Member[]>>;
|
|
21
|
+
|
|
22
|
+
type Member = {
|
|
23
|
+
name: string | 'Self';
|
|
24
|
+
alias: string | undefined;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Special function to print imports if import grouping is turned on.
|
|
29
|
+
* Important note: we don't use the `print` function for imports, as they're already
|
|
30
|
+
* parsed. We just need to print them in the correct order and format.
|
|
31
|
+
*
|
|
32
|
+
* We sort and avoid duplicates in the imports. To do so, we keep track of the
|
|
33
|
+
* printed keys (including alias!), and only print the import if it hasn't been
|
|
34
|
+
* printed before.
|
|
35
|
+
*/
|
|
36
|
+
export function printImports(imports: GroupedImports, option: 'module' | 'package'): Doc {
|
|
37
|
+
const pkgs = [...imports.keys()].sort();
|
|
38
|
+
const result = [] as Doc[];
|
|
39
|
+
|
|
40
|
+
for (const pkg of pkgs) {
|
|
41
|
+
const modules = imports.get(pkg);
|
|
42
|
+
|
|
43
|
+
// typescript wants this
|
|
44
|
+
if (modules == undefined) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const keys = [...modules.keys()].sort();
|
|
49
|
+
|
|
50
|
+
// if grouped by module
|
|
51
|
+
if (option === 'module') {
|
|
52
|
+
for (const mod of keys) {
|
|
53
|
+
if (!modules.get(mod)) continue;
|
|
54
|
+
result.push(['use ', pkg, '::', printModule(mod, modules.get(mod)!), ';']);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
// if grouped by package
|
|
58
|
+
const modulesDoc = [] as Doc[];
|
|
59
|
+
|
|
60
|
+
for (const mod of keys) {
|
|
61
|
+
if (!modules.has(mod)) continue;
|
|
62
|
+
modulesDoc.push(printModule(mod, modules.get(mod)!));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
modulesDoc.length === 1
|
|
66
|
+
? result.push(['use ', pkg, '::', modulesDoc[0]!, ';'])
|
|
67
|
+
: result.push([
|
|
68
|
+
'use ',
|
|
69
|
+
pkg,
|
|
70
|
+
'::',
|
|
71
|
+
group([
|
|
72
|
+
'{',
|
|
73
|
+
indent(softline),
|
|
74
|
+
indent(join([',', line], modulesDoc)),
|
|
75
|
+
softline,
|
|
76
|
+
'}',
|
|
77
|
+
]),
|
|
78
|
+
';',
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function printModule(mod: string, members: Member[]): Doc {
|
|
87
|
+
const printedKeys: string[] = [];
|
|
88
|
+
|
|
89
|
+
// perform deduplication of imports
|
|
90
|
+
members = members.filter((m) => {
|
|
91
|
+
const key = [mod, m.name, m.alias || '-'].join('');
|
|
92
|
+
if (printedKeys.includes(key)) return false;
|
|
93
|
+
printedKeys.push(key);
|
|
94
|
+
return true;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (members.length === 1) {
|
|
98
|
+
const member = members[0]!;
|
|
99
|
+
if (member.name === 'Self') {
|
|
100
|
+
const alias = member.alias ? ` as ${member.alias}` : '';
|
|
101
|
+
return `${mod}${alias}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return [mod, '::', printMember(member)];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const selfIdx = members.findIndex((m) => m.name === 'Self');
|
|
108
|
+
if (selfIdx !== -1) {
|
|
109
|
+
const self = members.splice(selfIdx, 1);
|
|
110
|
+
members = [...self, ...members];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return members.length === 0
|
|
114
|
+
? [mod]
|
|
115
|
+
: [
|
|
116
|
+
mod,
|
|
117
|
+
'::',
|
|
118
|
+
group([
|
|
119
|
+
'{',
|
|
120
|
+
indent(softline),
|
|
121
|
+
indent(join([',', line], members.map(printMember))),
|
|
122
|
+
softline,
|
|
123
|
+
'}',
|
|
124
|
+
]),
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Print a single member of a module with an optional alias.
|
|
130
|
+
*/
|
|
131
|
+
function printMember({ name, alias }: Member): Doc {
|
|
132
|
+
const a = alias ? ` as ${alias}` : '';
|
|
133
|
+
return `${name}${a}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Special function which walks the current node and collects all `use` imports.
|
|
138
|
+
* Returns a tree of all imports in this file to be used for grouping.
|
|
139
|
+
*
|
|
140
|
+
* There are 3 main types of imports:
|
|
141
|
+
* - `use_module` - `module_access` <as `alias`>;
|
|
142
|
+
* - `use_module_member` - `<module_identity>::<use_member>`;
|
|
143
|
+
* - `use_module_members` - `package::<use_member>, <use_member>, ...`
|
|
144
|
+
*
|
|
145
|
+
* @param node
|
|
146
|
+
* @returns
|
|
147
|
+
*/
|
|
148
|
+
export function collectImports(node: Node): GroupedImports {
|
|
149
|
+
const grouped: GroupedImports = new Map();
|
|
150
|
+
const imports = node.nonFormattingChildren
|
|
151
|
+
.filter((n) => n.isGroupedImport)
|
|
152
|
+
.map((n) => n.nonFormattingChildren[0]!);
|
|
153
|
+
|
|
154
|
+
for (let import_ of imports) {
|
|
155
|
+
switch (import_.type) {
|
|
156
|
+
// `module_access` <as `alias`>;
|
|
157
|
+
case UseDeclaration.UseModule: {
|
|
158
|
+
const moduleIdentity = import_.nonFormattingChildren[0]!;
|
|
159
|
+
const alias = import_.nonFormattingChildren[1];
|
|
160
|
+
const [pkg, mod] = parseModuleIdentity(moduleIdentity);
|
|
161
|
+
|
|
162
|
+
// we use `Self` in the tree to represent the current module
|
|
163
|
+
const rec = { name: 'Self', alias: alias?.text };
|
|
164
|
+
|
|
165
|
+
// if there hasn't been a registered package yet, add it
|
|
166
|
+
if (!grouped.has(pkg)) grouped.set(pkg, new Map());
|
|
167
|
+
const pkgMap = grouped.get(pkg)!;
|
|
168
|
+
// if there hasn't been a registered module yet, add it
|
|
169
|
+
if (!pkgMap.has(mod)) pkgMap.set(mod, []);
|
|
170
|
+
pkgMap.set(mod, pkgMap.get(mod)!);
|
|
171
|
+
pkgMap.get(mod)!.push(rec);
|
|
172
|
+
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
// `<module_identity>::<use_member>`
|
|
176
|
+
case UseDeclaration.UseModuleMember: {
|
|
177
|
+
const moduleIdentity = import_.nonFormattingChildren[0]!;
|
|
178
|
+
const [pkg, mod] = parseModuleIdentity(moduleIdentity);
|
|
179
|
+
const useMember = import_.nonFormattingChildren[1]!;
|
|
180
|
+
const [name, alias] = parseUseMember(useMember);
|
|
181
|
+
|
|
182
|
+
if (!grouped.has(pkg)) grouped.set(pkg, new Map());
|
|
183
|
+
const pkgMap = grouped.get(pkg)!;
|
|
184
|
+
if (!pkgMap.has(mod)) pkgMap.set(mod, []);
|
|
185
|
+
const modMap = pkgMap.get(mod)!;
|
|
186
|
+
modMap.push({ name, alias });
|
|
187
|
+
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
// The only tricky node in this scheme. `use_module_members` can be
|
|
191
|
+
// both for grouped by package and for grouped by module, so we have
|
|
192
|
+
// to detect which version it is and then dance off of that.
|
|
193
|
+
case UseDeclaration.UseModuleMembers: {
|
|
194
|
+
const children = import_.nonFormattingChildren;
|
|
195
|
+
const isGroupedByPackage = children[0]!.type === 'module_identifier';
|
|
196
|
+
|
|
197
|
+
if (!isGroupedByPackage && children[0]!.type !== UseDeclaration.ModuleIdentity) {
|
|
198
|
+
throw new Error('Expected `module_identity` or `module_identifier`');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// simple scenario: the first node is `module_identity`
|
|
202
|
+
if (!isGroupedByPackage) {
|
|
203
|
+
const moduleIdentity = children[0]!;
|
|
204
|
+
const [pkg, mod] = parseModuleIdentity(moduleIdentity);
|
|
205
|
+
const members = children.slice(1).map((n) => parseUseMember(n));
|
|
206
|
+
|
|
207
|
+
if (!grouped.has(pkg)) grouped.set(pkg, new Map());
|
|
208
|
+
const pkgMap = grouped.get(pkg)!;
|
|
209
|
+
if (!pkgMap.has(mod)) pkgMap.set(mod, []);
|
|
210
|
+
const modMap = pkgMap.get(mod)!;
|
|
211
|
+
|
|
212
|
+
modMap.push(...members.map(([name, alias]) => ({ name, alias })));
|
|
213
|
+
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// complex scenario: the first node is `module_identifier`
|
|
218
|
+
// `use_member` can be recursive in this scenario with 1 level of nesting
|
|
219
|
+
const pkg = children[0]!.text;
|
|
220
|
+
if (!grouped.has(pkg)) grouped.set(pkg, new Map());
|
|
221
|
+
const pkgMap = grouped.get(pkg)!;
|
|
222
|
+
|
|
223
|
+
children.slice(1).forEach((node) => {
|
|
224
|
+
if (!node) return;
|
|
225
|
+
|
|
226
|
+
if (node.type !== UseDeclaration.UseMember)
|
|
227
|
+
throw new Error('Expected `use_member` node got `' + node.type + '`');
|
|
228
|
+
|
|
229
|
+
const [first, ...rest] = node.nonFormattingChildren;
|
|
230
|
+
if (!first || first.type !== 'identifier')
|
|
231
|
+
throw new Error('Expected `identifier` node in `use_module_members`');
|
|
232
|
+
|
|
233
|
+
const mod = first.text;
|
|
234
|
+
if (!pkgMap.has(mod)) pkgMap.set(mod, []);
|
|
235
|
+
|
|
236
|
+
// if there's only one member and it's the module.
|
|
237
|
+
if (!rest.length) {
|
|
238
|
+
pkgMap.get(mod)!.push({ name: 'Self', alias: undefined });
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ident + ident is an alias
|
|
243
|
+
if (rest.length == 1 && rest[0]?.type === 'identifier') {
|
|
244
|
+
if (rest[0].previousSibling?.type !== 'as') {
|
|
245
|
+
pkgMap.get(mod)!.push({ name: rest[0].text, alias: undefined });
|
|
246
|
+
} else {
|
|
247
|
+
pkgMap.get(mod)!.push({ name: 'Self', alias: rest[0].text });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// special case, no use member, but already expanded pair of identifiers.
|
|
254
|
+
if (
|
|
255
|
+
rest.length == 2 &&
|
|
256
|
+
rest[0]?.type === 'identifier' &&
|
|
257
|
+
rest[1]?.type === 'identifier'
|
|
258
|
+
) {
|
|
259
|
+
if (rest[1].previousSibling?.type !== 'as') {
|
|
260
|
+
throw new Error('Expected `as` keyword after module name');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
pkgMap.get(mod)!.push({ name: rest[0].text, alias: rest[1].text });
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// the rest are `use_member` nodes
|
|
268
|
+
const members = rest.map(parseUseMember);
|
|
269
|
+
pkgMap.get(mod)!.push(...members.map(([name, alias]) => ({ name, alias })));
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return grouped;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Parse a `module_identity` node returning a tuple of package and module.
|
|
280
|
+
*/
|
|
281
|
+
function parseModuleIdentity(node: Node): [string, string] {
|
|
282
|
+
if (node.type !== UseDeclaration.ModuleIdentity) {
|
|
283
|
+
throw new Error('Expected `module_identity` node');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const [pkg, mod] = node.nonFormattingChildren.map((n) => n.text);
|
|
287
|
+
return [pkg!, mod!];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Parse a simple `use_member` node returning a tuple of member and alias.
|
|
292
|
+
*/
|
|
293
|
+
function parseUseMember(node: Node): [string, string | undefined] {
|
|
294
|
+
if (node.type !== UseDeclaration.UseMember) {
|
|
295
|
+
throw new Error('Expected `use_member` node, got `' + node.type + '`');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const [member, alias] = node.nonFormattingChildren.map((n) => n.text);
|
|
299
|
+
return [member!, alias];
|
|
300
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Copyright (c) The Move Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Contains the Prettier Plugin definition for the Move language.
|
|
6
|
+
* For more information on Prettier plugins, see https://prettier.io/docs/en/plugins.html
|
|
7
|
+
*
|
|
8
|
+
* The printing logic is implemented in the `printer` module, which is routing the
|
|
9
|
+
* specific node types defined in the `cst/*` modules.
|
|
10
|
+
*
|
|
11
|
+
* Additionally, `utilities` module contains helper functions for the printer.
|
|
12
|
+
*
|
|
13
|
+
* @module prettier-move
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import Parser = require('web-tree-sitter');
|
|
18
|
+
import { print } from './printer';
|
|
19
|
+
import { Tree } from './tree';
|
|
20
|
+
import {
|
|
21
|
+
Parser as PrettierParser,
|
|
22
|
+
Printer,
|
|
23
|
+
Plugin,
|
|
24
|
+
SupportOption,
|
|
25
|
+
SupportLanguage,
|
|
26
|
+
} from 'prettier';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Alias for easier refactoring if the SyntaxNode is changed.
|
|
30
|
+
*/
|
|
31
|
+
export type Node = Tree;
|
|
32
|
+
|
|
33
|
+
export const languages: SupportLanguage[] = [
|
|
34
|
+
{
|
|
35
|
+
name: 'move',
|
|
36
|
+
extensions: ['.move'],
|
|
37
|
+
parsers: ['move'],
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export const parsers: { [key: string]: PrettierParser } = {
|
|
42
|
+
move: {
|
|
43
|
+
parse: (text: string): Promise<Node> => {
|
|
44
|
+
return (async (): Promise<Node> => {
|
|
45
|
+
await Parser.init();
|
|
46
|
+
const parser = new Parser();
|
|
47
|
+
const Lang = await Parser.Language.load(
|
|
48
|
+
path.join(__dirname, '..', 'tree-sitter-move.wasm'),
|
|
49
|
+
);
|
|
50
|
+
parser.setLanguage(Lang);
|
|
51
|
+
return new Tree(parser.parse(text).rootNode);
|
|
52
|
+
})();
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
astFormat: 'move',
|
|
56
|
+
locStart: () => -1,
|
|
57
|
+
locEnd: () => -1,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const printers: { [key: string]: Printer } = {
|
|
62
|
+
move: { print },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const options: Record<string, SupportOption> = {
|
|
66
|
+
autoGroupImports: {
|
|
67
|
+
type: 'choice',
|
|
68
|
+
category: 'Global',
|
|
69
|
+
default: 'package',
|
|
70
|
+
description: "Group all use imports by 'package', 'module' or 'none'.",
|
|
71
|
+
choices: [
|
|
72
|
+
{
|
|
73
|
+
value: 'package',
|
|
74
|
+
description:
|
|
75
|
+
'Group imports by package, eg `use sui::{balance::Balance, coin::Coin}',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
value: 'module',
|
|
79
|
+
description:
|
|
80
|
+
'Group imports by module eg\n`use sui::balance::Balance;\nuse sui::coin::Coin`',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
wrapComments: {
|
|
85
|
+
type: 'boolean',
|
|
86
|
+
category: 'Global',
|
|
87
|
+
default: false,
|
|
88
|
+
description: 'Wrap comments to the next line if the line is too long.',
|
|
89
|
+
},
|
|
90
|
+
useModuleLabel: {
|
|
91
|
+
type: 'boolean',
|
|
92
|
+
category: 'Global',
|
|
93
|
+
default: true,
|
|
94
|
+
description:
|
|
95
|
+
'Enable module labels instead of module with braces. This option will be ignored if there is more than one module in the file.',
|
|
96
|
+
},
|
|
97
|
+
enableErrorDebug: {
|
|
98
|
+
type: 'boolean',
|
|
99
|
+
category: 'Global',
|
|
100
|
+
default: false,
|
|
101
|
+
description: 'Print ERROR nodes instead of throwing an error.',
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const defaultOptions = {
|
|
106
|
+
tabWidth: 4,
|
|
107
|
+
useTabs: false,
|
|
108
|
+
printWidth: 100,
|
|
109
|
+
useModuleLabel: false,
|
|
110
|
+
groupImports: 'module',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default {
|
|
114
|
+
languages,
|
|
115
|
+
parsers,
|
|
116
|
+
printers,
|
|
117
|
+
options,
|
|
118
|
+
defaultOptions,
|
|
119
|
+
} as Plugin;
|
package/src/printer.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright (c) The Move Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Implements the printing logic for the Move language. Takes a syntax tree and
|
|
6
|
+
* returns a formatted string.
|
|
7
|
+
*
|
|
8
|
+
* @see [Prettier Plugin API](https://prettier.io/docs/en/dev/plugins.html)
|
|
9
|
+
* @module printer
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { AstPath, Doc, ParserOptions } from 'prettier';
|
|
13
|
+
import { Node } from '.';
|
|
14
|
+
import Common from './cst/common';
|
|
15
|
+
import Formatting from './cst/formatting';
|
|
16
|
+
import Module from './cst/module';
|
|
17
|
+
import UseDeclaration from './cst/use_declaration';
|
|
18
|
+
import Constant from './cst/constant';
|
|
19
|
+
import StructDefinition from './cst/struct_definition';
|
|
20
|
+
import FunctionDefinition from './cst/function_definition';
|
|
21
|
+
import SourceFile from './cst/source_file';
|
|
22
|
+
import Expression from './cst/expression';
|
|
23
|
+
import Literal from './cst/literal';
|
|
24
|
+
import { printLeadingComment, printTrailingComment } from './utilities';
|
|
25
|
+
import EnumDefinition from './cst/enum_definition';
|
|
26
|
+
import Annotation from './cst/annotation';
|
|
27
|
+
|
|
28
|
+
export type MoveOptions = ParserOptions & {
|
|
29
|
+
wrapComments: boolean;
|
|
30
|
+
alwaysBreakConditionals: boolean;
|
|
31
|
+
alwaysBreakStructDefinition: boolean;
|
|
32
|
+
useModuleLabel: boolean;
|
|
33
|
+
enableErrorDebug: boolean;
|
|
34
|
+
autoGroupImports: 'package' | 'module';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type printFn = (path: AstPath) => Doc;
|
|
38
|
+
export type treeFn = (path: AstPath<Node>, options: MoveOptions, print: printFn) => Doc;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Print the AST node at the given path.
|
|
42
|
+
*/
|
|
43
|
+
export function print(path: AstPath<Node>, options: MoveOptions, print: printFn) {
|
|
44
|
+
// check if the node has an error child, if so, we throw an error or return the error text
|
|
45
|
+
const checkErrorsCb = (path: AstPath<Node>) => {
|
|
46
|
+
if (path.node.children.some((n) => n.type === 'ERROR')) {
|
|
47
|
+
if (options.enableErrorDebug) {
|
|
48
|
+
return ((path, options, print) => ['/* ERROR: */', path.node.text]) as treeFn;
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error('tree-sitter failure on \n```\n' + path.node.text + '\n```');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (path.node.children.some((n) => n.type === 'MISSING')) {
|
|
55
|
+
if (options.enableErrorDebug) {
|
|
56
|
+
return ((path, options, print) => ['/* MISSING: */', path.node.text]) as treeFn;
|
|
57
|
+
} else {
|
|
58
|
+
throw new Error('tree-sitter failure on \n```\n' + path.node.text + '\n```');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// for unimplemented / not yet implemented nodes, we just return the node type
|
|
66
|
+
const defautCb: treeFn = (path, options, print) => {
|
|
67
|
+
return path.node.type;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const fn =
|
|
71
|
+
checkErrorsCb(path) ||
|
|
72
|
+
SourceFile(path) ||
|
|
73
|
+
Annotation(path) ||
|
|
74
|
+
Formatting(path) ||
|
|
75
|
+
Common(path) ||
|
|
76
|
+
Module(path) ||
|
|
77
|
+
UseDeclaration(path) ||
|
|
78
|
+
Constant(path) ||
|
|
79
|
+
EnumDefinition(path) ||
|
|
80
|
+
StructDefinition(path) ||
|
|
81
|
+
FunctionDefinition(path) ||
|
|
82
|
+
Expression(path) ||
|
|
83
|
+
Literal(path) ||
|
|
84
|
+
defautCb;
|
|
85
|
+
|
|
86
|
+
return [
|
|
87
|
+
printLeadingComment(path, options),
|
|
88
|
+
// if the node has a `skipFormattingNode` property, we just return
|
|
89
|
+
// the text without formatting it
|
|
90
|
+
path.node.skipFormattingNode ? path.node.text : fn(path, options, print),
|
|
91
|
+
printTrailingComment(path),
|
|
92
|
+
];
|
|
93
|
+
}
|