@phoenix-wing/code-core 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +4 -0
- package/README.md +14 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/pnwCaaDialogHandoff.d.ts +17 -0
- package/dist/pnwCaaDialogHandoff.d.ts.map +1 -0
- package/dist/pnwCaaDialogHandoff.js +34 -0
- package/dist/pnwCaaDialogHandoff.js.map +1 -0
- package/dist/pnwCaaEnvironment.d.ts +17 -0
- package/dist/pnwCaaEnvironment.d.ts.map +1 -0
- package/dist/pnwCaaEnvironment.js +23 -0
- package/dist/pnwCaaEnvironment.js.map +1 -0
- package/dist/pnwCodeRenamePlan.d.ts +47 -0
- package/dist/pnwCodeRenamePlan.d.ts.map +1 -0
- package/dist/pnwCodeRenamePlan.js +105 -0
- package/dist/pnwCodeRenamePlan.js.map +1 -0
- package/dist/pnwFileResultTree.d.ts +25 -0
- package/dist/pnwFileResultTree.d.ts.map +1 -0
- package/dist/pnwFileResultTree.js +37 -0
- package/dist/pnwFileResultTree.js.map +1 -0
- package/dist/pnwReorderCpp.d.ts +7 -0
- package/dist/pnwReorderCpp.d.ts.map +1 -0
- package/dist/pnwReorderCpp.js +389 -0
- package/dist/pnwReorderCpp.js.map +1 -0
- package/dist/pnwReorderHeader.d.ts +21 -0
- package/dist/pnwReorderHeader.d.ts.map +1 -0
- package/dist/pnwReorderHeader.js +896 -0
- package/dist/pnwReorderHeader.js.map +1 -0
- package/dist/pnwUuid.d.ts +25 -0
- package/dist/pnwUuid.d.ts.map +1 -0
- package/dist/pnwUuid.js +140 -0
- package/dist/pnwUuid.js.map +1 -0
- package/dist/pnwUuidReplacementPlan.d.ts +57 -0
- package/dist/pnwUuidReplacementPlan.d.ts.map +1 -0
- package/dist/pnwUuidReplacementPlan.js +101 -0
- package/dist/pnwUuidReplacementPlan.js.map +1 -0
- package/dist/pnwWorkset.d.ts +20 -0
- package/dist/pnwWorkset.d.ts.map +1 -0
- package/dist/pnwWorkset.js +51 -0
- package/dist/pnwWorkset.js.map +1 -0
- package/package.json +50 -0
- package/src/fixtures/annotation_sep.cpp +27 -0
- package/src/fixtures/caa2_wizard_section.h +17 -0
- package/src/fixtures/clang_format_class_tail.h +23 -0
- package/src/fixtures/clang_format_doubled.h +16 -0
- package/src/fixtures/clang_format_nested_markers.h +24 -0
- package/src/fixtures/clang_format_off_on.h +18 -0
- package/src/fixtures/internal_calls.cpp +29 -0
- package/src/fixtures/kevin_wizard_section.h +23 -0
- package/src/fixtures/ktalarmclock.clang-format.yaml +57 -0
- package/src/fixtures/ktca_KTCAutoCodeFrm_KTCAutoCodeUI.m_src_KTCAutoDialog.cpp +242 -0
- package/src/fixtures/ktca_KTCAutoCodeFrm_PublicInterfaces_KTCAutoDialog.h +165 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_KTCAutoCodeItf.m_src_KTCAutoAttrAccess.cpp +423 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_KTCAutoCodeItf.m_src_KTCAutoBuildGSM.cpp +105 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_KTCAutoCodeItf.m_src_KTCAutoPartDoc.cpp +121 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_PublicInterfaces_KTCAutoAttrAccess.h +310 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_PublicInterfaces_KTCAutoBuildGSM.h +56 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_PublicInterfaces_KTCAutoCatalogParam.h +52 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_PublicInterfaces_KTCAutoGSM.h +91 -0
- package/src/fixtures/ktca_KTCAutoCodeInterfaces_PublicInterfaces_KTCAutoPartDoc.h +106 -0
- package/src/fixtures/pnx_bom_analysis_cmd_min.cpp +72 -0
- package/src/fixtures/pnx_bom_analysis_param_min.h +49 -0
- package/src/fixtures/pnx_bom_param_system_split.h +39 -0
- package/src/fixtures/pnx_caa2_widget_decl.h +19 -0
- package/src/fixtures/pragma_region.h +18 -0
- package/src/index.ts +78 -0
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
const LOCKED_RULES = [
|
|
2
|
+
{
|
|
3
|
+
id: "clang-format",
|
|
4
|
+
start: /^\s*\/\/\s*clang-format\s+off\s*(?:\/\/.*)?$/,
|
|
5
|
+
end: /^\s*\/\/\s*clang-format\s+on\s*(?:\/\/.*)?$/,
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
id: "kevin-caa-wizard",
|
|
9
|
+
start: /^\s*\/\/\s*START\s+KEVIN\s+CAA\s+WIZARD\s+SECTION\b/i,
|
|
10
|
+
end: /^\s*\/\/\s*END\s+KEVIN\s+CAA\s+WIZARD\s+SECTION\b/i,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "caa2-wizard",
|
|
14
|
+
start: /^\s*\/\/\s*CAA2\s+WIZARD\b/,
|
|
15
|
+
end: /^\s*\/\/\s*END\s+CAA2\s+WIZARD\b/,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "pragma-region",
|
|
19
|
+
start: /^\s*#\s*pragma\s+region\b/,
|
|
20
|
+
end: /^\s*#\s*pragma\s+endregion\b/,
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
const ACCESS_RE = /^([ \t]*)((?:public|private|protected)(?:[ \t]+slots)?|signals)[ \t]*:[ \t]*(?:\/\/[^\n]*)?[ \t]*$/gm;
|
|
24
|
+
const CLASS_RE = /\bclass\s+((?:[A-Za-z_]\w*\s+)*?[A-Za-z_]\w*)\s*(?=final\b|:|\{)(?:final\s*)?(?::\s*[^{;]+)?\s*\{/g;
|
|
25
|
+
const DOXYGEN_GROUP_OPEN_RE = /^\s*\/\/\/@\{/;
|
|
26
|
+
const DOXYGEN_GROUP_CLOSE_RE = /^\s*\/\/\/@\}/;
|
|
27
|
+
const KEVIN_SYSTEM_CODE_START_RE = /^\s*\/\/\s*KEVIN_SYSTEM_CODE\s+START\b/;
|
|
28
|
+
const KEVIN_SYSTEM_CODE_END_RE = /^\s*\/\/\s*KEVIN_SYSTEM_CODE\s+END\b/;
|
|
29
|
+
const KEVIN_SYSTEM_CODE_NOTE_RE = /@note\s+KEVIN_SYSTEM_CODE\b/;
|
|
30
|
+
const NUMBERED_DOXY_ITEM_RE = /\/\*\*\s*\d+\.|^\s*\*\s+\d+\./m;
|
|
31
|
+
const CPP_KEYWORDS = new Set([
|
|
32
|
+
"if", "for", "while", "switch", "return", "explicit", "virtual", "static", "constexpr", "inline",
|
|
33
|
+
"friend", "const", "volatile", "noexcept", "override", "final", "mutable", "thread_local", "extern",
|
|
34
|
+
"typedef", "typename", "class", "struct", "enum", "union", "template", "decltype", "consteval",
|
|
35
|
+
"concept", "requires", "operator", "using",
|
|
36
|
+
]);
|
|
37
|
+
function splitLinesKeepEnds(text) {
|
|
38
|
+
const out = [];
|
|
39
|
+
const re = /[^\r\n]*(?:\r\n|\r|\n|$)/g;
|
|
40
|
+
let match;
|
|
41
|
+
while ((match = re.exec(text))) {
|
|
42
|
+
if (!match[0])
|
|
43
|
+
break;
|
|
44
|
+
out.push(match[0]);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function matchRule(line, side) {
|
|
49
|
+
const content = line.replace(/[\r\n]+$/, "");
|
|
50
|
+
return LOCKED_RULES.find((rule) => rule[side].test(content));
|
|
51
|
+
}
|
|
52
|
+
function mergedRuleId(left, right) {
|
|
53
|
+
if (left.ruleId === right.ruleId)
|
|
54
|
+
return left.ruleId;
|
|
55
|
+
if (left.ruleId === "clang-format" || right.ruleId === "clang-format")
|
|
56
|
+
return "clang-format";
|
|
57
|
+
const outer = left.start <= right.start ? left : right;
|
|
58
|
+
const inner = outer === left ? right : left;
|
|
59
|
+
return inner.start >= outer.start && inner.end <= outer.end ? outer.ruleId : "mixed";
|
|
60
|
+
}
|
|
61
|
+
export function pnwFindLockedRegions(text) {
|
|
62
|
+
const raw = [];
|
|
63
|
+
const pending = [];
|
|
64
|
+
let offset = 0;
|
|
65
|
+
for (const line of splitLinesKeepEnds(text)) {
|
|
66
|
+
const lineStart = offset;
|
|
67
|
+
const lineEnd = offset + line.length;
|
|
68
|
+
const endRule = matchRule(line, "end");
|
|
69
|
+
if (endRule) {
|
|
70
|
+
for (let i = pending.length - 1; i >= 0; i -= 1) {
|
|
71
|
+
if (pending[i].id !== endRule.id)
|
|
72
|
+
continue;
|
|
73
|
+
const opened = pending.splice(i, 1)[0];
|
|
74
|
+
raw.push({ start: opened.start, end: lineEnd, content: text.slice(opened.start, lineEnd), ruleId: endRule.id });
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const startRule = matchRule(line, "start");
|
|
80
|
+
if (startRule)
|
|
81
|
+
pending.push({ id: startRule.id, start: lineStart });
|
|
82
|
+
}
|
|
83
|
+
offset = lineEnd;
|
|
84
|
+
}
|
|
85
|
+
const ordered = raw.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
86
|
+
const merged = [];
|
|
87
|
+
for (const span of ordered) {
|
|
88
|
+
const previous = merged[merged.length - 1];
|
|
89
|
+
if (!previous || span.start > previous.end) {
|
|
90
|
+
merged.push(span);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const end = Math.max(previous.end, span.end);
|
|
94
|
+
merged[merged.length - 1] = {
|
|
95
|
+
start: previous.start,
|
|
96
|
+
end,
|
|
97
|
+
content: text.slice(previous.start, end),
|
|
98
|
+
ruleId: mergedRuleId(previous, span),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return merged;
|
|
102
|
+
}
|
|
103
|
+
export function pnwExtractLockedRegionContents(text) {
|
|
104
|
+
return pnwFindLockedRegions(text).map((span) => span.content);
|
|
105
|
+
}
|
|
106
|
+
function splitByLockedRegions(text) {
|
|
107
|
+
const spans = pnwFindLockedRegions(text);
|
|
108
|
+
if (!spans.length)
|
|
109
|
+
return [{ text, locked: false }];
|
|
110
|
+
const out = [];
|
|
111
|
+
let pos = 0;
|
|
112
|
+
for (const span of spans) {
|
|
113
|
+
if (span.start > pos)
|
|
114
|
+
out.push({ text: text.slice(pos, span.start), locked: false });
|
|
115
|
+
out.push({ text: span.content, locked: true });
|
|
116
|
+
pos = span.end;
|
|
117
|
+
}
|
|
118
|
+
if (pos < text.length)
|
|
119
|
+
out.push({ text: text.slice(pos), locked: false });
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
function normalizeMode(raw) {
|
|
123
|
+
return raw === "keep_markers" || raw === "off" || raw === "merge_strip" ? raw : "merge_strip";
|
|
124
|
+
}
|
|
125
|
+
function normalizeAccessLabel(raw) {
|
|
126
|
+
return `${raw.trim().replace(/:$/, "")}:`;
|
|
127
|
+
}
|
|
128
|
+
function isClassBodyMacro(stripped) {
|
|
129
|
+
return stripped.startsWith("Q_") || stripped.startsWith("friend ") || /^[A-Z][A-Z0-9_]*.*(?:DECLARE|IMPLEMENT)\w*\s*\(/.test(stripped);
|
|
130
|
+
}
|
|
131
|
+
function stripComments(text) {
|
|
132
|
+
return text
|
|
133
|
+
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
|
134
|
+
.replace(/\/\/[^\r\n]*/g, " ")
|
|
135
|
+
.split(/\r?\n/)
|
|
136
|
+
.map((line) => line.trim())
|
|
137
|
+
.filter(Boolean)
|
|
138
|
+
.join(" ");
|
|
139
|
+
}
|
|
140
|
+
function declarationSignature(decl) {
|
|
141
|
+
const sig = stripComments(decl);
|
|
142
|
+
const brace = sig.indexOf("{");
|
|
143
|
+
if (brace < 0)
|
|
144
|
+
return sig;
|
|
145
|
+
const before = sig.slice(0, brace).trimEnd();
|
|
146
|
+
if (/\w_\s*$/.test(before) && /\{[^}]*\}\s*;/.test(sig.slice(brace)))
|
|
147
|
+
return sig;
|
|
148
|
+
return before;
|
|
149
|
+
}
|
|
150
|
+
function firstTopLevelParen(sig) {
|
|
151
|
+
let angle = 0;
|
|
152
|
+
for (let i = 0; i < sig.length; i += 1) {
|
|
153
|
+
if (sig[i] === "<")
|
|
154
|
+
angle += 1;
|
|
155
|
+
else if (sig[i] === ">")
|
|
156
|
+
angle = Math.max(0, angle - 1);
|
|
157
|
+
else if (sig[i] === "(" && angle === 0)
|
|
158
|
+
return i;
|
|
159
|
+
}
|
|
160
|
+
return -1;
|
|
161
|
+
}
|
|
162
|
+
function isDoxygenGroupBlock(text) {
|
|
163
|
+
let hasOpen = false;
|
|
164
|
+
let hasClose = false;
|
|
165
|
+
for (const line of splitLinesKeepEnds(text)) {
|
|
166
|
+
if (DOXYGEN_GROUP_OPEN_RE.test(line))
|
|
167
|
+
hasOpen = true;
|
|
168
|
+
if (DOXYGEN_GROUP_CLOSE_RE.test(line))
|
|
169
|
+
hasClose = true;
|
|
170
|
+
}
|
|
171
|
+
return hasOpen && hasClose;
|
|
172
|
+
}
|
|
173
|
+
function parseMemberName(decl) {
|
|
174
|
+
const sig = declarationSignature(decl) || stripComments(decl);
|
|
175
|
+
return (sig.match(/(\w+_)\s*\{[^}]*\}\s*;/)?.[1] ||
|
|
176
|
+
sig.match(/(\w+_)\s*(?:\/\/\/<|;)/)?.[1] ||
|
|
177
|
+
sig.match(/(\w+)\s*(?:\/\/\/<|;)/)?.[1] ||
|
|
178
|
+
sig.slice(0, 30));
|
|
179
|
+
}
|
|
180
|
+
function parseMethodName(decl) {
|
|
181
|
+
const sig = declarationSignature(decl) || decl.replace(/\s+/g, " ").slice(0, 200);
|
|
182
|
+
if (sig.includes("operator="))
|
|
183
|
+
return "operator=";
|
|
184
|
+
const op = sig.match(/\boperator\s*[^\s(]+\s*\(/);
|
|
185
|
+
if (op)
|
|
186
|
+
return op[0].split("(")[0].trim();
|
|
187
|
+
const paren = firstTopLevelParen(sig);
|
|
188
|
+
if (paren < 0)
|
|
189
|
+
return sig.slice(0, 40);
|
|
190
|
+
const name = sig.slice(0, paren).trimEnd().match(/([A-Za-z_]\w*)\s*$/)?.[1];
|
|
191
|
+
return name && !CPP_KEYWORDS.has(name) ? name : sig.slice(0, 40);
|
|
192
|
+
}
|
|
193
|
+
function parseSpecialName(decl, className) {
|
|
194
|
+
if (decl.includes(`~${className}`))
|
|
195
|
+
return `~${className}`;
|
|
196
|
+
if (decl.includes("operator="))
|
|
197
|
+
return "operator=";
|
|
198
|
+
if (new RegExp(`\\b${escapeRegExp(className)}\\s*\\(`).test(decl))
|
|
199
|
+
return className;
|
|
200
|
+
return decl.match(/operator\s*\S+\s*\(/)?.[0].split("(")[0].trim() || decl.slice(0, 30);
|
|
201
|
+
}
|
|
202
|
+
function firstMethodNameInBlock(text, className) {
|
|
203
|
+
for (const line of text.split(/\r?\n/)) {
|
|
204
|
+
const stripped = line.trim();
|
|
205
|
+
if (!stripped || stripped.startsWith("/**") || stripped.startsWith("*") || stripped.startsWith("//") || !stripped.includes("("))
|
|
206
|
+
continue;
|
|
207
|
+
const item = classifyDeclaration(stripped, className);
|
|
208
|
+
if ((item.kind === "method" || item.kind === "special") && item.name)
|
|
209
|
+
return item.name;
|
|
210
|
+
}
|
|
211
|
+
return "group";
|
|
212
|
+
}
|
|
213
|
+
function classifyDeclaration(text, className) {
|
|
214
|
+
const stripped = text.trim();
|
|
215
|
+
const item = { text, name: "", kind: "other", trailingGap: false };
|
|
216
|
+
if (!stripped)
|
|
217
|
+
return item;
|
|
218
|
+
if (isClassBodyMacro(stripped))
|
|
219
|
+
return { ...item, kind: "macro", name: stripped };
|
|
220
|
+
if (isDoxygenGroupBlock(text))
|
|
221
|
+
return { ...item, kind: "group", name: firstMethodNameInBlock(text, className) };
|
|
222
|
+
const sig = declarationSignature(text);
|
|
223
|
+
const code = stripComments(text);
|
|
224
|
+
const usingMatch = code.match(/\busing\s+([A-Za-z_]\w*)\s*=/);
|
|
225
|
+
if (usingMatch)
|
|
226
|
+
return { ...item, kind: "nested", name: usingMatch[1] };
|
|
227
|
+
const enumMatch = code.match(/\benum\s+(?:class\s+)?([A-Za-z_]\w*)/);
|
|
228
|
+
if (enumMatch && text.includes("{"))
|
|
229
|
+
return { ...item, kind: "nested", name: enumMatch[1] };
|
|
230
|
+
const structMatch = code.match(/\bstruct\s+([A-Za-z_]\w*)/);
|
|
231
|
+
if (structMatch && text.includes("{"))
|
|
232
|
+
return { ...item, kind: "nested", name: structMatch[1] };
|
|
233
|
+
if (sig.includes("= delete") || sig.includes("= default")) {
|
|
234
|
+
if (new RegExp(`\\b~?${escapeRegExp(className)}\\s*\\(`).test(sig) || sig.includes("operator=")) {
|
|
235
|
+
return { ...item, kind: "special", name: parseSpecialName(sig, className) };
|
|
236
|
+
}
|
|
237
|
+
return { ...item, kind: "method", name: parseMethodName(text) };
|
|
238
|
+
}
|
|
239
|
+
if (new RegExp(`\\b~?${escapeRegExp(className)}\\s*\\(`).test(sig)) {
|
|
240
|
+
return { ...item, kind: "special", name: parseSpecialName(sig, className) };
|
|
241
|
+
}
|
|
242
|
+
if (sig.includes("(")) {
|
|
243
|
+
const memberName = parseMemberName(text);
|
|
244
|
+
if (memberName.endsWith("_") && new RegExp(`\\b${escapeRegExp(memberName)}\\s*;`).test(sig) && !new RegExp(`\\b${escapeRegExp(memberName)}\\s*\\(`).test(sig)) {
|
|
245
|
+
return { ...item, kind: "member", name: memberName };
|
|
246
|
+
}
|
|
247
|
+
return { ...item, kind: "method", name: parseMethodName(text) };
|
|
248
|
+
}
|
|
249
|
+
if (sig.includes(";") && !sig.trimEnd().endsWith(");"))
|
|
250
|
+
return { ...item, kind: "member", name: parseMemberName(text) };
|
|
251
|
+
return { ...item, name: stripped.slice(0, 40) };
|
|
252
|
+
}
|
|
253
|
+
function escapeRegExp(value) {
|
|
254
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
255
|
+
}
|
|
256
|
+
function skipInterItemGap(chunk, start) {
|
|
257
|
+
let i = start;
|
|
258
|
+
while (i < chunk.length) {
|
|
259
|
+
let end = i;
|
|
260
|
+
while (end < chunk.length && chunk[end] !== "\r" && chunk[end] !== "\n")
|
|
261
|
+
end += 1;
|
|
262
|
+
if (chunk.slice(i, end).trim())
|
|
263
|
+
break;
|
|
264
|
+
i = end;
|
|
265
|
+
if (chunk[i] === "\r" && chunk[i + 1] === "\n")
|
|
266
|
+
i += 2;
|
|
267
|
+
else if (chunk[i] === "\r" || chunk[i] === "\n")
|
|
268
|
+
i += 1;
|
|
269
|
+
}
|
|
270
|
+
return i;
|
|
271
|
+
}
|
|
272
|
+
function stripOrphanGroupMarkers(text) {
|
|
273
|
+
return splitLinesKeepEnds(text).filter((line) => !DOXYGEN_GROUP_OPEN_RE.test(line) && !DOXYGEN_GROUP_CLOSE_RE.test(line)).join("");
|
|
274
|
+
}
|
|
275
|
+
function splitDeclarationRun(input, className) {
|
|
276
|
+
const chunk = stripOrphanGroupMarkers(input);
|
|
277
|
+
if (!chunk.trim())
|
|
278
|
+
return [];
|
|
279
|
+
const items = [];
|
|
280
|
+
let i = 0;
|
|
281
|
+
while (i < chunk.length) {
|
|
282
|
+
i = skipInterItemGap(chunk, i);
|
|
283
|
+
if (i >= chunk.length)
|
|
284
|
+
break;
|
|
285
|
+
const itemStart = i;
|
|
286
|
+
const firstLineEnd = chunk.indexOf("\n", i) < 0 ? chunk.length : chunk.indexOf("\n", i) + 1;
|
|
287
|
+
const firstLine = chunk.slice(i, firstLineEnd);
|
|
288
|
+
if ((firstLine.trimStart().startsWith("#") || isClassBodyMacro(firstLine.trim())) && !firstLine.includes(";")) {
|
|
289
|
+
const classified = classifyDeclaration(firstLine, className);
|
|
290
|
+
items.push(classified);
|
|
291
|
+
i = firstLineEnd;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
let brace = 0;
|
|
295
|
+
let paren = 0;
|
|
296
|
+
let hadBody = false;
|
|
297
|
+
let lineComment = false;
|
|
298
|
+
let blockComment = false;
|
|
299
|
+
let quote = null;
|
|
300
|
+
while (i < chunk.length) {
|
|
301
|
+
const ch = chunk[i];
|
|
302
|
+
const next = chunk[i + 1];
|
|
303
|
+
if (lineComment) {
|
|
304
|
+
if (ch === "\r" || ch === "\n")
|
|
305
|
+
lineComment = false;
|
|
306
|
+
i += 1;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (blockComment) {
|
|
310
|
+
if (ch === "*" && next === "/") {
|
|
311
|
+
blockComment = false;
|
|
312
|
+
i += 2;
|
|
313
|
+
}
|
|
314
|
+
else
|
|
315
|
+
i += 1;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (quote) {
|
|
319
|
+
if (ch === "\\")
|
|
320
|
+
i += 2;
|
|
321
|
+
else {
|
|
322
|
+
if (ch === quote)
|
|
323
|
+
quote = null;
|
|
324
|
+
i += 1;
|
|
325
|
+
}
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (ch === "/" && next === "/") {
|
|
329
|
+
lineComment = true;
|
|
330
|
+
i += 2;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (ch === "/" && next === "*") {
|
|
334
|
+
blockComment = true;
|
|
335
|
+
i += 2;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (ch === "\"" || ch === "'") {
|
|
339
|
+
quote = ch;
|
|
340
|
+
i += 1;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (ch === "(")
|
|
344
|
+
paren += 1;
|
|
345
|
+
else if (ch === ")")
|
|
346
|
+
paren = Math.max(0, paren - 1);
|
|
347
|
+
else if (ch === "{" && paren === 0) {
|
|
348
|
+
brace += 1;
|
|
349
|
+
if (brace === 1)
|
|
350
|
+
hadBody = true;
|
|
351
|
+
}
|
|
352
|
+
else if (ch === "}" && paren === 0) {
|
|
353
|
+
brace -= 1;
|
|
354
|
+
if (brace === 0 && hadBody) {
|
|
355
|
+
let j = i + 1;
|
|
356
|
+
while (j < chunk.length && /\s/.test(chunk[j]))
|
|
357
|
+
j += 1;
|
|
358
|
+
if (chunk[j] === ";")
|
|
359
|
+
hadBody = false;
|
|
360
|
+
else {
|
|
361
|
+
i += 1;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else if (ch === ";" && brace === 0 && paren === 0) {
|
|
367
|
+
i += 1;
|
|
368
|
+
while (i < chunk.length && chunk[i] !== "\r" && chunk[i] !== "\n")
|
|
369
|
+
i += 1;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
i += 1;
|
|
373
|
+
}
|
|
374
|
+
const raw = chunk.slice(itemStart, i);
|
|
375
|
+
if (!raw.trim())
|
|
376
|
+
continue;
|
|
377
|
+
const nextStart = skipInterItemGap(chunk, i);
|
|
378
|
+
const trailingGap = /\r?\n\s*\r?\n/.test(chunk.slice(i, nextStart));
|
|
379
|
+
items.push({ ...classifyDeclaration(raw, className), trailingGap });
|
|
380
|
+
i = nextStart;
|
|
381
|
+
}
|
|
382
|
+
return items;
|
|
383
|
+
}
|
|
384
|
+
function splitByDoxygenGroups(chunk) {
|
|
385
|
+
const lines = splitLinesKeepEnds(chunk);
|
|
386
|
+
const segments = [];
|
|
387
|
+
let buffer = [];
|
|
388
|
+
let pendingName = [];
|
|
389
|
+
let inGroup = false;
|
|
390
|
+
const flush = () => {
|
|
391
|
+
if (buffer.length)
|
|
392
|
+
segments.push(buffer.join(""));
|
|
393
|
+
buffer = [];
|
|
394
|
+
};
|
|
395
|
+
let i = 0;
|
|
396
|
+
while (i < lines.length) {
|
|
397
|
+
const line = lines[i];
|
|
398
|
+
const stripped = line.trim();
|
|
399
|
+
if (!inGroup && stripped.startsWith("/**")) {
|
|
400
|
+
pendingName = [line];
|
|
401
|
+
i += 1;
|
|
402
|
+
while (i < lines.length) {
|
|
403
|
+
if (DOXYGEN_GROUP_OPEN_RE.test(lines[i])) {
|
|
404
|
+
flush();
|
|
405
|
+
inGroup = true;
|
|
406
|
+
buffer = [...pendingName, lines[i]];
|
|
407
|
+
pendingName = [];
|
|
408
|
+
i += 1;
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
const current = lines[i].trim();
|
|
412
|
+
if (current && !current.startsWith("*") && !current.startsWith("/**")) {
|
|
413
|
+
buffer.push(...pendingName);
|
|
414
|
+
pendingName = [];
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
pendingName.push(lines[i]);
|
|
418
|
+
i += 1;
|
|
419
|
+
}
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (DOXYGEN_GROUP_OPEN_RE.test(line)) {
|
|
423
|
+
flush();
|
|
424
|
+
inGroup = true;
|
|
425
|
+
buffer = [line];
|
|
426
|
+
i += 1;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (inGroup && DOXYGEN_GROUP_CLOSE_RE.test(line)) {
|
|
430
|
+
buffer.push(line);
|
|
431
|
+
flush();
|
|
432
|
+
inGroup = false;
|
|
433
|
+
i += 1;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (pendingName.length) {
|
|
437
|
+
buffer.push(...pendingName);
|
|
438
|
+
pendingName = [];
|
|
439
|
+
}
|
|
440
|
+
buffer.push(line);
|
|
441
|
+
i += 1;
|
|
442
|
+
}
|
|
443
|
+
if (pendingName.length)
|
|
444
|
+
buffer.push(...pendingName);
|
|
445
|
+
flush();
|
|
446
|
+
return segments;
|
|
447
|
+
}
|
|
448
|
+
function splitDoxygenAndDeclarations(chunk, className) {
|
|
449
|
+
return splitByDoxygenGroups(chunk).flatMap((part) => {
|
|
450
|
+
if (isDoxygenGroupBlock(part))
|
|
451
|
+
return [{ ...classifyDeclaration(part, className), trailingGap: false }];
|
|
452
|
+
return splitDeclarationRun(part, className);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
function compareNames(a, b) {
|
|
456
|
+
return (a.name || a.text).localeCompare(b.name || b.text, "en", { sensitivity: "base" });
|
|
457
|
+
}
|
|
458
|
+
function specialRank(className, item) {
|
|
459
|
+
const flat = stripComments(item.text).replace(/\s+/g, " ");
|
|
460
|
+
if (item.name === `~${className}`)
|
|
461
|
+
return 2;
|
|
462
|
+
if (item.name.startsWith("operator")) {
|
|
463
|
+
if (item.name === "operator=")
|
|
464
|
+
return /operator\s*=\s*\([^)]*&&/.test(flat) ? 6 : 5;
|
|
465
|
+
return 7;
|
|
466
|
+
}
|
|
467
|
+
if (item.name === className) {
|
|
468
|
+
const escaped = escapeRegExp(className);
|
|
469
|
+
if (new RegExp(`\\b${escaped}\\s*\\(\\s*const\\s+${escaped}\\s*&`).test(flat))
|
|
470
|
+
return 3;
|
|
471
|
+
if (new RegExp(`\\b${escaped}\\s*\\(\\s*${escaped}\\s*&&`).test(flat))
|
|
472
|
+
return 4;
|
|
473
|
+
if (new RegExp(`\\b${escaped}\\s*\\(\\s*\\)`).test(flat))
|
|
474
|
+
return 0;
|
|
475
|
+
return 1;
|
|
476
|
+
}
|
|
477
|
+
return 99;
|
|
478
|
+
}
|
|
479
|
+
function sortableRank(item) {
|
|
480
|
+
return item.kind === "group" || item.kind === "method" || item.kind === "other" || item.kind === "macro" ? 0 : 99;
|
|
481
|
+
}
|
|
482
|
+
function sortPool(items, className) {
|
|
483
|
+
const specials = items.filter((item) => item.kind === "special");
|
|
484
|
+
const rest = items.filter((item) => item.kind !== "special");
|
|
485
|
+
specials.sort((a, b) => specialRank(className, a) - specialRank(className, b) || compareNames(a, b));
|
|
486
|
+
rest.sort((a, b) => sortableRank(a) - sortableRank(b) || compareNames(a, b));
|
|
487
|
+
return [...specials, ...rest];
|
|
488
|
+
}
|
|
489
|
+
function joinItems(items) {
|
|
490
|
+
return items.map((item, index) => emitItem(item, index < items.length - 1 && item.trailingGap)).join("");
|
|
491
|
+
}
|
|
492
|
+
function splitNonLockedChunk(chunk, className, mode) {
|
|
493
|
+
if (!chunk.trim())
|
|
494
|
+
return [];
|
|
495
|
+
if (mode === "off")
|
|
496
|
+
return splitDoxygenAndDeclarations(chunk, className);
|
|
497
|
+
const items = [];
|
|
498
|
+
const lines = splitLinesKeepEnds(chunk);
|
|
499
|
+
let buffer = [];
|
|
500
|
+
const flush = () => {
|
|
501
|
+
if (buffer.length)
|
|
502
|
+
items.push(...splitDoxygenAndDeclarations(buffer.join(""), className));
|
|
503
|
+
buffer = [];
|
|
504
|
+
};
|
|
505
|
+
let i = 0;
|
|
506
|
+
while (i < lines.length) {
|
|
507
|
+
if (!KEVIN_SYSTEM_CODE_START_RE.test(lines[i])) {
|
|
508
|
+
buffer.push(lines[i]);
|
|
509
|
+
i += 1;
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
flush();
|
|
513
|
+
const startLine = lines[i];
|
|
514
|
+
const inner = [];
|
|
515
|
+
i += 1;
|
|
516
|
+
let endLine = "";
|
|
517
|
+
while (i < lines.length) {
|
|
518
|
+
if (KEVIN_SYSTEM_CODE_END_RE.test(lines[i])) {
|
|
519
|
+
endLine = lines[i];
|
|
520
|
+
i += 1;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
inner.push(lines[i]);
|
|
524
|
+
i += 1;
|
|
525
|
+
}
|
|
526
|
+
if (!endLine) {
|
|
527
|
+
buffer.push(startLine, ...inner);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const sorted = sortPool(splitDeclarationRun(inner.join(""), className), className);
|
|
531
|
+
let innerText = joinItems(sorted);
|
|
532
|
+
if (innerText && !innerText.endsWith("\n"))
|
|
533
|
+
innerText += "\n";
|
|
534
|
+
items.push({
|
|
535
|
+
text: mode === "keep_markers" ? `${startLine}${innerText}${endLine}` : innerText,
|
|
536
|
+
name: "kevin_system_code",
|
|
537
|
+
kind: "system_code",
|
|
538
|
+
trailingGap: false,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
flush();
|
|
542
|
+
return items;
|
|
543
|
+
}
|
|
544
|
+
function splitDeclarations(chunk, className, mode) {
|
|
545
|
+
const items = [];
|
|
546
|
+
const parts = splitByLockedRegions(chunk);
|
|
547
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
548
|
+
const part = parts[i];
|
|
549
|
+
if (part.locked) {
|
|
550
|
+
if (part.text.trim()) {
|
|
551
|
+
items.push({
|
|
552
|
+
text: part.text,
|
|
553
|
+
name: "locked",
|
|
554
|
+
kind: "locked",
|
|
555
|
+
trailingGap: i + 1 < parts.length && /^[ \t]*\r?\n/.test(parts[i + 1].text),
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
let text = part.text;
|
|
561
|
+
if (items.at(-1)?.kind === "locked" && /^[ \t]*\r?\n/.test(text))
|
|
562
|
+
text = text.replace(/^[ \t]*\r?\n/, "");
|
|
563
|
+
items.push(...splitNonLockedChunk(text, className, mode));
|
|
564
|
+
}
|
|
565
|
+
return items;
|
|
566
|
+
}
|
|
567
|
+
function splitClassSections(body, className, mode) {
|
|
568
|
+
const regex = new RegExp(ACCESS_RE.source, ACCESS_RE.flags);
|
|
569
|
+
const matches = [...body.matchAll(regex)];
|
|
570
|
+
if (!matches.length)
|
|
571
|
+
return { preamble: body, sections: [] };
|
|
572
|
+
const sections = matches.map((match, index) => {
|
|
573
|
+
const start = match.index + match[0].length;
|
|
574
|
+
const end = index + 1 < matches.length ? matches[index + 1].index : body.length;
|
|
575
|
+
const rawChunk = body.slice(start, end);
|
|
576
|
+
const trailingGap = /(?:\n[ \t]*){2,}$/.test(rawChunk);
|
|
577
|
+
const chunk = trailingGap ? rawChunk.replace(/(?:\n[ \t]*){2,}$/, "\n") : rawChunk;
|
|
578
|
+
return {
|
|
579
|
+
label: normalizeAccessLabel(match[2]),
|
|
580
|
+
indent: match[1],
|
|
581
|
+
labelText: match[0].trimEnd(),
|
|
582
|
+
items: splitDeclarations(chunk, className, mode),
|
|
583
|
+
trailingGap,
|
|
584
|
+
};
|
|
585
|
+
});
|
|
586
|
+
return { preamble: body.slice(0, matches[0].index), sections };
|
|
587
|
+
}
|
|
588
|
+
function sectionKind(label) {
|
|
589
|
+
const normalized = label.toLowerCase().replace(/:$/, "");
|
|
590
|
+
if (normalized === "signals")
|
|
591
|
+
return "signals";
|
|
592
|
+
if (normalized.includes("slots"))
|
|
593
|
+
return "slots";
|
|
594
|
+
if (normalized.startsWith("public"))
|
|
595
|
+
return "public";
|
|
596
|
+
if (normalized.startsWith("private"))
|
|
597
|
+
return "private";
|
|
598
|
+
if (normalized.startsWith("protected"))
|
|
599
|
+
return "protected";
|
|
600
|
+
return "other";
|
|
601
|
+
}
|
|
602
|
+
function isMemberOnly(section, className) {
|
|
603
|
+
return section.items.length > 0 && section.items.every((item) => classifyDeclaration(item.text, className).kind === "member");
|
|
604
|
+
}
|
|
605
|
+
function hasNumberedDoxygenSequence(section) {
|
|
606
|
+
return section.items.filter((item) => NUMBERED_DOXY_ITEM_RE.test(item.text)).length >= 2;
|
|
607
|
+
}
|
|
608
|
+
function collectWarnings(sections, className) {
|
|
609
|
+
const warnings = [];
|
|
610
|
+
for (const section of sections) {
|
|
611
|
+
if (hasNumberedDoxygenSequence(section)) {
|
|
612
|
+
warnings.push(`${className}: ${section.label} contains numbered Doxygen declarations; source order was preserved`);
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
const nested = section.items.filter((item) => item.kind === "nested");
|
|
616
|
+
if (nested.length) {
|
|
617
|
+
warnings.push(`${className}: ${section.label} contains nested declarations (${nested.slice(0, 3).map((item) => item.name).join(", ")}); source order was preserved`);
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
for (const group of section.items.filter((item) => item.kind === "group")) {
|
|
621
|
+
warnings.push(`${className}: ${section.label} contains a Doxygen group near ${group.name || "group"}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return warnings;
|
|
625
|
+
}
|
|
626
|
+
function declarationItemsFromChunk(chunk, className) {
|
|
627
|
+
const cleaned = splitLinesKeepEnds(chunk)
|
|
628
|
+
.filter((line) => !KEVIN_SYSTEM_CODE_START_RE.test(line) && !KEVIN_SYSTEM_CODE_END_RE.test(line))
|
|
629
|
+
.join("");
|
|
630
|
+
return cleaned.trim() ? splitDoxygenAndDeclarations(cleaned, className) : [];
|
|
631
|
+
}
|
|
632
|
+
function isStaticMethod(item, className) {
|
|
633
|
+
const classified = classifyDeclaration(item.text, className);
|
|
634
|
+
return classified.kind === "method" && /\bstatic\b/.test(stripComments(item.text));
|
|
635
|
+
}
|
|
636
|
+
function normalizeSystemCodeSections(sections, className) {
|
|
637
|
+
for (let i = 0; i < sections.length; i += 1) {
|
|
638
|
+
const section = sections[i];
|
|
639
|
+
if (!section.labelText.includes("KEVIN_SYSTEM_CODE"))
|
|
640
|
+
continue;
|
|
641
|
+
const fixed = [];
|
|
642
|
+
const pool = [];
|
|
643
|
+
for (const item of section.items) {
|
|
644
|
+
if (item.kind === "locked")
|
|
645
|
+
fixed.push(item);
|
|
646
|
+
else {
|
|
647
|
+
const expanded = declarationItemsFromChunk(item.text, className);
|
|
648
|
+
if (item.trailingGap && expanded.length)
|
|
649
|
+
expanded[expanded.length - 1].trailingGap = true;
|
|
650
|
+
pool.push(...expanded);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
const next = sections[i + 1];
|
|
654
|
+
if (next && /\bfunctions\b/i.test(next.labelText)) {
|
|
655
|
+
const keep = [];
|
|
656
|
+
for (const item of next.items) {
|
|
657
|
+
if (item.kind === "locked") {
|
|
658
|
+
keep.push(item);
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const classified = classifyDeclaration(item.text, className);
|
|
662
|
+
const preserved = { ...classified, trailingGap: item.trailingGap };
|
|
663
|
+
if (KEVIN_SYSTEM_CODE_NOTE_RE.test(item.text) || isStaticMethod(preserved, className))
|
|
664
|
+
pool.push(preserved);
|
|
665
|
+
else
|
|
666
|
+
keep.push(preserved);
|
|
667
|
+
}
|
|
668
|
+
next.items = keep;
|
|
669
|
+
if (!keep.length)
|
|
670
|
+
sections.splice(i + 1, 1);
|
|
671
|
+
}
|
|
672
|
+
section.items = fixed;
|
|
673
|
+
if (pool.length) {
|
|
674
|
+
section.items.push({ text: joinItems(sortPool(pool, className)), name: "kevin_system_code", kind: "system_code", trailingGap: false });
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function sortSection(section, className, sortMembers) {
|
|
679
|
+
section.items = section.items.map((item) => item.kind === "locked" || item.kind === "system_code" ? item : { ...classifyDeclaration(item.text, className), trailingGap: item.trailingGap });
|
|
680
|
+
if (isMemberOnly(section, className) || hasNumberedDoxygenSequence(section) || section.items.some((item) => item.kind === "nested"))
|
|
681
|
+
return;
|
|
682
|
+
const kind = sectionKind(section.label);
|
|
683
|
+
const fixedKinds = new Set(["member", "nested", "locked", "system_code"]);
|
|
684
|
+
if (kind === "signals" || kind === "slots") {
|
|
685
|
+
const movable = section.items.filter((item) => !fixedKinds.has(item.kind)).sort(compareNames);
|
|
686
|
+
let cursor = 0;
|
|
687
|
+
section.items = section.items.map((item) => fixedKinds.has(item.kind) ? item : movable[cursor++]);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (kind === "public" || kind === "private" || kind === "protected") {
|
|
691
|
+
const movable = sortPool(section.items.filter((item) => !fixedKinds.has(item.kind)), className);
|
|
692
|
+
const fixed = section.items.filter((item) => fixedKinds.has(item.kind));
|
|
693
|
+
const sortedMembers = sortMembers ? fixed.filter((item) => item.kind === "member").sort(compareNames) : [];
|
|
694
|
+
let movableCursor = 0;
|
|
695
|
+
let memberCursor = 0;
|
|
696
|
+
section.items = section.items.map((item) => {
|
|
697
|
+
if (!fixedKinds.has(item.kind))
|
|
698
|
+
return movable[movableCursor++];
|
|
699
|
+
if (sortMembers && item.kind === "member")
|
|
700
|
+
return sortedMembers[memberCursor++];
|
|
701
|
+
return item;
|
|
702
|
+
});
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
section.items.sort(compareNames);
|
|
706
|
+
}
|
|
707
|
+
function startsWithDoxygen(text) {
|
|
708
|
+
return text.split(/\r?\n/).find((line) => line.trim())?.trim().startsWith("/**") || false;
|
|
709
|
+
}
|
|
710
|
+
function emitItem(item, trailingGap = item.trailingGap) {
|
|
711
|
+
let out;
|
|
712
|
+
if (item.kind === "locked")
|
|
713
|
+
out = item.text.endsWith("\n") ? item.text : `${item.text}\n`;
|
|
714
|
+
else
|
|
715
|
+
out = `${item.text.replace(/[\r\n \t]+$/g, "")}\n`;
|
|
716
|
+
return trailingGap && !out.endsWith("\n\n") ? `${out}\n` : out;
|
|
717
|
+
}
|
|
718
|
+
function removeClassTailGap(body) {
|
|
719
|
+
const closingIndent = body.match(/[ \t]*$/)?.[0] ?? "";
|
|
720
|
+
const content = body
|
|
721
|
+
.slice(0, body.length - closingIndent.length)
|
|
722
|
+
.replace(/(?:\n[ \t]*)+$/, "");
|
|
723
|
+
return `${content}\n${closingIndent}`;
|
|
724
|
+
}
|
|
725
|
+
function rebuildClassBody(preamble, sections, className, sortMembers) {
|
|
726
|
+
const ordinary = [];
|
|
727
|
+
const signals = [];
|
|
728
|
+
const slots = [];
|
|
729
|
+
const privateMembers = [];
|
|
730
|
+
for (const section of sections) {
|
|
731
|
+
sortSection(section, className, sortMembers);
|
|
732
|
+
const kind = sectionKind(section.label);
|
|
733
|
+
if (kind === "signals")
|
|
734
|
+
signals.push(section);
|
|
735
|
+
else if (kind === "slots")
|
|
736
|
+
slots.push(section);
|
|
737
|
+
else if (kind === "private" && isMemberOnly(section, className))
|
|
738
|
+
privateMembers.push(section);
|
|
739
|
+
else
|
|
740
|
+
ordinary.push(section);
|
|
741
|
+
}
|
|
742
|
+
let out = preamble;
|
|
743
|
+
for (const section of [...ordinary, ...signals, ...slots, ...privateMembers]) {
|
|
744
|
+
out += `${section.labelText || `${section.indent}${section.label}`}\n`;
|
|
745
|
+
for (let i = 0; i < section.items.length; i += 1) {
|
|
746
|
+
const item = section.items[i];
|
|
747
|
+
let trailing = item.trailingGap;
|
|
748
|
+
const next = section.items[i + 1];
|
|
749
|
+
if (!hasNumberedDoxygenSequence(section) && !section.items.some((entry) => entry.kind === "nested")) {
|
|
750
|
+
if (item.kind === "member" && startsWithDoxygen(item.text))
|
|
751
|
+
trailing = true;
|
|
752
|
+
else if (next && startsWithDoxygen(next.text))
|
|
753
|
+
trailing = true;
|
|
754
|
+
}
|
|
755
|
+
out += emitItem(item, trailing);
|
|
756
|
+
}
|
|
757
|
+
if (section.trailingGap && !out.endsWith("\n\n"))
|
|
758
|
+
out += "\n";
|
|
759
|
+
}
|
|
760
|
+
// KtAlarmClock's clang-format style keeps the class tail compact. Only trim
|
|
761
|
+
// padding between the final item and `};`; preserve blank lines between
|
|
762
|
+
// independent comment/locked-region lines, including before `clang-format on`.
|
|
763
|
+
return removeClassTailGap(out);
|
|
764
|
+
}
|
|
765
|
+
function isCodePosition(text, target) {
|
|
766
|
+
let lineComment = false;
|
|
767
|
+
let blockComment = false;
|
|
768
|
+
let quote = null;
|
|
769
|
+
for (let i = 0; i < target; i += 1) {
|
|
770
|
+
const ch = text[i];
|
|
771
|
+
const next = text[i + 1];
|
|
772
|
+
if (lineComment) {
|
|
773
|
+
if (ch === "\n")
|
|
774
|
+
lineComment = false;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (blockComment) {
|
|
778
|
+
if (ch === "*" && next === "/") {
|
|
779
|
+
blockComment = false;
|
|
780
|
+
i += 1;
|
|
781
|
+
}
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
if (quote) {
|
|
785
|
+
if (ch === "\\")
|
|
786
|
+
i += 1;
|
|
787
|
+
else if (ch === quote)
|
|
788
|
+
quote = null;
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (ch === "/" && next === "/") {
|
|
792
|
+
lineComment = true;
|
|
793
|
+
i += 1;
|
|
794
|
+
}
|
|
795
|
+
else if (ch === "/" && next === "*") {
|
|
796
|
+
blockComment = true;
|
|
797
|
+
i += 1;
|
|
798
|
+
}
|
|
799
|
+
else if (ch === "\"" || ch === "'")
|
|
800
|
+
quote = ch;
|
|
801
|
+
}
|
|
802
|
+
return !lineComment && !blockComment && !quote;
|
|
803
|
+
}
|
|
804
|
+
function findClosingBrace(text, open) {
|
|
805
|
+
let depth = 0;
|
|
806
|
+
let lineComment = false;
|
|
807
|
+
let blockComment = false;
|
|
808
|
+
let quote = null;
|
|
809
|
+
for (let i = open; i < text.length; i += 1) {
|
|
810
|
+
const ch = text[i];
|
|
811
|
+
const next = text[i + 1];
|
|
812
|
+
if (lineComment) {
|
|
813
|
+
if (ch === "\n")
|
|
814
|
+
lineComment = false;
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (blockComment) {
|
|
818
|
+
if (ch === "*" && next === "/") {
|
|
819
|
+
blockComment = false;
|
|
820
|
+
i += 1;
|
|
821
|
+
}
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
if (quote) {
|
|
825
|
+
if (ch === "\\")
|
|
826
|
+
i += 1;
|
|
827
|
+
else if (ch === quote)
|
|
828
|
+
quote = null;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (ch === "/" && next === "/") {
|
|
832
|
+
lineComment = true;
|
|
833
|
+
i += 1;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (ch === "/" && next === "*") {
|
|
837
|
+
blockComment = true;
|
|
838
|
+
i += 1;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (ch === "\"" || ch === "'") {
|
|
842
|
+
quote = ch;
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (ch === "{")
|
|
846
|
+
depth += 1;
|
|
847
|
+
else if (ch === "}" && --depth === 0)
|
|
848
|
+
return i;
|
|
849
|
+
}
|
|
850
|
+
return -1;
|
|
851
|
+
}
|
|
852
|
+
function reorderNormalizedHeader(text, options) {
|
|
853
|
+
let output = text;
|
|
854
|
+
let searchFrom = 0;
|
|
855
|
+
const warnings = [];
|
|
856
|
+
while (searchFrom < output.length) {
|
|
857
|
+
const regex = new RegExp(CLASS_RE.source, CLASS_RE.flags);
|
|
858
|
+
regex.lastIndex = searchFrom;
|
|
859
|
+
const match = regex.exec(output);
|
|
860
|
+
if (!match)
|
|
861
|
+
break;
|
|
862
|
+
if (!isCodePosition(output, match.index)) {
|
|
863
|
+
searchFrom = match.index + match[0].length;
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
const open = match.index + match[0].lastIndexOf("{");
|
|
867
|
+
const close = findClosingBrace(output, open);
|
|
868
|
+
if (close < 0)
|
|
869
|
+
break;
|
|
870
|
+
const className = match[1].trim().split(/\s+/).at(-1);
|
|
871
|
+
const body = output.slice(open + 1, close);
|
|
872
|
+
const parsed = splitClassSections(body, className, options.kevinSystemCodeMode);
|
|
873
|
+
if (!parsed.sections.length) {
|
|
874
|
+
searchFrom = close + 1;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (options.kevinSystemCodeMode === "merge_strip")
|
|
878
|
+
normalizeSystemCodeSections(parsed.sections, className);
|
|
879
|
+
warnings.push(...collectWarnings(parsed.sections, className));
|
|
880
|
+
const nextBody = rebuildClassBody(parsed.preamble, parsed.sections, className, options.sortMembers);
|
|
881
|
+
output = `${output.slice(0, open + 1)}${nextBody}${output.slice(close)}`;
|
|
882
|
+
searchFrom = open + 1 + nextBody.length + 1;
|
|
883
|
+
}
|
|
884
|
+
return { text: output, warnings: [...new Set(warnings)] };
|
|
885
|
+
}
|
|
886
|
+
export function pnwReorderHeaderText(text, options = {}) {
|
|
887
|
+
const eol = text.includes("\r\n") ? "\r\n" : text.includes("\r") ? "\r" : "\n";
|
|
888
|
+
const normalized = text.replace(/\r\n|\r/g, "\n");
|
|
889
|
+
const result = reorderNormalizedHeader(normalized, {
|
|
890
|
+
sortMembers: Boolean(options.sortMembers),
|
|
891
|
+
kevinSystemCodeMode: normalizeMode(options.kevinSystemCodeMode),
|
|
892
|
+
});
|
|
893
|
+
const output = eol === "\n" ? result.text : result.text.replace(/\n/g, eol);
|
|
894
|
+
return { text: output, changed: output !== text, warnings: result.warnings };
|
|
895
|
+
}
|
|
896
|
+
//# sourceMappingURL=pnwReorderHeader.js.map
|