Skip to content

Commit cc5b753

Browse files
CopilotGordonSmith
authored andcommitted
feat: add pretty print support
Signed-off-by: Gordon Smith <[email protected]>
1 parent b9042ce commit cc5b753

File tree

71 files changed

+4203
-3682
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+4203
-3682
lines changed

.gitattributes

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1-
# Set default behavior to automatically normalize line endings.
1+
*.wit text eol=lf
2+
*.snap text eol=lf
3+
*.ts text eol=lf
4+
*.json text eol=lf
5+
*.mjs text eol=lf
6+
Cargo.toml text eol=lf
7+
README.md text eol=lf
8+
LICENSE text eol=lf# Set default behavior to automatically normalize line endings.
29
* text=auto
310

package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@
181181
"command": "wit-idl.extractCoreWasm",
182182
"title": "Extract Core Wasm",
183183
"category": "WIT"
184+
},
185+
{
186+
"command": "wit-idl.formatDocument",
187+
"title": "Format Document",
188+
"category": "WIT"
184189
}
185190
],
186191
"submenus": [
@@ -196,6 +201,11 @@
196201
"when": "resourceExtname == .wit",
197202
"group": "navigation"
198203
},
204+
{
205+
"command": "wit-idl.formatDocument",
206+
"when": "resourceExtname == .wit",
207+
"group": "1_modification@10"
208+
},
199209
{
200210
"submenu": "wit-idl.generateBindings.submenu",
201211
"when": "resourceExtname == .wit || witIdl.isWasmComponent",
@@ -289,6 +299,10 @@
289299
{
290300
"command": "wit-idl.extractCoreWasm",
291301
"when": "witIdl.isWasmComponent"
302+
},
303+
{
304+
"command": "wit-idl.formatDocument",
305+
"when": "editorLangId == wit"
292306
}
293307
]
294308
},
@@ -301,6 +315,11 @@
301315
{
302316
"command": "wit-idl.syntaxCheckWorkspace",
303317
"key": "shift+f7"
318+
},
319+
{
320+
"command": "wit-idl.formatDocument",
321+
"key": "shift+alt+f",
322+
"when": "editorTextFocus && editorLangId == wit"
304323
}
305324
],
306325
"customEditors": [

scripts/format-test-wit-files.js

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/usr/bin/env node
2+
import fs from "node:fs";
3+
import path from "node:path";
4+
5+
function replaceAllSeq(line, replacements) {
6+
let out = line;
7+
for (const [re, repl] of replacements) out = out.replace(re, repl);
8+
return out;
9+
}
10+
function ensureSemicolon(line) {
11+
return line.replace(/\s*;\s*$/, ";");
12+
}
13+
function collapseSpaces(line) {
14+
return line.replace(/\s+/g, " ");
15+
}
16+
17+
function isOpeningBrace(line) {
18+
return line.endsWith("{") && !line.includes("}");
19+
}
20+
21+
function formatFunc(line) {
22+
const replacements = [
23+
[/:func/, ": func"],
24+
[/:\s*func/, ": func"],
25+
[/func\s*\(/, "func("],
26+
[/\)\s*->\s*/, ") -> "],
27+
[/\)->\s*/, ") -> "],
28+
[/\)->/, ") -> "],
29+
[/,\s*/g, ", "],
30+
[/:\s*/g, ": "],
31+
];
32+
return ensureSemicolon(replaceAllSeq(line, replacements));
33+
}
34+
function formatPackage(line) {
35+
return ensureSemicolon(collapseSpaces(line));
36+
}
37+
function formatNamedBlock(line) {
38+
return collapseSpaces(line).replace(/\s*{\s*$/, " {");
39+
}
40+
function formatImportExport(line) {
41+
const base = line.replace(/^(import|export)\s+/, "$1 ");
42+
return base.includes(": func") || base.includes(":func") ? formatFunc(base) : ensureSemicolon(base);
43+
}
44+
function formatUse(line) {
45+
const repl = [
46+
[/^use\s+/, "use "],
47+
[/\s+as\s+/, " as "],
48+
[/\s+from\s+/, " from "],
49+
];
50+
return ensureSemicolon(replaceAllSeq(line, repl));
51+
}
52+
function formatTypeAlias(line) {
53+
const repl = [
54+
[/^type\s+/, "type "],
55+
[/\s*=\s*/, " = "],
56+
];
57+
return ensureSemicolon(replaceAllSeq(line, repl));
58+
}
59+
function formatField(line) {
60+
return replaceAllSeq(line, [
61+
[/::?/g, (m) => (m.endsWith(":") ? ": " : m)],
62+
[/:\s*/g, ": "],
63+
[/,\s*/g, ", "],
64+
[/,\s*$/, ","],
65+
]);
66+
}
67+
function isTypeDecl(line) {
68+
return /^(record|variant|enum|flags|resource)\s+/.test(line);
69+
}
70+
function isFuncDecl(line) {
71+
if (line.startsWith("import ") || line.startsWith("export ")) return false;
72+
return /^[a-zA-Z][\w-]*\s*:\s*func\b/.test(line) || /:\s*func\b/.test(line) || /->/.test(line);
73+
}
74+
function isFieldDecl(line) {
75+
const t = line.trim();
76+
return /^[a-zA-Z][a-zA-Z0-9-]*\s*[:,(]/.test(t) || /^[a-zA-Z][a-zA-Z0-9-]*\s*,?\s*$/.test(t);
77+
}
78+
79+
function formatLine(line) {
80+
if (line.startsWith("package ")) return formatPackage(line);
81+
if (line.startsWith("interface ")) return formatNamedBlock(line);
82+
if (line.startsWith("world ")) return formatNamedBlock(line);
83+
if (isTypeDecl(line)) return formatNamedBlock(line);
84+
if (line.startsWith("type ") && line.includes("=")) return formatTypeAlias(line);
85+
if (isFuncDecl(line)) return formatFunc(line);
86+
if (line.startsWith("import ") || line.startsWith("export ")) return formatImportExport(line);
87+
if (line.startsWith("use ")) return formatUse(line);
88+
if (isFieldDecl(line)) return formatField(line);
89+
return line;
90+
}
91+
92+
function formatContent(content, tabSize = 2, insertSpaces = true) {
93+
const indentUnit = insertSpaces ? " ".repeat(tabSize) : "\t";
94+
const lines = content.split(/\r?\n/);
95+
const out = [];
96+
let indentLevel = 0;
97+
let inMultiLineTupleAlias = false;
98+
let aliasGenericDepth = 0;
99+
let inFuncParams = false;
100+
for (let i = 0; i < lines.length; i++) {
101+
const raw = lines[i];
102+
const trimmed = raw.trim();
103+
if (trimmed === "") {
104+
out.push("");
105+
continue;
106+
}
107+
if (/^\}/.test(trimmed)) indentLevel = Math.max(0, indentLevel - 1);
108+
if (/^\/\//.test(trimmed)) {
109+
const extra = inMultiLineTupleAlias && aliasGenericDepth > 0 && !/^>>/.test(trimmed) ? 1 : 0;
110+
out.push(indentUnit.repeat(indentLevel + extra) + trimmed);
111+
continue;
112+
}
113+
const formattedLine = formatLine(trimmed);
114+
const needsTupleExtra = inMultiLineTupleAlias && aliasGenericDepth > 0 && !/^>>/.test(trimmed);
115+
const needsFuncParamExtra = inFuncParams && !/^\)/.test(trimmed);
116+
out.push(
117+
indentUnit.repeat(indentLevel + (needsTupleExtra ? 1 : 0) + (needsFuncParamExtra ? 1 : 0)) + formattedLine
118+
);
119+
if (isOpeningBrace(trimmed)) indentLevel++;
120+
if (!inFuncParams && /func\($/.test(trimmed)) {
121+
let lookahead = i + 1;
122+
let activate = true;
123+
while (lookahead < lines.length) {
124+
const laTrim = lines[lookahead].trim();
125+
if (laTrim === "") {
126+
lookahead++;
127+
continue;
128+
}
129+
if (/^\/\//.test(laTrim)) {
130+
activate = false;
131+
}
132+
break;
133+
}
134+
if (activate) inFuncParams = true;
135+
} else if (inFuncParams && /^\)/.test(trimmed)) {
136+
inFuncParams = false;
137+
}
138+
if (!inMultiLineTupleAlias && /^type\s+[^=]+=.*tuple<\s*$/.test(trimmed)) {
139+
inMultiLineTupleAlias = true;
140+
aliasGenericDepth = (trimmed.match(/</g) || []).length - (trimmed.match(/>/g) || []).length;
141+
} else if (inMultiLineTupleAlias) {
142+
const opens = (trimmed.match(/</g) || []).length;
143+
const closes = (trimmed.match(/>/g) || []).length;
144+
aliasGenericDepth += opens - closes;
145+
if (aliasGenericDepth <= 0) inMultiLineTupleAlias = false;
146+
}
147+
}
148+
return out.join("\n");
149+
}
150+
151+
function walk(dir, acc) {
152+
for (const entry of fs.readdirSync(dir)) {
153+
const full = path.join(dir, entry);
154+
const stat = fs.statSync(full);
155+
if (stat.isDirectory()) walk(full, acc);
156+
else if (entry.endsWith(".wit")) acc.push(full);
157+
}
158+
return acc;
159+
}
160+
161+
const root = path.resolve("tests");
162+
if (!fs.existsSync(root)) {
163+
console.error("tests directory not found");
164+
process.exit(1);
165+
}
166+
const files = walk(root, []);
167+
let changed = 0;
168+
for (const f of files) {
169+
const original = fs.readFileSync(f, "utf8");
170+
const formatted = formatContent(original);
171+
if (formatted !== original) {
172+
fs.writeFileSync(f, formatted, "utf8");
173+
changed++;
174+
console.log("Formatted", path.relative(process.cwd(), f));
175+
}
176+
}
177+
console.log(`Processed ${files.length} .wit files. Changed ${changed}.`);

0 commit comments

Comments
 (0)