第一个插件实战:文件操作工具
上一个插件只是「说话」,这个插件要真正动手:右键一个文件,执行文件操作——读取内容、替换文本、选择文件另存。通过它掌握插件与文件系统交互的核心 API。
目标
实现一个文件操作工具插件:
- 编辑器右键菜单出现「替换文件内容」命令
- 读取当前文件内容,替换指定文本并保存
- 打开文件对话框选择文件,统计并展示字符数
第一步:声明右键菜单
在 package.json 的 contributes.menus 中注册右键菜单:
json
{
"contributes": {
"commands": [
{
"command": "file-tools.replaceContent",
"title": "替换文件内容",
"category": "文件工具"
},
{
"command": "file-tools.countChars",
"title": "统计字符数",
"category": "文件工具"
}
],
"menus": {
"editor/context": [
{
"command": "file-tools.replaceContent",
"when": "editorHasSelection",
"group": "1_modification"
},
{
"command": "file-tools.countChars",
"group": "navigation"
}
]
}
}
}关键点:
editor/context是编辑器右键菜单位置when条件控制菜单显示时机(有选区才显示替换)group控制菜单位置分组
第二步:注册命令与文件读取
创建 src/extension.ts:
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// 命令一:读取当前文件,替换选中文本为大小写转换结果
context.subscriptions.push(
vscode.commands.registerCommand(
'file-tools.replaceContent',
async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showWarningMessage('没有打开的编辑器');
return;
}
const selection = editor.selection;
if (selection.isEmpty) {
vscode.window.showWarningMessage('请先选中文本');
return;
}
const selected = editor.document.getText(selection);
const replaced = selected.toUpperCase();
// 使用 TextEditorEdit 批量替换
await editor.edit((editBuilder) => {
editBuilder.replace(selection, replaced);
});
}
)
);
// 命令二:打开文件对话框统计字符数
context.subscriptions.push(
vscode.commands.registerCommand('file-tools.countChars', async () => {
const options: vscode.OpenDialogOptions = {
canSelectMany: false,
openLabel: '统计字符',
filters: {
'文本文件': ['txt', 'md'],
'所有文件': ['*']
}
};
const uris = await vscode.window.showOpenDialog(options);
if (!uris || uris.length === 0) {
return;
}
const uri = uris[0];
const content = await vscode.workspace.fs.readFile(uri);
const text = Buffer.from(content).toString('utf8');
vscode.window.showInformationMessage(
`文件 ${uri.fsPath.split('\\').pop()} 共 ${text.length} 个字符`
);
})
);
}
export function deactivate() {}核心 API 拆解
右键菜单 contributes.menus/editor/context
| 配置项 | 作用 |
|---|---|
command | 绑定的命令 ID |
when | 显示条件(editorHasSelection 等) |
group | 分组排序(navigation、1_modification) |
文件读写 workspace.fs
vscode.workspace.fs 提供跨平台的文件系统操作:
| API | 作用 |
|---|---|
readFile(uri) | 读取文件,返回 Uint8Array |
writeFile(uri, data) | 写入文件 |
stat(uri) | 获取文件信息 |
readDirectory(uri) | 读取目录 |
createDirectory(uri) | 创建目录 |
delete(uri) | 删除文件 |
rename(old, new) | 重命名/移动 |
注意返回值是 Uint8Array,文本需转码:
typescript
const text = Buffer.from(bytes).toString('utf8');文件对话框 window.showOpenDialog
| 选项 | 作用 |
|---|---|
canSelectMany | 是否允许多选 |
openLabel | 确认按钮文案 |
filters | 文件类型过滤 |
defaultUri | 初始目录 |
返回选中文件的 Uri 数组,未选择返回 undefined。
编辑器编辑 editor.edit
typescript
await editor.edit((editBuilder) => {
editBuilder.replace(selection, replaced);
});TextEditorEdit 提供:
| 方法 | 作用 |
|---|---|
insert(position, text) | 在指定位置插入 |
delete(range) | 删除范围 |
replace(range, text) | 替换范围 |
进阶:写入文件
结合 workspace.fs.writeFile 实现「保存副本」:
typescript
vscode.commands.registerCommand('file-tools.saveCopy', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const uri = editor.document.uri;
const content = await vscode.workspace.fs.readFile(uri);
const dir = uri.fsPath.split('\\').slice(0, -1).join('\\');
const name = uri.fsPath.split('\\').pop();
const copyUri = vscode.Uri.file(`${dir}\\copy_${name}`);
await vscode.workspace.fs.writeFile(copyUri, content);
vscode.window.showInformationMessage(`已保存副本: copy_${name}`);
});运行与验证
按 F5 启动调试,在 Extension Development Host 窗口验证:
| 步骤 | 操作 | 预期 |
|---|---|---|
| 1 | 打开一个文本文件,选中文本 | 右键出现「替换文件内容」 |
| 2 | 点击替换 | 选中文本变为大写 |
| 3 | 右键空白处 | 出现「统计字符数」 |
| 4 | 点击统计 | 弹出文件对话框 |
| 5 | 选择一个 txt/md 文件 | 弹出字符数消息 |
常见问题
| 问题 | 处理 |
|---|---|
| 右键菜单不显示 | 检查 when 条件与 editor/context 位置 |
| 读取中文乱码 | 确认使用 utf8 转码 |
| 写文件失败 | 检查路径与目录是否存在 |
| 对话框打不开 | 检查 filters 配置是否合法 |
扩展方向
掌握了文件操作后,可扩展更多实用功能:
- 批量替换多个文件中的文本
- 文件重命名工具
- 目录扫描统计
- 模板文件生成器
文件操作工具把插件从「演示」推向「生产可用」,右键菜单 + 文件读写是大量实用插件的骨架。