469 lines
12 KiB
TypeScript
469 lines
12 KiB
TypeScript
import * as vscode from "vscode";
|
||
import { readFileContent } from "./readFiles";
|
||
import {
|
||
createFile,
|
||
createOrOverwriteFile,
|
||
deleteFile,
|
||
updateFile,
|
||
renameFile,
|
||
replaceFile,
|
||
} from "./createFiles";
|
||
|
||
/**
|
||
* 处理用户消息
|
||
*/
|
||
export async function handleUserMessage(
|
||
panel: vscode.WebviewPanel,
|
||
text: string
|
||
) {
|
||
console.log("收到用户消息:", text);
|
||
|
||
// 检查是否是文件操作命令
|
||
const fileOperation = parseFileOperation(text);
|
||
|
||
console.log("解析结果:", fileOperation);
|
||
|
||
if (fileOperation) {
|
||
console.log("执行文件操作:", fileOperation.type, fileOperation.filePath);
|
||
await handleFileOperation(panel, fileOperation);
|
||
return;
|
||
}
|
||
|
||
// 普通消息处理
|
||
console.log("作为普通消息处理");
|
||
const reply = getMockReply(text);
|
||
setTimeout(() => {
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: reply,
|
||
});
|
||
}, 500);
|
||
}
|
||
|
||
/**
|
||
* 解析文件操作命令
|
||
*/
|
||
function parseFileOperation(text: string): {
|
||
type: "create" | "delete" | "read" | "update" | "rename" | "replace";
|
||
filePath: string;
|
||
content?: string;
|
||
newPath?: string;
|
||
searchText?: string;
|
||
replaceText?: string;
|
||
} | null {
|
||
const lowerText = text.toLowerCase().trim();
|
||
|
||
// 匹配创建文件:创建一个 xxx.ts 文件
|
||
const createMatch = lowerText.match(/创建(?:一个)?(.+?\.\w+)(?:文件)?/);
|
||
if (createMatch) {
|
||
const filePath = createMatch[1].trim();
|
||
return {
|
||
type: "create",
|
||
filePath: filePath,
|
||
content: getDefaultContent(filePath),
|
||
};
|
||
}
|
||
|
||
// 匹配删除文件:删除 xxx.ts 文件
|
||
const deleteMatch = lowerText.match(/删除(.+?\.\w+)(?:文件)?/);
|
||
if (deleteMatch) {
|
||
const filePath = deleteMatch[1].trim();
|
||
return {
|
||
type: "delete",
|
||
filePath: filePath,
|
||
};
|
||
}
|
||
|
||
// 匹配重命名文件:将 xxx.ts 重命名为 yyy.ts 或 把 xxx.ts 改名为 yyy.ts(优先匹配,避免被修改匹配)
|
||
const renameMatch = lowerText.match(/(?:将|把)\s*(.+?\.\w+)\s*(?:重命名|改名)\s*(?:为|成)\s*(.+?\.\w+)/);
|
||
if (renameMatch) {
|
||
const oldPath = renameMatch[1].trim();
|
||
const newPath = renameMatch[2].trim();
|
||
return {
|
||
type: "rename",
|
||
filePath: oldPath,
|
||
newPath: newPath,
|
||
};
|
||
}
|
||
|
||
// 匹配替换内容:支持多种格式
|
||
// 格式1: 在 xxx.ts 中将 "aaa" 替换为 "bbb"
|
||
// 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb"
|
||
// 格式3: 将 xxx.ts 文件 'aaa' 替换为 'bbb'
|
||
const replaceMatch1 = lowerText.match(/在\s*(.+?\.\w+)\s*(?:文件)?中?\s*(?:将|把)\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/);
|
||
if (replaceMatch1) {
|
||
const filePath = replaceMatch1[1].trim();
|
||
const searchText = replaceMatch1[2].trim();
|
||
const replaceText = replaceMatch1[3].trim();
|
||
return {
|
||
type: "replace",
|
||
filePath: filePath,
|
||
searchText: searchText,
|
||
replaceText: replaceText,
|
||
};
|
||
}
|
||
|
||
// 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb"
|
||
const replaceMatch2 = lowerText.match(/(?:将|把)\s*(.+?\.\w+)\s*(?:文件)?\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/);
|
||
if (replaceMatch2) {
|
||
const filePath = replaceMatch2[1].trim();
|
||
const searchText = replaceMatch2[2].trim();
|
||
const replaceText = replaceMatch2[3].trim();
|
||
return {
|
||
type: "replace",
|
||
filePath: filePath,
|
||
searchText: searchText,
|
||
replaceText: replaceText,
|
||
};
|
||
}
|
||
|
||
// 匹配读取文件:读取 xxx.ts 文件 或 打开 xxx.ts
|
||
const readMatch = lowerText.match(/(?:读取|打开)\s*(.+?\.\w+)\s*(?:文件)?/);
|
||
if (readMatch) {
|
||
const filePath = readMatch[1].trim();
|
||
return {
|
||
type: "read",
|
||
filePath: filePath,
|
||
};
|
||
}
|
||
|
||
// 匹配修改文件:修改 xxx.ts 文件(放在最后,避免误匹配)
|
||
const updateMatch = lowerText.match(/修改\s*(.+?\.\w+)\s*(?:文件)?/);
|
||
if (updateMatch) {
|
||
const filePath = updateMatch[1].trim();
|
||
return {
|
||
type: "update",
|
||
filePath: filePath,
|
||
};
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 处理文件操作
|
||
*/
|
||
async function handleFileOperation(
|
||
panel: vscode.WebviewPanel,
|
||
operation: {
|
||
type: "create" | "delete" | "read" | "update" | "rename" | "replace";
|
||
filePath: string;
|
||
content?: string;
|
||
newPath?: string;
|
||
searchText?: string;
|
||
replaceText?: string;
|
||
}
|
||
) {
|
||
try {
|
||
switch (operation.type) {
|
||
case "create":
|
||
await createFile(operation.filePath, operation.content || "");
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: `✅ 文件创建成功: ${operation.filePath}`,
|
||
});
|
||
vscode.window.showInformationMessage(
|
||
`文件创建成功: ${operation.filePath}`
|
||
);
|
||
break;
|
||
|
||
case "delete":
|
||
await deleteFile(operation.filePath);
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: `✅ 文件删除成功: ${operation.filePath}`,
|
||
});
|
||
vscode.window.showInformationMessage(
|
||
`文件删除成功: ${operation.filePath}`
|
||
);
|
||
break;
|
||
|
||
case "read":
|
||
const content = await readFileContent(operation.filePath);
|
||
panel.webview.postMessage({
|
||
command: "fileContent",
|
||
content: content,
|
||
filePath: operation.filePath,
|
||
});
|
||
break;
|
||
|
||
case "update":
|
||
const currentContent = await readFileContent(operation.filePath);
|
||
panel.webview.postMessage({
|
||
command: "editFile",
|
||
content: currentContent,
|
||
filePath: operation.filePath,
|
||
});
|
||
break;
|
||
|
||
case "rename":
|
||
if (!operation.newPath) {
|
||
throw new Error("缺少新文件名");
|
||
}
|
||
await renameFile(operation.filePath, operation.newPath);
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: `✅ 文件重命名成功: ${operation.filePath} → ${operation.newPath}`,
|
||
});
|
||
vscode.window.showInformationMessage(
|
||
`文件重命名成功: ${operation.filePath} → ${operation.newPath}`
|
||
);
|
||
break;
|
||
|
||
case "replace":
|
||
if (!operation.searchText || !operation.replaceText) {
|
||
throw new Error("缺少替换内容");
|
||
}
|
||
await replaceFile(
|
||
operation.filePath,
|
||
operation.searchText,
|
||
operation.replaceText
|
||
);
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: `✅ 文件内容替换成功: ${operation.filePath}`,
|
||
});
|
||
vscode.window.showInformationMessage(
|
||
`文件内容替换成功: ${operation.filePath}`
|
||
);
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
const errorMsg = error instanceof Error ? error.message : "操作失败";
|
||
panel.webview.postMessage({
|
||
command: "receiveMessage",
|
||
text: `❌ ${errorMsg}`,
|
||
});
|
||
vscode.window.showErrorMessage(errorMsg);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据文件扩展名生成默认内容
|
||
*/
|
||
function getDefaultContent(filePath: string): string {
|
||
const ext = filePath.split(".").pop()?.toLowerCase();
|
||
|
||
switch (ext) {
|
||
case "ts":
|
||
return `// ${filePath}\n\nexport {};\n`;
|
||
case "js":
|
||
return `// ${filePath}\n\n`;
|
||
case "json":
|
||
return "{\n \n}\n";
|
||
case "md":
|
||
return `# ${filePath}\n\n`;
|
||
case "html":
|
||
return `<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Document</title>
|
||
</head>
|
||
<body>
|
||
|
||
</body>
|
||
</html>`;
|
||
case "css":
|
||
return `/* ${filePath} */\n\n`;
|
||
case "v":
|
||
case "sv":
|
||
return `// ${filePath}\n\nmodule ${
|
||
filePath.split(".")[0]
|
||
} (\n \n);\n\nendmodule\n`;
|
||
default:
|
||
return "";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理文件读取请求
|
||
*/
|
||
export async function handleReadFile(
|
||
panel: vscode.WebviewPanel,
|
||
filePath: string
|
||
) {
|
||
try {
|
||
const content = await readFileContent(filePath);
|
||
panel.webview.postMessage({
|
||
command: "fileContent",
|
||
content: content,
|
||
filePath: filePath,
|
||
});
|
||
} catch (error) {
|
||
panel.webview.postMessage({
|
||
command: "fileError",
|
||
error: error instanceof Error ? error.message : "读取文件失败",
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理文件创建请求
|
||
*/
|
||
export async function handleCreateFile(
|
||
panel: vscode.WebviewPanel,
|
||
filePath: string,
|
||
content: string,
|
||
overwrite: boolean = false //是否覆盖
|
||
) {
|
||
try {
|
||
if (overwrite) {
|
||
await createOrOverwriteFile(filePath, content);
|
||
} else {
|
||
await createFile(filePath, content);
|
||
}
|
||
|
||
panel.webview.postMessage({
|
||
command: "fileCreated",
|
||
filePath: filePath,
|
||
message: " 文件创建成功",
|
||
});
|
||
vscode.window.showInformationMessage(`文件创建成功: ${filePath}`);
|
||
} catch (error) {
|
||
panel.webview.postMessage({
|
||
command: "fileCreateError",
|
||
error: error instanceof Error ? error.message : "创建文件失败",
|
||
});
|
||
vscode.window.showErrorMessage(
|
||
`创建文件失败: ${error instanceof Error ? error.message : "未知错误"}`
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理文件更新请求
|
||
*/
|
||
export async function handleUpdateFile(
|
||
panel: vscode.WebviewPanel,
|
||
filePath: string,
|
||
content: string
|
||
) {
|
||
try {
|
||
await updateFile(filePath, content);
|
||
panel.webview.postMessage({
|
||
command: "fileUpdated",
|
||
filePath: filePath,
|
||
message: " 文件更新成功",
|
||
});
|
||
vscode.window.showInformationMessage(`文件更新成功: ${filePath}`);
|
||
} catch (error) {
|
||
panel.webview.postMessage({
|
||
command: "fileUpdateError",
|
||
error: error instanceof Error ? error.message : "更新文件失败",
|
||
});
|
||
vscode.window.showErrorMessage(
|
||
`更新文件失败: ${error instanceof Error ? error.message : "未知错误"}`
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理文件重命名请求
|
||
*/
|
||
export async function handleRenameFile(
|
||
panel: vscode.WebviewPanel,
|
||
oldPath: string,
|
||
newPath: string
|
||
) {
|
||
try {
|
||
await renameFile(oldPath, newPath);
|
||
panel.webview.postMessage({
|
||
command: "fileRenamed",
|
||
oldPath: oldPath,
|
||
newPath: newPath,
|
||
message: "文件重命名成功",
|
||
});
|
||
vscode.window.showInformationMessage(
|
||
`文件重命名成功: ${oldPath} → ${newPath}`
|
||
);
|
||
} catch (error) {
|
||
panel.webview.postMessage({
|
||
command: "fileRenameError",
|
||
error: error instanceof Error ? error.message : "重命名文件失败",
|
||
});
|
||
vscode.window.showErrorMessage(
|
||
`重命名文件失败: ${error instanceof Error ? error.message : "未知错误"}`
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理文件内容替换请求
|
||
*/
|
||
export async function handleReplaceInFile(
|
||
panel: vscode.WebviewPanel,
|
||
filePath: string,
|
||
searchText: string,
|
||
replaceText: string
|
||
) {
|
||
try {
|
||
await replaceFile(filePath, searchText, replaceText);
|
||
panel.webview.postMessage({
|
||
command: "fileReplaced",
|
||
filePath: filePath,
|
||
message: "文件内容替换成功",
|
||
});
|
||
vscode.window.showInformationMessage(`文件内容替换成功: ${filePath}`);
|
||
} catch (error) {
|
||
panel.webview.postMessage({
|
||
command: "fileReplaceError",
|
||
error: error instanceof Error ? error.message : "替换文件内容失败",
|
||
});
|
||
vscode.window.showErrorMessage(
|
||
`替换文件内容失败: ${error instanceof Error ? error.message : "未知错误"}`
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取模拟回复
|
||
*/
|
||
function getMockReply(question: string): string {
|
||
const replies = [
|
||
`已收到您的问题:"${question}"
|
||
|
||
这是一个演示版本,实际需要连接AI服务。
|
||
|
||
示例回复:这是一个计数器模板:
|
||
\`\`\`verilog
|
||
module counter (
|
||
input clk,
|
||
input rst_n,
|
||
output reg [3:0] count
|
||
);
|
||
always @(posedge clk or negedge rst_n) begin
|
||
if (!rst_n) count <= 0;
|
||
else count <= count + 1;
|
||
end
|
||
endmodule
|
||
\`\`\``,
|
||
|
||
`感谢提问!关于"${question}",在真实版本中我会:
|
||
1. 分析您的代码上下文
|
||
2. 提供优化建议
|
||
3. 生成完整代码
|
||
4. 解释设计原理
|
||
|
||
当前是演示版,请点击侧边栏按钮快速生成代码。`,
|
||
];
|
||
|
||
return replies[Math.floor(Math.random() * replies.length)];
|
||
}
|
||
|
||
/**
|
||
* 将代码插入到编辑器
|
||
*/
|
||
export function insertCodeToEditor(code: string) {
|
||
const editor = vscode.window.activeTextEditor;
|
||
if (editor) {
|
||
editor.edit((editBuilder) => {
|
||
editBuilder.insert(editor.selection.active, code);
|
||
});
|
||
vscode.window.showInformationMessage("代码已插入");
|
||
} else {
|
||
vscode.window.showWarningMessage("请先打开一个编辑器");
|
||
}
|
||
}
|