feat:实现对文件的创建和删除
- 还涵盖了对已存在的文件进行覆盖 - 对不存在的文件创建 - 还可以创建目录 - 可以一次创建多个文件
This commit is contained in:
196
src/utils/createFiles.ts
Normal file
196
src/utils/createFiles.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import * as vscode from "vscode";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* 创建文件(本来不存在的情况)
|
||||
*/
|
||||
export async function createFile(
|
||||
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 dirPath = path.dirname(absolutePath);
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
fs.writeFileSync(absolutePath, content, "utf-8");
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件(如果存在则覆盖)
|
||||
*/
|
||||
export async function createOrOverwriteFile(
|
||||
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("没有打开的工作区,无法创建相对路径的文件");
|
||||
}
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
const dirPath = path.dirname(absolutePath);
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 创建或覆盖文件
|
||||
fs.writeFileSync(absolutePath, content, "utf-8");
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
*/
|
||||
export async function createDirectory(dirPath: string): Promise<void> {
|
||||
try {
|
||||
// 如果是相对路径,转换为绝对路径
|
||||
let absolutePath = dirPath;
|
||||
if (!path.isAbsolute(dirPath)) {
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
if (workspaceFolders && workspaceFolders.length > 0) {
|
||||
absolutePath = path.join(workspaceFolders[0].uri.fsPath, dirPath);
|
||||
} else {
|
||||
throw new Error("没有打开的工作区,无法创建相对路径的目录");
|
||||
}
|
||||
}
|
||||
|
||||
// 检测创建目录是否存在
|
||||
if (fs.existsSync(absolutePath)) {
|
||||
const state = fs.statSync(absolutePath);
|
||||
if (state.isDirectory()) {
|
||||
throw new Error(`目录已存在: ${absolutePath}`);
|
||||
} else {
|
||||
throw new Error(`路径已存在且不是目录: ${absolutePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建目录
|
||||
fs.mkdirSync(absolutePath, { recursive: true });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建文件
|
||||
*/
|
||||
export async function createMultipleFiles(
|
||||
files: { path: string; content: string }[]
|
||||
): Promise<{ path: string; success: boolean; error?: string }[]> {
|
||||
const results = [];
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
await createFile(file.path, file.content);
|
||||
results.push({ path: file.path, success: true });
|
||||
} catch (error) {
|
||||
results.push({
|
||||
path: file.path,
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "未知错误",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
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 stats = fs.statSync(absolutePath);
|
||||
if (!stats.isFile()) {
|
||||
throw new Error(`路径不是文件: ${absolutePath}`);
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
fs.unlinkSync(absolutePath);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,21 @@
|
||||
import * as vscode from "vscode";
|
||||
import { readFileContent } from "./readFiles";
|
||||
import { createFile, createOrOverwriteFile, deleteFile } from "./createFiles";
|
||||
|
||||
/**
|
||||
* 处理用户消息
|
||||
*/
|
||||
export function handleUserMessage(panel: vscode.WebviewPanel, text: string) {
|
||||
// 模拟AI回复
|
||||
const reply = getMockReply(text);
|
||||
export async function handleUserMessage(panel: vscode.WebviewPanel, text: string) {
|
||||
// 检查是否是文件操作命令
|
||||
const fileOperation = parseFileOperation(text);
|
||||
|
||||
// 延迟回复,模拟AI思考
|
||||
if (fileOperation) {
|
||||
await handleFileOperation(panel, fileOperation);
|
||||
return;
|
||||
}
|
||||
|
||||
// 普通消息处理
|
||||
const reply = getMockReply(text);
|
||||
setTimeout(() => {
|
||||
panel.webview.postMessage({
|
||||
command: "receiveMessage",
|
||||
@ -17,6 +24,133 @@ export function handleUserMessage(panel: vscode.WebviewPanel, text: string) {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析文件操作命令
|
||||
*/
|
||||
function parseFileOperation(text: string): {
|
||||
type: 'create' | 'delete' | 'read';
|
||||
filePath: string;
|
||||
content?: 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 文件 或 打开 xxx.ts
|
||||
const readMatch = lowerText.match(/(?:读取|打开)(.+?\.\w+)(?:文件)?/);
|
||||
if (readMatch) {
|
||||
const filePath = readMatch[1].trim();
|
||||
return {
|
||||
type: 'read',
|
||||
filePath: filePath
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件操作
|
||||
*/
|
||||
async function handleFileOperation(
|
||||
panel: vscode.WebviewPanel,
|
||||
operation: { type: 'create' | 'delete' | 'read'; filePath: string; content?: 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;
|
||||
}
|
||||
} 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 '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件读取请求
|
||||
*/
|
||||
@ -39,6 +173,39 @@ export async function handleReadFile(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件创建请求
|
||||
*/
|
||||
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 : "未知错误"}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模拟回复
|
||||
*/
|
||||
|
||||
@ -1,4 +1,72 @@
|
||||
import * as vscode from "vscode";
|
||||
import { getWebviewContent } from "./webviewContent";
|
||||
import {
|
||||
handleUserMessage,
|
||||
insertCodeToEditor,
|
||||
handleReadFile,
|
||||
handleCreateFile,
|
||||
} from "../utils/messageHandler";
|
||||
|
||||
/**
|
||||
* 创建并显示IC 侧边栏视图
|
||||
*/
|
||||
export function showICHelperPanel(content: vscode.ExtensionContext) {
|
||||
// 创建WebView面板
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
"icCoder", // 面板ID
|
||||
"IC Coder", // 面板标题
|
||||
vscode.ViewColumn.Beside, // 显示在旁边
|
||||
{
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [vscode.Uri.joinPath(content.extensionUri, "media")],
|
||||
}
|
||||
);
|
||||
|
||||
// 设置标签页图标
|
||||
panel.iconPath = vscode.Uri.joinPath(
|
||||
content.extensionUri,
|
||||
"media",
|
||||
"图案(方底).png"
|
||||
);
|
||||
|
||||
// 获取页面内图标URI
|
||||
const iconUri = panel.webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(content.extensionUri, "media", "图案(方底).png")
|
||||
);
|
||||
// 设置HTML内容
|
||||
panel.webview.html = getWebviewContent(iconUri.toString());
|
||||
|
||||
// 处理消息
|
||||
panel.webview.onDidReceiveMessage(
|
||||
(message) => {
|
||||
switch (message.command) {
|
||||
case "sendMessage":
|
||||
handleUserMessage(panel, message.text);
|
||||
break;
|
||||
case "readFile":
|
||||
handleReadFile(panel, message.filePath);
|
||||
break;
|
||||
case "insertCode":
|
||||
insertCodeToEditor(message.code);
|
||||
break;
|
||||
case "createFile":
|
||||
handleCreateFile(
|
||||
panel,
|
||||
message.filePath,
|
||||
message.content,
|
||||
message.overwrite
|
||||
);
|
||||
break;
|
||||
case "showInfo":
|
||||
vscode.window.showInformationMessage(message.text);
|
||||
break;
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
content.subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 侧边栏视图提供者
|
||||
|
||||
Reference in New Issue
Block a user