活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

VS Code插件扩展开发教程手把手教你打造专属编辑器增强开发体验提高工作效率掌握核心技术轻松应对复杂项目

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-20 20:20:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

Visual Studio Code(简称VS Code)作为当前最流行的代码编辑器之一,其强大的扩展性是吸引开发者的重要因素。通过开发自定义插件,我们可以根据个人或团队需求定制编辑器功能,极大地提升开发效率。本教程将手把手教你从零开始开发VS Code插件,帮助你掌握核心技术,打造专属的编辑器增强功能,轻松应对复杂项目。

1. VS Code插件开发基础

1.1 什么是VS Code插件

VS Code插件(Extension)是一种可以扩展编辑器功能的软件模块。通过插件,你可以:

• 添加新的语言支持
• 提供代码补全、 linting、格式化功能
• 创建主题和快捷键
• 添加调试器支持
• 自定义工作区界面
• 集成外部工具和服务

1.2 插件的工作原理

VS Code插件基于Node.js环境,主要使用JavaScript或TypeScript开发。插件通过VS Code提供的扩展API与编辑器交互,这些API允许你访问编辑器的各个功能模块,如文件系统、窗口管理、命令系统等。

2. 开发环境搭建

2.1 前置条件

在开始VS Code插件开发之前,确保你的系统已安装以下软件:

• Node.js(建议版本14.x或更高)
• Visual Studio Code最新版
• Git(用于版本控制)

2.2 安装必要的工具

VS Code官方提供了一套用于插件开发的工具链,主要包含:

1. Yeoman:用于生成项目脚手架
2. VS Code Extension Generator:VS Code插件项目生成器

通过以下命令安装这些工具:
  1. npm install -g yo generator-code
复制代码

2.3 创建第一个插件项目

1. 打开终端,运行以下命令启动插件生成器:
  1. yo code
复制代码

1. 回答一系列问题来配置你的插件:选择插件类型(TypeScript或JavaScript)输入插件名称输入插件标识符(通常是小写字母,无空格)输入插件描述初始化Git仓库包管理器选择(npm或yarn)
2. 选择插件类型(TypeScript或JavaScript)
3. 输入插件名称
4. 输入插件标识符(通常是小写字母,无空格)
5. 输入插件描述
6. 初始化Git仓库
7. 包管理器选择(npm或yarn)
8. 生成项目后,进入项目目录:

回答一系列问题来配置你的插件:

• 选择插件类型(TypeScript或JavaScript)
• 输入插件名称
• 输入插件标识符(通常是小写字母,无空格)
• 输入插件描述
• 初始化Git仓库
• 包管理器选择(npm或yarn)

生成项目后,进入项目目录:
  1. cd your-extension-name
复制代码

1. 在VS Code中打开项目:
  1. code .
复制代码

3. 插件项目结构解析

创建完成后,你的插件项目结构通常如下:
  1. .
  2. ├── .vscode/
  3. │   ├── launch.json     // 调试配置
  4. │   ├── tasks.json      // 任务配置
  5. │   └── extensions.json // 推荐插件列表
  6. ├── src/
  7. │   └── extension.ts    // 插件主入口文件
  8. ├── package.json        // 插件配置文件
  9. ├── README.md           // 插件说明文档
  10. ├── CHANGELOG.md        // 更新日志
  11. ├── vsc-extension-quickstart.md // 快速开始指南
  12. └── tsconfig.json       // TypeScript配置文件
复制代码

3.1 package.json 文件详解

package.json是插件的核心配置文件,定义了插件的基本信息、依赖项以及VS Code特定的配置:
  1. {
  2.   "name": "my-first-extension",
  3.   "displayName": "My First Extension",
  4.   "description": "A sample extension to demonstrate VS Code extensibility",
  5.   "version": "0.0.1",
  6.   "engines": {
  7.     "vscode": "^1.60.0"
  8.   },
  9.   "categories": [
  10.     "Other"
  11.   ],
  12.   "activationEvents": [
  13.     "onCommand:my-first-extension.helloWorld"
  14.   ],
  15.   "main": "./out/extension.js",
  16.   "scripts": {
  17.     "compile": "tsc -p ./",
  18.     "watch": "tsc -w",
  19.     "pretest": "npm run compile && npm run lint",
  20.     "lint": "eslint src --ext ts",
  21.     "test": "node ./out/test/runTest.js",
  22.     "vscode:prepublish": "npm run compile"
  23.   },
  24.   "devDependencies": {
  25.     "@types/vscode": "^1.60.0",
  26.     "@types/node": "^14.x",
  27.     "eslint": "^7.32.0",
  28.     "typescript": "^4.4.4"
  29.   },
  30.   "contributes": {
  31.     "commands": [
  32.       {
  33.         "command": "my-first-extension.helloWorld",
  34.         "title": "Hello World"
  35.       }
  36.     ]
  37.   }
  38. }
复制代码

关键配置项说明:

• engines.vscode:指定插件兼容的VS Code版本
• activationEvents:定义插件激活的时机
• main:指定插件入口文件
• contributes:定义插件贡献给VS Code的功能点

3.2 插件入口文件

src/extension.ts是插件的主入口文件,包含插件的激活和停用逻辑:
  1. import * as vscode from 'vscode';
  2. // 插件激活时调用的函数
  3. export function activate(context: vscode.ExtensionContext) {
  4.     console.log('Congratulations, your extension "my-first-extension" is now active!');
  5.     // 注册一个命令
  6.     let disposable = vscode.commands.registerCommand('my-first-extension.helloWorld', () => {
  7.         vscode.window.showInformationMessage('Hello World from My First Extension!');
  8.     });
  9.     context.subscriptions.push(disposable);
  10. }
  11. // 插件停用时调用的函数
  12. export function deactivate() {}
复制代码

4. 核心API和概念详解

4.1 插件生命周期

VS Code插件的生命周期主要包括两个阶段:激活(activate)和停用(deactivate)。

• 激活:当满足activationEvents中定义的条件时,VS Code会调用插件的activate函数
• 停用:当VS Code关闭或插件被禁用时,会调用deactivate函数

4.2 常见的激活事件
  1. "activationEvents": [
  2.     "onLanguage:javascript",  // 当打开JavaScript文件时激活
  3.     "onCommand:extension.sayHello", // 当执行特定命令时激活
  4.     "workspaceContains:**/.vscode/settings.json", // 当工作区包含特定文件时激活
  5.     "onView:nodeDependencies", // 当打开特定视图时激活
  6.     "*" // 启动时立即激活(不推荐,会影响性能)
  7. ]
复制代码

4.3 VS Code API概览

VS Code提供了丰富的API,主要模块包括:

• vscode.commands:命令注册和执行
• vscode.window:UI交互(消息框、输入框、快速选择等)
• vscode.workspace:工作区操作(文件、文件夹、配置等)
• vscode.languages:语言相关功能(文档高亮、智能提示、格式化等)
• vscode.debug:调试功能
• vscode.tasks:任务执行
• vscode scm:源代码管理

5. 常见功能实现

5.1 注册和执行命令

命令是VS Code插件中最基础的功能,通过命令可以绑定快捷键、菜单项等。
  1. // 注册命令
  2. const disposable = vscode.commands.registerCommand('extension.myCommand', () => {
  3.     vscode.window.showInformationMessage('Command executed!');
  4. });
  5. context.subscriptions.push(disposable);
  6. // 执行命令
  7. vscode.commands.executeCommand('extension.anotherCommand');
复制代码

5.2 添加菜单项和快捷键

在package.json中的contributes部分添加菜单和快捷键配置:
  1. "contributes": {
  2.     "commands": [
  3.         {
  4.             "command": "extension.showHelloWorld",
  5.             "title": "Hello World"
  6.         }
  7.     ],
  8.     "menus": {
  9.         "editor/context": [
  10.             {
  11.                 "command": "extension.showHelloWorld",
  12.                 "when": "editorHasSelection",
  13.                 "group": "myGroup@1"
  14.             }
  15.         ],
  16.         "commandPalette": [
  17.             {
  18.                 "command": "extension.showHelloWorld",
  19.                 "when": "false" // 不在命令面板显示
  20.             }
  21.         ]
  22.     },
  23.     "keybindings": [
  24.         {
  25.             "command": "extension.showHelloWorld",
  26.             "key": "ctrl+h",
  27.             "mac": "cmd+h",
  28.             "when": "editorTextFocus"
  29.         }
  30.     ]
  31. }
复制代码

5.3 创建自定义视图

自定义视图可以让你在活动栏中添加新的视图容器,并在其中展示内容。
  1. // 在extension.ts中
  2. import * as vscode from 'vscode';
  3. export function activate(context: vscode.ExtensionContext) {
  4.     // 注册树形视图
  5.     const treeDataProvider = new MyTreeDataProvider();
  6.     vscode.window.registerTreeDataProvider('myView', treeDataProvider);
  7.     // 注册视图
  8.     vscode.window.createTreeView('myView', { treeDataProvider });
  9. }
  10. class MyTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
  11.     getTreeItem(element: TreeItem): vscode.TreeItem {
  12.         return element;
  13.     }
  14.     getChildren(element?: TreeItem): Thenable<TreeItem[]> {
  15.         if (!element) {
  16.             // 返回根节点
  17.             return Promise.resolve([
  18.                 new TreeItem('Item 1', vscode.TreeItemCollapsibleState.None),
  19.                 new TreeItem('Item 2', vscode.TreeItemCollapsibleState.Collapsed)
  20.             ]);
  21.         } else {
  22.             // 返回子节点
  23.             return Promise.resolve([
  24.                 new TreeItem('Child Item 1', vscode.TreeItemCollapsibleState.None),
  25.                 new TreeItem('Child Item 2', vscode.TreeItemCollapsibleState.None)
  26.             ]);
  27.         }
  28.     }
  29. }
  30. class TreeItem extends vscode.TreeItem {
  31.     constructor(
  32.         public readonly label: string,
  33.         public readonly collapsibleState: vscode.TreeItemCollapsibleState
  34.     ) {
  35.         super(label, collapsibleState);
  36.     }
  37. }
复制代码

在package.json中添加视图配置:
  1. "contributes": {
  2.     "views": {
  3.         "explorer": [
  4.             {
  5.                 "id": "myView",
  6.                 "name": "My View",
  7.                 "when": "workbenchState == workspace"
  8.             }
  9.         ]
  10.     }
  11. }
复制代码

5.4 处理文本编辑器

VS Code API提供了丰富的文本编辑器操作功能:
  1. // 获取当前活动的文本编辑器
  2. const editor = vscode.window.activeTextEditor;
  3. if (editor) {
  4.     const document = editor.document;
  5.     const selection = editor.selection;
  6.    
  7.     // 获取选中的文本
  8.     const selectedText = document.getText(selection);
  9.    
  10.     // 替换选中的文本
  11.     editor.edit(editBuilder => {
  12.         editBuilder.replace(selection, 'New text');
  13.     });
  14.    
  15.     // 在当前位置插入文本
  16.     const position = editor.selection.active;
  17.     editor.edit(editBuilder => {
  18.         editBuilder.insert(position, 'Inserted text');
  19.     });
  20.    
  21.     // 获取整个文档内容
  22.     const fullText = document.getText();
  23.    
  24.     // 修改文档内容
  25.     editor.edit(editBuilder => {
  26.         const lastLine = document.lineAt(document.lineCount - 1);
  27.         const text = lastLine.text;
  28.         editBuilder.replace(lastLine.range, text + ' (modified)');
  29.     });
  30. }
复制代码

5.5 实现代码补全

代码补全是提高开发效率的重要功能,可以通过实现CompletionItemProvider接口来提供自定义的代码补全:
  1. // 注册代码补全提供者
  2. const provider: vscode.CompletionItemProvider = {
  3.     provideCompletionItems(
  4.         document: vscode.TextDocument,
  5.         position: vscode.Position,
  6.         token: vscode.CancellationToken,
  7.         context: vscode.CompletionContext
  8.     ): vscode.CompletionItem[] | vscode.ProviderResult<vscode.CompletionItem[]> {
  9.         // 简单的代码补全示例
  10.         const linePrefix = document.lineAt(position).text.substring(0, position.character);
  11.         if (!linePrefix.endsWith('hello.')) {
  12.             return undefined;
  13.         }
  14.         
  15.         return [
  16.             new vscode.CompletionItem('world', vscode.CompletionItemKind.Keyword),
  17.             new vscode.CompletionItem('vscode', vscode.CompletionItemKind.Keyword)
  18.         ];
  19.     }
  20. };
  21. context.subscriptions.push(
  22.     vscode.languages.registerCompletionItemProvider(
  23.         { scheme: 'file', language: 'javascript' },
  24.         provider,
  25.         '.' // 触发补全的字符
  26.     )
  27. );
复制代码

5.6 实现悬停提示

悬停提示可以帮助开发者更好地理解代码:
  1. // 注册悬停提示提供者
  2. const hoverProvider: vscode.HoverProvider = {
  3.     provideHover(
  4.         document: vscode.TextDocument,
  5.         position: vscode.Position,
  6.         token: vscode.CancellationToken
  7.     ): vscode.ProviderResult<vscode.Hover> {
  8.         const range = document.getWordRangeAtPosition(position);
  9.         const word = document.getText(range);
  10.         
  11.         if (word === 'myFunction') {
  12.             const contents = new vscode.MarkdownString();
  13.             contents.appendMarkdown('# myFunction\n\n');
  14.             contents.appendMarkdown('This is a custom function.\n\n');
  15.             contents.appendMarkdown('```javascript\n');
  16.             contents.appendMarkdown('function myFunction(param1, param2) {\n');
  17.             contents.appendMarkdown('    // Function implementation\n');
  18.             contents.appendMarkdown('}\n');
  19.             contents.appendMarkdown('```');
  20.             
  21.             return new vscode.Hover(contents);
  22.         }
  23.         
  24.         return null;
  25.     }
  26. };
  27. context.subscriptions.push(
  28.     vscode.languages.registerHoverProvider(
  29.         { scheme: 'file', language: 'javascript' },
  30.         hoverProvider
  31.     )
  32. );
复制代码

5.7 实现代码格式化

代码格式化可以帮助保持代码风格一致:
  1. // 注册文档格式化提供者
  2. const formatter: vscode.DocumentFormattingEditProvider = {
  3.     provideDocumentFormattingEdits(
  4.         document: vscode.TextDocument,
  5.         options: vscode.FormattingOptions,
  6.         token: vscode.CancellationToken
  7.     ): vscode.ProviderResult<vscode.TextEdit[]> {
  8.         const firstLine = document.lineAt(0);
  9.         if (firstLine.text !== '// formatted') {
  10.             return [vscode.TextEdit.insert(firstLine.range.start, '// formatted\n')];
  11.         }
  12.         return [];
  13.     }
  14. };
  15. context.subscriptions.push(
  16.     vscode.languages.registerDocumentFormattingEditProvider(
  17.         { scheme: 'file', language: 'javascript' },
  18.         formatter
  19.     )
  20. );
复制代码

5.8 实现代码诊断

代码诊断可以在编辑器中显示错误和警告信息:
  1. // 创建诊断集合
  2. const diagnosticCollection = vscode.languages.createDiagnosticCollection('myExtension');
  3. // 更新诊断信息
  4. function updateDiagnostics(document: vscode.TextDocument) {
  5.     if (document.languageId !== 'javascript') {
  6.         return;
  7.     }
  8.    
  9.     const diagnostics: vscode.Diagnostic[] = [];
  10.     const text = document.getText();
  11.    
  12.     // 简单的示例:检测console.log的使用
  13.     const regex = /console\.log\(/g;
  14.     let match;
  15.     while ((match = regex.exec(text)) !== null) {
  16.         const startIndex = match.index;
  17.         const endIndex = startIndex + match[0].length;
  18.         const range = new vscode.Range(
  19.             document.positionAt(startIndex),
  20.             document.positionAt(endIndex)
  21.         );
  22.         
  23.         const diagnostic = new vscode.Diagnostic(
  24.             range,
  25.             'Avoid using console.log in production code',
  26.             vscode.DiagnosticSeverity.Warning
  27.         );
  28.         
  29.         diagnostics.push(diagnostic);
  30.     }
  31.    
  32.     diagnosticCollection.set(document.uri, diagnostics);
  33. }
  34. // 监听文档变化
  35. vscode.workspace.onDidChangeTextDocument(event => {
  36.     updateDiagnostics(event.document);
  37. });
  38. // 监听活动编辑器变化
  39. vscode.window.onDidChangeActiveTextEditor(editor => {
  40.     if (editor) {
  41.         updateDiagnostics(editor.document);
  42.     }
  43. });
复制代码

6. 调试和发布插件

6.1 调试插件

VS Code提供了便捷的插件调试功能:

1. 打开插件项目
2. 按F5或点击”Run”菜单中的”Start Debugging”
3. 这将启动一个新的VS Code实例(扩展开发主机),你的插件将在这个实例中运行
4. 在新实例中测试你的插件功能
5. 在原始实例中设置断点并调试代码

6.2 打包插件

插件开发完成后,可以使用vsce(Visual Studio Code Extensions)工具将其打包:

1. 安装vsce:
  1. npm install -g @vscode/vsce
复制代码

1. 打包插件:
  1. vsce package
复制代码

这将生成一个.vsix文件,这是VS Code插件的安装包格式。

6.3 发布插件

如果你想在VS Code市场发布插件,需要:

1. 创建一个Visual Studio Team Services账户
2. 获取发布者ID
3. 使用vsce登录:
  1. vsce login publisher-name
复制代码

1. 发布插件:
  1. vsce publish
复制代码

7. 实战案例:开发一个代码片段管理插件

让我们通过一个完整的实例来巩固所学知识。我们将开发一个代码片段管理插件,允许用户保存、管理和插入常用的代码片段。

7.1 项目初始化

首先,使用Yeoman生成器创建一个新的TypeScript插件项目:
  1. yo code
复制代码

选择以下选项:

• What type of extension do you want to create?-New Extension (TypeScript)
• What's the name of your extension?-code-snippet-manager
• What's the identifier of your extension?-code-snippet-manager
• What's the description of your extension?-A simple code snippet manager
• Initialize a git repository?-Yes
• Bundle the source code with webpack?-No
• Which package manager to use?-npm

7.2 设计插件功能

我们的代码片段管理插件将包含以下功能:

1. 添加新的代码片段
2. 查看所有代码片段
3. 插入选中的代码片段
4. 删除代码片段
5. 编辑代码片段

7.3 实现数据存储

首先,我们需要一个地方来存储代码片段。VS Code提供了Memento接口,用于存储简单的键值对数据:
  1. // src/storage.ts
  2. import * as vscode from 'vscode';
  3. export interface CodeSnippet {
  4.     id: string;
  5.     name: string;
  6.     code: string;
  7.     language: string;
  8.     description?: string;
  9. }
  10. export class SnippetStorage {
  11.     private static readonly SNIPPETS_KEY = 'codeSnippets';
  12.     static getSnippets(context: vscode.ExtensionContext): CodeSnippet[] {
  13.         const snippetsJson = context.globalState.get<string>(this.SNIPPETS_KEY, '[]');
  14.         return JSON.parse(snippetsJson);
  15.     }
  16.     static saveSnippets(context: vscode.ExtensionContext, snippets: CodeSnippet[]): Thenable<void> {
  17.         return context.globalState.update(this.SNIPPETS_KEY, JSON.stringify(snippets));
  18.     }
  19.     static addSnippet(context: vscode.ExtensionContext, snippet: CodeSnippet): Thenable<void> {
  20.         const snippets = this.getSnippets(context);
  21.         snippets.push(snippet);
  22.         return this.saveSnippets(context, snippets);
  23.     }
  24.     static updateSnippet(context: vscode.ExtensionContext, snippet: CodeSnippet): Thenable<void> {
  25.         const snippets = this.getSnippets(context);
  26.         const index = snippets.findIndex(s => s.id === snippet.id);
  27.         if (index !== -1) {
  28.             snippets[index] = snippet;
  29.             return this.saveSnippets(context, snippets);
  30.         }
  31.         return Promise.resolve();
  32.     }
  33.     static deleteSnippet(context: vscode.ExtensionContext, id: string): Thenable<void> {
  34.         const snippets = this.getSnippets(context);
  35.         const filteredSnippets = snippets.filter(s => s.id !== id);
  36.         return this.saveSnippets(context, filteredSnippets);
  37.     }
  38. }
复制代码

7.4 实现树形视图

我们需要一个树形视图来显示所有代码片段:
  1. // src/snippetTreeProvider.ts
  2. import * as vscode from 'vscode';
  3. import { CodeSnippet, SnippetStorage } from './storage';
  4. export class SnippetTreeProvider implements vscode.TreeDataProvider<CodeSnippetItem> {
  5.     private _onDidChangeTreeData: vscode.EventEmitter<CodeSnippetItem | undefined | null | void> = new vscode.EventEmitter<CodeSnippetItem | undefined | null | void>();
  6.     readonly onDidChangeTreeData: vscode.Event<CodeSnippetItem | undefined | null | void> = this._onDidChangeTreeData.event;
  7.     constructor(private context: vscode.ExtensionContext) {}
  8.     refresh(): void {
  9.         this._onDidChangeTreeData.fire();
  10.     }
  11.     getTreeItem(element: CodeSnippetItem): vscode.TreeItem {
  12.         return element;
  13.     }
  14.     getChildren(element?: CodeSnippetItem): Thenable<CodeSnippetItem[]> {
  15.         if (!element) {
  16.             // 返回所有代码片段
  17.             const snippets = SnippetStorage.getSnippets(this.context);
  18.             return Promise.resolve(snippets.map(snippet => new CodeSnippetItem(
  19.                 snippet.name,
  20.                 snippet,
  21.                 vscode.TreeItemCollapsibleState.None
  22.             )));
  23.         }
  24.         return Promise.resolve([]);
  25.     }
  26. }
  27. export class CodeSnippetItem extends vscode.TreeItem {
  28.     constructor(
  29.         public readonly label: string,
  30.         public readonly snippet: CodeSnippet,
  31.         public readonly collapsibleState: vscode.TreeItemCollapsibleState
  32.     ) {
  33.         super(label, collapsibleState);
  34.         this.tooltip = `${snippet.name} (${snippet.language})`;
  35.         this.description = snippet.description;
  36.         this.contextValue = 'snippet';
  37.         this.command = {
  38.             command: 'code-snippet-manager.insertSnippet',
  39.             title: 'Insert Snippet',
  40.             arguments: [snippet]
  41.         };
  42.     }
  43. }
复制代码

7.5 实现命令

接下来,实现各种命令:
  1. // src/commands.ts
  2. import * as vscode from 'vscode';
  3. import { CodeSnippet, SnippetStorage } from './storage';
  4. import { SnippetTreeProvider } from './snippetTreeProvider';
  5. export function registerCommands(context: vscode.ExtensionContext, treeDataProvider: SnippetTreeProvider) {
  6.     // 添加新代码片段
  7.     const addSnippetDisposable = vscode.commands.registerCommand('code-snippet-manager.addSnippet', async () => {
  8.         const name = await vscode.window.showInputBox({ prompt: 'Enter snippet name' });
  9.         if (!name) return;
  10.         const code = await vscode.window.showInputBox({ prompt: 'Enter snippet code' });
  11.         if (!code) return;
  12.         const language = await vscode.window.showQuickPick([
  13.             'javascript', 'typescript', 'html', 'css', 'json', 'python', 'java', 'csharp', 'cpp'
  14.         ], { placeHolder: 'Select language' });
  15.         if (!language) return;
  16.         const description = await vscode.window.showInputBox({ prompt: 'Enter snippet description (optional)' });
  17.         const snippet: CodeSnippet = {
  18.             id: Date.now().toString(),
  19.             name,
  20.             code,
  21.             language,
  22.             description
  23.         };
  24.         await SnippetStorage.addSnippet(context, snippet);
  25.         treeDataProvider.refresh();
  26.         vscode.window.showInformationMessage('Snippet added successfully!');
  27.     });
  28.     // 插入代码片段
  29.     const insertSnippetDisposable = vscode.commands.registerCommand('code-snippet-manager.insertSnippet', async (snippet: CodeSnippet) => {
  30.         const editor = vscode.window.activeTextEditor;
  31.         if (!editor) {
  32.             vscode.window.showErrorMessage('No active editor');
  33.             return;
  34.         }
  35.         editor.edit(editBuilder => {
  36.             editBuilder.insert(editor.selection.active, snippet.code);
  37.         });
  38.     });
  39.     // 删除代码片段
  40.     const deleteSnippetDisposable = vscode.commands.registerCommand('code-snippet-manager.deleteSnippet', async (item: any) => {
  41.         const snippet = item.snippet as CodeSnippet;
  42.         const confirm = await vscode.window.showWarningMessage(
  43.             `Are you sure you want to delete "${snippet.name}"?`,
  44.             { modal: true },
  45.             'Delete'
  46.         );
  47.         if (confirm === 'Delete') {
  48.             await SnippetStorage.deleteSnippet(context, snippet.id);
  49.             treeDataProvider.refresh();
  50.             vscode.window.showInformationMessage('Snippet deleted successfully!');
  51.         }
  52.     });
  53.     // 编辑代码片段
  54.     const editSnippetDisposable = vscode.commands.registerCommand('code-snippet-manager.editSnippet', async (item: any) => {
  55.         const snippet = item.snippet as CodeSnippet;
  56.         
  57.         const name = await vscode.window.showInputBox({
  58.             prompt: 'Enter snippet name',
  59.             value: snippet.name
  60.         });
  61.         if (!name) return;
  62.         const code = await vscode.window.showInputBox({
  63.             prompt: 'Enter snippet code',
  64.             value: snippet.code
  65.         });
  66.         if (!code) return;
  67.         const language = await vscode.window.showQuickPick([
  68.             'javascript', 'typescript', 'html', 'css', 'json', 'python', 'java', 'csharp', 'cpp'
  69.         ], { placeHolder: 'Select language', placeHolder: snippet.language });
  70.         if (!language) return;
  71.         const description = await vscode.window.showInputBox({
  72.             prompt: 'Enter snippet description (optional)',
  73.             value: snippet.description || ''
  74.         });
  75.         const updatedSnippet: CodeSnippet = {
  76.             ...snippet,
  77.             name,
  78.             code,
  79.             language,
  80.             description
  81.         };
  82.         await SnippetStorage.updateSnippet(context, updatedSnippet);
  83.         treeDataProvider.refresh();
  84.         vscode.window.showInformationMessage('Snippet updated successfully!');
  85.     });
  86.     context.subscriptions.push(
  87.         addSnippetDisposable,
  88.         insertSnippetDisposable,
  89.         deleteSnippetDisposable,
  90.         editSnippetDisposable
  91.     );
  92. }
复制代码

7.6 整合所有功能

现在,让我们在主入口文件中整合所有功能:
  1. // src/extension.ts
  2. import * as vscode from 'vscode';
  3. import { SnippetTreeProvider, CodeSnippetItem } from './snippetTreeProvider';
  4. import { registerCommands } from './commands';
  5. export function activate(context: vscode.ExtensionContext) {
  6.     console.log('Code Snippet Manager is now active!');
  7.     // 创建树形视图数据提供者
  8.     const treeDataProvider = new SnippetTreeProvider(context);
  9.    
  10.     // 注册树形视图
  11.     vscode.window.registerTreeDataProvider('codeSnippets', treeDataProvider);
  12.    
  13.     // 创建视图
  14.     const treeView = vscode.window.createTreeView('codeSnippets', {
  15.         treeDataProvider,
  16.         showCollapseAll: true
  17.     });
  18.    
  19.     // 注册命令
  20.     registerCommands(context, treeDataProvider);
  21. }
  22. export function deactivate() {}
复制代码

7.7 更新package.json

最后,更新package.json以添加必要的配置:
  1. {
  2.   "name": "code-snippet-manager",
  3.   "displayName": "Code Snippet Manager",
  4.   "description": "A simple code snippet manager",
  5.   "version": "0.0.1",
  6.   "engines": {
  7.     "vscode": "^1.60.0"
  8.   },
  9.   "categories": [
  10.     "Other"
  11.   ],
  12.   "activationEvents": [
  13.     "onView:codeSnippets"
  14.   ],
  15.   "main": "./out/extension.js",
  16.   "scripts": {
  17.     "compile": "tsc -p ./",
  18.     "watch": "tsc -w",
  19.     "pretest": "npm run compile && npm run lint",
  20.     "lint": "eslint src --ext ts",
  21.     "test": "node ./out/test/runTest.js",
  22.     "vscode:prepublish": "npm run compile"
  23.   },
  24.   "devDependencies": {
  25.     "@types/vscode": "^1.60.0",
  26.     "@types/node": "^14.x",
  27.     "eslint": "^7.32.0",
  28.     "typescript": "^4.4.4"
  29.   },
  30.   "contributes": {
  31.     "commands": [
  32.       {
  33.         "command": "code-snippet-manager.addSnippet",
  34.         "title": "Add New Snippet",
  35.         "icon": "$(add)"
  36.       },
  37.       {
  38.         "command": "code-snippet-manager.insertSnippet",
  39.         "title": "Insert Snippet"
  40.       },
  41.       {
  42.         "command": "code-snippet-manager.deleteSnippet",
  43.         "title": "Delete Snippet",
  44.         "icon": "$(trash)"
  45.       },
  46.       {
  47.         "command": "code-snippet-manager.editSnippet",
  48.         "title": "Edit Snippet",
  49.         "icon": "$(edit)"
  50.       }
  51.     ],
  52.     "menus": {
  53.       "view/title": [
  54.         {
  55.           "command": "code-snippet-manager.addSnippet",
  56.           "when": "view == codeSnippets",
  57.           "group": "navigation"
  58.         }
  59.       ],
  60.       "view/item/context": [
  61.         {
  62.           "command": "code-snippet-manager.insertSnippet",
  63.           "when": "view == codeSnippets && viewItem == snippet",
  64.           "group": "inline"
  65.         },
  66.         {
  67.           "command": "code-snippet-manager.editSnippet",
  68.           "when": "view == codeSnippets && viewItem == snippet",
  69.           "group": "inline"
  70.         },
  71.         {
  72.           "command": "code-snippet-manager.deleteSnippet",
  73.           "when": "view == codeSnippets && viewItem == snippet",
  74.           "group": "inline"
  75.         }
  76.       ]
  77.     },
  78.     "views": {
  79.       "explorer": [
  80.         {
  81.           "id": "codeSnippets",
  82.           "name": "Code Snippets",
  83.           "when": "workbenchState == workspace"
  84.         }
  85.       ]
  86.     },
  87.     "viewsWelcome": [
  88.       {
  89.         "view": "codeSnippets",
  90.         "contents": "No snippets found yet. [Add a new snippet](command:code-snippet-manager.addSnippet) to get started."
  91.       }
  92.     ]
  93.   }
  94. }
复制代码

7.8 测试插件

现在,我们可以测试我们的插件了:

1. 按F5启动调试
2. 在新打开的VS Code窗口中,你应该能在资源管理器中看到”Code Snippets”视图
3. 点击视图标题栏上的”+“按钮添加新的代码片段
4. 右键点击代码片段可以编辑、删除或插入代码片段

8. 最佳实践和性能优化

8.1 插件性能优化

1. 延迟加载:只在需要时加载插件功能,使用activationEvents控制激活时机
  1. "activationEvents": [
  2.     "onLanguage:javascript",
  3.     "onCommand:extension.myCommand"
  4. ]
复制代码

1. 避免阻塞UI线程:将耗时操作放在异步函数中执行
  1. async function processLargeData() {
  2.     // 使用setTimeout分批处理大量数据
  3.     const data = getLargeDataSet();
  4.     const batchSize = 100;
  5.    
  6.     for (let i = 0; i < data.length; i += batchSize) {
  7.         const batch = data.slice(i, i + batchSize);
  8.         processBatch(batch);
  9.         
  10.         // 让UI有机会更新
  11.         await new Promise(resolve => setTimeout(resolve, 0));
  12.     }
  13. }
复制代码

1. 缓存计算结果:避免重复计算
  1. class CacheManager {
  2.     private static cache = new Map<string, any>();
  3.    
  4.     static get(key: string): any {
  5.         return this.cache.get(key);
  6.     }
  7.    
  8.     static set(key: string, value: any): void {
  9.         this.cache.set(key, value);
  10.     }
  11.    
  12.     static clear(): void {
  13.         this.cache.clear();
  14.     }
  15. }
复制代码

8.2 代码组织最佳实践

1. 模块化设计:将功能拆分为多个模块
  1. src/
  2. ├── extension.ts          // 主入口文件
  3. ├── commands/             // 命令模块
  4. │   ├── index.ts
  5. │   ├── snippetCommands.ts
  6. │   └── editorCommands.ts
  7. ├── providers/            // 提供者模块
  8. │   ├── index.ts
  9. │   ├── completionProvider.ts
  10. │   └── hoverProvider.ts
  11. ├── models/              // 数据模型
  12. │   ├── index.ts
  13. │   └── snippet.ts
  14. └── utils/               // 工具函数
  15.     ├── index.ts
  16.     └── logger.ts
复制代码

1. 使用TypeScript类型:充分利用TypeScript的类型系统
  1. interface CodeSnippet {
  2.     id: string;
  3.     name: string;
  4.     code: string;
  5.     language: string;
  6.     description?: string;
  7. }
  8. type Language = 'javascript' | 'typescript' | 'html' | 'css' | 'json' | 'python' | 'java' | 'csharp' | 'cpp';
复制代码

1. 错误处理:妥善处理可能出现的错误
  1. async function safeExecute(operation: () => Promise<any>, errorMessage: string): Promise<void> {
  2.     try {
  3.         await operation();
  4.     } catch (error) {
  5.         vscode.window.showErrorMessage(`${errorMessage}: ${error.message}`);
  6.         console.error(error);
  7.     }
  8. }
复制代码

8.3 用户体验优化

1. 提供进度反馈:对于耗时操作,显示进度通知
  1. async function longRunningOperation() {
  2.     vscode.window.withProgress({
  3.         location: vscode.ProgressLocation.Notification,
  4.         title: "Processing data...",
  5.         cancellable: true
  6.     }, async (progress, token) => {
  7.         token.onCancellationRequested(() => {
  8.             console.log("User canceled the long running operation");
  9.         });
  10.         
  11.         const totalSteps = 100;
  12.         for (let i = 0; i <= totalSteps; i++) {
  13.             progress.report({ increment: 1, message: `${i} of ${totalSteps} steps completed` });
  14.             await new Promise(resolve => setTimeout(resolve, 100));
  15.             
  16.             if (token.isCancellationRequested) {
  17.                 return;
  18.             }
  19.         }
  20.     });
  21. }
复制代码

1. 使用快速选择:提供友好的选择界面
  1. async function selectFile() {
  2.     const files = await getFiles();
  3.     const selected = await vscode.window.showQuickPick(
  4.         files.map(file => ({
  5.             label: file.name,
  6.             description: file.path,
  7.             detail: `${file.size} bytes`,
  8.             file: file
  9.         })),
  10.         {
  11.             placeHolder: 'Select a file',
  12.             matchOnDetail: true,
  13.             matchOnDescription: true
  14.         }
  15.     );
  16.    
  17.     if (selected) {
  18.         // 处理选中的文件
  19.         processFile(selected.file);
  20.     }
  21. }
复制代码

1. 配置选项:允许用户自定义插件行为
  1. "contributes": {
  2.     "configuration": {
  3.         "title": "Code Snippet Manager",
  4.         "properties": {
  5.             "codeSnippetManager.storageLocation": {
  6.                 "type": "string",
  7.                 "default": "global",
  8.                 "enum": ["global", "workspace"],
  9.                 "enumDescriptions": [
  10.                     "Store snippets globally (across all workspaces)",
  11.                     "Store snippets in the current workspace"
  12.                 ],
  13.                 "description": "Where to store code snippets"
  14.             },
  15.             "codeSnippetManager.autoInsert": {
  16.                 "type": "boolean",
  17.                 "default": false,
  18.                 "description": "Automatically insert snippets when selected"
  19.             }
  20.         }
  21.     }
  22. }
复制代码

在代码中访问配置:
  1. const config = vscode.workspace.getConfiguration('codeSnippetManager');
  2. const storageLocation = config.get<string>('storageLocation', 'global');
  3. const autoInsert = config.get<boolean>('autoInsert', false);
复制代码

9. 进阶主题

9.1 Webview开发

Webview允许你在VS Code中创建自定义的UI界面,使用HTML、CSS和JavaScript构建复杂的交互界面。
  1. // 创建Webview面板
  2. const panel = vscode.window.createWebviewPanel(
  3.     'myWebview', // 标识符
  4.     'My Webview', // 面板标题
  5.     vscode.ViewColumn.One, // 显示在编辑器的哪个位置
  6.     {
  7.         enableScripts: true, // 启用JavaScript
  8.         localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, 'media')]
  9.     }
  10. );
  11. // 设置HTML内容
  12. panel.webview.html = getWebviewContent(context.extensionUri);
  13. // 处理来自Webview的消息
  14. panel.webview.onDidReceiveMessage(
  15.     async message => {
  16.         switch (message.command) {
  17.             case 'alert':
  18.                 vscode.window.showInformationMessage(message.text);
  19.                 return;
  20.             case 'getData':
  21.                 const data = await fetchData();
  22.                 panel.webview.postMessage({ command: 'dataResponse', data });
  23.                 return;
  24.         }
  25.     },
  26.     undefined,
  27.     context.subscriptions
  28. );
  29. // 获取Webview HTML内容
  30. function getWebviewContent(extensionUri: vscode.Uri): string {
  31.     const scriptUri = vscode.Uri.joinPath(extensionUri, 'media', 'main.js');
  32.     const styleUri = vscode.Uri.joinPath(extensionUri, 'media', 'styles.css');
  33.    
  34.     return `<!DOCTYPE html>
  35.     <html lang="en">
  36.     <head>
  37.         <meta charset="UTF-8">
  38.         <meta name="viewport" content="width=device-width, initial-scale=1.0">
  39.         <title>My Webview</title>
  40.         <link href="${styleUri}" rel="stylesheet">
  41.     </head>
  42.     <body>
  43.         <h1>My Webview</h1>
  44.         <button id="alertButton">Show Alert</button>
  45.         <button id="getDataButton">Get Data</button>
  46.         <div id="dataContainer"></div>
  47.         <script src="${scriptUri}"></script>
  48.     </body>
  49.     </html>`;
  50. }
复制代码

9.2 语言服务器协议(LSP)

语言服务器协议(Language Server Protocol, LSP)是一种协议,用于在编辑器和语言服务器之间通信,提供语言智能功能如代码补全、错误检查、定义跳转等。

创建语言服务器的步骤:

1. 创建语言服务器项目:
  1. npm init -y
  2. npm install vscode-languageserver vscode-languageserver-textdocument
复制代码

1. 实现服务器:
  1. // server.ts
  2. import {
  3.     createConnection,
  4.     TextDocuments,
  5.     ProposedFeatures,
  6.     InitializeParams,
  7.     DidChangeConfigurationNotification,
  8.     TextDocumentSyncKind,
  9.     InitializeResult,
  10.     CompletionItem,
  11.     CompletionItemKind,
  12.     TextDocumentPositionParams
  13. } from 'vscode-languageserver/node';
  14. import {
  15.     TextDocument
  16. } from 'vscode-languageserver-textdocument';
  17. // 创建连接和文档管理器
  18. const connection = createConnection(ProposedFeatures.all);
  19. const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
  20. let hasConfigurationCapability = false;
  21. let hasWorkspaceFolderCapability = false;
  22. let hasDiagnosticRelatedInformationCapability = false;
  23. connection.onInitialize((params: InitializeParams) => {
  24.     const capabilities = params.capabilities;
  25.     hasConfigurationCapability = !!(
  26.         capabilities.workspace && !!capabilities.workspace.configuration
  27.     );
  28.     hasWorkspaceFolderCapability = !!(
  29.         capabilities.workspace && !!capabilities.workspace.workspaceFolders
  30.     );
  31.     hasDiagnosticRelatedInformationCapability = !!(
  32.         capabilities.textDocument &&
  33.         capabilities.textDocument.publishDiagnostics &&
  34.         capabilities.textDocument.publishDiagnostics.relatedInformation
  35.     );
  36.     const result: InitializeResult = {
  37.         capabilities: {
  38.             textDocumentSync: TextDocumentSyncKind.Incremental,
  39.             completionProvider: {
  40.                 resolveProvider: true
  41.             }
  42.         }
  43.     };
  44.     if (hasWorkspaceFolderCapability) {
  45.         result.capabilities.workspace = {
  46.             workspaceFolders: {
  47.                 supported: true
  48.             }
  49.         };
  50.     }
  51.     return result;
  52. });
  53. // 注册文档内容变化监听器
  54. documents.onDidChangeContent(change => {
  55.     validateTextDocument(change.document);
  56. });
  57. // 验证文档
  58. async function validateTextDocument(textDocument: TextDocument): Promise<void> {
  59.     // 实现文档验证逻辑
  60. }
  61. // 提供代码补全
  62. connection.onCompletion(
  63.     (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
  64.         // 返回补全项
  65.         return [
  66.             {
  67.                 label: 'TypeScript',
  68.                 kind: CompletionItemKind.Text,
  69.                 data: 1
  70.             },
  71.             {
  72.                 label: 'JavaScript',
  73.                 kind: CompletionItemKind.Text,
  74.                 data: 2
  75.             }
  76.         ];
  77.     }
  78. );
  79. // 监听文档打开、关闭、变化事件
  80. documents.listen(connection);
  81. connection.listen();
复制代码

1. 在插件中连接语言服务器:
  1. // client.ts
  2. import * as path from 'path';
  3. import * as vscode from 'vscode';
  4. import {
  5.     LanguageClient,
  6.     LanguageClientOptions,
  7.     ServerOptions,
  8.     TransportKind
  9. } from 'vscode-languageclient/node';
  10. let client: LanguageClient;
  11. export function activate(context: vscode.ExtensionContext) {
  12.     // 服务器选项
  13.     const serverModule = context.asAbsolutePath(
  14.         path.join('server', 'out', 'server.js')
  15.     );
  16.    
  17.     const serverOptions: ServerOptions = {
  18.         run: { module: serverModule, transport: TransportKind.ipc },
  19.         debug: {
  20.             module: serverModule,
  21.             transport: TransportKind.ipc,
  22.             options: { execArgv: ['--nolazy', '--inspect=6009'] }
  23.         }
  24.     };
  25.     // 客户端选项
  26.     const clientOptions: LanguageClientOptions = {
  27.         documentSelector: [{ scheme: 'file', language: 'mylang' }],
  28.         synchronize: {
  29.             configurationSection: 'languageServerExample',
  30.             fileEvents: vscode.workspace.createFileSystemWatcher('**/.clientrc')
  31.         }
  32.     };
  33.     // 创建语言客户端
  34.     client = new LanguageClient(
  35.         'languageServerExample',
  36.         'Language Server Example',
  37.         serverOptions,
  38.         clientOptions
  39.     );
  40.     // 启动客户端
  41.     client.start();
  42. }
  43. export function deactivate(): Thenable<void> | undefined {
  44.     if (!client) {
  45.         return undefined;
  46.     }
  47.     return client.stop();
  48. }
复制代码

9.3 调试适配器协议(DAP)

调试适配器协议(Debug Adapter Protocol, DAP)是一种用于调试器和编辑器之间通信的协议,允许你为新的编程语言或运行时实现调试支持。

创建调试适配器的步骤:

1. 创建调试适配器项目:
  1. npm init -y
  2. npm install vscode-debugadapter
复制代码

1. 实现调试适配器:
  1. // debugAdapter.ts
  2. import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, ContinuedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter';
  3. import { DebugProtocol } from 'vscode-debugprotocol';
  4. interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
  5.     program: string;
  6. }
  7. class MyDebugSession extends DebugSession {
  8.     private static THREAD_ID = 1;
  9.     private _configurationDone = false;
  10.     private _variableHandles = new Handles<string>();
  11.     public constructor() {
  12.         super();
  13.         this.setDebuggerLinesStartAt1(false);
  14.         this.setDebuggerColumnsStartAt1(false);
  15.     }
  16.     protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {
  17.         response.body = response.body || {};
  18.         response.body.supportsConfigurationDoneRequest = true;
  19.         response.body.supportsEvaluateForHovers = true;
  20.         response.body.supportsStepBack = false;
  21.         this.sendResponse(response);
  22.     }
  23.     protected launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void {
  24.         // 启动调试目标
  25.         this.sendResponse(response);
  26.     }
  27.     protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments): void {
  28.         this._configurationDone = true;
  29.         this.sendResponse(response);
  30.     }
  31.     protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void {
  32.         // 设置断点
  33.         response.body = {
  34.             breakpoints: []
  35.         };
  36.         this.sendResponse(response);
  37.     }
  38.     protected threadsRequest(response: DebugProtocol.ThreadsResponse): void {
  39.         response.body = {
  40.             threads: [
  41.                 new Thread(MyDebugSession.THREAD_ID, "thread 1")
  42.             ]
  43.         };
  44.         this.sendResponse(response);
  45.     }
  46.     protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void {
  47.         // 返回堆栈跟踪
  48.         const startFrame = typeof args.startFrame === 'number' ? args.startFrame : 0;
  49.         const maxLevels = typeof args.levels === 'number' ? args.levels : 1000;
  50.         const endFrame = startFrame + maxLevels;
  51.         const stackFrames: StackFrame[] = [];
  52.         
  53.         response.body = {
  54.             stackFrames: stackFrames,
  55.             totalFrames: 0
  56.         };
  57.         this.sendResponse(response);
  58.     }
  59.     protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
  60.         // 返回变量作用域
  61.         response.body = {
  62.             scopes: [
  63.                 new Scope("Local", this._variableHandles.create("local"), false),
  64.                 new Scope("Global", this._variableHandles.create("global"), true)
  65.             ]
  66.         };
  67.         this.sendResponse(response);
  68.     }
  69.     protected variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): void {
  70.         // 返回变量
  71.         const variables: DebugProtocol.Variable[] = [];
  72.         response.body = {
  73.             variables: variables
  74.         };
  75.         this.sendResponse(response);
  76.     }
  77.     protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
  78.         // 继续执行
  79.         this.sendResponse(response);
  80.     }
  81.     protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): void {
  82.         // 单步执行
  83.         this.sendResponse(response);
  84.     }
  85.     protected stepInRequest(response: DebugProtocol.StepInResponse, args: DebugProtocol.StepInArguments): void {
  86.         // 步入
  87.         this.sendResponse(response);
  88.     }
  89.     protected stepOutRequest(response: DebugProtocol.StepOutResponse, args: DebugProtocol.StepOutArguments): void {
  90.         // 步出
  91.         this.sendResponse(response);
  92.     }
  93.     protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void {
  94.         // 计算表达式
  95.         response.body = {
  96.             result: 'evaluate result',
  97.             variablesReference: 0
  98.         };
  99.         this.sendResponse(response);
  100.     }
  101. }
  102. DebugSession.run(MyDebugSession);
复制代码

1. 在插件中注册调试适配器:
  1. // extension.ts
  2. import * as path from 'path';
  3. import * as vscode from 'vscode';
  4. import { workspace, ExtensionContext } from 'vscode';
  5. export function activate(context: ExtensionContext) {
  6.     context.subscriptions.push(
  7.         vscode.debug.registerDebugConfigurationProvider('mydebug', new MyDebugConfigurationProvider())
  8.     );
  9.     const provider = new MyDebugAdapterDescriptorFactory();
  10.     context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('mydebug', provider));
  11.     if ('dispose' in provider) {
  12.         context.subscriptions.push(provider);
  13.     }
  14. }
  15. class MyDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
  16.     resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
  17.         if (!config.type && !config.request && !config.name) {
  18.             const editor = vscode.window.activeTextEditor;
  19.             if (editor && editor.document.languageId === 'mylang') {
  20.                 config.type = 'mydebug';
  21.                 config.name = 'Launch';
  22.                 config.request = 'launch';
  23.                 config.program = '${file}';
  24.                 config.stopOnEntry = true;
  25.             }
  26.         }
  27.         
  28.         if (!config.program) {
  29.             return vscode.window.showInformationMessage("Cannot find a program to debug").then(_ => {
  30.                 return undefined;
  31.             });
  32.         }
  33.         
  34.         return config;
  35.     }
  36. }
  37. class MyDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory {
  38.     createDebugAdapterDescriptor(session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
  39.         return new vscode.DebugAdapterExecutable(path.join(__dirname, 'debugAdapter.js'));
  40.     }
  41.     dispose() {}
  42. }
复制代码

10. 总结

通过本教程,我们全面介绍了VS Code插件开发的各个方面,从基础概念到高级主题。我们学习了如何:

1. 搭建VS Code插件开发环境
2. 理解插件项目结构和核心API
3. 实现常见功能如命令、菜单、快捷键、代码补全等
4. 开发一个完整的代码片段管理插件
5. 优化插件性能和用户体验
6. 探索进阶主题如Webview、语言服务器和调试适配器

VS Code插件开发是一个强大而灵活的领域,通过掌握这些技术,你可以根据自己的需求定制编辑器,提高开发效率,解决复杂项目中的挑战。

希望本教程能帮助你开启VS Code插件开发之旅,不断探索和创新,打造出更多有价值的插件,为开发者社区做出贡献。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则