Skip to content

文档 API

文档 API 提供了管理和操作 VS Code 中文档的功能,包括文档内容访问、编辑、保存、以及文档生命周期管理。

概述

在 VS Code 中,文档(Document)代表编辑器中打开的文件内容。文档 API 允许扩展程序:

  • 访问和修改文档内容
  • 监听文档变化事件
  • 管理文档状态
  • 执行文档操作
  • 处理文档保存和关闭

主要接口

TextDocument

typescript
interface TextDocument {
    /**
     * 文档的 URI
     */
    readonly uri: Uri;

    /**
     * 文档的文件名
     */
    readonly fileName: string;

    /**
     * 文档是否未保存
     */
    readonly isUntitled: boolean;

    /**
     * 文档的语言标识符
     */
    readonly languageId: string;

    /**
     * 文档版本号
     */
    readonly version: number;

    /**
     * 文档是否已修改
     */
    readonly isDirty: boolean;

    /**
     * 文档是否已关闭
     */
    readonly isClosed: boolean;

    /**
     * 保存文档
     */
    save(): Thenable<boolean>;

    /**
     * 获取文档的行数
     */
    readonly lineCount: number;

    /**
     * 获取指定行的内容
     */
    lineAt(line: number): TextLine;
    lineAt(position: Position): TextLine;

    /**
     * 获取指定范围的文本
     */
    getText(range?: Range): string;

    /**
     * 获取指定位置的单词范围
     */
    getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined;

    /**
     * 验证位置是否有效
     */
    validatePosition(position: Position): Position;

    /**
     * 验证范围是否有效
     */
    validateRange(range: Range): Range;

    /**
     * 获取位置的偏移量
     */
    offsetAt(position: Position): number;

    /**
     * 根据偏移量获取位置
     */
    positionAt(offset: number): Position;
}

TextLine

typescript
interface TextLine {
    /**
     * 行号(从0开始)
     */
    readonly lineNumber: number;

    /**
     * 行的完整文本
     */
    readonly text: string;

    /**
     * 行的范围
     */
    readonly range: Range;

    /**
     * 行的范围(包含行结束符)
     */
    readonly rangeIncludingLineBreak: Range;

    /**
     * 第一个非空白字符的索引
     */
    readonly firstNonWhitespaceCharacterIndex: number;

    /**
     * 行是否为空或只包含空白字符
     */
    readonly isEmptyOrWhitespace: boolean;
}

文档访问

获取文档

typescript
import * as vscode from 'vscode';

// 获取当前活动文档
const activeDocument = vscode.window.activeTextEditor?.document;

// 获取所有打开的文档
const allDocuments = vscode.workspace.textDocuments;

// 根据 URI 获取文档
const uri = vscode.Uri.file('/path/to/file.ts');
const document = await vscode.workspace.openTextDocument(uri);

// 创建新的未命名文档
const newDocument = await vscode.workspace.openTextDocument({
    language: 'typescript',
    content: '// 新文档内容'
});

文档内容读取

typescript
function analyzeDocument(document: vscode.TextDocument) {
    // 获取文档基本信息
    console.log(`文件名: ${document.fileName}`);
    console.log(`语言: ${document.languageId}`);
    console.log(`行数: ${document.lineCount}`);
    console.log(`是否已修改: ${document.isDirty}`);

    // 获取全部文本
    const fullText = document.getText();

    // 获取指定范围的文本
    const range = new vscode.Range(0, 0, 10, 0); // 前10行
    const partialText = document.getText(range);

    // 逐行读取
    for (let i = 0; i < document.lineCount; i++) {
        const line = document.lineAt(i);
        console.log(`第${i + 1}行: ${line.text}`);
    }
}

文档监听

文档变化事件

typescript
// 监听文档打开
vscode.workspace.onDidOpenTextDocument(document => {
    console.log(`文档已打开: ${document.fileName}`);
});

// 监听文档关闭
vscode.workspace.onDidCloseTextDocument(document => {
    console.log(`文档已关闭: ${document.fileName}`);
});

// 监听文档内容变化
vscode.workspace.onDidChangeTextDocument(event => {
    const document = event.document;
    console.log(`文档已修改: ${document.fileName}`);
    
    // 处理具体的变化
    event.contentChanges.forEach(change => {
        console.log(`变化范围: ${change.range}`);
        console.log(`新文本: ${change.text}`);
    });
});

// 监听文档保存
vscode.workspace.onDidSaveTextDocument(document => {
    console.log(`文档已保存: ${document.fileName}`);
});

文档状态监听

typescript
// 监听文档状态变化
vscode.workspace.onDidChangeTextDocument(event => {
    const document = event.document;
    
    if (document.isDirty) {
        console.log('文档有未保存的更改');
    }
    
    // 检查特定类型的文档
    if (document.languageId === 'typescript') {
        // 处理 TypeScript 文档变化
        handleTypeScriptDocumentChange(event);
    }
});

function handleTypeScriptDocumentChange(event: vscode.TextDocumentChangeEvent) {
    // TypeScript 特定的处理逻辑
    const document = event.document;
    
    // 检查是否有语法错误
    checkSyntaxErrors(document);
    
    // 更新类型信息
    updateTypeInformation(document);
}

文档操作

文档编辑

typescript
async function editDocument(document: vscode.TextDocument) {
    const editor = await vscode.window.showTextDocument(document);
    
    // 创建编辑操作
    const edit = new vscode.WorkspaceEdit();
    
    // 在文档开头插入文本
    const insertPosition = new vscode.Position(0, 0);
    edit.insert(document.uri, insertPosition, '// 自动生成的注释\n');
    
    // 替换指定范围的文本
    const replaceRange = new vscode.Range(1, 0, 1, 10);
    edit.replace(document.uri, replaceRange, 'new content');
    
    // 删除指定范围的文本
    const deleteRange = new vscode.Range(2, 0, 3, 0);
    edit.delete(document.uri, deleteRange);
    
    // 应用编辑
    await vscode.workspace.applyEdit(edit);
}

文档格式化

typescript
async function formatDocument(document: vscode.TextDocument) {
    // 获取格式化编辑
    const formatEdits = await vscode.commands.executeCommand<vscode.TextEdit[]>(
        'vscode.executeFormatDocumentProvider',
        document.uri
    );
    
    if (formatEdits) {
        const edit = new vscode.WorkspaceEdit();
        edit.set(document.uri, formatEdits);
        await vscode.workspace.applyEdit(edit);
    }
}

async function formatRange(document: vscode.TextDocument, range: vscode.Range) {
    // 格式化指定范围
    const formatEdits = await vscode.commands.executeCommand<vscode.TextEdit[]>(
        'vscode.executeFormatRangeProvider',
        document.uri,
        range
    );
    
    if (formatEdits) {
        const edit = new vscode.WorkspaceEdit();
        edit.set(document.uri, formatEdits);
        await vscode.workspace.applyEdit(edit);
    }
}

实用示例

文档分析器

typescript
class DocumentAnalyzer {
    analyzeDocument(document: vscode.TextDocument): DocumentAnalysis {
        const analysis: DocumentAnalysis = {
            fileName: document.fileName,
            languageId: document.languageId,
            lineCount: document.lineCount,
            characterCount: document.getText().length,
            wordCount: this.countWords(document.getText()),
            emptyLines: 0,
            commentLines: 0,
            codeLines: 0
        };

        // 逐行分析
        for (let i = 0; i < document.lineCount; i++) {
            const line = document.lineAt(i);
            
            if (line.isEmptyOrWhitespace) {
                analysis.emptyLines++;
            } else if (this.isCommentLine(line.text, document.languageId)) {
                analysis.commentLines++;
            } else {
                analysis.codeLines++;
            }
        }

        return analysis;
    }

    private countWords(text: string): number {
        return text.split(/\s+/).filter(word => word.length > 0).length;
    }

    private isCommentLine(text: string, languageId: string): boolean {
        const trimmed = text.trim();
        
        switch (languageId) {
            case 'typescript':
            case 'javascript':
                return trimmed.startsWith('//') || trimmed.startsWith('/*');
            case 'python':
                return trimmed.startsWith('#');
            case 'html':
                return trimmed.startsWith('<!--');
            default:
                return false;
        }
    }
}

interface DocumentAnalysis {
    fileName: string;
    languageId: string;
    lineCount: number;
    characterCount: number;
    wordCount: number;
    emptyLines: number;
    commentLines: number;
    codeLines: number;
}

文档同步器

typescript
class DocumentSynchronizer {
    private disposables: vscode.Disposable[] = [];
    private documentStates = new Map<string, DocumentState>();

    constructor() {
        this.setupEventListeners();
    }

    private setupEventListeners() {
        // 监听文档变化
        this.disposables.push(
            vscode.workspace.onDidChangeTextDocument(this.onDocumentChanged.bind(this))
        );

        // 监听文档保存
        this.disposables.push(
            vscode.workspace.onDidSaveTextDocument(this.onDocumentSaved.bind(this))
        );

        // 监听文档关闭
        this.disposables.push(
            vscode.workspace.onDidCloseTextDocument(this.onDocumentClosed.bind(this))
        );
    }

    private onDocumentChanged(event: vscode.TextDocumentChangeEvent) {
        const document = event.document;
        const uri = document.uri.toString();

        // 更新文档状态
        const state = this.documentStates.get(uri) || {
            version: 0,
            lastModified: Date.now(),
            changeCount: 0
        };

        state.version = document.version;
        state.lastModified = Date.now();
        state.changeCount++;

        this.documentStates.set(uri, state);

        // 触发同步逻辑
        this.syncDocument(document, event.contentChanges);
    }

    private onDocumentSaved(document: vscode.TextDocument) {
        const uri = document.uri.toString();
        const state = this.documentStates.get(uri);
        
        if (state) {
            state.lastSaved = Date.now();
            this.documentStates.set(uri, state);
        }

        console.log(`文档已保存: ${document.fileName}`);
    }

    private onDocumentClosed(document: vscode.TextDocument) {
        const uri = document.uri.toString();
        this.documentStates.delete(uri);
        console.log(`文档已关闭: ${document.fileName}`);
    }

    private syncDocument(document: vscode.TextDocument, changes: readonly vscode.TextDocumentContentChangeEvent[]) {
        // 实现文档同步逻辑
        console.log(`同步文档: ${document.fileName}, 变化数量: ${changes.length}`);
    }

    dispose() {
        this.disposables.forEach(d => d.dispose());
        this.documentStates.clear();
    }
}

interface DocumentState {
    version: number;
    lastModified: number;
    lastSaved?: number;
    changeCount: number;
}

文档备份管理器

typescript
class DocumentBackupManager {
    private backupInterval = 30000; // 30秒
    private backupTimer?: NodeJS.Timeout;
    private disposables: vscode.Disposable[] = [];

    constructor(private backupPath: string) {
        this.setupAutoBackup();
    }

    private setupAutoBackup() {
        // 定期备份
        this.backupTimer = setInterval(() => {
            this.backupAllDocuments();
        }, this.backupInterval);

        // 监听文档保存,创建备份
        this.disposables.push(
            vscode.workspace.onDidSaveTextDocument(this.createBackup.bind(this))
        );
    }

    private async backupAllDocuments() {
        const documents = vscode.workspace.textDocuments;
        
        for (const document of documents) {
            if (document.isDirty && !document.isUntitled) {
                await this.createBackup(document);
            }
        }
    }

    private async createBackup(document: vscode.TextDocument) {
        try {
            const backupFileName = this.generateBackupFileName(document);
            const backupUri = vscode.Uri.file(path.join(this.backupPath, backupFileName));
            
            const content = document.getText();
            await vscode.workspace.fs.writeFile(backupUri, Buffer.from(content, 'utf8'));
            
            console.log(`备份已创建: ${backupFileName}`);
        } catch (error) {
            console.error('创建备份失败:', error);
        }
    }

    private generateBackupFileName(document: vscode.TextDocument): string {
        const fileName = path.basename(document.fileName);
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        return `${fileName}.${timestamp}.backup`;
    }

    dispose() {
        if (this.backupTimer) {
            clearInterval(this.backupTimer);
        }
        this.disposables.forEach(d => d.dispose());
    }
}

最佳实践

1. 性能优化

typescript
// 避免频繁的文档访问
let cachedContent: string | undefined;
let cachedVersion: number = -1;

function getDocumentContent(document: vscode.TextDocument): string {
    if (cachedVersion !== document.version) {
        cachedContent = document.getText();
        cachedVersion = document.version;
    }
    return cachedContent!;
}

2. 错误处理

typescript
async function safeDocumentOperation(document: vscode.TextDocument) {
    try {
        if (document.isClosed) {
            throw new Error('文档已关闭');
        }
        
        // 执行文档操作
        await performDocumentOperation(document);
    } catch (error) {
        vscode.window.showErrorMessage(`文档操作失败: ${error.message}`);
    }
}

3. 资源管理

typescript
// 正确管理事件监听器
const disposables: vscode.Disposable[] = [];

disposables.push(
    vscode.workspace.onDidChangeTextDocument(handler)
);

// 在适当时机清理
export function deactivate() {
    disposables.forEach(d => d.dispose());
}

相关 API

您的终极 AI 驱动 IDE 学习指南