@wrongstack/tools 1.0.11 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin.js +15 -15
- package/dist/{chunk-GU7TZE6Z.js → chunk-2BCRQAIW.js} +7 -7
- package/dist/{chunk-XRNN44QY.js → chunk-2IRHFHZY.js} +235 -71
- package/dist/{chunk-NEYUPZL6.js → chunk-6D3UIQMF.js} +651 -303
- package/dist/chunk-BJ5LXLKE.js +351 -0
- package/dist/{chunk-C6ZOXNXV.js → chunk-CSS42GRP.js} +2 -2
- package/dist/{chunk-SKRMUHTU.js → chunk-HSLDY55L.js} +41 -8
- package/dist/{chunk-ENE22I5W.js → chunk-K37AKCTN.js} +884 -362
- package/dist/{chunk-DKXMDO5F.js → chunk-K3VX6V5P.js} +3 -3
- package/dist/chunk-OIFY4XNP.js +855 -0
- package/dist/{chunk-7GOE2ZTZ.js → chunk-OMWC2OGT.js} +2 -2
- package/dist/{chunk-IE4KDAOB.js → chunk-OYQE2UNT.js} +3 -3
- package/dist/chunk-R3HR3QI3.js +97 -0
- package/dist/{chunk-XPVYZ2S6.js → chunk-RYLMSGR5.js} +2 -2
- package/dist/{chunk-HYHE4VNA.js → chunk-UMOP3VGK.js} +79 -45
- package/dist/{chunk-6OIEYJM4.js → chunk-VMNWQI4V.js} +2 -2
- package/dist/{chunk-2UVAIS5O.js → chunk-YPSVPCIE.js} +2 -2
- package/dist/{chunk-PWZUHZP7.js → chunk-Z77XMU3U.js} +2 -2
- package/dist/codebase-index/ast-invariant-engine.d.ts +19 -2
- package/dist/codebase-index/atlas-projection.d.ts +19 -0
- package/dist/codebase-index/background-indexer.d.ts +14 -0
- package/dist/codebase-index/circuit-breaker.d.ts +7 -0
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +6 -0
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +2 -0
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +26 -0
- package/dist/codebase-index/dead-code-scan.d.ts +4 -0
- package/dist/codebase-index/go-parser.d.ts +23 -3
- package/dist/codebase-index/graph-rank-pass.d.ts +10 -0
- package/dist/codebase-index/graph-rank.d.ts +8 -0
- package/dist/codebase-index/index.d.ts +1 -1
- package/dist/codebase-index/index.js +9 -9
- package/dist/codebase-index/lsp-kind.d.ts +3 -1
- package/dist/codebase-index/module-resolver.d.ts +21 -1
- package/dist/codebase-index/parser-batch.d.ts +6 -8
- package/dist/codebase-index/parser-output.d.ts +5 -1
- package/dist/codebase-index/parser-worker-script.js +4 -4
- package/dist/codebase-index/project-server-framing.d.ts +1 -17
- package/dist/codebase-index/project-server-watcher.d.ts +6 -0
- package/dist/codebase-index/project-server.js +72 -26
- package/dist/codebase-index/py-parser.d.ts +4 -4
- package/dist/codebase-index/rs-parser.d.ts +7 -0
- package/dist/codebase-index/toolchain-scripts.d.ts +64 -0
- package/dist/codebase-index/tree-sitter/queries.d.ts +14 -0
- package/dist/codebase-index/tree-sitter/util.d.ts +6 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +7 -0
- package/dist/codebase-index/worker.js +23 -16
- package/dist/codebase-index/writer-refs.d.ts +17 -2
- package/dist/codebase-index/writer-search-helpers.d.ts +9 -0
- package/dist/codebase-index/writer.d.ts +20 -1
- package/dist/edit.js +8 -8
- package/dist/{generic-parser-N2PLGBC2.js → generic-parser-IVWLFKMR.js} +2 -2
- package/dist/go-parser-LWV3L5V7.js +197 -0
- package/dist/index.js +17 -17
- package/dist/json-parser-PERV2JHX.js +204 -0
- package/dist/pack.js +16 -16
- package/dist/parser-dispatch-ZSWCQB6M.js +14 -0
- package/dist/patch.js +8 -8
- package/dist/{py-parser-4PFZYAJ7.js → py-parser-B6D6RGH7.js} +4 -4
- package/dist/read.js +8 -8
- package/dist/replace.js +8 -8
- package/dist/rs-parser-L7SP4GWJ.js +182 -0
- package/dist/tool-tier.js +17 -17
- package/dist/{tree-sitter-parser-GILYQLS4.js → tree-sitter-parser-KU6JF3ZC.js} +6 -4
- package/dist/{ts-parser-7W26YB26.js → ts-parser-PML6QIH7.js} +55 -23
- package/dist/write.js +8 -8
- package/dist/{yaml-parser-DFBC5FWH.js → yaml-parser-7BJN544B.js} +41 -6
- package/package.json +5 -5
- package/dist/chunk-4CE3RKEP.js +0 -390
- package/dist/chunk-KHUCKLOR.js +0 -899
- package/dist/chunk-ZKC5UNGD.js +0 -167
- package/dist/go-parser-Z4H6SR3R.js +0 -474
- package/dist/json-parser-IJD77VL4.js +0 -256
- package/dist/parser-dispatch-WG4DNBHH.js +0 -14
- package/dist/rs-parser-GIWPUA3T.js +0 -81
|
@@ -61,6 +61,32 @@ function getJsDoc(node, sourceFile) {
|
|
|
61
61
|
}
|
|
62
62
|
return "";
|
|
63
63
|
}
|
|
64
|
+
function declarationNameNodes(node) {
|
|
65
|
+
const name = node.name;
|
|
66
|
+
if (!name) return null;
|
|
67
|
+
if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
|
|
68
|
+
return [name];
|
|
69
|
+
}
|
|
70
|
+
if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
71
|
+
return bindingIdentifiers(name);
|
|
72
|
+
}
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
function bindingIdentifiers(pattern) {
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const element of pattern.elements) {
|
|
78
|
+
if (ts.isOmittedExpression(element)) continue;
|
|
79
|
+
if (ts.isIdentifier(element.name)) out.push(element.name);
|
|
80
|
+
else out.push(...bindingIdentifiers(element.name));
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function docHostOf(node) {
|
|
85
|
+
if (ts.isVariableDeclaration(node) && ts.isVariableDeclarationList(node.parent) && node.parent.declarations[0] === node && ts.isVariableStatement(node.parent.parent)) {
|
|
86
|
+
return node.parent.parent;
|
|
87
|
+
}
|
|
88
|
+
return node;
|
|
89
|
+
}
|
|
64
90
|
function pushScopeName(node, parts) {
|
|
65
91
|
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
66
92
|
parts.push(node.name?.text ?? "Anon");
|
|
@@ -87,30 +113,32 @@ async function parseSymbols(opts) {
|
|
|
87
113
|
if (kind) {
|
|
88
114
|
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") && funcDepth > 0) {
|
|
89
115
|
} else {
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
116
|
+
const nameNodes = declarationNameNodes(node);
|
|
117
|
+
if (nameNodes === null) {
|
|
92
118
|
return;
|
|
93
119
|
}
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
const signature = nameNodes.length > 0 ? getSignature(printer, node, sourceFile) : "";
|
|
121
|
+
const docComment = nameNodes.length > 0 ? getJsDoc(docHostOf(node), sourceFile) : "";
|
|
122
|
+
for (const nameNode of nameNodes) {
|
|
123
|
+
const name = nameNode.text;
|
|
124
|
+
const pos2 = nameNode.getStart(sourceFile);
|
|
125
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(pos2);
|
|
126
|
+
const scope = scopeParts.join(".");
|
|
127
|
+
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
128
|
+
symbols.push({
|
|
129
|
+
id: 0,
|
|
130
|
+
lang,
|
|
131
|
+
kind,
|
|
132
|
+
name,
|
|
133
|
+
file,
|
|
134
|
+
line: line2 + 1,
|
|
135
|
+
col: character,
|
|
136
|
+
signature,
|
|
137
|
+
docComment,
|
|
138
|
+
scope,
|
|
139
|
+
text
|
|
140
|
+
});
|
|
141
|
+
}
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
const pos = node.getStart(sourceFile);
|
|
@@ -121,6 +149,10 @@ async function parseSymbols(opts) {
|
|
|
121
149
|
if (ts.isIdentifier(expr)) {
|
|
122
150
|
refs.push({ fromId: 0, toName: expr.text, callType: "call", line: lineNum });
|
|
123
151
|
}
|
|
152
|
+
} else if (ts.isNewExpression(node)) {
|
|
153
|
+
if (ts.isIdentifier(node.expression)) {
|
|
154
|
+
refs.push({ fromId: 0, toName: node.expression.text, callType: "call", line: lineNum });
|
|
155
|
+
}
|
|
124
156
|
} else if (ts.isPropertyAccessExpression(node)) {
|
|
125
157
|
if (ts.isIdentifier(node.expression)) {
|
|
126
158
|
refs.push({ fromId: 0, toName: node.expression.text, callType: "call", line: lineNum });
|
|
@@ -229,4 +261,4 @@ export {
|
|
|
229
261
|
detectLang,
|
|
230
262
|
parseSymbols
|
|
231
263
|
};
|
|
232
|
-
//# sourceMappingURL=ts-parser-
|
|
264
|
+
//# sourceMappingURL=ts-parser-PML6QIH7.js.map
|
package/dist/write.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
writeTool
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z77XMU3U.js";
|
|
4
4
|
import "./chunk-MSOAX3GC.js";
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-UMOP3VGK.js";
|
|
6
|
+
import "./chunk-RYLMSGR5.js";
|
|
7
|
+
import "./chunk-6D3UIQMF.js";
|
|
8
|
+
import "./chunk-BJ5LXLKE.js";
|
|
9
|
+
import "./chunk-R3HR3QI3.js";
|
|
10
|
+
import "./chunk-HSLDY55L.js";
|
|
11
|
+
import "./chunk-OIFY4XNP.js";
|
|
12
12
|
import "./chunk-YOHO4DVT.js";
|
|
13
13
|
import "./chunk-XHSHNOAF.js";
|
|
14
14
|
import "./chunk-XZLVJD56.js";
|
|
@@ -30,10 +30,13 @@ function regexParse(opts) {
|
|
|
30
30
|
}
|
|
31
31
|
return lo + 1;
|
|
32
32
|
}
|
|
33
|
+
const scalarBody = blockScalarBodyLines(lines);
|
|
34
|
+
const inScalarBody = (offset) => scalarBody.has(lineFromOffset(offset) - 1);
|
|
33
35
|
const anchorRegex = /(?:^|(?<=[\s:\-[{,]))&(\w[\w-]*)/gm;
|
|
34
36
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
35
37
|
const name = expectDefined(match[1]);
|
|
36
38
|
const offset = match.index ?? 0;
|
|
39
|
+
if (inScalarBody(offset)) continue;
|
|
37
40
|
const line = lineFromOffset(offset);
|
|
38
41
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
39
42
|
symbols.push(
|
|
@@ -52,6 +55,7 @@ function regexParse(opts) {
|
|
|
52
55
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
53
56
|
const name = expectDefined(match[1]);
|
|
54
57
|
const offset = match.index ?? 0;
|
|
58
|
+
if (inScalarBody(offset)) continue;
|
|
55
59
|
const line = lineFromOffset(offset);
|
|
56
60
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
57
61
|
symbols.push(
|
|
@@ -69,9 +73,10 @@ function regexParse(opts) {
|
|
|
69
73
|
const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
|
|
70
74
|
for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {
|
|
71
75
|
const indent = match[1]?.length ?? 0;
|
|
72
|
-
const key = match[2];
|
|
76
|
+
const key = unquoteKey(match[2] ?? "");
|
|
73
77
|
if (!key) continue;
|
|
74
78
|
const offset = match.index ?? 0;
|
|
79
|
+
if (inScalarBody(offset)) continue;
|
|
75
80
|
const line = lineFromOffset(offset);
|
|
76
81
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
77
82
|
const lineContent = lines[line - 1] ?? "";
|
|
@@ -79,15 +84,16 @@ function regexParse(opts) {
|
|
|
79
84
|
if (key === "---" || key === "...") continue;
|
|
80
85
|
if (indent > 12) continue;
|
|
81
86
|
const value = extractValue(content, (match.index ?? 0) + expectDefined(match[0]).length);
|
|
82
|
-
if (
|
|
87
|
+
if (BLOCK_SCALAR_VALUE.test(value)) continue;
|
|
83
88
|
const kind = isScalar(value) ? "literal" : "property";
|
|
84
89
|
const signature = `${key}: ${truncate(value, 60)}`;
|
|
85
90
|
symbols.push(makeSymbol({ name: key, kind, line, col, signature, file, lang }));
|
|
86
91
|
}
|
|
87
92
|
const listItemRegex = /^(\s*)-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
88
93
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
89
|
-
const key = expectDefined(match[3]);
|
|
94
|
+
const key = unquoteKey(expectDefined(match[3]));
|
|
90
95
|
const offset = match.index ?? 0;
|
|
96
|
+
if (inScalarBody(offset)) continue;
|
|
91
97
|
const line = lineFromOffset(offset);
|
|
92
98
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
93
99
|
const value = extractValue(content, offset + match[0]?.length);
|
|
@@ -104,10 +110,11 @@ function regexParse(opts) {
|
|
|
104
110
|
})
|
|
105
111
|
);
|
|
106
112
|
}
|
|
107
|
-
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s
|
|
113
|
+
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:[ \t]*[|>][-+0-9]*(?=[ \t]|$)/gm;
|
|
108
114
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
109
|
-
const key = expectDefined(match[2]);
|
|
115
|
+
const key = unquoteKey(expectDefined(match[2]));
|
|
110
116
|
const offset = match.index ?? 0;
|
|
117
|
+
if (inScalarBody(offset)) continue;
|
|
111
118
|
const line = lineFromOffset(offset);
|
|
112
119
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
113
120
|
symbols.push(
|
|
@@ -124,6 +131,34 @@ function regexParse(opts) {
|
|
|
124
131
|
}
|
|
125
132
|
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
126
133
|
}
|
|
134
|
+
var BLOCK_SCALAR_VALUE = /^[|>][-+0-9]*(\s|#|$)/;
|
|
135
|
+
function blockScalarBodyLines(lines) {
|
|
136
|
+
const body = /* @__PURE__ */ new Set();
|
|
137
|
+
const header = /^(\s*)(?:(?:-[ \t]+)?[^#\n]*?:|-)[ \t]*[|>][-+0-9]*[ \t]*(?:#.*)?$/;
|
|
138
|
+
for (let i = 0; i < lines.length; i++) {
|
|
139
|
+
const line = (lines[i] ?? "").replace(/\r$/, "");
|
|
140
|
+
const match = header.exec(line);
|
|
141
|
+
if (!match) continue;
|
|
142
|
+
const baseIndent = match[1]?.length ?? 0;
|
|
143
|
+
let j = i + 1;
|
|
144
|
+
for (; j < lines.length; j++) {
|
|
145
|
+
const next = (lines[j] ?? "").replace(/\r$/, "");
|
|
146
|
+
if (next.trim() === "") {
|
|
147
|
+
body.add(j);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const indent = next.length - next.trimStart().length;
|
|
151
|
+
if (indent <= baseIndent) break;
|
|
152
|
+
body.add(j);
|
|
153
|
+
}
|
|
154
|
+
i = j - 1;
|
|
155
|
+
}
|
|
156
|
+
return body;
|
|
157
|
+
}
|
|
158
|
+
function unquoteKey(key) {
|
|
159
|
+
const quoted = /^(["'])(.*)\1$/.exec(key);
|
|
160
|
+
return quoted ? quoted[2] ?? "" : key;
|
|
161
|
+
}
|
|
127
162
|
function extractValue(content, afterColonOffset) {
|
|
128
163
|
const lineEnd = content.indexOf("\n", afterColonOffset);
|
|
129
164
|
const rest = content.slice(afterColonOffset, lineEnd < 0 ? void 0 : lineEnd);
|
|
@@ -155,4 +190,4 @@ export {
|
|
|
155
190
|
detectLang,
|
|
156
191
|
parseSymbols
|
|
157
192
|
};
|
|
158
|
-
//# sourceMappingURL=yaml-parser-
|
|
193
|
+
//# sourceMappingURL=yaml-parser-7BJN544B.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack built-in tools: read/write/edit, bash/exec, grep/glob, git, fetch, test, lint, and more.",
|
|
6
6
|
"repository": {
|
|
@@ -237,10 +237,10 @@
|
|
|
237
237
|
"@msgpack/msgpack": "^3.1.3",
|
|
238
238
|
"@playwright/test": "^1.63.0",
|
|
239
239
|
"@typescript/typescript6": "^6.0.2",
|
|
240
|
-
"@wrongstack/core": "1.0.
|
|
241
|
-
"@wrongstack/kanban": "1.0.
|
|
242
|
-
"@wrongstack/persistence": "1.0.
|
|
243
|
-
"@wrongstack/primitives": "1.0.
|
|
240
|
+
"@wrongstack/core": "1.0.12",
|
|
241
|
+
"@wrongstack/kanban": "1.0.12",
|
|
242
|
+
"@wrongstack/persistence": "1.0.12",
|
|
243
|
+
"@wrongstack/primitives": "1.0.12",
|
|
244
244
|
"tree-sitter-wasm": "1.1.8",
|
|
245
245
|
"turndown": "^7.2.4",
|
|
246
246
|
"undici": "^8.10.2",
|
package/dist/chunk-4CE3RKEP.js
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseGeneric
|
|
3
|
-
} from "./chunk-SKRMUHTU.js";
|
|
4
|
-
import {
|
|
5
|
-
parseParserOutput,
|
|
6
|
-
recordParserSubprocess,
|
|
7
|
-
withSpawnGate
|
|
8
|
-
} from "./chunk-ZKC5UNGD.js";
|
|
9
|
-
import {
|
|
10
|
-
resolveWin32Command
|
|
11
|
-
} from "./chunk-XZLVJD56.js";
|
|
12
|
-
|
|
13
|
-
// src/codebase-index/py-parser.ts
|
|
14
|
-
import { spawn } from "node:child_process";
|
|
15
|
-
import * as fs from "node:fs/promises";
|
|
16
|
-
import * as os from "node:os";
|
|
17
|
-
import * as path from "node:path";
|
|
18
|
-
import { StringDecoder } from "node:string_decoder";
|
|
19
|
-
async function parseSymbols(opts) {
|
|
20
|
-
const { file, content, lang } = opts;
|
|
21
|
-
try {
|
|
22
|
-
const native = await withSpawnGate(() => syncPyParse(file, content, lang));
|
|
23
|
-
if (native !== null) return native;
|
|
24
|
-
} catch {
|
|
25
|
-
}
|
|
26
|
-
return parseGeneric({ file, content, lang: lang === "py" ? "py" : lang });
|
|
27
|
-
}
|
|
28
|
-
var PY_PARSE_SCRIPT = `import ast, json, sys, os
|
|
29
|
-
|
|
30
|
-
def get_name(node):
|
|
31
|
-
if isinstance(node, ast.Name):
|
|
32
|
-
return node.id
|
|
33
|
-
elif isinstance(node, ast.Attribute):
|
|
34
|
-
return get_name(node.value) + "." + node.attr
|
|
35
|
-
elif isinstance(node, ast.Subscript):
|
|
36
|
-
return get_name(node.value)
|
|
37
|
-
elif isinstance(node, ast.Call):
|
|
38
|
-
return get_name(node.func)
|
|
39
|
-
elif isinstance(node, ast.Constant):
|
|
40
|
-
return str(node.value)
|
|
41
|
-
return ""
|
|
42
|
-
|
|
43
|
-
def get_decorators(node):
|
|
44
|
-
decs = []
|
|
45
|
-
for dec in node.decorator_list:
|
|
46
|
-
decs.append(get_name(dec))
|
|
47
|
-
return decs
|
|
48
|
-
|
|
49
|
-
def get_bases(node):
|
|
50
|
-
bases = []
|
|
51
|
-
for base in node.bases:
|
|
52
|
-
bases.append(get_name(base))
|
|
53
|
-
return bases
|
|
54
|
-
|
|
55
|
-
def get_args(args):
|
|
56
|
-
parts = []
|
|
57
|
-
for arg in args.args:
|
|
58
|
-
parts.append(arg.arg)
|
|
59
|
-
return ", ".join(parts)
|
|
60
|
-
|
|
61
|
-
def get_returns(node):
|
|
62
|
-
if node.returns is None:
|
|
63
|
-
return ""
|
|
64
|
-
return get_name(node.returns)
|
|
65
|
-
|
|
66
|
-
class Sym:
|
|
67
|
-
def __init__(self, name, kind, line, col, signature, scope):
|
|
68
|
-
self.name = name
|
|
69
|
-
self.kind = kind
|
|
70
|
-
self.line = line
|
|
71
|
-
self.col = col
|
|
72
|
-
self.signature = signature
|
|
73
|
-
self.scope = scope
|
|
74
|
-
def to_dict(self):
|
|
75
|
-
return {
|
|
76
|
-
"name": self.name,
|
|
77
|
-
"kind": self.kind,
|
|
78
|
-
"line": self.line,
|
|
79
|
-
"col": self.col,
|
|
80
|
-
"signature": self.signature,
|
|
81
|
-
"scope": self.scope,
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
def is_private(name):
|
|
85
|
-
return name.startswith("__") and not name.endswith("__")
|
|
86
|
-
|
|
87
|
-
def leaf_name(node):
|
|
88
|
-
# Declared name of the callee: \`join\` of \`os.path.join\`. Matches how the
|
|
89
|
-
# TypeScript and Go extractors record call refs, so resolution behaves the
|
|
90
|
-
# same across languages.
|
|
91
|
-
if isinstance(node, ast.Attribute):
|
|
92
|
-
return node.attr
|
|
93
|
-
if isinstance(node, ast.Name):
|
|
94
|
-
return node.id
|
|
95
|
-
return get_name(node).split(".")[-1]
|
|
96
|
-
|
|
97
|
-
syms = []
|
|
98
|
-
refs = []
|
|
99
|
-
errors = []
|
|
100
|
-
|
|
101
|
-
try:
|
|
102
|
-
source = sys.stdin.read()
|
|
103
|
-
tree = ast.parse(source, filename=sys.argv[1])
|
|
104
|
-
except Exception as e:
|
|
105
|
-
errors.append(str(e))
|
|
106
|
-
print(json.dumps({"symbols": [], "refs": []}))
|
|
107
|
-
sys.exit(0)
|
|
108
|
-
|
|
109
|
-
# Module-level scope
|
|
110
|
-
module_scope = os.path.basename(sys.argv[1])[:-3] # strip .py
|
|
111
|
-
|
|
112
|
-
class ModuleVisitor(ast.NodeVisitor):
|
|
113
|
-
def __init__(self):
|
|
114
|
-
self.scope_stack = [module_scope]
|
|
115
|
-
|
|
116
|
-
def visit_ClassDef(self, node):
|
|
117
|
-
bases = get_bases(node)
|
|
118
|
-
decs = get_decorators(node)
|
|
119
|
-
sig = "class " + node.name
|
|
120
|
-
if bases:
|
|
121
|
-
sig += "(" + ", ".join(bases) + ")"
|
|
122
|
-
sig += ": ..."
|
|
123
|
-
syms.append(Sym(
|
|
124
|
-
name=node.name,
|
|
125
|
-
kind="class",
|
|
126
|
-
line=node.lineno,
|
|
127
|
-
col=node.col_offset,
|
|
128
|
-
signature=sig,
|
|
129
|
-
scope=".".join(self.scope_stack) + "." + node.name,
|
|
130
|
-
))
|
|
131
|
-
self.scope_stack.append(node.name)
|
|
132
|
-
self.generic_visit(node)
|
|
133
|
-
self.scope_stack.pop()
|
|
134
|
-
|
|
135
|
-
def visit_FunctionDef(self, node):
|
|
136
|
-
decs = get_decorators(node)
|
|
137
|
-
args = get_args(node.args)
|
|
138
|
-
returns = get_returns(node)
|
|
139
|
-
is_async = isinstance(node, ast.AsyncFunctionDef)
|
|
140
|
-
|
|
141
|
-
kind = "function"
|
|
142
|
-
prefix = "def "
|
|
143
|
-
if decs:
|
|
144
|
-
for d in decs:
|
|
145
|
-
if d.endswith(".staticmethod"):
|
|
146
|
-
kind = "staticmethod"
|
|
147
|
-
elif d.endswith(".classmethod"):
|
|
148
|
-
kind = "classmethod"
|
|
149
|
-
elif d == "property":
|
|
150
|
-
kind = "property"
|
|
151
|
-
|
|
152
|
-
if is_async:
|
|
153
|
-
kind = "async_" + kind
|
|
154
|
-
|
|
155
|
-
sig = f"{prefix}{node.name}({args})"
|
|
156
|
-
if returns:
|
|
157
|
-
sig += f" -> {returns}"
|
|
158
|
-
scope = ".".join(self.scope_stack) + "." + node.name
|
|
159
|
-
|
|
160
|
-
syms.append(Sym(
|
|
161
|
-
name=node.name,
|
|
162
|
-
kind=kind,
|
|
163
|
-
line=node.lineno,
|
|
164
|
-
col=node.col_offset,
|
|
165
|
-
signature=sig,
|
|
166
|
-
scope=scope,
|
|
167
|
-
))
|
|
168
|
-
# Don't descend into function bodies to avoid local symbols
|
|
169
|
-
# self.generic_visit(node)
|
|
170
|
-
|
|
171
|
-
def visit_AsyncFunctionDef(self, node):
|
|
172
|
-
# Treat as function
|
|
173
|
-
self.visit_FunctionDef(node)
|
|
174
|
-
|
|
175
|
-
def visit_Assign(self, node):
|
|
176
|
-
for target in node.targets:
|
|
177
|
-
if isinstance(target, ast.Name):
|
|
178
|
-
name = target.id
|
|
179
|
-
if is_private(name):
|
|
180
|
-
continue
|
|
181
|
-
# Infer constness from UPPER_CASE naming
|
|
182
|
-
kind = "const" if name.isupper() else "var"
|
|
183
|
-
col = target.col_offset if hasattr(target, 'col_offset') else 0
|
|
184
|
-
syms.append(Sym(
|
|
185
|
-
name=name,
|
|
186
|
-
kind=kind,
|
|
187
|
-
line=node.lineno,
|
|
188
|
-
col=col,
|
|
189
|
-
signature=f"{name} = ...",
|
|
190
|
-
scope=".".join(self.scope_stack),
|
|
191
|
-
))
|
|
192
|
-
|
|
193
|
-
def visit_AnnAssign(self, node):
|
|
194
|
-
if isinstance(node.target, ast.Name):
|
|
195
|
-
name = node.target.id
|
|
196
|
-
if is_private(name):
|
|
197
|
-
return
|
|
198
|
-
kind = "const" if name.isupper() else "var"
|
|
199
|
-
col = node.target.col_offset if hasattr(node.target, 'col_offset') else 0
|
|
200
|
-
sig = f"{name}: {get_name(node.annotation)}"
|
|
201
|
-
if node.value:
|
|
202
|
-
sig += " = ..."
|
|
203
|
-
syms.append(Sym(
|
|
204
|
-
name=name,
|
|
205
|
-
kind=kind,
|
|
206
|
-
line=node.lineno,
|
|
207
|
-
col=col,
|
|
208
|
-
signature=sig,
|
|
209
|
-
scope=".".join(self.scope_stack),
|
|
210
|
-
))
|
|
211
|
-
|
|
212
|
-
def visit_Import(self, node):
|
|
213
|
-
for alias in node.names:
|
|
214
|
-
name = alias.asname or alias.name
|
|
215
|
-
syms.append(Sym(
|
|
216
|
-
name=name,
|
|
217
|
-
kind="import",
|
|
218
|
-
line=node.lineno,
|
|
219
|
-
col=node.col_offset,
|
|
220
|
-
signature=f"import {alias.name}",
|
|
221
|
-
scope=".".join(self.scope_stack),
|
|
222
|
-
))
|
|
223
|
-
|
|
224
|
-
def visit_ImportFrom(self, node):
|
|
225
|
-
module = node.module or ""
|
|
226
|
-
for alias in node.names:
|
|
227
|
-
name = alias.asname or alias.name
|
|
228
|
-
syms.append(Sym(
|
|
229
|
-
name=name,
|
|
230
|
-
kind="import",
|
|
231
|
-
line=node.lineno,
|
|
232
|
-
col=node.col_offset,
|
|
233
|
-
signature=f"from {module} import {alias.name}",
|
|
234
|
-
scope=".".join(self.scope_stack),
|
|
235
|
-
))
|
|
236
|
-
|
|
237
|
-
visitor = ModuleVisitor()
|
|
238
|
-
visitor.visit(tree)
|
|
239
|
-
|
|
240
|
-
# Refs need a separate full walk: ModuleVisitor deliberately does not descend
|
|
241
|
-
# into function bodies (it would index locals as symbols), but that is exactly
|
|
242
|
-
# where the calls are.
|
|
243
|
-
for node in ast.walk(tree):
|
|
244
|
-
if isinstance(node, ast.Call):
|
|
245
|
-
name = leaf_name(node.func)
|
|
246
|
-
if name:
|
|
247
|
-
refs.append({"toName": name, "callType": "call", "line": node.lineno})
|
|
248
|
-
elif isinstance(node, ast.Import):
|
|
249
|
-
for alias in node.names:
|
|
250
|
-
refs.append({
|
|
251
|
-
"toName": alias.name.split(".")[-1],
|
|
252
|
-
"callType": "import",
|
|
253
|
-
"line": node.lineno,
|
|
254
|
-
"module": alias.name,
|
|
255
|
-
})
|
|
256
|
-
elif isinstance(node, ast.ImportFrom):
|
|
257
|
-
# PEP 328: node.level is the number of leading dots. Preserving them is
|
|
258
|
-
# what lets the resolver walk up from the importing file's package \u2014
|
|
259
|
-
# dropping them made \`from .foo import X\` indistinguishable from an
|
|
260
|
-
# absolute \`foo\`.
|
|
261
|
-
module = ("." * (node.level or 0)) + (node.module or "")
|
|
262
|
-
for alias in node.names:
|
|
263
|
-
refs.append({
|
|
264
|
-
"toName": alias.name,
|
|
265
|
-
"callType": "import",
|
|
266
|
-
"line": node.lineno,
|
|
267
|
-
"module": module,
|
|
268
|
-
})
|
|
269
|
-
elif isinstance(node, ast.ClassDef):
|
|
270
|
-
for base in node.bases:
|
|
271
|
-
name = leaf_name(base)
|
|
272
|
-
if name:
|
|
273
|
-
refs.append({"toName": name, "callType": "inherit", "line": node.lineno})
|
|
274
|
-
|
|
275
|
-
print(json.dumps({"symbols": [s.to_dict() for s in syms], "refs": refs}))
|
|
276
|
-
`;
|
|
277
|
-
async function resolvePython() {
|
|
278
|
-
const candidates = process.platform === "win32" ? ["python3", "python", "py"] : ["python3", "python"];
|
|
279
|
-
for (const name of candidates) {
|
|
280
|
-
const resolved = resolveWin32Command(name);
|
|
281
|
-
if (!await commandIsAvailable(resolved)) continue;
|
|
282
|
-
return resolved;
|
|
283
|
-
}
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
function commandIsAvailable(command) {
|
|
287
|
-
return new Promise((resolve) => {
|
|
288
|
-
let settled = false;
|
|
289
|
-
const proc = spawn(command, ["--version"], {
|
|
290
|
-
stdio: "ignore",
|
|
291
|
-
windowsHide: true
|
|
292
|
-
});
|
|
293
|
-
const finish = (available) => {
|
|
294
|
-
if (settled) return;
|
|
295
|
-
settled = true;
|
|
296
|
-
clearTimeout(timer);
|
|
297
|
-
resolve(available);
|
|
298
|
-
};
|
|
299
|
-
const timer = setTimeout(() => {
|
|
300
|
-
proc.kill("SIGKILL");
|
|
301
|
-
finish(false);
|
|
302
|
-
}, 5e3);
|
|
303
|
-
timer.unref?.();
|
|
304
|
-
proc.once("error", () => finish(false));
|
|
305
|
-
proc.once("close", (code) => finish(code === 0));
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
309
|
-
return new Promise((resolve, reject) => {
|
|
310
|
-
let settled = false;
|
|
311
|
-
recordParserSubprocess();
|
|
312
|
-
const proc = spawn(pyBinary, [scriptPath, filePath], {
|
|
313
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
314
|
-
windowsHide: true
|
|
315
|
-
});
|
|
316
|
-
proc.on("error", (err) => {
|
|
317
|
-
if (settled) return;
|
|
318
|
-
settled = true;
|
|
319
|
-
reject(err);
|
|
320
|
-
});
|
|
321
|
-
proc.stdin?.write(content);
|
|
322
|
-
proc.stdin?.end();
|
|
323
|
-
let stdout = "";
|
|
324
|
-
const stdoutDecoder = new StringDecoder("utf8");
|
|
325
|
-
proc.stdout?.on("data", (chunk) => {
|
|
326
|
-
stdout += stdoutDecoder.write(chunk);
|
|
327
|
-
});
|
|
328
|
-
proc.stderr?.resume();
|
|
329
|
-
const timer = setTimeout(() => {
|
|
330
|
-
if (settled) return;
|
|
331
|
-
settled = true;
|
|
332
|
-
proc.kill("SIGKILL");
|
|
333
|
-
reject(new Error("timeout"));
|
|
334
|
-
}, 15e3);
|
|
335
|
-
timer.unref?.();
|
|
336
|
-
proc.on("close", (code) => {
|
|
337
|
-
if (settled) return;
|
|
338
|
-
settled = true;
|
|
339
|
-
clearTimeout(timer);
|
|
340
|
-
stdout += stdoutDecoder.end();
|
|
341
|
-
resolve({ code, stdout });
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
var _cachedScriptPath = null;
|
|
346
|
-
var cachedPyBinary;
|
|
347
|
-
function resolvePythonBinary() {
|
|
348
|
-
cachedPyBinary ??= resolvePython();
|
|
349
|
-
return cachedPyBinary;
|
|
350
|
-
}
|
|
351
|
-
async function syncPyParse(filePath, content, lang) {
|
|
352
|
-
try {
|
|
353
|
-
if (!_cachedScriptPath) {
|
|
354
|
-
const tmpDir = path.join(os.tmpdir(), "ws-py-parse");
|
|
355
|
-
await fs.mkdir(tmpDir, { recursive: true });
|
|
356
|
-
_cachedScriptPath = path.join(tmpDir, "parse.py");
|
|
357
|
-
await fs.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
358
|
-
}
|
|
359
|
-
cachedPyBinary ??= resolvePython();
|
|
360
|
-
const pyBinary = await cachedPyBinary;
|
|
361
|
-
if (!pyBinary) return null;
|
|
362
|
-
const { code, stdout } = await spawnPyParser(pyBinary, _cachedScriptPath, filePath, content);
|
|
363
|
-
if (code !== 0 || !stdout.trim()) {
|
|
364
|
-
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
365
|
-
}
|
|
366
|
-
const { symbols: raw, refs } = parseParserOutput(stdout, lang);
|
|
367
|
-
const symbols = raw.map((s) => ({
|
|
368
|
-
id: 0,
|
|
369
|
-
lang,
|
|
370
|
-
kind: s.kind,
|
|
371
|
-
name: s.name,
|
|
372
|
-
file: filePath,
|
|
373
|
-
line: s.line,
|
|
374
|
-
col: s.col,
|
|
375
|
-
signature: s.signature ?? "",
|
|
376
|
-
docComment: "",
|
|
377
|
-
scope: s.scope ?? "",
|
|
378
|
-
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
379
|
-
}));
|
|
380
|
-
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
381
|
-
} catch {
|
|
382
|
-
return null;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
export {
|
|
387
|
-
parseSymbols,
|
|
388
|
-
resolvePythonBinary
|
|
389
|
-
};
|
|
390
|
-
//# sourceMappingURL=chunk-4CE3RKEP.js.map
|