vba-runner 0.1.0-alpha.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.
@@ -0,0 +1,1012 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // ../../test-libs/vba-formatter.ts
26
+ var fs = __toESM(require("fs"), 1);
27
+ var path = __toESM(require("path"), 1);
28
+
29
+ // ../../src/engine/lexer.ts
30
+ var LexError = class extends Error {
31
+ constructor(message, line, column) {
32
+ super(`${message} (line ${line})`);
33
+ this.line = line;
34
+ this.column = column;
35
+ this.name = "LexError";
36
+ }
37
+ };
38
+ var Lexer = class _Lexer {
39
+ input = "";
40
+ pos = 0;
41
+ line = 1;
42
+ column = 1;
43
+ diagnostics = [];
44
+ // MS-VBAL §3.3.5: name-start-character = Unicode-Letter (Lu,Ll,Lt,Lm,Lo,Nl) | "_"
45
+ static reUnicodeNameStart = /^[\p{L}\p{Nl}]$/u;
46
+ // name-continue-character adds Unicode-Combining-Character (Mn,Mc), Digit (Nd), Connector-Punct (Pc)
47
+ static reUnicodeNameContinue = /^[\p{Mn}\p{Mc}\p{Nd}\p{Pc}]$/u;
48
+ constructor(input) {
49
+ this.input = input;
50
+ }
51
+ peek() {
52
+ if (this.pos >= this.input.length) return "\0";
53
+ return this.input[this.pos];
54
+ }
55
+ advance() {
56
+ if (this.pos >= this.input.length) return "\0";
57
+ const char = this.input[this.pos++];
58
+ if (char === "\n") {
59
+ this.line++;
60
+ this.column = 1;
61
+ } else {
62
+ this.column++;
63
+ }
64
+ return char;
65
+ }
66
+ isWhitespace(char) {
67
+ return char === " " || char === " " || char === "\r";
68
+ }
69
+ isAlpha(char) {
70
+ if (char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_") return true;
71
+ const code = char.codePointAt(0) ?? 0;
72
+ return code > 127 && _Lexer.reUnicodeNameStart.test(char);
73
+ }
74
+ isAlphaNumeric(char) {
75
+ if (char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_") return true;
76
+ if (char >= "0" && char <= "9") return true;
77
+ const code = char.codePointAt(0) ?? 0;
78
+ return code > 127 && (_Lexer.reUnicodeNameStart.test(char) || _Lexer.reUnicodeNameContinue.test(char));
79
+ }
80
+ isDigit(char) {
81
+ return char >= "0" && char <= "9";
82
+ }
83
+ skipWhitespace() {
84
+ while (this.isWhitespace(this.peek())) {
85
+ this.advance();
86
+ }
87
+ }
88
+ getNextToken() {
89
+ while (true) {
90
+ this.skipWhitespace();
91
+ const startLine = this.line;
92
+ const startColumn = this.column;
93
+ if (this.pos >= this.input.length) {
94
+ return { type: 136 /* EOF */, value: "", line: startLine, column: startColumn };
95
+ }
96
+ const char = this.peek();
97
+ if (char === "#") {
98
+ let foundClosingHash = false;
99
+ for (let j = this.pos + 1; j < this.input.length; j++) {
100
+ if (this.input[j] === "#") {
101
+ foundClosingHash = true;
102
+ break;
103
+ }
104
+ if (this.input[j] === "\n") break;
105
+ }
106
+ if (foundClosingHash) {
107
+ let potentialDate = "";
108
+ let k = this.pos + 1;
109
+ while (k < this.input.length && this.input[k] !== "#" && this.input[k] !== "\n") {
110
+ potentialDate += this.input[k];
111
+ k++;
112
+ }
113
+ const dateRegex = /^[0-9\/\-\s:apm,]+$/i;
114
+ const monthsRegex = /jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/i;
115
+ if (dateRegex.test(potentialDate) || monthsRegex.test(potentialDate)) {
116
+ this.advance();
117
+ let dateValue = "";
118
+ while (this.peek() !== "#" && this.peek() !== "\n" && this.peek() !== "\0") {
119
+ dateValue += this.advance();
120
+ }
121
+ if (this.peek() === "#") {
122
+ this.advance();
123
+ return { type: 134 /* Date */, value: dateValue, line: startLine, column: startColumn };
124
+ }
125
+ }
126
+ }
127
+ this.advance();
128
+ return { type: 111 /* OperatorHash */, value: "#", line: startLine, column: startColumn };
129
+ }
130
+ if (char === "_") {
131
+ const next = this.pos + 1 < this.input.length ? this.input[this.pos + 1] : "\0";
132
+ const afterCr = next === "\r" && this.pos + 2 < this.input.length ? this.input[this.pos + 2] : "\0";
133
+ if (next === "\n" || next === "\r" && afterCr === "\n") {
134
+ this.advance();
135
+ if (this.peek() === "\r") this.advance();
136
+ if (this.peek() === "\n") this.advance();
137
+ continue;
138
+ }
139
+ let lookahead = this.pos + 1;
140
+ while (lookahead < this.input.length && this.input[lookahead] === " ") lookahead++;
141
+ const afterSpaces = lookahead < this.input.length ? this.input[lookahead] : "\0";
142
+ if (afterSpaces === "\n" || afterSpaces === "\r" || afterSpaces === "'") {
143
+ const msg = afterSpaces === "'" ? "\u884C\u7D99\u7D9A\u6587\u5B57 '_' \u306E\u5F8C\u306B\u30B3\u30E1\u30F3\u30C8\u306F\u8A18\u8FF0\u3067\u304D\u307E\u305B\u3093" : "\u884C\u7D99\u7D9A\u6587\u5B57 '_' \u306E\u76F4\u5F8C\u306B\u7A7A\u767D\u3092\u7F6E\u304F\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093";
144
+ this.advance();
145
+ while (this.peek() !== "\n" && this.peek() !== "\0") this.advance();
146
+ if (this.peek() === "\n") this.advance();
147
+ throw new LexError(msg, startLine, startColumn);
148
+ }
149
+ }
150
+ if (char === "'") {
151
+ let commentText = "";
152
+ while (this.peek() !== "\n" && this.peek() !== "\0") {
153
+ commentText += this.advance();
154
+ }
155
+ if (/[ \t]*_[ \t]*$/.test(commentText)) {
156
+ this.diagnostics.push({
157
+ message: "\u30B3\u30E1\u30F3\u30C8\u672B\u5C3E\u306E '_' \u306F\u884C\u7D99\u7D9A\u3068\u3057\u3066\u6A5F\u80FD\u3057\u307E\u305B\u3093",
158
+ line: startLine,
159
+ column: startColumn + 1 + commentText.lastIndexOf("_")
160
+ });
161
+ }
162
+ continue;
163
+ }
164
+ if (char === "\n") {
165
+ this.advance();
166
+ return { type: 135 /* Newline */, value: "\n", line: startLine, column: startColumn };
167
+ }
168
+ if (char === '"') {
169
+ this.advance();
170
+ let strValue = "";
171
+ while (this.peek() !== "\0") {
172
+ if (this.peek() === '"') {
173
+ this.advance();
174
+ if (this.peek() === '"') {
175
+ strValue += '"';
176
+ this.advance();
177
+ } else {
178
+ return { type: 2 /* String */, value: strValue, line: startLine, column: startColumn };
179
+ }
180
+ } else {
181
+ strValue += this.advance();
182
+ }
183
+ }
184
+ return { type: 2 /* String */, value: strValue, line: startLine, column: startColumn };
185
+ }
186
+ if (char === "=") {
187
+ this.advance();
188
+ return { type: 120 /* OperatorEquals */, value: "=", line: startLine, column: startColumn };
189
+ }
190
+ if (char === "<") {
191
+ this.advance();
192
+ if (this.peek() === ">") {
193
+ this.advance();
194
+ return { type: 121 /* OperatorNotEquals */, value: "<>", line: startLine, column: startColumn };
195
+ }
196
+ if (this.peek() === "=") {
197
+ this.advance();
198
+ return { type: 124 /* OperatorLessThanOrEqual */, value: "<=", line: startLine, column: startColumn };
199
+ }
200
+ return { type: 122 /* OperatorLessThan */, value: "<", line: startLine, column: startColumn };
201
+ }
202
+ if (char === ">") {
203
+ this.advance();
204
+ if (this.peek() === "=") {
205
+ this.advance();
206
+ return { type: 125 /* OperatorGreaterThanOrEqual */, value: ">=", line: startLine, column: startColumn };
207
+ }
208
+ return { type: 123 /* OperatorGreaterThan */, value: ">", line: startLine, column: startColumn };
209
+ }
210
+ if (char === "+") {
211
+ this.advance();
212
+ return { type: 112 /* OperatorPlus */, value: "+", line: startLine, column: startColumn };
213
+ }
214
+ if (char === "-") {
215
+ this.advance();
216
+ return { type: 113 /* OperatorMinus */, value: "-", line: startLine, column: startColumn };
217
+ }
218
+ if (char === "!") {
219
+ this.advance();
220
+ return { type: 132 /* OperatorExclamation */, value: "!", line: startLine, column: startColumn };
221
+ }
222
+ if (char === "&") {
223
+ this.advance();
224
+ const next = this.peek().toLowerCase();
225
+ if (next === "h") {
226
+ this.advance();
227
+ let hexStr = "";
228
+ while (/[0-9a-f]/i.test(this.peek())) {
229
+ hexStr += this.advance();
230
+ }
231
+ return { type: 1 /* Number */, value: "0x" + hexStr, line: startLine, column: startColumn };
232
+ } else if (next === "o" || this.isDigit(next)) {
233
+ if (next === "o") this.advance();
234
+ let octStr = "";
235
+ while (/[0-7]/.test(this.peek())) {
236
+ octStr += this.advance();
237
+ }
238
+ return { type: 1 /* Number */, value: "0o" + octStr, line: startLine, column: startColumn };
239
+ }
240
+ return { type: 117 /* OperatorAmpersand */, value: "&", line: startLine, column: startColumn };
241
+ }
242
+ if (char === ",") {
243
+ this.advance();
244
+ return { type: 126 /* OperatorComma */, value: ",", line: startLine, column: startColumn };
245
+ }
246
+ if (char === "#") {
247
+ this.advance();
248
+ return { type: 111 /* OperatorHash */, value: "#", line: startLine, column: startColumn };
249
+ }
250
+ if (char === "(") {
251
+ this.advance();
252
+ return { type: 127 /* OperatorLParen */, value: "(", line: startLine, column: startColumn };
253
+ }
254
+ if (char === ")") {
255
+ this.advance();
256
+ return { type: 128 /* OperatorRParen */, value: ")", line: startLine, column: startColumn };
257
+ }
258
+ if (char === "[") {
259
+ this.advance();
260
+ let foreignStr = "";
261
+ while (this.peek() !== "]" && this.peek() !== "\n" && this.peek() !== "\r" && this.peek() !== "\0") {
262
+ foreignStr += this.advance();
263
+ }
264
+ if (this.peek() === "]") this.advance();
265
+ return { type: 138 /* ForeignName */, value: foreignStr, line: startLine, column: startColumn };
266
+ }
267
+ if (char === ".") {
268
+ this.advance();
269
+ return { type: 129 /* OperatorDot */, value: ".", line: startLine, column: startColumn };
270
+ }
271
+ if (char === ":") {
272
+ this.advance();
273
+ if (this.peek() === "=") {
274
+ this.advance();
275
+ return { type: 131 /* OperatorColonEquals */, value: ":=", line: startLine, column: startColumn };
276
+ }
277
+ return { type: 130 /* OperatorColon */, value: ":", line: startLine, column: startColumn };
278
+ }
279
+ if (char === ";") {
280
+ this.advance();
281
+ return { type: 133 /* OperatorSemicolon */, value: ";", line: startLine, column: startColumn };
282
+ }
283
+ if (this.isDigit(char)) {
284
+ let numStr = "";
285
+ while (this.isDigit(this.peek())) {
286
+ numStr += this.advance();
287
+ }
288
+ if (this.peek() === ".") {
289
+ numStr += this.advance();
290
+ while (this.isDigit(this.peek())) {
291
+ numStr += this.advance();
292
+ }
293
+ }
294
+ const nextChar = this.peek().toLowerCase();
295
+ if (nextChar === "e") {
296
+ numStr += this.advance();
297
+ if (this.peek() === "+" || this.peek() === "-") {
298
+ numStr += this.advance();
299
+ }
300
+ while (this.isDigit(this.peek())) {
301
+ numStr += this.advance();
302
+ }
303
+ }
304
+ const peekChar = this.peek();
305
+ if (["%", "&", "@", "!", "#", "^"].indexOf(peekChar) !== -1) {
306
+ numStr += this.advance();
307
+ }
308
+ return { type: 1 /* Number */, value: numStr, line: startLine, column: startColumn };
309
+ }
310
+ if (this.isAlpha(char)) {
311
+ let idStr = "";
312
+ while (this.isAlphaNumeric(this.peek())) {
313
+ idStr += this.advance();
314
+ }
315
+ const typeHints = ["$", "%", "&", "#", "@"];
316
+ if (typeHints.includes(this.peek())) {
317
+ idStr += this.advance();
318
+ }
319
+ const lowerId = idStr.toLowerCase();
320
+ const lowerBase = lowerId.replace(/[$%&#@]$/, "");
321
+ if (lowerBase === "rem") {
322
+ while (this.peek() !== "\n" && this.peek() !== "\0") {
323
+ this.advance();
324
+ }
325
+ continue;
326
+ }
327
+ if (lowerBase === "for") return { type: 3 /* KeywordFor */, value: idStr, line: startLine, column: startColumn };
328
+ if (lowerBase === "to") return { type: 4 /* KeywordTo */, value: idStr, line: startLine, column: startColumn };
329
+ if (lowerBase === "next") return { type: 5 /* KeywordNext */, value: idStr, line: startLine, column: startColumn };
330
+ if (lowerBase === "if") return { type: 6 /* KeywordIf */, value: idStr, line: startLine, column: startColumn };
331
+ if (lowerBase === "then") return { type: 7 /* KeywordThen */, value: idStr, line: startLine, column: startColumn };
332
+ if (lowerBase === "elseif") return { type: 8 /* KeywordElseIf */, value: idStr, line: startLine, column: startColumn };
333
+ if (lowerBase === "else") return { type: 9 /* KeywordElse */, value: idStr, line: startLine, column: startColumn };
334
+ if (lowerBase === "end") return { type: 10 /* KeywordEnd */, value: idStr, line: startLine, column: startColumn };
335
+ if (lowerBase === "do") return { type: 11 /* KeywordDo */, value: idStr, line: startLine, column: startColumn };
336
+ if (lowerBase === "while") return { type: 12 /* KeywordWhile */, value: idStr, line: startLine, column: startColumn };
337
+ if (lowerBase === "wend") return { type: 13 /* KeywordWend */, value: idStr, line: startLine, column: startColumn };
338
+ if (lowerBase === "loop") return { type: 14 /* KeywordLoop */, value: idStr, line: startLine, column: startColumn };
339
+ if (lowerBase === "until") return { type: 15 /* KeywordUntil */, value: idStr, line: startLine, column: startColumn };
340
+ if (lowerBase === "gosub") return { type: 37 /* KeywordGoSub */, value: idStr, line: startLine, column: startColumn };
341
+ if (lowerBase === "return") return { type: 38 /* KeywordReturn */, value: idStr, line: startLine, column: startColumn };
342
+ if (lowerBase === "lset") return { type: 39 /* KeywordLSet */, value: idStr, line: startLine, column: startColumn };
343
+ if (lowerBase === "rset") return { type: 40 /* KeywordRSet */, value: idStr, line: startLine, column: startColumn };
344
+ if (lowerBase === "stop") return { type: 16 /* KeywordStop */, value: idStr, line: startLine, column: startColumn };
345
+ if (lowerBase === "sub") return { type: 17 /* KeywordSub */, value: idStr, line: startLine, column: startColumn };
346
+ if (lowerBase === "function") return { type: 18 /* KeywordFunction */, value: idStr, line: startLine, column: startColumn };
347
+ if (lowerBase === "property") return { type: 19 /* KeywordProperty */, value: idStr, line: startLine, column: startColumn };
348
+ if (lowerBase === "get") return { type: 20 /* KeywordGet */, value: idStr, line: startLine, column: startColumn };
349
+ if (lowerBase === "let") return { type: 21 /* KeywordLet */, value: idStr, line: startLine, column: startColumn };
350
+ if (lowerBase === "dim") return { type: 22 /* KeywordDim */, value: idStr, line: startLine, column: startColumn };
351
+ if (lowerBase === "as") return { type: 23 /* KeywordAs */, value: idStr, line: startLine, column: startColumn };
352
+ if (lowerBase === "new") return { type: 24 /* KeywordNew */, value: idStr, line: startLine, column: startColumn };
353
+ if (lowerBase === "collection") return { type: 25 /* KeywordCollection */, value: idStr, line: startLine, column: startColumn };
354
+ if (lowerBase === "like") return { type: 63 /* KeywordLike */, value: idStr, line: startLine, column: startColumn };
355
+ if (lowerBase === "and") return { type: 26 /* KeywordAnd */, value: idStr, line: startLine, column: startColumn };
356
+ if (lowerBase === "or") return { type: 27 /* KeywordOr */, value: idStr, line: startLine, column: startColumn };
357
+ if (lowerBase === "xor") return { type: 64 /* KeywordXor */, value: idStr, line: startLine, column: startColumn };
358
+ if (lowerBase === "eqv") return { type: 65 /* KeywordEqv */, value: idStr, line: startLine, column: startColumn };
359
+ if (lowerBase === "imp") return { type: 66 /* KeywordImp */, value: idStr, line: startLine, column: startColumn };
360
+ if (lowerBase === "not") return { type: 28 /* KeywordNot */, value: idStr, line: startLine, column: startColumn };
361
+ if (lowerBase === "option") return { type: 29 /* KeywordOption */, value: idStr, line: startLine, column: startColumn };
362
+ if (lowerBase === "explicit") return { type: 30 /* KeywordExplicit */, value: idStr, line: startLine, column: startColumn };
363
+ if (lowerBase === "const") return { type: 31 /* KeywordConst */, value: idStr, line: startLine, column: startColumn };
364
+ if (lowerBase === "set") return { type: 32 /* KeywordSet */, value: idStr, line: startLine, column: startColumn };
365
+ if (lowerBase === "call") return { type: 33 /* KeywordCall */, value: idStr, line: startLine, column: startColumn };
366
+ if (lowerBase === "on") return { type: 34 /* KeywordOn */, value: idStr, line: startLine, column: startColumn };
367
+ if (lowerBase === "error") return { type: 35 /* KeywordError */, value: idStr, line: startLine, column: startColumn };
368
+ if (lowerBase === "goto") return { type: 36 /* KeywordGoTo */, value: idStr, line: startLine, column: startColumn };
369
+ if (lowerBase === "erase") return { type: 41 /* KeywordErase */, value: idStr, line: startLine, column: startColumn };
370
+ if (lowerBase === "redim") return { type: 42 /* KeywordReDim */, value: idStr, line: startLine, column: startColumn };
371
+ if (lowerBase === "step") return { type: 43 /* KeywordStep */, value: idStr, line: startLine, column: startColumn };
372
+ if (lowerBase === "empty") return { type: 44 /* KeywordEmpty */, value: idStr, line: startLine, column: startColumn };
373
+ if (lowerBase === "exit") return { type: 45 /* KeywordExit */, value: idStr, line: startLine, column: startColumn };
374
+ if (lowerBase === "byref") return { type: 46 /* KeywordByRef */, value: idStr, line: startLine, column: startColumn };
375
+ if (lowerBase === "byval") return { type: 47 /* KeywordByVal */, value: idStr, line: startLine, column: startColumn };
376
+ if (lowerBase === "mod") return { type: 118 /* KeywordMod */, value: idStr, line: startLine, column: startColumn };
377
+ if (lowerBase === "type") return { type: 48 /* KeywordType */, value: idStr, line: startLine, column: startColumn };
378
+ if (lowerBase === "nothing") return { type: 49 /* KeywordNothing */, value: idStr, line: startLine, column: startColumn };
379
+ if (lowerBase === "optional") return { type: 50 /* KeywordOptional */, value: idStr, line: startLine, column: startColumn };
380
+ if (lowerBase === "is") return { type: 51 /* KeywordIs */, value: idStr, line: startLine, column: startColumn };
381
+ if (lowerBase === "resume") return { type: 52 /* KeywordResume */, value: idStr, line: startLine, column: startColumn };
382
+ if (lowerBase === "select") return { type: 53 /* KeywordSelect */, value: idStr, line: startLine, column: startColumn };
383
+ if (lowerBase === "case") return { type: 54 /* KeywordCase */, value: idStr, line: startLine, column: startColumn };
384
+ if (lowerBase === "each") return { type: 55 /* KeywordEach */, value: idStr, line: startLine, column: startColumn };
385
+ if (lowerBase === "in") return { type: 56 /* KeywordIn */, value: idStr, line: startLine, column: startColumn };
386
+ if (lowerBase === "public") return { type: 57 /* KeywordPublic */, value: idStr, line: startLine, column: startColumn };
387
+ if (lowerBase === "private") return { type: 58 /* KeywordPrivate */, value: idStr, line: startLine, column: startColumn };
388
+ if (lowerBase === "enum") return { type: 59 /* KeywordEnum */, value: idStr, line: startLine, column: startColumn };
389
+ if (lowerBase === "typeof") return { type: 62 /* KeywordTypeOf */, value: idStr, line: startLine, column: startColumn };
390
+ if (lowerBase === "friend") return { type: 60 /* KeywordFriend */, value: idStr, line: startLine, column: startColumn };
391
+ if (lowerBase === "with") return { type: 61 /* KeywordWith */, value: idStr, line: startLine, column: startColumn };
392
+ if (lowerBase === "null") return { type: 67 /* KeywordNull */, value: idStr, line: startLine, column: startColumn };
393
+ if (lowerBase === "static") return { type: 68 /* KeywordStatic */, value: idStr, line: startLine, column: startColumn };
394
+ if (lowerBase === "class") return { type: 69 /* KeywordClass */, value: idStr, line: startLine, column: startColumn };
395
+ if (lowerBase === "me") return { type: 70 /* KeywordMe */, value: idStr, line: startLine, column: startColumn };
396
+ if (lowerBase === "compare") return { type: 71 /* KeywordCompare */, value: idStr, line: startLine, column: startColumn };
397
+ if (lowerBase === "binary") return { type: 72 /* KeywordBinary */, value: idStr, line: startLine, column: startColumn };
398
+ if (lowerBase === "text") return { type: 73 /* KeywordText */, value: idStr, line: startLine, column: startColumn };
399
+ if (lowerBase === "attribute") return { type: 74 /* KeywordAttribute */, value: idStr, line: startLine, column: startColumn };
400
+ if (lowerBase === "declare") return { type: 75 /* KeywordDeclare */, value: idStr, line: startLine, column: startColumn };
401
+ if (lowerBase === "lib") return { type: 76 /* KeywordLib */, value: idStr, line: startLine, column: startColumn };
402
+ if (lowerBase === "alias") return { type: 77 /* KeywordAlias */, value: idStr, line: startLine, column: startColumn };
403
+ if (lowerBase === "ptrsafe") return { type: 78 /* KeywordPtrSafe */, value: idStr, line: startLine, column: startColumn };
404
+ if (lowerBase === "open") return { type: 81 /* KeywordOpen */, value: idStr, line: startLine, column: startColumn };
405
+ if (lowerBase === "close") return { type: 82 /* KeywordClose */, value: idStr, line: startLine, column: startColumn };
406
+ if (lowerBase === "line") return { type: 83 /* KeywordLine */, value: idStr, line: startLine, column: startColumn };
407
+ if (lowerBase === "input") return { type: 84 /* KeywordInput */, value: idStr, line: startLine, column: startColumn };
408
+ if (lowerBase === "print") return { type: 85 /* KeywordPrint */, value: idStr, line: startLine, column: startColumn };
409
+ if (lowerBase === "put") return { type: 86 /* KeywordPut */, value: idStr, line: startLine, column: startColumn };
410
+ if (lowerBase === "output") return { type: 87 /* KeywordOutput */, value: idStr, line: startLine, column: startColumn };
411
+ if (lowerBase === "append") return { type: 88 /* KeywordAppend */, value: idStr, line: startLine, column: startColumn };
412
+ if (lowerBase === "random") return { type: 89 /* KeywordRandom */, value: idStr, line: startLine, column: startColumn };
413
+ if (lowerBase === "access") return { type: 90 /* KeywordAccess */, value: idStr, line: startLine, column: startColumn };
414
+ if (lowerBase === "read") return { type: 91 /* KeywordRead */, value: idStr, line: startLine, column: startColumn };
415
+ if (lowerBase === "write") return { type: 92 /* KeywordWrite */, value: idStr, line: startLine, column: startColumn };
416
+ if (lowerBase === "lock") return { type: 93 /* KeywordLock */, value: idStr, line: startLine, column: startColumn };
417
+ if (lowerBase === "shared") return { type: 94 /* KeywordShared */, value: idStr, line: startLine, column: startColumn };
418
+ if (lowerBase === "spc") return { type: 95 /* KeywordSpc */, value: idStr, line: startLine, column: startColumn };
419
+ if (lowerBase === "tab") return { type: 96 /* KeywordTab */, value: idStr, line: startLine, column: startColumn };
420
+ if (lowerBase === "kill") return { type: 97 /* KeywordKill */, value: idStr, line: startLine, column: startColumn };
421
+ if (lowerBase === "get") return { type: 20 /* KeywordGet */, value: idStr, line: startLine, column: startColumn };
422
+ if (lowerBase === "seek") return { type: 98 /* KeywordSeek */, value: idStr, line: startLine, column: startColumn };
423
+ if (lowerBase === "reset") return { type: 99 /* KeywordReset */, value: idStr, line: startLine, column: startColumn };
424
+ if (lowerBase === "unlock") return { type: 100 /* KeywordUnlock */, value: idStr, line: startLine, column: startColumn };
425
+ if (lowerBase === "paramarray") return { type: 101 /* KeywordParamArray */, value: idStr, line: startLine, column: startColumn };
426
+ if (lowerBase === "module") return { type: 80 /* KeywordModule */, value: idStr, line: startLine, column: startColumn };
427
+ if (lowerBase === "event") return { type: 102 /* KeywordEvent */, value: idStr, line: startLine, column: startColumn };
428
+ if (lowerBase === "raiseevent") return { type: 103 /* KeywordRaiseEvent */, value: idStr, line: startLine, column: startColumn };
429
+ if (lowerBase === "withevents") return { type: 104 /* KeywordWithEvents */, value: idStr, line: startLine, column: startColumn };
430
+ if (lowerBase === "implements") return { type: 105 /* KeywordImplements */, value: idStr, line: startLine, column: startColumn };
431
+ if (lowerBase === "appactivate") return { type: 106 /* KeywordAppActivate */, value: idStr, line: startLine, column: startColumn };
432
+ if (lowerBase === "sendkeys") return { type: 107 /* KeywordSendKeys */, value: idStr, line: startLine, column: startColumn };
433
+ if (lowerBase === "mid") return { type: 108 /* KeywordMid */, value: idStr, line: startLine, column: startColumn };
434
+ if (lowerBase === "midb") return { type: 108 /* KeywordMid */, value: idStr, line: startLine, column: startColumn };
435
+ if (lowerBase === "width") return { type: 109 /* KeywordWidth */, value: idStr, line: startLine, column: startColumn };
436
+ if (lowerBase === "addressof") return { type: 110 /* KeywordAddressOf */, value: idStr, line: startLine, column: startColumn };
437
+ return { type: 0 /* Identifier */, value: idStr, line: startLine, column: startColumn };
438
+ }
439
+ if (char === "*") {
440
+ this.advance();
441
+ return { type: 114 /* OperatorMultiply */, value: "*", line: startLine, column: startColumn };
442
+ }
443
+ if (char === "/") {
444
+ this.advance();
445
+ return { type: 115 /* OperatorDivide */, value: "/", line: startLine, column: startColumn };
446
+ }
447
+ if (char === "\\") {
448
+ this.advance();
449
+ return { type: 116 /* OperatorIntDivide */, value: "\\", line: startLine, column: startColumn };
450
+ }
451
+ if (char === "^") {
452
+ this.advance();
453
+ return { type: 119 /* OperatorPower */, value: "^", line: startLine, column: startColumn };
454
+ }
455
+ const unknownChar = this.advance();
456
+ return { type: 137 /* Unknown */, value: unknownChar, line: startLine, column: startColumn };
457
+ }
458
+ }
459
+ tokenize() {
460
+ const tokens = [];
461
+ let token;
462
+ do {
463
+ token = this.getNextToken();
464
+ tokens.push(token);
465
+ } while (token.type !== 136 /* EOF */);
466
+ return tokens;
467
+ }
468
+ };
469
+
470
+ // ../../src/lsp/formatter.ts
471
+ var KEYWORD_CANONICAL = /* @__PURE__ */ new Map([
472
+ [22 /* KeywordDim */, "Dim"],
473
+ [17 /* KeywordSub */, "Sub"],
474
+ [18 /* KeywordFunction */, "Function"],
475
+ [19 /* KeywordProperty */, "Property"],
476
+ [20 /* KeywordGet */, "Get"],
477
+ [21 /* KeywordLet */, "Let"],
478
+ [32 /* KeywordSet */, "Set"],
479
+ [10 /* KeywordEnd */, "End"],
480
+ [6 /* KeywordIf */, "If"],
481
+ [7 /* KeywordThen */, "Then"],
482
+ [9 /* KeywordElse */, "Else"],
483
+ [8 /* KeywordElseIf */, "ElseIf"],
484
+ [3 /* KeywordFor */, "For"],
485
+ [4 /* KeywordTo */, "To"],
486
+ [43 /* KeywordStep */, "Step"],
487
+ [5 /* KeywordNext */, "Next"],
488
+ [55 /* KeywordEach */, "Each"],
489
+ [56 /* KeywordIn */, "In"],
490
+ [11 /* KeywordDo */, "Do"],
491
+ [14 /* KeywordLoop */, "Loop"],
492
+ [12 /* KeywordWhile */, "While"],
493
+ [13 /* KeywordWend */, "Wend"],
494
+ [15 /* KeywordUntil */, "Until"],
495
+ [61 /* KeywordWith */, "With"],
496
+ [53 /* KeywordSelect */, "Select"],
497
+ [54 /* KeywordCase */, "Case"],
498
+ [48 /* KeywordType */, "Type"],
499
+ [59 /* KeywordEnum */, "Enum"],
500
+ [69 /* KeywordClass */, "Class"],
501
+ [57 /* KeywordPublic */, "Public"],
502
+ [58 /* KeywordPrivate */, "Private"],
503
+ [60 /* KeywordFriend */, "Friend"],
504
+ [68 /* KeywordStatic */, "Static"],
505
+ [31 /* KeywordConst */, "Const"],
506
+ [23 /* KeywordAs */, "As"],
507
+ [24 /* KeywordNew */, "New"],
508
+ [47 /* KeywordByVal */, "ByVal"],
509
+ [46 /* KeywordByRef */, "ByRef"],
510
+ [50 /* KeywordOptional */, "Optional"],
511
+ [101 /* KeywordParamArray */, "ParamArray"],
512
+ [33 /* KeywordCall */, "Call"],
513
+ [45 /* KeywordExit */, "Exit"],
514
+ [38 /* KeywordReturn */, "Return"],
515
+ [36 /* KeywordGoTo */, "GoTo"],
516
+ [37 /* KeywordGoSub */, "GoSub"],
517
+ [34 /* KeywordOn */, "On"],
518
+ [35 /* KeywordError */, "Error"],
519
+ [52 /* KeywordResume */, "Resume"],
520
+ [42 /* KeywordReDim */, "ReDim"],
521
+ [41 /* KeywordErase */, "Erase"],
522
+ [28 /* KeywordNot */, "Not"],
523
+ [26 /* KeywordAnd */, "And"],
524
+ [27 /* KeywordOr */, "Or"],
525
+ [64 /* KeywordXor */, "Xor"],
526
+ [65 /* KeywordEqv */, "Eqv"],
527
+ [66 /* KeywordImp */, "Imp"],
528
+ [118 /* KeywordMod */, "Mod"],
529
+ [51 /* KeywordIs */, "Is"],
530
+ [63 /* KeywordLike */, "Like"],
531
+ [49 /* KeywordNothing */, "Nothing"],
532
+ [44 /* KeywordEmpty */, "Empty"],
533
+ [67 /* KeywordNull */, "Null"],
534
+ [70 /* KeywordMe */, "Me"],
535
+ [29 /* KeywordOption */, "Option"],
536
+ [30 /* KeywordExplicit */, "Explicit"],
537
+ [79 /* KeywordBase */, "Base"],
538
+ [71 /* KeywordCompare */, "Compare"],
539
+ [16 /* KeywordStop */, "Stop"],
540
+ [81 /* KeywordOpen */, "Open"],
541
+ [82 /* KeywordClose */, "Close"],
542
+ [85 /* KeywordPrint */, "Print"],
543
+ [84 /* KeywordInput */, "Input"],
544
+ [87 /* KeywordOutput */, "Output"],
545
+ [88 /* KeywordAppend */, "Append"],
546
+ [89 /* KeywordRandom */, "Random"],
547
+ [90 /* KeywordAccess */, "Access"],
548
+ [91 /* KeywordRead */, "Read"],
549
+ [92 /* KeywordWrite */, "Write"],
550
+ [93 /* KeywordLock */, "Lock"],
551
+ [94 /* KeywordShared */, "Shared"],
552
+ [75 /* KeywordDeclare */, "Declare"],
553
+ [76 /* KeywordLib */, "Lib"],
554
+ [77 /* KeywordAlias */, "Alias"],
555
+ [78 /* KeywordPtrSafe */, "PtrSafe"],
556
+ [105 /* KeywordImplements */, "Implements"],
557
+ [102 /* KeywordEvent */, "Event"],
558
+ [103 /* KeywordRaiseEvent */, "RaiseEvent"],
559
+ [104 /* KeywordWithEvents */, "WithEvents"],
560
+ [62 /* KeywordTypeOf */, "TypeOf"],
561
+ [110 /* KeywordAddressOf */, "AddressOf"],
562
+ [25 /* KeywordCollection */, "Collection"],
563
+ [39 /* KeywordLSet */, "LSet"],
564
+ [40 /* KeywordRSet */, "RSet"],
565
+ [108 /* KeywordMid */, "Mid"],
566
+ [97 /* KeywordKill */, "Kill"],
567
+ [98 /* KeywordSeek */, "Seek"],
568
+ [99 /* KeywordReset */, "Reset"],
569
+ [100 /* KeywordUnlock */, "Unlock"],
570
+ [109 /* KeywordWidth */, "Width"],
571
+ [83 /* KeywordLine */, "Line"],
572
+ [86 /* KeywordPut */, "Put"],
573
+ [74 /* KeywordAttribute */, "Attribute"],
574
+ [80 /* KeywordModule */, "Module"],
575
+ [95 /* KeywordSpc */, "Spc"],
576
+ [96 /* KeywordTab */, "Tab"],
577
+ [106 /* KeywordAppActivate */, "AppActivate"],
578
+ [107 /* KeywordSendKeys */, "SendKeys"],
579
+ [72 /* KeywordBinary */, "Binary"],
580
+ [73 /* KeywordText */, "Text"]
581
+ ]);
582
+ var PARAM_INTRO = /* @__PURE__ */ new Set([
583
+ 127 /* OperatorLParen */,
584
+ 126 /* OperatorComma */,
585
+ 46 /* KeywordByRef */,
586
+ 47 /* KeywordByVal */,
587
+ 50 /* KeywordOptional */,
588
+ 101 /* KeywordParamArray */
589
+ ]);
590
+ function collectParamNames(tokens, startIdx, define) {
591
+ let i = startIdx;
592
+ while (i < tokens.length && tokens[i].type !== 127 /* OperatorLParen */) {
593
+ if (i > startIdx + 2) return;
594
+ i++;
595
+ }
596
+ i++;
597
+ let depth = 1;
598
+ while (i < tokens.length && depth > 0) {
599
+ const t = tokens[i];
600
+ if (t.type === 127 /* OperatorLParen */) {
601
+ depth++;
602
+ i++;
603
+ continue;
604
+ }
605
+ if (t.type === 128 /* OperatorRParen */) {
606
+ depth--;
607
+ i++;
608
+ continue;
609
+ }
610
+ if (depth === 1 && t.type === 0 /* Identifier */) {
611
+ const prev = tokens[i - 1];
612
+ if (prev && PARAM_INTRO.has(prev.type)) define(t.value);
613
+ }
614
+ i++;
615
+ }
616
+ }
617
+ function collectDefinitions(source) {
618
+ const moduleDefs = /* @__PURE__ */ new Map();
619
+ const procs = [];
620
+ let tokens;
621
+ try {
622
+ tokens = new Lexer(source).tokenize().filter((t) => t.type !== 135 /* Newline */ && t.type !== 136 /* EOF */);
623
+ } catch {
624
+ return { moduleDefs, procs };
625
+ }
626
+ const defModule = (name) => {
627
+ if (!moduleDefs.has(name.toLowerCase())) moduleDefs.set(name.toLowerCase(), name);
628
+ };
629
+ let currentProc = null;
630
+ const defLocal = (name) => {
631
+ const map = currentProc?.defs ?? moduleDefs;
632
+ if (!map.has(name.toLowerCase())) map.set(name.toLowerCase(), name);
633
+ };
634
+ let inTypeOrEnum = false;
635
+ let prevLine = -1;
636
+ for (let i = 0; i < tokens.length; i++) {
637
+ const t = tokens[i];
638
+ const next = tokens[i + 1];
639
+ if (inTypeOrEnum) {
640
+ if (t.type === 10 /* KeywordEnd */) {
641
+ if (next?.type === 48 /* KeywordType */ || next?.type === 59 /* KeywordEnum */) {
642
+ inTypeOrEnum = false;
643
+ }
644
+ prevLine = t.line;
645
+ continue;
646
+ }
647
+ if (t.type === 0 /* Identifier */ && t.line !== prevLine) {
648
+ defModule(t.value);
649
+ }
650
+ prevLine = t.line;
651
+ continue;
652
+ }
653
+ if (ACCESS_MODIFIERS.has(t.type)) continue;
654
+ if (t.type === 10 /* KeywordEnd */) {
655
+ if (next?.type === 17 /* KeywordSub */ || next?.type === 18 /* KeywordFunction */ || next?.type === 19 /* KeywordProperty */) {
656
+ if (currentProc) {
657
+ procs.push({ startLine: currentProc.startLine, endLine: next.line, defs: currentProc.defs });
658
+ currentProc = null;
659
+ }
660
+ }
661
+ if (next?.type === 48 /* KeywordType */ || next?.type === 59 /* KeywordEnum */) {
662
+ inTypeOrEnum = false;
663
+ }
664
+ continue;
665
+ }
666
+ switch (t.type) {
667
+ case 22 /* KeywordDim */:
668
+ case 31 /* KeywordConst */:
669
+ case 68 /* KeywordStatic */: {
670
+ if (next?.type === 0 /* Identifier */) defLocal(next.value);
671
+ break;
672
+ }
673
+ case 42 /* KeywordReDim */: {
674
+ const skip = next?.value?.toLowerCase() === "preserve" ? tokens[i + 2] : next;
675
+ if (skip?.type === 0 /* Identifier */) defLocal(skip.value);
676
+ break;
677
+ }
678
+ case 17 /* KeywordSub */:
679
+ case 18 /* KeywordFunction */: {
680
+ if (next?.type === 0 /* Identifier */) {
681
+ defModule(next.value);
682
+ currentProc = { startLine: t.line, defs: /* @__PURE__ */ new Map() };
683
+ collectParamNames(tokens, i + 2, (name) => defLocal(name));
684
+ }
685
+ break;
686
+ }
687
+ case 19 /* KeywordProperty */: {
688
+ let nameIdx = i + 1;
689
+ const verb = tokens[nameIdx]?.value?.toLowerCase();
690
+ if (verb === "get" || verb === "let" || verb === "set") nameIdx++;
691
+ if (tokens[nameIdx]?.type === 0 /* Identifier */) {
692
+ defModule(tokens[nameIdx].value);
693
+ currentProc = { startLine: t.line, defs: /* @__PURE__ */ new Map() };
694
+ collectParamNames(tokens, nameIdx + 1, (name) => defLocal(name));
695
+ }
696
+ break;
697
+ }
698
+ case 48 /* KeywordType */:
699
+ case 59 /* KeywordEnum */: {
700
+ if (next?.type === 0 /* Identifier */) defModule(next.value);
701
+ inTypeOrEnum = true;
702
+ break;
703
+ }
704
+ }
705
+ }
706
+ return { moduleDefs, procs };
707
+ }
708
+ function format(source, options = {}) {
709
+ const indentSize = options.indentSize ?? 4;
710
+ const indentChar = options.indentChar ?? " ";
711
+ const doKeywordCase = options.keywordCase !== false;
712
+ const unit = indentChar.repeat(indentSize);
713
+ const physicalLines = source.split("\n");
714
+ const scopedDefs = collectDefinitions(source);
715
+ const keywordChangesByLine = /* @__PURE__ */ new Map();
716
+ {
717
+ let tokens;
718
+ try {
719
+ tokens = new Lexer(source).tokenize();
720
+ } catch {
721
+ tokens = [];
722
+ }
723
+ for (const token of tokens) {
724
+ let canonical;
725
+ if (doKeywordCase) {
726
+ canonical = KEYWORD_CANONICAL.get(token.type);
727
+ }
728
+ if (!canonical && token.type === 0 /* Identifier */) {
729
+ const lv = token.value.toLowerCase();
730
+ const proc = scopedDefs.procs.find(
731
+ (p) => token.line >= p.startLine && token.line <= p.endLine
732
+ );
733
+ canonical = proc ? proc.defs.get(lv) ?? scopedDefs.moduleDefs.get(lv) : scopedDefs.moduleDefs.get(lv);
734
+ }
735
+ if (!canonical) continue;
736
+ const value = token.value;
737
+ if (value === canonical) continue;
738
+ const lineIdx = token.line - 1;
739
+ if (!keywordChangesByLine.has(lineIdx)) keywordChangesByLine.set(lineIdx, []);
740
+ keywordChangesByLine.get(lineIdx).push({
741
+ startCol: token.column - 1,
742
+ endCol: token.column - 1 + value.length,
743
+ text: canonical
744
+ });
745
+ }
746
+ }
747
+ const edits = [];
748
+ let level = 0;
749
+ let selectDepth = 0;
750
+ let continuationAccum = [];
751
+ for (let i = 0; i < physicalLines.length; i++) {
752
+ const rawLine = physicalLines[i];
753
+ const hasCR = rawLine.endsWith("\r");
754
+ const line = hasCR ? rawLine.slice(0, -1) : rawLine;
755
+ const trimmed = line.trim();
756
+ if (trimmed === "") continue;
757
+ const prevLine = i > 0 ? physicalLines[i - 1].replace(/\r$/, "").trimEnd() : "";
758
+ const isContinuation = prevLine.endsWith(" _") || prevLine.endsWith(" _");
759
+ if (!isContinuation) {
760
+ continuationAccum = [trimmed];
761
+ } else {
762
+ continuationAccum.push(trimmed);
763
+ }
764
+ const isLastContinuation = isContinuation && !trimmed.endsWith(" _") && !trimmed.endsWith(" _");
765
+ const lineTokens = tokenizeLine(trimmed);
766
+ const first = lineTokens[0]?.type;
767
+ const second = lineTokens[1]?.type;
768
+ const isLabel = lineTokens.length >= 2 && lineTokens[0].type === 0 /* Identifier */ && lineTokens[1].type === 130 /* OperatorColon */;
769
+ let expectedLevel = level;
770
+ if (!isContinuation && !isLabel) {
771
+ const startsMultiLine = trimmed.endsWith(" _") || trimmed.endsWith(" _");
772
+ const { closerBefore, openerAfter } = classifyLine(lineTokens, selectDepth);
773
+ if (closerBefore) {
774
+ level = Math.max(0, level - 1);
775
+ if (first === 10 /* KeywordEnd */ && second === 53 /* KeywordSelect */) {
776
+ selectDepth = Math.max(0, selectDepth - 1);
777
+ }
778
+ }
779
+ expectedLevel = level;
780
+ if (openerAfter && !startsMultiLine) {
781
+ level++;
782
+ if (first === 53 /* KeywordSelect */ && second === 54 /* KeywordCase */) {
783
+ selectDepth++;
784
+ }
785
+ }
786
+ } else if (isLastContinuation && !isLabel) {
787
+ const logicalLine = continuationAccum.map((l) => l.replace(/[ \t]+_[ \t]*$/, "")).join(" ");
788
+ const logTokens = tokenizeLine(logicalLine);
789
+ const logFirst = logTokens[0]?.type;
790
+ const logSecond = logTokens[1]?.type;
791
+ const { openerAfter } = classifyLine(logTokens, selectDepth);
792
+ if (openerAfter) {
793
+ level++;
794
+ if (logFirst === 53 /* KeywordSelect */ && logSecond === 54 /* KeywordCase */) {
795
+ selectDepth++;
796
+ }
797
+ }
798
+ }
799
+ const expectedIndent = isContinuation ? "" : isLabel ? "" : unit.repeat(expectedLevel);
800
+ const actualIndent = leadingWhitespace(line);
801
+ const contentStart = actualIndent.length;
802
+ const changes = keywordChangesByLine.get(i) ?? [];
803
+ let content = line.slice(contentStart);
804
+ if (changes.length > 0) {
805
+ const contentChanges = changes.map((c) => ({ start: c.startCol - contentStart, end: c.endCol - contentStart, text: c.text })).filter((c) => c.start >= 0).sort((a, b) => b.start - a.start);
806
+ for (const c of contentChanges) {
807
+ content = content.slice(0, c.start) + c.text + content.slice(c.end);
808
+ }
809
+ }
810
+ const newLine = (isContinuation ? actualIndent : expectedIndent) + content + (hasCR ? "\r" : "");
811
+ if (newLine !== rawLine) {
812
+ edits.push({
813
+ range: {
814
+ start: { line: i, character: 0 },
815
+ end: { line: i, character: rawLine.length }
816
+ },
817
+ newText: newLine
818
+ });
819
+ }
820
+ }
821
+ return edits;
822
+ }
823
+ var BLOCK_OPEN = /* @__PURE__ */ new Set([
824
+ 17 /* KeywordSub */,
825
+ 18 /* KeywordFunction */,
826
+ 19 /* KeywordProperty */,
827
+ 3 /* KeywordFor */,
828
+ 11 /* KeywordDo */,
829
+ 12 /* KeywordWhile */,
830
+ 61 /* KeywordWith */,
831
+ 48 /* KeywordType */,
832
+ 59 /* KeywordEnum */,
833
+ 69 /* KeywordClass */
834
+ ]);
835
+ var BLOCK_CLOSE_SINGLE = /* @__PURE__ */ new Set([
836
+ 5 /* KeywordNext */,
837
+ 13 /* KeywordWend */,
838
+ 14 /* KeywordLoop */
839
+ ]);
840
+ var ACCESS_MODIFIERS = /* @__PURE__ */ new Set([
841
+ 57 /* KeywordPublic */,
842
+ 58 /* KeywordPrivate */,
843
+ 60 /* KeywordFriend */,
844
+ 68 /* KeywordStatic */
845
+ ]);
846
+ function classifyLine(tokens, selectDepth) {
847
+ if (tokens.length === 0) return { closerBefore: false, openerAfter: false };
848
+ let offset = 0;
849
+ while (offset < tokens.length && ACCESS_MODIFIERS.has(tokens[offset].type)) {
850
+ offset++;
851
+ }
852
+ const first = tokens[offset]?.type ?? tokens[0].type;
853
+ const second = tokens[offset + 1]?.type;
854
+ if (first === 10 /* KeywordEnd */ && second !== void 0) {
855
+ const isEndProc = second === 17 /* KeywordSub */ || second === 18 /* KeywordFunction */ || second === 19 /* KeywordProperty */;
856
+ const isEndBlock = second === 6 /* KeywordIf */ || second === 61 /* KeywordWith */ || second === 48 /* KeywordType */ || second === 59 /* KeywordEnum */ || second === 69 /* KeywordClass */ || second === 53 /* KeywordSelect */;
857
+ if (isEndProc || isEndBlock) {
858
+ return { closerBefore: true, openerAfter: false };
859
+ }
860
+ }
861
+ if (BLOCK_CLOSE_SINGLE.has(first)) {
862
+ return { closerBefore: true, openerAfter: false };
863
+ }
864
+ if (first === 9 /* KeywordElse */ || first === 8 /* KeywordElseIf */) {
865
+ return { closerBefore: true, openerAfter: true };
866
+ }
867
+ if (first === 54 /* KeywordCase */ && selectDepth > 0) {
868
+ return { closerBefore: true, openerAfter: true };
869
+ }
870
+ if (BLOCK_OPEN.has(first)) {
871
+ return { closerBefore: false, openerAfter: true };
872
+ }
873
+ if (first === 53 /* KeywordSelect */ && second === 54 /* KeywordCase */) {
874
+ return { closerBefore: false, openerAfter: true };
875
+ }
876
+ if (first === 6 /* KeywordIf */) {
877
+ const thenIdx = tokens.findIndex((t) => t.type === 7 /* KeywordThen */);
878
+ if (thenIdx !== -1) {
879
+ const afterThen = tokens.slice(thenIdx + 1).filter((t) => t.type !== 130 /* OperatorColon */);
880
+ if (afterThen.length === 0) {
881
+ return { closerBefore: false, openerAfter: true };
882
+ }
883
+ }
884
+ return { closerBefore: false, openerAfter: false };
885
+ }
886
+ return { closerBefore: false, openerAfter: false };
887
+ }
888
+ function tokenizeLine(trimmed) {
889
+ try {
890
+ return new Lexer(trimmed).tokenize().filter(
891
+ (t) => t.type !== 135 /* Newline */ && t.type !== 136 /* EOF */
892
+ );
893
+ } catch {
894
+ return [];
895
+ }
896
+ }
897
+ function leadingWhitespace(line) {
898
+ const match = line.match(/^[ \t]*/);
899
+ return match ? match[0] : "";
900
+ }
901
+ function applyEdits(source, edits) {
902
+ if (edits.length === 0) return source;
903
+ const lines = source.split("\n");
904
+ const sorted = [...edits].sort((a, b) => b.range.start.line - a.range.start.line);
905
+ for (const edit of sorted) {
906
+ const lineIdx = edit.range.start.line;
907
+ const origLine = lines[lineIdx];
908
+ lines[lineIdx] = origLine.slice(0, edit.range.start.character) + edit.newText + origLine.slice(edit.range.end.character);
909
+ }
910
+ return lines.join("\n");
911
+ }
912
+
913
+ // ../../test-libs/vba-formatter.ts
914
+ var VBA_EXTENSIONS = /\.(bas|cls|frm)$/i;
915
+ function collectFiles(target) {
916
+ const stat = fs.statSync(target);
917
+ if (stat.isFile()) return [target];
918
+ if (stat.isDirectory()) {
919
+ return fs.readdirSync(target).flatMap((entry) => {
920
+ const full = path.join(target, entry);
921
+ const s = fs.statSync(full);
922
+ if (s.isDirectory()) return collectFiles(full);
923
+ if (VBA_EXTENSIONS.test(entry)) return [full];
924
+ return [];
925
+ });
926
+ }
927
+ return [];
928
+ }
929
+ function buildDiff(original, formatted, filePath) {
930
+ const origLines = original.split("\n");
931
+ const fmtLines = formatted.split("\n");
932
+ const maxLen = Math.max(origLines.length, fmtLines.length);
933
+ const diffLines = [`--- ${filePath} (original)`, `+++ ${filePath} (formatted)`];
934
+ let hasDiff = false;
935
+ for (let i = 0; i < maxLen; i++) {
936
+ const orig = origLines[i] ?? "";
937
+ const fmtd = fmtLines[i] ?? "";
938
+ if (orig !== fmtd) {
939
+ hasDiff = true;
940
+ diffLines.push(`@@ line ${i + 1} @@`);
941
+ diffLines.push(`-${orig}`);
942
+ diffLines.push(`+${fmtd}`);
943
+ }
944
+ }
945
+ return hasDiff ? diffLines.join("\n") : "";
946
+ }
947
+ function main() {
948
+ const args = process.argv.slice(2);
949
+ if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
950
+ console.log(`Usage: vba-formatter <file-or-dir> [--check | --write] [options]
951
+
952
+ Options:
953
+ --check \u5DEE\u5206\u304C\u3042\u308C\u3070\u8868\u793A\u3057\u3066 exit code 1 \u3067\u7D42\u4E86
954
+ --write \u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D\uFF08\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306F stdout \u306B\u51FA\u529B\uFF09
955
+ --indent-size=N \u30A4\u30F3\u30C7\u30F3\u30C8\u5E45\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8: 4\uFF09
956
+ --no-keyword-case \u30AD\u30FC\u30EF\u30FC\u30C9\u306E\u5927\u6587\u5B57\u5316\u3092\u7121\u52B9\u5316
957
+
958
+ Example:
959
+ npx tsx test-libs/vba-formatter.ts src/vba/Main.bas
960
+ npx tsx test-libs/vba-formatter.ts src/vba/ --check
961
+ npx tsx test-libs/vba-formatter.ts src/vba/Main.bas --write --indent-size=2`);
962
+ process.exit(0);
963
+ }
964
+ const target = args.find((a) => !a.startsWith("-"));
965
+ const isCheck = args.includes("--check");
966
+ const isWrite = args.includes("--write");
967
+ const noKeywordCase = args.includes("--no-keyword-case");
968
+ const indentSizeArg = args.find((a) => a.startsWith("--indent-size="));
969
+ const indentSize = indentSizeArg ? parseInt(indentSizeArg.split("=")[1], 10) : 4;
970
+ if (!target || !fs.existsSync(target)) {
971
+ console.error(`Error: "${target ?? "(\u6307\u5B9A\u306A\u3057)"}" \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093`);
972
+ process.exit(1);
973
+ }
974
+ const options = {
975
+ indentSize,
976
+ keywordCase: noKeywordCase ? false : "pascal"
977
+ };
978
+ const files = collectFiles(target);
979
+ if (files.length === 0) {
980
+ console.error(`Error: VBA\u30D5\u30A1\u30A4\u30EB\uFF08.bas/.cls/.frm\uFF09\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${target}`);
981
+ process.exit(1);
982
+ }
983
+ let hasAnyDiff = false;
984
+ for (const filePath of files) {
985
+ const original = fs.readFileSync(filePath, "utf8");
986
+ const edits = format(original, options);
987
+ const formatted = applyEdits(original, edits);
988
+ const hasDiff = formatted !== original;
989
+ if (isCheck) {
990
+ if (hasDiff) {
991
+ hasAnyDiff = true;
992
+ const diff = buildDiff(original, formatted, filePath);
993
+ console.log(diff);
994
+ }
995
+ } else if (isWrite) {
996
+ if (hasDiff) {
997
+ fs.writeFileSync(filePath, formatted, "utf8");
998
+ console.log(`\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u6E08\u307F: ${filePath}`);
999
+ }
1000
+ } else {
1001
+ if (files.length > 1) {
1002
+ console.error("Error: stdout \u30E2\u30FC\u30C9\u306F\u5358\u4E00\u30D5\u30A1\u30A4\u30EB\u306E\u307F\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059\u3002\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u306F --check \u307E\u305F\u306F --write \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
1003
+ process.exit(1);
1004
+ }
1005
+ process.stdout.write(formatted);
1006
+ }
1007
+ }
1008
+ if (isCheck && hasAnyDiff) {
1009
+ process.exit(1);
1010
+ }
1011
+ }
1012
+ main();