codn_ts
Version:
智能代码分析工具 - 支持语义搜索、调用链分析和代码结构可视化,对大模型/AI agent 友好
278 lines • 12.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeProjectReferences = analyzeProjectReferences;
exports.analyzeSymbolReferences = analyzeSymbolReferences;
const lsp_core_1 = require("./lsp_core");
const symbol_utils_1 = require("./symbol_utils");
const os_utils_1 = require("./os_utils");
const file_utils_1 = require("./file_utils");
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const glob_1 = require("glob");
const logger = {
trace: (...args) => console.error("TRACE", ...args),
error: (...args) => console.error("ERROR", ...args),
};
async function analyzeProjectReferences(projectRoot) {
const rootUri = (0, file_utils_1.toFileUri)(projectRoot);
const client = new lsp_core_1.LSPClient(rootUri, {
...lsp_core_1.DEFAULT_CONFIG,
timeout: 60000,
logLevel: "debug",
});
const languages = (0, os_utils_1.detectDominantLanguages)(projectRoot, {
ignoredDirs: new Set(["node_modules", "dist"]),
topN: 1,
});
const lang = languages[0];
const file_ext = (0, os_utils_1.getLanguageFileExtensions)(lang);
try {
logger.trace("Initializing LSP client...");
await client.start(lang);
const files = await (0, glob_1.glob)(`**/*.{${file_ext}}`, {
cwd: projectRoot,
ignore: "node_modules/**",
absolute: true,
});
const allReferences = [];
for (const filePath of files) {
const fileUri = (0, file_utils_1.toFileUri)(filePath);
const relativePath = path.relative(projectRoot, filePath);
try {
const content = fs.readFileSync(filePath, "utf-8");
await client.sendDidOpen(fileUri, content, "typescript");
const symbols = await client.sendDocumentSymbol(fileUri);
if (!symbols)
continue;
for (const symbol of symbols) {
if (isAnalyzableSymbol(symbol.kind)) {
const str_code = await client.readFile(fileUri);
const lines = str_code.split("\n");
const line = lines
.slice(symbol.location.range.start.line, symbol.location.range.end.line + 1)
.join("\n");
const pos = (0, symbol_utils_1.checkRealFuncChar)(line, symbol.location.range.start.line, symbol.location.range.start.character, symbol.name, symbol.kind, fileUri);
if (pos.character < 0)
continue;
const refs = await client.sendReferences(fileUri, pos.line, pos.character);
if (refs?.result?.length) {
for (const ref of refs.result) {
allReferences.push(createReferenceTriple(projectRoot, ref, relativePath, symbol, pos));
}
}
}
}
await client.sendDidClose(fileUri);
}
catch (err) {
logger.error(`Error analyzing ${relativePath}:`, err);
}
}
return allReferences;
}
finally {
await client.shutdown();
}
}
async function analyzeSymbolReferences(projectRoot, symbolName, options = {}) {
const { includeExternal = false, maxDepth = 3, caseSensitive = false, } = options;
const rootUri = (0, file_utils_1.toFileUri)(projectRoot);
const client = new lsp_core_1.LSPClient(rootUri, {
...lsp_core_1.DEFAULT_CONFIG,
timeout: 60000,
logLevel: "debug",
});
const languages = (0, os_utils_1.detectDominantLanguages)(projectRoot, {
ignoredDirs: new Set(["node_modules", "dist"]),
topN: 1,
});
const lang = languages[0];
const file_ext = (0, os_utils_1.getLanguageFileExtensions)(lang);
try {
logger.trace("Initializing LSP client...");
await client.start(lang);
const files = await (0, glob_1.glob)(`**/*.{${file_ext}}`, {
cwd: projectRoot,
ignore: "node_modules/**",
absolute: true,
});
const symbolReferences = [];
const allReferences = [];
// 第一步:收集所有引用关系
for (const filePath of files) {
const fileUri = (0, file_utils_1.toFileUri)(filePath);
const relativePath = path.relative(projectRoot, filePath);
try {
const content = fs.readFileSync(filePath, "utf-8");
await client.sendDidOpen(fileUri, content, "typescript");
const symbols = await client.sendDocumentSymbol(fileUri);
if (!symbols)
continue;
for (const symbol of symbols) {
if (isAnalyzableSymbol(symbol.kind)) {
const str_code = await client.readFile(fileUri);
const lines = str_code.split("\n");
const line = lines
.slice(symbol.location.range.start.line, symbol.location.range.end.line + 1)
.join("\n");
const pos = (0, symbol_utils_1.checkRealFuncChar)(line, symbol.location.range.start.line, symbol.location.range.start.character, symbol.name, symbol.kind, fileUri);
if (pos.character < 0)
continue;
const refs = await client.sendReferences(fileUri, pos.line, pos.character);
if (refs?.result?.length) {
for (const ref of refs.result) {
allReferences.push(createReferenceTriple(projectRoot, ref, relativePath, symbol, pos));
}
}
}
}
await client.sendDidClose(fileUri);
}
catch (err) {
logger.error(`Error analyzing ${relativePath}:`, err);
}
}
// 第二步:查找匹配的符号
for (const filePath of files) {
const fileUri = (0, file_utils_1.toFileUri)(filePath);
const relativePath = path.relative(projectRoot, filePath);
try {
const content = fs.readFileSync(filePath, "utf-8");
await client.sendDidOpen(fileUri, content, "typescript");
const symbols = await client.sendDocumentSymbol(fileUri);
if (!symbols)
continue;
for (const symbol of symbols) {
if (isAnalyzableSymbol(symbol.kind)) {
const symbolMatch = caseSensitive
? symbol.name === symbolName
: symbol.name.toLowerCase() === symbolName.toLowerCase();
if (symbolMatch) {
const str_code = await client.readFile(fileUri);
const lines = str_code.split("\n");
const line = lines
.slice(symbol.location.range.start.line, symbol.location.range.end.line + 1)
.join("\n");
const pos = (0, symbol_utils_1.checkRealFuncChar)(line, symbol.location.range.start.line, symbol.location.range.start.character, symbol.name, symbol.kind, fileUri);
if (pos.character < 0)
continue;
// 查找调用方(谁调用了这个符号)
const callers = allReferences.filter((ref) => ref.callee === `${relativePath}:${symbol.name}`);
// 查找被调用方(这个符号调用了谁)
const callees = allReferences.filter((ref) => ref.caller === `${relativePath}:${symbol.name}`);
// 过滤外部调用
const filteredCallers = includeExternal
? callers
: callers.filter((ref) => !ref.caller.includes("node_modules"));
const filteredCallees = includeExternal
? callees
: callees.filter((ref) => !ref.callee.includes("node_modules"));
symbolReferences.push({
symbol: symbol.name,
file: relativePath,
line: pos.line + 1,
column: pos.character + 1,
kind: getSymbolKindName(symbol.kind),
callers: filteredCallers,
callees: filteredCallees,
definition: {
file: relativePath,
line: pos.line + 1,
column: pos.character + 1,
content: line.trim(),
},
});
}
}
}
await client.sendDidClose(fileUri);
}
catch (err) {
logger.error(`Error analyzing ${relativePath}:`, err);
}
}
return symbolReferences;
}
finally {
await client.shutdown();
}
}
// Helper functions
function isAnalyzableSymbol(kind) {
const analyzableKinds = new Set([
lsp_core_1.SymbolKind.Function,
lsp_core_1.SymbolKind.Method,
lsp_core_1.SymbolKind.Class,
lsp_core_1.SymbolKind.Interface,
lsp_core_1.SymbolKind.Property,
]);
return analyzableKinds.has(kind);
}
function createReferenceTriple(projectRoot, ref, relativePath, symbol, pos) {
const absolutePath = (0, file_utils_1.fileUriToPath)(ref.uri);
const callerFile = path.relative(projectRoot, absolutePath);
const callerPos = `${ref.range.start.line + 1}:${ref.range.start.character + 1}`;
return {
caller: `${callerFile}:${callerPos}`,
callee: `${relativePath}:${symbol.name}`,
location: `${relativePath}:${pos.line + 1}:${pos.character + 1}`,
};
}
function getSymbolKindName(kind) {
switch (kind) {
case lsp_core_1.SymbolKind.Function:
return "function";
case lsp_core_1.SymbolKind.Method:
return "method";
case lsp_core_1.SymbolKind.Class:
return "class";
case lsp_core_1.SymbolKind.Interface:
return "interface";
case lsp_core_1.SymbolKind.Property:
return "property";
case lsp_core_1.SymbolKind.Variable:
return "variable";
case lsp_core_1.SymbolKind.Constant:
return "constant";
case lsp_core_1.SymbolKind.Constructor:
return "constructor";
case lsp_core_1.SymbolKind.Field:
return "field";
default:
return "unknown";
}
}
//# sourceMappingURL=reference_analyzer.js.map