diff --git a/src/tools/rg.ts b/src/tools/rg.ts new file mode 100644 index 0000000..ad6a01b --- /dev/null +++ b/src/tools/rg.ts @@ -0,0 +1,73 @@ +import { rgPath } from "@vscode/ripgrep"; + +// 建议使用你之前用的颜色库 + +/** + * 获取搜索结果及其上下文代码块 + * @param pattern 搜索词 + * @param contextLines 上下文行数 (前后各取几行) + * @param targetPath 搜索路径 + */ +async function getCodeBlocks(pattern: string, contextLines: number = 5, targetPath: string = ".") { + const args = [ + pattern, + targetPath, + "--json", // 输出 JSON 格式以便解析 + "--context", String(contextLines), // 带出上下文 + "--max-columns", "500", // 防止单行过长 + "--heading", // 保持文件分组 + ]; + + const proc = Bun.spawn([rgPath, ...args]); + const text = await new Response(proc.stdout).text(); + + // Ripgrep 的 JSON 输出是每行一个 JSON 对象 + const lines = text.split("\n").filter(Boolean); + + const results: any[] = []; + let currentFile = ""; + let currentBlock: any = null; + + for (const line of lines) { + const node = JSON.parse(line); + + // 1. 处理文件路径切换 + if (node.type === "begin") { + currentFile = node.data.path.text; + } + + // 2. 处理匹配行和上下文行 + if (node.type === "match" || node.type === "context") { + const isMatch = node.type === "match"; + const lineText = node.data.lines.text; + const lineNumber = node.data.line_number; + + // 这里简单的逻辑:如果是连续行,就放进同一个 block + if (!currentBlock || (lineNumber !== currentBlock.endLine + 1)) { + if (currentBlock) results.push(currentBlock); + currentBlock = { + file: currentFile, + startLine: lineNumber, + endLine: lineNumber, + code: lineText, + hasMatch: isMatch + }; + } else { + currentBlock.code += lineText; + currentBlock.endLine = lineNumber; + if (isMatch) currentBlock.hasMatch = true; + } + } + + if (node.type === "end" && currentBlock) { + results.push(currentBlock); + currentBlock = null; + } + } + + return results; +} + +// 使用示例 +const blocks = await getCodeBlocks("deepseek", 3); +console.log(JSON.stringify(blocks, null, 2)); \ No newline at end of file