TextDocument 与 TextEditor
操作文本是编辑器插件最常见的需求。TextDocument(文档内容)与 TextEditor(编辑器视图)是两个核心概念:文档是数据,编辑器是视图,理解二者的关系是文本操作的基础。
文档与编辑器的关系
TextDocument(数据层)
├── 文件名、语言、内容
└── 不依赖编辑器窗口
TextEditor(视图层)
├── 显示一个 TextDocument
├── 选区、光标、滚动位置
└── 可以有多个编辑器显示同一文档- TextDocument:一个文件对应一个文档对象,全局唯一
- TextEditor:编辑器窗口中的视图,可同时打开多个
- 文档内容修改通过编辑器的
edit()执行
打开文档 openTextDocument
workspace.openTextDocument 打开文档并返回 TextDocument:
typescript
import * as vscode from 'vscode';
// 1. 按文件路径打开
const doc1 = await vscode.workspace.openTextDocument(
vscode.Uri.file('C:\\project\\app.ts')
);
// 2. 按 Uri 打开
const doc2 = await vscode.workspace.openTextDocument(uri);
// 3. 新建未保存文档
const doc3 = await vscode.workspace.openTextDocument({
language: 'typescript',
content: 'const x = 1;'
});
// 4. 当前激活编辑器文档
const activeDoc = vscode.window.activeTextEditor?.document;
// 5. 按 Uri 查找已打开的文档
const doc4 = vscode.workspace.textDocuments.find(
(d) => d.uri.toString() === uri.toString()
);在编辑器显示
打开文档后需要显示到编辑器窗口:
typescript
const editor = await vscode.window.showTextDocument(doc1, {
viewColumn: vscode.ViewColumn.One, // 显示的列
preview: true, // 是否预览模式
preserveFocus: false // 是否保持焦点
});| 选项 | 作用 |
|---|---|
viewColumn | 显示位置(One/Two/Beside) |
preview | 预览模式(浅色标题,可被覆盖) |
preserveFocus | 打开后是否聚焦 |
activeTextEditor 当前编辑器
window.activeTextEditor 返回当前激活的编辑器:
typescript
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showWarningMessage('没有激活的编辑器');
return;
}
const document = editor.document; // 文档对象
const selection = editor.selection; // 当前选区
const visible = editor.visibleRanges; // 可见范围监听编辑器变化
typescript
// 激活编辑器切换
context.subscriptions.push(
vscode.window.onDidChangeActiveTextEditor((editor) => {
if (editor) {
console.log(`切换到: ${editor.document.fileName}`);
}
})
);
// 文本选择变化
context.subscriptions.push(
vscode.window.onDidChangeTextEditorSelection((event) => {
const editor = event.textEditor;
const selection = event.selections[0];
console.log(`选区: ${selection.start.line}-${selection.end.line}`);
})
);TextEditorEdit 批量编辑
editor.edit() 接收回调,通过 TextEditorEdit 执行批量修改:
typescript
await editor.edit((editBuilder) => {
// 插入
editBuilder.insert(
new vscode.Position(0, 0), // 位置
'// 注释\n'
);
// 删除
editBuilder.delete(
new vscode.Range(0, 0, 1, 0)
);
// 替换
editBuilder.replace(
editor.selection,
'替换后的文本'
);
});批量编辑的优势
| 方式 | 特点 |
|---|---|
多次 edit() | 每次触发一次撤销栈、一次格式化 |
| 单次批量 | 一次操作完成,撤销一步到位 |
批量编辑应尽量在一次 edit() 内完成所有修改,避免产生多个历史记录。
edit 返回值
edit() 返回 Promise,可判断是否成功:
typescript
const applied = await editor.edit((editBuilder) => {
editBuilder.replace(selection, 'new text');
});
if (applied) {
vscode.window.showInformationMessage('修改成功');
} else {
vscode.window.showErrorMessage('修改失败(文档可能已关闭)');
}selections 选区与光标管理
editor.selections 支持多光标操作:
typescript
// 读取当前选区
const selections = editor.selections;
selections.forEach((sel) => {
console.log(`选区: ${sel.start.line}:${sel.start.character}`);
});
// 设置多选区(多光标)
editor.selections = [
new vscode.Selection(0, 0, 0, 5), // 行0 0-5字符
new vscode.Selection(2, 0, 2, 3) // 行2 0-3字符
];选区常用属性
| 属性 | 说明 |
|---|---|
selection.start | 开始位置 |
selection.end | 结束位置 |
selection.isEmpty | 是否空选区(光标无选中) |
selection.active | 活动端(光标位置) |
selection.anchor | 锚点端 |
常用选区操作
typescript
// 全部选中
await editor.edit((builder) => {
const full = new vscode.Range(
new vscode.Position(0, 0),
editor.document.lineAt(editor.document.lineCount - 1).range.end
);
builder.replace(full, '全部内容替换');
});Position 与 Range
| 类型 | 说明 |
|---|---|
Position | 位置(行、列),从 0 开始 |
Range | 范围(起始 Position、结束 Position) |
Selection | 选区(锚点、活动端) |
typescript
const pos = new vscode.Position(3, 5); // 第 4 行第 6 列
const range = new vscode.Range(0, 0, 1, 0); // 从 0:0 到 1:0完整示例:文档工具
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('docTools.uppercaseSelection', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const document = editor.document;
const selections = editor.selections;
// 批量处理所有选区
await editor.edit((builder) => {
for (const selection of selections) {
if (selection.isEmpty) {
continue;
}
const text = document.getText(selection);
builder.replace(selection, text.toUpperCase());
}
});
})
);
context.subscriptions.push(
vscode.commands.registerCommand('docTools.countLines', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const count = editor.document.lineCount;
vscode.window.showInformationMessage(
`文档共 ${count} 行`
);
})
);
}常见问题
| 问题 | 处理 |
|---|---|
| activeTextEditor 为空 | 无激活编辑器时返回 undefined |
| 修改不生效 | edit() 是异步的,需 await |
| 多选区丢失 | 一次性设置 editor.selections |
| 位置越界 | 检查 Position 是否在文档范围内 |
TextDocument 与 TextEditor 是文本操作的基石,掌握后即可实现格式转换、批量编辑、代码生成等核心功能。