---
title: 02-第一个扩展
date: 2025-07-20 11:00:00
icon: famicons:logo-markdown
index: true
tags:
categories:
---

## 一、vscode官方demo

先用vscode官方提供的demo了解一下扩展的基本结构。

### 1. demo在哪里？

去哪里找vscode提供的demo？在官网 [Extension API | Visual Studio Code Extension API](https://code.visualstudio.com/api) 这里是有说明的：

<img src="./02-第一个扩展/img/image-20250530093120329.png" alt="image-20250530093120329"  />

直接点这里就可以跳转到 [GitHub - microsoft/vscode-extension-samples](https://github.com/microsoft/vscode-extension-samples)，这个仓库中就是官网提供的所有demo，我们下载完毕后，找到这个 [vscode-extension-samples/helloworld-sample](https://github.com/microsoft/vscode-extension-samples/tree/main/helloworld-sample)，它包含以下文件：

```bash
.
+--- .gitignore
+--- .vscode
|   +--- launch.json
|   +--- tasks.json
+--- demo.gif
+--- eslint.config.mjs
+--- package-lock.json
+--- package.json
+--- README.md
+--- src
|   +--- extension.ts
+--- tsconfig.json
```

### 2. 项目结构

#### 2.1 `src/extension.ts`

```json
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
	// Use the console to output diagnostic information (console.log) and errors (console.error)
	// This line of code will only be executed once when your extension is activated
	console.log('Congratulations, your extension "helloworld-sample" is now active!');

	// The command has been defined in the package.json file
	// Now provide the implementation of the command with registerCommand
	// The commandId parameter must match the command field in package.json
	const disposable = vscode.commands.registerCommand('extension.helloWorld', () => {
		// The code you place here will be executed every time your command is executed

		// Display a message box to the user
		vscode.window.showInformationMessage('Hello World!');
	});

	context.subscriptions.push(disposable);
}
```

这个具体的先不看，知道它会被编译成对应的js文件，然后打印一个Hello World!就可以了。

#### 2.2 `package.json`

```json
{
    //......
	// 扩展的激活事件
	"activationEvents": [],
    // 入口文件
	"main": "./out/extension.js",
    // 贡献点，vscode插件大部分功能配置都在这里
	"contributes": {
		"commands": [
			{
				"command": "extension.helloWorld",
				"title": "Hello World"
			}
		]
	},
	//......
}
```

- `main`定义了整个插件的主入口；
- 我们在`contributes.commands`里面注册了一个名为`extension.sayHello`的命令，并在`out/extension.js`中去实现了它（弹出一个`Hello World`的提示）。
- `title`这个定义了我们在命令行调用的时候的命令名称，我们后面运行调试的时候会使用`Ctrl+shift+P`打开命令框来运行命令，搜索这个`Hello World`就可以调用这个插件。

#### 2.3 `.vscode`

这个目录下有两个文件：

```bash
+--- .vscode
|   +--- launch.json
|   +--- tasks.json
```

- launch.json 是用于设置调试器的启动配置。具体的可以看这里：[Debug code with Visual Studio Code](https://code.visualstudio.com/docs/debugtest/debugging#_launch-versus-attach-configurations)
- tasks.json 是用来配置任务运行的文件。具体可以看这里：[Integrate with External Tools via Tasks](https://code.visualstudio.com/docs/debugtest/tasks)

### 3. 安装所需模块

在调试之前，我们需要先安装模块，进入helloworld-sample目录，执行：

```bash
npm install
```

<img src="./02-第一个扩展/img/image-20250530102846316.png" alt="image-20250530102846316"  />

安装完成后，会在工程目录下生成node_modules目录，里面就存放着工程所需的模块。

### 4. 运行调试

在编辑器中，打开`src/extension.ts`并按`F5`或【Ctrl+Shift+P】&rarr;【Debug：Start Debuging】。这将在一个新的【扩展开发主机窗口】中编译和运行扩展。

> Tips：这个新窗口已经加载了我们的插件，窗口标题会注明【扩展开发主机】。

<img src="./02-第一个扩展/img/image-20250530103432119.png" alt="image-20250530103432119"  />

我们在【新窗口】中按下 Ctrl+Shift+P，并输入`hello world`：

<img src="./02-第一个扩展/img/image-20250530103922999.png" alt="image-20250530103922999"  />

就会在右下角弹出如下窗口：

<img src="./02-第一个扩展/img/image-20250530104004639.png" alt="image-20250530104004639"  />



## 二、第一个扩展

自己创建扩展工程需要通过微软的[GitHub - microsoft/vscode-generator-code](https://github.com/Microsoft/vscode-generator-code)脚手架来生成项目结构。可以参考这里：[Your First Extension | Visual Studio Code Extension API](https://code.visualstudio.com/api/get-started/your-first-extension)

### 1. Generator安装

通过一下命令安装所需工具：

```bash
npm install -g yo generator-code
```

<img src="./02-第一个扩展/img/image-20250530105439915.png" alt="image-20250530105439915"  />

### 2. TypeScript 工程

我们执行以下命令：

```bash
yo code
```

然后会有一堆的交互提示，按照官网的说明填写就可以了，我这里修改了demo名称：

```bash
D:\sumu_blog> yo code

     _-----_     ╭──────────────────────────╮
    |       |    │   Welcome to the Visual  │
    |--(o)--|    │   Studio Code Extension  │
   `---------´   │        generator!        │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? vssm-tool
? What's the identifier of your extension? vssm-tool
? What's the description of your extension? 苏木的vscode扩展小工具
? Initialize a git repository? Yes
? Which bundler to use? unbundled
? Which package manager to use? npm
# 中间就是安装一些模块相关的东西...
? Do you want to open the new folder with Visual Studio Code? (Use arrow keys)
❯ Open with `code`
  Skip
```

然后我们就会得到这样一个目录：

![image-20250723213649999](./02-第一个扩展/img/image-20250723213649999.png)

这就是我们创建的工程啦。

> Tips：在 Initialize a git repository? 这一步会自动初始化一个git仓库，看个人需求。

### 3. 项目结构

这个创建出来的工程和前面是一样的，我们主要分析一下怎么运行，将会打印什么。

#### 3.1 `src/extension.ts`

```typescript
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {

  // Use the console to output diagnostic information (console.log) and errors (console.error)
  // This line of code will only be executed once when your extension is activated
  console.log('Congratulations, your extension "vssm-tool" is now active!');

  // The command has been defined in the package.json file
  // Now provide the implementation of the command with registerCommand
  // The commandId parameter must match the command field in package.json
  const disposable = vscode.commands.registerCommand('vssm-tool.helloWorld', () => {
    // The code you place here will be executed every time your command is executed
    // Display a message box to the user
    vscode.window.showInformationMessage('Hello World from vssm-tool!');
  });

  context.subscriptions.push(disposable);
}

// This method is called when your extension is deactivated
export function deactivate() { }
```

这里将会给出弹窗，然后打印 `Hello World from vssm-tool!`。

#### 3.2 `package.json`

```json
{
	//......
    "main": "./out/extension.js",
    "contributes": {
        "commands": [
            {
                "command": "vssm-tool.helloWorld",
                "title": "Hello World"
            }
        ]
    },
	//......
}
```

我们在`contributes.commands`里面注册了一个名为`vssm-tool.helloWorld`的命令，标题还是叫`Hello World`，这意味着我们在调试的时候还是输入这个命令，并在`out/extension.js`中去实现了它。

### 4. 运行调试

和前面一样，在编辑器中，打开`src/extension.ts`并按`F5`或【Ctrl+Shift+P】&rarr;【Debug：Start Debuging】，这将在一个新的 **Extension Development Host** 窗口中编译并运行扩展。然后在新的编辑器中【Ctrl+Shift+P】&rarr;【Hello World】

![image-20250723222154346](./02-第一个扩展/img/image-20250723222154346.png)

- 可能出现的问题

这里可能会有一个问题，就是找不到这个Hello World命令，这个原因可能是因为 vscode 版本不一致造成的：

```json
{
  //......
  "engines": {
    "vscode": "^1.102.0"
  },
  // ......
  "devDependencies": {
    "@types/vscode": "^1.102.0",
    "@types/mocha": "^10.0.10",
    "@types/node": "20.x",
    "@typescript-eslint/eslint-plugin": "^8.31.1",
    "@typescript-eslint/parser": "^8.31.1",
    "eslint": "^9.25.1",
    "typescript": "^5.8.3",
    "@vscode/test-cli": "^0.0.11",
    "@vscode/test-electron": "^2.5.2"
  }
}

```

可能是这个时候vscode最新版本是`1.102.0`，但是我的vscode是`1.100.2`，所以就造成了版本不匹配，上面的`^1.102.0`就表示至少需要是`1.102.0`，所以这里我们直接修改一下：

```json
  "engines": {
    "vscode": "^1.100.0"
  },
```

对于下面的依赖（后来发现其实不更新也没问题），我们执行：

```bash
npm i -D @types/vscode@1.100.0
```

然后重新开启调试窗口就可以啦（我后来更新了新版本的VSCode）。其实这里在官方文档是有提示的：

> 如果你在调试窗口中看不到 **Hello World** 命令，请检查 `package.json` 文件，并确保 `engines.vscode` 版本与已安装的 VS Code 版本兼容。——[你的第一个扩展 | Visual Studio Code 扩展 API - VSCode 编辑器](https://vscode.js.cn/api/get-started/your-first-extension)

## 三、Hello world优化

### 1. 右键菜单

可以参考：[Wrapping Up | Visual Studio Code Extension API](https://code.visualstudio.com/api/get-started/wrapping-up#issue-reporting)和[Contribution Points | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/contribution-points#contributesmenus)

#### 1.1  `package.json`

在 `package.json`中添加以下内容：

```json
{
	//......
	"contributes": {
		"commands": [
			{
				"command": "vssm-tool.helloWorld",
				"title": "Hello World"
			}
		],
		"menus": {
			"editor/context": [
				{
					"command": "vssm-tool.helloWorld",
					"group": "navigation",
					"when": "editorTextFocus"
				}
			]
		}
	},
	//......
}
```

#### 1.2 显示效果

![image-20250723202340538](./02-第一个扩展/img/image-20250723202340538.png)

### 2. 快捷键绑定

我要是想定义一个快捷键调用这个HelloWorld呢？参考这里：[Contribution Points | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/contribution-points#contributeskeybindings)

#### 2.1 一般格式

```json
{
  "contributes": {
    "keybindings": [
      {
        "command": "extension.sayHello",
        "key": "ctrl+f1",
        "mac": "cmd+f1",
        "when": "editorTextFocus"
      }
    ]
  }
}
```

#### 2.2 `package.json`

```json
{
    //......
	"contributes": {
		//......
		"keybindings": [
            {
                "command": "vssm-tool.helloWorld",
                "key": "ctrl+alt+f10",
                "mac": "cmd+alt+f10",
                "when": "editorTextFocus"
            }
        ],
		//......
	},
}
```

#### 2.3 绑定结果

![image-20250723222336819](./02-第一个扩展/img/image-20250723222336819.png)

### 3. 下方状态栏

- [Status Bar | Visual Studio Code Extension API](https://code.visualstudio.com/api/ux-guidelines/status-bar)
- [状态栏 | Visual Studio Code 扩展 API - VSCode 编辑器](https://vscode.js.cn/api/ux-guidelines/status-bar)。

- 官方demo：[vscode-extension-samples/statusbar-sample at main · microsoft/vscode-extension-samples](https://github.com/microsoft/vscode-extension-samples/tree/main/statusbar-sample)

#### 3.1 API属性

具体的属性可以看这里：[StatusBarItem](https://vscode.js.cn/api/references/vscode-api#StatusBarItem)，还可以为状态栏添加一些内置的图标，可以参考这里：[产品图标参考 | Visual Studio Code 扩展 API - VSCode 编辑器](https://vscode.js.cn/api/references/icons-in-labels)

#### 3.2 demo实例

在前面的基础上添加：

```typescript
	// 在 VS Code 界面中创建一个状态栏项
	// 参数说明：
	// - vscode.StatusBarAlignment.Left: 将项目放置在状态栏的左侧
	// - 100: 优先级值（数字越大，项目位置越靠左）
	const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
	
	// 设置状态栏中显示的文本
	// $(rocket) 是一个 codicon（VS Code 图标），在文本前显示一个火箭图标
	// 你可以使用不同的图标，如 $(heart)、$(star)、$(check) 等
	statusBarItem.text = "$(rocket) Hello World";
	
	// 设置鼠标悬停在状态栏项上时显示的提示文本
	// 这有助于用户理解该项的功能
	statusBarItem.tooltip = "点击运行 Hello World 命令";
	
	// 将状态栏项与命令关联
	// 当用户点击状态栏项时，将执行此命令
	// 命令 ID 必须与 package.json 中定义的一致
	statusBarItem.command = 'vssm-tool.helloWorld';
	
	// 使状态栏项在 UI 中可见
	// 没有这个调用，项目将被创建但不会显示
	statusBarItem.show();

	// 将命令和状态栏项都添加到扩展上下文的订阅中
	// 这确保它们在扩展被停用时正确处理
	// 正确的处理可以防止内存泄漏并确保扩展干净地关闭
	context.subscriptions.push(disposable, statusBarItem);
```

#### 3.3 显示效果

<img src="./02-第一个扩展/img/image-20250530194615345.png" alt="image-20250530194615345" />

### 4. 激活事件

可以参考：[激活事件 | Visual Studio Code 扩展 API - VSCode 编辑器](https://vscode.js.cn/api/references/activation-events)

#### 4.1 出现的问题

上面的扩展在只有右键菜单和命令行运行时没有什么说的，但是添加了状态栏之后，会发现运行后，状态栏是不显示的，只有当扩展命令运行一次后，状态栏才会显示出Hello World的标签，这是为什么？

原因就是扩展在`VS Code`中默认是没有被激活的，所以不会出现状态栏。

#### 4.2 activationEvents

**激活事件**是一组 JSON 声明，需要在在 `package.json` [扩展清单](https://vscode.js.cn/api/references/extension-manifest)的 `activationEvents` 字段中进行这些声明。当**激活事件**发生时，我们的扩展就会被激活。常用的有以下配置：

```txt
onLanguage:$
onCommand:$
onDebug
workspaceContains:$
onFileSystem:$
onView:$
onUri
*
```

这里以[onLanguage](https://vscode.js.cn/api/references/activation-events#onLanguage)为例，当配置如下：

```json
"activationEvents": [
    "onLanguage:python"，
    "onLanguage:typescript"
]
```

当我打开python和typescript文件时，扩展就会被激活。再来说一下`*`，如果配置了`*`，只要一启动vscode，插件就会被激活，为了出色的用户体验，官方不推荐这么做。

### 5. package.json文件

这里就不详细说了，看这里即可：[扩展清单 | Visual Studio Code 扩展 API - VSCode 编辑器](https://vscode.js.cn/api/references/extension-manifest)

## 五、为扩展添加图标

### 1. 修改package.json

```json
{
  //......
  "icon": "images/logo.png",
  //......
}

```

### 2. 效果

![image-20250723225949477](./02-第一个扩展/img/image-20250723225949477.png)
