feat:实现删除文件确认功能

This commit is contained in:
Roe-xin
2026-03-03 17:08:59 +08:00
parent 35c63802b5
commit f56ad33366

View File

@ -2,23 +2,31 @@
* 工具执行器 * 工具执行器
* 接收后端的 tool_call 事件,执行本地工具,返回结果 * 接收后端的 tool_call 事件,执行本地工具,返回结果
*/ */
import * as vscode from 'vscode'; import * as vscode from "vscode";
import * as path from 'path'; import * as path from "path";
import * as os from 'os'; import * as os from "os";
import * as fs from 'fs'; import * as fs from "fs";
import { readFileContent, readDirectory } from '../utils/readFiles'; import { readFileContent, readDirectory } from "../utils/readFiles";
import { createOrOverwriteFile } from '../utils/createFiles'; import { createOrOverwriteFile } from "../utils/createFiles";
import { resolveWorkspaceFilePath, showFileDiff } from '../utils/fileDiff'; import { resolveWorkspaceFilePath, showFileDiff } from "../utils/fileDiff";
import { changeTracker } from './changeTracker'; import { changeTracker } from "./changeTracker";
import { generateVCD, checkIverilogAvailable, generateMultiVCD, DumpModule } from '../utils/iverilogRunner'; import {
import { analyzeVcdFile } from '../utils/vcdParser'; generateVCD,
import { executeWaveformTrace, WaveformTraceArgs } from '../utils/waveformTracer'; checkIverilogAvailable,
generateMultiVCD,
DumpModule,
} from "../utils/iverilogRunner";
import { analyzeVcdFile } from "../utils/vcdParser";
import {
executeWaveformTrace,
WaveformTraceArgs,
} from "../utils/waveformTracer";
import { import {
submitToolResult, submitToolResult,
createSuccessResult, createSuccessResult,
createBusinessErrorResult, createBusinessErrorResult,
createSystemErrorResult createSystemErrorResult,
} from './apiClient'; } from "./apiClient";
import type { import type {
ToolCallRequest, ToolCallRequest,
ToolName, ToolName,
@ -31,8 +39,8 @@ import type {
SimulationArgs, SimulationArgs,
WaveformSummaryArgs, WaveformSummaryArgs,
KnowledgeSaveArgs, KnowledgeSaveArgs,
KnowledgeLoadArgs KnowledgeLoadArgs,
} from '../types/api'; } from "../types/api";
/** /**
* 工具执行器上下文 * 工具执行器上下文
@ -51,7 +59,7 @@ export interface ToolExecutorContext {
*/ */
export async function executeToolCall( export async function executeToolCall(
request: ToolCallRequest, request: ToolCallRequest,
context: ToolExecutorContext context: ToolExecutorContext,
): Promise<void> { ): Promise<void> {
const toolName = request.params.name as ToolName; const toolName = request.params.name as ToolName;
const args = request.params.arguments; const args = request.params.arguments;
@ -63,37 +71,53 @@ export async function executeToolCall(
let resultText: string; let resultText: string;
switch (toolName) { switch (toolName) {
case 'file_read': case "file_read":
resultText = await executeFileRead(args as unknown as FileReadArgs); resultText = await executeFileRead(args as unknown as FileReadArgs);
break; break;
case 'file_write': case "file_write":
resultText = await executeFileWrite(args as unknown as FileWriteArgs); resultText = await executeFileWrite(args as unknown as FileWriteArgs);
break; break;
case 'file_delete': case "file_delete":
resultText = await executeFileDelete(args as unknown as FileDeleteArgs); resultText = await executeFileDelete(args as unknown as FileDeleteArgs);
break; break;
case 'file_list': case "file_list":
resultText = await executeFileList(args as unknown as FileListArgs); resultText = await executeFileList(args as unknown as FileListArgs);
break; break;
case 'syntax_check': case "syntax_check":
resultText = await executeSyntaxCheck(args as unknown as SyntaxCheckArgs, context); resultText = await executeSyntaxCheck(
args as unknown as SyntaxCheckArgs,
context,
);
break; break;
case 'iverilog': case "iverilog":
resultText = await executeIverilog(args as unknown as IverilogArgs, context); resultText = await executeIverilog(
args as unknown as IverilogArgs,
context,
);
break; break;
case 'simulation': case "simulation":
resultText = await executeSimulation(args as unknown as SimulationArgs, context); resultText = await executeSimulation(
args as unknown as SimulationArgs,
context,
);
break; break;
case 'waveform_summary': case "waveform_summary":
resultText = await executeWaveformSummary(args as unknown as WaveformSummaryArgs); resultText = await executeWaveformSummary(
args as unknown as WaveformSummaryArgs,
);
break; break;
case 'waveform_trace': case "waveform_trace":
resultText = await executeWaveformTrace(args as unknown as WaveformTraceArgs, context); resultText = await executeWaveformTrace(
args as unknown as WaveformTraceArgs,
context,
);
break; break;
case 'knowledge_save': case "knowledge_save":
resultText = await executeKnowledgeSave(args as unknown as KnowledgeSaveArgs); resultText = await executeKnowledgeSave(
args as unknown as KnowledgeSaveArgs,
);
break; break;
case 'knowledge_load': case "knowledge_load":
resultText = await executeKnowledgeLoad(); resultText = await executeKnowledgeLoad();
break; break;
default: default:
@ -104,10 +128,12 @@ export async function executeToolCall(
const result = createSuccessResult(callId, resultText); const result = createSuccessResult(callId, resultText);
await submitToolResult(result); await submitToolResult(result);
console.log(`[ToolExecutor] 工具执行成功: ${toolName}, callId=${callId}`); console.log(`[ToolExecutor] 工具执行成功: ${toolName}, callId=${callId}`);
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : '未知错误'; const errorMessage = error instanceof Error ? error.message : "未知错误";
console.error(`[ToolExecutor] 工具执行失败: ${toolName}, callId=${callId}`, error); console.error(
`[ToolExecutor] 工具执行失败: ${toolName}, callId=${callId}`,
error,
);
// 提交错误结果 // 提交错误结果
const result = createBusinessErrorResult(callId, errorMessage); const result = createBusinessErrorResult(callId, errorMessage);
@ -129,7 +155,7 @@ async function executeFileRead(args: FileReadArgs): Promise<string> {
async function executeFileWrite(args: FileWriteArgs): Promise<string> { async function executeFileWrite(args: FileWriteArgs): Promise<string> {
const absolutePath = resolveWorkspaceFilePath(args.path); const absolutePath = resolveWorkspaceFilePath(args.path);
const existedBeforeWrite = fs.existsSync(absolutePath); const existedBeforeWrite = fs.existsSync(absolutePath);
const oldContent = existedBeforeWrite ? await readFileContent(args.path) : ''; const oldContent = existedBeforeWrite ? await readFileContent(args.path) : "";
await createOrOverwriteFile(args.path, args.content); await createOrOverwriteFile(args.path, args.content);
@ -137,11 +163,11 @@ async function executeFileWrite(args: FileWriteArgs): Promise<string> {
try { try {
changeTracker.trackChange(args.path, oldContent, args.content); changeTracker.trackChange(args.path, oldContent, args.content);
} catch (error) { } catch (error) {
console.warn('[ToolExecutor] 记录文件变更失败:', error); console.warn("[ToolExecutor] 记录文件变更失败:", error);
} }
// Verilog 文件添加知识图谱提示 // Verilog 文件添加知识图谱提示
const isVerilogFile = args.path.endsWith('.v') || args.path.endsWith('.sv'); const isVerilogFile = args.path.endsWith(".v") || args.path.endsWith(".sv");
if (isVerilogFile) { if (isVerilogFile) {
return `文件已写入: ${args.path}\n\n[提示] 如有新信号或规则,请更新知识图谱`; return `文件已写入: ${args.path}\n\n[提示] 如有新信号或规则,请更新知识图谱`;
} }
@ -151,7 +177,7 @@ async function executeFileWrite(args: FileWriteArgs): Promise<string> {
/** /**
* 执行 file_delete 工具 * 执行 file_delete 工具
* 删除指定路径的文件 * 删除指定路径的文件(带用户确认)
*/ */
async function executeFileDelete(args: FileDeleteArgs): Promise<string> { async function executeFileDelete(args: FileDeleteArgs): Promise<string> {
const filePath = args.path; const filePath = args.path;
@ -159,7 +185,7 @@ async function executeFileDelete(args: FileDeleteArgs): Promise<string> {
// 获取工作区路径 // 获取工作区路径
const workspaceFolders = vscode.workspace.workspaceFolders; const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { if (!workspaceFolders || workspaceFolders.length === 0) {
throw new Error('请先打开一个工作区'); throw new Error("请先打开一个工作区");
} }
const workspacePath = workspaceFolders[0].uri.fsPath; const workspacePath = workspaceFolders[0].uri.fsPath;
@ -180,18 +206,60 @@ async function executeFileDelete(args: FileDeleteArgs): Promise<string> {
throw new Error(`不能删除目录,请指定文件路径: ${filePath}`); throw new Error(`不能删除目录,请指定文件路径: ${filePath}`);
} }
// 验证文件路径在工作区内
const isInWorkspace = workspaceFolders.some((folder) =>
absolutePath.startsWith(folder.uri.fsPath),
);
if (!isInWorkspace) {
throw new Error("只能删除工作区内的文件");
}
// 保护敏感文件
const protectedFiles = [
"package.json",
"tsconfig.json",
".git",
"node_modules",
];
const fileName = path.basename(absolutePath);
if (protectedFiles.includes(fileName)) {
throw new Error(`不允许删除系统文件: ${fileName}`);
}
// 弹出确认对话框
const confirmed = await vscode.window.showWarningMessage(
`确定要删除文件吗?\n\n📄 ${path.basename(filePath)}\n📁 ${path.dirname(filePath)}`,
{
modal: true, // 模态对话框,阻止其他操作
detail: "⚠️ 文件将被移到回收站,可以恢复",
},
"确定删除",
"取消",
);
// 用户取消或关闭对话框
if (confirmed !== "确定删除") {
throw new Error("用户取消了删除操作");
}
// 读取文件内容用于变更追踪 // 读取文件内容用于变更追踪
const oldContent = fs.readFileSync(absolutePath, 'utf-8'); const oldContent = fs.readFileSync(absolutePath, "utf-8");
// 记录删除变更 // 记录删除变更
const relativePath = path.relative(workspacePath, absolutePath); const relativePath = path.relative(workspacePath, absolutePath);
changeTracker.trackChange(relativePath, oldContent, ''); changeTracker.trackChange(relativePath, oldContent, "");
// 删除文件 // 删除文件(移到回收站)
fs.unlinkSync(absolutePath); const uri = vscode.Uri.file(absolutePath);
await vscode.workspace.fs.delete(uri, {
recursive: false, // 不是目录,设为 false
useTrash: true, // 移到回收站而非永久删除
});
// Verilog 文件添加知识图谱提示 // Verilog 文件添加知识图谱提示
const isVerilogFile = filePath.endsWith('.v') || filePath.endsWith('.sv'); const isVerilogFile = filePath.endsWith(".v") || filePath.endsWith(".sv");
if (isVerilogFile) { if (isVerilogFile) {
return `文件已删除: ${filePath}\n\n[提示] 请删除知识图谱中相关节点`; return `文件已删除: ${filePath}\n\n[提示] 请删除知识图谱中相关节点`;
} }
@ -203,13 +271,13 @@ async function executeFileDelete(args: FileDeleteArgs): Promise<string> {
* 执行 file_list 工具 * 执行 file_list 工具
*/ */
async function executeFileList(args: FileListArgs): Promise<string> { async function executeFileList(args: FileListArgs): Promise<string> {
const dirPath = args.path || '.'; const dirPath = args.path || ".";
const extensions = args.extension ? [args.extension] : undefined; const extensions = args.extension ? [args.extension] : undefined;
const files = await readDirectory(dirPath, extensions); const files = await readDirectory(dirPath, extensions);
const fileList = files.map(f => f.path).join('\n'); const fileList = files.map((f) => f.path).join("\n");
return fileList || '(目录为空)'; return fileList || "(目录为空)";
} }
/** /**
@ -218,7 +286,7 @@ async function executeFileList(args: FileListArgs): Promise<string> {
*/ */
async function executeSyntaxCheck( async function executeSyntaxCheck(
args: SyntaxCheckArgs, args: SyntaxCheckArgs,
context: ToolExecutorContext context: ToolExecutorContext,
): Promise<string> { ): Promise<string> {
// 检查 iverilog 是否可用 // 检查 iverilog 是否可用
const iverilogCheck = await checkIverilogAvailable(context.extensionPath); const iverilogCheck = await checkIverilogAvailable(context.extensionPath);
@ -232,33 +300,33 @@ async function executeSyntaxCheck(
try { try {
// 写入代码到临时文件 // 写入代码到临时文件
fs.writeFileSync(tempFile, args.code, 'utf-8'); fs.writeFileSync(tempFile, args.code, "utf-8");
// 调用 iverilog 进行语法检查 // 调用 iverilog 进行语法检查
const { spawn } = require('child_process'); const { spawn } = require("child_process");
const iverilogPath = getIverilogPath(context.extensionPath); const iverilogPath = getIverilogPath(context.extensionPath);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = spawn(iverilogPath, ['-t', 'null', tempFile], { const child = spawn(iverilogPath, ["-t", "null", tempFile], {
cwd: tempDir, cwd: tempDir,
env: { env: {
...process.env, ...process.env,
IVERILOG_ROOT: path.join(context.extensionPath, 'tools', 'iverilog') IVERILOG_ROOT: path.join(context.extensionPath, "tools", "iverilog"),
} },
}); });
let stdout = ''; let stdout = "";
let stderr = ''; let stderr = "";
child.stdout.on('data', (data: Buffer) => { child.stdout.on("data", (data: Buffer) => {
stdout += data.toString(); stdout += data.toString();
}); });
child.stderr.on('data', (data: Buffer) => { child.stderr.on("data", (data: Buffer) => {
stderr += data.toString(); stderr += data.toString();
}); });
child.on('close', (code: number) => { child.on("close", (code: number) => {
// 清理临时文件 // 清理临时文件
try { try {
fs.unlinkSync(tempFile); fs.unlinkSync(tempFile);
@ -267,13 +335,13 @@ async function executeSyntaxCheck(
} }
if (code === 0) { if (code === 0) {
resolve('语法检查通过,无错误。'); resolve("语法检查通过,无错误。");
} else { } else {
resolve(`语法检查发现错误:\n${stderr || stdout}`); resolve(`语法检查发现错误:\n${stderr || stdout}`);
} }
}); });
child.on('error', (error: Error) => { child.on("error", (error: Error) => {
try { try {
fs.unlinkSync(tempFile); fs.unlinkSync(tempFile);
} catch (e) { } catch (e) {
@ -282,7 +350,6 @@ async function executeSyntaxCheck(
reject(error); reject(error);
}); });
}); });
} catch (error) { } catch (error) {
// 确保清理临时文件 // 确保清理临时文件
try { try {
@ -300,7 +367,7 @@ async function executeSyntaxCheck(
*/ */
async function executeIverilog( async function executeIverilog(
args: IverilogArgs, args: IverilogArgs,
context: ToolExecutorContext context: ToolExecutorContext,
): Promise<string> { ): Promise<string> {
// 检查 iverilog 是否可用 // 检查 iverilog 是否可用
const iverilogCheck = await checkIverilogAvailable(context.extensionPath); const iverilogCheck = await checkIverilogAvailable(context.extensionPath);
@ -311,7 +378,7 @@ async function executeIverilog(
// 获取工作目录 // 获取工作目录
const workspaceFolders = vscode.workspace.workspaceFolders; const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { if (!workspaceFolders || workspaceFolders.length === 0) {
throw new Error('没有打开的工作区'); throw new Error("没有打开的工作区");
} }
const projectPath = workspaceFolders[0].uri.fsPath; const projectPath = workspaceFolders[0].uri.fsPath;
const workDir = args.workDir const workDir = args.workDir
@ -320,32 +387,32 @@ async function executeIverilog(
// 解析参数 // 解析参数
const iverilogPath = getIverilogPath(context.extensionPath); const iverilogPath = getIverilogPath(context.extensionPath);
const cmdArgs = args.args.split(/\s+/).filter(a => a.length > 0); const cmdArgs = args.args.split(/\s+/).filter((a) => a.length > 0);
const { spawn } = require('child_process'); const { spawn } = require("child_process");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const child = spawn(iverilogPath, cmdArgs, { const child = spawn(iverilogPath, cmdArgs, {
cwd: workDir, cwd: workDir,
env: { env: {
...process.env, ...process.env,
IVERILOG_ROOT: path.join(context.extensionPath, 'tools', 'iverilog') IVERILOG_ROOT: path.join(context.extensionPath, "tools", "iverilog"),
} },
}); });
let stdout = ''; let stdout = "";
let stderr = ''; let stderr = "";
child.stdout.on('data', (data: Buffer) => { child.stdout.on("data", (data: Buffer) => {
stdout += data.toString(); stdout += data.toString();
}); });
child.stderr.on('data', (data: Buffer) => { child.stderr.on("data", (data: Buffer) => {
stderr += data.toString(); stderr += data.toString();
}); });
child.on('close', (code: number) => { child.on("close", (code: number) => {
const output = stderr || stdout || '(无输出)'; const output = stderr || stdout || "(无输出)";
if (code === 0) { if (code === 0) {
resolve(`执行成功\n${output}`); resolve(`执行成功\n${output}`);
} else { } else {
@ -353,7 +420,7 @@ async function executeIverilog(
} }
}); });
child.on('error', (error: Error) => { child.on("error", (error: Error) => {
reject(error); reject(error);
}); });
}); });
@ -364,12 +431,12 @@ async function executeIverilog(
*/ */
async function executeSimulation( async function executeSimulation(
args: SimulationArgs, args: SimulationArgs,
context: ToolExecutorContext context: ToolExecutorContext,
): Promise<string> { ): Promise<string> {
// 获取工作区路径 // 获取工作区路径
const workspaceFolders = vscode.workspace.workspaceFolders; const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { if (!workspaceFolders || workspaceFolders.length === 0) {
throw new Error('请先打开一个工作区'); throw new Error("请先打开一个工作区");
} }
const projectPath = workspaceFolders[0].uri.fsPath; const projectPath = workspaceFolders[0].uri.fsPath;
@ -377,21 +444,24 @@ async function executeSimulation(
// 检查是否有 dumpModules 参数(多 VCD 模式) // 检查是否有 dumpModules 参数(多 VCD 模式)
if (args.dumpModules) { if (args.dumpModules) {
const modules = parseDumpModules(args.dumpModules); const modules = parseDumpModules(args.dumpModules);
const vcdDir = args.vcdDir || 'vcd'; const vcdDir = args.vcdDir || "vcd";
const result = await generateMultiVCD( const result = await generateMultiVCD(
projectPath, projectPath,
context.extensionPath, context.extensionPath,
args.tbPath, args.tbPath,
modules, modules,
vcdDir vcdDir,
); );
if (result.success) { if (result.success) {
const vcdList = result.vcdFiles const vcdList = result.vcdFiles
.map(f => `- ${f.moduleName}: ${f.success ? f.vcdPath : '失败 - ' + f.error}`) .map(
.join('\n'); (f) =>
return `${result.message}\n\nVCD 文件列表:\n${vcdList}${result.stdout ? '\n\n仿真输出:' + result.stdout : ''}`; `- ${f.moduleName}: ${f.success ? f.vcdPath : "失败 - " + f.error}`,
)
.join("\n");
return `${result.message}\n\nVCD 文件列表:\n${vcdList}${result.stdout ? "\n\n仿真输出:" + result.stdout : ""}`;
} else { } else {
throw new Error(result.message); throw new Error(result.message);
} }
@ -420,8 +490,8 @@ async function executeSimulation(
* 格式name:path,name:path * 格式name:path,name:path
*/ */
function parseDumpModules(dumpModules: string): DumpModule[] { function parseDumpModules(dumpModules: string): DumpModule[] {
return dumpModules.split(',').map(item => { return dumpModules.split(",").map((item) => {
const [name, modulePath] = item.trim().split(':'); const [name, modulePath] = item.trim().split(":");
return { name: name.trim(), path: modulePath.trim() }; return { name: name.trim(), path: modulePath.trim() };
}); });
} }
@ -430,13 +500,15 @@ function parseDumpModules(dumpModules: string): DumpModule[] {
* 执行 waveform_summary 工具 * 执行 waveform_summary 工具
* 解析 VCD 文件并返回波形摘要 * 解析 VCD 文件并返回波形摘要
*/ */
async function executeWaveformSummary(args: WaveformSummaryArgs): Promise<string> { async function executeWaveformSummary(
args: WaveformSummaryArgs,
): Promise<string> {
const { vcdPath, signals, checkpoints } = args; const { vcdPath, signals, checkpoints } = args;
// 获取工作区路径 // 获取工作区路径
const workspaceFolders = vscode.workspace.workspaceFolders; const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { if (!workspaceFolders || workspaceFolders.length === 0) {
throw new Error('请先打开一个工作区'); throw new Error("请先打开一个工作区");
} }
const workspacePath = workspaceFolders[0].uri.fsPath; const workspacePath = workspaceFolders[0].uri.fsPath;
@ -467,17 +539,20 @@ async function executeWaveformSummary(args: WaveformSummaryArgs): Promise<string
async function executeKnowledgeSave(args: KnowledgeSaveArgs): Promise<string> { async function executeKnowledgeSave(args: KnowledgeSaveArgs): Promise<string> {
const workspaceFolder = getWorkspaceFolder(); const workspaceFolder = getWorkspaceFolder();
if (!workspaceFolder) { if (!workspaceFolder) {
throw new Error('请先打开一个工作区'); throw new Error("请先打开一个工作区");
} }
const iccoderDirUri = vscode.Uri.joinPath(workspaceFolder.uri, '.iccoder'); const iccoderDirUri = vscode.Uri.joinPath(workspaceFolder.uri, ".iccoder");
const knowledgeUri = vscode.Uri.joinPath(iccoderDirUri, 'knowledge.json'); const knowledgeUri = vscode.Uri.joinPath(iccoderDirUri, "knowledge.json");
// 确保 .iccoder 目录存在(兼容远程/虚拟工作区) // 确保 .iccoder 目录存在(兼容远程/虚拟工作区)
await vscode.workspace.fs.createDirectory(iccoderDirUri); await vscode.workspace.fs.createDirectory(iccoderDirUri);
// 写入知识图谱UTF-8 // 写入知识图谱UTF-8
await vscode.workspace.fs.writeFile(knowledgeUri, Buffer.from(args.data || '', 'utf-8')); await vscode.workspace.fs.writeFile(
knowledgeUri,
Buffer.from(args.data || "", "utf-8"),
);
return `知识图谱已保存: .iccoder/knowledge.json`; return `知识图谱已保存: .iccoder/knowledge.json`;
} }
@ -489,20 +564,33 @@ async function executeKnowledgeSave(args: KnowledgeSaveArgs): Promise<string> {
async function executeKnowledgeLoad(): Promise<string> { async function executeKnowledgeLoad(): Promise<string> {
const workspaceFolder = getWorkspaceFolder(); const workspaceFolder = getWorkspaceFolder();
if (!workspaceFolder) { if (!workspaceFolder) {
throw new Error('请先打开一个工作区'); throw new Error("请先打开一个工作区");
} }
const knowledgeUri = vscode.Uri.joinPath(workspaceFolder.uri, '.iccoder', 'knowledge.json'); const knowledgeUri = vscode.Uri.joinPath(
workspaceFolder.uri,
".iccoder",
"knowledge.json",
);
try { try {
const bytes = await vscode.workspace.fs.readFile(knowledgeUri); const bytes = await vscode.workspace.fs.readFile(knowledgeUri);
const content = Buffer.from(bytes).toString('utf-8'); const content = Buffer.from(bytes).toString("utf-8");
return content; return content;
} catch (error) { } catch (error) {
// 文件不存在:返回空图谱 // 文件不存在:返回空图谱
if (error instanceof vscode.FileSystemError && error.code === 'FileNotFound') { if (
error instanceof vscode.FileSystemError &&
error.code === "FileNotFound"
) {
// 与后端 KnowledgeGraph 结构保持一致nodes/edges + nodeClass 多态字段) // 与后端 KnowledgeGraph 结构保持一致nodes/edges + nodeClass 多态字段)
return JSON.stringify({ taskId: '', version: 1, module: null, nodes: [], edges: [] }); return JSON.stringify({
taskId: "",
version: 1,
module: null,
nodes: [],
edges: [],
});
} }
throw error; throw error;
} }
@ -515,7 +603,9 @@ function getWorkspaceFolder(): vscode.WorkspaceFolder | undefined {
} }
const activeUri = vscode.window.activeTextEditor?.document?.uri; const activeUri = vscode.window.activeTextEditor?.document?.uri;
const activeFolder = activeUri ? vscode.workspace.getWorkspaceFolder(activeUri) : undefined; const activeFolder = activeUri
? vscode.workspace.getWorkspaceFolder(activeUri)
: undefined;
return activeFolder ?? folders[0]; return activeFolder ?? folders[0];
} }
@ -524,22 +614,24 @@ function getWorkspaceFolder(): vscode.WorkspaceFolder | undefined {
*/ */
function getIverilogPath(extensionPath: string): string { function getIverilogPath(extensionPath: string): string {
const platform = process.platform; const platform = process.platform;
if (platform === 'win32') { if (platform === "win32") {
return path.join(extensionPath, 'tools', 'iverilog', 'bin', 'iverilog.exe'); return path.join(extensionPath, "tools", "iverilog", "bin", "iverilog.exe");
} else { } else {
return path.join(extensionPath, 'tools', 'iverilog', 'bin', 'iverilog'); return path.join(extensionPath, "tools", "iverilog", "bin", "iverilog");
} }
} }
/** /**
* 创建工具执行器上下文 * 创建工具执行器上下文
*/ */
export function createToolExecutorContext(extensionPath: string): ToolExecutorContext { export function createToolExecutorContext(
extensionPath: string,
): ToolExecutorContext {
const workspaceFolders = vscode.workspace.workspaceFolders; const workspaceFolders = vscode.workspace.workspaceFolders;
const workspacePath = workspaceFolders?.[0]?.uri.fsPath || ''; const workspacePath = workspaceFolders?.[0]?.uri.fsPath || "";
return { return {
extensionPath, extensionPath,
workspacePath workspacePath,
}; };
} }