sigmap 1.5.2 → 2.0.0-beta.2
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/CHANGELOG.md +50 -0
- package/README.md +2 -2
- package/gen-context.config.json.example +28 -1
- package/gen-context.js +677 -114
- package/package.json +2 -1
- package/src/config/defaults.js +36 -0
- package/src/extractors/coverage.js +60 -0
- package/src/extractors/cpp.js +15 -6
- package/src/extractors/csharp.js +14 -3
- package/src/extractors/dart.js +9 -7
- package/src/extractors/deps.js +81 -0
- package/src/extractors/go.js +9 -5
- package/src/extractors/java.js +14 -3
- package/src/extractors/javascript.js +34 -6
- package/src/extractors/kotlin.js +9 -5
- package/src/extractors/php.js +13 -4
- package/src/extractors/prdiff.js +45 -0
- package/src/extractors/python.js +140 -20
- package/src/extractors/ruby.js +13 -2
- package/src/extractors/rust.js +15 -5
- package/src/extractors/scala.js +13 -4
- package/src/extractors/svelte.js +11 -4
- package/src/extractors/swift.js +15 -5
- package/src/extractors/todos.js +26 -0
- package/src/extractors/typescript.js +48 -4
- package/src/extractors/vue.js +16 -2
- package/src/mcp/server.js +1 -1
package/src/extractors/python.js
CHANGED
|
@@ -9,61 +9,181 @@ function extract(src) {
|
|
|
9
9
|
if (!src || typeof src !== 'string') return [];
|
|
10
10
|
const sigs = [];
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
const
|
|
14
|
-
|
|
12
|
+
// noComments: strip only # comments, keep docstrings (needed for @decorator detection)
|
|
13
|
+
const noComments = src.replace(/#.*$/gm, '');
|
|
14
|
+
// stripped: also strip docstrings (safe for regex matching)
|
|
15
|
+
const stripped = noComments
|
|
15
16
|
.replace(/"""[\s\S]*?"""/g, '')
|
|
16
17
|
.replace(/'''[\s\S]*?'''/g, '');
|
|
17
18
|
|
|
18
19
|
// Classes
|
|
19
20
|
for (const m of stripped.matchAll(/^class\s+(\w+)(?:\s*\(([^)]*)\))?\s*:/gm)) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
// Get class body methods
|
|
21
|
+
const className = m[1];
|
|
22
|
+
const baseName = m[2] ? m[2].trim() : '';
|
|
23
23
|
const bodyStart = m.index + m[0].length;
|
|
24
|
+
|
|
25
|
+
// Try @dataclass collapse
|
|
26
|
+
const dcFields = tryExtractDataclassFields(stripped, m.index);
|
|
27
|
+
if (dcFields !== null) {
|
|
28
|
+
sigs.push(`@dataclass ${className}(${dcFields})`);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Try BaseModel/BaseSettings collapse
|
|
33
|
+
if (/(BaseModel|BaseSettings)/.test(baseName)) {
|
|
34
|
+
const bmFields = tryExtractBaseModelFields(stripped, bodyStart);
|
|
35
|
+
if (bmFields) {
|
|
36
|
+
sigs.push(`class ${className}(${baseName}) ${bmFields}`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const baseStr = baseName ? `(${baseName})` : '';
|
|
42
|
+
sigs.push(`class ${className}${baseStr}`);
|
|
43
|
+
|
|
44
|
+
// Class-level ALL_CAPS constants
|
|
45
|
+
for (const c of extractClassConstants(stripped, bodyStart)) {
|
|
46
|
+
sigs.push(` ${c}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Methods
|
|
24
50
|
const methods = extractClassMethods(stripped, bodyStart);
|
|
25
51
|
for (const meth of methods) sigs.push(` ${meth}`);
|
|
26
52
|
}
|
|
27
53
|
|
|
28
54
|
// Top-level functions
|
|
29
|
-
for (const m of stripped.matchAll(/^(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)
|
|
30
|
-
if (/^_/.test(m[
|
|
31
|
-
const asyncKw = m[
|
|
32
|
-
const params = normalizeParams(m[
|
|
33
|
-
|
|
55
|
+
for (const m of stripped.matchAll(/^((?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*[^:]+)?)\s*:/gm)) {
|
|
56
|
+
if (/^_/.test(m[2])) continue;
|
|
57
|
+
const asyncKw = m[1].trimStart().startsWith('async') ? 'async ' : '';
|
|
58
|
+
const params = normalizeParams(m[3]);
|
|
59
|
+
const retType = extractReturnType(m[1] + ':');
|
|
60
|
+
const retStr = retType ? ` → ${retType}` : '';
|
|
61
|
+
const hint = extractDocHint(src, m[2], m[0]);
|
|
62
|
+
const hintStr = hint ? ` # ${hint}` : '';
|
|
63
|
+
sigs.push(`${asyncKw}def ${m[2]}(${params})${retStr}${hintStr}`);
|
|
34
64
|
}
|
|
35
65
|
|
|
36
66
|
return sigs.slice(0, 25);
|
|
37
67
|
}
|
|
38
68
|
|
|
39
|
-
function extractClassMethods(
|
|
69
|
+
function extractClassMethods(stripped, startIndex) {
|
|
40
70
|
const methods = [];
|
|
41
|
-
|
|
42
|
-
const lines = src.slice(startIndex).split('\n');
|
|
71
|
+
const lines = stripped.slice(startIndex).split('\n');
|
|
43
72
|
for (const line of lines) {
|
|
44
73
|
if (line.trim() === '') continue;
|
|
45
|
-
// End of class body: line with no leading indent that is not blank
|
|
46
74
|
const indent = line.match(/^(\s+)/);
|
|
47
75
|
if (!indent) break;
|
|
48
|
-
const m = line.match(/^\s+(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)
|
|
76
|
+
const m = line.match(/^\s+(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*([^:]+))?\s*:/);
|
|
49
77
|
if (m) {
|
|
50
78
|
if (m[1].startsWith('__') && m[1] !== '__init__') continue;
|
|
51
79
|
if (m[1].startsWith('_') && !m[1].startsWith('__')) continue;
|
|
52
80
|
const asyncKw = line.trimStart().startsWith('async') ? 'async ' : '';
|
|
53
|
-
const params = normalizeParams(m[2]).replace(/^self,?\s*/, '');
|
|
54
|
-
|
|
81
|
+
const params = normalizeParams(m[2]).replace(/^self,?\s*/, '').replace(/^cls,?\s*/, '');
|
|
82
|
+
const retType = m[3] ? m[3].trim().replace(/Optional\[([^\]]+)\]/, '$1|None').slice(0, 30) : '';
|
|
83
|
+
const retStr = retType ? ` → ${retType}` : '';
|
|
84
|
+
methods.push(`${asyncKw}def ${m[1]}(${params})${retStr}`);
|
|
55
85
|
}
|
|
56
86
|
}
|
|
57
87
|
return methods.slice(0, 8);
|
|
58
88
|
}
|
|
59
89
|
|
|
90
|
+
function tryExtractDataclassFields(stripped, classIndex) {
|
|
91
|
+
const before = stripped.slice(Math.max(0, classIndex - 120), classIndex);
|
|
92
|
+
if (!/@dataclass/.test(before)) return null;
|
|
93
|
+
const lines = stripped.slice(classIndex).split('\n');
|
|
94
|
+
const fields = [];
|
|
95
|
+
for (const line of lines.slice(1)) {
|
|
96
|
+
if (line.trim() === '') continue;
|
|
97
|
+
if (!line.match(/^\s+/)) break;
|
|
98
|
+
const f = line.match(/^\s+(\w+)\s*:\s*([^=\n]+?)(?:\s*=.*)?$/);
|
|
99
|
+
if (!f) break;
|
|
100
|
+
const isOptional = f[2].includes('Optional') || /=\s*None/.test(line);
|
|
101
|
+
fields.push(isOptional ? `${f[1]}?` : f[1]);
|
|
102
|
+
}
|
|
103
|
+
return fields.length ? fields.join(', ') : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function tryExtractBaseModelFields(stripped, bodyStart) {
|
|
107
|
+
const lines = stripped.slice(bodyStart, bodyStart + 800).split('\n');
|
|
108
|
+
const fields = [];
|
|
109
|
+
let foundFirst = false;
|
|
110
|
+
for (const line of lines) {
|
|
111
|
+
if (line.trim() === '') continue;
|
|
112
|
+
if (foundFirst && !/^\s/.test(line)) break;
|
|
113
|
+
if (!line.match(/^\s{4}\w/)) continue;
|
|
114
|
+
foundFirst = true;
|
|
115
|
+
const f = line.match(/^\s{4}(\w+)\s*(?::\s*([^=\n]+?))?(?:\s*=\s*(.*))?$/);
|
|
116
|
+
if (!f || f[1].startsWith('_') || f[1] === 'class' || f[1] === 'def') continue;
|
|
117
|
+
const isOptional = (f[2] || '').includes('Optional') || f[3] !== undefined;
|
|
118
|
+
fields.push(isOptional ? `${f[1]}?` : `${f[1]}*`);
|
|
119
|
+
}
|
|
120
|
+
return fields.length ? `{${fields.slice(0, 6).join(', ')}}` : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function extractClassConstants(stripped, startIndex) {
|
|
124
|
+
const lines = stripped.slice(startIndex).split('\n');
|
|
125
|
+
const consts = [];
|
|
126
|
+
for (const line of lines) {
|
|
127
|
+
if (!line.match(/^\s+/)) break;
|
|
128
|
+
const c = line.match(/^\s+([A-Z][A-Z0-9_]{2,})\s*=\s*(.+)/);
|
|
129
|
+
if (!c) continue;
|
|
130
|
+
let val = c[2].trim();
|
|
131
|
+
const items = (val.match(/"([^"]+)"/g) || val.match(/'([^']+)'/g) || []);
|
|
132
|
+
if (items.length > 3) val = `[${items[0].replace(/['"]/g, '')}..${items[items.length - 1].replace(/['"]/g, '')}]`;
|
|
133
|
+
if (val.length > 40) val = val.slice(0, 37) + '...';
|
|
134
|
+
consts.push(`${c[1]}=${val}`);
|
|
135
|
+
}
|
|
136
|
+
return consts.slice(0, 3);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function extractReturnType(sigLine) {
|
|
140
|
+
const m = sigLine.match(/->\s*([^:]+):/);
|
|
141
|
+
if (!m) return '';
|
|
142
|
+
let rt = m[1].trim();
|
|
143
|
+
rt = rt.replace(/Optional\[([^\]]+)\]/, '$1|None');
|
|
144
|
+
return rt.length > 30 ? rt.slice(0, 27) + '...' : rt;
|
|
145
|
+
}
|
|
146
|
+
|
|
60
147
|
function normalizeParams(params) {
|
|
61
148
|
if (!params) return '';
|
|
62
149
|
return params.trim()
|
|
63
150
|
.split(',')
|
|
64
|
-
.map((p) =>
|
|
65
|
-
|
|
151
|
+
.map((p) => {
|
|
152
|
+
const part = p.trim();
|
|
153
|
+
if (!part) return '';
|
|
154
|
+
const stars = part.match(/^(\*{1,2})/)?.[1] || '';
|
|
155
|
+
const rest = part.slice(stars.length);
|
|
156
|
+
const eqIdx = rest.indexOf('=');
|
|
157
|
+
const noDefault = eqIdx !== -1 ? rest.slice(0, eqIdx).trim() : rest.trim();
|
|
158
|
+
const clean = noDefault
|
|
159
|
+
.replace(/Optional\[([^\]]+)\]/, '$1?')
|
|
160
|
+
.replace(/Union\[([^\]]+),\s*None\]/, '$1?');
|
|
161
|
+
return stars + clean;
|
|
162
|
+
})
|
|
163
|
+
.filter((p) => p && p !== 'self' && p !== 'cls')
|
|
66
164
|
.join(', ');
|
|
67
165
|
}
|
|
68
166
|
|
|
167
|
+
function extractDocHint(src, fnName, fnSigLine) {
|
|
168
|
+
if (!src || !fnName || !fnSigLine) return '';
|
|
169
|
+
const escSig = fnSigLine.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
170
|
+
const re = new RegExp(`^${escSig.replace(/\s+/g, '\\s+')}`, 'm');
|
|
171
|
+
const pos = src.search(re);
|
|
172
|
+
if (pos === -1) return '';
|
|
173
|
+
const afterSig = src.slice(pos + fnSigLine.length, pos + fnSigLine.length + 600);
|
|
174
|
+
|
|
175
|
+
// Find first non-empty line after function signature indentation
|
|
176
|
+
const m = afterSig.match(/^\s*\n\s*(?:[rubfRUBF]{0,2})?("""|''')([\s\S]*?)\1/m);
|
|
177
|
+
if (!m) return '';
|
|
178
|
+
|
|
179
|
+
const firstLine = m[2]
|
|
180
|
+
.split('\n')
|
|
181
|
+
.map((l) => l.trim())
|
|
182
|
+
.find(Boolean);
|
|
183
|
+
if (!firstLine) return '';
|
|
184
|
+
|
|
185
|
+
const sentence = firstLine.split(/[.!?]/)[0].trim();
|
|
186
|
+
return sentence.slice(0, 60);
|
|
187
|
+
}
|
|
188
|
+
|
|
69
189
|
module.exports = { extract };
|
package/src/extractors/ruby.js
CHANGED
|
@@ -22,14 +22,16 @@ function extract(src) {
|
|
|
22
22
|
if (m[1].startsWith('_')) continue;
|
|
23
23
|
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
24
24
|
const selfPrefix = m[0].includes('self.') ? 'self.' : '';
|
|
25
|
-
|
|
25
|
+
const retStr = extractReturnHint(stripped, m.index);
|
|
26
|
+
sigs.push(` def ${selfPrefix}${m[1]}${params}${retStr}`);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// Top-level def
|
|
29
30
|
for (const m of stripped.matchAll(/^def\s+(\w+)(?:\s*\(([^)]*)\))?/gm)) {
|
|
30
31
|
if (m[1].startsWith('_')) continue;
|
|
31
32
|
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
32
|
-
|
|
33
|
+
const retStr = extractReturnHint(stripped, m.index);
|
|
34
|
+
sigs.push(`def ${m[1]}${params}${retStr}`);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
return sigs.slice(0, 25);
|
|
@@ -40,4 +42,13 @@ function normalizeParams(params) {
|
|
|
40
42
|
return params.trim().replace(/\s+/g, ' ');
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
function extractReturnHint(stripped, index) {
|
|
46
|
+
const start = Math.max(0, index - 180);
|
|
47
|
+
const before = stripped.slice(start, index);
|
|
48
|
+
const m = before.match(/sig\s*\{[\s\S]*?returns\(([^)]+)\)[\s\S]*?\}\s*$/);
|
|
49
|
+
if (!m) return '';
|
|
50
|
+
const type = m[1].trim().replace(/\s+/g, ' ').slice(0, 25);
|
|
51
|
+
return type ? ` → ${type}` : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
module.exports = { extract };
|
package/src/extractors/rust.js
CHANGED
|
@@ -35,10 +35,11 @@ function extract(src) {
|
|
|
35
35
|
for (const fn of extractMethods(block)) sigs.push(` ${fn}`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
// Top-level pub fns
|
|
39
|
-
for (const m of stripped.matchAll(/^pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
38
|
+
// Top-level pub fns — capture everything after ) up to { or ; for return type
|
|
39
|
+
for (const m of stripped.matchAll(/^pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{;]*)/gm)) {
|
|
40
40
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
41
|
-
|
|
41
|
+
const retStr = extractReturnType(m[3]);
|
|
42
|
+
sigs.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
return sigs.slice(0, 25);
|
|
@@ -57,9 +58,10 @@ function extractBlock(src, startIndex) {
|
|
|
57
58
|
|
|
58
59
|
function extractMethods(block) {
|
|
59
60
|
const methods = [];
|
|
60
|
-
for (const m of block.matchAll(/^\s+pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
61
|
+
for (const m of block.matchAll(/^\s+pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{;]*)/gm)) {
|
|
61
62
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
62
|
-
|
|
63
|
+
const retStr = extractReturnType(m[3]);
|
|
64
|
+
methods.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
63
65
|
}
|
|
64
66
|
return methods.slice(0, 8);
|
|
65
67
|
}
|
|
@@ -69,4 +71,12 @@ function normalizeParams(params) {
|
|
|
69
71
|
return params.trim().replace(/\s+/g, ' ');
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
function extractReturnType(afterParen) {
|
|
75
|
+
if (!afterParen) return '';
|
|
76
|
+
const m = afterParen.match(/->\s*([^{;]+)/);
|
|
77
|
+
if (!m) return '';
|
|
78
|
+
const rt = m[1].trim().replace(/\s+/g, ' ');
|
|
79
|
+
return ` \u2192 ${rt.length > 30 ? rt.slice(0, 27) + '...' : rt}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
module.exports = { extract };
|
package/src/extractors/scala.js
CHANGED
|
@@ -25,10 +25,12 @@ function extract(src) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Top-level defs
|
|
28
|
-
for (const m of stripped.matchAll(/^def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?/gm)) {
|
|
28
|
+
for (const m of stripped.matchAll(/^def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?(?:\s*:\s*([^=\n{]+))?/gm)) {
|
|
29
29
|
if (m[1].startsWith('_')) continue;
|
|
30
30
|
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
31
|
-
|
|
31
|
+
const ret = normalizeType(m[3]);
|
|
32
|
+
const retStr = ret ? ` → ${ret}` : '';
|
|
33
|
+
sigs.push(`def ${m[1]}${params}${retStr}`);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
return sigs.slice(0, 25);
|
|
@@ -47,10 +49,12 @@ function extractBlock(src, startIndex) {
|
|
|
47
49
|
|
|
48
50
|
function extractMembers(block) {
|
|
49
51
|
const members = [];
|
|
50
|
-
for (const m of block.matchAll(/^\s+def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?/gm)) {
|
|
52
|
+
for (const m of block.matchAll(/^\s+def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?(?:\s*:\s*([^=\n{]+))?/gm)) {
|
|
51
53
|
if (m[1].startsWith('_')) continue;
|
|
52
54
|
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
53
|
-
|
|
55
|
+
const ret = normalizeType(m[3]);
|
|
56
|
+
const retStr = ret ? ` → ${ret}` : '';
|
|
57
|
+
members.push(`def ${m[1]}${params}${retStr}`);
|
|
54
58
|
}
|
|
55
59
|
return members.slice(0, 8);
|
|
56
60
|
}
|
|
@@ -64,4 +68,9 @@ function normalizeParams(params) {
|
|
|
64
68
|
.join(', ');
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
function normalizeType(type) {
|
|
72
|
+
if (!type) return '';
|
|
73
|
+
return type.trim().replace(/\s+/g, ' ').slice(0, 25);
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
module.exports = { extract };
|
package/src/extractors/svelte.js
CHANGED
|
@@ -23,16 +23,18 @@ function extract(src) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// Exported functions
|
|
26
|
-
for (const m of script.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)
|
|
26
|
+
for (const m of script.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
27
27
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
28
|
-
|
|
28
|
+
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
29
|
+
sigs.push(`export ${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Top-level functions
|
|
32
|
-
for (const m of script.matchAll(/^(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)
|
|
33
|
+
for (const m of script.matchAll(/^(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
33
34
|
if (m[1].startsWith('_')) continue;
|
|
34
35
|
const asyncKw = m[0].startsWith('async') ? 'async ' : '';
|
|
35
|
-
|
|
36
|
+
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
37
|
+
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
// Reactive declarations $:
|
|
@@ -48,4 +50,9 @@ function normalizeParams(params) {
|
|
|
48
50
|
return params.trim().replace(/\s+/g, ' ');
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
function normalizeType(type) {
|
|
54
|
+
if (!type) return '';
|
|
55
|
+
return type.trim().replace(/[;\s]+$/g, '').replace(/\s+/g, ' ').slice(0, 25);
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
module.exports = { extract };
|
package/src/extractors/swift.js
CHANGED
|
@@ -21,10 +21,11 @@ function extract(src) {
|
|
|
21
21
|
for (const fn of extractMembers(block)) sigs.push(` ${fn}`);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Top-level public functions
|
|
25
|
-
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:static\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
24
|
+
// Top-level public functions — capture everything after ) to end of line for arrow type
|
|
25
|
+
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:static\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{\n]*)/gm)) {
|
|
26
26
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
27
|
-
|
|
27
|
+
const retStr = extractArrowType(m[3]);
|
|
28
|
+
sigs.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
return sigs.slice(0, 25);
|
|
@@ -43,10 +44,11 @@ function extractBlock(src, startIndex) {
|
|
|
43
44
|
|
|
44
45
|
function extractMembers(block) {
|
|
45
46
|
const members = [];
|
|
46
|
-
for (const m of block.matchAll(/^\s+(?:public\s+|internal\s+|open\s+)?(?:static\s+|class\s+)?(?:mutating\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
47
|
+
for (const m of block.matchAll(/^\s+(?:public\s+|internal\s+|open\s+)?(?:static\s+|class\s+)?(?:mutating\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{\n]*)/gm)) {
|
|
47
48
|
if (m[1].startsWith('_')) continue;
|
|
48
49
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
49
|
-
|
|
50
|
+
const retStr = extractArrowType(m[3]);
|
|
51
|
+
members.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
50
52
|
}
|
|
51
53
|
return members.slice(0, 8);
|
|
52
54
|
}
|
|
@@ -60,4 +62,12 @@ function normalizeParams(params) {
|
|
|
60
62
|
.join(', ');
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
function extractArrowType(str) {
|
|
66
|
+
if (!str) return '';
|
|
67
|
+
const m = str.match(/->\s*([^\n{]+)/);
|
|
68
|
+
if (!m) return '';
|
|
69
|
+
const rt = m[1].trim().replace(/\s+/g, ' ');
|
|
70
|
+
return ` \u2192 ${rt.length > 25 ? rt.slice(0, 22) + '...' : rt}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
module.exports = { extract };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract TODO/FIXME/HACK/XXX comments from source text.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {{line:number, tag:string, text:string}[]}
|
|
7
|
+
*/
|
|
8
|
+
function extractTodos(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const todos = [];
|
|
11
|
+
const lines = src.split('\n');
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14
|
+
const m = lines[i].match(/(?:\/\/|#)\s*(TODO|FIXME|HACK|XXX)\s*:?\s*(.+)/i);
|
|
15
|
+
if (!m) continue;
|
|
16
|
+
todos.push({
|
|
17
|
+
line: i + 1,
|
|
18
|
+
tag: m[1].toUpperCase(),
|
|
19
|
+
text: m[2].trim().slice(0, 70),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return todos;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { extractTodos };
|
|
@@ -50,7 +50,27 @@ function extract(src) {
|
|
|
50
50
|
for (const m of stripped.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)(?:\s*:\s*[^{]+)?\s*\{/gm)) {
|
|
51
51
|
const asyncKw = /export\s+async/.test(m[0]) ? 'async ' : '';
|
|
52
52
|
const params = normalizeParams(m[2]);
|
|
53
|
-
|
|
53
|
+
const retMatch = m[0].match(/\)\s*:\s*([^{]+)\s*\{/);
|
|
54
|
+
const retType = retMatch ? retMatch[1].trim().replace(/\s+/g, ' ').slice(0, 30) : '';
|
|
55
|
+
const retStr = retType ? ` → ${retType}` : '';
|
|
56
|
+
sigs.push(`export ${asyncKw}function ${m[1]}(${params})${retStr}`);
|
|
57
|
+
|
|
58
|
+
// Hooks: capture compact return object shape for use* functions.
|
|
59
|
+
if (m[1].startsWith('use')) {
|
|
60
|
+
const bodyStart = m.index + m[0].length;
|
|
61
|
+
const body = stripped.slice(bodyStart, bodyStart + 800);
|
|
62
|
+
const ret = body.match(/return\s*\{([^}]{1,260})\}/);
|
|
63
|
+
if (ret) {
|
|
64
|
+
const keys = ret[1]
|
|
65
|
+
.split(',')
|
|
66
|
+
.map((s) => s.trim().split(':')[0].split('(')[0].trim())
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.slice(0, 8);
|
|
69
|
+
if (keys.length) {
|
|
70
|
+
sigs[sigs.length - 1] += ` → { ${keys.join(', ')} }`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
// Exported arrow functions / const functions
|
|
@@ -58,6 +78,25 @@ function extract(src) {
|
|
|
58
78
|
const asyncKw = /=\s*async\s+/.test(m[0]) ? 'async ' : '';
|
|
59
79
|
const params = normalizeParams(m[2]);
|
|
60
80
|
sigs.push(`export const ${m[1]} = ${asyncKw}(${params}) =>`);
|
|
81
|
+
|
|
82
|
+
// Hooks: capture compact return object shape for use* functions.
|
|
83
|
+
if (m[1].startsWith('use')) {
|
|
84
|
+
const bodyStart = stripped.indexOf('{', m.index + m[0].length);
|
|
85
|
+
if (bodyStart !== -1) {
|
|
86
|
+
const body = stripped.slice(bodyStart, bodyStart + 800);
|
|
87
|
+
const ret = body.match(/return\s*\{([^}]{1,260})\}/);
|
|
88
|
+
if (ret) {
|
|
89
|
+
const keys = ret[1]
|
|
90
|
+
.split(',')
|
|
91
|
+
.map((s) => s.trim().split(':')[0].split('(')[0].trim())
|
|
92
|
+
.filter(Boolean)
|
|
93
|
+
.slice(0, 8);
|
|
94
|
+
if (keys.length) {
|
|
95
|
+
sigs[sigs.length - 1] += ` → { ${keys.join(', ')} }`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
61
100
|
}
|
|
62
101
|
|
|
63
102
|
return sigs.slice(0, 25);
|
|
@@ -77,9 +116,11 @@ function extractBlock(src, startIndex) {
|
|
|
77
116
|
|
|
78
117
|
function extractInterfaceMembers(block) {
|
|
79
118
|
const members = [];
|
|
80
|
-
for (const m of block.matchAll(/^\s+(readonly\s+)?(\w+)
|
|
119
|
+
for (const m of block.matchAll(/^\s+(readonly\s+)?(\w+)(\??):\s*([^;]+);/gm)) {
|
|
81
120
|
const readonly = m[1] ? 'readonly ' : '';
|
|
82
|
-
|
|
121
|
+
const optional = m[3] ? '?' : '';
|
|
122
|
+
const typeStr = m[4].trim().replace(/\s+/g, ' ').slice(0, 35);
|
|
123
|
+
members.push(`${readonly}${m[2]}${optional}: ${typeStr}`);
|
|
83
124
|
}
|
|
84
125
|
for (const m of block.matchAll(/^\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)\s*:/gm)) {
|
|
85
126
|
members.push(`${m[1]}(${normalizeParams(m[2])})`);
|
|
@@ -96,7 +137,10 @@ function extractClassMembers(block) {
|
|
|
96
137
|
if (m[1] === 'constructor') { members.push(`constructor(${normalizeParams(m[2])})`); continue; }
|
|
97
138
|
const isAsync = m[0].includes('async ') ? 'async ' : '';
|
|
98
139
|
const isStatic = m[0].includes('static ') ? 'static ' : '';
|
|
99
|
-
|
|
140
|
+
const retMatch = m[0].match(/\)\s*:\s*([^{;]+)\s*\{/);
|
|
141
|
+
const retType = retMatch ? retMatch[1].trim().replace(/\s+/g, ' ').slice(0, 20) : '';
|
|
142
|
+
const retStr = retType ? ` → ${retType}` : '';
|
|
143
|
+
members.push(`${isStatic}${isAsync}${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
100
144
|
}
|
|
101
145
|
return members.slice(0, 8);
|
|
102
146
|
}
|
package/src/extractors/vue.js
CHANGED
|
@@ -34,13 +34,22 @@ function extract(src) {
|
|
|
34
34
|
// Methods in options API
|
|
35
35
|
const methodsMatch = script.match(/methods\s*:\s*\{([\s\S]*?)\},?\s*(?:computed|watch|mounted|created|data|\})/);
|
|
36
36
|
if (methodsMatch) {
|
|
37
|
-
for (const m of methodsMatch[1].matchAll(/^\s+(?:async\s+)?(\w+)\s*\(([^)]*)\)
|
|
37
|
+
for (const m of methodsMatch[1].matchAll(/^\s+(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
38
38
|
if (m[1].startsWith('_')) continue;
|
|
39
39
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
40
|
-
|
|
40
|
+
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
41
|
+
sigs.push(` ${asyncKw}${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// Top-level functions in <script> (e.g., composition API helpers)
|
|
46
|
+
for (const m of script.matchAll(/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
47
|
+
if (m[1].startsWith('_')) continue;
|
|
48
|
+
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
49
|
+
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
50
|
+
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
// defineProps (Composition API)
|
|
45
54
|
const definePropsMatch = script.match(/defineProps(?:<[^>]*>)?\s*\(\s*(\{[\s\S]*?\})\s*\)/);
|
|
46
55
|
if (definePropsMatch) {
|
|
@@ -63,4 +72,9 @@ function normalizeParams(params) {
|
|
|
63
72
|
return params.trim().replace(/\s+/g, ' ');
|
|
64
73
|
}
|
|
65
74
|
|
|
75
|
+
function normalizeType(type) {
|
|
76
|
+
if (!type) return '';
|
|
77
|
+
return type.trim().replace(/[;\s]+$/g, '').replace(/\s+/g, ' ').slice(0, 25);
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
module.exports = { extract };
|
package/src/mcp/server.js
CHANGED