feat:将调用node.js的fs模块改为vscode官方的API

- 这样可以避免用户本地没有node环境导致插件无法运行的原因
This commit is contained in:
Roe-xin
2025-12-15 17:29:15 +08:00
parent c77187eec1
commit e541b85005
3 changed files with 197 additions and 120 deletions

View File

@ -1,5 +1,4 @@
import * as vscode from "vscode"; import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path"; import * as path from "path";
/** /**
@ -21,21 +20,31 @@ export async function createFile(
} }
} }
const fileUri = vscode.Uri.file(absolutePath);
// 检测文件是否已存在 // 检测文件是否已存在
if (fs.existsSync(absolutePath)) { try {
await vscode.workspace.fs.stat(fileUri);
throw new Error(`文件已存在: ${absolutePath}`); throw new Error(`文件已存在: ${absolutePath}`);
} catch (error: any) {
// 如果文件不存在,继续创建
if (error.code !== 'FileNotFound') {
throw error;
}
} }
// 确保目录存在 // 确保目录存在
const dirPath = path.dirname(absolutePath); const dirPath = path.dirname(absolutePath);
if (!fs.existsSync(dirPath)) { const dirUri = vscode.Uri.file(dirPath);
fs.mkdirSync(dirPath, { try {
recursive: true, await vscode.workspace.fs.stat(dirUri);
}); } catch {
await vscode.workspace.fs.createDirectory(dirUri);
} }
// 创建文件 // 创建文件
fs.writeFileSync(absolutePath, content, "utf-8"); const contentBytes = Buffer.from(content, "utf-8");
await vscode.workspace.fs.writeFile(fileUri, contentBytes);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -62,14 +71,17 @@ export async function createOrOverwriteFile(
// 确保目录存在 // 确保目录存在
const dirPath = path.dirname(absolutePath); const dirPath = path.dirname(absolutePath);
if (!fs.existsSync(dirPath)) { const dirUri = vscode.Uri.file(dirPath);
fs.mkdirSync(dirPath, { try {
recursive: true, await vscode.workspace.fs.stat(dirUri);
}); } catch {
await vscode.workspace.fs.createDirectory(dirUri);
} }
// 创建或覆盖文件 // 创建或覆盖文件
fs.writeFileSync(absolutePath, content, "utf-8"); const fileUri = vscode.Uri.file(absolutePath);
const contentBytes = Buffer.from(content, "utf-8");
await vscode.workspace.fs.writeFile(fileUri, contentBytes);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -91,18 +103,25 @@ export async function createDirectory(dirPath: string): Promise<void> {
} }
} }
const dirUri = vscode.Uri.file(absolutePath);
// 检测创建目录是否存在 // 检测创建目录是否存在
if (fs.existsSync(absolutePath)) { try {
const state = fs.statSync(absolutePath); const stat = await vscode.workspace.fs.stat(dirUri);
if (state.isDirectory()) { if (stat.type === vscode.FileType.Directory) {
throw new Error(`目录已存在: ${absolutePath}`); throw new Error(`目录已存在: ${absolutePath}`);
} else { } else {
throw new Error(`路径已存在且不是目录: ${absolutePath}`); throw new Error(`路径已存在且不是目录: ${absolutePath}`);
} }
} catch (error: any) {
// 如果目录不存在,继续创建
if (error.code !== 'FileNotFound') {
throw error;
}
} }
// 创建目录 // 创建目录
fs.mkdirSync(absolutePath, { recursive: true }); await vscode.workspace.fs.createDirectory(dirUri);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -146,19 +165,20 @@ export async function deleteFile(filePath: string): Promise<void> {
} }
} }
const fileUri = vscode.Uri.file(absolutePath);
// 检查文件是否存在 // 检查文件是否存在
if (!fs.existsSync(absolutePath)) { try {
const stat = await vscode.workspace.fs.stat(fileUri);
if (stat.type !== vscode.FileType.File) {
throw new Error(`路径不是文件: ${absolutePath}`);
}
} catch (error) {
throw new Error(`文件不存在: ${absolutePath}`); throw new Error(`文件不存在: ${absolutePath}`);
} }
// 检查是否是文件
const stats = fs.statSync(absolutePath);
if (!stats.isFile()) {
throw new Error(`路径不是文件: ${absolutePath}`);
}
// 删除文件 // 删除文件
fs.unlinkSync(absolutePath); await vscode.workspace.fs.delete(fileUri);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -181,19 +201,21 @@ export async function updateFile(
} }
} }
const fileUri = vscode.Uri.file(absolutePath);
// 检查文件是否存在 // 检查文件是否存在
if (!fs.existsSync(absolutePath)) { try {
const stat = await vscode.workspace.fs.stat(fileUri);
if (stat.type !== vscode.FileType.File) {
throw new Error(`路径不是文件: ${absolutePath}`);
}
} catch (error) {
throw new Error(`文件不存在: ${absolutePath}`); throw new Error(`文件不存在: ${absolutePath}`);
} }
// 检查是否是文件内容
const state = fs.statSync(absolutePath);
if (!state.isFile()) {
throw new Error(`路径不是文件: ${absolutePath}`);
}
// 修改文件内容 // 修改文件内容
fs.writeFileSync(absolutePath, content, "utf-8"); const contentBytes = Buffer.from(content, "utf-8");
await vscode.workspace.fs.writeFile(fileUri, contentBytes);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -218,13 +240,21 @@ export async function appendToFile(
} }
} }
// 检查文件是否存在 const fileUri = vscode.Uri.file(absolutePath);
if (!fs.existsSync(absolutePath)) {
// 检查文件是否存在并读取原内容
let existingContent = "";
try {
const contentBytes = await vscode.workspace.fs.readFile(fileUri);
existingContent = Buffer.from(contentBytes).toString("utf-8");
} catch (error) {
throw new Error(`文件不存在: ${absolutePath}`); throw new Error(`文件不存在: ${absolutePath}`);
} }
// 追加内容 // 追加内容
fs.appendFileSync(absolutePath, content, "utf-8"); const newContent = existingContent + content;
const contentBytes = Buffer.from(newContent, "utf-8");
await vscode.workspace.fs.writeFile(fileUri, contentBytes);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -248,13 +278,16 @@ export async function replaceFile(
} }
} }
// 检查文件是否存在 const fileUri = vscode.Uri.file(absolutePath);
if (!fs.existsSync(absolutePath)) {
throw new Error(`文件不存在: ${absolutePath}`);
}
// 读取文件内容 // 读取文件内容
const fileContent = fs.readFileSync(absolutePath, "utf-8"); let fileContent: string;
try {
const contentBytes = await vscode.workspace.fs.readFile(fileUri);
fileContent = Buffer.from(contentBytes).toString("utf-8");
} catch (error) {
throw new Error(`文件不存在: ${absolutePath}`);
}
// 转义特殊字符,将字符串作为字面量处理 // 转义特殊字符,将字符串作为字面量处理
const escapeRegExp = (str: string) => { const escapeRegExp = (str: string) => {
@ -276,7 +309,8 @@ export async function replaceFile(
} }
// 写回文件 // 写回文件
fs.writeFileSync(absolutePath, newContent, "utf-8"); const contentBytes = Buffer.from(newContent, "utf-8");
await vscode.workspace.fs.writeFile(fileUri, contentBytes);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -300,20 +334,26 @@ export async function insertAtLine(
} }
} }
// 检查文件是否存在 const fileUri = vscode.Uri.file(absolutePath);
if (!fs.existsSync(absolutePath)) {
// 读取文件内容
let fileContent: string;
try {
const contentBytes = await vscode.workspace.fs.readFile(fileUri);
fileContent = Buffer.from(contentBytes).toString("utf-8");
} catch (error) {
throw new Error(`文件不存在: ${absolutePath}`); throw new Error(`文件不存在: ${absolutePath}`);
} }
// 读取文件内容
const fileContent = fs.readFileSync(absolutePath, "utf-8");
const lines = fileContent.split("\n"); const lines = fileContent.split("\n");
// 插入内容 // 插入内容
lines.splice(lineNumber, 0, content); lines.splice(lineNumber, 0, content);
// 写回文件 // 写回文件
fs.writeFileSync(absolutePath, lines.join("\n"), "utf-8"); const newContent = lines.join("\n");
const newContentBytes = Buffer.from(newContent, "utf-8");
await vscode.workspace.fs.writeFile(fileUri, newContentBytes);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -345,24 +385,41 @@ export async function renameFile(
throw new Error("没有打开的工作区,无法重命名相对路径的文件"); throw new Error("没有打开的工作区,无法重命名相对路径的文件");
} }
const oldUri = vscode.Uri.file(absoluteOldPath);
const newUri = vscode.Uri.file(absoluteNewPath);
// 检查原文件是否存在 // 检查原文件是否存在
if (!fs.existsSync(absoluteOldPath)) { try {
await vscode.workspace.fs.stat(oldUri);
} catch (error) {
throw new Error(`文件不存在: ${absoluteOldPath}`); throw new Error(`文件不存在: ${absoluteOldPath}`);
} }
// 检查新文件名是否已存在 // 检查新文件名是否已存在
if (fs.existsSync(absoluteNewPath)) { try {
await vscode.workspace.fs.stat(newUri);
throw new Error(`目标文件已存在: ${absoluteNewPath}`); throw new Error(`目标文件已存在: ${absoluteNewPath}`);
} catch (error: any) {
// 如果文件不存在,继续重命名
if (error.code !== 'FileNotFound' && !error.message.includes('目标文件已存在')) {
throw error;
}
if (error.message.includes('目标文件已存在')) {
throw error;
}
} }
// 确保目标目录存在 // 确保目标目录存在
const newDir = path.dirname(absoluteNewPath); const newDir = path.dirname(absoluteNewPath);
if (!fs.existsSync(newDir)) { const newDirUri = vscode.Uri.file(newDir);
fs.mkdirSync(newDir, { recursive: true }); try {
await vscode.workspace.fs.stat(newDirUri);
} catch {
await vscode.workspace.fs.createDirectory(newDirUri);
} }
// 重命名文件 // 重命名文件
fs.renameSync(absoluteOldPath, absoluteNewPath); await vscode.workspace.fs.rename(oldUri, newUri, { overwrite: false });
} catch (error) { } catch (error) {
throw error; throw error;
} }

View File

@ -1,5 +1,4 @@
import * as vscode from "vscode"; import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import { spawn } from "child_process"; import { spawn } from "child_process";
import { promisify } from "util"; import { promisify } from "util";
@ -107,13 +106,16 @@ export async function checkVerilogProject(
try { try {
// 检查项目路径是否存在 // 检查项目路径是否存在
if (!fs.existsSync(projectPath)) { const projectUri = vscode.Uri.file(projectPath);
try {
await vscode.workspace.fs.stat(projectUri);
} catch (error) {
result.errors.push(`项目路径不存在: ${projectPath}`); result.errors.push(`项目路径不存在: ${projectPath}`);
return result; return result;
} }
// 查找所有 Verilog 文件 (.v, .sv) // 查找所有 Verilog 文件 (.v, .sv)
const verilogFiles = findVerilogFiles(projectPath); const verilogFiles = await findVerilogFiles(projectPath);
result.allVerilogFiles = verilogFiles; result.allVerilogFiles = verilogFiles;
if (verilogFiles.length === 0) { if (verilogFiles.length === 0) {
@ -123,7 +125,9 @@ export async function checkVerilogProject(
// 分析文件内容,查找 top module 和 testbench // 分析文件内容,查找 top module 和 testbench
for (const file of verilogFiles) { for (const file of verilogFiles) {
const content = fs.readFileSync(file, "utf-8"); const fileUri = vscode.Uri.file(file);
const contentBytes = await vscode.workspace.fs.readFile(fileUri);
const content = Buffer.from(contentBytes).toString("utf-8");
const fileName = path.basename(file).toLowerCase(); const fileName = path.basename(file).toLowerCase();
// 检查是否是 testbench 文件 // 检查是否是 testbench 文件
@ -169,23 +173,23 @@ export async function checkVerilogProject(
/** /**
* 递归查找目录下所有 Verilog 文件 * 递归查找目录下所有 Verilog 文件
*/ */
function findVerilogFiles(dir: string): string[] { async function findVerilogFiles(dir: string): Promise<string[]> {
const verilogFiles: string[] = []; const verilogFiles: string[] = [];
function searchDir(currentDir: string) { async function searchDir(currentDir: string) {
const files = fs.readdirSync(currentDir); const dirUri = vscode.Uri.file(currentDir);
const entries = await vscode.workspace.fs.readDirectory(dirUri);
for (const file of files) { for (const [fileName, fileType] of entries) {
const filePath = path.join(currentDir, file); const filePath = path.join(currentDir, fileName);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) { if (fileType === vscode.FileType.Directory) {
// 跳过 node_modules 等目录 // 跳过 node_modules 等目录
if (!file.startsWith(".") && file !== "node_modules") { if (!fileName.startsWith(".") && fileName !== "node_modules") {
searchDir(filePath); await searchDir(filePath);
} }
} else if (stat.isFile()) { } else if (fileType === vscode.FileType.File) {
const ext = path.extname(file).toLowerCase(); const ext = path.extname(fileName).toLowerCase();
if (ext === ".v" || ext === ".sv") { if (ext === ".v" || ext === ".sv") {
verilogFiles.push(filePath); verilogFiles.push(filePath);
} }
@ -193,14 +197,14 @@ function findVerilogFiles(dir: string): string[] {
} }
} }
searchDir(dir); await searchDir(dir);
return verilogFiles; return verilogFiles;
} }
/** /**
* 获取 iverilog 可执行文件路径 * 获取 iverilog 可执行文件路径
*/ */
function getIverilogPath(extensionPath: string): string { async function getIverilogPath(extensionPath: string): Promise<string> {
const platform = process.platform; const platform = process.platform;
let iverilogBin = ""; let iverilogBin = "";
@ -214,17 +218,19 @@ function getIverilogPath(extensionPath: string): string {
} }
// 如果插件包中没有,尝试使用系统安装的 iverilog // 如果插件包中没有,尝试使用系统安装的 iverilog
if (!fs.existsSync(iverilogBin)) { const iverilogUri = vscode.Uri.file(iverilogBin);
try {
await vscode.workspace.fs.stat(iverilogUri);
return iverilogBin;
} catch {
return "iverilog"; // 使用系统 PATH 中的 iverilog return "iverilog"; // 使用系统 PATH 中的 iverilog
} }
return iverilogBin;
} }
/** /**
* 获取 vvp 可执行文件路径 * 获取 vvp 可执行文件路径
*/ */
function getVvpPath(extensionPath: string): string { async function getVvpPath(extensionPath: string): Promise<string> {
const platform = process.platform; const platform = process.platform;
let vvpBin = ""; let vvpBin = "";
@ -238,11 +244,13 @@ function getVvpPath(extensionPath: string): string {
} }
// 如果插件包中没有,尝试使用系统安装的 vvp // 如果插件包中没有,尝试使用系统安装的 vvp
if (!fs.existsSync(vvpBin)) { const vvpUri = vscode.Uri.file(vvpBin);
try {
await vscode.workspace.fs.stat(vvpUri);
return vvpBin;
} catch {
return "vvp"; // 使用系统 PATH 中的 vvp return "vvp"; // 使用系统 PATH 中的 vvp
} }
return vvpBin;
} }
/** /**
@ -264,8 +272,8 @@ export async function generateVCD(
} }
// 2. 获取 iverilog 和 vvp 路径 // 2. 获取 iverilog 和 vvp 路径
const iverilogPath = getIverilogPath(extensionPath); const iverilogPath = await getIverilogPath(extensionPath);
const vvpPath = getVvpPath(extensionPath); const vvpPath = await getVvpPath(extensionPath);
// 3. 准备输出路径 // 3. 准备输出路径
const outputDir = projectPath; const outputDir = projectPath;
@ -320,7 +328,11 @@ export async function generateVCD(
} }
// 8. 查找生成的 VCD 文件 // 8. 查找生成的 VCD 文件
const vcdFiles = fs.readdirSync(projectPath).filter(file => file.endsWith('.vcd')); const projectUri = vscode.Uri.file(projectPath);
const entries = await vscode.workspace.fs.readDirectory(projectUri);
const vcdFiles = entries
.filter(([fileName, fileType]) => fileType === vscode.FileType.File && fileName.endsWith('.vcd'))
.map(([fileName]) => fileName);
if (vcdFiles.length === 0) { if (vcdFiles.length === 0) {
return { return {
@ -335,9 +347,9 @@ export async function generateVCD(
// 9. 清理中间文件 // 9. 清理中间文件
try { try {
if (fs.existsSync(outputFile)) { const outputUri = vscode.Uri.file(outputFile);
fs.unlinkSync(outputFile); await vscode.workspace.fs.stat(outputUri);
} await vscode.workspace.fs.delete(outputUri);
} catch (error) { } catch (error) {
// 忽略清理错误 // 忽略清理错误
} }
@ -364,10 +376,13 @@ 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 = getIverilogPath(extensionPath); const iverilogPath = await getIverilogPath(extensionPath);
// 检查文件是否存在 // 检查文件是否存在
if (!fs.existsSync(iverilogPath)) { const iverilogUri = vscode.Uri.file(iverilogPath);
try {
await vscode.workspace.fs.stat(iverilogUri);
} catch (error) {
return { return {
available: false, available: false,
message: `iverilog 不可用。未找到文件: ${iverilogPath}`, message: `iverilog 不可用。未找到文件: ${iverilogPath}`,

View File

@ -1,5 +1,4 @@
import * as vscode from "vscode"; import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path"; import * as path from "path";
/** /**
@ -16,19 +15,21 @@ export async function readFileContent(filePath: string): Promise<string> {
} }
} }
// 检查文件是否存在 const fileUri = vscode.Uri.file(absolutePath);
if (!fs.existsSync(absolutePath)) {
// 检查文件是否存在并获取文件类型
try {
const stat = await vscode.workspace.fs.stat(fileUri);
if (stat.type !== vscode.FileType.File) {
throw new Error(`路径不是文件: ${absolutePath}`);
}
} catch (error) {
throw new Error(`文件不存在: ${absolutePath}`); throw new Error(`文件不存在: ${absolutePath}`);
} }
// 检查是否是文件
const stats = fs.statSync(absolutePath);
if (!stats.isFile()) {
throw new Error(`路径不是文件: ${absolutePath}`);
}
// 读取文件内容 // 读取文件内容
const content = fs.readFileSync(absolutePath, "utf-8"); const contentBytes = await vscode.workspace.fs.readFile(fileUri);
const content = Buffer.from(contentBytes).toString("utf-8");
return content; return content;
} catch (error) { } catch (error) {
throw error; throw error;
@ -76,36 +77,38 @@ export async function readDirectory(
} }
} }
const dirUri = vscode.Uri.file(absolutePath);
// 检查目录是否存在 // 检查目录是否存在
if (!fs.existsSync(absolutePath)) { try {
const stat = await vscode.workspace.fs.stat(dirUri);
if (stat.type !== vscode.FileType.Directory) {
throw new Error(`路径不是目录: ${absolutePath}`);
}
} catch (error) {
throw new Error(`目录不存在: ${absolutePath}`); throw new Error(`目录不存在: ${absolutePath}`);
} }
const stats = fs.statSync(absolutePath);
if (!stats.isDirectory()) {
throw new Error(`路径不是目录: ${absolutePath}`);
}
// 读取目录内容 // 读取目录内容
const files = fs.readdirSync(absolutePath); const entries = await vscode.workspace.fs.readDirectory(dirUri);
const results = []; const results = [];
for (const file of files) { for (const [fileName, fileType] of entries) {
const filePath = path.join(absolutePath, file); if (fileType === vscode.FileType.File) {
const fileStats = fs.statSync(filePath);
if (fileStats.isFile()) {
// 如果指定了扩展名过滤 // 如果指定了扩展名过滤
if (extensions && extensions.length > 0) { if (extensions && extensions.length > 0) {
const ext = path.extname(file); const ext = path.extname(fileName);
if (!extensions.includes(ext)) { if (!extensions.includes(ext)) {
continue; continue;
} }
} }
try { try {
const content = fs.readFileSync(filePath, "utf-8"); const filePath = path.join(absolutePath, fileName);
results.push({ path: file, content }); const fileUri = vscode.Uri.file(filePath);
const contentBytes = await vscode.workspace.fs.readFile(fileUri);
const content = Buffer.from(contentBytes).toString("utf-8");
results.push({ path: fileName, content });
} catch (error) { } catch (error) {
// 跳过无法读取的文件 // 跳过无法读取的文件
continue; continue;
@ -122,13 +125,13 @@ export async function readDirectory(
/** /**
* 获取文件信息 * 获取文件信息
*/ */
export function getFileInfo(filePath: string): { export async function getFileInfo(filePath: string): Promise<{
exists: boolean; exists: boolean;
isFile: boolean; isFile: boolean;
isDirectory: boolean; isDirectory: boolean;
size?: number; size?: number;
extension?: string; extension?: string;
} { }> {
try { try {
let absolutePath = filePath; let absolutePath = filePath;
if (!path.isAbsolute(filePath)) { if (!path.isAbsolute(filePath)) {
@ -138,20 +141,15 @@ export function getFileInfo(filePath: string): {
} }
} }
if (!fs.existsSync(absolutePath)) { const fileUri = vscode.Uri.file(absolutePath);
return {
exists: false,
isFile: false,
isDirectory: false,
};
}
const stats = fs.statSync(absolutePath); try {
const stat = await vscode.workspace.fs.stat(fileUri);
return { return {
exists: true, exists: true,
isFile: stats.isFile(), isFile: stat.type === vscode.FileType.File,
isDirectory: stats.isDirectory(), isDirectory: stat.type === vscode.FileType.Directory,
size: stats.size, size: stat.size,
extension: path.extname(absolutePath), extension: path.extname(absolutePath),
}; };
} catch (error) { } catch (error) {
@ -161,4 +159,11 @@ export function getFileInfo(filePath: string): {
isDirectory: false, isDirectory: false,
}; };
} }
} catch (error) {
return {
exists: false,
isFile: false,
isDirectory: false,
};
}
} }