@codemap-ai/code-index 1.0.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/LICENSE +21 -0
- package/dist/file-discovery.d.ts +36 -0
- package/dist/file-discovery.d.ts.map +1 -0
- package/dist/file-discovery.js +204 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/language-utils.d.ts +10 -0
- package/dist/language-utils.d.ts.map +1 -0
- package/dist/language-utils.js +75 -0
- package/dist/parsers/dart.d.ts +4 -0
- package/dist/parsers/dart.d.ts.map +1 -0
- package/dist/parsers/dart.js +353 -0
- package/dist/parsers/index.d.ts +11 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +32 -0
- package/dist/parsers/java.d.ts +4 -0
- package/dist/parsers/java.d.ts.map +1 -0
- package/dist/parsers/java.js +222 -0
- package/dist/parsers/kotlin.d.ts +4 -0
- package/dist/parsers/kotlin.d.ts.map +1 -0
- package/dist/parsers/kotlin.js +221 -0
- package/dist/parsers/php.d.ts +4 -0
- package/dist/parsers/php.d.ts.map +1 -0
- package/dist/parsers/php.js +215 -0
- package/dist/parsers/python.d.ts +4 -0
- package/dist/parsers/python.d.ts.map +1 -0
- package/dist/parsers/python.js +200 -0
- package/dist/parsers/shared.d.ts +30 -0
- package/dist/parsers/shared.d.ts.map +1 -0
- package/dist/parsers/shared.js +200 -0
- package/dist/parsers/types.d.ts +93 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/types.js +12 -0
- package/dist/parsers/typescript.d.ts +5 -0
- package/dist/parsers/typescript.d.ts.map +1 -0
- package/dist/parsers/typescript.js +549 -0
- package/dist/ts-resolver.d.ts +17 -0
- package/dist/ts-resolver.d.ts.map +1 -0
- package/dist/ts-resolver.js +110 -0
- package/package.json +30 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseKotlinFile = parseKotlinFile;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const web_tree_sitter_1 = require("web-tree-sitter");
|
|
9
|
+
const shared_js_1 = require("./shared.js");
|
|
10
|
+
const types_js_1 = require("./types.js");
|
|
11
|
+
let parserReady = null;
|
|
12
|
+
function getKotlinParser() {
|
|
13
|
+
if (!parserReady) {
|
|
14
|
+
parserReady = (async () => {
|
|
15
|
+
await web_tree_sitter_1.Parser.init();
|
|
16
|
+
const wasmPath = node_path_1.default.resolve(node_path_1.default.dirname(require.resolve("tree-sitter-wasms/package.json")), "out", "tree-sitter-kotlin.wasm");
|
|
17
|
+
const language = await web_tree_sitter_1.Language.load(wasmPath);
|
|
18
|
+
const parser = new web_tree_sitter_1.Parser();
|
|
19
|
+
parser.setLanguage(language);
|
|
20
|
+
return { parser, language };
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
return parserReady;
|
|
24
|
+
}
|
|
25
|
+
async function parseKotlinFile(file, projectImportId) {
|
|
26
|
+
const content = file.content ?? "";
|
|
27
|
+
if (!content.trim())
|
|
28
|
+
return { ...types_js_1.EMPTY_SEMANTICS };
|
|
29
|
+
let parser;
|
|
30
|
+
try {
|
|
31
|
+
({ parser } = await getKotlinParser());
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return parseKotlinFileWithRegexFallback(file, projectImportId);
|
|
35
|
+
}
|
|
36
|
+
const tree = parser.parse(content);
|
|
37
|
+
if (!tree)
|
|
38
|
+
return parseKotlinFileWithRegexFallback(file, projectImportId);
|
|
39
|
+
const semantics = createEmptySemantics();
|
|
40
|
+
const lines = content.split(/\r?\n/);
|
|
41
|
+
const seenSymbols = new Set();
|
|
42
|
+
const seenImports = new Set();
|
|
43
|
+
walkKotlinAst(tree.rootNode, (node) => {
|
|
44
|
+
const line = node.startPosition.row + 1;
|
|
45
|
+
const col = node.startPosition.column;
|
|
46
|
+
const packageName = parseKotlinPackage(node.text);
|
|
47
|
+
if (packageName) {
|
|
48
|
+
addKotlinSymbol(semantics, seenSymbols, file, "namespace", packageName, lines[node.startPosition.row] ?? node.text, line, col, node.endPosition.row + 1, node.endPosition.column);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const importName = parseKotlinImport(node.text);
|
|
52
|
+
if (importName) {
|
|
53
|
+
addKotlinImport(semantics, seenImports, file, projectImportId, importName, line, col, node.endPosition.row + 1, node.endPosition.column);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const symbol = parseKotlinSymbol(node.text, isKotlinMemberNode(node));
|
|
57
|
+
if (!symbol)
|
|
58
|
+
return;
|
|
59
|
+
addKotlinSymbol(semantics, seenSymbols, file, symbol.kind, symbol.displayName, lines[node.startPosition.row] ?? node.text, line, col, node.endPosition.row + 1, node.endPosition.column, findParentKotlinSymbolLocalKey(node, file.path));
|
|
60
|
+
});
|
|
61
|
+
return semantics;
|
|
62
|
+
}
|
|
63
|
+
function createEmptySemantics() {
|
|
64
|
+
return {
|
|
65
|
+
symbols: [],
|
|
66
|
+
imports: [],
|
|
67
|
+
exports: [],
|
|
68
|
+
relationships: [],
|
|
69
|
+
calls: [],
|
|
70
|
+
issues: [],
|
|
71
|
+
externalSymbols: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function walkKotlinAst(node, visitor) {
|
|
75
|
+
visitor(node);
|
|
76
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
77
|
+
const child = node.child(i);
|
|
78
|
+
if (child)
|
|
79
|
+
walkKotlinAst(child, visitor);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function parseKotlinPackage(text) {
|
|
83
|
+
return text.trim().match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/)?.[1] ?? null;
|
|
84
|
+
}
|
|
85
|
+
function parseKotlinImport(text) {
|
|
86
|
+
return text.trim().match(/^import\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:\.\*)?)(?:\s+as\s+[A-Za-z_]\w*)?/)?.[1] ?? null;
|
|
87
|
+
}
|
|
88
|
+
function parseKotlinSymbol(text, isMemberNode) {
|
|
89
|
+
const trimmed = text.trim();
|
|
90
|
+
const typeMatch = trimmed.match(/^(?:(?:public|protected|private|internal|open|abstract|sealed|data|value|inner|companion)\s+)*(class|interface|object|enum\s+class)\s+([A-Za-z_]\w*)/);
|
|
91
|
+
if (typeMatch?.[1] && typeMatch[2]) {
|
|
92
|
+
const kind = typeMatch[1] === "interface"
|
|
93
|
+
? "interface"
|
|
94
|
+
: typeMatch[1] === "enum class"
|
|
95
|
+
? "enum"
|
|
96
|
+
: "class";
|
|
97
|
+
return { displayName: typeMatch[2], kind };
|
|
98
|
+
}
|
|
99
|
+
const funMatch = trimmed.match(/^(?:(?:public|protected|private|internal|open|override|suspend|inline|tailrec|operator|infix|external)\s+)*fun\s+(?:[A-Za-z_]\w*\.)?([A-Za-z_]\w*)\s*(?:<[^>{}]+>)?\(/);
|
|
100
|
+
if (funMatch?.[1]) {
|
|
101
|
+
return { displayName: funMatch[1], kind: isMemberNode ? "method" : "function" };
|
|
102
|
+
}
|
|
103
|
+
const propertyMatch = trimmed.match(/^(?:(?:public|protected|private|internal|open|override|lateinit|const)\s+)*(val|var)\s+([A-Za-z_]\w*)\b/);
|
|
104
|
+
if (propertyMatch?.[2]) {
|
|
105
|
+
return { displayName: propertyMatch[2], kind: isMemberNode ? "property" : "variable" };
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function isKotlinMemberNode(node) {
|
|
110
|
+
let current = node.parent;
|
|
111
|
+
while (current) {
|
|
112
|
+
if (/^(?:(?:public|protected|private|internal|open|abstract|sealed|data|value|inner|companion)\s+)*(class|interface|object|enum\s+class)\s+/.test(current.text.trimStart())) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
current = current.parent;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
function findParentKotlinSymbolLocalKey(node, filePath) {
|
|
120
|
+
let current = node.parent;
|
|
121
|
+
while (current) {
|
|
122
|
+
const parentSymbol = parseKotlinSymbol(current.text, false);
|
|
123
|
+
if (parentSymbol && ["class", "interface", "enum"].includes(parentSymbol.kind)) {
|
|
124
|
+
return (0, shared_js_1.buildLocalSymbolKey)(filePath, parentSymbol.kind, parentSymbol.displayName);
|
|
125
|
+
}
|
|
126
|
+
current = current.parent;
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
function addKotlinImport(semantics, seenImports, file, projectImportId, moduleSpecifier, line, col, endLine, endCol) {
|
|
131
|
+
const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, "import", moduleSpecifier, line, col);
|
|
132
|
+
if (seenImports.has(localKey))
|
|
133
|
+
return;
|
|
134
|
+
seenImports.add(localKey);
|
|
135
|
+
semantics.imports.push({
|
|
136
|
+
localKey,
|
|
137
|
+
moduleSpecifier,
|
|
138
|
+
importKind: "import",
|
|
139
|
+
isTypeOnly: false,
|
|
140
|
+
importedNames: [moduleSpecifier.split(".").pop() ?? moduleSpecifier],
|
|
141
|
+
line,
|
|
142
|
+
col,
|
|
143
|
+
endLine,
|
|
144
|
+
endCol,
|
|
145
|
+
resolutionKind: "package",
|
|
146
|
+
targetPathText: null,
|
|
147
|
+
targetExternalSymbolKey: `kotlin:${moduleSpecifier}`,
|
|
148
|
+
});
|
|
149
|
+
semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
|
|
150
|
+
}
|
|
151
|
+
function addKotlinSymbol(semantics, seenSymbols, file, kind, displayName, signature, line, col, endLine, endCol, parentSymbolLocalKey) {
|
|
152
|
+
const stableKey = (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, line);
|
|
153
|
+
if (seenSymbols.has(stableKey))
|
|
154
|
+
return;
|
|
155
|
+
seenSymbols.add(stableKey);
|
|
156
|
+
semantics.symbols.push({
|
|
157
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
|
|
158
|
+
stableKey,
|
|
159
|
+
displayName,
|
|
160
|
+
kind,
|
|
161
|
+
language: file.language,
|
|
162
|
+
signature: signature.trim(),
|
|
163
|
+
returnType: null,
|
|
164
|
+
doc: null,
|
|
165
|
+
isExported: true,
|
|
166
|
+
isDefaultExport: false,
|
|
167
|
+
line,
|
|
168
|
+
col,
|
|
169
|
+
endLine,
|
|
170
|
+
endCol,
|
|
171
|
+
parentSymbolLocalKey,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function parseKotlinFileWithRegexFallback(file, projectImportId) {
|
|
175
|
+
const semantics = createEmptySemantics();
|
|
176
|
+
const lines = (0, shared_js_1.maskCommentsAndTemplateLiterals)(file.content ?? "").split(/\r?\n/);
|
|
177
|
+
const originalLines = (file.content ?? "").split(/\r?\n/);
|
|
178
|
+
const seenSymbols = new Set();
|
|
179
|
+
const seenImports = new Set();
|
|
180
|
+
const containerStack = [];
|
|
181
|
+
let braceDepth = 0;
|
|
182
|
+
lines.forEach((line, index) => {
|
|
183
|
+
const originalLine = originalLines[index] ?? line;
|
|
184
|
+
const lineNumber = index + 1;
|
|
185
|
+
while (containerStack.length > 0 && braceDepth < containerStack[containerStack.length - 1].depth) {
|
|
186
|
+
containerStack.pop();
|
|
187
|
+
}
|
|
188
|
+
const parentContainer = containerStack[containerStack.length - 1];
|
|
189
|
+
let matchedContainer = null;
|
|
190
|
+
const packageName = parseKotlinPackage(line);
|
|
191
|
+
if (packageName) {
|
|
192
|
+
addKotlinSymbol(semantics, seenSymbols, file, "namespace", packageName, originalLine, lineNumber, line.indexOf(packageName), lineNumber, line.length);
|
|
193
|
+
}
|
|
194
|
+
const importName = parseKotlinImport(line);
|
|
195
|
+
if (importName) {
|
|
196
|
+
addKotlinImport(semantics, seenImports, file, projectImportId, importName, lineNumber, line.indexOf(importName), lineNumber, line.length);
|
|
197
|
+
}
|
|
198
|
+
const typeSymbol = parseKotlinSymbol(line, false);
|
|
199
|
+
if (typeSymbol && ["class", "interface", "enum"].includes(typeSymbol.kind)) {
|
|
200
|
+
addKotlinSymbol(semantics, seenSymbols, file, typeSymbol.kind, typeSymbol.displayName, originalLine, lineNumber, line.indexOf(typeSymbol.displayName), lineNumber, line.length);
|
|
201
|
+
matchedContainer = {
|
|
202
|
+
kind: typeSymbol.kind,
|
|
203
|
+
displayName: typeSymbol.displayName,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const funSymbol = parseKotlinSymbol(line, Boolean(parentContainer));
|
|
207
|
+
if (funSymbol && (funSymbol.kind === "function" || funSymbol.kind === "method" || funSymbol.kind === "property" || funSymbol.kind === "variable")) {
|
|
208
|
+
const parentSymbolLocalKey = parentContainer
|
|
209
|
+
? (0, shared_js_1.buildLocalSymbolKey)(file.path, parentContainer.kind, parentContainer.displayName)
|
|
210
|
+
: undefined;
|
|
211
|
+
addKotlinSymbol(semantics, seenSymbols, file, funSymbol.kind, funSymbol.displayName, originalLine, lineNumber, line.indexOf(funSymbol.displayName), lineNumber, line.length, parentSymbolLocalKey);
|
|
212
|
+
}
|
|
213
|
+
const openBraces = (line.match(/{/g) ?? []).length;
|
|
214
|
+
const closeBraces = (line.match(/}/g) ?? []).length;
|
|
215
|
+
if (matchedContainer && openBraces > 0) {
|
|
216
|
+
containerStack.push({ ...matchedContainer, depth: braceDepth + 1 });
|
|
217
|
+
}
|
|
218
|
+
braceDepth += openBraces - closeBraces;
|
|
219
|
+
});
|
|
220
|
+
return semantics;
|
|
221
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceFileCandidate } from "../file-discovery.js";
|
|
2
|
+
import { type ParsedWorkspaceSemantics } from "./types.js";
|
|
3
|
+
export declare function parsePhpFile(file: WorkspaceFileCandidate, projectImportId: string): Promise<ParsedWorkspaceSemantics>;
|
|
4
|
+
//# sourceMappingURL=php.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"php.d.ts","sourceRoot":"","sources":["../../src/parsers/php.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAsB5E,wBAAsB,YAAY,CAChC,IAAI,EAAE,sBAAsB,EAC5B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,wBAAwB,CAAC,CA6HnC"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parsePhpFile = parsePhpFile;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const web_tree_sitter_1 = require("web-tree-sitter");
|
|
9
|
+
const shared_js_1 = require("./shared.js");
|
|
10
|
+
const types_js_1 = require("./types.js");
|
|
11
|
+
let parserReady = null;
|
|
12
|
+
function getPhpParser() {
|
|
13
|
+
if (!parserReady) {
|
|
14
|
+
parserReady = (async () => {
|
|
15
|
+
await web_tree_sitter_1.Parser.init();
|
|
16
|
+
const wasmPath = node_path_1.default.resolve(node_path_1.default.dirname(require.resolve("tree-sitter-wasms/package.json")), "out", "tree-sitter-php.wasm");
|
|
17
|
+
const language = await web_tree_sitter_1.Language.load(wasmPath);
|
|
18
|
+
const parser = new web_tree_sitter_1.Parser();
|
|
19
|
+
parser.setLanguage(language);
|
|
20
|
+
return { parser, language };
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
return parserReady;
|
|
24
|
+
}
|
|
25
|
+
async function parsePhpFile(file, projectImportId) {
|
|
26
|
+
const content = file.content ?? "";
|
|
27
|
+
if (!content.trim())
|
|
28
|
+
return { ...types_js_1.EMPTY_SEMANTICS };
|
|
29
|
+
let parser;
|
|
30
|
+
let language;
|
|
31
|
+
try {
|
|
32
|
+
({ parser, language } = await getPhpParser());
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return parsePhpFileWithRegexFallback(file, projectImportId);
|
|
36
|
+
}
|
|
37
|
+
const tree = parser.parse(content);
|
|
38
|
+
if (!tree)
|
|
39
|
+
return parsePhpFileWithRegexFallback(file, projectImportId);
|
|
40
|
+
const semantics = {
|
|
41
|
+
symbols: [],
|
|
42
|
+
imports: [],
|
|
43
|
+
exports: [],
|
|
44
|
+
relationships: [],
|
|
45
|
+
calls: [],
|
|
46
|
+
issues: [],
|
|
47
|
+
externalSymbols: [],
|
|
48
|
+
};
|
|
49
|
+
const lines = content.split(/\r?\n/);
|
|
50
|
+
// Extract symbols: namespace, class, interface, trait, enum, function, method
|
|
51
|
+
const symbolQuery = new web_tree_sitter_1.Query(language, `
|
|
52
|
+
(namespace_definition name: (namespace_name) @name) @namespace
|
|
53
|
+
(class_declaration name: (name) @name) @class
|
|
54
|
+
(interface_declaration name: (name) @name) @interface
|
|
55
|
+
(trait_declaration name: (name) @name) @trait
|
|
56
|
+
(enum_declaration name: (name) @name) @enum
|
|
57
|
+
(function_definition name: (name) @name) @function
|
|
58
|
+
(method_declaration name: (name) @name) @method
|
|
59
|
+
`);
|
|
60
|
+
for (const match of symbolQuery.matches(tree.rootNode)) {
|
|
61
|
+
const defCapture = match.captures.find((c) => ["namespace", "class", "interface", "trait", "enum", "function", "method"].includes(c.name));
|
|
62
|
+
const nameCapture = match.captures.find((c) => c.name === "name");
|
|
63
|
+
if (!defCapture || !nameCapture)
|
|
64
|
+
continue;
|
|
65
|
+
const captureName = defCapture.name;
|
|
66
|
+
const kind = captureName === "namespace" ? "namespace" : captureName;
|
|
67
|
+
const displayName = nameCapture.node.text;
|
|
68
|
+
const startPos = defCapture.node.startPosition;
|
|
69
|
+
const endPos = defCapture.node.endPosition;
|
|
70
|
+
const line = startPos.row + 1;
|
|
71
|
+
const col = startPos.column;
|
|
72
|
+
semantics.symbols.push({
|
|
73
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
|
|
74
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, line),
|
|
75
|
+
displayName,
|
|
76
|
+
kind,
|
|
77
|
+
language: file.language,
|
|
78
|
+
signature: lines[startPos.row]?.trim() ?? null,
|
|
79
|
+
returnType: null,
|
|
80
|
+
doc: null,
|
|
81
|
+
isExported: true,
|
|
82
|
+
isDefaultExport: false,
|
|
83
|
+
line,
|
|
84
|
+
col,
|
|
85
|
+
endLine: endPos.row + 1,
|
|
86
|
+
endCol: endPos.column,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Extract use statements and require/include
|
|
90
|
+
const importQuery = new web_tree_sitter_1.Query(language, `
|
|
91
|
+
(use_declaration (use_declarator (qualified_name) @name)) @use
|
|
92
|
+
(namespace_use_declaration (namespace_use_clause (qualified_name) @name)) @use
|
|
93
|
+
(require_expression (string) @path) @require
|
|
94
|
+
(require_once_expression (string) @path) @require_once
|
|
95
|
+
(include_expression (string) @path) @include
|
|
96
|
+
(include_once_expression (string) @path) @include_once
|
|
97
|
+
`);
|
|
98
|
+
const seenImportKeys = new Set();
|
|
99
|
+
for (const match of importQuery.matches(tree.rootNode)) {
|
|
100
|
+
const stmtCapture = match.captures.find((c) => ["use", "require", "require_once", "include", "include_once"].includes(c.name));
|
|
101
|
+
const valueCapture = match.captures.find((c) => c.name === "name" || c.name === "path");
|
|
102
|
+
if (!stmtCapture || !valueCapture)
|
|
103
|
+
continue;
|
|
104
|
+
const stmtNode = stmtCapture.node;
|
|
105
|
+
const startPos = stmtNode.startPosition;
|
|
106
|
+
const endPos = stmtNode.endPosition;
|
|
107
|
+
const line = startPos.row + 1;
|
|
108
|
+
const col = startPos.column;
|
|
109
|
+
const isUse = stmtCapture.name === "use";
|
|
110
|
+
const specifier = valueCapture.node.text.replace(/^['"]|['"]$/g, "");
|
|
111
|
+
const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, isUse ? "use" : "import", specifier, line, col);
|
|
112
|
+
if (seenImportKeys.has(localKey))
|
|
113
|
+
continue;
|
|
114
|
+
seenImportKeys.add(localKey);
|
|
115
|
+
semantics.imports.push({
|
|
116
|
+
localKey,
|
|
117
|
+
moduleSpecifier: specifier,
|
|
118
|
+
importKind: isUse ? "use" : "include",
|
|
119
|
+
isTypeOnly: false,
|
|
120
|
+
importedNames: [],
|
|
121
|
+
line,
|
|
122
|
+
col,
|
|
123
|
+
endLine: endPos.row + 1,
|
|
124
|
+
endCol: endPos.column,
|
|
125
|
+
resolutionKind: isUse ? "package" : "unresolved",
|
|
126
|
+
targetPathText: isUse ? null : specifier,
|
|
127
|
+
targetExternalSymbolKey: isUse ? `php:${specifier}` : null,
|
|
128
|
+
});
|
|
129
|
+
if (isUse) {
|
|
130
|
+
semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, specifier));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return semantics;
|
|
134
|
+
}
|
|
135
|
+
function parsePhpFileWithRegexFallback(file, projectImportId) {
|
|
136
|
+
const semantics = {
|
|
137
|
+
symbols: [],
|
|
138
|
+
imports: [],
|
|
139
|
+
exports: [],
|
|
140
|
+
relationships: [],
|
|
141
|
+
calls: [],
|
|
142
|
+
issues: [],
|
|
143
|
+
externalSymbols: [],
|
|
144
|
+
};
|
|
145
|
+
const lines = (file.content ?? "").split(/\r?\n/);
|
|
146
|
+
lines.forEach((line, index) => {
|
|
147
|
+
const lineNumber = index + 1;
|
|
148
|
+
const namespaceMatch = line.match(/^\s*namespace\s+([^;{]+)\s*[;{]/);
|
|
149
|
+
if (namespaceMatch?.[1]) {
|
|
150
|
+
const displayName = namespaceMatch[1].trim();
|
|
151
|
+
const col = line.indexOf(displayName);
|
|
152
|
+
semantics.symbols.push({
|
|
153
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, "namespace", displayName),
|
|
154
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, "namespace", displayName, lineNumber),
|
|
155
|
+
displayName,
|
|
156
|
+
kind: "namespace",
|
|
157
|
+
language: file.language,
|
|
158
|
+
signature: line.trim(),
|
|
159
|
+
returnType: null,
|
|
160
|
+
doc: null,
|
|
161
|
+
isExported: true,
|
|
162
|
+
isDefaultExport: false,
|
|
163
|
+
line: lineNumber,
|
|
164
|
+
col: Math.max(col, 0),
|
|
165
|
+
endLine: lineNumber,
|
|
166
|
+
endCol: Math.max(col, 0) + displayName.length,
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const useMatch = line.match(/^\s*use\s+([^;]+)\s*;/);
|
|
171
|
+
if (useMatch?.[1]) {
|
|
172
|
+
const moduleSpecifier = useMatch[1].trim();
|
|
173
|
+
const col = line.indexOf(moduleSpecifier);
|
|
174
|
+
semantics.imports.push({
|
|
175
|
+
localKey: (0, shared_js_1.buildImportLocalKey)(file.path, "use", moduleSpecifier, lineNumber, Math.max(col, 0)),
|
|
176
|
+
moduleSpecifier,
|
|
177
|
+
importKind: "use",
|
|
178
|
+
isTypeOnly: false,
|
|
179
|
+
importedNames: [moduleSpecifier.split("\\").pop() ?? moduleSpecifier],
|
|
180
|
+
line: lineNumber,
|
|
181
|
+
col: Math.max(col, 0),
|
|
182
|
+
endLine: lineNumber,
|
|
183
|
+
endCol: Math.max(col, 0) + moduleSpecifier.length,
|
|
184
|
+
resolutionKind: "package",
|
|
185
|
+
targetPathText: null,
|
|
186
|
+
targetExternalSymbolKey: `php:${moduleSpecifier}`,
|
|
187
|
+
});
|
|
188
|
+
semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const symbolMatch = line.match(/^\s*(class|interface|trait|enum|function)\s+([A-Za-z_][A-Za-z0-9_]*)/);
|
|
192
|
+
if (!symbolMatch?.[1] || !symbolMatch[2])
|
|
193
|
+
return;
|
|
194
|
+
const kind = symbolMatch[1];
|
|
195
|
+
const displayName = symbolMatch[2];
|
|
196
|
+
const col = line.indexOf(displayName);
|
|
197
|
+
semantics.symbols.push({
|
|
198
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
|
|
199
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, lineNumber),
|
|
200
|
+
displayName,
|
|
201
|
+
kind,
|
|
202
|
+
language: file.language,
|
|
203
|
+
signature: line.trim(),
|
|
204
|
+
returnType: null,
|
|
205
|
+
doc: null,
|
|
206
|
+
isExported: true,
|
|
207
|
+
isDefaultExport: false,
|
|
208
|
+
line: lineNumber,
|
|
209
|
+
col: Math.max(col, 0),
|
|
210
|
+
endLine: lineNumber,
|
|
211
|
+
endCol: Math.max(col, 0) + displayName.length,
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
return semantics;
|
|
215
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceFileCandidate } from "../file-discovery.js";
|
|
2
|
+
import { type ParsedWorkspaceSemantics } from "./types.js";
|
|
3
|
+
export declare function parsePythonFile(file: WorkspaceFileCandidate, filePathSet: Set<string>, projectImportId: string): Promise<ParsedWorkspaceSemantics>;
|
|
4
|
+
//# sourceMappingURL=python.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/parsers/python.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAqB5E,wBAAsB,eAAe,CACnC,IAAI,EAAE,sBAAsB,EAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,wBAAwB,CAAC,CA4LnC"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parsePythonFile = parsePythonFile;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const web_tree_sitter_1 = require("web-tree-sitter");
|
|
9
|
+
const shared_js_1 = require("./shared.js");
|
|
10
|
+
const types_js_1 = require("./types.js");
|
|
11
|
+
let parserReady = null;
|
|
12
|
+
function getPythonParser() {
|
|
13
|
+
if (!parserReady) {
|
|
14
|
+
parserReady = (async () => {
|
|
15
|
+
await web_tree_sitter_1.Parser.init();
|
|
16
|
+
const wasmPath = node_path_1.default.resolve(node_path_1.default.dirname(require.resolve("tree-sitter-python/package.json")), "tree-sitter-python.wasm");
|
|
17
|
+
const language = await web_tree_sitter_1.Language.load(wasmPath);
|
|
18
|
+
const parser = new web_tree_sitter_1.Parser();
|
|
19
|
+
parser.setLanguage(language);
|
|
20
|
+
return { parser, language };
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
return parserReady;
|
|
24
|
+
}
|
|
25
|
+
async function parsePythonFile(file, filePathSet, projectImportId) {
|
|
26
|
+
const content = file.content ?? "";
|
|
27
|
+
if (!content.trim())
|
|
28
|
+
return { ...types_js_1.EMPTY_SEMANTICS };
|
|
29
|
+
let parser;
|
|
30
|
+
let language;
|
|
31
|
+
try {
|
|
32
|
+
({ parser, language } = await getPythonParser());
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { ...types_js_1.EMPTY_SEMANTICS };
|
|
36
|
+
}
|
|
37
|
+
const tree = parser.parse(content);
|
|
38
|
+
if (!tree)
|
|
39
|
+
return { ...types_js_1.EMPTY_SEMANTICS };
|
|
40
|
+
const semantics = {
|
|
41
|
+
symbols: [],
|
|
42
|
+
imports: [],
|
|
43
|
+
exports: [],
|
|
44
|
+
relationships: [],
|
|
45
|
+
calls: [],
|
|
46
|
+
issues: [],
|
|
47
|
+
externalSymbols: [],
|
|
48
|
+
};
|
|
49
|
+
// Extract class and function/method definitions
|
|
50
|
+
const symbolQuery = new web_tree_sitter_1.Query(language, `
|
|
51
|
+
(class_definition name: (identifier) @name) @class
|
|
52
|
+
(function_definition name: (identifier) @name) @function
|
|
53
|
+
`);
|
|
54
|
+
for (const match of symbolQuery.matches(tree.rootNode)) {
|
|
55
|
+
const defCapture = match.captures.find((c) => c.name === "class" || c.name === "function");
|
|
56
|
+
const nameCapture = match.captures.find((c) => c.name === "name");
|
|
57
|
+
if (!defCapture || !nameCapture)
|
|
58
|
+
continue;
|
|
59
|
+
const isClass = defCapture.name === "class";
|
|
60
|
+
const displayName = nameCapture.node.text;
|
|
61
|
+
const startPos = defCapture.node.startPosition;
|
|
62
|
+
const endPos = defCapture.node.endPosition;
|
|
63
|
+
const line = startPos.row + 1;
|
|
64
|
+
const col = startPos.column;
|
|
65
|
+
const parentType = defCapture.node.parent?.type;
|
|
66
|
+
const grandparentType = defCapture.node.parent?.parent?.type;
|
|
67
|
+
const isMethod = !isClass && parentType === "block" && grandparentType === "class_definition";
|
|
68
|
+
const kind = isClass ? "class" : isMethod ? "method" : "function";
|
|
69
|
+
semantics.symbols.push({
|
|
70
|
+
localKey: (0, shared_js_1.buildLocalSymbolKey)(file.path, kind, displayName),
|
|
71
|
+
stableKey: (0, shared_js_1.buildStableSymbolKey)(file.path, kind, displayName, line),
|
|
72
|
+
displayName,
|
|
73
|
+
kind,
|
|
74
|
+
language: file.language,
|
|
75
|
+
signature: content.split("\n")[startPos.row]?.trim() ?? null,
|
|
76
|
+
returnType: null,
|
|
77
|
+
doc: null,
|
|
78
|
+
isExported: true,
|
|
79
|
+
isDefaultExport: false,
|
|
80
|
+
line,
|
|
81
|
+
col,
|
|
82
|
+
endLine: endPos.row + 1,
|
|
83
|
+
endCol: endPos.column,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Extract imports: `import X` and `from X import Y`
|
|
87
|
+
const importQuery = new web_tree_sitter_1.Query(language, `
|
|
88
|
+
(import_statement) @stmt
|
|
89
|
+
(import_from_statement) @stmt
|
|
90
|
+
`);
|
|
91
|
+
const seenImportKeys = new Set();
|
|
92
|
+
for (const match of importQuery.matches(tree.rootNode)) {
|
|
93
|
+
const stmtCapture = match.captures.find((c) => c.name === "stmt");
|
|
94
|
+
if (!stmtCapture)
|
|
95
|
+
continue;
|
|
96
|
+
const stmtNode = stmtCapture.node;
|
|
97
|
+
const startPos = stmtNode.startPosition;
|
|
98
|
+
const endPos = stmtNode.endPosition;
|
|
99
|
+
const line = startPos.row + 1;
|
|
100
|
+
const col = startPos.column;
|
|
101
|
+
if (stmtNode.type === "import_statement") {
|
|
102
|
+
for (let i = 0; i < stmtNode.childCount; i++) {
|
|
103
|
+
const child = stmtNode.child(i);
|
|
104
|
+
if (!child || (child.type !== "dotted_name" && child.type !== "aliased_import"))
|
|
105
|
+
continue;
|
|
106
|
+
const moduleNode = child.type === "aliased_import" ? child.child(0) : child;
|
|
107
|
+
if (!moduleNode)
|
|
108
|
+
continue;
|
|
109
|
+
const moduleSpecifier = moduleNode.text;
|
|
110
|
+
const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, "import", moduleSpecifier, line, col);
|
|
111
|
+
if (seenImportKeys.has(localKey))
|
|
112
|
+
continue;
|
|
113
|
+
seenImportKeys.add(localKey);
|
|
114
|
+
semantics.imports.push({
|
|
115
|
+
localKey,
|
|
116
|
+
moduleSpecifier,
|
|
117
|
+
importKind: "import",
|
|
118
|
+
isTypeOnly: false,
|
|
119
|
+
importedNames: [],
|
|
120
|
+
line,
|
|
121
|
+
col,
|
|
122
|
+
endLine: endPos.row + 1,
|
|
123
|
+
endCol: endPos.column,
|
|
124
|
+
resolutionKind: "package",
|
|
125
|
+
targetPathText: null,
|
|
126
|
+
targetExternalSymbolKey: `python:${moduleSpecifier}`,
|
|
127
|
+
});
|
|
128
|
+
semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, moduleSpecifier));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (stmtNode.type === "import_from_statement") {
|
|
132
|
+
const moduleNameNode = stmtNode.childForFieldName("module_name");
|
|
133
|
+
if (!moduleNameNode)
|
|
134
|
+
continue;
|
|
135
|
+
const rawSpecifier = moduleNameNode.text;
|
|
136
|
+
const leadingDots = rawSpecifier.match(/^(\.+)/)?.[1] ?? "";
|
|
137
|
+
const isRelative = leadingDots.length > 0;
|
|
138
|
+
const importedNames = [];
|
|
139
|
+
for (let i = 0; i < stmtNode.childCount; i++) {
|
|
140
|
+
const child = stmtNode.child(i);
|
|
141
|
+
if (!child)
|
|
142
|
+
continue;
|
|
143
|
+
if (child.type === "import_from_names" || child.type === "wildcard_import") {
|
|
144
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
145
|
+
const nameNode = child.child(j);
|
|
146
|
+
if (nameNode?.type === "identifier")
|
|
147
|
+
importedNames.push(nameNode.text);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
let targetPathText = null;
|
|
152
|
+
let resolutionKind = "package";
|
|
153
|
+
if (isRelative) {
|
|
154
|
+
const levels = leadingDots.length;
|
|
155
|
+
const cleanSpecifier = rawSpecifier.replace(/^\.+/, "");
|
|
156
|
+
let baseDir = node_path_1.default.posix.dirname(file.path);
|
|
157
|
+
for (let i = 1; i < levels; i++)
|
|
158
|
+
baseDir = node_path_1.default.posix.dirname(baseDir);
|
|
159
|
+
const candidate = cleanSpecifier
|
|
160
|
+
? `${baseDir}/${cleanSpecifier.replace(/\./g, "/")}`.replace(/\/+/g, "/")
|
|
161
|
+
: baseDir;
|
|
162
|
+
const candidates = [`${candidate}.py`, `${candidate}/__init__.py`];
|
|
163
|
+
const resolved = candidates.find((c) => filePathSet.has(c));
|
|
164
|
+
targetPathText = resolved ?? `${candidate}.py`;
|
|
165
|
+
resolutionKind = resolved ? "relative_path" : "unresolved";
|
|
166
|
+
}
|
|
167
|
+
const localKey = (0, shared_js_1.buildImportLocalKey)(file.path, "import", rawSpecifier, line, col);
|
|
168
|
+
if (seenImportKeys.has(localKey))
|
|
169
|
+
continue;
|
|
170
|
+
seenImportKeys.add(localKey);
|
|
171
|
+
semantics.imports.push({
|
|
172
|
+
localKey,
|
|
173
|
+
moduleSpecifier: rawSpecifier,
|
|
174
|
+
importKind: "import",
|
|
175
|
+
isTypeOnly: false,
|
|
176
|
+
importedNames,
|
|
177
|
+
line,
|
|
178
|
+
col,
|
|
179
|
+
endLine: endPos.row + 1,
|
|
180
|
+
endCol: endPos.column,
|
|
181
|
+
resolutionKind,
|
|
182
|
+
targetPathText,
|
|
183
|
+
targetExternalSymbolKey: isRelative ? null : `python:${rawSpecifier}`,
|
|
184
|
+
});
|
|
185
|
+
if (!isRelative) {
|
|
186
|
+
semantics.externalSymbols.push((0, shared_js_1.createExternalSymbolDraft)(projectImportId, file.language, rawSpecifier));
|
|
187
|
+
}
|
|
188
|
+
else if (resolutionKind === "unresolved") {
|
|
189
|
+
semantics.issues.push({
|
|
190
|
+
projectImportId,
|
|
191
|
+
severity: "warning",
|
|
192
|
+
code: "UNRESOLVED_IMPORT",
|
|
193
|
+
message: `Unable to resolve import "${rawSpecifier}" from ${file.path}`,
|
|
194
|
+
detailJson: { filePath: file.path, moduleSpecifier: rawSpecifier },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return semantics;
|
|
200
|
+
}
|