ucn 5.3.6 → 5.3.8
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/.claude/skills/ucn/SKILL.md +25 -1
- package/.claude/skills/ucn/references/commands.md +18 -1
- package/README.md +17 -0
- package/cli/index.js +31 -19
- package/core/analysis.js +12 -2
- package/core/cache.js +15 -1
- package/core/check.js +7 -0
- package/core/discovery.js +15 -5
- package/core/execute.js +55 -19
- package/core/graph.js +6 -2
- package/core/output/check.js +5 -1
- package/core/output/doctor.js +1 -1
- package/core/output/lines.js +8 -2
- package/core/output/public.js +8 -0
- package/core/output/reporting.js +1 -1
- package/core/output/search.js +6 -1
- package/core/project.js +3 -1
- package/core/public-command.js +1 -1
- package/core/registry.js +6 -5
- package/core/reporting.js +2 -0
- package/core/search.js +17 -4
- package/languages/c-family.js +9 -8
- package/languages/csharp.js +11 -10
- package/languages/go.js +8 -7
- package/languages/java.js +3 -2
- package/languages/javascript.js +2 -1
- package/languages/python.js +3 -2
- package/languages/rust.js +3 -2
- package/languages/utils.js +51 -29
- package/mcp/server.js +12 -8
- package/package.json +2 -2
package/core/graph.js
CHANGED
|
@@ -564,6 +564,8 @@ function api(index, filePath, options = {}) {
|
|
|
564
564
|
const results = [];
|
|
565
565
|
let scopedFiles = 0;
|
|
566
566
|
let pythonImplicitFiles = 0;
|
|
567
|
+
let excludedTestFiles = 0;
|
|
568
|
+
let explicitFile = false;
|
|
567
569
|
|
|
568
570
|
let fileIterator;
|
|
569
571
|
if (filePath) {
|
|
@@ -573,6 +575,7 @@ function api(index, filePath, options = {}) {
|
|
|
573
575
|
const fileEntry = index.files.get(resolved);
|
|
574
576
|
if (!fileEntry) return { error: 'file-not-found', filePath };
|
|
575
577
|
fileIterator = [[resolved, fileEntry]];
|
|
578
|
+
explicitFile = true;
|
|
576
579
|
} else {
|
|
577
580
|
// Fall back to pattern filter (substring match on relative path)
|
|
578
581
|
const matches = [];
|
|
@@ -596,7 +599,8 @@ function api(index, filePath, options = {}) {
|
|
|
596
599
|
}
|
|
597
600
|
|
|
598
601
|
// Skip test files by default (test classes aren't part of public API)
|
|
599
|
-
if (!options.includeTests && isTestFile(fileEntry.relativePath, fileEntry.language)) {
|
|
602
|
+
if (!explicitFile && !options.includeTests && isTestFile(fileEntry.relativePath, fileEntry.language)) {
|
|
603
|
+
excludedTestFiles++;
|
|
600
604
|
continue;
|
|
601
605
|
}
|
|
602
606
|
scopedFiles++;
|
|
@@ -612,7 +616,7 @@ function api(index, filePath, options = {}) {
|
|
|
612
616
|
results.sort((a, b) => codeUnitCompare(a.file, b.file) ||
|
|
613
617
|
(a.startLine - b.startLine) || codeUnitCompare(a.name, b.name));
|
|
614
618
|
Object.defineProperty(results, 'apiInfo', {
|
|
615
|
-
value: { scopedFiles, pythonImplicitFiles },
|
|
619
|
+
value: { scopedFiles, pythonImplicitFiles, excludedTestFiles },
|
|
616
620
|
enumerable: false, writable: true, configurable: true,
|
|
617
621
|
});
|
|
618
622
|
return results;
|
package/core/output/check.js
CHANGED
|
@@ -10,13 +10,17 @@ function formatCheck(result) {
|
|
|
10
10
|
// The gate could not run — never render this like a clean tree.
|
|
11
11
|
return `Pre-commit Check (${result.base}${result.staged ? ', staged' : ''})\n${'═'.repeat(60)}\nCHECK DID NOT RUN [${result.status || 'diff-failed'}] — ${result.error || 'git diff failed'}\nThis is not a pass. Fix the git context (run inside a git repository with a valid base ref) and rerun.`;
|
|
12
12
|
}
|
|
13
|
+
const pathNotes = [];
|
|
14
|
+
if (result.nonSourcePaths > 0) pathNotes.push(`Note: ${result.nonSourcePaths} changed path(s) outside supported source files not analyzed.`);
|
|
15
|
+
if (result.untrackedPaths > 0) pathNotes.push(`Note: ${result.untrackedPaths} untracked source file(s) included as whole-file additions.`);
|
|
13
16
|
if (result.empty) {
|
|
14
|
-
return `Pre-commit Check (${result.base}${result.staged ? ', staged' : ''})\n${'═'.repeat(60)}\nNo changes to analyze${result.reason ? ` (${result.reason})` : ''}
|
|
17
|
+
return [`Pre-commit Check (${result.base}${result.staged ? ', staged' : ''})\n${'═'.repeat(60)}\nNo changes to analyze${result.reason ? ` (${result.reason})` : ''}.`, ...pathNotes].join('\n');
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
const lines = [];
|
|
18
21
|
lines.push(`Pre-commit Check vs ${result.base}${result.staged ? ' (staged)' : ''}`);
|
|
19
22
|
lines.push('═'.repeat(60));
|
|
23
|
+
lines.push(...pathNotes);
|
|
20
24
|
if (result.trust) {
|
|
21
25
|
lines.push(`TRUST: ${result.trust.status} — UCN evidence is not semantic proof; compiler and tests are required`);
|
|
22
26
|
const trustDetails = [];
|
package/core/output/doctor.js
CHANGED
|
@@ -33,7 +33,7 @@ function formatDoctor(result, options = {}) {
|
|
|
33
33
|
// Cache state
|
|
34
34
|
if (result.cache) {
|
|
35
35
|
const state = result.cache.fresh === true ? 'fresh' : result.cache.fresh === false ? 'stale' : 'unknown';
|
|
36
|
-
const buildHint = result.cache.buildMs ? `, ${result.cache.buildMs}ms build` : '';
|
|
36
|
+
const buildHint = result.cache.buildMs ? `, ${result.cache.buildMs}ms last index build (excludes cache I/O and query execution)` : '';
|
|
37
37
|
lines.push(`Cache: ${state}${buildHint}`);
|
|
38
38
|
}
|
|
39
39
|
if (result.commandTrust) {
|
package/core/output/lines.js
CHANGED
|
@@ -29,7 +29,8 @@ function record(pathLike, line, text, tag = '') {
|
|
|
29
29
|
// Keep unusual filenames from becoming notes or extra physical records.
|
|
30
30
|
let file = String(pathLike).replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
|
|
31
31
|
if (file.startsWith('# ')) file = './' + file;
|
|
32
|
-
|
|
32
|
+
const tagText = String(tag || '').replace(/\s+/g, ' ').trim();
|
|
33
|
+
return `${file}:${line == null ? 0 : line}:${body}${tagText ? `\t# ${tagText}` : ''}`;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
function commentLines(text) {
|
|
@@ -95,9 +96,14 @@ function searchRecords(result) {
|
|
|
95
96
|
if (result && !Array.isArray(result) && Array.isArray(result.results)) {
|
|
96
97
|
for (const item of result.results) {
|
|
97
98
|
const text = item.params != null ? `${item.name}(${item.params})` : item.name;
|
|
98
|
-
|
|
99
|
+
const decorators = (item.decorators || []).map(d => `@${String(d).replace(/^@/, '')}`).join(', ');
|
|
100
|
+
const tag = [item.kind || item.type, decorators].filter(Boolean).join('; ');
|
|
101
|
+
out.push(record(item.file, item.line, text, tag));
|
|
99
102
|
}
|
|
100
103
|
const meta = result.meta;
|
|
104
|
+
if (meta?.query?.unused) {
|
|
105
|
+
notes.push(...commentLines(require('./search').unusedSearchNote()));
|
|
106
|
+
}
|
|
101
107
|
if (meta && meta.totalMatched > meta.shown) {
|
|
102
108
|
notes.push(`# ${meta.totalMatched - meta.shown} more match(es) (--limit=N / --all)`);
|
|
103
109
|
}
|
package/core/output/public.js
CHANGED
|
@@ -372,6 +372,11 @@ function formatPublicText(command, result, params = {}, execution = {}) {
|
|
|
372
372
|
function formatPublicJson(command, result, params = {}, execution = {}) {
|
|
373
373
|
let commandMeta = {};
|
|
374
374
|
let data = result;
|
|
375
|
+
if (Array.isArray(result) && result.limitInfo) {
|
|
376
|
+
commandMeta.total = result.limitInfo.total;
|
|
377
|
+
commandMeta.shown = result.limitInfo.shown;
|
|
378
|
+
commandMeta.truncated = result.limitInfo.shown < result.limitInfo.total;
|
|
379
|
+
}
|
|
375
380
|
|
|
376
381
|
// Ambiguous bare-name resolution is a surface-level trust decision. Keep
|
|
377
382
|
// it in the common envelope even for array results (notably `tests`, whose
|
|
@@ -419,6 +424,9 @@ function formatPublicJson(command, result, params = {}, execution = {}) {
|
|
|
419
424
|
if (result.meta.truncatedMatches > 0) commandMeta.truncated = true;
|
|
420
425
|
if (result.unsupportedMatches) commandMeta.unsupportedMatches = result.unsupportedMatches;
|
|
421
426
|
}
|
|
427
|
+
if (command === 'api' && result?.apiInfo) {
|
|
428
|
+
commandMeta.apiInfo = result.apiInfo;
|
|
429
|
+
}
|
|
422
430
|
if (command === 'entrypoints' && result?.filterInfo) {
|
|
423
431
|
commandMeta.hiddenTestEntrypoints = result.filterInfo.hiddenTests;
|
|
424
432
|
commandMeta.testsIncluded = result.filterInfo.testsIncluded;
|
package/core/output/reporting.js
CHANGED
|
@@ -125,7 +125,7 @@ function formatStats(stats, options = {}) {
|
|
|
125
125
|
lines.push(`Files: ${stats.files}`);
|
|
126
126
|
}
|
|
127
127
|
lines.push(`Symbols: ${stats.symbols}`);
|
|
128
|
-
lines.push(`
|
|
128
|
+
lines.push(`Last index build: ${stats.buildTime}ms (excludes cache I/O and query execution; reused from cache)`);
|
|
129
129
|
|
|
130
130
|
lines.push('\nBy Language:');
|
|
131
131
|
for (const [lang, info] of Object.entries(stats.byLanguage)) {
|
package/core/output/search.js
CHANGED
|
@@ -87,6 +87,10 @@ function formatSearchJson(results, term) {
|
|
|
87
87
|
/**
|
|
88
88
|
* Format structural search results (index-based queries)
|
|
89
89
|
*/
|
|
90
|
+
function unusedSearchNote(flag = '--unused') {
|
|
91
|
+
return `${flag} lists callable symbols with no resolved call edge; it does not assess type/field/reference liveness and is not safe-delete proof. Confirm with deadcode and usages.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
90
94
|
function formatStructuralSearch(result, options = {}) {
|
|
91
95
|
const { results, meta } = result;
|
|
92
96
|
const lines = [];
|
|
@@ -106,7 +110,7 @@ function formatStructuralSearch(result, options = {}) {
|
|
|
106
110
|
lines.push(`Structural search: ${queryStr}`);
|
|
107
111
|
lines.push('═'.repeat(60));
|
|
108
112
|
if (meta.query.unused) {
|
|
109
|
-
lines.push(`NOTE: ${options.unusedFlag
|
|
113
|
+
lines.push(`NOTE: ${unusedSearchNote(options.unusedFlag)}`);
|
|
110
114
|
lines.push('');
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -447,6 +451,7 @@ function formatTestsJson(tests, name) {
|
|
|
447
451
|
}
|
|
448
452
|
|
|
449
453
|
module.exports = {
|
|
454
|
+
unusedSearchNote,
|
|
450
455
|
formatSearch,
|
|
451
456
|
formatSearchJson,
|
|
452
457
|
formatStructuralSearch,
|
package/core/project.js
CHANGED
|
@@ -424,6 +424,7 @@ class ProjectIndex {
|
|
|
424
424
|
const implicitProjectDiscovery = !Array.isArray(pattern) && !pattern;
|
|
425
425
|
this.unsupportedFiles = [];
|
|
426
426
|
this.discoveryIssues = [];
|
|
427
|
+
this.includeBundled = options.includeBundled === true;
|
|
427
428
|
const discoveryIssueKeys = new Set();
|
|
428
429
|
const recordDiscoveryIssue = (issue) => {
|
|
429
430
|
const rel = path.relative(this.root, issue.path || this.root) || '.';
|
|
@@ -450,7 +451,8 @@ class ProjectIndex {
|
|
|
450
451
|
maxFiles: options.maxFiles || this.config.maxFiles || 50000,
|
|
451
452
|
maxDepth: options.maxDepth ?? this.config.maxDepth,
|
|
452
453
|
maxFileSize: options.maxFileSize ?? this.config.maxFileSize,
|
|
453
|
-
followSymlinks: options.followSymlinks
|
|
454
|
+
followSymlinks: options.followSymlinks,
|
|
455
|
+
includeBundled: this.includeBundled,
|
|
454
456
|
};
|
|
455
457
|
|
|
456
458
|
// Merge .gitignore and .ucn.json exclude into file discovery
|
package/core/public-command.js
CHANGED
|
@@ -22,7 +22,7 @@ function buildPublicParams(command, arg, params = {}) {
|
|
|
22
22
|
if (clean.top === 0 && params.topRaw == null) delete clean.top;
|
|
23
23
|
if (clean.maxLines == null) delete clean.maxLines;
|
|
24
24
|
// Surface-only values never belong in execute params.
|
|
25
|
-
for (const key of ['json', 'quiet', 'cache', 'clearCache', 'followSymlinks', 'interactive',
|
|
25
|
+
for (const key of ['json', 'quiet', 'cache', 'clearCache', 'followSymlinks', 'includeBundled', 'interactive',
|
|
26
26
|
'topRaw', 'limitRaw', 'maxFilesRaw', 'maxLinesRaw', 'maxChars', 'maxCharsRaw',
|
|
27
27
|
'depthRaw', 'contextRaw', 'workersRaw',
|
|
28
28
|
'_fileFromFileMode']) delete clean[key];
|
package/core/registry.js
CHANGED
|
@@ -114,6 +114,7 @@ const PARAM_MAP = {
|
|
|
114
114
|
max_files: 'maxFiles',
|
|
115
115
|
max_chars: 'maxChars',
|
|
116
116
|
follow_symlinks: 'followSymlinks',
|
|
117
|
+
include_bundled: 'includeBundled',
|
|
117
118
|
unreachable_only: 'unreachableOnly',
|
|
118
119
|
server_only: 'serverOnly',
|
|
119
120
|
client_only: 'clientOnly',
|
|
@@ -142,7 +143,7 @@ const FLAG_APPLICABILITY = {
|
|
|
142
143
|
impact: ['name', 'file', 'exclude', 'className', 'line', 'includeMethods', 'top', 'unreachableOnly', 'compact', 'base', 'staged', 'limit', 'all', 'lines'],
|
|
143
144
|
tests: ['name', 'file', 'exclude', 'className', 'line', 'callsOnly', 'depth', 'includeMethods', 'all'],
|
|
144
145
|
deps: ['file', 'exclude', 'depth', 'direction', 'all', 'detailed', 'cycles'],
|
|
145
|
-
api: ['file', 'in', 'limit'],
|
|
146
|
+
api: ['file', 'in', 'limit', 'includeTests'],
|
|
146
147
|
check: ['name', 'file', 'className', 'line', 'includeMethods', 'base', 'staged', 'limit'],
|
|
147
148
|
plan: ['name', 'file', 'className', 'line', 'addParam', 'removeParam', 'renameTo', 'defaultValue'],
|
|
148
149
|
repo: ['file', 'exclude', 'top', 'limit', 'all', 'detailed', 'topLevel', 'in', 'functions', 'hot', 'deep', 'sections'],
|
|
@@ -303,7 +304,7 @@ function formatSurfaceMessage(message, surface = 'cli') {
|
|
|
303
304
|
const booleanParams = new Set([
|
|
304
305
|
'includeTests', 'excludeTests', 'includeMethods', 'withTypes',
|
|
305
306
|
'codeOnly', 'caseSensitive', 'includeExported', 'includeDecorated',
|
|
306
|
-
'showConfidence', 'callsOnly', 'topLevel', 'followSymlinks',
|
|
307
|
+
'showConfidence', 'callsOnly', 'topLevel', 'followSymlinks', 'includeBundled',
|
|
307
308
|
'unreachableOnly', 'serverOnly', 'clientOnly', 'hideUncertain',
|
|
308
309
|
'expandUnverified', 'withSource', 'all', 'compact', 'exact',
|
|
309
310
|
'regex', 'exported', 'unused', 'staged', 'detailed', 'functions',
|
|
@@ -314,7 +315,7 @@ function formatSurfaceMessage(message, surface = 'cli') {
|
|
|
314
315
|
rendered = rendered.replace(/--([a-z][a-z0-9-]*)(?:=([^\s,.)]+))?/g,
|
|
315
316
|
(whole, flag, rawValue) => {
|
|
316
317
|
const camel = flag.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
317
|
-
if (!knownParams.has(camel) && !['maxChars', 'maxFiles'].includes(camel)) {
|
|
318
|
+
if (!knownParams.has(camel) && !['maxChars', 'maxFiles', 'includeBundled'].includes(camel)) {
|
|
318
319
|
return whole;
|
|
319
320
|
}
|
|
320
321
|
const snake = REVERSE_PARAM_MAP[camel] || camel;
|
|
@@ -365,7 +366,7 @@ const CLI_GLOBAL_FLAGS = Object.freeze([
|
|
|
365
366
|
'--help', '-h', '--version', '-v', '--mcp',
|
|
366
367
|
'--json', '--verbose', '--no-quiet', '--quiet',
|
|
367
368
|
'--interactive', '-i',
|
|
368
|
-
'--no-cache', '--clear-cache', '--no-follow-symlinks',
|
|
369
|
+
'--no-cache', '--clear-cache', '--no-follow-symlinks', '--include-bundled',
|
|
369
370
|
'--max-files', '--max-chars', '--workers',
|
|
370
371
|
]);
|
|
371
372
|
|
|
@@ -408,7 +409,7 @@ function getCliAcceptedFlags() {
|
|
|
408
409
|
* One line per command: `show: file, exclude, class_name, ...`
|
|
409
410
|
*/
|
|
410
411
|
function generateMcpParamSection() {
|
|
411
|
-
const lines = ['', 'ACCEPTED FLAGS PER COMMAND (max_chars, max_files, follow_symlinks always accepted; flags not listed below are ignored):'];
|
|
412
|
+
const lines = ['', 'ACCEPTED FLAGS PER COMMAND (max_chars, max_files, follow_symlinks, include_bundled always accepted; flags not listed below are ignored):'];
|
|
412
413
|
for (const cmd of CANONICAL_COMMANDS) {
|
|
413
414
|
const flags = FLAG_APPLICABILITY[cmd];
|
|
414
415
|
if (!flags || flags.length === 0) continue;
|
package/core/reporting.js
CHANGED
|
@@ -48,6 +48,7 @@ function getStats(index, options = {}) {
|
|
|
48
48
|
files: scopedFiles.length,
|
|
49
49
|
symbols: totalSymbols, // Total symbol count, not unique names
|
|
50
50
|
buildTime: index.buildTime,
|
|
51
|
+
buildTimeNote: 'Last index build, including discovery and graphs; excludes cache I/O and query execution. Reused when loading a cached index.',
|
|
51
52
|
byLanguage: {},
|
|
52
53
|
byType: {},
|
|
53
54
|
...(index.truncated && { truncated: index.truncated })
|
|
@@ -1044,6 +1045,7 @@ function orient(index, options = {}) {
|
|
|
1044
1045
|
files: stats.files,
|
|
1045
1046
|
symbols: stats.symbols,
|
|
1046
1047
|
buildTime: stats.buildTime,
|
|
1048
|
+
buildTimeNote: stats.buildTimeNote,
|
|
1047
1049
|
byLanguage: stats.byLanguage,
|
|
1048
1050
|
dirs,
|
|
1049
1051
|
hot: {
|
package/core/search.js
CHANGED
|
@@ -204,9 +204,19 @@ function _applyFindFilters(index, matches, options) {
|
|
|
204
204
|
return filtered;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
// Rank cheaply before adjudicating callers. A broad inventory must never
|
|
208
|
+
// pay for pinned caller resolution on rows the caller will not receive.
|
|
209
|
+
const ranked = filtered.map(symbol => ({
|
|
210
|
+
symbol, counts: index.countSymbolUsages(symbol),
|
|
211
|
+
}));
|
|
212
|
+
if (options.limit > 0 && ranked.length > options.limit) {
|
|
213
|
+
ranked.sort((a, b) => b.counts.total - a.counts.total ||
|
|
214
|
+
codeUnitCompare(a.symbol.relativePath || '', b.symbol.relativePath || '') ||
|
|
215
|
+
a.symbol.startLine - b.symbol.startLine ||
|
|
216
|
+
codeUnitCompare(a.symbol.name, b.symbol.name));
|
|
217
|
+
}
|
|
218
|
+
const survivors = options.limit > 0 ? ranked.slice(0, options.limit) : ranked;
|
|
219
|
+
const withCounts = survivors.map(({ symbol: m, counts }) => {
|
|
210
220
|
// The fast count supplies cheap definition/import totals, but its
|
|
211
221
|
// name-only call bucket cannot distinguish json.dumps from a project
|
|
212
222
|
// dumps, or one class's save from another's. The public `find`
|
|
@@ -873,7 +883,10 @@ function structuralSearch(index, options = {}) {
|
|
|
873
883
|
// expression position — never "unused" (the deadcode
|
|
874
884
|
// twin of the bodyScopedName audit skip).
|
|
875
885
|
if (def.bodyScopedName) continue;
|
|
876
|
-
|
|
886
|
+
// buildCalleeIndex rebuilds the whole project. Reuse
|
|
887
|
+
// the eagerly built (or cache-loaded) index, including
|
|
888
|
+
// across every candidate in this operation.
|
|
889
|
+
if (!index.calleeIndex) index.buildCalleeIndex();
|
|
877
890
|
// A name whose every call site is its own recursion
|
|
878
891
|
// has zero callers (fix #253c — the deadcode
|
|
879
892
|
// carve-out, applied here). Class-kind names are
|
package/languages/c-family.js
CHANGED
|
@@ -13,6 +13,7 @@ const { typeOrigin } = require('./type-evidence');
|
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
15
|
traverseTree,
|
|
16
|
+
nodeTextWithoutComments,
|
|
16
17
|
traverseTreeCached,
|
|
17
18
|
nodeToLocation,
|
|
18
19
|
extractJSDocstring,
|
|
@@ -989,7 +990,7 @@ function paramTypeText(param, identity) {
|
|
|
989
990
|
const defaultValue = param.childForFieldName('default_value');
|
|
990
991
|
const end = defaultValue ? defaultValue.startIndex : param.endIndex;
|
|
991
992
|
if (identity.nameNode.startIndex < base || identity.nameNode.endIndex > end) return null;
|
|
992
|
-
const text = param.
|
|
993
|
+
const text = nodeTextWithoutComments(param).slice(0, end - base);
|
|
993
994
|
const typeText = (text.slice(0, identity.nameNode.startIndex - base) +
|
|
994
995
|
text.slice(identity.nameNode.endIndex - base))
|
|
995
996
|
.replace(/\s+/g, ' ')
|
|
@@ -1014,10 +1015,10 @@ function structuredParams(paramsNode) {
|
|
|
1014
1015
|
// their type text alone — the type must not double as both name and
|
|
1015
1016
|
// annotation, and `void *` must not collapse into the `(void)` form.
|
|
1016
1017
|
const info = {
|
|
1017
|
-
name: identity.name || param.
|
|
1018
|
+
name: identity.name || nodeTextWithoutComments(param).replace(/\s+/g, ' ').trim(),
|
|
1018
1019
|
};
|
|
1019
1020
|
if (typeNode && identity.name) {
|
|
1020
|
-
info.type = paramTypeText(param, identity) || typeNode
|
|
1021
|
+
info.type = paramTypeText(param, identity) || nodeTextWithoutComments(typeNode);
|
|
1021
1022
|
}
|
|
1022
1023
|
if (param.type === 'optional_parameter_declaration') info.optional = true;
|
|
1023
1024
|
let declaratorCursor = declarator;
|
|
@@ -1164,7 +1165,7 @@ function returnTypeOf(node) {
|
|
|
1164
1165
|
const descriptor = (current.namedChildren || []).find(child =>
|
|
1165
1166
|
child.type === 'type_descriptor') || current.namedChild(0);
|
|
1166
1167
|
const type = descriptor?.childForFieldName('type') || descriptor;
|
|
1167
|
-
return type
|
|
1168
|
+
return nodeTextWithoutComments(type) || null;
|
|
1168
1169
|
}
|
|
1169
1170
|
for (const child of current.namedChildren || []) {
|
|
1170
1171
|
const found = findTrailing(child);
|
|
@@ -1193,7 +1194,7 @@ function returnTypeOf(node) {
|
|
|
1193
1194
|
current = current.childForFieldName('declarator') ||
|
|
1194
1195
|
(current.namedChildren || []).find(child => child.type.endsWith('_declarator'));
|
|
1195
1196
|
}
|
|
1196
|
-
return stars > 0 ? `${typeNode
|
|
1197
|
+
return stars > 0 ? `${nodeTextWithoutComments(typeNode)} ${'*'.repeat(stars)}` : nodeTextWithoutComments(typeNode);
|
|
1197
1198
|
}
|
|
1198
1199
|
|
|
1199
1200
|
function memberFromNode(node, className, access, lines, mode) {
|
|
@@ -1209,7 +1210,7 @@ function memberFromNode(node, className, access, lines, mode) {
|
|
|
1209
1210
|
if (isConstructor && identity.name.startsWith('~')) modifiers.push('destructor');
|
|
1210
1211
|
return {
|
|
1211
1212
|
name: identity.name,
|
|
1212
|
-
params: paramsNode ? paramsNode.
|
|
1213
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
1213
1214
|
paramsStructured: structuredParams(paramsNode),
|
|
1214
1215
|
returnType: isConstructor ? null :
|
|
1215
1216
|
(identity.conversionType || returnTypeOf(node)),
|
|
@@ -1510,7 +1511,7 @@ function findFunctionsInTree(code, tree, mode, sourceLines = null) {
|
|
|
1510
1511
|
: null;
|
|
1511
1512
|
functions.push({
|
|
1512
1513
|
name: identity.name,
|
|
1513
|
-
params: paramsNode ? paramsNode.
|
|
1514
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
1514
1515
|
paramsStructured: structuredParams(paramsNode),
|
|
1515
1516
|
returnType: isConstructor ? null :
|
|
1516
1517
|
(identity.conversionType || returnTypeOf(node)),
|
|
@@ -1800,7 +1801,7 @@ function findMacrosInTree(tree, lines, parser) {
|
|
|
1800
1801
|
startLine,
|
|
1801
1802
|
endLine,
|
|
1802
1803
|
indent,
|
|
1803
|
-
params: paramsNode ? paramsNode.
|
|
1804
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : undefined,
|
|
1804
1805
|
paramsStructured: paramsNode
|
|
1805
1806
|
? (paramsNode.namedChildren || [])
|
|
1806
1807
|
.filter(child => child.type === 'identifier')
|
package/languages/csharp.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
traverseTree,
|
|
5
|
+
nodeTextWithoutComments,
|
|
5
6
|
traverseTreeCached,
|
|
6
7
|
nodeToLocation,
|
|
7
8
|
extractJSDocstring,
|
|
@@ -127,7 +128,7 @@ function structuredParams(paramsNode) {
|
|
|
127
128
|
if (nameNode) {
|
|
128
129
|
recoveredParams.push({
|
|
129
130
|
name: nameNode.text,
|
|
130
|
-
...(typeNode && { type: typeNode
|
|
131
|
+
...(typeNode && { type: nodeTextWithoutComments(typeNode) }),
|
|
131
132
|
rest: true,
|
|
132
133
|
});
|
|
133
134
|
}
|
|
@@ -138,14 +139,14 @@ function structuredParams(paramsNode) {
|
|
|
138
139
|
const typeNode = param.childForFieldName('type');
|
|
139
140
|
if (!nameNode) continue;
|
|
140
141
|
const info = { name: nameNode.text };
|
|
141
|
-
if (typeNode) info.type = typeNode
|
|
142
|
+
if (typeNode) info.type = nodeTextWithoutComments(typeNode);
|
|
142
143
|
if (modifiersOf(param).includes('this')) info.extensionReceiver = true;
|
|
143
144
|
if (param.type === 'parameter_array') info.rest = true;
|
|
144
145
|
const value = param.childForFieldName('value') ||
|
|
145
146
|
param.namedChildren.find(child => child !== nameNode && child !== typeNode &&
|
|
146
|
-
!['attribute_list', 'modifier'].includes(child.type));
|
|
147
|
+
!['attribute_list', 'modifier', 'comment'].includes(child.type));
|
|
147
148
|
if (value) {
|
|
148
|
-
info.default = value
|
|
149
|
+
info.default = nodeTextWithoutComments(value);
|
|
149
150
|
info.optional = true;
|
|
150
151
|
}
|
|
151
152
|
params.push(info);
|
|
@@ -208,9 +209,9 @@ function memberFromNode(node, className, lines) {
|
|
|
208
209
|
const paramsStructured = structuredParams(paramsNode);
|
|
209
210
|
return {
|
|
210
211
|
name,
|
|
211
|
-
params: paramsNode ? paramsNode.
|
|
212
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
212
213
|
paramsStructured,
|
|
213
|
-
returnType: isConstructor ? null : returnNode
|
|
214
|
+
returnType: isConstructor ? null : nodeTextWithoutComments(returnNode).trim() || null,
|
|
214
215
|
startLine,
|
|
215
216
|
endLine,
|
|
216
217
|
indent,
|
|
@@ -263,9 +264,9 @@ function indexerMember(node, className, lines) {
|
|
|
263
264
|
const { startLine, endLine, indent } = nodeToLocation(node, lines);
|
|
264
265
|
return {
|
|
265
266
|
name: 'this[]',
|
|
266
|
-
params: paramsNode ? paramsNode.
|
|
267
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\[|\]$/g, '').trim() : '...',
|
|
267
268
|
paramsStructured: structuredParams(paramsNode),
|
|
268
|
-
returnType: typeNode
|
|
269
|
+
returnType: nodeTextWithoutComments(typeNode).trim() || null,
|
|
269
270
|
startLine,
|
|
270
271
|
endLine,
|
|
271
272
|
indent,
|
|
@@ -495,9 +496,9 @@ function findFunctions(code, parser) {
|
|
|
495
496
|
const modifiers = modifiersOf(node);
|
|
496
497
|
functions.push({
|
|
497
498
|
name: nameNode.text,
|
|
498
|
-
params: paramsNode ? paramsNode.
|
|
499
|
+
params: paramsNode ? nodeTextWithoutComments(paramsNode).replace(/^\(|\)$/g, '').trim() : '...',
|
|
499
500
|
paramsStructured: structuredParams(paramsNode),
|
|
500
|
-
returnType: returnNode
|
|
501
|
+
returnType: nodeTextWithoutComments(returnNode).trim() || null,
|
|
501
502
|
startLine,
|
|
502
503
|
endLine,
|
|
503
504
|
indent,
|
package/languages/go.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -29,7 +30,7 @@ function parseTree(parser, code) {
|
|
|
29
30
|
function extractReturnType(node) {
|
|
30
31
|
const resultNode = node.childForFieldName('result');
|
|
31
32
|
if (resultNode) {
|
|
32
|
-
return resultNode.
|
|
33
|
+
return nodeTextWithoutComments(resultNode).trim() || null;
|
|
33
34
|
}
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
@@ -39,7 +40,7 @@ function extractReturnedFunctionResult(node) {
|
|
|
39
40
|
const resultNode = node.childForFieldName('result');
|
|
40
41
|
if (resultNode?.type !== 'function_type') return null;
|
|
41
42
|
const innerResult = resultNode.childForFieldName('result');
|
|
42
|
-
return innerResult
|
|
43
|
+
return nodeTextWithoutComments(innerResult).trim() || null;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|
|
@@ -51,7 +52,7 @@ function extractGoParams(paramsNode) {
|
|
|
51
52
|
// unknown signatures in JSON output (fix #238; the shared
|
|
52
53
|
// utils.extractParams already had this fix).
|
|
53
54
|
if (!paramsNode) return '...';
|
|
54
|
-
const text = paramsNode
|
|
55
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
55
56
|
return text.replace(/^\(|\)$/g, '').trim();
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -115,7 +116,7 @@ function _processFunction(node, functions, processedRanges, lines) {
|
|
|
115
116
|
indent,
|
|
116
117
|
modifiers: isExported ? ['export'] : [],
|
|
117
118
|
isFunctionVariable: true,
|
|
118
|
-
...(resultNode
|
|
119
|
+
...(resultNode && { returnType: nodeTextWithoutComments(resultNode).trim() || null }),
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
return true;
|
|
@@ -491,11 +492,11 @@ function extractInterfaceMembers(interfaceNode, codeOrLines) {
|
|
|
491
492
|
} else if (sub.type === 'parameter_list') {
|
|
492
493
|
hasParams = true;
|
|
493
494
|
if (!paramsText) {
|
|
494
|
-
paramsText = sub.
|
|
495
|
+
paramsText = nodeTextWithoutComments(sub).slice(1, -1); // strip parens
|
|
495
496
|
paramsNode = sub;
|
|
496
497
|
} else {
|
|
497
498
|
// Second parameter_list is the return type tuple
|
|
498
|
-
returnType = sub
|
|
499
|
+
returnType = nodeTextWithoutComments(sub);
|
|
499
500
|
}
|
|
500
501
|
}
|
|
501
502
|
}
|
|
@@ -514,7 +515,7 @@ function extractInterfaceMembers(interfaceNode, codeOrLines) {
|
|
|
514
515
|
for (let j = 0; j < child.namedChildCount; j++) {
|
|
515
516
|
const sub = child.namedChild(j);
|
|
516
517
|
if (returnTypeNodes.has(sub.type) && sub.text !== nameText) {
|
|
517
|
-
returnType = sub
|
|
518
|
+
returnType = nodeTextWithoutComments(sub);
|
|
518
519
|
}
|
|
519
520
|
}
|
|
520
521
|
}
|
package/languages/java.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -32,7 +33,7 @@ function extractJavaParams(paramsNode) {
|
|
|
32
33
|
// unknown signatures in JSON output (fix #241; go/rust got this in #238,
|
|
33
34
|
// the shared utils.extractParams already had it).
|
|
34
35
|
if (!paramsNode) return '...';
|
|
35
|
-
const text = paramsNode
|
|
36
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
36
37
|
let params = text.replace(/^\(|\)$/g, '').trim();
|
|
37
38
|
return params;
|
|
38
39
|
}
|
|
@@ -201,7 +202,7 @@ function stripJavaString(text) {
|
|
|
201
202
|
function extractReturnType(node) {
|
|
202
203
|
const typeNode = node.childForFieldName('type');
|
|
203
204
|
if (typeNode) {
|
|
204
|
-
return typeNode
|
|
205
|
+
return nodeTextWithoutComments(typeNode);
|
|
205
206
|
}
|
|
206
207
|
return null;
|
|
207
208
|
}
|
package/languages/javascript.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
extractParams,
|
|
@@ -34,7 +35,7 @@ function parseTree(parser, code) {
|
|
|
34
35
|
function extractReturnType(node) {
|
|
35
36
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
36
37
|
if (returnTypeNode) {
|
|
37
|
-
let text = returnTypeNode.
|
|
38
|
+
let text = nodeTextWithoutComments(returnTypeNode).trim();
|
|
38
39
|
if (text.startsWith(':')) {
|
|
39
40
|
text = text.slice(1).trim();
|
|
40
41
|
}
|
package/languages/python.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -32,7 +33,7 @@ function parseTree(parser, code) {
|
|
|
32
33
|
function extractReturnType(node) {
|
|
33
34
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
34
35
|
if (returnTypeNode) {
|
|
35
|
-
let text = returnTypeNode.
|
|
36
|
+
let text = nodeTextWithoutComments(returnTypeNode).trim();
|
|
36
37
|
if (text.startsWith('->')) {
|
|
37
38
|
text = text.slice(2).trim();
|
|
38
39
|
}
|
|
@@ -129,7 +130,7 @@ function extractPythonParams(paramsNode) {
|
|
|
129
130
|
// unknown signatures in JSON output (fix #241; go/rust got this in #238,
|
|
130
131
|
// the shared utils.extractParams already had it).
|
|
131
132
|
if (!paramsNode) return '...';
|
|
132
|
-
const text = paramsNode
|
|
133
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
133
134
|
let params = text.replace(/^\(|\)$/g, '').trim();
|
|
134
135
|
return params;
|
|
135
136
|
}
|
package/languages/rust.js
CHANGED
|
@@ -10,6 +10,7 @@ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
traverseTree,
|
|
13
|
+
nodeTextWithoutComments,
|
|
13
14
|
traverseTreeCached,
|
|
14
15
|
nodeToLocation,
|
|
15
16
|
parseStructuredParams,
|
|
@@ -128,7 +129,7 @@ function declarationTrees(code, parser) {
|
|
|
128
129
|
function extractReturnType(node) {
|
|
129
130
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
130
131
|
if (returnTypeNode) {
|
|
131
|
-
let text = returnTypeNode.
|
|
132
|
+
let text = nodeTextWithoutComments(returnTypeNode).trim();
|
|
132
133
|
if (text.startsWith('->')) {
|
|
133
134
|
text = text.slice(2).trim();
|
|
134
135
|
}
|
|
@@ -198,7 +199,7 @@ function extractRustParams(paramsNode) {
|
|
|
198
199
|
// unknown signatures in JSON output (fix #238; the shared
|
|
199
200
|
// utils.extractParams already had this fix).
|
|
200
201
|
if (!paramsNode) return '...';
|
|
201
|
-
const text = paramsNode
|
|
202
|
+
const text = nodeTextWithoutComments(paramsNode);
|
|
202
203
|
return text.replace(/^\(|\)$/g, '').trim();
|
|
203
204
|
}
|
|
204
205
|
|