feat:对本地文件进行修改
- 对某一行进行修改 - 将文件中的某些词进行替换 - 将文件重命名
This commit is contained in:
@ -1,6 +1,13 @@
|
|||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
import { getWebviewContent } from "../views/webviewContent";
|
import { getWebviewContent } from "../views/webviewContent";
|
||||||
import { handleUserMessage, insertCodeToEditor, handleReadFile } from "../utils/messageHandler";
|
import {
|
||||||
|
handleUserMessage,
|
||||||
|
insertCodeToEditor,
|
||||||
|
handleReadFile,
|
||||||
|
handleUpdateFile,
|
||||||
|
handleRenameFile,
|
||||||
|
handleReplaceInFile
|
||||||
|
} from "../utils/messageHandler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建并显示 IC 助手面板
|
* 创建并显示 IC 助手面板
|
||||||
@ -39,6 +46,15 @@ export function showICHelperPanel(context: vscode.ExtensionContext) {
|
|||||||
case "readFile":
|
case "readFile":
|
||||||
handleReadFile(panel, message.filePath);
|
handleReadFile(panel, message.filePath);
|
||||||
break;
|
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":
|
case "insertCode":
|
||||||
insertCodeToEditor(message.code);
|
insertCodeToEditor(message.code);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -132,37 +132,6 @@ export async function createMultipleFiles(
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 追加文件内容
|
|
||||||
*/
|
|
||||||
export async function appendToFile(
|
|
||||||
filePath: string,
|
|
||||||
content: string
|
|
||||||
): Promise<void> {
|
|
||||||
try {
|
|
||||||
// 如果是相对路径,转换为绝对路径
|
|
||||||
let absolutePath = filePath;
|
|
||||||
if (!path.isAbsolute(filePath)) {
|
|
||||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
||||||
if (workspaceFolders && workspaceFolders.length > 0) {
|
|
||||||
absolutePath = path.join(workspaceFolders[0].uri.fsPath, filePath);
|
|
||||||
} else {
|
|
||||||
throw new Error("没有打开的工作区,无法追加相对路径的文件");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查文件是否存在
|
|
||||||
if (!fs.existsSync(absolutePath)) {
|
|
||||||
throw new Error(`文件不存在: ${absolutePath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 追加内容
|
|
||||||
fs.appendFileSync(absolutePath, content, "utf-8");
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除文件
|
// 删除文件
|
||||||
export async function deleteFile(filePath: string): Promise<void> {
|
export async function deleteFile(filePath: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@ -194,3 +163,207 @@ export async function deleteFile(filePath: string): Promise<void> {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改文件内容(完全替换)
|
||||||
|
export async function updateFile(
|
||||||
|
filePath: string,
|
||||||
|
content: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 如果是相对路径,转换为绝对路径
|
||||||
|
let absolutePath = filePath;
|
||||||
|
if (!path.isAbsolute(filePath)) {
|
||||||
|
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||||
|
if (workspaceFolders && workspaceFolders.length > 0) {
|
||||||
|
absolutePath = path.join(workspaceFolders[0].uri.fsPath, filePath);
|
||||||
|
} else {
|
||||||
|
throw new Error("没有打开的工作区,无法修改相对路径的文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!fs.existsSync(absolutePath)) {
|
||||||
|
throw new Error(`文件不存在: ${absolutePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否是文件内容
|
||||||
|
const state = fs.statSync(absolutePath);
|
||||||
|
if (!state.isFile()) {
|
||||||
|
throw new Error(`路径不是文件: ${absolutePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改文件内容
|
||||||
|
fs.writeFileSync(absolutePath, content, "utf-8");
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加文件内容
|
||||||
|
*/
|
||||||
|
export async function appendToFile(
|
||||||
|
filePath: string,
|
||||||
|
content: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 如果是相对路径,转换为绝对路径
|
||||||
|
let absolutePath = filePath;
|
||||||
|
if (!path.isAbsolute(filePath)) {
|
||||||
|
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||||
|
if (workspaceFolders && workspaceFolders.length > 0) {
|
||||||
|
absolutePath = path.join(workspaceFolders[0].uri.fsPath, filePath);
|
||||||
|
} else {
|
||||||
|
throw new Error("没有打开的工作区,无法追加相对路径的文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!fs.existsSync(absolutePath)) {
|
||||||
|
throw new Error(`文件不存在: ${absolutePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加内容
|
||||||
|
fs.appendFileSync(absolutePath, content, "utf-8");
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 部分修改文件内容(查找替换)
|
||||||
|
export async function replaceFile(
|
||||||
|
filePath: string,
|
||||||
|
searchValue: string | RegExp,
|
||||||
|
replaceValue: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 如果是相对路径,转换为绝对路径
|
||||||
|
let absolutePath = filePath;
|
||||||
|
if (!path.isAbsolute(filePath)) {
|
||||||
|
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||||
|
if (workspaceFolders && workspaceFolders.length > 0) {
|
||||||
|
absolutePath = path.join(workspaceFolders[0].uri.fsPath, filePath);
|
||||||
|
} else {
|
||||||
|
throw new Error("没有打开的工作区,无法修改相对路径的文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!fs.existsSync(absolutePath)) {
|
||||||
|
throw new Error(`文件不存在: ${absolutePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
const fileContent = fs.readFileSync(absolutePath, "utf-8");
|
||||||
|
|
||||||
|
// 转义特殊字符,将字符串作为字面量处理
|
||||||
|
const escapeRegExp = (str: string) => {
|
||||||
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 替换内容 - 如果是字符串,先转义特殊字符
|
||||||
|
let newContent: string;
|
||||||
|
if (typeof searchValue === 'string') {
|
||||||
|
const escapedSearch = escapeRegExp(searchValue);
|
||||||
|
newContent = fileContent.replace(new RegExp(escapedSearch, "g"), replaceValue);
|
||||||
|
} else {
|
||||||
|
newContent = fileContent.replace(searchValue, replaceValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有内容被替换
|
||||||
|
if (fileContent === newContent) {
|
||||||
|
throw new Error(`未找到要替换的内容: ${searchValue}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写回文件
|
||||||
|
fs.writeFileSync(absolutePath, newContent, "utf-8");
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在指定位置插入内容
|
||||||
|
export async function insertAtLine(
|
||||||
|
filePath: string,
|
||||||
|
lineNumber: number,
|
||||||
|
content: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 如果是相对路径,转换为绝对路径
|
||||||
|
let absolutePath = filePath;
|
||||||
|
if (!path.isAbsolute(filePath)) {
|
||||||
|
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||||
|
if (workspaceFolders && workspaceFolders.length > 0) {
|
||||||
|
absolutePath = path.join(workspaceFolders[0].uri.fsPath, filePath);
|
||||||
|
} else {
|
||||||
|
throw new Error("没有打开的工作区,无法修改相对路径的文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!fs.existsSync(absolutePath)) {
|
||||||
|
throw new Error(`文件不存在: ${absolutePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
const fileContent = fs.readFileSync(absolutePath, "utf-8");
|
||||||
|
const lines = fileContent.split("\n");
|
||||||
|
|
||||||
|
// 插入内容
|
||||||
|
lines.splice(lineNumber, 0, content);
|
||||||
|
|
||||||
|
// 写回文件
|
||||||
|
fs.writeFileSync(absolutePath, lines.join("\n"), "utf-8");
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重命名文件
|
||||||
|
*/
|
||||||
|
export async function renameFile(
|
||||||
|
oldPath: string,
|
||||||
|
newPath: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 如果是相对路径,转换为绝对路径
|
||||||
|
let absoluteOldPath = oldPath;
|
||||||
|
let absoluteNewPath = newPath;
|
||||||
|
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||||
|
if (workspaceFolders && workspaceFolders.length > 0) {
|
||||||
|
const workspaceRoot = workspaceFolders[0].uri.fsPath;
|
||||||
|
|
||||||
|
if (!path.isAbsolute(oldPath)) {
|
||||||
|
absoluteOldPath = path.join(workspaceRoot, oldPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path.isAbsolute(newPath)) {
|
||||||
|
absoluteNewPath = path.join(workspaceRoot, newPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("没有打开的工作区,无法重命名相对路径的文件");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查原文件是否存在
|
||||||
|
if (!fs.existsSync(absoluteOldPath)) {
|
||||||
|
throw new Error(`文件不存在: ${absoluteOldPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查新文件名是否已存在
|
||||||
|
if (fs.existsSync(absoluteNewPath)) {
|
||||||
|
throw new Error(`目标文件已存在: ${absoluteNewPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保目标目录存在
|
||||||
|
const newDir = path.dirname(absoluteNewPath);
|
||||||
|
if (!fs.existsSync(newDir)) {
|
||||||
|
fs.mkdirSync(newDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重命名文件
|
||||||
|
fs.renameSync(absoluteOldPath, absoluteNewPath);
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,20 +1,36 @@
|
|||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
import { readFileContent } from "./readFiles";
|
import { readFileContent } from "./readFiles";
|
||||||
import { createFile, createOrOverwriteFile, deleteFile } from "./createFiles";
|
import {
|
||||||
|
createFile,
|
||||||
|
createOrOverwriteFile,
|
||||||
|
deleteFile,
|
||||||
|
updateFile,
|
||||||
|
renameFile,
|
||||||
|
replaceFile,
|
||||||
|
} from "./createFiles";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理用户消息
|
* 处理用户消息
|
||||||
*/
|
*/
|
||||||
export async function handleUserMessage(panel: vscode.WebviewPanel, text: string) {
|
export async function handleUserMessage(
|
||||||
|
panel: vscode.WebviewPanel,
|
||||||
|
text: string
|
||||||
|
) {
|
||||||
|
console.log("收到用户消息:", text);
|
||||||
|
|
||||||
// 检查是否是文件操作命令
|
// 检查是否是文件操作命令
|
||||||
const fileOperation = parseFileOperation(text);
|
const fileOperation = parseFileOperation(text);
|
||||||
|
|
||||||
|
console.log("解析结果:", fileOperation);
|
||||||
|
|
||||||
if (fileOperation) {
|
if (fileOperation) {
|
||||||
|
console.log("执行文件操作:", fileOperation.type, fileOperation.filePath);
|
||||||
await handleFileOperation(panel, fileOperation);
|
await handleFileOperation(panel, fileOperation);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 普通消息处理
|
// 普通消息处理
|
||||||
|
console.log("作为普通消息处理");
|
||||||
const reply = getMockReply(text);
|
const reply = getMockReply(text);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
panel.webview.postMessage({
|
panel.webview.postMessage({
|
||||||
@ -28,9 +44,12 @@ export async function handleUserMessage(panel: vscode.WebviewPanel, text: string
|
|||||||
* 解析文件操作命令
|
* 解析文件操作命令
|
||||||
*/
|
*/
|
||||||
function parseFileOperation(text: string): {
|
function parseFileOperation(text: string): {
|
||||||
type: 'create' | 'delete' | 'read';
|
type: "create" | "delete" | "read" | "update" | "rename" | "replace";
|
||||||
filePath: string;
|
filePath: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
|
newPath?: string;
|
||||||
|
searchText?: string;
|
||||||
|
replaceText?: string;
|
||||||
} | null {
|
} | null {
|
||||||
const lowerText = text.toLowerCase().trim();
|
const lowerText = text.toLowerCase().trim();
|
||||||
|
|
||||||
@ -39,9 +58,9 @@ function parseFileOperation(text: string): {
|
|||||||
if (createMatch) {
|
if (createMatch) {
|
||||||
const filePath = createMatch[1].trim();
|
const filePath = createMatch[1].trim();
|
||||||
return {
|
return {
|
||||||
type: 'create',
|
type: "create",
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
content: getDefaultContent(filePath)
|
content: getDefaultContent(filePath),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,18 +69,71 @@ function parseFileOperation(text: string): {
|
|||||||
if (deleteMatch) {
|
if (deleteMatch) {
|
||||||
const filePath = deleteMatch[1].trim();
|
const filePath = deleteMatch[1].trim();
|
||||||
return {
|
return {
|
||||||
type: 'delete',
|
type: "delete",
|
||||||
filePath: filePath
|
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
|
// 匹配读取文件:读取 xxx.ts 文件 或 打开 xxx.ts
|
||||||
const readMatch = lowerText.match(/(?:读取|打开)(.+?\.\w+)(?:文件)?/);
|
const readMatch = lowerText.match(/(?:读取|打开)\s*(.+?\.\w+)\s*(?:文件)?/);
|
||||||
if (readMatch) {
|
if (readMatch) {
|
||||||
const filePath = readMatch[1].trim();
|
const filePath = readMatch[1].trim();
|
||||||
return {
|
return {
|
||||||
type: 'read',
|
type: "read",
|
||||||
filePath: filePath
|
filePath: filePath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配修改文件:修改 xxx.ts 文件(放在最后,避免误匹配)
|
||||||
|
const updateMatch = lowerText.match(/修改\s*(.+?\.\w+)\s*(?:文件)?/);
|
||||||
|
if (updateMatch) {
|
||||||
|
const filePath = updateMatch[1].trim();
|
||||||
|
return {
|
||||||
|
type: "update",
|
||||||
|
filePath: filePath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,29 +145,40 @@ function parseFileOperation(text: string): {
|
|||||||
*/
|
*/
|
||||||
async function handleFileOperation(
|
async function handleFileOperation(
|
||||||
panel: vscode.WebviewPanel,
|
panel: vscode.WebviewPanel,
|
||||||
operation: { type: 'create' | 'delete' | 'read'; filePath: string; content?: string }
|
operation: {
|
||||||
|
type: "create" | "delete" | "read" | "update" | "rename" | "replace";
|
||||||
|
filePath: string;
|
||||||
|
content?: string;
|
||||||
|
newPath?: string;
|
||||||
|
searchText?: string;
|
||||||
|
replaceText?: string;
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
switch (operation.type) {
|
switch (operation.type) {
|
||||||
case 'create':
|
case "create":
|
||||||
await createFile(operation.filePath, operation.content || '');
|
await createFile(operation.filePath, operation.content || "");
|
||||||
panel.webview.postMessage({
|
panel.webview.postMessage({
|
||||||
command: "receiveMessage",
|
command: "receiveMessage",
|
||||||
text: `✅ 文件创建成功: ${operation.filePath}`,
|
text: `✅ 文件创建成功: ${operation.filePath}`,
|
||||||
});
|
});
|
||||||
vscode.window.showInformationMessage(`文件创建成功: ${operation.filePath}`);
|
vscode.window.showInformationMessage(
|
||||||
|
`文件创建成功: ${operation.filePath}`
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case "delete":
|
||||||
await deleteFile(operation.filePath);
|
await deleteFile(operation.filePath);
|
||||||
panel.webview.postMessage({
|
panel.webview.postMessage({
|
||||||
command: "receiveMessage",
|
command: "receiveMessage",
|
||||||
text: `✅ 文件删除成功: ${operation.filePath}`,
|
text: `✅ 文件删除成功: ${operation.filePath}`,
|
||||||
});
|
});
|
||||||
vscode.window.showInformationMessage(`文件删除成功: ${operation.filePath}`);
|
vscode.window.showInformationMessage(
|
||||||
|
`文件删除成功: ${operation.filePath}`
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'read':
|
case "read":
|
||||||
const content = await readFileContent(operation.filePath);
|
const content = await readFileContent(operation.filePath);
|
||||||
panel.webview.postMessage({
|
panel.webview.postMessage({
|
||||||
command: "fileContent",
|
command: "fileContent",
|
||||||
@ -103,6 +186,47 @@ async function handleFileOperation(
|
|||||||
filePath: operation.filePath,
|
filePath: operation.filePath,
|
||||||
});
|
});
|
||||||
break;
|
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) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : "操作失败";
|
const errorMsg = error instanceof Error ? error.message : "操作失败";
|
||||||
@ -118,18 +242,18 @@ async function handleFileOperation(
|
|||||||
* 根据文件扩展名生成默认内容
|
* 根据文件扩展名生成默认内容
|
||||||
*/
|
*/
|
||||||
function getDefaultContent(filePath: string): string {
|
function getDefaultContent(filePath: string): string {
|
||||||
const ext = filePath.split('.').pop()?.toLowerCase();
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
||||||
|
|
||||||
switch (ext) {
|
switch (ext) {
|
||||||
case 'ts':
|
case "ts":
|
||||||
return `// ${filePath}\n\nexport {};\n`;
|
return `// ${filePath}\n\nexport {};\n`;
|
||||||
case 'js':
|
case "js":
|
||||||
return `// ${filePath}\n\n`;
|
return `// ${filePath}\n\n`;
|
||||||
case 'json':
|
case "json":
|
||||||
return '{\n \n}\n';
|
return "{\n \n}\n";
|
||||||
case 'md':
|
case "md":
|
||||||
return `# ${filePath}\n\n`;
|
return `# ${filePath}\n\n`;
|
||||||
case 'html':
|
case "html":
|
||||||
return `<!DOCTYPE html>
|
return `<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
@ -141,13 +265,15 @@ function getDefaultContent(filePath: string): string {
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
case 'css':
|
case "css":
|
||||||
return `/* ${filePath} */\n\n`;
|
return `/* ${filePath} */\n\n`;
|
||||||
case 'v':
|
case "v":
|
||||||
case 'sv':
|
case "sv":
|
||||||
return `// ${filePath}\n\nmodule ${filePath.split('.')[0]} (\n \n);\n\nendmodule\n`;
|
return `// ${filePath}\n\nmodule ${
|
||||||
|
filePath.split(".")[0]
|
||||||
|
} (\n \n);\n\nendmodule\n`;
|
||||||
default:
|
default:
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +332,91 @@ export async function handleCreateFile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理文件更新请求
|
||||||
|
*/
|
||||||
|
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 : "未知错误"}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取模拟回复
|
* 获取模拟回复
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -145,6 +145,38 @@ export function getWebviewContent(iconUri?: string): string {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
.file-editor-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.file-editor-section.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.file-editor-section h3 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
color: var(--vscode-button-background);
|
||||||
|
}
|
||||||
|
.file-editor-textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.editor-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -172,6 +204,15 @@ export function getWebviewContent(iconUri?: string): string {
|
|||||||
<div id="errorMessage" style="display: none;"></div>
|
<div id="errorMessage" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="fileEditorSection" class="file-editor-section">
|
||||||
|
<h3>✏️ 编辑文件: <span id="editingFileName"></span></h3>
|
||||||
|
<textarea id="fileEditorTextarea" class="file-editor-textarea"></textarea>
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button onclick="saveFile()">保存修改</button>
|
||||||
|
<button onclick="cancelEdit()" style="background: var(--vscode-button-secondaryBackground); color: var(--vscode-button-secondaryForeground);">取消</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
<div id="messages" class="messages">
|
<div id="messages" class="messages">
|
||||||
<div class="message bot-message">
|
<div class="message bot-message">
|
||||||
@ -204,6 +245,11 @@ export function getWebviewContent(iconUri?: string): string {
|
|||||||
const filePathInput = document.getElementById('filePathInput');
|
const filePathInput = document.getElementById('filePathInput');
|
||||||
const fileContentEl = document.getElementById('fileContent');
|
const fileContentEl = document.getElementById('fileContent');
|
||||||
const errorMessageEl = document.getElementById('errorMessage');
|
const errorMessageEl = document.getElementById('errorMessage');
|
||||||
|
const fileEditorSection = document.getElementById('fileEditorSection');
|
||||||
|
const fileEditorTextarea = document.getElementById('fileEditorTextarea');
|
||||||
|
const editingFileName = document.getElementById('editingFileName');
|
||||||
|
|
||||||
|
let currentEditingFile = null;
|
||||||
|
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
const text = messageInput.value.trim();
|
const text = messageInput.value.trim();
|
||||||
@ -268,6 +314,42 @@ export function getWebviewContent(iconUri?: string): string {
|
|||||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openFileEditor(filePath, content) {
|
||||||
|
currentEditingFile = filePath;
|
||||||
|
editingFileName.textContent = filePath;
|
||||||
|
fileEditorTextarea.value = content;
|
||||||
|
fileEditorSection.classList.add('active');
|
||||||
|
|
||||||
|
// 滚动到编辑器位置
|
||||||
|
fileEditorSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
|
||||||
|
// 在消息区域显示提示
|
||||||
|
addMessage(\`正在编辑文件: \${filePath}\`, 'bot');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFile() {
|
||||||
|
if (!currentEditingFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = fileEditorTextarea.value;
|
||||||
|
vscode.postMessage({
|
||||||
|
command: 'updateFile',
|
||||||
|
filePath: currentEditingFile,
|
||||||
|
content: content
|
||||||
|
});
|
||||||
|
|
||||||
|
addMessage(\`正在保存文件: \${currentEditingFile}\`, 'user');
|
||||||
|
cancelEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEdit() {
|
||||||
|
currentEditingFile = null;
|
||||||
|
fileEditorSection.classList.remove('active');
|
||||||
|
fileEditorTextarea.value = '';
|
||||||
|
editingFileName.textContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', event => {
|
||||||
const message = event.data;
|
const message = event.data;
|
||||||
|
|
||||||
@ -281,6 +363,15 @@ export function getWebviewContent(iconUri?: string): string {
|
|||||||
case 'fileError':
|
case 'fileError':
|
||||||
showError(message.error);
|
showError(message.error);
|
||||||
break;
|
break;
|
||||||
|
case 'editFile':
|
||||||
|
openFileEditor(message.filePath, message.content);
|
||||||
|
break;
|
||||||
|
case 'fileUpdated':
|
||||||
|
addMessage(\`✅ \${message.message}: \${message.filePath}\`, 'bot');
|
||||||
|
break;
|
||||||
|
case 'fileUpdateError':
|
||||||
|
addMessage(\`❌ \${message.error}\`, 'bot');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user