refactor: 优化代码格式和用户提示

- 统一代码格式化(Prettier)
- 将 iverilog 相关错误提示改为 'IC Coder编译器'
- 优化后端服务错误提示为 '当前访问人数过多,请稍后重试'
- 修复代码风格一致性问题
This commit is contained in:
Roe-xin
2026-03-09 11:10:56 +08:00
parent 1b7259d1c1
commit 7cde4fa138
3 changed files with 220 additions and 155 deletions

View File

@ -291,7 +291,7 @@ async function executeSyntaxCheck(
// 检查 iverilog 是否可用 // 检查 iverilog 是否可用
const iverilogCheck = await checkIverilogAvailable(context.extensionPath); const iverilogCheck = await checkIverilogAvailable(context.extensionPath);
if (!iverilogCheck.available) { if (!iverilogCheck.available) {
throw new Error(`iverilog 不可用: ${iverilogCheck.message}`); throw new Error(`IC Coder编译器不可用: ${iverilogCheck.message}`);
} }
// 创建临时文件 // 创建临时文件
@ -372,7 +372,7 @@ async function executeIverilog(
// 检查 iverilog 是否可用 // 检查 iverilog 是否可用
const iverilogCheck = await checkIverilogAvailable(context.extensionPath); const iverilogCheck = await checkIverilogAvailable(context.extensionPath);
if (!iverilogCheck.available) { if (!iverilogCheck.available) {
throw new Error(`iverilog 不可用: ${iverilogCheck.message}`); throw new Error(`IC Coder编译器不可用: ${iverilogCheck.message}`);
} }
// 获取工作目录 // 获取工作目录

View File

@ -7,7 +7,7 @@ import { promisify } from "util";
function execCommand( function execCommand(
command: string, command: string,
args: string[], args: string[],
options: { cwd: string; env?: any } options: { cwd: string; env?: any },
): Promise<{ stdout: string; stderr: string }> { ): Promise<{ stdout: string; stderr: string }> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 在 Windows 上,如果路径包含空格,不使用 shell直接用 spawn // 在 Windows 上,如果路径包含空格,不使用 shell直接用 spawn
@ -23,25 +23,25 @@ function execCommand(
let stderr = ""; let stderr = "";
// 在 Windows 上使用 GBK 编码解码输出 // 在 Windows 上使用 GBK 编码解码输出
const encoding = process.platform === 'win32' ? 'gbk' : 'utf8'; const encoding = process.platform === "win32" ? "gbk" : "utf8";
child.stdout.on("data", (data) => { child.stdout.on("data", (data) => {
try { try {
// 尝试使用 iconv-lite 解码(如果可用) // 尝试使用 iconv-lite 解码(如果可用)
const iconv = require('iconv-lite'); const iconv = require("iconv-lite");
stdout += iconv.decode(data, encoding); stdout += iconv.decode(data, encoding);
} catch { } catch {
// 如果 iconv-lite 不可用,使用默认解码 // 如果 iconv-lite 不可用,使用默认解码
stdout += data.toString('utf8'); stdout += data.toString("utf8");
} }
}); });
child.stderr.on("data", (data) => { child.stderr.on("data", (data) => {
try { try {
const iconv = require('iconv-lite'); const iconv = require("iconv-lite");
stderr += iconv.decode(data, encoding); stderr += iconv.decode(data, encoding);
} catch { } catch {
stderr += data.toString('utf8'); stderr += data.toString("utf8");
} }
}); });
@ -93,7 +93,7 @@ export interface VCDGenerationResult {
* 检查项目中的 Verilog 文件完整性 * 检查项目中的 Verilog 文件完整性
*/ */
export async function checkVerilogProject( export async function checkVerilogProject(
projectPath: string projectPath: string,
): Promise<VerilogProjectCheck> { ): Promise<VerilogProjectCheck> {
const result: VerilogProjectCheck = { const result: VerilogProjectCheck = {
isComplete: false, isComplete: false,
@ -164,7 +164,7 @@ export async function checkVerilogProject(
return result; return result;
} catch (error) { } catch (error) {
result.errors.push( result.errors.push(
`检查项目时出错: ${error instanceof Error ? error.message : "未知错误"}` `检查项目时出错: ${error instanceof Error ? error.message : "未知错误"}`,
); );
return result; return result;
} }
@ -209,12 +209,30 @@ async function getIverilogPath(extensionPath: string): Promise<string> {
let iverilogBin = ""; let iverilogBin = "";
if (platform === "win32") { if (platform === "win32") {
iverilogBin = path.join(extensionPath, "tools", "iverilog", "bin", "iverilog.exe"); iverilogBin = path.join(
extensionPath,
"tools",
"iverilog",
"bin",
"iverilog.exe",
);
} else if (platform === "darwin") { } else if (platform === "darwin") {
iverilogBin = path.join(extensionPath, "tools", "iverilog", "bin", "iverilog"); iverilogBin = path.join(
extensionPath,
"tools",
"iverilog",
"bin",
"iverilog",
);
} else { } else {
// Linux // Linux
iverilogBin = path.join(extensionPath, "tools", "iverilog", "bin", "iverilog"); iverilogBin = path.join(
extensionPath,
"tools",
"iverilog",
"bin",
"iverilog",
);
} }
// 如果插件包中没有,尝试使用系统安装的 iverilog // 如果插件包中没有,尝试使用系统安装的 iverilog
@ -258,7 +276,7 @@ async function getVvpPath(extensionPath: string): Promise<string> {
*/ */
export async function generateVCD( export async function generateVCD(
projectPath: string, projectPath: string,
extensionPath: string extensionPath: string,
): Promise<VCDGenerationResult> { ): Promise<VCDGenerationResult> {
try { try {
// 1. 检查项目完整性 // 1. 检查项目完整性
@ -302,7 +320,7 @@ export async function generateVCD(
} catch (error: any) { } catch (error: any) {
return { return {
success: false, success: false,
message: `iverilog 编译失败:\n${error.message}`, message: `IC Coder编译器编译失败:\n${error.message}`,
stderr: error.stderr, stderr: error.stderr,
stdout: error.stdout, stdout: error.stdout,
}; };
@ -310,17 +328,17 @@ export async function generateVCD(
// 6.5. 删除 shebang 行(修复 Windows 上的 vvp 解析错误) // 6.5. 删除 shebang 行(修复 Windows 上的 vvp 解析错误)
try { try {
const fs = require('fs'); const fs = require("fs");
const vvpContent = fs.readFileSync(outputFile, 'utf8'); const vvpContent = fs.readFileSync(outputFile, "utf8");
const lines = vvpContent.split('\n'); const lines = vvpContent.split("\n");
if (lines.length > 0 && lines[0].startsWith('#!')) { if (lines.length > 0 && lines[0].startsWith("#!")) {
const cleanedContent = lines.slice(1).join('\n'); const cleanedContent = lines.slice(1).join("\n");
fs.writeFileSync(outputFile, cleanedContent, 'utf8'); fs.writeFileSync(outputFile, cleanedContent, "utf8");
console.log('已删除 .vvp 文件的 shebang 行'); console.log("已删除 .vvp 文件的 shebang 行");
} }
} catch (error) { } catch (error) {
console.warn('删除 shebang 失败,继续执行:', error); console.warn("删除 shebang 失败,继续执行:", error);
} }
// 7. 执行仿真生成 VCD // 7. 执行仿真生成 VCD
@ -346,13 +364,17 @@ export async function generateVCD(
const projectUri = vscode.Uri.file(projectPath); const projectUri = vscode.Uri.file(projectPath);
const entries = await vscode.workspace.fs.readDirectory(projectUri); const entries = await vscode.workspace.fs.readDirectory(projectUri);
const vcdFiles = entries const vcdFiles = entries
.filter(([fileName, fileType]) => fileType === vscode.FileType.File && fileName.endsWith('.vcd')) .filter(
([fileName, fileType]) =>
fileType === vscode.FileType.File && fileName.endsWith(".vcd"),
)
.map(([fileName]) => fileName); .map(([fileName]) => fileName);
if (vcdFiles.length === 0) { if (vcdFiles.length === 0) {
return { return {
success: false, success: false,
message: "VCD 文件未生成。请确保 testbench 中包含 $dumpfile 和 $dumpvars 语句。", message:
"VCD 文件未生成。请确保 testbench 中包含 $dumpfile 和 $dumpvars 语句。",
stdout: simResult.stdout, stdout: simResult.stdout,
}; };
} }
@ -388,7 +410,7 @@ export async function generateVCD(
* 检查 iverilog 是否可用 * 检查 iverilog 是否可用
*/ */
export async function checkIverilogAvailable( export async function checkIverilogAvailable(
extensionPath: string extensionPath: string,
): Promise<{ available: boolean; version?: string; message: string }> { ): Promise<{ available: boolean; version?: string; message: string }> {
try { try {
const iverilogPath = await getIverilogPath(extensionPath); const iverilogPath = await getIverilogPath(extensionPath);
@ -400,7 +422,7 @@ export async function checkIverilogAvailable(
} catch (error) { } catch (error) {
return { return {
available: false, available: false,
message: `iverilog 不可用。未找到文件: ${iverilogPath}`, message: `IC Coder编译器不可用。未找到文件: ${iverilogPath}`,
}; };
} }
@ -419,12 +441,12 @@ export async function checkIverilogAvailable(
return { return {
available: true, available: true,
version: version, version: version,
message: `iverilog 可用: ${version}`, message: `IC Coder编译器可用: ${version}`,
}; };
} catch (error: any) { } catch (error: any) {
return { return {
available: false, available: false,
message: `iverilog 执行失败: ${error.message}\n${error.stderr || ""}`, message: `IC Coder编译器执行失败: ${error.message}\n${error.stderr || ""}`,
}; };
} }
} }
@ -433,8 +455,8 @@ export async function checkIverilogAvailable(
* 要 dump 的模块定义 * 要 dump 的模块定义
*/ */
export interface DumpModule { export interface DumpModule {
name: string; // 模块名(用于 VCD 文件名和宏名) name: string; // 模块名(用于 VCD 文件名和宏名)
path: string; // 实例路径(如 dut.u_tx path: string; // 实例路径(如 dut.u_tx
} }
/** /**
@ -459,10 +481,11 @@ export interface MultiVCDResult {
function injectConditionalDump( function injectConditionalDump(
tbContent: string, tbContent: string,
dumpModules: DumpModule[], dumpModules: DumpModule[],
vcdDir: string vcdDir: string,
): string { ): string {
// 匹配 $dumpfile 和 $dumpvars 语句(可能跨多行) // 匹配 $dumpfile 和 $dumpvars 语句(可能跨多行)
const dumpPattern = /(\$dumpfile\s*\([^)]+\)\s*;[\s\S]*?\$dumpvars\s*\([^)]+\)\s*;)/g; const dumpPattern =
/(\$dumpfile\s*\([^)]+\)\s*;[\s\S]*?\$dumpvars\s*\([^)]+\)\s*;)/g;
// 生成条件编译代码 // 生成条件编译代码
const conditionalCode = generateConditionalDumpCode(dumpModules, vcdDir); const conditionalCode = generateConditionalDumpCode(dumpModules, vcdDir);
@ -484,7 +507,7 @@ function injectConditionalDump(
*/ */
function generateConditionalDumpCode( function generateConditionalDumpCode(
dumpModules: DumpModule[], dumpModules: DumpModule[],
vcdDir: string vcdDir: string,
): string { ): string {
if (dumpModules.length === 0) { if (dumpModules.length === 0) {
return '$dumpfile("output.vcd");\n $dumpvars(0, dut);'; return '$dumpfile("output.vcd");\n $dumpvars(0, dut);';
@ -495,7 +518,7 @@ function generateConditionalDumpCode(
dumpModules.forEach((module, index) => { dumpModules.forEach((module, index) => {
const macroName = `DUMP_${module.name.toUpperCase()}`; const macroName = `DUMP_${module.name.toUpperCase()}`;
const vcdPath = `${vcdDir}/${module.name}.vcd`; const vcdPath = `${vcdDir}/${module.name}.vcd`;
const directive = index === 0 ? '`ifdef' : '`elsif'; const directive = index === 0 ? "`ifdef" : "`elsif";
lines.push(`${directive} ${macroName}`); lines.push(`${directive} ${macroName}`);
lines.push(` $dumpfile("${vcdPath}");`); lines.push(` $dumpfile("${vcdPath}");`);
@ -503,12 +526,12 @@ function generateConditionalDumpCode(
}); });
// 添加默认分支(使用第一个模块) // 添加默认分支(使用第一个模块)
lines.push('`else'); lines.push("`else");
lines.push(` $dumpfile("${vcdDir}/${dumpModules[0].name}.vcd");`); lines.push(` $dumpfile("${vcdDir}/${dumpModules[0].name}.vcd");`);
lines.push(` $dumpvars(1, ${dumpModules[0].path});`); lines.push(` $dumpvars(1, ${dumpModules[0].path});`);
lines.push('`endif'); lines.push("`endif");
return lines.join('\n'); return lines.join("\n");
} }
/** /**
@ -519,10 +542,10 @@ export async function generateMultiVCD(
extensionPath: string, extensionPath: string,
tbPath: string, tbPath: string,
dumpModules: DumpModule[], dumpModules: DumpModule[],
vcdDir: string = 'vcd' vcdDir: string = "vcd",
): Promise<MultiVCDResult> { ): Promise<MultiVCDResult> {
const results: MultiVCDResult['vcdFiles'] = []; const results: MultiVCDResult["vcdFiles"] = [];
let allStdout = ''; let allStdout = "";
try { try {
// 1. 创建 vcd 目录 // 1. 创建 vcd 目录
@ -535,16 +558,21 @@ export async function generateMultiVCD(
} }
// 2. 读取原始 testbench // 2. 读取原始 testbench
const tbFullPath = path.isAbsolute(tbPath) ? tbPath : path.join(projectPath, tbPath); const tbFullPath = path.isAbsolute(tbPath)
? tbPath
: path.join(projectPath, tbPath);
const tbUri = vscode.Uri.file(tbFullPath); const tbUri = vscode.Uri.file(tbFullPath);
const tbBytes = await vscode.workspace.fs.readFile(tbUri); const tbBytes = await vscode.workspace.fs.readFile(tbUri);
const originalTb = Buffer.from(tbBytes).toString('utf-8'); const originalTb = Buffer.from(tbBytes).toString("utf-8");
// 3. 注入条件编译代码 // 3. 注入条件编译代码
const modifiedTb = injectConditionalDump(originalTb, dumpModules, vcdDir); const modifiedTb = injectConditionalDump(originalTb, dumpModules, vcdDir);
await vscode.workspace.fs.writeFile(tbUri, Buffer.from(modifiedTb, 'utf-8')); await vscode.workspace.fs.writeFile(
tbUri,
Buffer.from(modifiedTb, "utf-8"),
);
console.log('[generateMultiVCD] Testbench 已修改,开始多次仿真...'); console.log("[generateMultiVCD] Testbench 已修改,开始多次仿真...");
// 4. 获取工具路径 // 4. 获取工具路径
const iverilogPath = await getIverilogPath(extensionPath); const iverilogPath = await getIverilogPath(extensionPath);
@ -569,27 +597,34 @@ export async function generateMultiVCD(
// 编译(带宏定义) // 编译(带宏定义)
const compileArgs = [ const compileArgs = [
`-D${macroName}`, `-D${macroName}`,
"-o", outputFile, "-o",
...projectCheck.allVerilogFiles outputFile,
...projectCheck.allVerilogFiles,
]; ];
await execCommand(iverilogPath, compileArgs, { cwd: projectPath, env }); await execCommand(iverilogPath, compileArgs, { cwd: projectPath, env });
// 仿真 // 仿真
const simResult = await execCommand(vvpPath, [outputFile], { cwd: projectPath, env }); const simResult = await execCommand(vvpPath, [outputFile], {
cwd: projectPath,
env,
});
allStdout += `\n[${module.name}] ${simResult.stdout}`; allStdout += `\n[${module.name}] ${simResult.stdout}`;
results.push({ results.push({
moduleName: module.name, moduleName: module.name,
vcdPath: vcdPath, vcdPath: vcdPath,
success: true success: true,
}); });
} catch (error: any) { } catch (error: any) {
console.error(`[generateMultiVCD] 模块 ${module.name} 仿真失败:`, error.message); console.error(
`[generateMultiVCD] 模块 ${module.name} 仿真失败:`,
error.message,
);
results.push({ results.push({
moduleName: module.name, moduleName: module.name,
vcdPath: vcdPath, vcdPath: vcdPath,
success: false, success: false,
error: error.message error: error.message,
}); });
// 继续执行其他模块 // 继续执行其他模块
} }
@ -602,19 +637,18 @@ export async function generateMultiVCD(
// 忽略 // 忽略
} }
const successCount = results.filter(r => r.success).length; const successCount = results.filter((r) => r.success).length;
return { return {
success: successCount > 0, success: successCount > 0,
vcdFiles: results, vcdFiles: results,
message: `生成完成:${successCount}/${dumpModules.length} 个 VCD 文件成功`, message: `生成完成:${successCount}/${dumpModules.length} 个 VCD 文件成功`,
stdout: allStdout stdout: allStdout,
}; };
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
vcdFiles: results, vcdFiles: results,
message: `生成多 VCD 文件失败: ${error instanceof Error ? error.message : '未知错误'}` message: `生成多 VCD 文件失败: ${error instanceof Error ? error.message : "未知错误"}`,
}; };
} }
} }

View File

@ -41,7 +41,11 @@ let currentSession: DialogSession | null = null;
/** 最后一个活跃的 taskId用于压缩等操作 */ /** 最后一个活跃的 taskId用于压缩等操作 */
let lastTaskId: string | null = null; let lastTaskId: string | null = null;
async function trackFileChange(filePath: string, oldContent: string, newContent: string): Promise<void> { async function trackFileChange(
filePath: string,
oldContent: string,
newContent: string,
): Promise<void> {
try { try {
changeTracker.trackChange(filePath, oldContent, newContent); changeTracker.trackChange(filePath, oldContent, newContent);
} catch (error) { } catch (error) {
@ -58,7 +62,7 @@ export async function handleUserMessage(
extensionPath?: string, extensionPath?: string,
mode?: RunMode, mode?: RunMode,
serviceTier?: ServiceTier, // 服务等级参数 serviceTier?: ServiceTier, // 服务等级参数
contextItems?: Array<{ id: number; type: string; path: string }> // 上下文项参数 contextItems?: Array<{ id: number; type: string; path: string }>, // 上下文项参数
) { ) {
console.log("收到用户消息:", text); console.log("收到用户消息:", text);
@ -68,7 +72,9 @@ export async function handleUserMessage(
// 从 session 中获取 token // 从 session 中获取 token
let token: string | undefined; let token: string | undefined;
try { try {
const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false }); const session = await vscode.authentication.getSession("iccoder", [], {
createIfNone: false,
});
token = session?.accessToken; token = session?.accessToken;
} catch (error) { } catch (error) {
console.warn("[MessageHandler] 获取 session 失败:", error); console.warn("[MessageHandler] 获取 session 失败:", error);
@ -78,20 +84,20 @@ export async function handleUserMessage(
console.warn("[MessageHandler] 未登录,阻止发送"); console.warn("[MessageHandler] 未登录,阻止发送");
// 保存待发送的消息 // 保存待发送的消息
await context.globalState.update('pendingMessage', { await context.globalState.update("pendingMessage", {
text, text,
mode, mode,
serviceTier, serviceTier,
timestamp: Date.now() timestamp: Date.now(),
}); });
// 显示弹窗提示 // 显示弹窗提示
const action = await vscode.window.showWarningMessage( const action = await vscode.window.showWarningMessage(
'请先登录后再发送消息', "请先登录后再发送消息",
'立即登录' "立即登录",
); );
if (action === '立即登录') { if (action === "立即登录") {
vscode.commands.executeCommand("ic-coder.login", { vscode.commands.executeCommand("ic-coder.login", {
forceReauth: true, forceReauth: true,
}); });
@ -110,24 +116,24 @@ export async function handleUserMessage(
console.warn("[MessageHandler] Token 已过期,阻止发送"); console.warn("[MessageHandler] Token 已过期,阻止发送");
// 保存待发送的消息 // 保存待发送的消息
await context.globalState.update('pendingMessage', { await context.globalState.update("pendingMessage", {
text, text,
mode, mode,
serviceTier, serviceTier,
timestamp: Date.now() timestamp: Date.now(),
}); });
// 清除过期的 session // 清除过期的 session
await context.globalState.update('icCoderSessions', []); await context.globalState.update("icCoderSessions", []);
await context.globalState.update('icCoderUserInfo', undefined); await context.globalState.update("icCoderUserInfo", undefined);
// 显示弹窗提示 // 显示弹窗提示
const action = await vscode.window.showWarningMessage( const action = await vscode.window.showWarningMessage(
'登录已过期,请重新登录', "登录已过期,请重新登录",
'立即登录' "立即登录",
); );
if (action === '立即登录') { if (action === "立即登录") {
vscode.commands.executeCommand("ic-coder.login", { vscode.commands.executeCommand("ic-coder.login", {
forceReauth: true, forceReauth: true,
}); });
@ -190,11 +196,11 @@ export async function handleUserMessage(
// 显示错误提示 // 显示错误提示
const selection = await vscode.window.showWarningMessage( const selection = await vscode.window.showWarningMessage(
balanceCheck.message || "资源点余额不足", balanceCheck.message || "资源点余额不足",
"去充值" "去充值",
); );
if (selection === "去充值") { if (selection === "去充值") {
vscode.env.openExternal( vscode.env.openExternal(
vscode.Uri.parse("https://iccoder.com/memberCenter") vscode.Uri.parse("https://iccoder.com/memberCenter"),
); );
} }
// 恢复输入状态 // 恢复输入状态
@ -216,14 +222,14 @@ export async function handleUserMessage(
mode, mode,
undefined, undefined,
serviceTier, serviceTier,
contextItems contextItems,
); );
return; return;
} catch (error) { } catch (error) {
console.error("后端服务不可用:", error); console.error("当前访问人数过多,请稍后重试:", error);
panel.webview.postMessage({ panel.webview.postMessage({
command: "updateStatus", command: "updateStatus",
text: "后端服务不可用", text: "当前访问人数过多,请稍后重试",
type: "error", type: "error",
}); });
// 恢复输入状态 // 恢复输入状态
@ -254,7 +260,7 @@ async function handleUserMessageWithBackend(
mode?: RunMode, mode?: RunMode,
reuseTaskId?: string, // 可选,复用现有 taskId用于 Plan 模式确认后继续执行) reuseTaskId?: string, // 可选,复用现有 taskId用于 Plan 模式确认后继续执行)
serviceTier?: ServiceTier, // 服务等级参数 serviceTier?: ServiceTier, // 服务等级参数
contextItems?: Array<{ id: number; type: string; path: string }> // 上下文项参数 contextItems?: Array<{ id: number; type: string; path: string }>, // 上下文项参数
): Promise<void> { ): Promise<void> {
const historyManager = ChatHistoryManager.getInstance(); const historyManager = ChatHistoryManager.getInstance();
@ -262,7 +268,7 @@ async function handleUserMessageWithBackend(
let enhancedText = text; let enhancedText = text;
if (contextItems && contextItems.length > 0) { if (contextItems && contextItems.length > 0) {
console.log("[MessageHandler] 处理上下文项:", contextItems.length); console.log("[MessageHandler] 处理上下文项:", contextItems.length);
const paths = contextItems.map(item => item.path).join('\n'); const paths = contextItems.map((item) => item.path).join("\n");
enhancedText = `${paths}\n\n${text}`; enhancedText = `${paths}\n\n${text}`;
} }
@ -273,7 +279,7 @@ async function handleUserMessageWithBackend(
// 创建会话dialogManager 会自动处理旧会话的中止) // 创建会话dialogManager 会自动处理旧会话的中止)
currentSession = dialogManager.createSession( currentSession = dialogManager.createSession(
extensionPath, extensionPath,
taskIdToUse || undefined taskIdToUse || undefined,
); );
// 保存 taskId 用于后续操作(如压缩) // 保存 taskId 用于后续操作(如压缩)
lastTaskId = currentSession.getTaskId(); lastTaskId = currentSession.getTaskId();
@ -281,7 +287,7 @@ async function handleUserMessageWithBackend(
"[MessageHandler] 创建会话: taskId=", "[MessageHandler] 创建会话: taskId=",
lastTaskId, lastTaskId,
"来源=", "来源=",
taskIdToUse ? "historyManager" : "新生成" taskIdToUse ? "historyManager" : "新生成",
); );
// 显示状态栏 // 显示状态栏
@ -324,7 +330,10 @@ async function handleUserMessageWithBackend(
// 工具错误,不需要单独处理,通过 onSegmentUpdate 统一更新 // 工具错误,不需要单独处理,通过 onSegmentUpdate 统一更新
}, },
onQuestion: (askId: string, questions: import("../types/api").QuestionItem[]) => { onQuestion: (
askId: string,
questions: import("../types/api").QuestionItem[],
) => {
// 只更新状态栏,问题显示由 onSegmentUpdate 统一处理 // 只更新状态栏,问题显示由 onSegmentUpdate 统一处理
panel.webview.postMessage({ panel.webview.postMessage({
command: "updateStatus", command: "updateStatus",
@ -379,18 +388,21 @@ async function handleUserMessageWithBackend(
// 发送系统通知 - AI 响应完成 // 发送系统通知 - AI 响应完成
const notificationService = NotificationService.getInstance(); const notificationService = NotificationService.getInstance();
notificationService.success( notificationService.success(
'IC Coder - AI 响应完成', "IC Coder - AI 响应完成",
'您的问题已得到回复,点击查看详情', "您的问题已得到回复,点击查看详情",
() => { () => {
// 点击通知时聚焦到面板 // 点击通知时聚焦到面板
panel.reveal(); panel.reveal();
} },
); );
// 发送代码变更到前端 // 发送代码变更到前端
sendChangesToWebview(panel); sendChangesToWebview(panel);
} catch (error) { } catch (error) {
console.warn("[MessageHandler] 更新面板失败(面板可能已关闭):", error); console.warn(
"[MessageHandler] 更新面板失败(面板可能已关闭):",
error,
);
} }
resolve(); resolve();
@ -458,7 +470,7 @@ async function handleUserMessageWithBackend(
}, },
}, },
mode, mode,
serviceTier // 传递服务等级 serviceTier, // 传递服务等级
); );
}); });
} }
@ -470,7 +482,7 @@ export async function handleUserAnswer(
askId: string, askId: string,
selected?: string[], selected?: string[],
customInput?: string, customInput?: string,
answers?: { [questionIndex: string]: string[] } answers?: { [questionIndex: string]: string[] },
): Promise<void> { ): Promise<void> {
if (currentSession) { if (currentSession) {
await currentSession.submitAnswer(askId, selected, customInput, answers); await currentSession.submitAnswer(askId, selected, customInput, answers);
@ -540,7 +552,7 @@ export async function handlePlanAction(
action: string, action: string,
planTitle: string, planTitle: string,
extensionPath: string, extensionPath: string,
serviceTier?: ServiceTier serviceTier?: ServiceTier,
): Promise<void> { ): Promise<void> {
console.log( console.log(
"[handlePlanAction] action:", "[handlePlanAction] action:",
@ -548,7 +560,7 @@ export async function handlePlanAction(
"planTitle:", "planTitle:",
planTitle, planTitle,
"serviceTier:", "serviceTier:",
serviceTier serviceTier,
); );
switch (action) { switch (action) {
@ -564,7 +576,7 @@ export async function handlePlanAction(
`请按照刚才的计划执行:${planTitle}`, `请按照刚才的计划执行:${planTitle}`,
extensionPath, extensionPath,
"agent", "agent",
serviceTier serviceTier,
); );
break; break;
@ -581,7 +593,7 @@ export async function handlePlanAction(
`请根据以下建议修改计划:${modification}`, `请根据以下建议修改计划:${modification}`,
extensionPath, extensionPath,
"plan", "plan",
serviceTier serviceTier,
); );
} }
break; break;
@ -636,7 +648,7 @@ function parseFileOperation(text: string): {
// 匹配重命名文件:将 xxx.ts 重命名为 yyy.ts 或 把 xxx.ts 改名为 yyy.ts优先匹配避免被修改匹配 // 匹配重命名文件:将 xxx.ts 重命名为 yyy.ts 或 把 xxx.ts 改名为 yyy.ts优先匹配避免被修改匹配
const renameMatch = lowerText.match( const renameMatch = lowerText.match(
/(?:将|把)\s*(.+?\.\w+)\s*(?:重命名|改名)\s*(?:为|成)\s*(.+?\.\w+)/ /(?:将|把)\s*(.+?\.\w+)\s*(?:重命名|改名)\s*(?:为|成)\s*(.+?\.\w+)/,
); );
if (renameMatch) { if (renameMatch) {
const oldPath = renameMatch[1].trim(); const oldPath = renameMatch[1].trim();
@ -653,7 +665,7 @@ function parseFileOperation(text: string): {
// 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb" // 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb"
// 格式3: 将 xxx.ts 文件 'aaa' 替换为 'bbb' // 格式3: 将 xxx.ts 文件 'aaa' 替换为 'bbb'
const replaceMatch1 = lowerText.match( const replaceMatch1 = lowerText.match(
/在\s*(.+?\.\w+)\s*(?:文件)?中?\s*(?:将|把)\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/ /在\s*(.+?\.\w+)\s*(?:文件)?中?\s*(?:将|把)\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/,
); );
if (replaceMatch1) { if (replaceMatch1) {
const filePath = replaceMatch1[1].trim(); const filePath = replaceMatch1[1].trim();
@ -669,7 +681,7 @@ function parseFileOperation(text: string): {
// 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb" // 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb"
const replaceMatch2 = lowerText.match( const replaceMatch2 = lowerText.match(
/(?:将|把)\s*(.+?\.\w+)\s*(?:文件)?\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/ /(?:将|把)\s*(.+?\.\w+)\s*(?:文件)?\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/,
); );
if (replaceMatch2) { if (replaceMatch2) {
const filePath = replaceMatch2[1].trim(); const filePath = replaceMatch2[1].trim();
@ -718,7 +730,7 @@ async function handleFileOperation(
newPath?: string; newPath?: string;
searchText?: string; searchText?: string;
replaceText?: string; replaceText?: string;
} },
) { ) {
const historyManager = ChatHistoryManager.getInstance(); const historyManager = ChatHistoryManager.getInstance();
@ -734,7 +746,7 @@ async function handleFileOperation(
text: responseText, text: responseText,
}); });
vscode.window.showInformationMessage( vscode.window.showInformationMessage(
`文件创建成功: ${operation.filePath}` `文件创建成功: ${operation.filePath}`,
); );
await historyManager.addAiMessage(responseText); await historyManager.addAiMessage(responseText);
break; break;
@ -747,7 +759,7 @@ async function handleFileOperation(
text: responseText, text: responseText,
}); });
vscode.window.showInformationMessage( vscode.window.showInformationMessage(
`文件删除成功: ${operation.filePath}` `文件删除成功: ${operation.filePath}`,
); );
await historyManager.addAiMessage(responseText); await historyManager.addAiMessage(responseText);
break; break;
@ -783,7 +795,7 @@ async function handleFileOperation(
text: responseText, text: responseText,
}); });
vscode.window.showInformationMessage( vscode.window.showInformationMessage(
`文件重命名成功: ${operation.filePath}${operation.newPath}` `文件重命名成功: ${operation.filePath}${operation.newPath}`,
); );
await historyManager.addAiMessage(responseText); await historyManager.addAiMessage(responseText);
break; break;
@ -792,21 +804,29 @@ async function handleFileOperation(
if (!operation.searchText || !operation.replaceText) { if (!operation.searchText || !operation.replaceText) {
throw new Error("缺少替换内容"); throw new Error("缺少替换内容");
} }
const oldContentBeforeReplace = await readFileContent(operation.filePath); const oldContentBeforeReplace = await readFileContent(
operation.filePath,
);
await replaceFile( await replaceFile(
operation.filePath, operation.filePath,
operation.searchText, operation.searchText,
operation.replaceText operation.replaceText,
);
const newContentAfterReplace = await readFileContent(
operation.filePath,
);
await trackFileChange(
operation.filePath,
oldContentBeforeReplace,
newContentAfterReplace,
); );
const newContentAfterReplace = await readFileContent(operation.filePath);
await trackFileChange(operation.filePath, oldContentBeforeReplace, newContentAfterReplace);
responseText = `✅ 文件内容替换成功: ${operation.filePath}`; responseText = `✅ 文件内容替换成功: ${operation.filePath}`;
panel.webview.postMessage({ panel.webview.postMessage({
command: "receiveMessage", command: "receiveMessage",
text: responseText, text: responseText,
}); });
vscode.window.showInformationMessage( vscode.window.showInformationMessage(
`文件内容替换成功: ${operation.filePath}` `文件内容替换成功: ${operation.filePath}`,
); );
await historyManager.addAiMessage(responseText); await historyManager.addAiMessage(responseText);
break; break;
@ -866,7 +886,7 @@ function getDefaultContent(filePath: string): string {
*/ */
export async function handleReadFile( export async function handleReadFile(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
filePath: string filePath: string,
) { ) {
try { try {
const content = await readFileContent(filePath); const content = await readFileContent(filePath);
@ -890,7 +910,7 @@ export async function handleCreateFile(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
filePath: string, filePath: string,
content: string, content: string,
overwrite: boolean = false //是否覆盖 overwrite: boolean = false, //是否覆盖
) { ) {
try { try {
if (overwrite) { if (overwrite) {
@ -909,11 +929,14 @@ export async function handleCreateFile(
// 发送系统通知 // 发送系统通知
const notificationService = NotificationService.getInstance(); const notificationService = NotificationService.getInstance();
notificationService.success( notificationService.success(
'IC Coder - 文件创建', "IC Coder - 文件创建",
`文件已创建: ${path.basename(filePath)}`, `文件已创建: ${path.basename(filePath)}`,
() => { () => {
vscode.commands.executeCommand('vscode.open', vscode.Uri.file(filePath)); vscode.commands.executeCommand(
} "vscode.open",
vscode.Uri.file(filePath),
);
},
); );
} catch (error) { } catch (error) {
panel.webview.postMessage({ panel.webview.postMessage({
@ -921,7 +944,7 @@ export async function handleCreateFile(
error: error instanceof Error ? error.message : "创建文件失败", error: error instanceof Error ? error.message : "创建文件失败",
}); });
vscode.window.showErrorMessage( vscode.window.showErrorMessage(
`创建文件失败: ${error instanceof Error ? error.message : "未知错误"}` `创建文件失败: ${error instanceof Error ? error.message : "未知错误"}`,
); );
} }
} }
@ -932,7 +955,7 @@ export async function handleCreateFile(
export async function handleUpdateFile( export async function handleUpdateFile(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
filePath: string, filePath: string,
content: string content: string,
) { ) {
try { try {
const oldContent = await readFileContent(filePath); const oldContent = await readFileContent(filePath);
@ -948,8 +971,8 @@ export async function handleUpdateFile(
// 发送系统通知 // 发送系统通知
const notificationService = NotificationService.getInstance(); const notificationService = NotificationService.getInstance();
notificationService.info( notificationService.info(
'IC Coder - 文件更新', "IC Coder - 文件更新",
`文件已更新: ${path.basename(filePath)}` `文件已更新: ${path.basename(filePath)}`,
); );
} catch (error) { } catch (error) {
panel.webview.postMessage({ panel.webview.postMessage({
@ -957,7 +980,7 @@ export async function handleUpdateFile(
error: error instanceof Error ? error.message : "更新文件失败", error: error instanceof Error ? error.message : "更新文件失败",
}); });
vscode.window.showErrorMessage( vscode.window.showErrorMessage(
`更新文件失败: ${error instanceof Error ? error.message : "未知错误"}` `更新文件失败: ${error instanceof Error ? error.message : "未知错误"}`,
); );
} }
} }
@ -968,7 +991,7 @@ export async function handleUpdateFile(
export async function handleRenameFile( export async function handleRenameFile(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
oldPath: string, oldPath: string,
newPath: string newPath: string,
) { ) {
try { try {
await renameFile(oldPath, newPath); await renameFile(oldPath, newPath);
@ -979,7 +1002,7 @@ export async function handleRenameFile(
message: "文件重命名成功", message: "文件重命名成功",
}); });
vscode.window.showInformationMessage( vscode.window.showInformationMessage(
`文件重命名成功: ${oldPath}${newPath}` `文件重命名成功: ${oldPath}${newPath}`,
); );
} catch (error) { } catch (error) {
panel.webview.postMessage({ panel.webview.postMessage({
@ -987,7 +1010,7 @@ export async function handleRenameFile(
error: error instanceof Error ? error.message : "重命名文件失败", error: error instanceof Error ? error.message : "重命名文件失败",
}); });
vscode.window.showErrorMessage( vscode.window.showErrorMessage(
`重命名文件失败: ${error instanceof Error ? error.message : "未知错误"}` `重命名文件失败: ${error instanceof Error ? error.message : "未知错误"}`,
); );
} }
} }
@ -999,7 +1022,7 @@ export async function handleReplaceInFile(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
filePath: string, filePath: string,
searchText: string, searchText: string,
replaceText: string replaceText: string,
) { ) {
try { try {
const oldContent = await readFileContent(filePath); const oldContent = await readFileContent(filePath);
@ -1018,7 +1041,7 @@ export async function handleReplaceInFile(
error: error instanceof Error ? error.message : "替换文件内容失败", error: error instanceof Error ? error.message : "替换文件内容失败",
}); });
vscode.window.showErrorMessage( vscode.window.showErrorMessage(
`替换文件内容失败: ${error instanceof Error ? error.message : "未知错误"}` `替换文件内容失败: ${error instanceof Error ? error.message : "未知错误"}`,
); );
} }
} }
@ -1063,7 +1086,7 @@ function isVCDGenerationCommand(text: string): boolean {
*/ */
async function handleVCDGeneration( async function handleVCDGeneration(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
extensionPath: string extensionPath: string,
) { ) {
try { try {
// 获取当前工作区路径 // 获取当前工作区路径
@ -1090,7 +1113,7 @@ async function handleVCDGeneration(
if (!iverilogCheck.available) { if (!iverilogCheck.available) {
panel.webview.postMessage({ panel.webview.postMessage({
command: "receiveMessage", command: "receiveMessage",
text: `${iverilogCheck.message}\n\n请参考插件文档安装 iverilog 工具`, text: `${iverilogCheck.message}`,
}); });
vscode.window.showErrorMessage(iverilogCheck.message); vscode.window.showErrorMessage(iverilogCheck.message);
return; return;
@ -1165,12 +1188,15 @@ async function handleVCDGeneration(
// 发送系统通知 // 发送系统通知
const notificationService = NotificationService.getInstance(); const notificationService = NotificationService.getInstance();
notificationService.success( notificationService.success(
'IC Coder - 仿真完成', "IC Coder - 仿真完成",
`VCD 文件已生成: ${fileName}`, `VCD 文件已生成: ${fileName}`,
() => { () => {
// 点击通知时打开 VCD 查看器 // 点击通知时打开 VCD 查看器
vscode.commands.executeCommand('ic-coder.openVCDViewer', result.vcdFilePath); vscode.commands.executeCommand(
} "ic-coder.openVCDViewer",
result.vcdFilePath,
);
},
); );
} else { } else {
panel.webview.postMessage({ panel.webview.postMessage({
@ -1199,12 +1225,12 @@ async function handleVCDGeneration(
// 发送系统通知 // 发送系统通知
const notificationService = NotificationService.getInstance(); const notificationService = NotificationService.getInstance();
notificationService.error( notificationService.error(
'IC Coder - 仿真失败', "IC Coder - 仿真失败",
'VCD 文件生成失败,请查看错误信息', "VCD 文件生成失败,请查看错误信息",
() => { () => {
// 点击通知时聚焦到面板 // 点击通知时聚焦到面板
panel.reveal(); panel.reveal();
} },
); );
} }
} catch (error) { } catch (error) {
@ -1222,11 +1248,11 @@ async function handleVCDGeneration(
// 发送系统通知 // 发送系统通知
const notificationService = NotificationService.getInstance(); const notificationService = NotificationService.getInstance();
notificationService.error( notificationService.error(
'IC Coder - 仿真错误', "IC Coder - 仿真错误",
error instanceof Error ? error.message : '生成 VCD 文件时出错', error instanceof Error ? error.message : "生成 VCD 文件时出错",
() => { () => {
panel.reveal(); panel.reveal();
} },
); );
} }
} }
@ -1236,7 +1262,7 @@ async function handleVCDGeneration(
*/ */
export async function handleOptimizePrompt( export async function handleOptimizePrompt(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
prompt: string prompt: string,
): Promise<void> { ): Promise<void> {
console.log("[MessageHandler] ========== 收到提示词优化请求 =========="); console.log("[MessageHandler] ========== 收到提示词优化请求 ==========");
console.log("[MessageHandler] prompt:", prompt); console.log("[MessageHandler] prompt:", prompt);
@ -1268,7 +1294,7 @@ export async function handleOptimizePrompt(
*/ */
export async function handleAcceptChange( export async function handleAcceptChange(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
changeId: string changeId: string,
) { ) {
try { try {
const success = await changeTracker.acceptChange(changeId); const success = await changeTracker.acceptChange(changeId);
@ -1276,14 +1302,14 @@ export async function handleAcceptChange(
panel.webview.postMessage({ panel.webview.postMessage({
command: "changeAccepted", command: "changeAccepted",
changeId: changeId, changeId: changeId,
success: true success: true,
}); });
} else { } else {
panel.webview.postMessage({ panel.webview.postMessage({
command: "changeAccepted", command: "changeAccepted",
changeId: changeId, changeId: changeId,
success: false, success: false,
error: "采纳变更失败" error: "采纳变更失败",
}); });
} }
} catch (error) { } catch (error) {
@ -1292,7 +1318,7 @@ export async function handleAcceptChange(
command: "changeAccepted", command: "changeAccepted",
changeId: changeId, changeId: changeId,
success: false, success: false,
error: String(error) error: String(error),
}); });
} }
} }
@ -1302,7 +1328,7 @@ export async function handleAcceptChange(
*/ */
export async function handleRejectChange( export async function handleRejectChange(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
changeId: string changeId: string,
) { ) {
try { try {
const success = await changeTracker.rejectChange(changeId); const success = await changeTracker.rejectChange(changeId);
@ -1310,14 +1336,14 @@ export async function handleRejectChange(
panel.webview.postMessage({ panel.webview.postMessage({
command: "changeRejected", command: "changeRejected",
changeId: changeId, changeId: changeId,
success: true success: true,
}); });
} else { } else {
panel.webview.postMessage({ panel.webview.postMessage({
command: "changeRejected", command: "changeRejected",
changeId: changeId, changeId: changeId,
success: false, success: false,
error: "拒绝变更失败" error: "拒绝变更失败",
}); });
} }
} catch (error) { } catch (error) {
@ -1326,7 +1352,7 @@ export async function handleRejectChange(
command: "changeRejected", command: "changeRejected",
changeId: changeId, changeId: changeId,
success: false, success: false,
error: String(error) error: String(error),
}); });
} }
} }
@ -1337,18 +1363,18 @@ export async function handleRejectChange(
export function sendChangesToWebview(panel: vscode.WebviewPanel) { export function sendChangesToWebview(panel: vscode.WebviewPanel) {
const session = changeTracker.endSession(); const session = changeTracker.endSession();
if (session && session.changes.length > 0) { if (session && session.changes.length > 0) {
const changesWithDiff = session.changes.map(change => { const changesWithDiff = session.changes.map((change) => {
const diffLines = generateDiff(change.oldContent, change.newContent); const diffLines = generateDiff(change.oldContent, change.newContent);
const diffHtml = renderDiffHtml(diffLines); const diffHtml = renderDiffHtml(diffLines);
return { return {
...change, ...change,
diffHtml diffHtml,
}; };
}); });
panel.webview.postMessage({ panel.webview.postMessage({
command: "showChanges", command: "showChanges",
changes: changesWithDiff changes: changesWithDiff,
}); });
} }
} }
@ -1365,62 +1391,67 @@ export function startChangeSession(sessionId: string) {
*/ */
export async function handleOpenFileDiff( export async function handleOpenFileDiff(
panel: vscode.WebviewPanel, panel: vscode.WebviewPanel,
changeId: string changeId: string,
) { ) {
try { try {
const session = changeTracker.getCurrentSession(); const session = changeTracker.getCurrentSession();
if (!session) { if (!session) {
vscode.window.showErrorMessage('没有找到变更会话'); vscode.window.showErrorMessage("没有找到变更会话");
return; return;
} }
const change = session.changes.find(c => c.changeId === changeId); const change = session.changes.find((c) => c.changeId === changeId);
if (!change) { if (!change) {
vscode.window.showErrorMessage('没有找到该变更'); vscode.window.showErrorMessage("没有找到该变更");
return; return;
} }
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (!workspaceFolder) { if (!workspaceFolder) {
vscode.window.showErrorMessage('没有打开的工作区'); vscode.window.showErrorMessage("没有打开的工作区");
return; return;
} }
// 创建临时文件用于对比 // 创建临时文件用于对比
const filePath = change.filePath; const filePath = change.filePath;
const absolutePath = vscode.Uri.file( const absolutePath = vscode.Uri.file(
path.join(workspaceFolder.uri.fsPath, filePath) path.join(workspaceFolder.uri.fsPath, filePath),
); );
// 创建虚拟文档显示旧内容 // 创建虚拟文档显示旧内容
const oldUri = vscode.Uri.parse( const oldUri = vscode.Uri.parse(
`ic-coder-diff:${filePath}.old?${changeId}` `ic-coder-diff:${filePath}.old?${changeId}`,
).with({ scheme: 'ic-coder-diff' }); ).with({ scheme: "ic-coder-diff" });
// 注册文档内容提供者(如果还没注册) // 注册文档内容提供者(如果还没注册)
if (!(global as any).__diffProviderRegistered) { if (!(global as any).__diffProviderRegistered) {
const provider = new (class implements vscode.TextDocumentContentProvider { const provider = new (class
implements vscode.TextDocumentContentProvider
{
provideTextDocumentContent(uri: vscode.Uri): string { provideTextDocumentContent(uri: vscode.Uri): string {
const changeId = uri.query; const changeId = uri.query;
const session = changeTracker.getCurrentSession(); const session = changeTracker.getCurrentSession();
const change = session?.changes.find(c => c.changeId === changeId); const change = session?.changes.find((c) => c.changeId === changeId);
return change?.oldContent || ''; return change?.oldContent || "";
} }
})(); })();
vscode.workspace.registerTextDocumentContentProvider('ic-coder-diff', provider); vscode.workspace.registerTextDocumentContentProvider(
"ic-coder-diff",
provider,
);
(global as any).__diffProviderRegistered = true; (global as any).__diffProviderRegistered = true;
} }
// 打开 diff 编辑器 // 打开 diff 编辑器
await vscode.commands.executeCommand( await vscode.commands.executeCommand(
'vscode.diff', "vscode.diff",
oldUri, oldUri,
absolutePath, absolutePath,
`${filePath} (变更对比)` `${filePath} (变更对比)`,
); );
} catch (error) { } catch (error) {
console.error('[MessageHandler] 打开 diff 失败:', error); console.error("[MessageHandler] 打开 diff 失败:", error);
vscode.window.showErrorMessage(`打开 diff 失败: ${error}`); vscode.window.showErrorMessage(`打开 diff 失败: ${error}`);
} }
} }