@boyingliu01/xp-gate 0.31.3 → 0.32.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/adapters/java.sh +256 -4
- package/adapters/powershell.sh +45 -6
- package/bin/xp-gate.js +8 -2
- package/build-integrity/__tests__/gate-10.test.ts +88 -1
- package/build-integrity/gate-10.ts +51 -16
- package/dsh.bundle.patch.yml +15 -0
- package/gate-13-compiler-strictness.sh +1031 -0
- package/gate-3.sh +48 -13
- package/hooks/gate-13-compiler-strictness.sh +1031 -0
- package/hooks/gate-3.sh +48 -13
- package/hooks/lib/coverage-total.sh +18 -2
- package/hooks/lib/java-test-selection.sh +251 -0
- package/hooks/lib/jscpd-run.sh +9 -0
- package/hooks/lib/validate-code-walkthrough.cjs +186 -29
- package/hooks/pre-commit +269 -13
- package/lib/__tests__/detect-deps-dsh-profile.test.js +106 -0
- package/lib/__tests__/doctor-tui-skill-freshness.test.js +140 -0
- package/lib/__tests__/gate-runner.test.js +29 -2
- package/lib/__tests__/issue-586-principles-cmdline.test.js +35 -0
- package/lib/__tests__/issue-618-gate-not-run.test.js +100 -0
- package/lib/__tests__/shared-utils.test.js +21 -0
- package/lib/check.js +6 -2
- package/lib/detect-deps.js +42 -10
- package/lib/doctor-tui.js +111 -21
- package/lib/gate-runner.js +111 -37
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/principles.js +48 -6
- package/lib/shared-utils.js +8 -0
- package/mock-policy/AGENTS.md +3 -3
- package/mock-policy/gate-m3.ts +57 -23
- package/mutation/AGENTS.md +3 -3
- package/mutation/__tests__/pitest-gradle-availability.test.ts +74 -0
- package/mutation/__tests__/test-filter-parity.test.ts +5 -2
- package/mutation/gate-m.ts +30 -24
- package/mutation/runners/mutmut-runner.ts +80 -30
- package/mutation/runners/pitest-runner.ts +60 -42
- package/package.json +14 -2
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/lib/command.d.ts +38 -0
- package/plugins/dsh/lib/command.d.ts.map +1 -0
- package/plugins/dsh/lib/command.js +87 -0
- package/plugins/dsh/lib/credentials.d.ts +39 -0
- package/plugins/dsh/lib/credentials.d.ts.map +1 -0
- package/plugins/dsh/lib/credentials.js +57 -0
- package/plugins/dsh/lib/delphi-config.d.ts +129 -0
- package/plugins/dsh/lib/delphi-config.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-config.js +369 -0
- package/plugins/dsh/lib/delphi-run.d.ts +24 -0
- package/plugins/dsh/lib/delphi-run.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-run.js +113 -0
- package/plugins/dsh/lib/delphi.d.ts +94 -0
- package/plugins/dsh/lib/delphi.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi.js +207 -0
- package/plugins/dsh/lib/dialect.d.ts +24 -0
- package/plugins/dsh/lib/dialect.d.ts.map +1 -0
- package/plugins/dsh/lib/dialect.js +36 -0
- package/plugins/dsh/lib/evidence.d.ts +70 -0
- package/plugins/dsh/lib/evidence.d.ts.map +1 -0
- package/plugins/dsh/lib/evidence.js +92 -0
- package/plugins/dsh/lib/gate-runner.d.ts +49 -0
- package/plugins/dsh/lib/gate-runner.d.ts.map +1 -0
- package/plugins/dsh/lib/gate-runner.js +95 -0
- package/plugins/dsh/lib/git-context.d.ts +26 -0
- package/plugins/dsh/lib/git-context.d.ts.map +1 -0
- package/plugins/dsh/lib/git-context.js +30 -0
- package/plugins/dsh/lib/hooks.d.ts +42 -0
- package/plugins/dsh/lib/hooks.d.ts.map +1 -0
- package/plugins/dsh/lib/hooks.js +113 -0
- package/plugins/dsh/lib/index.d.ts +20 -0
- package/plugins/dsh/lib/index.d.ts.map +1 -0
- package/plugins/dsh/lib/index.js +430 -0
- package/plugins/dsh/lib/skill-provider.d.ts +15 -0
- package/plugins/dsh/lib/skill-provider.d.ts.map +1 -0
- package/plugins/dsh/lib/skill-provider.js +205 -0
- package/plugins/dsh/lib/sprint.d.ts +60 -0
- package/plugins/dsh/lib/sprint.d.ts.map +1 -0
- package/plugins/dsh/lib/sprint.js +92 -0
- package/plugins/dsh/package.json +14 -8
- package/plugins/dsh/scripts/verify-skill-provider.mjs +113 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/src/command-single-form.test.ts +67 -0
- package/plugins/dsh/src/command.test.ts +21 -17
- package/plugins/dsh/src/command.ts +19 -21
- package/plugins/dsh/src/compat.test.ts +200 -0
- package/plugins/dsh/src/contract.test.ts +7 -1
- package/plugins/dsh/src/credentials.test.ts +93 -0
- package/plugins/dsh/src/credentials.ts +71 -0
- package/plugins/dsh/src/delphi-cancellation.test.ts +71 -0
- package/plugins/dsh/src/delphi-config-apikey-override.test.ts +94 -0
- package/plugins/dsh/src/delphi-config-credential-refs.test.ts +75 -0
- package/plugins/dsh/src/delphi-config.ts +146 -53
- package/plugins/dsh/src/delphi-run.ts +27 -4
- package/plugins/dsh/src/delphi.ts +133 -65
- package/plugins/dsh/src/evidence.test.ts +74 -0
- package/plugins/dsh/src/evidence.ts +141 -0
- package/plugins/dsh/src/gate-runner.test.ts +14 -6
- package/plugins/dsh/src/gate-runner.ts +68 -5
- package/plugins/dsh/src/git-context.test.ts +39 -0
- package/plugins/dsh/src/git-context.ts +38 -0
- package/plugins/dsh/src/hooks.test.ts +87 -0
- package/plugins/dsh/src/hooks.ts +126 -0
- package/plugins/dsh/src/index.test.ts +63 -6
- package/plugins/dsh/src/index.ts +340 -13
- package/plugins/dsh/src/skill-provider.test.ts +194 -0
- package/plugins/dsh/src/skill-provider.ts +231 -0
- package/plugins/dsh/src/sprint.test.ts +64 -0
- package/plugins/dsh/src/sprint.ts +122 -0
- package/plugins/dsh/src/tri-state.test.ts +123 -0
- package/plugins/opencode/index.ts +192 -58
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/principles/__tests__/analyze-read-failure.test.ts +70 -0
- package/principles/__tests__/baseline-entry-total-warnings.test.ts +76 -0
- package/principles/__tests__/baseline-prune.test.ts +61 -0
- package/principles/__tests__/cli-files-separators.test.ts +69 -0
- package/principles/__tests__/config-merge-warnings.test.ts +148 -0
- package/principles/adapters/__tests__/cpp-code-block.test.ts +123 -0
- package/principles/adapters/__tests__/cpp-scanner-equivalence.test.ts +67 -0
- package/principles/adapters/__tests__/typescript-block-comment-lines.test.ts +53 -0
- package/principles/adapters/__tests__/typescript-masking.test.ts +96 -0
- package/principles/adapters/cpp.ts +102 -50
- package/principles/adapters/java.ts +22 -3
- package/principles/adapters/typescript.ts +548 -178
- package/principles/analyzer.ts +36 -3
- package/principles/baseline-prune.ts +19 -7
- package/principles/baseline.ts +58 -70
- package/principles/config.ts +56 -35
- package/principles/index.ts +122 -40
- package/principles/rules/clean-code/deep-nesting.ts +36 -22
- package/principles/rules/clean-code/unused-imports.ts +30 -19
- package/principles/rules/solid/dip.ts +30 -20
- package/principles/rules/solid/lsp.ts +17 -2
- package/principles/rules/solid/ocp.ts +33 -23
- package/principles/rules/solid/srp.ts +65 -23
- package/scripts/delphi-external-review.cjs +25 -2
- package/scripts/sync-package-content.js +28 -1
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @test REQ-620
|
|
3
|
+
* @intent Pin the behaviour of the literal/comment masker before it is
|
|
4
|
+
* refactored. The masker is a four-state character scanner (line
|
|
5
|
+
* comment, block comment, regex literal, string/template) whose output
|
|
6
|
+
* feeds EVERY metric the rule set reads: a masking mistake changes
|
|
7
|
+
* reported nesting depth, function length and body text for the whole
|
|
8
|
+
* adapter. These cases are the safety net for the CCN-36 split, so they
|
|
9
|
+
* assert observable masking outcomes (offsets preserved, literals
|
|
10
|
+
* blanked, structure kept) rather than any internal helper shape.
|
|
11
|
+
* @covers AC-620-01
|
|
12
|
+
*
|
|
13
|
+
* Masking contract (from the function's own docblock):
|
|
14
|
+
* - every comment / string / template / regex literal becomes spaces
|
|
15
|
+
* - offsets and newlines are preserved exactly (length and line count equal)
|
|
16
|
+
* - everything else is left byte-identical
|
|
17
|
+
*/
|
|
18
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
19
|
+
import { TypeScriptAdapter } from '../typescript';
|
|
20
|
+
|
|
21
|
+
vi.mock('fs', () => ({
|
|
22
|
+
readFileSync: vi.fn(),
|
|
23
|
+
existsSync: vi.fn(() => true),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
import { readFileSync } from 'fs';
|
|
27
|
+
|
|
28
|
+
/** Mask a source string by round-tripping it through the adapter's parser. */
|
|
29
|
+
function mask(src: string): string {
|
|
30
|
+
(readFileSync as unknown as { mockReturnValue: (v: string) => void }).mockReturnValue(src);
|
|
31
|
+
const adapter = new TypeScriptAdapter('probe.ts');
|
|
32
|
+
const ast = adapter.parseAST() as { masked?: string };
|
|
33
|
+
// The adapter exposes the masked source through a getter on the instance.
|
|
34
|
+
const masked = (adapter as unknown as { maskedSource?: string }).maskedSource;
|
|
35
|
+
return masked ?? ast.masked ?? '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('AC-620-01: literal masking keeps offsets and blanks exactly the non-code', () => {
|
|
39
|
+
const cases: Array<[string, string]> = [
|
|
40
|
+
['line comment', 'const a = 1; // hidden\nconst b = 2;'],
|
|
41
|
+
['block comment', 'const a = 1; /* hidden\nstill hidden */ const b = 2;'],
|
|
42
|
+
['single-quoted string', "const s = 'hidden text';"],
|
|
43
|
+
['double-quoted string', 'const s = "hidden text";'],
|
|
44
|
+
['template literal', 'const s = `hidden ${x} text`;'],
|
|
45
|
+
['regex literal', 'const re = /hid+den/g;'],
|
|
46
|
+
['escaped quote in string', "const s = 'it\\'s hidden';"],
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
for (const [label, src] of cases) {
|
|
50
|
+
it(`preserves length and line count for a ${label}`, () => {
|
|
51
|
+
const out = mask(src);
|
|
52
|
+
expect(out.length).toBe(src.length);
|
|
53
|
+
expect(out.split('\n').length).toBe(src.split('\n').length);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
it('blanks a line comment but keeps the code before it', () => {
|
|
58
|
+
const out = mask('const a = 1; // hidden');
|
|
59
|
+
expect(out).toContain('const a = 1;');
|
|
60
|
+
expect(out).not.toContain('hidden');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('blanks a block comment across lines without eating the newline', () => {
|
|
64
|
+
const src = '/* a\nb */\nconst keep = 1;';
|
|
65
|
+
const out = mask(src);
|
|
66
|
+
expect(out).not.toContain('a\nb');
|
|
67
|
+
expect(out.split('\n').length).toBe(3);
|
|
68
|
+
expect(out).toContain('const keep = 1;');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('blanks string contents and keeps the code around them', () => {
|
|
72
|
+
const out = mask('const s = "hidden"; const n = 42;');
|
|
73
|
+
expect(out).not.toContain('hidden');
|
|
74
|
+
expect(out).toContain('const s =');
|
|
75
|
+
expect(out).toContain('const n = 42;');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('blanks a template literal including its interpolations', () => {
|
|
79
|
+
const out = mask('const s = `before ${x} after`;');
|
|
80
|
+
expect(out).not.toContain('before');
|
|
81
|
+
expect(out).not.toContain('after');
|
|
82
|
+
expect(out).toContain('const s =');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('does not treat a division slash as the start of a regex literal', () => {
|
|
86
|
+
// `a / b` is division: the slash must not swallow the rest of the line.
|
|
87
|
+
const out = mask('const r = a / b; const keep = 1;');
|
|
88
|
+
expect(out).toContain('const keep = 1;');
|
|
89
|
+
expect(out).toContain('a / b');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('leaves code with no literals byte-identical', () => {
|
|
93
|
+
const src = 'function f(a, b) {\n return a + b;\n}';
|
|
94
|
+
expect(mask(src)).toBe(src);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { BaseAdapter } from './base';
|
|
2
2
|
import { Adapter } from '../types';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Lexical state for the C/C++ block scanner: which comment/quote the scan is
|
|
6
|
+
* currently inside. Braces seen in any of these states are data, not structure.
|
|
7
|
+
*/
|
|
8
|
+
interface CppLexState {
|
|
9
|
+
inString: boolean;
|
|
10
|
+
/** Which quote character opened the string (`'` or `"`). */
|
|
11
|
+
stringChar: string;
|
|
12
|
+
inBlockComment: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
export class CppAdapter extends BaseAdapter implements Adapter {
|
|
5
16
|
detectLanguage(): string {
|
|
6
17
|
const ext = this.filePath.toLowerCase();
|
|
@@ -49,61 +60,102 @@ export class CppAdapter extends BaseAdapter implements Adapter {
|
|
|
49
60
|
return classMatches;
|
|
50
61
|
}
|
|
51
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Report how far the lexer can skip from `i`, updating `lex` as it goes.
|
|
65
|
+
*
|
|
66
|
+
* C and C++ share three constructs the brace tracker must not see into: line
|
|
67
|
+
* comments, block comments, and quoted literals. A `}` inside any of them is
|
|
68
|
+
* data, so counting it would truncate the function body at the wrong place.
|
|
69
|
+
*
|
|
70
|
+
* Extracted from `extractCodeBlock`, which carried all three state machines
|
|
71
|
+
* plus the brace tracking in one CCN-28 body (#620).
|
|
72
|
+
*
|
|
73
|
+
* IMPORTANT: this reports a skip distance and never decides that a character
|
|
74
|
+
* is "consumed". Returning 0 means "the caller must look at this character
|
|
75
|
+
* itself" -- which is what lets a `{` both end a literal-free run and open the
|
|
76
|
+
* block. A `//` or `/*` opener IS skippable (the tracker never wants it), and
|
|
77
|
+
* so are quotes; braces are not.
|
|
78
|
+
*
|
|
79
|
+
* @returns the number of characters to skip, or 0 when the caller must inspect.
|
|
80
|
+
*/
|
|
81
|
+
private lexSkip(i: number, lex: CppLexState): number {
|
|
82
|
+
const char = this.fileContent[i];
|
|
83
|
+
const nextChar = this.fileContent[i + 1] || '';
|
|
84
|
+
|
|
85
|
+
if (lex.inBlockComment) return this.skipBlockCommentBody(i, char, nextChar, lex);
|
|
86
|
+
if (lex.inString) return this.skipStringBody(i, char, lex);
|
|
87
|
+
|
|
88
|
+
if (char === '/' && nextChar === '/') return this.skipLineComment(i);
|
|
89
|
+
if (char === '/' && nextChar === '*') {
|
|
90
|
+
lex.inBlockComment = true;
|
|
91
|
+
return 2;
|
|
92
|
+
}
|
|
93
|
+
if (char === '"' || char === "'") {
|
|
94
|
+
lex.inString = true;
|
|
95
|
+
lex.stringChar = char;
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Skip a `//` comment through, but not including, the newline. */
|
|
102
|
+
private skipLineComment(start: number): number {
|
|
103
|
+
let i = start;
|
|
104
|
+
while (i < this.fileContent.length && this.fileContent[i] !== '\n') i++;
|
|
105
|
+
return i - start;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private skipBlockCommentBody(
|
|
109
|
+
i: number,
|
|
110
|
+
char: string,
|
|
111
|
+
nextChar: string,
|
|
112
|
+
lex: CppLexState,
|
|
113
|
+
): number {
|
|
114
|
+
if (char === '*' && nextChar === '/') {
|
|
115
|
+
lex.inBlockComment = false;
|
|
116
|
+
return 2;
|
|
117
|
+
}
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private skipStringBody(i: number, char: string, lex: CppLexState): number {
|
|
122
|
+
if (char !== lex.stringChar) return 1;
|
|
123
|
+
// A quote closes the literal only after an EVEN number of backslashes:
|
|
124
|
+
// `"\\"` is a backslash then a terminator, `"\""` is a quote as data.
|
|
125
|
+
let backslashCount = 0;
|
|
126
|
+
let j = i - 1;
|
|
127
|
+
while (j >= 0 && this.fileContent[j] === '\\') {
|
|
128
|
+
backslashCount++;
|
|
129
|
+
j--;
|
|
130
|
+
}
|
|
131
|
+
if (backslashCount % 2 === 0) lex.inString = false;
|
|
132
|
+
return 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
52
135
|
override extractCodeBlock(startPos: number, maxFallback: number = 200): string {
|
|
53
136
|
const state = { inBlock: false, braceCount: 0 };
|
|
137
|
+
const lex: CppLexState = { inString: false, stringChar: '', inBlockComment: false };
|
|
54
138
|
let endPos = startPos;
|
|
55
|
-
let inString = false;
|
|
56
|
-
let stringChar = '';
|
|
57
|
-
let inComment = false;
|
|
58
|
-
|
|
59
|
-
for (let i = startPos; i < this.fileContent.length; i++) {
|
|
60
|
-
const char = this.fileContent[i];
|
|
61
|
-
const nextChar = this.fileContent[i + 1] || '';
|
|
62
|
-
|
|
63
|
-
if (!inString && !inComment) {
|
|
64
|
-
if (char === '/' && nextChar === '/') {
|
|
65
|
-
while (i < this.fileContent.length && this.fileContent[i] !== '\n') {
|
|
66
|
-
i++;
|
|
67
|
-
}
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (char === '/' && nextChar === '*') {
|
|
71
|
-
inComment = true;
|
|
72
|
-
i++;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
} else if (inComment) {
|
|
76
|
-
if (char === '*' && nextChar === '/') {
|
|
77
|
-
inComment = false;
|
|
78
|
-
i++;
|
|
79
|
-
}
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!inComment) {
|
|
84
|
-
if (!inString && (char === '"' || char === "'")) {
|
|
85
|
-
inString = true;
|
|
86
|
-
stringChar = char;
|
|
87
|
-
} else if (inString && char === stringChar) {
|
|
88
|
-
let backslashCount = 0;
|
|
89
|
-
let j = i - 1;
|
|
90
|
-
while (j >= 0 && this.fileContent[j] === '\\') {
|
|
91
|
-
backslashCount++;
|
|
92
|
-
j--;
|
|
93
|
-
}
|
|
94
|
-
if (backslashCount % 2 === 0) {
|
|
95
|
-
inString = false;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
139
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
140
|
+
for (let i = startPos; i < this.fileContent.length;) {
|
|
141
|
+
// The lexer reports how far to skip, but must NEVER consume a character
|
|
142
|
+
// the brace tracker still has to see. A `{` both ends a literal-free run
|
|
143
|
+
// and opens the block; consuming it here would hide the function's own
|
|
144
|
+
// opening brace and leave every later brace off by one.
|
|
145
|
+
//
|
|
146
|
+
// `lexSkip` returns 0 ONLY for characters outside any literal or comment,
|
|
147
|
+
// so a positive skip already guarantees "not structural". There is
|
|
148
|
+
// deliberately no separate inString/inBlockComment test: it would be dead
|
|
149
|
+
// (verified by deleting it -- no test changes colour).
|
|
150
|
+
const skip = this.lexSkip(i, lex);
|
|
151
|
+
if (skip > 0) { i += skip; continue; }
|
|
152
|
+
|
|
153
|
+
const result = this.advanceBraceTracker(this.fileContent[i], i, state);
|
|
154
|
+
if (result.closed) {
|
|
155
|
+
endPos = result.endPos;
|
|
156
|
+
break;
|
|
106
157
|
}
|
|
158
|
+
i++;
|
|
107
159
|
}
|
|
108
160
|
|
|
109
161
|
if (endPos > startPos) {
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { BaseAdapter } from './base';
|
|
2
2
|
import { Adapter } from '../types';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Depth change per nesting delimiter in a Java parameter list.
|
|
6
|
+
*
|
|
7
|
+
* Angular and square brackets nest exactly like parentheses here: the only thing
|
|
8
|
+
* that matters is whether a comma sits at depth 0, because that is the only
|
|
9
|
+
* place it separates parameters.
|
|
10
|
+
*/
|
|
11
|
+
const PARAM_NESTING: Record<string, number> = {
|
|
12
|
+
'(': 1, ')': -1,
|
|
13
|
+
'<': 1, '>': -1,
|
|
14
|
+
'[': 1, ']': -1,
|
|
15
|
+
};
|
|
16
|
+
|
|
4
17
|
/**
|
|
5
18
|
* Structural fields the clean-code rules consume (mirror of the TypeScript
|
|
6
19
|
* adapter's function shape). Before #507 this adapter returned only
|
|
@@ -213,12 +226,18 @@ export const javaParse = {
|
|
|
213
226
|
countParams(paramList: string): number {
|
|
214
227
|
const trimmed = paramList.trim();
|
|
215
228
|
if (!trimmed) return 0;
|
|
229
|
+
// Generic and array brackets nest too: `Map<String, List<Integer>>` is ONE
|
|
230
|
+
// parameter, so the commas inside its arguments must not be counted.
|
|
231
|
+
// Which characters change depth and which separate is data, not control
|
|
232
|
+
// flow -- table lookups keep the scan to a single conditional.
|
|
216
233
|
let depth = 0;
|
|
217
234
|
let count = 1;
|
|
218
235
|
for (const ch of trimmed) {
|
|
219
|
-
if (ch === '
|
|
220
|
-
|
|
221
|
-
|
|
236
|
+
if (ch === ',') {
|
|
237
|
+
if (depth === 0) count += 1;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
depth += PARAM_NESTING[ch] ?? 0;
|
|
222
241
|
}
|
|
223
242
|
return count;
|
|
224
243
|
},
|