代码片段 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;
});
}