UNPKG

codn_ts

Version:

智能代码分析工具 - 支持语义搜索、调用链分析和代码结构可视化,对大模型/AI agent 友好

614 lines 21.9 kB
"use strict"; 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.semanticSearch = semanticSearch; const path = __importStar(require("path")); const fs = __importStar(require("fs")); const glob_1 = require("glob"); const os_utils_1 = require("./utils/os_utils"); const lsp_core_1 = require("./utils/lsp_core"); // 扩展的语义关键词映射(50+ 语义类别) const SEMANTIC_KEYWORDS = { // 认证相关 auth: [ "authentication", "login", "logout", "password", "token", "jwt", "oauth", "session", "signin", "signout", ], login: [ "authentication", "auth", "signin", "signon", "user", "password", "credentials", "login", "login", ], register: [ "signup", "registration", "create", "user", "account", "register", "enroll", ], logout: [ "signout", "logout", "logout", "logout", "logout", "logout", "logout", ], // 数据处理 parse: [ "parse", "parse", "convert", "transform", "parse", "parse", "parse", "parse", "parse", ], validate: [ "validation", "check", "verify", "validate", "sanitize", "validate", "validate", ], format: [ "format", "formatting", "format", "format", "format", "format", "format", ], transform: [ "transform", "convert", "parse", "format", "transform", "transform", ], // 网络相关 api: [ "api", "endpoint", "route", "http", "request", "response", "fetch", "axios", "rest", ], http: [ "http", "https", "request", "response", "api", "endpoint", "http", "http", ], fetch: [ "fetch", "request", "http", "api", "get", "post", "put", "delete", "fetch", ], request: ["request", "http", "api", "fetch", "axios", "request", "request"], // 数据库相关 database: [ "db", "database", "query", "sql", "mongodb", "mysql", "postgres", "database", ], query: [ "query", "database", "sql", "select", "insert", "update", "delete", "query", ], save: [ "save", "store", "persist", "database", "insert", "update", "save", "save", ], find: ["find", "search", "query", "select", "lookup", "find", "find"], // 文件操作 file: ["file", "fs", "read", "write", "upload", "download", "path", "file"], read: ["read", "file", "fs", "load", "parse", "import", "read", "read"], write: ["write", "file", "fs", "save", "export", "create", "write", "write"], upload: ["upload", "file", "multipart", "form", "upload", "upload"], // 错误处理 error: [ "error", "exception", "catch", "try", "throw", "handle", "error", "error", ], exception: ["exception", "error", "catch", "try", "throw", "exception"], catch: ["catch", "try", "error", "exception", "handle", "catch"], // 配置相关 config: ["config", "configuration", "settings", "options", "env", "config"], settings: ["settings", "config", "options", "preferences", "settings"], env: ["environment", "env", "config", "settings", "env"], // 工具函数 util: ["util", "utility", "helper", "tool", "common", "utils", "util"], helper: ["helper", "util", "utility", "assist", "support", "helper"], common: ["common", "shared", "util", "helper", "common"], // 测试相关 test: ["test", "spec", "unit", "integration", "mock", "stub", "test"], mock: ["mock", "stub", "fake", "test", "simulate", "mock"], spec: ["spec", "test", "specification", "spec"], // 业务逻辑 user: ["user", "customer", "member", "account", "user"], order: ["order", "purchase", "transaction", "order"], payment: ["payment", "pay", "billing", "invoice", "payment"], notification: ["notification", "alert", "message", "email", "sms"], // 安全相关 security: ["security", "secure", "encrypt", "hash", "security"], encrypt: ["encrypt", "encryption", "hash", "security", "encrypt"], hash: ["hash", "encrypt", "password", "security", "hash"], // 缓存相关 cache: ["cache", "caching", "redis", "memory", "cache"], redis: ["redis", "cache", "memory", "redis"], // 日志相关 log: ["log", "logging", "logger", "debug", "log"], debug: ["debug", "log", "trace", "debug"], // 时间相关 time: ["time", "date", "timestamp", "duration", "time"], date: ["date", "time", "timestamp", "date"], // 数学计算 calculate: ["calculate", "compute", "math", "sum", "calculate"], sum: ["sum", "add", "calculate", "total", "sum"], // 字符串处理 string: ["string", "text", "char", "string", "string"], replace: ["replace", "substitute", "string", "replace"], // 数组处理 array: ["array", "list", "collection", "array"], filter: ["filter", "array", "list", "filter"], map: ["map", "array", "transform", "map"], reduce: ["reduce", "array", "aggregate", "reduce"], // 对象处理 object: ["object", "obj", "entity", "object"], merge: ["merge", "combine", "object", "merge"], clone: ["clone", "copy", "duplicate", "clone"], // 异步处理 async: ["async", "await", "promise", "async"], promise: ["promise", "async", "await", "promise"], await: ["await", "async", "promise", "await"], }; // 代码复杂度评估 function calculateComplexity(content) { let complexity = 1; // 循环复杂度 const loops = (content.match(/\b(for|while|do)\b/g) || []).length; complexity += loops * 2; // 条件复杂度 const conditions = (content.match(/\b(if|else|switch|case)\b/g) || []).length; complexity += conditions; // 函数调用复杂度 const functionCalls = (content.match(/\w+\(/g) || []).length; complexity += functionCalls * 0.5; // 嵌套深度 const maxNesting = Math.max(...content.split("\n").map((line) => { const match = line.match(/^\s*/); const indent = match ? match[0].length : 0; return Math.floor(indent / 2); })); complexity += maxNesting * 0.5; return Math.min(10, complexity); } // 提取函数参数和返回值 function extractFunctionSignature(content, symbolName) { const parameters = []; let returnType = ""; // 匹配函数定义 const functionPatterns = [ // 普通函数 new RegExp(`function\\s+${escapeRegExp(symbolName)}\\s*\\(([^)]*)\\)\\s*:\\s*([^{\\s]+)?`, "i"), // 箭头函数 new RegExp(`(?:const|let|var)\\s+${escapeRegExp(symbolName)}\\s*=\\s*(?:async\\s*)?\\(([^)]*)\\)\\s*=>\\s*([^{\\s]+)?`, "i"), // 方法定义 new RegExp(`${escapeRegExp(symbolName)}\\s*\\(([^)]*)\\)\\s*:\\s*([^{\\s]+)?`, "i"), ]; for (const pattern of functionPatterns) { const match = content.match(pattern); if (match) { // 提取参数 const paramString = match[1]; if (paramString.trim()) { const params = paramString.split(",").map((p) => { const param = p.trim(); // 提取参数名(去掉类型注解) const nameMatch = param.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/); return nameMatch ? nameMatch[1] : param; }); parameters.push(...params); } // 提取返回值类型 if (match[2]) { returnType = match[2].trim(); } break; } } return { parameters, returnType }; } // 分析调用关系 function analyzeCallRelationships(content, symbolName) { const callers = []; const callees = []; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; // 查找调用当前函数的地方 const callerPattern = new RegExp(`\\b(\\w+)\\s*\\([^)]*\\)\\s*;?\\s*(?://.*)?$`, "g"); let callerMatch; while ((callerMatch = callerPattern.exec(line)) !== null) { const callerName = callerMatch[1]; if (callerName !== symbolName && !callees.includes(callerName)) { callees.push(callerName); } } // 查找当前函数调用的其他函数 const calleePattern = new RegExp(`\\b${escapeRegExp(symbolName)}\\s*\\([^)]*\\)`, "g"); if (calleePattern.test(line)) { // 提取这一行中调用的函数 const functionCallPattern = /\b(\w+)\s*\(/g; let functionMatch; while ((functionMatch = functionCallPattern.exec(line)) !== null) { const functionName = functionMatch[1]; if (functionName !== symbolName && !callers.includes(functionName)) { callers.push(functionName); } } } } return { callers, callees }; } // 1. 精确匹配维度 (权重: 0.8) function exactMatchScore(query, symbolName) { const queryLower = query.toLowerCase(); const nameLower = symbolName.toLowerCase(); // 完全匹配 if (nameLower === queryLower) return 1.0; // 包含匹配 if (nameLower.includes(queryLower) || queryLower.includes(nameLower)) return 0.8; // 驼峰命名匹配 (如 "userLogin" 匹配 "user login") const camelCaseWords = nameLower.replace(/([A-Z])/g, " $1").toLowerCase(); if (camelCaseWords.includes(queryLower)) return 0.7; // 下划线命名匹配 const underscoreWords = nameLower.replace(/_/g, " "); if (underscoreWords.includes(queryLower)) return 0.6; return 0.0; } // 2. 语义关键词匹配维度 (权重: 0.6) function semanticKeywordScore(query, symbolName, description) { const queryLower = query.toLowerCase(); const nameLower = symbolName.toLowerCase(); const descLower = description.toLowerCase(); let score = 0; const queryWords = queryLower.split(/\s+/); for (const word of queryWords) { if (SEMANTIC_KEYWORDS[word]) { const synonyms = SEMANTIC_KEYWORDS[word]; for (const synonym of synonyms) { if (nameLower.includes(synonym) || descLower.includes(synonym)) { score += 0.3; } } } } return Math.min(1.0, score); } // 3. 上下文相关性维度 (权重: 0.4) function contextRelevanceScore(query, parameters, returnType, comments) { const queryLower = query.toLowerCase(); let score = 0; // 3.1 函数参数分析 (权重: 0.4) const paramScore = parameters.reduce((acc, param) => { if (param.toLowerCase().includes(queryLower)) return acc + 0.5; return acc; }, 0); score += Math.min(1.0, paramScore) * 0.4; // 3.2 返回值分析 (权重: 0.3) if (returnType && returnType.toLowerCase().includes(queryLower)) { score += 0.3; } // 3.3 注释内容分析 (权重: 0.2) if (comments && comments.toLowerCase().includes(queryLower)) { score += 0.2; } // 3.4 代码上下文分析 (权重: 0.1) const contextWords = queryLower.split(/\s+/); for (const word of contextWords) { if (word.length > 2 && (parameters.some((p) => p.toLowerCase().includes(word)) || (returnType && returnType.toLowerCase().includes(word)))) { score += 0.1; } } return Math.min(1.0, score); } // 4. 依赖关系相关性维度 (权重: 0.3) function dependencyRelevanceScore(query, callers, callees) { const queryLower = query.toLowerCase(); let score = 0; // 4.1 调用者分析 (权重: 0.5) const callerScore = callers.reduce((acc, caller) => { if (caller.toLowerCase().includes(queryLower)) return acc + 0.4; return acc; }, 0); score += Math.min(1.0, callerScore) * 0.5; // 4.2 被调用者分析 (权重: 0.3) const calleeScore = callees.reduce((acc, callee) => { if (callee.toLowerCase().includes(queryLower)) return acc + 0.3; return acc; }, 0); score += Math.min(1.0, calleeScore) * 0.3; // 4.3 模块依赖分析 (权重: 0.2) const allRelated = [...callers, ...callees]; const moduleScore = allRelated.reduce((acc, related) => { if (related.toLowerCase().includes(queryLower)) return acc + 0.2; return acc; }, 0); score += Math.min(1.0, moduleScore) * 0.2; return Math.min(1.0, score); } // 5. 使用频率相关性维度 (权重: 0.2) function usageRelevanceScore(complexity, callCount) { let score = 0; // 5.1 调用频率 (权重: 0.4) const frequencyScore = callCount > 100 ? 1.0 : callCount > 50 ? 0.8 : callCount > 20 ? 0.6 : callCount > 10 ? 0.4 : callCount > 5 ? 0.2 : 0.1; score += frequencyScore * 0.4; // 5.2 代码复杂度 (权重: 0.3) const complexityScore = complexity > 8 ? 1.0 : complexity > 6 ? 0.8 : complexity > 4 ? 0.6 : complexity > 2 ? 0.4 : 0.2; score += complexityScore * 0.3; // 5.3 函数重要性 (权重: 0.2) const importanceScore = callCount > 0 ? 0.8 : 0.2; score += importanceScore * 0.2; // 5.4 代码质量 (权重: 0.1) const qualityScore = complexity < 5 ? 1.0 : complexity < 8 ? 0.6 : 0.3; score += qualityScore * 0.1; return score; } // 计算最终相关性分数 function calculateFinalRelevance(query, symbolName, description, parameters, returnType, callers, callees, complexity, callCount) { const weights = { exactMatch: 0.8, semanticKeyword: 0.6, context: 0.4, dependency: 0.3, usage: 0.2, }; const scores = { exactMatch: exactMatchScore(query, symbolName), semanticKeyword: semanticKeywordScore(query, symbolName, description), context: contextRelevanceScore(query, parameters, returnType, description), dependency: dependencyRelevanceScore(query, callers, callees), usage: usageRelevanceScore(complexity, callCount), }; // 加权平均 let totalScore = 0; let totalWeight = 0; for (const [dimension, weight] of Object.entries(weights)) { totalScore += scores[dimension] * weight; totalWeight += weight; } return totalScore / totalWeight; } // 提取代码关键词 function extractCodeKeywords(content, symbolName) { const keywords = []; // 提取函数参数 const { parameters } = extractFunctionSignature(content, symbolName); keywords.push(...parameters); // 提取注释中的关键词 const commentPattern = /\/\*\*?([^*]|\*(?!\/))*\*\/|\/\/.*$/gm; const comments = content.match(commentPattern) || []; for (const comment of comments) { const words = comment.replace(/[\/\*]/g, "").split(/\s+/); keywords.push(...words.filter((w) => w.length > 3)); } // 提取变量名和函数调用 const identifierPattern = /\b[a-zA-Z_][a-zA-Z0-9_]*\b/g; const identifiers = content.match(identifierPattern) || []; keywords.push(...identifiers.filter((id) => id.length > 2 && id !== symbolName)); return [...new Set(keywords)]; } function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } async function semanticSearch(query, projectRoot, options = {}) { const { limit = 20, minRelevance = 0.1, includeComments = true, includeTests = false, } = options; const results = []; try { const languages = (0, os_utils_1.detectDominantLanguages)(projectRoot, { ignoredDirs: new Set(["node_modules", "dist"]), topN: 1, }) ?? []; if (languages.length === 0) { return results; } const lang = languages[0]; const file_ext = (0, os_utils_1.getLanguageFileExtensions)(lang); const client = new lsp_core_1.LSPClient(`file://${projectRoot}`, { ...lsp_core_1.DEFAULT_CONFIG, timeout: 30000, logLevel: "error", }); await client.start(lang); try { const files = (await (0, glob_1.glob)(`**/*.{${file_ext}}`, { cwd: projectRoot, ignore: includeTests ? "node_modules/**" : ["node_modules/**", "**/*.test.*", "**/*.spec.*"], absolute: true, })) ?? []; for (const filePath of files) { const fileUri = `file://${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) { // 提取函数签名信息 const { parameters, returnType } = extractFunctionSignature(content, symbol.name); // 分析调用关系 const { callers, callees } = analyzeCallRelationships(content, symbol.name); // 计算代码复杂度 const complexity = calculateComplexity(content); // 估算调用次数(基于函数名在代码中的出现次数) const matches = content.match(new RegExp(`\\b${escapeRegExp(symbol.name)}\\s*\\(`, "g")); const callCount = matches ? matches.length : 0; // 计算相关性分数 const relevance = calculateFinalRelevance(query, symbol.name, symbol.detail || "", parameters, returnType, callers, callees, complexity, callCount); if (relevance >= minRelevance) { const keywords = extractCodeKeywords(content, symbol.name); results.push({ type: "semantic", name: symbol.name, file: relativePath, line: symbol.location.range.start.line + 1, column: symbol.location.range.start.character + 1, description: symbol.detail || undefined, kind: symbol.kind, relevance, context: `Function with ${parameters.length} parameters${returnType ? `, returns ${returnType}` : ""}`, keywords, parameters, returnType, callers, callees, complexity, callCount, }); } } await client.sendDidClose(fileUri); } catch (err) { // 忽略单个文件的错误 } } } finally { await client.shutdown(); } // 按相关性排序 return results.sort((a, b) => b.relevance - a.relevance).slice(0, limit); } catch (error) { console.error("语义搜索错误:", error); return results; } } //# sourceMappingURL=semantic_search.js.map