Skip to content

代码片段 API

代码片段 API 提供了在 VS Code 中创建、管理和使用代码片段的功能。代码片段是预定义的代码模板,可以快速插入到编辑器中。

概述

代码片段是提高开发效率的重要工具,允许扩展程序:

  • 定义自定义代码片段
  • 动态生成代码片段
  • 管理片段的作用域和触发条件
  • 提供智能片段补全
  • 支持占位符和变量替换

主要接口

CompletionItemProvider

typescript
interface CompletionItemProvider {
    /**
     * 提供补全项(包括代码片段)
     */
    provideCompletionItems(
        document: TextDocument,
        position: Position,
        token: CancellationToken,
        context: CompletionContext
    ): ProviderResult<CompletionItem[] | CompletionList>;

    /**
     * 解析补全项
     */
    resolveCompletionItem?(
        item: CompletionItem,
        token: CancellationToken
    ): ProviderResult<CompletionItem>;
}

CompletionItem

typescript
class CompletionItem {
    /**
     * 补全项的标签
     */
    label: string | CompletionItemLabel;

    /**
     * 补全项的种类
     */
    kind?: CompletionItemKind;

    /**
     * 详细信息
     */
    detail?: string;

    /**
     * 文档说明
     */
    documentation?: string | MarkdownString;

    /**
     * 排序文本
     */
    sortText?: string;

    /**
     * 过滤文本
     */
    filterText?: string;

    /**
     * 预选择
     */
    preselect?: boolean;

    /**
     * 插入文本
     */
    insertText?: string | SnippetString;

    /**
     * 要替换的范围
     */
    range?: Range | { inserting: Range; replacing: Range };

    /**
     * 提交字符
     */
    commitCharacters?: string[];

    /**
     * 保持空白
     */
    keepWhitespace?: boolean;

    /**
     * 额外的文本编辑
     */
    additionalTextEdits?: TextEdit[];

    /**
     * 执行的命令
     */
    command?: Command;

    constructor(label: string, kind?: CompletionItemKind);
}

SnippetString

typescript
class SnippetString {
    /**
     * 片段的值
     */
    value: string;

    constructor(value?: string);

    /**
     * 追加文本
     */
    appendText(string: string): SnippetString;

    /**
     * 追加占位符
     */
    appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString;

    /**
     * 追加选择
     */
    appendChoice(values: string[], number?: number): SnippetString;

    /**
     * 追加变量
     */
    appendVariable(name: string, defaultValue: string | ((snippet: SnippetString) => any)): SnippetString;

    /**
     * 追加制表符停止
     */
    appendTabstop(number?: number): SnippetString;
}

创建代码片段

基本代码片段

typescript
import * as vscode from 'vscode';

class SnippetProvider implements vscode.CompletionItemProvider {
    provideCompletionItems(
        document: vscode.TextDocument,
        position: vscode.Position,
        token: vscode.CancellationToken,
        context: vscode.CompletionContext
    ): vscode.ProviderResult<vscode.CompletionItem[]> {
        const snippets: vscode.CompletionItem[] = [];

        // 创建函数片段
        const functionSnippet = new vscode.CompletionItem(
            'func',
            vscode.CompletionItemKind.Snippet
        );
        
        functionSnippet.insertText = new vscode.SnippetString(
            'function ${1:name}(${2:params}) {\n\t${0}\n}'
        );
        
        functionSnippet.documentation = new vscode.MarkdownString(
            '创建一个新函数'
        );
        
        snippets.push(functionSnippet);

        // 创建类片段
        const classSnippet = new vscode.CompletionItem(
            'class',
            vscode.CompletionItemKind.Snippet
        );
        
        classSnippet.insertText = new vscode.SnippetString([
            'class ${1:ClassName} {',
            '\tconstructor(${2:params}) {',
            '\t\t${0}',
            '\t}',
            '}'
        ].join('\n'));
        
        classSnippet.documentation = '创建一个新类';
        snippets.push(classSnippet);

        return snippets;
    }
}

动态代码片段

typescript
class DynamicSnippetProvider implements vscode.CompletionItemProvider {
    provideCompletionItems(
        document: vscode.TextDocument,
        position: vscode.Position
    ): vscode.ProviderResult<vscode.CompletionItem[]> {
        const snippets: vscode.CompletionItem[] = [];

        // 根据文件类型提供不同的片段
        switch (document.languageId) {
            case 'typescript':
                snippets.push(...this.getTypeScriptSnippets());
                break;
            case 'javascript':
                snippets.push(...this.getJavaScriptSnippets());
                break;
            case 'html':
                snippets.push(...this.getHtmlSnippets());
                break;
        }

        // 根据上下文提供片段
        const contextSnippets = this.getContextualSnippets(document, position);
        snippets.push(...contextSnippets);

        return snippets;
    }

    private getTypeScriptSnippets(): vscode.CompletionItem[] {
        const snippets: vscode.CompletionItem[] = [];

        // 接口片段
        const interfaceSnippet = new vscode.CompletionItem(
            'interface',
            vscode.CompletionItemKind.Snippet
        );
        
        interfaceSnippet.insertText = new vscode.SnippetString([
            'interface ${1:InterfaceName} {',
            '\t${2:property}: ${3:type};',
            '\t${0}',
            '}'
        ].join('\n'));
        
        snippets.push(interfaceSnippet);

        // 类型别名片段
        const typeSnippet = new vscode.CompletionItem(
            'type',
            vscode.CompletionItemKind.Snippet
        );
        
        typeSnippet.insertText = new vscode.SnippetString(
            'type ${1:TypeName} = ${0:type};'
        );
        
        snippets.push(typeSnippet);

        return snippets;
    }

    private getJavaScriptSnippets(): vscode.CompletionItem[] {
        const snippets: vscode.CompletionItem[] = [];

        // Promise 片段
        const promiseSnippet = new vscode.CompletionItem(
            'promise',
            vscode.CompletionItemKind.Snippet
        );
        
        promiseSnippet.insertText = new vscode.SnippetString([
            'new Promise((resolve, reject) => {',
            '\t${0}',
            '})'
        ].join('\n'));
        
        snippets.push(promiseSnippet);

        return snippets;
    }

    private getHtmlSnippets(): vscode.CompletionItem[] {
        const snippets: vscode.CompletionItem[] = [];

        // HTML5 模板片段
        const html5Snippet = new vscode.CompletionItem(
            'html5',
            vscode.CompletionItemKind.Snippet
        );
        
        html5Snippet.insertText = new vscode.SnippetString([
            '<!DOCTYPE html>',
            '<html lang="${1:en}">',
            '<head>',
            '\t<meta charset="UTF-8">',
            '\t<meta name="viewport" content="width=device-width, initial-scale=1.0">',
            '\t<title>${2:Document}</title>',
            '</head>',
            '<body>',
            '\t${0}',
            '</body>',
            '</html>'
        ].join('\n'));
        
        snippets.push(html5Snippet);

        return snippets;
    }

    private getContextualSnippets(
        document: vscode.TextDocument,
        position: vscode.Position
    ): vscode.CompletionItem[] {
        const snippets: vscode.CompletionItem[] = [];
        const line = document.lineAt(position.line);
        const lineText = line.text.substring(0, position.character);

        // 在类内部提供方法片段
        if (this.isInsideClass(document, position)) {
            const methodSnippet = new vscode.CompletionItem(
                'method',
                vscode.CompletionItemKind.Snippet
            );
            
            methodSnippet.insertText = new vscode.SnippetString([
                '${1:public} ${2:methodName}(${3:params}): ${4:returnType} {',
                '\t${0}',
                '}'
            ].join('\n'));
            
            snippets.push(methodSnippet);
        }

        // 在函数内部提供常用语句片段
        if (this.isInsideFunction(document, position)) {
            // console.log 片段
            const logSnippet = new vscode.CompletionItem(
                'log',
                vscode.CompletionItemKind.Snippet
            );
            
            logSnippet.insertText = new vscode.SnippetString(
                'console.log(${1:message});${0}'
            );
            
            snippets.push(logSnippet);

            // try-catch 片段
            const tryCatchSnippet = new vscode.CompletionItem(
                'try',
                vscode.CompletionItemKind.Snippet
            );
            
            tryCatchSnippet.insertText = new vscode.SnippetString([
                'try {',
                '\t${1}',
                '} catch (${2:error}) {',
                '\t${0}',
                '}'
            ].join('\n'));
            
            snippets.push(tryCatchSnippet);
        }

        return snippets;
    }

    private isInsideClass(document: vscode.TextDocument, position: vscode.Position): boolean {
        // 简单检查:向上查找 class 关键字
        for (let i = position.line; i >= 0; i--) {
            const line = document.lineAt(i);
            if (line.text.includes('class ')) {
                return true;
            }
        }
        return false;
    }

    private isInsideFunction(document: vscode.TextDocument, position: vscode.Position): boolean {
        // 简单检查:向上查找函数定义
        for (let i = position.line; i >= 0; i--) {
            const line = document.lineAt(i);
            if (line.text.includes('function ') || line.text.includes(') {')) {
                return true;
            }
        }
        return false;
    }
}

高级片段功能

带变量的片段

typescript
class VariableSnippetProvider implements vscode.CompletionItemProvider {
    provideCompletionItems(): vscode.CompletionItem[] {
        const snippets: vscode.CompletionItem[] = [];

        // 文件头注释片段
        const headerSnippet = new vscode.CompletionItem(
            'header',
            vscode.CompletionItemKind.Snippet
        );
        
        const headerSnippetString = new vscode.SnippetString();
        headerSnippetString
            .appendText('/**\n')
            .appendText(' * @file ')
            .appendVariable('TM_FILENAME', 'filename')
            .appendText('\n')
            .appendText(' * @author ')
            .appendVariable('USER', 'author')
            .appendText('\n')
            .appendText(' * @date ')
            .appendVariable('CURRENT_DATE', 'date')
            .appendText('\n')
            .appendText(' * @description ')
            .appendPlaceholder('description')
            .appendText('\n')
            .appendText(' */\n')
            .appendTabstop(0);
        
        headerSnippet.insertText = headerSnippetString;
        snippets.push(headerSnippet);

        // 当前时间片段
        const timeSnippet = new vscode.CompletionItem(
            'now',
            vscode.CompletionItemKind.Snippet
        );
        
        const timeSnippetString = new vscode.SnippetString();
        timeSnippetString
            .appendText('// Created at ')
            .appendVariable('CURRENT_DATE', 'date')
            .appendText(' ')
            .appendVariable('CURRENT_TIME', 'time')
            .appendText('\n')
            .appendTabstop(0);
        
        timeSnippet.insertText = timeSnippetString;
        snippets.push(timeSnippet);

        return snippets;
    }
}

选择片段

typescript
class ChoiceSnippetProvider implements vscode.CompletionItemProvider {
    provideCompletionItems(): vscode.CompletionItem[] {
        const snippets: vscode.CompletionItem[] = [];

        // HTTP 方法片段
        const httpSnippet = new vscode.CompletionItem(
            'http',
            vscode.CompletionItemKind.Snippet
        );
        
        const httpSnippetString = new vscode.SnippetString();
        httpSnippetString
            .appendText('app.')
            .appendChoice(['get', 'post', 'put', 'delete', 'patch'], 1)
            .appendText("('")
            .appendPlaceholder('path', 2)
            .appendText("', (req, res) => {\n\t")
            .appendTabstop(0)
            .appendText('\n});');
        
        httpSnippet.insertText = httpSnippetString;
        snippets.push(httpSnippet);

        // 日志级别片段
        const logLevelSnippet = new vscode.CompletionItem(
            'loglevel',
            vscode.CompletionItemKind.Snippet
        );
        
        const logLevelSnippetString = new vscode.SnippetString();
        logLevelSnippetString
            .appendText('console.')
            .appendChoice(['log', 'info', 'warn', 'error', 'debug'], 1)
            .appendText('(')
            .appendPlaceholder('message', 2)
            .appendText(');')
            .appendTabstop(0);
        
        logLevelSnippet.insertText = logLevelSnippetString;
        snippets.push(logLevelSnippet);

        return snippets;
    }
}

片段管理

片段注册和管理

typescript
export function activate(context: vscode.ExtensionContext) {
    // 注册不同类型的片段提供者
    const providers = [
        { selector: { scheme: 'file', language: 'typescript' }, provider: new TypeScriptSnippetProvider() },
        { selector: { scheme: 'file', language: 'javascript' }, provider: new JavaScriptSnippetProvider() },
        { selector: { scheme: 'file', language: 'html' }, provider: new HtmlSnippetProvider() },
        { selector: '*', provider: new UniversalSnippetProvider() }
    ];

    providers.forEach(({ selector, provider }) => {
        const disposable = vscode.languages.registerCompletionItemProvider(
            selector,
            provider,
            '.' // 触发字符
        );
        context.subscriptions.push(disposable);
    });

    // 注册片段管理命令
    const insertSnippetCommand = vscode.commands.registerCommand(
        'myExtension.insertSnippet',
        insertCustomSnippet
    );
    context.subscriptions.push(insertSnippetCommand);
}

async function insertCustomSnippet() {
    const snippets = [
        { label: '函数模板', snippet: 'function ${1:name}() {\n\t${0}\n}' },
        { label: '类模板', snippet: 'class ${1:Name} {\n\tconstructor() {\n\t\t${0}\n\t}\n}' },
        { label: '接口模板', snippet: 'interface ${1:Name} {\n\t${0}\n}' }
    ];

    const selected = await vscode.window.showQuickPick(
        snippets.map(s => s.label),
        { placeHolder: '选择要插入的代码片段' }
    );

    if (selected) {
        const snippet = snippets.find(s => s.label === selected);
        if (snippet) {
            const editor = vscode.window.activeTextEditor;
            if (editor) {
                await editor.insertSnippet(new vscode.SnippetString(snippet.snippet));
            }
        }
    }
}

片段配置管理

typescript
class SnippetConfigManager {
    private config: vscode.WorkspaceConfiguration;

    constructor() {
        this.config = vscode.workspace.getConfiguration('myExtension.snippets');
        
        // 监听配置变化
        vscode.workspace.onDidChangeConfiguration(event => {
            if (event.affectsConfiguration('myExtension.snippets')) {
                this.reloadConfig();
            }
        });
    }

    private reloadConfig() {
        this.config = vscode.workspace.getConfiguration('myExtension.snippets');
    }

    getCustomSnippets(): CustomSnippet[] {
        return this.config.get<CustomSnippet[]>('custom', []);
    }

    isSnippetEnabled(snippetName: string): boolean {
        const disabled = this.config.get<string[]>('disabled', []);
        return !disabled.includes(snippetName);
    }

    getSnippetPrefix(snippetName: string): string {
        const prefixes = this.config.get<Record<string, string>>('prefixes', {});
        return prefixes[snippetName] || snippetName;
    }
}

interface CustomSnippet {
    name: string;
    prefix: string;
    body: string[];
    description?: string;
    scope?: string;
}

实用示例

智能代码生成器

typescript
class SmartCodeGenerator {
    async generateComponent(componentName: string): Promise<vscode.SnippetString> {
        const snippet = new vscode.SnippetString();
        
        // 生成 React 组件
        snippet
            .appendText(`import React from 'react';\n\n`)
            .appendText(`interface ${componentName}Props {\n`)
            .appendText('\t')
            .appendPlaceholder('// props', 1)
            .appendText('\n}\n\n')
            .appendText(`const ${componentName}: React.FC<${componentName}Props> = (`)
            .appendPlaceholder('props', 2)
            .appendText(') => {\n')
            .appendText('\treturn (\n')
            .appendText('\t\t<div>\n')
            .appendText('\t\t\t')
            .appendPlaceholder('// component content', 3)
            .appendText('\n\t\t</div>\n')
            .appendText('\t);\n')
            .appendText('};\n\n')
            .appendText(`export default ${componentName};`)
            .appendTabstop(0);

        return snippet;
    }

    async generateApiEndpoint(method: string, path: string): Promise<vscode.SnippetString> {
        const snippet = new vscode.SnippetString();
        
        snippet
            .appendText(`app.${method.toLowerCase()}('${path}', async (req, res) => {\n`)
            .appendText('\ttry {\n')
            .appendText('\t\t')
            .appendPlaceholder('// implementation', 1)
            .appendText('\n\t\tres.json({ success: true });\n')
            .appendText('\t} catch (error) {\n')
            .appendText('\t\tres.status(500).json({ error: error.message });\n')
            .appendText('\t}\n')
            .appendText('});')
            .appendTabstop(0);

        return snippet;
    }
}

片段模板引擎

typescript
class SnippetTemplateEngine {
    private templates = new Map<string, SnippetTemplate>();

    registerTemplate(name: string, template: SnippetTemplate) {
        this.templates.set(name, template);
    }

    async renderTemplate(
        templateName: string,
        variables: Record<string, string>
    ): Promise<vscode.SnippetString> {
        const template = this.templates.get(templateName);
        if (!template) {
            throw new Error(`模板 ${templateName} 不存在`);
        }

        const snippet = new vscode.SnippetString();
        let content = template.content;

        // 替换变量
        for (const [key, value] of Object.entries(variables)) {
            content = content.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value);
        }

        // 处理占位符
        content = content.replace(/\$\{(\d+):([^}]+)\}/g, (match, index, placeholder) => {
            snippet.appendPlaceholder(placeholder, parseInt(index));
            return '';
        });

        snippet.appendText(content);
        return snippet;
    }
}

interface SnippetTemplate {
    name: string;
    content: string;
    variables: string[];
    description?: string;
}

// 使用示例
const engine = new SnippetTemplateEngine();

engine.registerTemplate('react-component', {
    name: 'React Component',
    content: `import React from 'react';

interface \${componentName}Props {
    \${1:// props}
}

const \${componentName}: React.FC<\${componentName}Props> = (props) => {
    return (
        <div>
            \${2:// content}
        </div>
    );
};

export default \${componentName};`,
    variables: ['componentName']
});

最佳实践

1. 性能优化

typescript
// 延迟加载复杂片段
class LazySnippetProvider implements vscode.CompletionItemProvider {
    private snippetCache = new Map<string, vscode.CompletionItem[]>();

    provideCompletionItems(
        document: vscode.TextDocument,
        position: vscode.Position
    ): vscode.ProviderResult<vscode.CompletionItem[]> {
        const cacheKey = `${document.languageId}-${position.line}`;
        
        if (this.snippetCache.has(cacheKey)) {
            return this.snippetCache.get(cacheKey);
        }

        const snippets = this.generateSnippets(document, position);
        this.snippetCache.set(cacheKey, snippets);
        
        return snippets;
    }
}

2. 用户体验

typescript
// 提供有意义的片段描述
function createSnippetWithDocumentation(
    label: string,
    snippet: string,
    description: string
): vscode.CompletionItem {
    const item = new vscode.CompletionItem(label, vscode.CompletionItemKind.Snippet);
    item.insertText = new vscode.SnippetString(snippet);
    item.documentation = new vscode.MarkdownString(description);
    item.detail = '代码片段';
    return item;
}

3. 可配置性

typescript
// 允许用户自定义片段
function loadUserSnippets(): vscode.CompletionItem[] {
    const config = vscode.workspace.getConfiguration('myExtension');
    const userSnippets = config.get<any[]>('customSnippets', []);
    
    return userSnippets.map(snippet => {
        const item = new vscode.CompletionItem(
            snippet.prefix,
            vscode.CompletionItemKind.Snippet
        );
        item.insertText = new vscode.SnippetString(snippet.body.join('\n'));
        item.documentation = snippet.description;
        return item;
    });
}

相关 API

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