refactor: 优化 ICHelperPanel 组件结构
- 将 1346 行的单文件拆分为 7 个职责单一的模块 - authHelper: 认证和登录检查 - userInfoHelper: 用户信息管理 - conversationHelper: 会话历史加载 - vcdHelper: VCD 文件处理 - contextHelper: 上下文管理 - fileHelper: 文件操作 - messageRouter: 消息路由分发 - 主组件精简至 157 行,提升可维护性
This commit is contained in:
File diff suppressed because it is too large
Load Diff
66
src/panels/helpers/authHelper.ts
Normal file
66
src/panels/helpers/authHelper.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* 认证辅助模块
|
||||||
|
* 功能:处理用户登录状态检查和 token 验证
|
||||||
|
* 依赖:vscode, jwtUtils
|
||||||
|
* 使用场景:面板初始化时验证用户登录状态
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import { isTokenExpired } from "../../utils/jwtUtils";
|
||||||
|
|
||||||
|
export async function checkAuthAndPromptLogin(
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
): Promise<boolean> {
|
||||||
|
let token: string | undefined;
|
||||||
|
try {
|
||||||
|
const session = await vscode.authentication.getSession("iccoder", [], {
|
||||||
|
createIfNone: false,
|
||||||
|
});
|
||||||
|
token = session?.accessToken;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[AuthHelper] 获取 session 失败:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token && isTokenExpired(token)) {
|
||||||
|
await context.globalState.update("icCoderSessions", []);
|
||||||
|
await context.globalState.update("icCoderUserInfo", undefined);
|
||||||
|
const action = await vscode.window.showWarningMessage(
|
||||||
|
"登录已过期,请重新登录",
|
||||||
|
"立即登录",
|
||||||
|
);
|
||||||
|
if (action === "立即登录") {
|
||||||
|
vscode.commands.executeCommand("ic-coder.login", { forceReauth: true });
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const session = await vscode.authentication.getSession("iccoder", [], {
|
||||||
|
createIfNone: false,
|
||||||
|
});
|
||||||
|
if (!session) {
|
||||||
|
vscode.window
|
||||||
|
.showWarningMessage("请先登录后再使用 IC Coder", "立即登录")
|
||||||
|
.then((selection) => {
|
||||||
|
if (selection === "立即登录") {
|
||||||
|
vscode.commands.executeCommand("ic-coder.login", {
|
||||||
|
forceReauth: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
vscode.window
|
||||||
|
.showWarningMessage("请先登录后再使用 IC Coder", "立即登录")
|
||||||
|
.then((selection) => {
|
||||||
|
if (selection === "立即登录") {
|
||||||
|
vscode.commands.executeCommand("ic-coder.login", {
|
||||||
|
forceReauth: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
104
src/panels/helpers/contextHelper.ts
Normal file
104
src/panels/helpers/contextHelper.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* 上下文管理模块
|
||||||
|
* 功能:处理文件、文件夹、图片、文档上下文添加
|
||||||
|
* 依赖:vscode, fs, path
|
||||||
|
* 使用场景:用户添加上下文项时
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
export async function handleAddContextFile(panel: vscode.WebviewPanel) {
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
if (!workspaceFolder) {
|
||||||
|
vscode.window.showWarningMessage("请先打开一个工作区");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = await vscode.workspace.findFiles(
|
||||||
|
"**/*",
|
||||||
|
"**/node_modules/**",
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "showWorkspaceFileList",
|
||||||
|
files: files.map((uri) => ({
|
||||||
|
path: uri.fsPath,
|
||||||
|
relativePath: vscode.workspace.asRelativePath(uri),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleAddContextFolder(panel: vscode.WebviewPanel) {
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
if (!workspaceFolder) {
|
||||||
|
vscode.window.showWarningMessage("请先打开一个工作区");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const folders: Array<{ path: string; relativePath: string }> = [];
|
||||||
|
|
||||||
|
function scanFolders(dir: string, baseDir: string) {
|
||||||
|
try {
|
||||||
|
const items = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
for (const item of items) {
|
||||||
|
if (
|
||||||
|
item.isDirectory() &&
|
||||||
|
item.name !== "node_modules" &&
|
||||||
|
!item.name.startsWith(".")
|
||||||
|
) {
|
||||||
|
const fullPath = path.join(dir, item.name);
|
||||||
|
const relativePath = path.relative(baseDir, fullPath);
|
||||||
|
folders.push({ path: fullPath, relativePath });
|
||||||
|
scanFolders(fullPath, baseDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("扫描文件夹失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanFolders(workspaceFolder.uri.fsPath, workspaceFolder.uri.fsPath);
|
||||||
|
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "showWorkspaceFolderList",
|
||||||
|
folders: folders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleAddContextImage(panel: vscode.WebviewPanel) {
|
||||||
|
const imageUris = await vscode.window.showOpenDialog({
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: true,
|
||||||
|
openLabel: "选择图片",
|
||||||
|
filters: {
|
||||||
|
图片文件: ["png", "jpg", "jpeg", "gif", "bmp", "svg", "webp"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (imageUris && imageUris.length > 0) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "contextImagesSelected",
|
||||||
|
images: imageUris.map((uri) => uri.fsPath),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleAddContextDocument(panel: vscode.WebviewPanel) {
|
||||||
|
const docUris = await vscode.window.showOpenDialog({
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: true,
|
||||||
|
openLabel: "选择文档",
|
||||||
|
filters: {
|
||||||
|
文档文件: ["pdf", "doc", "docx", "txt", "md"],
|
||||||
|
所有文件: ["*"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (docUris && docUris.length > 0) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "contextDocumentsSelected",
|
||||||
|
documents: docUris.map((uri) => uri.fsPath),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
219
src/panels/helpers/conversationHelper.ts
Normal file
219
src/panels/helpers/conversationHelper.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/**
|
||||||
|
* 会话历史管理模块
|
||||||
|
* 功能:加载和选择会话历史
|
||||||
|
* 依赖:vscode, chatHistoryManager, messageHandler
|
||||||
|
* 使用场景:会话历史列表和切换
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import { ChatHistoryManager } from "../../utils/chatHistoryManager";
|
||||||
|
import { MessageType } from "../../types/chatHistory";
|
||||||
|
import { setLastTaskId } from "../../utils/messageHandler";
|
||||||
|
|
||||||
|
export async function loadConversationHistory(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
offset: number = 0,
|
||||||
|
limit: number = 10,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const historyManager = ChatHistoryManager.getInstance();
|
||||||
|
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
||||||
|
|
||||||
|
if (!workspacePath) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "conversationHistory",
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
hasMore: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await historyManager.getConversationHistoryList(
|
||||||
|
workspacePath,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "conversationHistory",
|
||||||
|
items: result.items,
|
||||||
|
total: result.total,
|
||||||
|
hasMore: result.hasMore,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载会话历史失败:", error);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "conversationHistory",
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
hasMore: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function selectConversation(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
taskId: string,
|
||||||
|
extensionPath: string,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const historyManager = ChatHistoryManager.getInstance();
|
||||||
|
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
||||||
|
|
||||||
|
if (!workspacePath) {
|
||||||
|
vscode.window.showErrorMessage("没有打开的工作区");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskSession = await historyManager.loadTaskSession(
|
||||||
|
workspacePath,
|
||||||
|
taskId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!taskSession) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
`加载任务 ${taskId} 失败: 任务不存在或数据损坏`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const switched = await historyManager.switchTask(workspacePath, taskId);
|
||||||
|
if (!switched) {
|
||||||
|
vscode.window.showErrorMessage(`切换到任务 ${taskId} 失败`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastTaskId(taskId);
|
||||||
|
|
||||||
|
const panelId = (panel as any).__uniqueId;
|
||||||
|
historyManager.setPanelTask(panelId, taskId, workspacePath);
|
||||||
|
|
||||||
|
panel.webview.postMessage({ command: "clearChat" });
|
||||||
|
|
||||||
|
const segments: any[] = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < taskSession.messages.length) {
|
||||||
|
const message = taskSession.messages[i];
|
||||||
|
|
||||||
|
if (message.type === MessageType.USER) {
|
||||||
|
if (segments.length > 0) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveSegments",
|
||||||
|
segments: [...segments],
|
||||||
|
});
|
||||||
|
segments.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const textContent = message.contents?.find((c) => c.type === "TEXT");
|
||||||
|
if (textContent && "text" in textContent) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "addUserMessage",
|
||||||
|
text: textContent.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} else if (message.type === MessageType.AI) {
|
||||||
|
if (message.segments && message.segments.length > 0) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveSegments",
|
||||||
|
segments: message.segments,
|
||||||
|
});
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
if (message.text) {
|
||||||
|
segments.push({ type: "text", content: message.text });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
message.toolExecutionRequests &&
|
||||||
|
message.toolExecutionRequests.length > 0
|
||||||
|
) {
|
||||||
|
for (const toolReq of message.toolExecutionRequests) {
|
||||||
|
let toolResult = "";
|
||||||
|
if (i + 1 < taskSession.messages.length) {
|
||||||
|
const nextMsg = taskSession.messages[i + 1];
|
||||||
|
if (
|
||||||
|
nextMsg.type === MessageType.TOOL_EXECUTION_RESULT &&
|
||||||
|
nextMsg.id === toolReq.id
|
||||||
|
) {
|
||||||
|
toolResult = nextMsg.text;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.push({
|
||||||
|
type: "tool",
|
||||||
|
toolName: toolReq.name,
|
||||||
|
askId: toolReq.id,
|
||||||
|
toolResult: toolResult,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
while (i < taskSession.messages.length) {
|
||||||
|
const nextMsg = taskSession.messages[i];
|
||||||
|
if (nextMsg.type === MessageType.USER) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nextMsg.type === MessageType.AI) {
|
||||||
|
if (nextMsg.segments && nextMsg.segments.length > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nextMsg.text) {
|
||||||
|
segments.push({ type: "text", content: nextMsg.text });
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
nextMsg.toolExecutionRequests &&
|
||||||
|
nextMsg.toolExecutionRequests.length > 0
|
||||||
|
) {
|
||||||
|
for (const toolReq of nextMsg.toolExecutionRequests) {
|
||||||
|
let toolResult = "";
|
||||||
|
if (i + 1 < taskSession.messages.length) {
|
||||||
|
const resultMsg = taskSession.messages[i + 1];
|
||||||
|
if (
|
||||||
|
resultMsg.type === MessageType.TOOL_EXECUTION_RESULT &&
|
||||||
|
resultMsg.id === toolReq.id
|
||||||
|
) {
|
||||||
|
toolResult = resultMsg.text;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segments.push({
|
||||||
|
type: "tool",
|
||||||
|
toolName: toolReq.name,
|
||||||
|
askId: toolReq.id,
|
||||||
|
toolResult: toolResult,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} else if (nextMsg.type === MessageType.TOOL_EXECUTION_RESULT) {
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segments.length > 0) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveSegments",
|
||||||
|
segments: segments,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
vscode.window.showInformationMessage(
|
||||||
|
`已加载会话: ${taskSession.meta.taskName}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("选择会话失败:", error);
|
||||||
|
vscode.window.showErrorMessage(`加载会话失败: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/panels/helpers/fileHelper.ts
Normal file
78
src/panels/helpers/fileHelper.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* 文件操作辅助模块
|
||||||
|
* 功能:处理文件打开、选择等操作
|
||||||
|
* 依赖:vscode, fs, path
|
||||||
|
* 使用场景:打开文件、跳转到代码位置
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
export async function openFile(filePath: string) {
|
||||||
|
const path = require("path");
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
const fullPath =
|
||||||
|
path.isAbsolute(filePath) || !workspaceFolder
|
||||||
|
? filePath
|
||||||
|
: vscode.Uri.joinPath(workspaceFolder.uri, filePath).fsPath;
|
||||||
|
const doc = await vscode.workspace.openTextDocument(fullPath);
|
||||||
|
await vscode.window.showTextDocument(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openFileWithSelection(
|
||||||
|
filePath: string,
|
||||||
|
startLine: number,
|
||||||
|
endLine: number,
|
||||||
|
) {
|
||||||
|
const path = require("path");
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
const fullPath =
|
||||||
|
path.isAbsolute(filePath) || !workspaceFolder
|
||||||
|
? filePath
|
||||||
|
: vscode.Uri.joinPath(workspaceFolder.uri, filePath).fsPath;
|
||||||
|
const doc = await vscode.workspace.openTextDocument(fullPath);
|
||||||
|
const editor = await vscode.window.showTextDocument(doc);
|
||||||
|
const start = new vscode.Position(startLine - 1, 0);
|
||||||
|
const end = new vscode.Position(
|
||||||
|
endLine - 1,
|
||||||
|
doc.lineAt(endLine - 1).text.length,
|
||||||
|
);
|
||||||
|
editor.selection = new vscode.Selection(start, end);
|
||||||
|
editor.revealRange(new vscode.Range(start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openFilePathTag(
|
||||||
|
filePath: string,
|
||||||
|
startLine?: number,
|
||||||
|
endLine?: number,
|
||||||
|
) {
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
|
|
||||||
|
let fullPath = filePath;
|
||||||
|
|
||||||
|
if (!path.isAbsolute(filePath) && workspaceFolder) {
|
||||||
|
const candidatePath = vscode.Uri.joinPath(
|
||||||
|
workspaceFolder.uri,
|
||||||
|
filePath,
|
||||||
|
).fsPath;
|
||||||
|
if (fs.existsSync(candidatePath)) {
|
||||||
|
fullPath = candidatePath;
|
||||||
|
} else {
|
||||||
|
const fileName = path.basename(filePath);
|
||||||
|
const files = await vscode.workspace.findFiles(
|
||||||
|
`**/${fileName}`,
|
||||||
|
"**/node_modules/**",
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
if (files.length > 0) {
|
||||||
|
fullPath = files[0].fsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startLine && endLine) {
|
||||||
|
await openFileWithSelection(fullPath, startLine, endLine);
|
||||||
|
} else {
|
||||||
|
await openFile(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
396
src/panels/helpers/messageRouter.ts
Normal file
396
src/panels/helpers/messageRouter.ts
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
/**
|
||||||
|
* 消息路由处理模块
|
||||||
|
* 功能:处理 webview 消息的路由分发
|
||||||
|
* 依赖:各个 helper 模块和 messageHandler
|
||||||
|
* 使用场景:webview 消息接收时
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import {
|
||||||
|
handleUserMessage,
|
||||||
|
insertCodeToEditor,
|
||||||
|
handleReadFile,
|
||||||
|
handleUpdateFile,
|
||||||
|
handleRenameFile,
|
||||||
|
handleReplaceInFile,
|
||||||
|
handleUserAnswer,
|
||||||
|
abortCurrentDialog,
|
||||||
|
handleOptimizePrompt,
|
||||||
|
handlePlanAction,
|
||||||
|
getCurrentTaskId,
|
||||||
|
handleAcceptChange,
|
||||||
|
handleRejectChange,
|
||||||
|
handleOpenFileDiff,
|
||||||
|
startChangeSession,
|
||||||
|
} from "../../utils/messageHandler";
|
||||||
|
import { compactDialog } from "../../services/apiClient";
|
||||||
|
import { ChatHistoryManager } from "../../utils/chatHistoryManager";
|
||||||
|
import { getCachedUserInfo } from "../../services/userService";
|
||||||
|
import { loadConversationHistory, selectConversation } from "./conversationHelper";
|
||||||
|
import { getVCDFileInfo } from "./vcdHelper";
|
||||||
|
import {
|
||||||
|
handleAddContextFile,
|
||||||
|
handleAddContextFolder,
|
||||||
|
handleAddContextImage,
|
||||||
|
handleAddContextDocument,
|
||||||
|
} from "./contextHelper";
|
||||||
|
import { openFile, openFileWithSelection, openFilePathTag } from "./fileHelper";
|
||||||
|
|
||||||
|
export async function handleWebviewMessage(
|
||||||
|
message: any,
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
) {
|
||||||
|
const historyManager = ChatHistoryManager.getInstance();
|
||||||
|
const panelId = (panel as any).__uniqueId;
|
||||||
|
|
||||||
|
switch (message.command) {
|
||||||
|
case "sendMessage":
|
||||||
|
if (!historyManager.getPanelTask(panelId)) {
|
||||||
|
const workspacePath =
|
||||||
|
vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
||||||
|
if (workspacePath) {
|
||||||
|
try {
|
||||||
|
const taskMeta = await historyManager.createTask(
|
||||||
|
workspacePath,
|
||||||
|
"新对话",
|
||||||
|
);
|
||||||
|
historyManager.setPanelTask(
|
||||||
|
panelId,
|
||||||
|
taskMeta.taskId,
|
||||||
|
workspacePath,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("创建任务失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
historyManager.switchToPanelTask(panelId);
|
||||||
|
const sessionId = `session_${panelId}_${Date.now()}`;
|
||||||
|
startChangeSession(sessionId);
|
||||||
|
panel.webview.postMessage({ type: "showProgress" });
|
||||||
|
|
||||||
|
handleUserMessage(
|
||||||
|
panel,
|
||||||
|
message.text,
|
||||||
|
context.extensionPath,
|
||||||
|
message.mode,
|
||||||
|
message.model,
|
||||||
|
message.contextItems,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "readFile":
|
||||||
|
handleReadFile(panel, message.filePath);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "updateFile":
|
||||||
|
handleUpdateFile(panel, message.filePath, message.content);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "renameFile":
|
||||||
|
handleRenameFile(panel, message.oldPath, message.newPath);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "replaceInFile":
|
||||||
|
handleReplaceInFile(
|
||||||
|
panel,
|
||||||
|
message.filePath,
|
||||||
|
message.searchText,
|
||||||
|
message.replaceText,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "insertCode":
|
||||||
|
insertCodeToEditor(message.code);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "showInfo":
|
||||||
|
vscode.window.showInformationMessage(message.text);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openWaveformViewer":
|
||||||
|
if (message.vcdFilePath) {
|
||||||
|
vscode.commands.executeCommand(
|
||||||
|
"ic-coder.openVCDViewer",
|
||||||
|
message.vcdFilePath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "getVCDInfo":
|
||||||
|
if (message.vcdFilePath && message.containerId) {
|
||||||
|
getVCDFileInfo(panel, message.vcdFilePath, message.containerId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "createNewConversation":
|
||||||
|
const { showICHelperPanel } = require("../ICHelperPanel");
|
||||||
|
showICHelperPanel(context, panel.viewColumn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "loadConversationHistory":
|
||||||
|
loadConversationHistory(
|
||||||
|
panel,
|
||||||
|
message.offset || 0,
|
||||||
|
message.limit || 10,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "selectConversation":
|
||||||
|
if (message.conversationId) {
|
||||||
|
selectConversation(panel, message.conversationId, context.extensionPath);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "submitAnswer":
|
||||||
|
void handleUserAnswer(
|
||||||
|
message.askId,
|
||||||
|
message.selected,
|
||||||
|
message.customInput,
|
||||||
|
message.answers,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "abortDialog":
|
||||||
|
void abortCurrentDialog();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "compressConversation":
|
||||||
|
{
|
||||||
|
const taskId = getCurrentTaskId();
|
||||||
|
if (taskId) {
|
||||||
|
compactDialog(taskId)
|
||||||
|
.then((result) => {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: result.success
|
||||||
|
? "✅ 会话压缩完成"
|
||||||
|
: `❌ 压缩失败: ${result.error || "未知错误"}`,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: `❌ 压缩失败: ${err.message || "网络错误"}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "receiveMessage",
|
||||||
|
text: "❌ 没有活跃的会话",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "optimizePrompt":
|
||||||
|
if (typeof message.prompt === "string") {
|
||||||
|
void handleOptimizePrompt(panel, message.prompt);
|
||||||
|
} else {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "optimizeResult",
|
||||||
|
success: false,
|
||||||
|
error: "提示词为空或格式错误",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "logout":
|
||||||
|
vscode.commands.executeCommand("ic-coder.logout");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openFile":
|
||||||
|
if (message.filePath) {
|
||||||
|
await openFile(message.filePath);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openFileWithSelection":
|
||||||
|
if (message.filePath) {
|
||||||
|
await openFileWithSelection(
|
||||||
|
message.filePath,
|
||||||
|
message.startLine,
|
||||||
|
message.endLine,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openFilePathTag":
|
||||||
|
if (message.filePath) {
|
||||||
|
await openFilePathTag(
|
||||||
|
message.filePath,
|
||||||
|
message.startLine,
|
||||||
|
message.endLine,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "acceptChange":
|
||||||
|
if (message.changeId) {
|
||||||
|
await handleAcceptChange(panel, message.changeId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "rejectChange":
|
||||||
|
if (message.changeId) {
|
||||||
|
await handleRejectChange(panel, message.changeId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openFileDiff":
|
||||||
|
if (message.changeId) {
|
||||||
|
await handleOpenFileDiff(panel, message.changeId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "checkInvitationCode":
|
||||||
|
{
|
||||||
|
const userInfo = getCachedUserInfo();
|
||||||
|
if (userInfo?.isPluginTrial === true) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "invitationCodeStatus",
|
||||||
|
verified: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const { InvitationService } = require("../../services/invitationService");
|
||||||
|
const isVerified = await InvitationService.isVerified(context);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "invitationCodeStatus",
|
||||||
|
verified: isVerified,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "checkWelcomeModal":
|
||||||
|
{
|
||||||
|
const userInfo = getCachedUserInfo();
|
||||||
|
if (userInfo?.isPluginTrial === true) {
|
||||||
|
if (userInfo.pluginTrialExpiresAt === undefined) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (userInfo.pluginTrialExpiresAt !== null) {
|
||||||
|
const now = Date.now();
|
||||||
|
const isExpired = now >= userInfo.pluginTrialExpiresAt;
|
||||||
|
if (isExpired) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel.webview.postMessage({ command: "showWelcomeModal" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "checkTrialExpiration":
|
||||||
|
{
|
||||||
|
const { TrialExpirationService } = require("../../services/trialExpirationService");
|
||||||
|
const trialService = new TrialExpirationService(context, panel);
|
||||||
|
await trialService.checkExpiration();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "verifyInvitationCode":
|
||||||
|
{
|
||||||
|
const { InvitationService } = require("../../services/invitationService");
|
||||||
|
const result = await InvitationService.verifyCode(message.code);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
await InvitationService.saveVerificationStatus(context, message.code);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "invitationCodeVerified",
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
panel.webview.postMessage({ command: "showNdtWelcomeModal" });
|
||||||
|
}, 300);
|
||||||
|
} else {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "invitationCodeVerified",
|
||||||
|
success: false,
|
||||||
|
message: result.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openICCoder":
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openTutorial":
|
||||||
|
vscode.env.openExternal(
|
||||||
|
vscode.Uri.parse(
|
||||||
|
"https://www.iccoder.com/guides/quick-start/first-task-plugin",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openUserManual":
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse("https://www.iccoder.com"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openUserFeedback":
|
||||||
|
panel.webview.postMessage({ command: "showFeedbackQRCode" });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "planAction":
|
||||||
|
if (message.action === "confirm") {
|
||||||
|
panel.webview.postMessage({ command: "switchMode", mode: "agent" });
|
||||||
|
} else if (message.action === "modify" || message.action === "cancel") {
|
||||||
|
void handlePlanAction(
|
||||||
|
panel,
|
||||||
|
message.action,
|
||||||
|
message.planTitle || "",
|
||||||
|
context.extensionPath,
|
||||||
|
message.model,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "addContextFile":
|
||||||
|
await handleAddContextFile(panel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "addContextFolder":
|
||||||
|
await handleAddContextFolder(panel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "addContextImage":
|
||||||
|
await handleAddContextImage(panel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "addContextDocument":
|
||||||
|
await handleAddContextDocument(panel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "checkWorkspace":
|
||||||
|
const hasWorkspace = !!(
|
||||||
|
vscode.workspace.workspaceFolders &&
|
||||||
|
vscode.workspace.workspaceFolders.length > 0
|
||||||
|
);
|
||||||
|
if (!hasWorkspace) {
|
||||||
|
vscode.window
|
||||||
|
.showWarningMessage(
|
||||||
|
"请先打开一个文件夹作为工作区,这样我就能更好地为您服务了 😊",
|
||||||
|
"打开文件夹",
|
||||||
|
)
|
||||||
|
.then((selection) => {
|
||||||
|
if (selection === "打开文件夹") {
|
||||||
|
vscode.commands.executeCommand("vscode.openFolder");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "workspaceStatus",
|
||||||
|
hasWorkspace: hasWorkspace,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "openExternalUrl":
|
||||||
|
if (message.url) {
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse(message.url));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/panels/helpers/userInfoHelper.ts
Normal file
116
src/panels/helpers/userInfoHelper.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* 用户信息辅助模块
|
||||||
|
* 功能:管理用户信息的获取、更新和发送
|
||||||
|
* 依赖:vscode, userService, creditsService
|
||||||
|
* 使用场景:面板初始化和余额更新时
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import { getCachedUserInfo } from "../../services/userService";
|
||||||
|
import { setBalanceUpdateCallback } from "../../services/creditsService";
|
||||||
|
|
||||||
|
export function getTierIconUri(
|
||||||
|
webview: vscode.Webview,
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
tierCode?: string,
|
||||||
|
): string | undefined {
|
||||||
|
if (!tierCode) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tierIconMap: Record<string, string> = {
|
||||||
|
BASIC: "free.png",
|
||||||
|
TRIAL: "PRO-Try.png",
|
||||||
|
ADVANCED: "PRO.png",
|
||||||
|
PROFESSIONAL: "PRO+.png",
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconFile = tierIconMap[tierCode];
|
||||||
|
if (!iconFile) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconUri = webview.asWebviewUri(
|
||||||
|
vscode.Uri.joinPath(
|
||||||
|
context.extensionUri,
|
||||||
|
"dist",
|
||||||
|
"assets",
|
||||||
|
"titleIcon",
|
||||||
|
iconFile,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return iconUri.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendUserInfoToWebview(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
let userInfo = getCachedUserInfo();
|
||||||
|
|
||||||
|
if (userInfo) {
|
||||||
|
console.log("[UserInfoHelper] 使用缓存的用户信息:", userInfo);
|
||||||
|
const tierIconUrl = getTierIconUri(
|
||||||
|
panel.webview,
|
||||||
|
context,
|
||||||
|
userInfo.membership?.tierCode,
|
||||||
|
);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "updateUserInfo",
|
||||||
|
userInfo: {
|
||||||
|
userId: userInfo.userId,
|
||||||
|
nickname: userInfo.nickname,
|
||||||
|
username: userInfo.username,
|
||||||
|
credits: userInfo.credits,
|
||||||
|
membership: userInfo.membership,
|
||||||
|
},
|
||||||
|
tierIconUrl: tierIconUrl,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const session = await vscode.authentication.getSession("iccoder", [], {
|
||||||
|
createIfNone: false,
|
||||||
|
});
|
||||||
|
if (session) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "updateUserInfo",
|
||||||
|
userInfo: {
|
||||||
|
userId: session.account.id,
|
||||||
|
nickname: session.account.label,
|
||||||
|
username: session.account.label,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[UserInfoHelper] 获取用户信息失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupBalanceUpdateCallback(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
context: vscode.ExtensionContext,
|
||||||
|
) {
|
||||||
|
setBalanceUpdateCallback((balance: number) => {
|
||||||
|
const userInfo = getCachedUserInfo();
|
||||||
|
if (userInfo) {
|
||||||
|
userInfo.credits = balance;
|
||||||
|
const tierIconUrl = getTierIconUri(
|
||||||
|
panel.webview,
|
||||||
|
context,
|
||||||
|
userInfo.membership?.tierCode,
|
||||||
|
);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "updateUserInfo",
|
||||||
|
userInfo: {
|
||||||
|
userId: userInfo.userId,
|
||||||
|
nickname: userInfo.nickname,
|
||||||
|
username: userInfo.username,
|
||||||
|
credits: balance,
|
||||||
|
membership: userInfo.membership,
|
||||||
|
},
|
||||||
|
tierIconUrl: tierIconUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
158
src/panels/helpers/vcdHelper.ts
Normal file
158
src/panels/helpers/vcdHelper.ts
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* VCD 文件处理模块
|
||||||
|
* 功能:VCD 文件信息获取和信号解析
|
||||||
|
* 依赖:vscode, fs
|
||||||
|
* 使用场景:波形查看器相关功能
|
||||||
|
*/
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
export async function getVCDFileInfo(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
vcdFilePath: string,
|
||||||
|
containerId: string,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
if (!fs.existsSync(vcdFilePath)) {
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "vcdInfo",
|
||||||
|
containerId: containerId,
|
||||||
|
vcdInfo: {
|
||||||
|
signalCount: "N/A",
|
||||||
|
timeRange: "N/A",
|
||||||
|
fileSize: "N/A",
|
||||||
|
error: "文件不存在",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = fs.statSync(vcdFilePath);
|
||||||
|
const fileSizeKB = stats.size / 1024;
|
||||||
|
const fileSize =
|
||||||
|
fileSizeKB < 1024
|
||||||
|
? `${fileSizeKB.toFixed(2)} KB`
|
||||||
|
: `${(fileSizeKB / 1024).toFixed(2)} MB`;
|
||||||
|
|
||||||
|
const content = fs.readFileSync(vcdFilePath, "utf-8");
|
||||||
|
const varMatches = content.match(/\$var/g);
|
||||||
|
const signalCount = varMatches ? varMatches.length : 0;
|
||||||
|
|
||||||
|
let timeRange = "N/A";
|
||||||
|
const timeMatch = content.match(/#(\d+)/g);
|
||||||
|
if (timeMatch && timeMatch.length > 0) {
|
||||||
|
const times = timeMatch.map((t: string) => parseInt(t.substring(1)));
|
||||||
|
const minTime = Math.min(...times);
|
||||||
|
const maxTime = Math.max(...times);
|
||||||
|
timeRange = `${minTime} - ${maxTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signals = parseVCDSignals(content, 3);
|
||||||
|
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "vcdInfo",
|
||||||
|
containerId: containerId,
|
||||||
|
vcdInfo: {
|
||||||
|
signalCount: signalCount.toString(),
|
||||||
|
timeRange: timeRange,
|
||||||
|
fileSize: fileSize,
|
||||||
|
signals: signals,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取 VCD 文件信息失败:", error);
|
||||||
|
panel.webview.postMessage({
|
||||||
|
command: "vcdInfo",
|
||||||
|
containerId: containerId,
|
||||||
|
vcdInfo: {
|
||||||
|
signalCount: "N/A",
|
||||||
|
timeRange: "N/A",
|
||||||
|
fileSize: "N/A",
|
||||||
|
error: error instanceof Error ? error.message : "未知错误",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVCDSignals(content: string, maxSignals: number = 3) {
|
||||||
|
const signals: Array<{
|
||||||
|
name: string;
|
||||||
|
identifier: string;
|
||||||
|
width: number;
|
||||||
|
values: Array<{ time: number; value: string }>;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const varRegex = /\$var\s+(\w+)\s+(\d+)\s+(\S+)\s+([^\$]+?)\s+\$end/g;
|
||||||
|
let match;
|
||||||
|
const signalDefs: Array<{
|
||||||
|
name: string;
|
||||||
|
identifier: string;
|
||||||
|
width: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
while (
|
||||||
|
(match = varRegex.exec(content)) !== null &&
|
||||||
|
signalDefs.length < maxSignals
|
||||||
|
) {
|
||||||
|
const width = parseInt(match[2]);
|
||||||
|
const identifier = match[3];
|
||||||
|
const name = match[4].trim();
|
||||||
|
signalDefs.push({ name, identifier, width });
|
||||||
|
}
|
||||||
|
|
||||||
|
const dumpvarsIndex = content.indexOf("$dumpvars");
|
||||||
|
if (dumpvarsIndex === -1) {
|
||||||
|
return signals;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataSection = content.substring(dumpvarsIndex);
|
||||||
|
|
||||||
|
for (const signalDef of signalDefs) {
|
||||||
|
const values: Array<{ time: number; value: string }> = [];
|
||||||
|
let currentTime = 0;
|
||||||
|
const lines = dataSection.split("\n");
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
|
if (trimmedLine.startsWith("#")) {
|
||||||
|
currentTime = parseInt(trimmedLine.substring(1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signalDef.width === 1) {
|
||||||
|
const singleBitMatch = trimmedLine.match(
|
||||||
|
new RegExp(`^([01xz])${signalDef.identifier}$`),
|
||||||
|
);
|
||||||
|
if (singleBitMatch) {
|
||||||
|
values.push({ time: currentTime, value: singleBitMatch[1] });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const multiBitMatch = trimmedLine.match(
|
||||||
|
new RegExp(`^b([01xz]+)\\s+${signalDef.identifier}$`),
|
||||||
|
);
|
||||||
|
if (multiBitMatch) {
|
||||||
|
values.push({ time: currentTime, value: multiBitMatch[1] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.length >= 50) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals.push({
|
||||||
|
name: signalDef.name,
|
||||||
|
identifier: signalDef.identifier,
|
||||||
|
width: signalDef.width,
|
||||||
|
values: values,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析 VCD 信号数据失败:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return signals;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user