kopytko-formatter 1.0.9 → 1.1.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/dist/src/config.d.ts +4 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +3 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/cst-passes/aaThreshold.d.ts +4 -0
- package/dist/src/cst-passes/aaThreshold.d.ts.map +1 -0
- package/dist/src/cst-passes/aaThreshold.js +11 -0
- package/dist/src/cst-passes/aaThreshold.js.map +1 -0
- package/dist/src/cst-passes/alignAssignments.d.ts +4 -0
- package/dist/src/cst-passes/alignAssignments.d.ts.map +1 -0
- package/dist/src/cst-passes/alignAssignments.js +11 -0
- package/dist/src/cst-passes/alignAssignments.js.map +1 -0
- package/dist/src/cst-passes/blankLines.d.ts +19 -0
- package/dist/src/cst-passes/blankLines.d.ts.map +1 -0
- package/dist/src/cst-passes/blankLines.js +40 -0
- package/dist/src/cst-passes/blankLines.js.map +1 -0
- package/dist/src/cst-passes/casingPass.d.ts +12 -0
- package/dist/src/cst-passes/casingPass.d.ts.map +1 -0
- package/dist/src/cst-passes/casingPass.js +214 -0
- package/dist/src/cst-passes/casingPass.js.map +1 -0
- package/dist/src/cst-passes/commentNormalization.d.ts +24 -0
- package/dist/src/cst-passes/commentNormalization.d.ts.map +1 -0
- package/dist/src/cst-passes/commentNormalization.js +76 -0
- package/dist/src/cst-passes/commentNormalization.js.map +1 -0
- package/dist/src/cst-passes/commentWidth.d.ts +4 -0
- package/dist/src/cst-passes/commentWidth.d.ts.map +1 -0
- package/dist/src/cst-passes/commentWidth.js +50 -0
- package/dist/src/cst-passes/commentWidth.js.map +1 -0
- package/dist/src/cst-passes/elseOnNewLine.d.ts +4 -0
- package/dist/src/cst-passes/elseOnNewLine.d.ts.map +1 -0
- package/dist/src/cst-passes/elseOnNewLine.js +11 -0
- package/dist/src/cst-passes/elseOnNewLine.js.map +1 -0
- package/dist/src/cst-passes/emptyLinesBetweenMethods.d.ts +4 -0
- package/dist/src/cst-passes/emptyLinesBetweenMethods.d.ts.map +1 -0
- package/dist/src/cst-passes/emptyLinesBetweenMethods.js +11 -0
- package/dist/src/cst-passes/emptyLinesBetweenMethods.js.map +1 -0
- package/dist/src/cst-passes/endKeywordStyle.d.ts +21 -0
- package/dist/src/cst-passes/endKeywordStyle.d.ts.map +1 -0
- package/dist/src/cst-passes/endKeywordStyle.js +95 -0
- package/dist/src/cst-passes/endKeywordStyle.js.map +1 -0
- package/dist/src/cst-passes/fieldAccessConsistency.d.ts +4 -0
- package/dist/src/cst-passes/fieldAccessConsistency.d.ts.map +1 -0
- package/dist/src/cst-passes/fieldAccessConsistency.js +11 -0
- package/dist/src/cst-passes/fieldAccessConsistency.js.map +1 -0
- package/dist/src/cst-passes/functionVsSub.d.ts +13 -0
- package/dist/src/cst-passes/functionVsSub.d.ts.map +1 -0
- package/dist/src/cst-passes/functionVsSub.js +112 -0
- package/dist/src/cst-passes/functionVsSub.js.map +1 -0
- package/dist/src/cst-passes/importSorting.d.ts +25 -0
- package/dist/src/cst-passes/importSorting.d.ts.map +1 -0
- package/dist/src/cst-passes/importSorting.js +126 -0
- package/dist/src/cst-passes/importSorting.js.map +1 -0
- package/dist/src/cst-passes/indentation.d.ts +21 -0
- package/dist/src/cst-passes/indentation.d.ts.map +1 -0
- package/dist/src/cst-passes/indentation.js +115 -0
- package/dist/src/cst-passes/indentation.js.map +1 -0
- package/dist/src/cst-passes/index.d.ts +45 -0
- package/dist/src/cst-passes/index.d.ts.map +1 -0
- package/dist/src/cst-passes/index.js +77 -0
- package/dist/src/cst-passes/index.js.map +1 -0
- package/dist/src/cst-passes/infrastructure.d.ts +39 -0
- package/dist/src/cst-passes/infrastructure.d.ts.map +1 -0
- package/dist/src/cst-passes/infrastructure.js +63 -0
- package/dist/src/cst-passes/infrastructure.js.map +1 -0
- package/dist/src/cst-passes/lineCommentPosition.d.ts +4 -0
- package/dist/src/cst-passes/lineCommentPosition.d.ts.map +1 -0
- package/dist/src/cst-passes/lineCommentPosition.js +42 -0
- package/dist/src/cst-passes/lineCommentPosition.js.map +1 -0
- package/dist/src/cst-passes/mPrefixStyle.d.ts +4 -0
- package/dist/src/cst-passes/mPrefixStyle.d.ts.map +1 -0
- package/dist/src/cst-passes/mPrefixStyle.js +11 -0
- package/dist/src/cst-passes/mPrefixStyle.js.map +1 -0
- package/dist/src/cst-passes/observeFieldStyle.d.ts +6 -0
- package/dist/src/cst-passes/observeFieldStyle.d.ts.map +1 -0
- package/dist/src/cst-passes/observeFieldStyle.js +21 -0
- package/dist/src/cst-passes/observeFieldStyle.js.map +1 -0
- package/dist/src/cst-passes/paramAlignment.d.ts +4 -0
- package/dist/src/cst-passes/paramAlignment.d.ts.map +1 -0
- package/dist/src/cst-passes/paramAlignment.js +11 -0
- package/dist/src/cst-passes/paramAlignment.js.map +1 -0
- package/dist/src/cst-passes/parenthesisIfCase.d.ts +6 -0
- package/dist/src/cst-passes/parenthesisIfCase.d.ts.map +1 -0
- package/dist/src/cst-passes/parenthesisIfCase.js +42 -0
- package/dist/src/cst-passes/parenthesisIfCase.js.map +1 -0
- package/dist/src/cst-passes/printStatementRemoval.d.ts +20 -0
- package/dist/src/cst-passes/printStatementRemoval.d.ts.map +1 -0
- package/dist/src/cst-passes/printStatementRemoval.js +67 -0
- package/dist/src/cst-passes/printStatementRemoval.js.map +1 -0
- package/dist/src/cst-passes/spacing.d.ts +19 -0
- package/dist/src/cst-passes/spacing.d.ts.map +1 -0
- package/dist/src/cst-passes/spacing.js +81 -0
- package/dist/src/cst-passes/spacing.js.map +1 -0
- package/dist/src/cst-passes/splitArrayOpenBracket.d.ts +4 -0
- package/dist/src/cst-passes/splitArrayOpenBracket.d.ts.map +1 -0
- package/dist/src/cst-passes/splitArrayOpenBracket.js +11 -0
- package/dist/src/cst-passes/splitArrayOpenBracket.js.map +1 -0
- package/dist/src/cst-passes/stringConcatStyle.d.ts +4 -0
- package/dist/src/cst-passes/stringConcatStyle.d.ts.map +1 -0
- package/dist/src/cst-passes/stringConcatStyle.js +11 -0
- package/dist/src/cst-passes/stringConcatStyle.js.map +1 -0
- package/dist/src/cst-passes/stripCatchParens.d.ts +4 -0
- package/dist/src/cst-passes/stripCatchParens.d.ts.map +1 -0
- package/dist/src/cst-passes/stripCatchParens.js +29 -0
- package/dist/src/cst-passes/stripCatchParens.js.map +1 -0
- package/dist/src/cst-passes/thenStyle.d.ts +15 -0
- package/dist/src/cst-passes/thenStyle.d.ts.map +1 -0
- package/dist/src/cst-passes/thenStyle.js +140 -0
- package/dist/src/cst-passes/thenStyle.js.map +1 -0
- package/dist/src/cst-passes/trailingCommas.d.ts +13 -0
- package/dist/src/cst-passes/trailingCommas.d.ts.map +1 -0
- package/dist/src/cst-passes/trailingCommas.js +56 -0
- package/dist/src/cst-passes/trailingCommas.js.map +1 -0
- package/dist/src/cst-passes/trailingWhitespace.d.ts +13 -0
- package/dist/src/cst-passes/trailingWhitespace.d.ts.map +1 -0
- package/dist/src/cst-passes/trailingWhitespace.js +26 -0
- package/dist/src/cst-passes/trailingWhitespace.js.map +1 -0
- package/dist/src/cst-passes/wrapLongStrings.d.ts +4 -0
- package/dist/src/cst-passes/wrapLongStrings.d.ts.map +1 -0
- package/dist/src/cst-passes/wrapLongStrings.js +11 -0
- package/dist/src/cst-passes/wrapLongStrings.js.map +1 -0
- package/dist/src/formatter.d.ts +2 -1
- package/dist/src/formatter.d.ts.map +1 -1
- package/dist/src/formatter.js +74 -333
- package/dist/src/formatter.js.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -10
- package/dist/src/index.js.map +1 -1
- package/package.json +5 -2
package/dist/src/formatter.js
CHANGED
|
@@ -2,22 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.formatText = formatText;
|
|
4
4
|
exports.checkFormatting = checkFormatting;
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const _builtinMap = new Map(builtins_1.BRIGHTSCRIPT_BUILTINS.map((b) => [b.name.toLowerCase(), b.name]));
|
|
9
|
-
const _keywordSet = new Set(builtins_1.BRIGHTSCRIPT_KEYWORDS.map((k) => k.toLowerCase()));
|
|
10
|
-
/** Mapping from compact end-keyword to spaced form. */
|
|
11
|
-
const COMPACT_TO_SPACED = {
|
|
12
|
-
'endif': 'end if',
|
|
13
|
-
'endfunction': 'end function',
|
|
14
|
-
'endsub': 'end sub',
|
|
15
|
-
'endwhile': 'end while',
|
|
16
|
-
'endfor': 'end for',
|
|
17
|
-
'endtry': 'end try',
|
|
18
|
-
};
|
|
5
|
+
const kopytko_brightscript_parser_1 = require("kopytko-brightscript-parser");
|
|
6
|
+
const kopytko_brightscript_parser_2 = require("kopytko-brightscript-parser");
|
|
7
|
+
const index_1 = require("./cst-passes/index");
|
|
19
8
|
/** Reverse mapping: spaced form → compact. */
|
|
20
|
-
const SPACED_TO_COMPACT = Object.fromEntries(Object.entries(COMPACT_TO_SPACED).map(([k, v]) => [v, k]));
|
|
21
9
|
/**
|
|
22
10
|
* Formats BrightScript source code using an 11-pass engine.
|
|
23
11
|
*
|
|
@@ -30,7 +18,7 @@ const SPACED_TO_COMPACT = Object.fromEntries(Object.entries(COMPACT_TO_SPACED).m
|
|
|
30
18
|
* @param userFunctions - Known user-defined functions for casing normalization (optional).
|
|
31
19
|
* @returns The formatted source text.
|
|
32
20
|
*/
|
|
33
|
-
function formatText(source, config, casing =
|
|
21
|
+
function formatText(source, config, casing = kopytko_brightscript_parser_1.DEFAULT_CASING_CONFIG, userFunctions = []) {
|
|
34
22
|
let lines = source.split(/\r?\n/);
|
|
35
23
|
const detectedEnding = detectLineEnding(source);
|
|
36
24
|
const lineEndStr = resolveLineEnding(config.lineEnding, detectedEnding);
|
|
@@ -39,62 +27,82 @@ function formatText(source, config, casing = casing_1.DEFAULT_CASING_CONFIG, use
|
|
|
39
27
|
if (!userFuncMap.has(fn.nameLower))
|
|
40
28
|
userFuncMap.set(fn.nameLower, fn.name);
|
|
41
29
|
}
|
|
42
|
-
// Pass 1 — Import sorting
|
|
30
|
+
// Pass 1 — Import sorting (regex — complex multi-group sorting with trivia)
|
|
43
31
|
lines = passImportSorting(lines, config);
|
|
44
|
-
// Pass 2 — Comment normalization
|
|
45
|
-
lines =
|
|
46
|
-
// Pass 3 — End keyword style + function vs sub
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
// Pass 2 — Comment normalization (CST)
|
|
33
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.commentNormalizationPass)({ commentStyle: config.commentStyle, spaceAfterCommentMarker: config.spaceAfterCommentMarker }));
|
|
34
|
+
// Pass 3 — End keyword style (CST) + function vs sub (CST)
|
|
35
|
+
if (config.endKeywordStyle !== 'preserve') {
|
|
36
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.endKeywordStylePass)(config.endKeywordStyle));
|
|
37
|
+
}
|
|
38
|
+
if (config.functionVsSubForVoid !== 'preserve' && config.functionVsSubForVoid !== 'allow-void') {
|
|
39
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.functionVsSubPass)(config.functionVsSubForVoid));
|
|
40
|
+
}
|
|
41
|
+
// Pass 4 — Then style (CST) + parenthesis if case (regex) + catch paren strip (regex)
|
|
42
|
+
if (config.thenStyle !== 'preserve') {
|
|
43
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.thenStylePass)(config.thenStyle));
|
|
44
|
+
}
|
|
51
45
|
lines = passParenthesisIfCase(lines, config);
|
|
52
46
|
lines = passStripCatchParens(lines);
|
|
53
|
-
// Pass 4c — Else on new line
|
|
47
|
+
// Pass 4c — Else on new line (regex)
|
|
54
48
|
lines = passElseOnNewLine(lines, config);
|
|
55
|
-
// Pass 5 — Print statement handling
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
// Pass 5 — Print statement handling (CST)
|
|
50
|
+
if (config.printStatement === 'remove') {
|
|
51
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.printStatementRemovalPass)());
|
|
52
|
+
}
|
|
53
|
+
// Pass 5b — Line comment position (regex)
|
|
58
54
|
lines = passLineCommentPosition(lines, config);
|
|
59
|
-
// Pass 6 — Spacing rules
|
|
55
|
+
// Pass 6 — Spacing rules (regex)
|
|
60
56
|
lines = passSpacing(lines, config);
|
|
61
|
-
// Pass 6b — Wrap long strings
|
|
57
|
+
// Pass 6b — Wrap long strings (regex)
|
|
62
58
|
lines = passWrapLongStrings(lines, config);
|
|
63
|
-
// Pass 6c — String concatenation style
|
|
59
|
+
// Pass 6c — String concatenation style (regex)
|
|
64
60
|
lines = passStringConcatStyle(lines, config);
|
|
65
|
-
// Pass 7 — Casing
|
|
66
|
-
lines =
|
|
67
|
-
// Pass 7b — Split array open bracket
|
|
61
|
+
// Pass 7 — Casing (CST)
|
|
62
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.casingPass)(casing, userFuncMap));
|
|
63
|
+
// Pass 7b — Split array open bracket (regex)
|
|
68
64
|
lines = passSplitArrayOpenBracket(lines, config);
|
|
69
|
-
// Pass 7c — Associative array single-line threshold
|
|
65
|
+
// Pass 7c — Associative array single-line threshold (regex)
|
|
70
66
|
lines = passAAThreshold(lines, config);
|
|
71
|
-
// Pass 8 — Indentation
|
|
67
|
+
// Pass 8 — Indentation (regex)
|
|
72
68
|
lines = passIndentation(lines, config);
|
|
73
|
-
// Pass 8b — Trailing commas
|
|
69
|
+
// Pass 8b — Trailing commas (regex)
|
|
74
70
|
lines = passTrailingCommas(lines, config);
|
|
75
|
-
// Pass 8c — Align assignments
|
|
71
|
+
// Pass 8c — Align assignments (regex)
|
|
76
72
|
lines = passAlignAssignments(lines, config);
|
|
77
|
-
// Pass 8d — Multi-line param alignment
|
|
73
|
+
// Pass 8d — Multi-line param alignment (regex)
|
|
78
74
|
lines = passParamAlignment(lines, config);
|
|
79
|
-
// Pass 9 — Blank line rules
|
|
75
|
+
// Pass 9 — Blank line rules (regex)
|
|
80
76
|
lines = passBlankLines(lines, config);
|
|
81
|
-
// Pass 9b — Empty lines between methods
|
|
77
|
+
// Pass 9b — Empty lines between methods (regex)
|
|
82
78
|
lines = passEmptyLinesBetweenMethods(lines, config);
|
|
83
|
-
// Pass 10 — Trailing whitespace
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
// Pass 10 — Trailing whitespace (CST)
|
|
80
|
+
if (config.trimTrailingWhitespace) {
|
|
81
|
+
lines = runCstOnLines(lines, lineEndStr, (0, index_1.trailingWhitespacePass)());
|
|
82
|
+
}
|
|
83
|
+
// Pass 11 — Comment width (regex)
|
|
86
84
|
lines = passCommentWidth(lines, config);
|
|
87
|
-
// Pass 12 — observeField style
|
|
85
|
+
// Pass 12 — observeField style (regex)
|
|
88
86
|
lines = passObserveFieldStyle(lines, config);
|
|
89
|
-
// Pass 13 — m prefix style
|
|
87
|
+
// Pass 13 — m prefix style (regex)
|
|
90
88
|
lines = passMPrefixStyle(lines, config);
|
|
91
|
-
// Pass 14 — Field access consistency
|
|
89
|
+
// Pass 14 — Field access consistency (regex)
|
|
92
90
|
lines = passFieldAccessConsistency(lines, config);
|
|
93
91
|
// Assemble result
|
|
94
92
|
let newText = lines.join(lineEndStr);
|
|
95
93
|
if (config.insertFinalNewline && newText.length > 0 && !newText.endsWith(lineEndStr)) {
|
|
96
94
|
newText += lineEndStr;
|
|
97
95
|
}
|
|
96
|
+
// ── Syntax safety verification ──────────────────────────────────────────
|
|
97
|
+
// Parse the formatted output to detect if formatting broke the syntax.
|
|
98
|
+
// If errors are found, return the original source unchanged.
|
|
99
|
+
if (config.verifySyntax !== false) {
|
|
100
|
+
const result = (0, kopytko_brightscript_parser_2.parse)(newText);
|
|
101
|
+
if (result.diagnostics.length > 0) {
|
|
102
|
+
// Formatting introduced syntax errors — fall back to original source
|
|
103
|
+
return source;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
98
106
|
return newText;
|
|
99
107
|
}
|
|
100
108
|
/**
|
|
@@ -106,6 +114,25 @@ function checkFormatting(source, config, casing, userFunctions) {
|
|
|
106
114
|
return formatText(source, config, casing, userFunctions) === source;
|
|
107
115
|
}
|
|
108
116
|
// ---------------------------------------------------------------------------
|
|
117
|
+
// CST ↔ lines bridge
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
/**
|
|
120
|
+
* Runs a CST pass on a string[] lines array.
|
|
121
|
+
* Joins lines → runs CST pass → splits back to lines.
|
|
122
|
+
* Falls back to original lines if the source has parse errors.
|
|
123
|
+
*/
|
|
124
|
+
function runCstOnLines(lines, lineEnd, pass) {
|
|
125
|
+
const source = lines.join(lineEnd);
|
|
126
|
+
const parseResult = (0, kopytko_brightscript_parser_2.parse)(source);
|
|
127
|
+
if (parseResult.diagnostics.length > 0)
|
|
128
|
+
return lines; // can't CST-transform broken code
|
|
129
|
+
const edits = pass(parseResult.root, source);
|
|
130
|
+
if (edits.length === 0)
|
|
131
|
+
return lines;
|
|
132
|
+
const result = (0, index_1.applyEdits)(source, edits);
|
|
133
|
+
return result.split(/\r?\n/);
|
|
134
|
+
}
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
109
136
|
// Line ending helpers
|
|
110
137
|
// ---------------------------------------------------------------------------
|
|
111
138
|
function detectLineEnding(text) {
|
|
@@ -212,203 +239,9 @@ function passImportSorting(lines, config) {
|
|
|
212
239
|
return [...sorted, ...lines.slice(blockEnd)];
|
|
213
240
|
}
|
|
214
241
|
// ---------------------------------------------------------------------------
|
|
215
|
-
// Pass 2 — Comment normalization
|
|
216
242
|
// ---------------------------------------------------------------------------
|
|
217
|
-
function passCommentNormalization(lines, config) {
|
|
218
|
-
if (config.commentStyle === 'preserve' && !config.spaceAfterCommentMarker)
|
|
219
|
-
return lines;
|
|
220
|
-
return lines.map(line => {
|
|
221
|
-
const trimmed = line.trim();
|
|
222
|
-
if (/^\s*'\s*@(?:import|mock)\s+/.test(line))
|
|
223
|
-
return line;
|
|
224
|
-
const isTickComment = trimmed.startsWith("'");
|
|
225
|
-
const isRemComment = /^rem\b/i.test(trimmed);
|
|
226
|
-
if (!isTickComment && !isRemComment)
|
|
227
|
-
return line;
|
|
228
|
-
const indent = line.match(/^(\s*)/)?.[1] ?? '';
|
|
229
|
-
let content = isTickComment ? trimmed.slice(1) : trimmed.slice(3);
|
|
230
|
-
let marker;
|
|
231
|
-
if (config.commentStyle === 'preserve') {
|
|
232
|
-
marker = isTickComment ? "'" : trimmed.slice(0, 3);
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
marker = config.commentStyle === "'" ? "'" : 'rem';
|
|
236
|
-
}
|
|
237
|
-
if (config.spaceAfterCommentMarker && content.length > 0 && !content.startsWith(' ')) {
|
|
238
|
-
content = ' ' + content;
|
|
239
|
-
}
|
|
240
|
-
return indent + marker + content;
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
// ---------------------------------------------------------------------------
|
|
244
|
-
// Pass 3 — End keyword style
|
|
245
|
-
// ---------------------------------------------------------------------------
|
|
246
|
-
function passEndKeywordStyle(lines, config) {
|
|
247
|
-
if (config.endKeywordStyle === 'preserve')
|
|
248
|
-
return lines;
|
|
249
|
-
return lines.map(line => {
|
|
250
|
-
const lower = line.trim().toLowerCase();
|
|
251
|
-
const indent = line.match(/^(\s*)/)?.[1] ?? '';
|
|
252
|
-
if (config.endKeywordStyle === 'compact') {
|
|
253
|
-
const compact = SPACED_TO_COMPACT[lower];
|
|
254
|
-
if (compact)
|
|
255
|
-
return indent + compact;
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
const spaced = COMPACT_TO_SPACED[lower];
|
|
259
|
-
if (spaced)
|
|
260
|
-
return indent + spaced;
|
|
261
|
-
}
|
|
262
|
-
return line;
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
// ---------------------------------------------------------------------------
|
|
266
|
-
// Pass 3b — function vs sub for void
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
|
-
function passFunctionVsSub(lines, config) {
|
|
269
|
-
if (config.functionVsSubForVoid === 'preserve')
|
|
270
|
-
return lines;
|
|
271
|
-
const result = [...lines];
|
|
272
|
-
const namedDeclRegex = /^(\s*)(function|sub)\s+(\w+)\s*\((.*)\)(?:\s+as\s+(\w+))?\s*$/i;
|
|
273
|
-
const anonDeclRegex = /^(.*\b)(function|sub)(\s*\(.*\))(?:\s+as\s+(\w+))?\s*$/i;
|
|
274
|
-
for (let i = 0; i < result.length; i++) {
|
|
275
|
-
// Named declarations
|
|
276
|
-
const nm = namedDeclRegex.exec(result[i]);
|
|
277
|
-
if (nm) {
|
|
278
|
-
const [, indent, keyword, name, params, returnType] = nm;
|
|
279
|
-
const kw = keyword.toLowerCase();
|
|
280
|
-
const endIdx = findMatchingEnd(result, i);
|
|
281
|
-
if (endIdx < 0)
|
|
282
|
-
continue;
|
|
283
|
-
const hasExplicitReturnType = returnType && returnType.toLowerCase() !== 'void';
|
|
284
|
-
const hasValueReturn = hasReturnWithValue(result, i + 1, endIdx);
|
|
285
|
-
const isVoid = !hasExplicitReturnType && !hasValueReturn;
|
|
286
|
-
if (config.functionVsSubForVoid === 'sub' && kw === 'function' && isVoid) {
|
|
287
|
-
result[i] = `${indent}sub ${name}(${params})`;
|
|
288
|
-
const ei = result[endIdx].match(/^(\s*)/)?.[1] ?? '';
|
|
289
|
-
const el = result[endIdx].trim().toLowerCase();
|
|
290
|
-
if (el === 'end function')
|
|
291
|
-
result[endIdx] = ei + 'end sub';
|
|
292
|
-
else if (el === 'endfunction')
|
|
293
|
-
result[endIdx] = ei + 'endsub';
|
|
294
|
-
}
|
|
295
|
-
else if (config.functionVsSubForVoid === 'function' && kw === 'sub') {
|
|
296
|
-
result[i] = `${indent}function ${name}(${params}) as Void`;
|
|
297
|
-
const ei = result[endIdx].match(/^(\s*)/)?.[1] ?? '';
|
|
298
|
-
const el = result[endIdx].trim().toLowerCase();
|
|
299
|
-
if (el === 'end sub')
|
|
300
|
-
result[endIdx] = ei + 'end function';
|
|
301
|
-
else if (el === 'endsub')
|
|
302
|
-
result[endIdx] = ei + 'endfunction';
|
|
303
|
-
}
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
// Anonymous function declarations
|
|
307
|
-
const am = anonDeclRegex.exec(result[i]);
|
|
308
|
-
if (!am)
|
|
309
|
-
continue;
|
|
310
|
-
if (/^\s*(?:function|sub)\b/i.test(result[i]))
|
|
311
|
-
continue;
|
|
312
|
-
const [, prefix, keyword, paramsWithParens, returnType] = am;
|
|
313
|
-
const kw = keyword.toLowerCase();
|
|
314
|
-
const endIdx = findMatchingEnd(result, i);
|
|
315
|
-
if (endIdx < 0)
|
|
316
|
-
continue;
|
|
317
|
-
const hasExplicitReturnType = returnType && returnType.toLowerCase() !== 'void';
|
|
318
|
-
const hasValueReturn = hasReturnWithValue(result, i + 1, endIdx);
|
|
319
|
-
const isVoid = !hasExplicitReturnType && !hasValueReturn;
|
|
320
|
-
if (config.functionVsSubForVoid === 'sub' && kw === 'function' && isVoid) {
|
|
321
|
-
result[i] = `${prefix}sub${paramsWithParens}`;
|
|
322
|
-
replaceEndKeyword(result, endIdx, 'function', 'sub');
|
|
323
|
-
}
|
|
324
|
-
else if (config.functionVsSubForVoid === 'function' && kw === 'sub') {
|
|
325
|
-
result[i] = `${prefix}function${paramsWithParens} as Void`;
|
|
326
|
-
replaceEndKeyword(result, endIdx, 'sub', 'function');
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
return result;
|
|
330
|
-
}
|
|
331
|
-
function replaceEndKeyword(lines, idx, from, to) {
|
|
332
|
-
const spaced = new RegExp(`\\bend\\s+${from}\\b`, 'i');
|
|
333
|
-
const joined = new RegExp(`\\bend${from}\\b`, 'i');
|
|
334
|
-
if (spaced.test(lines[idx])) {
|
|
335
|
-
lines[idx] = lines[idx].replace(spaced, `end ${to}`);
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
lines[idx] = lines[idx].replace(joined, `end${to}`);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function hasReturnWithValue(lines, startIdx, endIdx) {
|
|
342
|
-
let depth = 0;
|
|
343
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
344
|
-
const trimmed = lines[i].trim().toLowerCase();
|
|
345
|
-
if (/^(?:function|sub)\b/.test(trimmed) || isAnonFunctionOpener(trimmed))
|
|
346
|
-
depth++;
|
|
347
|
-
else if (/^(?:end\s*function|end\s*sub|endfunction|endsub)\b/.test(trimmed))
|
|
348
|
-
depth--;
|
|
349
|
-
if (depth > 0)
|
|
350
|
-
continue;
|
|
351
|
-
if (/^return(?:\s+\S|\{|\[|\()/i.test(trimmed))
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
function findMatchingEnd(lines, startIdx) {
|
|
357
|
-
let depth = 1;
|
|
358
|
-
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
359
|
-
const lower = lines[i].trim().toLowerCase();
|
|
360
|
-
if (/^(?:function|sub)\b/i.test(lower) || isAnonFunctionOpener(lower))
|
|
361
|
-
depth++;
|
|
362
|
-
else if (/^(?:end\s*function|end\s*sub|endfunction|endsub)\b/i.test(lower)) {
|
|
363
|
-
if (--depth === 0)
|
|
364
|
-
return i;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return -1;
|
|
368
|
-
}
|
|
369
243
|
// ---------------------------------------------------------------------------
|
|
370
|
-
// Pass 4 — Then style
|
|
371
244
|
// ---------------------------------------------------------------------------
|
|
372
|
-
function passThenStyle(lines, config) {
|
|
373
|
-
if (config.thenStyle === 'preserve')
|
|
374
|
-
return lines;
|
|
375
|
-
return lines.map(line => {
|
|
376
|
-
const trimmed = line.trim();
|
|
377
|
-
if (!/^(?:if|else\s*if|elseif)\b/i.test(trimmed))
|
|
378
|
-
return line;
|
|
379
|
-
const indent = line.match(/^(\s*)/)?.[1] ?? '';
|
|
380
|
-
const hasThen = /\bthen\b/i.test(trimmed);
|
|
381
|
-
let isSingleLine = false;
|
|
382
|
-
if (hasThen) {
|
|
383
|
-
const afterThen = trimmed.replace(/^.*?\bthen\b/i, '').trim();
|
|
384
|
-
isSingleLine = afterThen !== '' && !afterThen.startsWith("'") && !/^rem\b/i.test(afterThen);
|
|
385
|
-
}
|
|
386
|
-
const removeThen = () => {
|
|
387
|
-
const split = splitTrailingComment(trimmed);
|
|
388
|
-
const codeOnly = split.code.replace(/\s*\bthen\b\s*$/i, '').trimEnd();
|
|
389
|
-
return indent + codeOnly + (split.comment ? ' ' + split.comment : '');
|
|
390
|
-
};
|
|
391
|
-
switch (config.thenStyle) {
|
|
392
|
-
case 'always':
|
|
393
|
-
if (!hasThen)
|
|
394
|
-
return indent + trimmed + ' then';
|
|
395
|
-
break;
|
|
396
|
-
case 'never':
|
|
397
|
-
if (hasThen && !isSingleLine)
|
|
398
|
-
return removeThen();
|
|
399
|
-
break;
|
|
400
|
-
case 'multiline-only':
|
|
401
|
-
if (!hasThen)
|
|
402
|
-
return indent + trimmed + ' then';
|
|
403
|
-
break;
|
|
404
|
-
case 'singleline-only':
|
|
405
|
-
if (hasThen && !isSingleLine)
|
|
406
|
-
return removeThen();
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
return line;
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
245
|
// ---------------------------------------------------------------------------
|
|
413
246
|
// Pass 4b — Strip catch parentheses (always — BrightScript does not allow them)
|
|
414
247
|
// ---------------------------------------------------------------------------
|
|
@@ -546,13 +379,6 @@ function passElseOnNewLine(lines, config) {
|
|
|
546
379
|
return result;
|
|
547
380
|
}
|
|
548
381
|
// ---------------------------------------------------------------------------
|
|
549
|
-
// Pass 5 — Print statement handling
|
|
550
|
-
// ---------------------------------------------------------------------------
|
|
551
|
-
function passPrintStatement(lines, config) {
|
|
552
|
-
if (config.printStatement !== 'remove')
|
|
553
|
-
return lines;
|
|
554
|
-
return lines.filter(line => !/^\s*(?:print|\?)\b/i.test(line));
|
|
555
|
-
}
|
|
556
382
|
// ---------------------------------------------------------------------------
|
|
557
383
|
// Pass 5b — Line comment position
|
|
558
384
|
// ---------------------------------------------------------------------------
|
|
@@ -796,17 +622,6 @@ function applyBracketAndCommaSpacing(line, config) {
|
|
|
796
622
|
return result;
|
|
797
623
|
}
|
|
798
624
|
// ---------------------------------------------------------------------------
|
|
799
|
-
// Pass 7 — Casing
|
|
800
|
-
// ---------------------------------------------------------------------------
|
|
801
|
-
function passCasing(lines, casing, userFuncMap) {
|
|
802
|
-
return lines.map(line => {
|
|
803
|
-
const trimmed = line.trim();
|
|
804
|
-
if (trimmed === '' || trimmed.startsWith("'") || /^rem\b/i.test(trimmed))
|
|
805
|
-
return line;
|
|
806
|
-
const indent = line.match(/^(\s*)/)?.[1] ?? '';
|
|
807
|
-
return indent + applyCasingToLine(trimmed, casing, userFuncMap);
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
625
|
// ---------------------------------------------------------------------------
|
|
811
626
|
// Pass 7b — Split array open bracket
|
|
812
627
|
// ---------------------------------------------------------------------------
|
|
@@ -1290,14 +1105,6 @@ function passEmptyLinesBetweenMethods(lines, config) {
|
|
|
1290
1105
|
return out;
|
|
1291
1106
|
}
|
|
1292
1107
|
// ---------------------------------------------------------------------------
|
|
1293
|
-
// Pass 10 — Trim trailing whitespace
|
|
1294
|
-
// ---------------------------------------------------------------------------
|
|
1295
|
-
function passTrimTrailing(lines, config) {
|
|
1296
|
-
if (!config.trimTrailingWhitespace)
|
|
1297
|
-
return lines;
|
|
1298
|
-
return lines.map(line => line.trimEnd());
|
|
1299
|
-
}
|
|
1300
|
-
// ---------------------------------------------------------------------------
|
|
1301
1108
|
// Pass 11 — Comment width
|
|
1302
1109
|
// ---------------------------------------------------------------------------
|
|
1303
1110
|
function passCommentWidth(lines, config) {
|
|
@@ -1998,22 +1805,6 @@ function isReturnAloneInBlock(lines, returnIdx) {
|
|
|
1998
1805
|
}
|
|
1999
1806
|
return true;
|
|
2000
1807
|
}
|
|
2001
|
-
// ---------------------------------------------------------------------------
|
|
2002
|
-
// Casing helpers
|
|
2003
|
-
// ---------------------------------------------------------------------------
|
|
2004
|
-
function applyCasingToLine(line, casing, userFuncMap) {
|
|
2005
|
-
const segments = splitCodeSegments(line);
|
|
2006
|
-
let result = '';
|
|
2007
|
-
for (const seg of segments) {
|
|
2008
|
-
if (seg.isCode) {
|
|
2009
|
-
result += transformCodeSegment(seg.text, casing, userFuncMap);
|
|
2010
|
-
}
|
|
2011
|
-
else {
|
|
2012
|
-
result += seg.text;
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
|
-
return result;
|
|
2016
|
-
}
|
|
2017
1808
|
function splitCodeSegments(line) {
|
|
2018
1809
|
const segments = [];
|
|
2019
1810
|
let current = '';
|
|
@@ -2053,54 +1844,4 @@ function splitCodeSegments(line) {
|
|
|
2053
1844
|
}
|
|
2054
1845
|
return segments;
|
|
2055
1846
|
}
|
|
2056
|
-
function transformCodeSegment(code, casing, userFuncMap) {
|
|
2057
|
-
const exactMap = casing.exact ?? {};
|
|
2058
|
-
const userFuncCasing = casing.userFunction ?? 'preserve';
|
|
2059
|
-
return code.replace(/\b([a-zA-Z_]\w*)\b/g, (match, _group, offset) => {
|
|
2060
|
-
const afterIdx = offset + match.length;
|
|
2061
|
-
const restAfter = code.slice(afterIdx);
|
|
2062
|
-
if (/^\s*:/.test(restAfter))
|
|
2063
|
-
return match;
|
|
2064
|
-
if (offset > 0 && code[offset - 1] === '.')
|
|
2065
|
-
return match;
|
|
2066
|
-
const lower = match.toLowerCase();
|
|
2067
|
-
const exact = Object.prototype.hasOwnProperty.call(exactMap, lower) ? exactMap[lower] : undefined;
|
|
2068
|
-
if (exact !== undefined)
|
|
2069
|
-
return exact;
|
|
2070
|
-
if (_keywordSet.has(lower)) {
|
|
2071
|
-
let category = (0, builtins_1.getKeywordCategory)(lower);
|
|
2072
|
-
if (lower === 'function' && offset >= 3) {
|
|
2073
|
-
const before = code.slice(Math.max(0, offset - 10), offset);
|
|
2074
|
-
if (/\bas\s+$/i.test(before)) {
|
|
2075
|
-
category = 'type';
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
const effectiveCasing = (0, casing_1.resolveKeywordCasing)(category, casing);
|
|
2079
|
-
if (effectiveCasing !== 'preserve') {
|
|
2080
|
-
return (0, casing_1.applyCasingWithOverrides)(match, effectiveCasing, exactMap);
|
|
2081
|
-
}
|
|
2082
|
-
return match;
|
|
2083
|
-
}
|
|
2084
|
-
if (_builtinMap.has(lower)) {
|
|
2085
|
-
// Only treat as a built-in function when it is actually being called.
|
|
2086
|
-
// Identifiers that happen to share a name with a built-in (e.g. a
|
|
2087
|
-
// parameter named `str`) should not be re-cased.
|
|
2088
|
-
if (!/^\s*\(/.test(restAfter))
|
|
2089
|
-
return match;
|
|
2090
|
-
const canonical = _builtinMap.get(lower);
|
|
2091
|
-
if (casing.builtin !== 'preserve') {
|
|
2092
|
-
return (0, casing_1.applyCasingWithOverrides)(canonical, casing.builtin, exactMap);
|
|
2093
|
-
}
|
|
2094
|
-
return canonical;
|
|
2095
|
-
}
|
|
2096
|
-
if (userFuncMap.has(lower)) {
|
|
2097
|
-
const definitionName = userFuncMap.get(lower);
|
|
2098
|
-
if (userFuncCasing !== 'preserve') {
|
|
2099
|
-
return (0, casing_1.applyCasing)(definitionName, userFuncCasing);
|
|
2100
|
-
}
|
|
2101
|
-
return definitionName;
|
|
2102
|
-
}
|
|
2103
|
-
return match;
|
|
2104
|
-
});
|
|
2105
|
-
}
|
|
2106
1847
|
//# sourceMappingURL=formatter.js.map
|