`作为一个插件的ID。你可以这么理解,Hello World 例子的 ID 就是`vscode-samples.helloworld-sample`。VS Code 使用 ID 来区分各个不同的插件。
47 | - `main`: 插件的主入口。
48 | - `activationEvents` 和 `contributes`: [激活事件](/references/activation-events.md) and [发布内容配置](/references/contribution-points.md)。
49 | - `engines.vscode`: 描述了这个插件依赖的最低VS Code API版本。
50 | - `postinstall` 脚本: 如果你的`engines.vscode`声明的是1.25版的VS Code API,那它就会按照这个声明去安装目标版本。一旦`vscode.d.ts`文件存在于`node_modules/vscode/vscode.d.ts`,IntelliSense就会开始运作,你就可以对所有VS Code API进行定义跳转或者语法检查了。
51 |
52 | ```json
53 | {
54 | "name": "helloworld-sample",
55 | "displayName": "helloworld-sample",
56 | "description": "HelloWorld example for VS Code",
57 | "version": "0.0.1",
58 | "publisher": "vscode-samples",
59 | "repository": "https://github.com/Microsoft/vscode-extension-samples/helloworld-sample",
60 | "engines": {
61 | "vscode": "^1.25.0"
62 | },
63 | "categories": ["Other"],
64 | "activationEvents": ["onCommand:extension.helloWorld"],
65 | "main": "./out/extension.js",
66 | "contributes": {
67 | "commands": [
68 | {
69 | "command": "extension.helloWorld",
70 | "title": "Hello World"
71 | }
72 | ]
73 | },
74 | "scripts": {
75 | "vscode:prepublish": "npm run compile",
76 | "compile": "tsc -p ./",
77 | "watch": "tsc -watch -p ./",
78 | "postinstall": "node ./node_modules/vscode/bin/install"
79 | },
80 | "devDependencies": {
81 | "@types/node": "^8.10.25",
82 | "tslint": "^5.11.0",
83 | "typescript": "^2.6.1",
84 | "vscode": "^1.1.22"
85 | }
86 | }
87 | ```
88 |
89 | ## 插件入口文件
90 | ---
91 |
92 | 插件入口文件会导出两个函数,`activate` 和 `deactivate`,你注册的**激活事件**被触发之时执行`activate`,`deactivate`则提供了插件关闭前执行清理工作的机会。
93 |
94 | [`vscode`](https://www.npmjs.com/package/vscode)模块包含了一个位于`node ./node_modules/vscode/bin/install`的脚本,这个脚本会拉取`package.json`中`engines.vscode`字段定义的VS Code API。这个脚本执行过后,你就得到了智能代码提示,定义跳转等TS特性了。
95 |
96 | ```typescript
97 | // 'vscode'模块包含了VS Code extensibility API
98 | // 按下述方式导入这个模块
99 | import * as vscode from 'vscode';
100 |
101 | // 一旦你的插件激活,vscode会立刻调用下述方法
102 | export function activate(context: vscode.ExtensionContext) {
103 |
104 | // 用console输出诊断信息(console.log)和错误(console.error)
105 | // 下面的代码只会在你的插件激活时执行一次
106 | console.log('Congratulations, your extension "my-first-extension" is now active!');
107 |
108 | // 入口命令已经在package.json文件中定义好了,现在调用registerCommand方法
109 | // registerCommand中的参数必须与package.json中的command保持一致
110 | let disposable = vscode.commands.registerCommand('extension.helloWorld', () => {
111 | // 把你的代码写在这里,每次命令执行时都会调用这里的代码
112 | // ...
113 | // 给用户显示一个消息提示
114 | vscode.window.showInformationMessage('Hello World!');
115 | });
116 |
117 | context.subscriptions.push(disposable);
118 | }
119 | ```
120 |
--------------------------------------------------------------------------------
/docs/extension-guides/tree-view.md:
--------------------------------------------------------------------------------
1 | # 树视图
2 |
3 | 本节将教你如何为VS Code添加*视图容器*和*树视图*的插件,示例插件的源代码请查看[https://github.com/Microsoft/vscode-extension-samples/tree/master/tree-view-sample](https://github.com/Microsoft/vscode-extension-samples/tree/master/tree-view-sample)。
4 |
5 | ## 视图容器
6 | ---
7 |
8 | *视图容器*包含了一列*视图(views)*,这些*视图*又包含在内置的*视图容器*中。
9 |
10 | 
11 |
12 | 要想配置一个视图容器,你首先得注册`package.json`中的[`contributes.viewsContainers`](/references/contribution-points#contributesviewscontainers)。你还必须配置以下字段:
13 |
14 | - `id`: 新视图容器的名称
15 | - `title`: 展示给用户的视图容器名称,它会显示在视图容器上方
16 | - `icon`: 在活动栏中展示的图标
17 |
18 | ```json
19 | "contributes": {
20 | "viewsContainers": {
21 | "activitybar": [
22 | {
23 | "id": "package-explorer",
24 | "title": "Package Explorer",
25 | "icon": "media/dep.svg"
26 | }
27 | ]
28 | }
29 | }
30 | ```
31 |
32 | ## 树视图
33 | ---
34 |
35 | *视图*是显示在视图容器中的UI片段。使用[`contributes.views`](/references/contribution-points#contributesviews)进行配置,你就可以将新的*视图*添加到内置或者你配置好的视图容器中了。
36 |
37 | 
38 |
39 | 要想配置一个*视图*,你首先得注册`package.json`中的[`contributes.views`](/references/vscode-api)。你必须给*视图*配置一个ID外加一个名称,你还可以配置*视图*出现的位置:
40 |
41 | - `explorer`: 显示在资源管理器侧边栏
42 | - `debug`: 显示在调试侧边栏
43 | - `scm`: 显示在源代码侧边栏
44 | - `test`: 测试侧边栏中的资源管理器视图
45 | - 显示在你定义好的*视图容器*中
46 |
47 | ```json
48 | "contributes": {
49 | "views": {
50 | "package-explorer": [
51 | {
52 | "id": "nodeDependencies",
53 | "name": "Node Dependencies",
54 | "when": "explorer"
55 | }
56 | ]
57 | }
58 | }
59 | ```
60 |
61 | 当用户打开了对应的视图,VS Code会触发[`onView:${viewId}`](/references/activation-events?id=onview)事件(如上面例子中,这个事件写为`onView:nodeDependencies`)。你也可以通过配置`when`字段控制视图的展示。
62 |
63 | ## 视图的操作
64 | ---
65 |
66 | 你可以配置*视图*下述位置的操作:
67 |
68 | - `view/title`: *视图*标题位置显示的操作。这里可以配置主要的操作,使用`"group": "navigation"`进行配置,剩余的二级操作则出现在`...`菜单中。
69 | - `view/item/context`: 每个*视图项*的操作。这里可以配置主要的操作,使用`"group": "inline"`,剩余的二级操作则出现在`...`菜单中。
70 |
71 | 使用`when`属性控制这些操作的展示。
72 |
73 | 
74 |
75 | 例如:
76 | ```json
77 | "contributes": {
78 | "commands": [
79 | {
80 | "command": "nodeDependencies.refreshEntry",
81 | "title": "Refresh",
82 | "icon": {
83 | "light": "resources/light/refresh.svg",
84 | "dark": "resources/dark/refresh.svg"
85 | }
86 | },
87 | {
88 | "command": "nodeDependencies.addEntry",
89 | "title": "Add"
90 | },
91 | {
92 | "command": "nodeDependencies.editEntry",
93 | "title": "Edit",
94 | "icon": {
95 | "light": "resources/light/edit.svg",
96 | "dark": "resources/dark/edit.svg"
97 | }
98 | },
99 | {
100 | "command": "nodeDependencies.deleteEntry",
101 | "title": "Delete"
102 | }
103 | ],
104 | "menus": {
105 | "view/title": [
106 | {
107 | "command": "nodeDependencies.refreshEntry",
108 | "when": "view == nodeDependencies",
109 | "group": "navigation"
110 | },
111 | {
112 | "command": "nodeDependencies.addEntry",
113 | "when": "view == nodeDependencies"
114 | }
115 | ],
116 | "view/item/context": [
117 | {
118 | "command": "nodeDependencies.editEntry",
119 | "when": "view == nodeDependencies && viewItem == dependency",
120 | "group": "inline"
121 | },
122 | {
123 | "command": "nodeDependencies.deleteEntry",
124 | "when": "view == nodeDependencies && viewItem == dependency"
125 | }
126 | ]
127 | }
128 | }
129 | ```
130 |
131 | !> **注意**:如果你需要针对特定的条目显示特殊的操作,定义树视图项的`TreeItem.contextValue`,并且在`when`中使用表达式,视图项的值储存在表达式的`viewItem`中。
132 |
133 | 如:
134 |
135 | ```json
136 | "contributes": {
137 | "menus": {
138 | "view/item/context": [
139 | {
140 | "command": "nodeDependencies.deleteEntry",
141 | "when": "view == nodeDependencies && viewItem == dependency"
142 | }
143 | ]
144 | }
145 | }
146 | ```
147 |
148 | ## 为树视图提供数据
149 | ---
150 |
151 | 插件创作者需要注册[`TreeDataProvider`](https://code.visualstudio.com/api/references/vscode-api#TreeDataProvider),以便动态生成*视图*中的数据。
152 |
153 | ```typescript
154 | vscode.window.registerTreeDataProvider('nodeDependencies', new DepNodeProvider());
155 | ```
156 |
157 | 更多实现请参考[nodeDependencies.ts](https://github.com/Microsoft/vscode-extension-samples/tree/master/tree-view-sample/src/nodeDependencies.ts)
158 |
159 | ## 动态创建树视图
160 | ---
161 |
162 | 如果你想在*视图*中通过编程手段创建一些操作,你就不能再注册`window.registerTreeDataProvider`了,而是`window.createTreeView`,这样一来你就有权限提供你喜欢的视图操作了:
163 |
164 | ```typescript
165 | vscode.window.createTreeView('ftpExplorer', {
166 | treeDataProvider: new FtpTreeDataProvider()
167 | });
168 | ```
169 |
170 | 更多实现请参考[ftpExplorer.ts](https://github.com/Microsoft/vscode-extension-samples/tree/master/tree-view-sample/src/ftpExplorer.ts)
--------------------------------------------------------------------------------
/docs/references/activation-events.md:
--------------------------------------------------------------------------------
1 | # 激活事件
2 |
3 | **激活事件**是`package.json`中`activationEvents`字段声明的一个JSON对象, 参考[插件清单](/references/extension-manifest). 当**激活事件**触发时, 插件就会被激活. 下面是可用的**激活事件**列表:
4 |
5 | - [激活事件](#激活事件)
6 | - [onLanguage](#onlanguage)
7 | - [onCommand](#oncommand)
8 | - [onDebug](#ondebug)
9 | - [onDebugInitialConfigurations](#ondebuginitialconfigurations)
10 | - [onDebugResolve](#ondebugresolve)
11 | - [workspaceContains](#workspacecontains)
12 | - [onFileSystem](#onfilesystem)
13 | - [onView](#onview)
14 | - [onUri](#onuri)
15 | - [onWebviewPanel](#onwebviewpanel)
16 | - [onCustomEditor](#oncustomeditor)
17 | - [onAuthenticationRequest](#onauthenticationrequest)
18 | - [onStartupFinished](#onstartupfinished)
19 | - [*](#start-up)
20 |
21 | `package.json`的配置项都可以在[插件清单](/references/extension-manifest)中找到.
22 |
23 | ## onLanguage
24 | ---
25 |
26 | 打开特定语言文件时激活事件和相关插件
27 |
28 | ```json
29 | ...
30 | "activationEvents": [
31 | "onLanguage:python"
32 | ]
33 | ...
34 | ```
35 |
36 | `onLanguage`事件需要指定特定的[语言标识符](https://code.visualstudio.com/docs/languages/identifiers)
37 |
38 | 也可以添加多种语言:
39 |
40 | ```json
41 | "activationEvents": [
42 | "onLanguage:json",
43 | "onLanguage:markdown",
44 | "onLanguage:typescript"
45 | ]
46 | ```
47 |
48 | ## onCommand
49 | ---
50 |
51 | 调用命令时激活
52 |
53 | ```json
54 | ...
55 | "activationEvents": [
56 | "onCommand:extension.sayHello"
57 | ]
58 | ...
59 | ```
60 |
61 | ## onDebug
62 | ---
63 |
64 | 调试会话(debug session)启动前激活
65 |
66 | ```json
67 | ...
68 | "activationEvents": [
69 | "onDebug"
70 | ]
71 | ...
72 | ```
73 |
74 | ### onDebugInitialConfigurations
75 |
76 | ### onDebugResolve
77 |
78 | 这是两个粒度更细的`onDebug`激活事件:
79 |
80 | - `DebugConfigurationProvider`中的`provideDebugConfigurations`在`onDebugInitialConfigurations`之后触发
81 | - `onDebugResolve:type`在`DebugConfigurationProvider`的`resolveDebugConfiguration`方法之前触发.
82 |
83 | **友情提示**: 如果调试插件比较轻量, 使用`onDebug`. 相反, 根据`DebugConfigurationProvider`实现的对应方法(`provideDebugConfigurations`或`resolveDebugConfiguration`),使用`onDebugInitialConfigurations`或`onDebugResolve`. 参阅[使用调试器插件](/extension-guides/debugger-extension#using-a-debugconfigurationprovider).
84 |
85 | ## workspaceContains
86 | ---
87 |
88 | 文件夹打开后,且文件夹中至少包含一个符合glob模式的文件时触发.
89 |
90 | ```json
91 | "activationEvents": [
92 | "workspaceContains:**/.editorconfig"
93 | ]
94 | ```
95 |
96 | ## onFileSystem
97 | ---
98 |
99 | 以协议(scheme)打开文件或文件夹时触发. 通常是`file`-协议,也可以用自定义的文件供应器函数替换掉,比如`ftp`、`ssh`.
100 |
101 | ```json
102 | ...
103 | "activationEvents": [
104 | "onFileSystem:sftp"
105 | ]
106 | ...
107 | ```
108 |
109 | ## onView
110 | ---
111 |
112 | 指定id的视图展开时触发:
113 |
114 | ```json
115 | ...
116 | "activationEvents": [
117 | "onView:nodeDependencies"
118 | ]
119 | ...
120 | ```
121 |
122 | ## onUri
123 | ---
124 |
125 | 插件的系统级URI打开时触发. 这个URI协议需要带上`vscode`或者 `vscode-insiders`协议. URI主机名必须是插件的唯一标识,剩余的URI是可选的.
126 |
127 | ```json
128 | ...
129 | "activationEvents": [
130 | "onUri"
131 | ]
132 | ...
133 | ```
134 |
135 | 如果`vscode.git`插件定义了`onUri`激活事件,那么下列任意URI打开时就会触发:
136 |
137 | - `vscode://vscode.git/init`
138 | - `vscode://vscode.git/clone?url=https%3A%2F%2Fgithub.com%2FMicrosoft%2Fvscode-vsce.git`
139 | - `vscode-insiders://vscode.git/init`(for VS Code Insiders)
140 |
141 | ## onWebviewPanel
142 | ---
143 |
144 | 当相应`viewType`的`webview`恢复时触发.
145 |
146 | 下面是一个例子:
147 |
148 | ```json
149 | "activationEvents": [
150 | ...,
151 | "onWebviewPanel:catCoding"
152 | ]
153 | ```
154 |
155 | VS Code 恢复 (restore) `viewType` 为 `catCoding` 的 webview 时会激活插件. 调用 `window.createWebviewPanel` 可以设置 `viewType`, 你可能会需要其它的激活事件(比如: `onCommand`)初始化你的插件,然后再创建`webview`视图.
156 |
157 | ## onCustomEditor
158 | ---
159 |
160 | 当相应 `viewType` 的 [自定义编辑器](../extension-guides/custom-editors.md) 被创建时触发.
161 |
162 |
163 | 下面是一个例子:
164 | ```json
165 | "activationEvents": [
166 | "onCustomEditor:catCustoms.pawDraw"
167 | ]
168 | ```
169 |
170 | VS Code 恢复 (restore) `viewType` 为 `catCustoms.pawDraw` 的自定义编辑器时会激活插件.
171 | 首先通过 [`自定义编辑器` 的发布内容配置](../extension-guides/custom-editors.md#发布内容配置) 设置 `viewType` ,然后为 `registerCustomEditorProvider` 提供一个 供应器函数.
172 |
173 | !> **注意**: 从VS Code 1.74.0开始,包含自定义编辑器的插件,不再需要单独配置 `onCustomEditor` 事件来激活.
174 |
175 | ## onAuthenticationRequest
176 | ---
177 |
178 | 当插件通过 `authentication.getSession()` API和相应的 `providerId` 请求认证会话时会激活插件.
179 |
180 | 下面是一个例子:
181 |
182 | ```json
183 | "activationEvents": [
184 | "onAuthenticationRequest:github"
185 | ]
186 | ```
187 |
188 | 当 VS Code 需要获取类型为 `github` 的 `AuthenticationSession` 将激活插件.
189 |
190 | !> **注意**: 从VS Code 1.74.0开始,包含认证程序的插件,不再需要单独配置 `onAuthenticationRequest` 事件来激活.
191 |
192 | ## onStartupFinished
193 | ---
194 |
195 | VS Code启动一段时间后才会激活插件. 它类似于 `*` 类激活事件,但它不会减慢VS Code启动. 目前,该事件在所有 `*` 类插件激活完成后触发.
196 |
197 | ```json
198 | ...
199 | "activationEvents": [
200 | "onStartupFinished"
201 | ]
202 | ...
203 | ```
204 |
205 | ## Start up
206 | ---
207 |
208 | 当VS Code启动时触发. 为了保证良好的用户体验,只在你的插件没有其他任何激活事件的前提下,添加这个激活事件.
209 |
210 | ```json
211 | ...
212 | "activationEvents": [
213 | "*"
214 | ]
215 | ...
216 | ```
217 |
218 | !> **注意**: 一个插件如果侦听了多个激活事件, 那么最好用`"*"`替换掉.
219 |
220 | !> **注意**: 插件**必须**从它的主模块中输出一个`activate()`函数,当任意的激活事件触发时,VS Code会**仅仅调用一次这个函数**. 此外,插件也**应该** 导出一个`deactivate()`函数,当VS Code关闭时执行清理的任务. 如果清理进程是异步的,插件的`deactivate()`**必须**返回一个Promise. 如果这个清理任务是同步的,那么`deactivate()`可以返回`undefined`.
221 |
--------------------------------------------------------------------------------
/docs/language-extensions/language-configuration-guide.md:
--------------------------------------------------------------------------------
1 | # 语言配置
2 |
3 | 通过[`contributes.languages`](/extensibility-reference/contribution-points#contributeslanguages)发布内容配置,你可以配置以下*声明式语言特性*:
4 |
5 | - 启用/关闭注释
6 | - 定义括号
7 | - 自动闭合符号
8 | - 自动环绕符号
9 | - 代码折叠
10 | - 单词匹配
11 | - 缩进规则
12 |
13 | [语言配置示例](https://github.com/Microsoft/vscode-extension-samples/tree/master/language-configuration-sample)中配置JavaScript文件中的编辑功能。本篇指南会详细解释`language-configuration.json`中的内容:
14 |
15 | !> **注意**:如果你的语言配置文件以**`language-configuration.json`**结尾,那么VS Code会帮你添加代码补全和校验功能。
16 |
17 | ```json
18 | {
19 | "comments": {
20 | "lineComment": "//",
21 | "blockComment": ["/*", "*/"]
22 | },
23 | "brackets": [["{", "}"], ["[", "]"], ["(", ")"]],
24 | "autoClosingPairs": [
25 | { "open": "{", "close": "}" },
26 | { "open": "[", "close": "]" },
27 | { "open": "(", "close": ")" },
28 | { "open": "'", "close": "'", "notIn": ["string", "comment"] },
29 | { "open": "\"", "close": "\"", "notIn": ["string"] },
30 | { "open": "`", "close": "`", "notIn": ["string", "comment"] },
31 | { "open": "/**", "close": " */", "notIn": ["string"] }
32 | ],
33 | "autoCloseBefore": ";:.,=}])>` \n\t",
34 | "surroundingPairs": [
35 | ["{", "}"],
36 | ["[", "]"],
37 | ["(", ")"],
38 | ["'", "'"],
39 | ["\"", "\""],
40 | ["`", "`"]
41 | ],
42 | "folding": {
43 | "markers": {
44 | "start": "^\\s*//\\s*#?region\\b",
45 | "end": "^\\s*//\\s*#?endregion\\b"
46 | }
47 | },
48 | "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)",
49 | "indentationRules": {
50 | "increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
51 | "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\}\\]].*$"
52 | }
53 | }
54 | ```
55 |
56 | ## 启用/关闭注释
57 | ---
58 |
59 | VS Code提供了切换注释开关的命令:
60 |
61 | - **Toggle Line Comment**
62 | - **Toggle Block Comment**
63 |
64 | 分别来配置`comments.lineComment`控制块注释和`comments.blockComment`控制行注释。
65 |
66 | ```json
67 | {
68 | "comments": {
69 | "lineComment": "//",
70 | "blockComment": ["/*", "*/"]
71 | }
72 | }
73 | ```
74 |
75 | ## 定义括号
76 | ---
77 |
78 | 你在VS Code中将鼠标移动到一个括号边上时,VS Code会自动高亮对应的括号。
79 |
80 | ```json
81 | {
82 | "brackets": [["{", "}"], ["[", "]"], ["(", ")"]]
83 | }
84 | ```
85 |
86 | 另外,当你运行**Go to Bracket**或**Select to Bracket**时,VS Code会自动使用你的定义找到最近、最匹配的括号。
87 |
88 | ## 自动闭合符号
89 | ---
90 |
91 | 当你输入`'`时,VS Code会自动帮你补全另一个单引号然后将光标放在引号中间,我们来看看是怎么做的:
92 |
93 | ```json
94 | {
95 | "autoClosingPairs": [
96 | { "open": "{", "close": "}" },
97 | { "open": "[", "close": "]" },
98 | { "open": "(", "close": ")" },
99 | { "open": "'", "close": "'", "notIn": ["string", "comment"] },
100 | { "open": "\"", "close": "\"", "notIn": ["string"] },
101 | { "open": "`", "close": "`", "notIn": ["string", "comment"] },
102 | { "open": "/**", "close": " */", "notIn": ["string"] }
103 | ]
104 | }
105 | ```
106 |
107 | 配置`notIn`键(key)可以在你需要的时候关闭这个功能。比如你在写下面的代码:
108 |
109 | ```javascript
110 | // ES6's Template String
111 | `ES6's Template String`;
112 | ```
113 |
114 | 此时单引号就不会闭合。
115 |
116 | 用户可以使用`editor.autoClosingQuotes`和`editor.autoClosingBrackets`设置*自动闭合符号*的行为。
117 |
118 | #### 在XXX前闭合符号
119 |
120 | 如果符号的右边有空白,那么VS Code默认会启用符号闭合,所以当你在JSX代码中输入`{`时,符号并不会闭合:
121 |
122 | ```javascript
123 | const Component = () =>
124 |
125 | ^ VS Code默认不会闭合此处的括号
126 |
127 | ```
128 |
129 | 但是你可以用下面的定义覆盖默认行为:
130 |
131 | ```json
132 | {
133 | "autoCloseBefore": ";:.,=}])>` \n\t"
134 | }
135 | ```
136 |
137 | 现在如果你在`>`前面输入`{`,VS Code会自动补全`}`。
138 |
139 | ## 自动环绕符号
140 | ---
141 |
142 | 当你选择了一堆文本然后输入左括号时,VS Code会对选中内容外围加上对应的括号。这个功能叫做*自动环绕符号*,你可以参考下面的代码指定这项功能:
143 |
144 | ```json
145 | {
146 | "surroundingPairs": [
147 | ["{", "}"],
148 | ["[", "]"],
149 | ["(", ")"],
150 | ["'", "'"],
151 | ["\"", "\""],
152 | ["`", "`"]
153 | ]
154 | }
155 | ```
156 |
157 | 注意用户可以通过`editor.autoSurround`设置*自动环绕符号*的行为。
158 |
159 | ## 代码折叠
160 | ---
161 |
162 | 在VS Code中有三种代码折叠类型:
163 |
164 | - 缩进折叠:这是VS Code中默认的缩进行为,当两行内容有着相同的缩进级别时,你就可以看到折叠标记了。
165 | - 语言配置折叠:当VS Code发现`folding.markers`同时定义了`start`和`end`时,对应区域内就会出现折叠标记。下述配置会对`//#region`和`//#endregionJSON`区域创建代码折叠标记:
166 |
167 | ```json
168 | {
169 | "folding": {
170 | "markers": {
171 | "start": "^\\s*//\\s*#?region\\b",
172 | "end": "^\\s*//\\s*#?endregion\\b"
173 | }
174 | }
175 | }
176 | ```
177 |
178 | - 语言服务器折叠:语言服务器获取到[textDocument/foldingRange](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)请求中的代码折叠列表数据,VS Code之后根据这份列表创建折叠标记。通过语言服务器协议学习更多关于[程序性语言特性](/language-extensions/programmatic-language-features)。
179 |
180 | ## 单词匹配
181 | ---
182 |
183 | `wordPattern`定义了程序语言中*单词*单位。因此当你使用词语相关的命令,如:**Move cursor to word start**(Ctrl+Left)或者**Move cursor to word end**(Ctrl+Right)时,编辑器会根据正则寻找单词边界。
184 |
185 | ```json
186 | {
187 | "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)"
188 | }
189 | ```
190 |
191 | ## 缩进规则
192 | ---
193 |
194 | `indentationRules`定义了编辑器应该如何调整当前行或你粘贴、输入、移动的下一行缩进。
195 |
196 | ```json
197 | {
198 | "indentationRules": {
199 | "increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
200 | "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
201 | }
202 | }
203 | ```
204 |
205 | 比如,`if (true) {`匹配`increasedIndentPattern`,然后如果你在`{`后面按下Enter后,编辑器会自动缩进一次,你如代码看起来会像这样:
206 |
207 | ```javascript
208 | if (true) {
209 | console.log();
210 | ```
211 |
212 | 如果没有设置缩进规则,当行尾以开符号结尾时编辑器会左缩进,以闭合符号结尾时右缩进。这里的*开闭符号*由`brackets`定义。
213 |
214 | 注意`editor.formatOnPaste`是由[DocumentRangeFormattingEditProvider](https://code.visualstudio.com/api/references/vscode-api#DocumentRangeFormattingEditProvider)控制,而不由自动缩进控制。
--------------------------------------------------------------------------------
/docs/extension-authoring/developing-extensions.md:
--------------------------------------------------------------------------------
1 | # 开发插件
2 |
3 | 这个章节会详细地讲解如何插件的运行,调试和故障排除。这些技巧也同样适用于调试你从市场安装的问题应用。
4 |
5 | ## 创建你自己的插件
6 | ---
7 | 为了开发VS Code插件,VS Code本身提供了以下几个工具,帮你快速开发:
8 | - Yeoman生成器,用脚手脚创建一个插件
9 | - 智能补全——悬停和找到你需要的插件API
10 | - 编译Typescript(如果用Typescript实现的话)
11 | - 运行和调试插件
12 | - 发布插件
13 |
14 | 我们建议你用脚手架生成基础文件,Yeoman的`yo code`命令可以完成这个工作,生成器能极大地提升你的开发体验,我们在[插件生成器](/extension-authoring/extension-generator.md)里面有详细的步骤。
15 |
16 | !> **注意**:以下部分假设你已经掌握了`yo code`Yeoman 插件生成器,和创建`launch.json`、`task.json`的基础知识。
17 |
18 | ## 运行和调试插件
19 | ---
20 | 按下`F5`,你就可以很轻松地调试插件了。这个命令会打开一个加载了你的插件的新窗口,插件输出的信息能在`调试控制台`里找到,在这个模式下你可以打断点、一步步地调试代码,在`调试`侧边栏或者`调试控制台`(用console方法输出才可以)检查变量。
21 |
22 | 
23 |
24 | 根据插件的不同,你可能需要更多关于调试适配器或者语言服务器插件的相关教程:
25 |
26 | - [示例:语言服务器](/extension-authoring/example-language-server.md) - 学习如何实现一个语言服务器插件
27 | - [示例:调试器](/extension-authoring/developing-extensions.md) - 通过VS Code Debug Protocol接入调试器
28 |
29 | ## 编译Typescript
30 | 如果你的插件是Typescript实现的,那你首先就要把代码编译成Javascript。
31 | 编译过程的配置如下所示:
32 | - `tsconfig.json`定义了Typescript的编译选项。了解更多[Typescript wiki](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)或者[Typescript语言章节](https://code.visualstudio.com/docs/languages/typescript#_tsconfigjson)
33 | - 当前Typescript编译器的版本在`node_modules`文件夹里面
34 | - API定义在`node_modules/vscode`中
35 |
36 | 运行插件前,Typescript编译器会被触发。`.vscode/launch.json`中的`preLaunchTask`属性定义了这项工作,在调试会话启动前会先启动这个配置好的任务。
37 |
38 | 如果你的插件在很久之前就编译好了,比如依赖于`0.1.0`ts版本,而`2.0.0`已经老早就出来了,那么通过下列步骤来升级你的插件:
39 |
40 | ```json
41 | // 访问 https://go.microsoft.com/fwlink/?LinkId=733558
42 | // 查看更多关于tasks.json格式的内容
43 | {
44 | "version": "2.0.0",
45 | "tasks": [
46 | {
47 | "type": "npm",
48 | "script": "watch",
49 | "problemMatcher": "$tsc-watch",
50 | "isBackground": true,
51 | "presentation": {
52 | "reveal": "never"
53 | },
54 | "group": {
55 | "kind": "build",
56 | "isDefault": true
57 | }
58 | }
59 | ]
60 | }
61 | ```
62 |
63 | 将下列设置添加到*settings.json*中
64 |
65 |
66 | ```json
67 | // 关边tsc的自动检测任务,因为我们在添加了npm脚本
68 | "typescript.tsc.autoDetect": "off"
69 | ```
70 |
71 | 确保*launch.json.*中引用了正确的*预运行任务*:
72 | ```json
73 | "preLaunchTask": "npm: watch"
74 | ```
75 | ?> **注意:**Typescript编译器运行在watch模式下,所以任何文件变动都会自动编译
76 |
77 | ## 加载你的插件
78 | 你的插件会加载在`插件开发环境`的新窗口中。用`extensionDevelopmentPath`命令行也可以达到同样的效果,这个选项告诉VS Code去哪里查找新的插件:
79 |
80 | ```bash
81 | code --extensionDevelopmentPath=_my_extension_folder
82 | ```
83 |
84 | 一旦插件环境加载完毕,VS Code就会把调试器附加上去,然后启动调试会话。
85 |
86 | 下面是按下`F5`之后的会发生的事情:
87 |
88 | 1. `.vscode/launch.json`指示VS Code先运行一个叫做`npm`的任务
89 | 2. `.vscode/tasks.json`定义了`npm`任务,其实就是`npm run compile`的脚本命令
90 | 3. `package.json`定义`compile`为`tsc -watch -p ./`
91 | 4. 然后会调用node_modules文件夹下的Typescript编译器,然后生成`out/extension.js`和`out/extension.js.map`
92 | 5. Typescript编译成功之后,生成`code --extensionDevelopmentPath=${workspaceFolder}`进程
93 | 6. 加载运行在**扩展环境**下的第二个VS Code实例,它会搜寻`${workspaceFolder}`下的插件
94 |
95 | ## 修改你的插件
96 |
97 | 你可以在VS Code状态栏的左侧观察Typscript编译器的进度,在状态栏中你也可以找到编译的错误和警告。当编译通过之后,你必须重启**扩展开发环境**才能查看到你的修改。重启扩展开发环境有两种可选的方式:
98 |
99 | - 在调试面板中按下`重启`
100 | - 在插件开发环境中按`Ctrl+R`(macOS:Cmd+R)
101 |
102 | ## 分析你的插件
103 |
104 | 你可以用Chrome DevTool内存和CUP分析工具分析你的插件。
105 |
106 | 跟我做:
107 | 1. 在命令行中带上`--inspect-extensions=`标识,启动你的VS Code,比如:`code --inspect-extensions=9333`
108 | 2. 在VS Code的**命令面板(`Shfit + Cttl+ P`)**中选择并打开**Developer: Toggle Developer Tools**
109 | 3. 在打开的DevTool中查看`Console`标签,找到`Debugger listening on port 9333`开头的`chrome-devtools`链接信息
110 | 4. 打开你自己的Chrome浏览器,输入该链接,然后会进入DevTool提供的专用调试环境
111 | 5. 使用内存和CPU分析器,了解内存和计算资源的使用情况
112 |
113 | 
114 |
115 | ## 问题追踪
116 |
117 | 当你自己的的插件或者市场上的插件出现问题时,你可以参考这些建议进行问题追踪:
118 |
119 | #### 禁用插件,启动VS Code
120 |
121 | 通过`--disable-extensions`命令行,禁用VS Code会话中的所有插件。这个方式可以帮你逐步缩小问题范围——到底是插件问题,还是VS Code本身的问题。
122 |
123 | ```bash
124 | code --disable-extensions
125 | ```
126 | #### 使用开发工具控制台
127 |
128 | 如果你安装的插件不能正常工作,第一步工作最好是先检查一下VS Code**开发者工具**的Console面板。插件作者可能在开发插件的时候已经添加了一些日志——VS Code 运行于Electron之上,所以你可以通过Chrome**开发者工具**获得有力的支持。
129 |
130 | 通过**帮助**>**切换开发人员工具**(Windows/Linux:`Ctrl + Shift + I`,macOS:`Cmd + Shift + I`)打开**开发工具**然后选择**Console**标签。试着运行插件的功能,检查Console的输出。你可以使用`console.log`输出各类信息以及VS Code扩展环境抛出的详细异常。
131 |
132 | ?> **开发者小贴士:**当你制作插件的时候,请为用户提供有用的日志信息,你给用户的信息越多,用户越能够独立地解决问题。好的日志也同样能帮你快速地找到真正的问题所在。
133 |
134 | #### 重装插件
135 |
136 | 有时候安装插件会失败或者报错,所以任何插件不能正常工作的时候,你都可以尝试卸载然后重装插件的方式来解决。VS Code提供了一个非常方便的命令**Developer: Reinstall Extension**,输入这串命令之后,你可以在下拉框里选择你要重装的插件,最后按照指示重载VS Code就好了。
137 |
138 | #### 查看插件的README
139 |
140 | 为了正常运行,有些插件会有一些其他的依赖,比如独立的语法检查器、编译器、自定义的配置文件等。因此在侧边栏**插件**中点击某个插件后显示的**插件详情**页面会非常有用,它就是插件的**README**,其中可能包含了插件的配置和使用方式。
141 |
142 | ## FAQ
143 |
144 | **问:我怎么在插件里使用VS Code新版本引入的API?**
145 |
146 | 答:如果你想用最新的VS Code API,你可以在插件的`package.json`中的`engines`声明你想要用的引擎版本。
147 |
148 | 步骤如下:
149 |
150 | - 在`package.json`中的`engines`字段设置你的插件所需要的最低VS Code版本
151 | - 在`package.json`中添加`postinstall`脚本:
152 | ```json
153 | "scripts": {
154 | "postinstall": "node ./node_modules/vscode/bin/install"
155 | }
156 | ```
157 | - 在你的插件根目录下输入`npm install`
158 | - `vscode`模块根据`engine`中声明的版本下载对应的`vscode.d.ts`
159 | - 回到VS Code,用代码补全验证是否出现了你想要的API提示
160 |
161 | ## 下一步
162 |
163 | - [测试你的插件](/extension-authoring/testing-extensions.md) - 学习如何写单元测试和集成测试。
164 | - [发布工具](/extension-authoring/publish-extension.md) - 用vsce命令行工具发布你的插件。
165 | - [插件配置清单](/extensibility-reference/extension-manifest.md) - VS Code插件清单文件参阅。
166 | - [插件API](/extensibility-reference/vscode-api.md) - 学习更多VS Code扩展性API。
--------------------------------------------------------------------------------
/docs/extensibility-reference/api-scm.md:
--------------------------------------------------------------------------------
1 | # VS Code中的源控制
2 | ---
3 |
4 | VS Code 允许插件创作者通过扩展API去定义*源控制管理*特性(Source Control Management,SCM),VS Code整合了各式各样的SCM体系,而只给用户展现了一组小巧、强大的API接口,还是带用户界面的那种。
5 |
6 | 
7 |
8 |
9 | VS Code自带一个源控制器:Git,本篇能帮你在VS Code中加入你自己的SCM系统。
10 |
11 | 如果你需要帮助,请查看[vscode命名空间API](https://github.com/Microsoft/vscode-docs/blob/master/docs/extensionAPI/vscode-api.md#scm)。
12 |
13 | ## 源控制模型
14 | ---
15 | `SourceControl`负责生产源控制模型的实体,它里面有`SourceControlResourceState`实例的**资源状态**,而资源状态又是`SourceControlResourceGroup`实例整理成**组**的。
16 |
17 | 通过`vscode.scm.createSourceControl`创建一个新的*源控制器*。
18 |
19 | 为了更好地理解这几种实体的交互,让我们拿Git来做例子,考虑下列`git status`输出:
20 | ```bash
21 | vsce master* → git status
22 | On branch master
23 | Your branch is up-to-date with 'origin/master'.
24 | Changes to be committed:
25 | (use "git reset HEAD ..." to unstage)
26 |
27 | modified: README.md
28 | renamed: src/api.ts -> src/test/api.ts
29 |
30 | Changes not staged for commit:
31 | (use "git add/rm ..." to update what will be committed)
32 | (use "git checkout -- ..." to discard changes in working directory)
33 |
34 | deleted: .travis.yml
35 | modified: README.md
36 | ```
37 | 这个工作区里面发生了很多事,首先,`README.md`文件已经被修改了但还没有提交,然后立刻又被修改了。 其次,`src/api.ts`文件被移动到了`src/test/api.ts`,这个修改已经存备(staged), 最后,`.travis.yml`文件被删除。
38 |
39 | 对于这个工作区而言,Git定义了两个资源组:**工作中(Working tree)**和**已索引(Index)**,对于单个组而言,每次**文件修改**就会产生一些资源状态:
40 | - **已索引** - 资源组
41 | - 修改`README.md` - 资源状态
42 | - 移动`src/api.ts`到`src/test/api.ts` - 资源状态
43 | - **工作中** - 资源组
44 | - 删除`.travis.yml` - 资源状态
45 | - 修改`README.md` - 资源状态
46 |
47 | 同一个`README.md`是怎么成为两组截然不同的资源状态呢?
48 |
49 | 下面揭秘Git是如何创建这个模型的:
50 |
51 | ```typescript
52 | function createResourceUri(relativePath: string): vscode.Uri {
53 | const absolutePath = path.join(vscode.workspace.rootPath, relativePath);
54 | return vscode.Uri.file(absolutePath);
55 | }
56 |
57 | const gitSCM = vscode.scm.createSourceControl('git', "Git");
58 |
59 | const index = gitSCM.createResourceGroup('index', "Index");
60 | index.resourceStates = [
61 | { resourceUri: createResourceUri('README.md') },
62 | { resourceUri: createResourceUri('src/test/api.ts') }
63 | ];
64 |
65 | const workingTree = gitSCM.createResourceGroup('workingTree', "Changes");
66 | workingTree.resourceStates = [
67 | { resourceUri: createResourceUri('.travis.yml') },
68 | { resourceUri: createResourceUri('README.md') }
69 | ];
70 | ```
71 |
72 | 源变动和最终产生的资源组会传递到源控制视图上。
73 |
74 | ## 源控制视图
75 | ---
76 | 当源变动时,VS Code会生成源控制视图。源状态可通过`SourceControlResourceDecorations`自定义:
77 |
78 | ```typescript
79 | export interface SourceControlResourceState {
80 | readonly decorations?: SourceControlResourceDecorations;
81 | }
82 | ```
83 |
84 | 上述例子已经足以让源控制视图生成一个简单的列表,不过用户可能想要在不同的资源状态上进行不同的操作。比如,当用户点击资源状态时,会发生什么呢?资源状态提供了一个可选命令去处理这类场景:
85 | ```typescript
86 | export interface SourceControlResourceState {
87 | readonly command?: Command;
88 | }
89 | ```
90 |
91 | ### 菜单
92 |
93 | 要想提供更加丰富的交互效果,我们提供了5个源控制菜单项供你使用。
94 |
95 | `scm/title`菜单在源控制视图的顶部右上方,菜单项水平排列在标题栏中,另外一些会在`...`下拉菜单中。`scm/resourceGroup/context`和`scm/resourceState/context`是类似的,你可以通过前者自定义资源组,后者则是定义资源状态。将菜单项放在`inline`组里,可以水平在视图中展示它们。而其他的菜单项可以通过鼠标右击的形式展示在菜单中。菜单中调用的命令会传入资源状态作为参数。注意SCM视图提供多选,因此命令函数可能一次性会接收一个或多个参数。
96 |
97 | 例如,Git支持往`scm/resourceState/context`菜单中添加`git.stage`命令和使用下列方法,提供多个文件的存备(staged):
98 | ```typescript
99 | stage(...resourceStates: SourceControlResourceState[]): Promise;
100 | ```
101 | 创建它们的时候,`SourceControl`和`SourceControlResourceGroup`实例会需要你提供一个string类型的`id`,这些值最终会在`scmProvider`和`scmResourceGroup`以上下文键值的形式出现。在菜单项的`when`语法中使用这些[上下文键值](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts)。看个Git如何通过`git.stage`命令显示菜单项的:
102 | ```
103 | {
104 | "command": "git.stage",
105 | "when": "scmProvider == git && scmResourceGroup == merge",
106 | "group": "inline"
107 | }
108 | ```
109 |
110 | `scm/change/title`可以对*行内变动*配置标题栏的命令(contribute commands to the title bar of an inline change)。命令中的参数有文档的URI,变动数组,当前行内变动所在索引。例如下面是一个可以配置菜单的Git`stageChange`命令声明:
111 |
112 | ```typescript
113 | async stageChange(uri: Uri, changes: LineChange[], index: number): Promise;
114 | ```
115 |
116 | `scm/sourceControl`菜单根据环境出现在源控制实例的边上。
117 |
118 | 
119 |
120 | 最后,`scm/change/title`菜单是和快速Diff功能相关联的,越新的文件越靠前,你可以针对特定的代码变动调用命令。
121 |
122 | ### SCM 输入框
123 | 源控制输入框位于每个源控制视图的顶部,接收用户输入的信息。你可以获取(或设置)这个信息供后续使用。在Git中,比如说,这可以作为一个commit框,用户输入了提交信息后,触发`git commit`命令:
124 |
125 | ```typescript
126 | export interface SourceControlInputBox {
127 | value: string;
128 | }
129 |
130 | export interface SourceControl {
131 | readonly inputBox: SourceControlInputBox;
132 | }
133 | ```
134 |
135 | 用户可以通过Ctrl+Enter(Mac上是Cmd+Enter)接收任意信息,在`SourceControl`中的`acceptInputCommand`处理这类事件。
136 | ```typescript
137 | export interface SourceControl {
138 | readonly acceptInputCommand?: Command;
139 | }
140 | ```
141 | ## 快速Diff
142 | ---
143 | VS Code支持显示快速Diff编辑器的高亮槽,点击这些槽会出现一个内部diff交互器,你可以在这里为上下文配置命令。
144 |
145 | 
146 |
147 | 这些高亮槽是VS Code自己计算出来的,你要做的就是根据给定的文件提供原始文件内容
148 |
149 | ```typescript
150 | export interface SourceControl {
151 | quickDiffProvider?: QuickDiffProvider;
152 | }
153 | ```
154 |
155 | 使用`QuickDiffProvider`,你的实现需要告诉VS Code——参数传入的给定资源URI所对应的原始资源URI。
156 |
157 | ## 下一步
158 |
159 | 想要学习更多关于VS Code扩展性模型,请参考:
160 |
161 | * [SCM API 参考](https://code.visualstudio.com/docs/extensionAPI/vscode-api#_scm) - 查看完整的SCM API文档
162 | * [Git 插件](https://github.com/Microsoft/vscode/tree/master/extensions/git) - 学习Git插件实现
163 | * [插件API概览](/extensibility-reference/overview.md) - 学习全部的VS Code扩展性模型
164 | * [插件配置清单](/extensibility-reference/extension-manifest.md) - VS Code package.json插件配置清单参考
165 | * [发布内容配置点](/extensibility-reference/contribution-points.md) - VS Code发布内容配置点参考
166 |
--------------------------------------------------------------------------------
/docs/extension-guides/command.md:
--------------------------------------------------------------------------------
1 | # 命令
2 |
3 | 命令会触发VS Code中注册的行为,如果你[配置过键位](https://code.visualstudio.com/docs/getstarted/keybindings),那么你就处理过了命令。命令也是插件将功能暴露给用户的地方,它绑定了VS Code UI中的行为,并在内部处理了相关逻辑。
4 |
5 | ## 使用命令
6 | ---
7 |
8 | VS Code内部含有大量和编辑器交互、控制UI、后台操作的[内置命令](/references/commands)。许多插件将它们的核心功能暴露为*命令*的形式供用户或者其他插件使用。
9 |
10 | ### 程序性执行一个命令
11 |
12 | [`vscode.commands.executeCommand`](https://code.visualstudio.com/api/references/vscode-api#commands.executeCommand)API可以程序性调用一个命令,你可以通过它将VS Code的内置函数构建在你的插件中,比如VS Code内置的Git和Markdown插件中的东西。
13 |
14 | 我们看个例子🌰:`editor.action.addCommentLine`命令可以将当前选中行变成注释(你可以偷偷把这个功能地集成到你自己的插件中哦):
15 | ```typescript
16 | import * as vscode from 'vscode';
17 |
18 | function commentLine() {
19 | vscode.commands.executeCommand('editor.action.addCommentLine');
20 | }
21 | ```
22 |
23 | 有些命令可以接收改变行为的参数,有些会有返回结果。形如`vscode.executeDefinitionProvider`的API,它要求传入一个document的URI地址和position作为参数,并返回一个包含定义列表的promise:
24 |
25 | ```typescript
26 | import * as vscode from 'vscode';
27 |
28 | async function printDefinitionsForActiveEditor() {
29 | const activeEditor = vscode.window.activeTextEditor;
30 | if (!activeEditor) {
31 | return;
32 | }
33 |
34 | const definitions = await vscode.commands.executeCommand(
35 | 'vscode.executeDefinitionProvider',
36 | activeEditor.document.uri,
37 | activeEditor.selection.active
38 | );
39 |
40 | for (const definition of definitions) {
41 | console.log(definition);
42 | }
43 | }
44 | ```
45 |
46 | 更多命令详见:
47 | - [浏览键盘快捷键](https://code.visualstudio.com/docs/getstarted/keybindings)
48 | - [VS Code内置高级命令API](/references/commands)
49 |
50 | ### 命令的URLs
51 |
52 | 命令URI是执行注册命令的链接。它们可被用于悬停文本上的可点击链接,代码补全提示中的细节信息,甚至可以出现在webview中。
53 |
54 | 命令URI使用`command`作为协议头,然后紧跟着命令名称。比如:`editor.action.addCommentLine`的命令URI是:`command:editor.action.addCommentLine`。下面是一个显示在当前行注释中显示链接的悬停函数。
55 |
56 | ```typescript
57 | import * as vscode from 'vscode';
58 |
59 | export function activate(context: vscode.ExtensionContext) {
60 | vscode.languages.registerHoverProvider(
61 | 'javascript',
62 | new class implements vscode.HoverProvider {
63 | provideHover(
64 | _document: vscode.TextDocument,
65 | _position: vscode.Position,
66 | _token: vscode.CancellationToken
67 | ): vscode.ProviderResult {
68 | const commentCommandUri = vscode.Uri.parse(`command:editor.action.addCommentLine`);
69 | const contents = new vscode.MarkdownString(`[Add comment](${commentCommandUri})`);
70 |
71 | // command URIs如果想在Markdown 内容中生效, 你必须设置`isTrusted`。
72 | // 当创建可信的Markdown 字符, 请合理地清理所有的输入内容
73 | // 以便你期望的命令command URIs生效
74 | contents.isTrusted = true;
75 |
76 | return new vscode.Hover(contents);
77 | }
78 | }()
79 | );
80 | }
81 | ```
82 |
83 | 命令上的参数列表会从JSON数组变成URI格式:下面的例子使用了`git.stage`命令创建一个悬停操作——将当前文件进行git暂存:
84 |
85 | ```typescript
86 | import * as vscode from 'vscode';
87 |
88 | export function activate(context: vscode.ExtensionContext) {
89 | vscode.languages.registerHoverProvider(
90 | 'javascript',
91 | new class implements vscode.HoverProvider {
92 | provideHover(
93 | document: vscode.TextDocument,
94 | _position: vscode.Position,
95 | _token: vscode.CancellationToken
96 | ): vscode.ProviderResult {
97 | const args = [{ resourceUri: document.uri }];
98 | const commentCommandUri = vscode.Uri.parse(
99 | `command:git.stage?${encodeURIComponent(JSON.stringify(args))}`
100 | );
101 | const contents = new vscode.MarkdownString(`[Stage file](${commentCommandUri})`);
102 | contents.isTrusted = true;
103 | return new vscode.Hover(contents);
104 | }
105 | }()
106 | );
107 | }
108 | ```
109 |
110 | ## 新建命令
111 | ---
112 |
113 | ### 注册一个命令
114 |
115 | [`vscode.commands.registerCommand`](https://code.visualstudio.com/api/references/vscode-api#commands.registerCommand)会把命令ID绑定到你插件的函数上:
116 | ```typescript
117 | import * as vscode from 'vscode';
118 |
119 | export function activate(context: vscode.ExtensionContext) {
120 | const command = 'myExtension.sayHello';
121 |
122 | const commandHandler = (name?: string = 'world') => {
123 | console.log(`Hello ${name}!!!`);
124 | };
125 |
126 | context.subscriptions.push(vscode.commands.registerCommand(command, commandHandler));
127 | }
128 | ```
129 |
130 | 只要`myExtension.sayHello`命令执行,就会调用对应的处理函数,你也可以通过`executeCommand`程序性调用它,或者从VS Code UI中,抑或快捷键的方式调用。
131 |
132 | ### 创建面向用户的命令
133 | `vscode.commands.registerCommand`仅仅是将命令id绑定到了处理函数上,如果想让用户从*命令面板*中找到你的命令,你还需要在`package.json`中配置对应的命令`配置项(contribution)`:
134 | ```json
135 | {
136 | "contributes": {
137 | "commands": [
138 | {
139 | "command": "myExtension.sayHello",
140 | "title": "Say Hello"
141 | }
142 | ]
143 | }
144 | }
145 | ```
146 |
147 | `commands`配置告诉VS Code你的插件提供了一个命令,而且允许你控制命令在UI中的显示。现在,我们的命令终于出现在*命令面板*中了:
148 |
149 | 
150 |
151 | 我们依旧需要使用`registerCommand`将真实的命令id绑定到函数上。也就是说,如果我们的插件没有激活,那么用户从*命令面板*中选择`myExtension.sayHello`也不会有任何效果。为了避免这种事,插件必须注册一个面向全部用户场景的命令`onCommand` `activiationEvent`:
152 | ```json
153 | {
154 | "activationEvents": ["onCommand:myExtension.sayHello"]
155 | }
156 | ```
157 | 现在当用户第一次调用`myExtension.sayHello`时,插件就会自动激活,`registerCommand`会将`myExtension.sayHello`绑定到正确的处理函数上。
158 |
159 | 对于内部命令你不需要使用`onCommand`,但是下面的场景中你必须定义好激活事件:
160 | - 需要使用*命令面板*调用
161 | - 需要快捷键调用
162 | - 需要通过VS Code UI调用,比如在编辑器标题栏上触发
163 | - 意在供其他插件使用时
164 |
165 | ### 控制命令出现在*命令面板*的时机
166 |
167 | 默认情况下,所有*命令面板*中出现的命令都可以在`package.json`的`commands`部分中配置。不过,有些命令是场景相关的,比如在特定的语言的编辑器中,或者只有用户设置了某些选项时才展示。
168 |
169 | [`menus.commandPalette`](/references/contribution-points#contributesmenus)发布内容配置运行你限制命令出现在*命令面板*的时机。你需要配置命令ID和一条[when语句](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts):
170 | ```json
171 | {
172 | "contributes": {
173 | "menus": {
174 | "commandPalette": [
175 | {
176 | "command": "myExtension.sayHello",
177 | "when": "editorLangId == markdown"
178 | }
179 | ]
180 | }
181 | }
182 | }
183 | ```
184 |
185 | 现在`myExtension.sayHello`命令只会出现在用户的Markdown文件中了。
--------------------------------------------------------------------------------
/docs/extension-authoring/publish-extension.md:
--------------------------------------------------------------------------------
1 | # 发布插件
2 |
3 | ## vsce —— 发布工具参阅
4 |
5 | [vsce](https://github.com/Microsoft/vsce)是一个用于将插件发布到[市场](https://code.visualstudio.com/docs/editor/extension-gallery)上的命令行工具。
6 |
7 | ## 安装
8 | ---
9 | 请确认本机已经安装了[Node.js](https://nodejs.org/),然后运行:
10 | ```bash
11 | npm install -g vsce
12 | ```
13 |
14 | ## 使用
15 | ---
16 | 然后你就可以在命令行里直接使用`vsce`了。下面是一个快速发布的示例(在你登录和打包好插件之后):
17 |
18 | ```bash
19 | $ vsce publish
20 | Publishing uuid@0.0.1...
21 | Successfully published uuid@0.0.1!
22 | ```
23 |
24 | 更多可用的命令参数,请使用`vsce --help`
25 |
26 | ## 发布教程
27 | ---
28 | !> **注意:** 出于安全考虑,`vsce`不会发布包含用户提供SVG图片的插件。
29 |
30 | 发布工具会检查以下内容:
31 | - `pacakge.json`文件中的icon不可以是SVG。
32 | - `pacakge.json`中的标记不可以是SVG,除非来自于[可靠的图标来源](/extensibility-reference/extension-manifest.md#使用认证过的标志)
33 | - `README.md`和`CHANGELOG.md`中的图片链接需要使用`https`协议
34 | - `README.md`和`CHANGELOG.md`中的图片不可以是SVG,除非来自[可靠的图标来源](/extensibility-reference/extension-manifest.md#使用认证过的标志)
35 |
36 | ---
37 |
38 | VS Code插件市场的服务是[Visual Studio Team Services](https://visualstudio.microsoft.com/team-services)提供的,因此验证、代理、管理插件都是由这个服务提供的。
39 |
40 | `vsce`只能用[Personal Access Tokens](https://docs.microsoft.com/vsts/integrate/get-started/authentication/pats)发布插件,所以至少要创建一个Token以便发布插件。
41 |
42 | #### 获取Personal Access Token
43 |
44 | 首先,请先确保你有一个[Visual Studio Team Services](https://docs.microsoft.com/vsts/accounts/create-account-msa-or-work-student)账户。
45 |
46 | 下面的例子里,我们假设账户名为`monacotools`,从你的账户主页(例如:https://monacotools.visualstudio.com )进入**安全(Security)**页:
47 |
48 | 
49 |
50 | 点击**添加(Add)**创建一个新的Personal Access Token:
51 |
52 | 
53 |
54 | 给Personal Access Token添加描述,过期时间等等,给它设置**最高权限**:
55 |
56 | 
57 |
58 | 然后会显示你刚创建好的Personal Access Token,复制好,待会我们还要用来发布插件呢。
59 |
60 | #### 创建一个发行方
61 |
62 | **发行方**是VS Code市场有权发布插件的唯一标识,每个插件的[package.json](/extensibility-reference/extension-manifest.md)文件都包含着`publisher`字段。
63 |
64 | 现在我们已经有了[Personal Access Token](#获取Personal-Access-Token),我们马上可以用`vsce`创建一个发行方:
65 |
66 | ```bash
67 | vsce create-publisher (publisher name)
68 | ```
69 | `vsce`会记住这个Personal Access Token,日后再用这个发行方的时候会自动带上。
70 |
71 | ?> 注意:另外,你也可以在市场的发行方[管理页](https://marketplace.visualstudio.com/manage)中创建发行方,然后用这个账号登录`vsce`。
72 |
73 | #### 发行方登录
74 |
75 | 如果你已经有发行方账号了:
76 | ```bash
77 | vsce login (publisher name)
78 | ```
79 | 和`create-publisher`命令类似地,`vsce`会要求输入你的Personal Access Token。
80 |
81 | 你也可以用命令参数`-p `直接登录发行方然后立即发布插件:
82 |
83 | ```bash
84 | vsce publish -p
85 | ```
86 |
87 | #### 增量更新插件版本
88 |
89 | 用SemVer语义标识符:`major`,`minor`,`patch`增量更新插件版本号。
90 |
91 | 例如,你想把插件从1.0.0更新到1.1.0,那么加上`minor`:
92 | ```bash
93 | vsce publish minor
94 | ```
95 |
96 | 插件`package.json`的version会先更新,然后才发布插件。
97 |
98 | 你也可以通过命令行指定版本号:
99 | ```bash
100 | vsce publish 2.0.1
101 | ```
102 |
103 | #### 下架插件
104 |
105 | 通过指定插件id`publisher.extension`下架插件:
106 | ```
107 | vsce unpublish (publisher name).(extension name)
108 | ```
109 | !> **注意:**当你下架插件的时候,市场会移除所有插件的历史统计数据,请在下架前再三考虑,最好还是更新插件吧。
110 |
111 | #### 插件打包
112 | 你也可能只是想打包一下插件,而不是发布到商店里。用下列命令将插件打包到`.vsix`文件中:
113 |
114 | ```
115 | vsce package
116 | ```
117 |
118 | 这个命令会在当前目录生成一个`.vsix`文件,直接从`.vsix`安装插件是允许的,查看[从VSIX安装插件](https://github.com/Microsoft/vscode-docs/blob/master/docs/editor/extension-gallery.md#install-from-a-vsix)了解更多内容。
119 |
120 | #### VS Code版本兼容性
121 |
122 | 当你制作插件的时候,你需要描述插件对VS Code的版本兼容性——修改`package.json`中的`engines.vscode`:
123 |
124 | ```json
125 | {
126 | "engines": {
127 | "vscode": "^1.8.0"
128 | }
129 | }
130 | ```
131 |
132 | `1.8.0`标识你的插件只与`1.8.0`的VS Code兼容,`^1.8.0`则表示你的插件向上兼容,包括`1.8.1, 1.9.0`等等。
133 |
134 | 用`engines.vscode`可以保证插件的安装环境包含了插件依赖的API。这个机制在稳定版和Insider版本都适用。
135 |
136 | 例如,最新的稳定版VS Code版本是`1.8.0`,而新的API在`1.9.0`开发版中释出,所以用`1.9.0-insider`标识插件在Insider版本中都可用。
137 | 如果你想发布一个使用这些API的插件,则设置版本依赖为`^1.9.0`,你的插件则只能安装在`>=1.9.0`的VS Code上,也就意味着所有当前的Insider版本都可以用得上,而稳定版只有在更新到`1.9.0`才能使用。
138 |
139 | ## 进阶用法
140 | ---
141 |
142 | #### 符合市场的插件
143 |
144 | 你可以自定义插件在市场中的外观,查看示例`GO 插件`。
145 |
146 | 下面是一些让你的插件在市场看起来更酷的小提示:
147 |
148 | - 插件根目录下面的`README.md`文件可以用来填充插件市场的页面内容。`vsce`会将README中的链接通过下面两种方式修改掉:
149 | - 如果你的`package.json`的`repository`字段是一个Github仓库,`vsce`会自动检测,然后相应地调整链接。
150 | - 运行`vsce package`时,加上`--baseContentUrl`和`--baseImagesUrl`标识重载上述行为。
151 | - 插件根目录下的`LICENSE`会成为插件的license。
152 | - 同样的`CHANGELOG.md`文件会成为插件的发布日志。
153 | - 通过设置`package.json`的`galleryBanner.color`hex值,修改banner的背景色。
154 | - 通过给`package.json`中的`icon`设置一个相对路径,可以将一个`128px`的PNG图标放进你的插件中。
155 |
156 | - 参见[插件市场展现小提示](https://github.com/Microsoft/vscode-docs/blob/master/docs/extensionAPI/extension-manifest.md#marketplace-presentation-tips)
157 |
158 | #### `.vscodeignore`
159 |
160 | 创建一个`.vscodeignore`文件可以排除插件目录中的内容。这个文件支持[glob](https://github.com/isaacs/minimatch)模式,每个表达式一行。
161 |
162 | 例如:
163 | ```
164 | **/*.ts
165 | **/tsconfig.json
166 | !file.ts
167 | ```
168 | 你应该忽略哪些不必在运行时用到的文件。例如:你的插件是用Typescript写的,那么你就应该忽略所有的`**/*.ts`文件。
169 |
170 | ?> **注意:**在`devDependencies`列出的开发依赖会被自动忽略,你不必将他们加入到`.vscodeignore`中。
171 |
172 | ##### 预发布步骤
173 |
174 | 你是可以在清单文件中添加预发布步骤的,下面的命令会在插件每次打包时执行:
175 | ```json
176 | {
177 | "name": "uuid",
178 | "version": "0.0.1",
179 | "publisher": "joaomoreno",
180 | "engines": {
181 | "vscode": "0.10.x"
182 | },
183 | "scripts": {
184 | "vscode:prepublish": "tsc"
185 | }
186 | }
187 | ```
188 | 上面的示例会在每次插件打包时调用Typescript编译器。
189 |
190 | ## FAQ
191 | ##### 问:当我发布插件时遇到了403 Forbidden(或 401 Unauthorized)错误?
192 |
193 | 答:很有可能是你创建PAT (Personal Access Token) 时没有选择`all accessible accounts`。你还需要将授权范围设置为`All scopes`,这样发布工具才能工作。
194 |
195 | ##### 问:我没办法用`vsce`工具下架插件?
196 |
197 | 答:你可能改变了插件ID或者发行方名称。不过你还可以在[管理页面](https://marketplace.visualstudio.com/manage)发布或者下架插件。
198 |
199 | ##### 问:为什么vsce不保留文件属性?
200 |
201 | 答:请注意,当你在Windows平台构建和发布插件的时候,所有插件包内的文件会缺失POSIX文件属性,或称之为执行位(executable bit)的东西。一些基于这些属性的`node_modules`依赖则会调整工作方式。从Linux和macOS平台构建则会如预期执行。
202 |
203 | ## 下一步
204 | - [插件市场](https://code.visualstudio.com/docs/editor/extension-gallery) - 学习更多VS Code公共插件市场。
205 | - [测试插件](/extension-authoring/testing-extensions.md) - 添加插件测试,提高插件质量。
206 |
--------------------------------------------------------------------------------
/docs/extensibility-reference/api-debugging.md:
--------------------------------------------------------------------------------
1 | # 调试器API
2 |
3 | VS Code 允许插件创作者制作新的**调试器插件**,或为已有的调试功能添加**特性**。
4 |
5 | 我们提供了两个领域的API:
6 | - 通过成熟又强力的**协议**生成新的VS Code常规调试界面
7 | - 常规的**插件API**,但不包括VS Code的全部调试功能
8 |
9 | 提供这两种截然不同的API是因为VS Code的“热拔插调试器”插件架构(我们不会移除向后兼容的基于*协议*的调试器连接方法)
10 |
11 | 下列图片展示了两种API在VS Code中协作的方式:
12 |
13 | 
14 |
15 | **调试适配器**是一个典型的脱机程序,通过**调试适配器协议(Debug Adapter Protocol)**连接到真实的调试器和具体的调试器API上。因为调试适配器可以用任意的语言实现,因此非常适合先前*已有*的调试器或者运行时存在的场合,连接协议比实现了协议本身的客户端库提供的API更加重要。
16 |
17 | 调试适配器并不是VS Code本身的插件,而要创作者封装出一个调试器插件才行,不要担心,你并不需要写太多代码。这个插件只是一个容器,在`package.json`中只要提供必要的**配置项**即可。当调试器会话运行后,VS Code会进入调试器插件,启动调试适配器,然后用调试适配器协议通信。
18 |
19 | 下面是我们为调试器插件提供的最新的API。
20 |
21 | ## 调试器插件API
22 | ---
23 |
24 | 所有供调试使用的插件API都在`vscode.debug`下的命名空间中,你可以在[vscode 命名空间API参考](https://github.com/Microsoft/vscode-docs/blob/master/docs/extensionAPI/vscode-api.md#debug)中查看。
25 |
26 | #### 调试型钩子
27 |
28 | 所有调试型的钩子都在`DebugConfigurationProvider`中。
29 |
30 | `registerDebugConfigurationProvider`注册了`DebugConfigurationProvider`,调试器类型本身是在[发布内容配置项`debugger`](https://github.com/Microsoft/vscode-docs/blob/master/docs/extensionAPI/extension-points.md#contributesdebuggers)中配置的。
31 |
32 | 当前,你可以使用的钩子有:
33 |
34 | - 只要VS Code通过`launch.json`初始化出一个新的调试器配置就会为所有注册了`DebugConfigurationProviders`的插件调用`provideDebugConfigurations`,然后合并返回的调试器配置,注入到package.json中。
35 | - VS Code每次在启动调试会话前调用`resolveDebugConfiguration`方法,`resolveDebugConfiguration`的实现可以通过给调试配置中传入缺省值或者添加/改变/移除配置项的方式和调试配置“通信”。通过这个机制,甚至可以实时调整调试类型。
36 |
37 | - `debugAdapterExecutable`方法会在VS Code启动了调试器之后调用,这个方法会返回调试适配器执行的路径(接受可选参数)。如果没有实现这个方法,VS Code则会使用package.json中配置的静态路径。
38 |
39 |
40 | #### 调试会话生命周期API
41 |
42 | 一个调试会话在插件API中表示为`DebugSession`,它的声明周期可以通过下列方式控制和追踪:
43 | - `startDebugging `:调试启动时触发,可以接受一个命名的调试器/复合配置/内存中的配置。
44 | - `onDidStartDebugSession`:调试会话启动后触发。
45 | - 当前激活的调试会话可由变量`activeDebugSession`获得,调试会话变动反应在`onDidChangeActiveDebugSession`事件中。
46 | - `onDidTerminateDebugSession`:调试会话关闭后触发。
47 |
48 | #### 调试会话API
49 |
50 | 目前调试会话的API还比较少:
51 |
52 | - 通过`customRequest`方法将调试适配器的请求发送到受控调试方。
53 | - 自定义的调试适配器事件在`onDidReceiveDebugSessionCustomEvent`中获取。
54 |
55 | #### 断点API
56 |
57 | 所有的断点类型都是`Breakpoint`的子类,当前提供的子类有`SourceBreakpoint`和`FunctionBreakpoint`
58 |
59 | - `vscode.debug.breakpoints`提供了工作区所有的断点集合。用`instanceof`检查单个断点的具体对象类型。
60 | - `vscode.debug.onDidChangeBreakpoints`侦听断点的添加、移除、改变事件。
61 | - `SourceBreakpoints`和`FunctionBreakpoints`只能通过`addBreakpoints`和`removeBreakpoints`函数添加。
62 |
63 | !>注意:一开始获取断点可能是一个空数组,而随后则会触发`BreakpointsChangeEvent`事件并更新`vscode.debug.breakpoints`,在这个时间点你就能获得正确的集合。所以如果你需要正确的断点集合,不要忘了注册`BreakpointsChangeEvent`事件。
64 |
65 | ## 调试适配器协议(DAP - Debug Adapter Protocol)
66 | ---
67 | 你可以在[vscode-debugadapter-node](https://github.com/Microsoft/vscode-debugadapter-node)仓库中找到JSON格式或者TypeScript定义的[调试适配器协议](https://microsoft.github.io/debug-adapter-protocol)规格说明书。这两个文件都详细地列出了每个协议的请求、响应和事件结构。这个协议在NPM库[vscode-debugprotocol](https://www.npmjs.com/package/vscode-debugprotocol)中也可以找到。
68 |
69 | 我们已经实现了调试适配器协议的TypeScript和C#客户端版本,不过只有JavaScript/TypeScript的客户端库在NPM[vscode-debugadapter-node](https://github.com/Microsoft/vscode-debugadapter-node)中是可用的。C#的库可以在[Mono Debug](https://github.com/Microsoft/vscode-mono-debug/blob/master/src/DebugSession.cs)中找到。
70 |
71 | 下列调试器插件项目会教你如何实现调试适配器:
72 |
73 | GitHub项目 | 描述 | 实现语言
74 | --- | --- | ---
75 | [Mock Debug](https://github.com/Microsoft/vscode-mock-debug.git) | 一个假的调试器 | TypeScript
76 | [Node Debug2](https://github.com/Microsoft/vscode-node-debug2.git) | 内建的基于CDP-based的Node.js调试器 |TypeScript
77 | [Node Debug](https://github.com/Microsoft/vscode-node-debug.git) | 内建的传统Node.js调试器 |TypeScript
78 | [Mono Debug](https://github.com/Microsoft/vscode-mono-debug.git) | 一个供Mono使用的简单的C#调试器 | C#
79 |
80 | ## 一语道破——调试适配器协议
81 | ---
82 | 我们快速地看一下VS Code和调试器间的互动,这应该能帮你快速地实现基于调试适配器协议的调试适配器。
83 |
84 | 调试器会话启动,VS Code加载调试适配器,通过*stdin*和*stdout*进行通信。VS Code发送了一个**初始化**请求,然后用*行列值是0,1*的路径格式信息(原生或URI)配置好调试器。如果你的调试器是TypeScript或C#实现的`Debugsession`中派生出来的,你则不需要自行处理初始化请求。
85 |
86 | 根据用户创建的启动配置文件中的*请求*属性,VS Code会发送*加载(launch)*或是*附加(attch)*请求。对于**加载**类型,调试适配器需要加载一个运行时或者可以调试的程序。如果这个程序可以通过stdin/stdout和用户进行交互,那么调试适配器就会在一个可交互的终端或者控制台加载这个程序。对于**附加**类型,调试适配器则会连接或者附加到一个已经运行的程序上面。
87 |
88 | 因为这两种请求的参数都高度依赖特定的调试适配器实现,所以调试适配器协议不提供任何参数描述。而VS Code则会把所有用户启动配置传给*加载*或*附加*请求。这两种属性的智能补全和悬停信息提示可以在适配器插件中的`package.json`进行配置,以帮助用户知道何时可以创建或编辑*启动配置*。
89 |
90 | VS Code会帮调试适配器保留断点,所以必须要在调试会话启动时,对应地注册适配器中的断点。因为VS Code不知道注册断点的最佳时机,所以调试适配器会发送一个**initialize**事件给VS Code,告知它已经准备好接收断点配置请求了。
91 |
92 | 然后VS Code就会调用断点配置请求,发送所有的断点:
93 |
94 | * **setBreakpoints** 为每个源文件带上断点,
95 | * **setFunctionBreakpoints** 如果调试适配器支持函数断点,
96 | * **setExceptionBreakpoints** 如果调试适配器支持异常选项,
97 | * **configurationDoneRequest** 指示配置序列已经结束。
98 |
99 | 所以,当你准备好了的时候不要忘了发送*initialize*事件接收断点。不然已保留的断点不会再储存下来。
100 |
101 | *setBreakpoints*请求为文件设置所有已存在的断点(所以不是增量的哦)。常见的场景就是为某个文件清除所有的断点,然后再根据请求设置断点。*setFunctionBreakpoints*和*setFunctionBreakpoints*需要返回真正的断点,然后VS Code就会动态地更新UI,如果断点无法跟随请求设置然后就会在后台移除。
102 |
103 | 当程序停止(在程序入口,断点命中,抛出异常,或者用户需要暂停执行),那么调试适配器必须发出**stopped**事件,带上原因和线程id。根据这条信息,VS Code会先请求**threads**(见下),然后列出停止的线程的堆栈追踪日志(栈帧列表)。如果用户深入到栈帧(stack frame)中去,VS Code会先请求这个栈帧的**scopes**,然后是这个scope的变量。如果变量是自构建的,VS Code会通过额外的*variables*请求获取它的属性。这个过程会生成下列事件层级:
104 |
105 | ```
106 | Threads
107 | Stackframes
108 | Scopes
109 | Variables
110 | ...
111 | Variables
112 | ```
113 | VS Code调试界面支持多线程(如果你只用Node.js调试器的话可能还不知道这个功能)。当VS Code接收到**stopped**或**thread**事件,然后它会立刻请求当时所有的**threads**并显示到界面上。如果只检测到一个线程,VS Code则会保留在单线程模式。**Thread**事件是可选的,不过调试适配器可以强制发送这个事件,即使不在暂停状态,VS Code也会动态地更新线程界面。
114 |
115 | 成功地**加载**或**附加**了调试适配器后,VS Code会发送**threads**请求当前线程,获取线程基线,然后开始侦听**threads**事件检查是否有新的或是终止的线程。即使你的调试适配器不支持多线程,它也必须实现**threads**请求,然后返回一个(虚假的)线程。线程的id必须要在所有需要线程id的地方消费掉,比如:**stacktrace**, **pause**,**continue**,**next**,**stepIn**,和 **stepOut**。
116 |
117 | 当发送**disconnect**请求时,VS Code会终止调试会话。如果调试目标在*加载*时被断开了,那么则会终止目标程序(如果必要的话,会强制终止)。如果调试目标在*附加*初始化时断开,那么则会立刻断开目标(程序则会继续执行)。在目标正常终止或崩溃时,调试适配器必须触发一个**terminated**事件。收到断开请求后,VS Code就会关闭调试适配器。
118 |
119 | ## 下一步
120 | 学习更多VS Code扩展性模型,请参阅下列主题:
121 |
122 | * [示例:调试器](/extension-authoring/example-debug-adapter.md) - 查看一个可执行的'模拟'调试器示例。
123 | * [插件API概览](/extensibility-reference/vscode-api.md) - 学习完整的VS Code扩展性模型。
124 | * [插件配置清单](/extensibility-reference/extension-manifest.md) - VS Code的package.json插件配置清单参阅
125 | * [发布内容配置](/extensibility-reference/contribution-points.md) - VS Code发布内容配置参阅
--------------------------------------------------------------------------------
/docs/preknowledge/class.md:
--------------------------------------------------------------------------------
1 | # 类
2 |
3 | 传统的JavaScript程序使用**函数**和基于**原型**的继承来创建可重用的组件。Typescript则在此基础上加入了语法糖**类**。
4 |
5 | 了解Javascript的基础内容请参考[对象原型](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes)。
6 |
7 | ## 声明类
8 |
9 | 我们首先来看一个例子
10 |
11 | ```typescript
12 | class Greeter {
13 | greeting: string;
14 | constructor(message: string) {
15 | this.greeting = message;
16 | }
17 | greet() {
18 | return "Hello, " + this.greeting;
19 | }
20 | }
21 |
22 | let greeter = new Greeter("world");
23 | ```
24 |
25 | 如果你使用过C#或Java,你会对这种语法非常熟悉。 我们声明一个 Greeter类。这个类有3个成员:一个叫做 greeting的属性,一个构造函数和一个 greet方法。
26 |
27 | 你会注意到,我们在引用任何一个类成员的时候都用了 this。 它表示我们访问的是类的成员。
28 |
29 | 最后一行,我们使用 new构造了 Greeter类的一个实例。 它会调用之前定义的构造函数,创建一个 Greeter类型的新对象,并执行构造函数初始化它。
30 |
31 | ## 继承
32 |
33 | 在TypeScript里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。
34 |
35 | 看下面的例子:
36 |
37 | ```typescript
38 | class Animal {
39 | move(distance: number = 0) {
40 | console.log(`Animal moved ${distance}m.`);
41 | }
42 | }
43 |
44 | class Dog extends Animal {
45 | bark() {
46 | console.log('Woof! Woof!');
47 | }
48 | }
49 |
50 | const dog = new Dog();
51 | dog.bark();
52 | dog.move(10);
53 | dog.bark();
54 | ```
55 |
56 | 这个例子展示了最基本的继承:类从基类中继承了属性和方法。这里,`Dog`是一个 *派生类*,通过 `extends`关键字使它从 `Animal` 基类中派生出来。 派生类通常被称作 子类,基类通常被称作 超类或父类。
57 |
58 | 因为 Dog继承了 Animal的功能,因此我们可以创建一个 Dog的实例,它能够 bark()和 move()。
59 |
60 | 下面我们来看个更加复杂的例子。
61 |
62 | ```typescript
63 | class Animal {
64 | name: string;
65 | constructor(theName: string) { this.name = theName; }
66 |
67 | move(distance: number = 0) {
68 | console.log(`${this.name} moved ${distance}m.`);
69 | }
70 | }
71 |
72 | class Snake extends Animal {
73 | constructor(name: string) { super(name); }
74 |
75 | move(distance = 5) {
76 | console.log("Slithering...");
77 | super.move(distance);
78 | }
79 | }
80 |
81 | class Horse extends Animal {
82 | constructor(name: string) { super(name); }
83 |
84 | move(distance = 45) {
85 | console.log("Galloping...");
86 | super.move(distance);
87 | }
88 | }
89 |
90 | let sam = new Snake("Sammy the Python");
91 | let tom: Animal = new Horse("Tommy the Palomino");
92 |
93 | sam.move();
94 | tom.move(34);
95 | ```
96 |
97 | 与前一个例子的不同点是,这次两个派生类包含了一个构造函数,它**必须调用 `super()`**,它会执行基类的构造函数。 而且,在构造函数里访问 this的属性之前,我们 **必须**要调用 super(),这个是TypeScript强制规定的。
98 |
99 | ## 修饰符
100 |
101 |
102 | #### public
103 |
104 | 如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用 `public`来做修饰;例如,C#要求必须明确地使用 `public`指定成员是可见的。 在TypeScript里,成员都默认为 `public`。在上面的例子里,我们可以自由的访问程序里定义的成员。
105 |
106 | #### private
107 |
108 | 当成员被标记成 `private`时,它就不能在声明它的类的外部访问。比如
109 |
110 | ```typescript
111 | class Animal {
112 | private name: string;
113 | constructor(theName: string) { this.name = theName; }
114 | }
115 |
116 | new Animal("Cat").name; // 错误: 'name' 是私有的.
117 | ```
118 |
119 | #### protected
120 | protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。
121 | ```typescript
122 | class Person {
123 | protected name: string;
124 | constructor(name: string) { this.name = name; }
125 | }
126 |
127 | class Employee extends Person {
128 | private department: string;
129 |
130 | constructor(name: string, department: string) {
131 | super(name)
132 | this.department = department;
133 | }
134 |
135 | public getElevatorPitch() {
136 | return `Hello, my name is ${this.name} and I work in ${this.department}.`;
137 | }
138 | }
139 |
140 | let howard = new Employee("Howard", "Sales");
141 | console.log(howard.getElevatorPitch());
142 | console.log(howard.name); // 错误
143 | ```
144 |
145 | 注意,我们不能在 `Person`类外使用 `name`,但是我们仍然可以通过 `Employee`类的**实例方法**访问,因为 `Employee`是由 `Person`派生而来的。
146 |
147 | #### readonly
148 |
149 | 你可以使用 `readonly`关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
150 |
151 | ```typescript
152 | class Octopus {
153 | readonly name: string;
154 | readonly numberOfLegs: number = 8;
155 | constructor (theName: string) {
156 | this.name = theName;
157 | }
158 | }
159 | let dad = new Octopus("Man with the 8 strong legs");
160 | dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.
161 | ```
162 |
163 | #### get/set
164 |
165 | TypeScript支持通过`getters/setters`来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
166 |
167 | 下面来看如何把一个简单的类改写成使用 `get`和 `set`。 首先,我们从一个没有使用存取器的例子开始。
168 |
169 | ```typescript
170 | class Employee {
171 | fullName: string;
172 | }
173 |
174 | let employee = new Employee();
175 | employee.fullName = "Bob Smith";
176 | if (employee.fullName) {
177 | console.log(employee.fullName);
178 | }
179 | ```
180 |
181 | 下面这个版本里,我们先检查用户密码是否正确,然后再允许其修改员工信息。 我们把对 `fullName`的直接访问改成了可以检查密码的 set方法。 我们也加了一个 get方法,让上面的例子仍然可以工作。
182 | ```typescript
183 | let passcode = "secret passcode";
184 |
185 | class Employee {
186 | private _fullName: string;
187 |
188 | get fullName(): string {
189 | return this._fullName;
190 | }
191 |
192 | set fullName(newName: string) {
193 | if (passcode && passcode == "secret passcode") {
194 | this._fullName = newName;
195 | }
196 | else {
197 | console.log("Error: Unauthorized update of employee!");
198 | }
199 | }
200 | }
201 |
202 | let employee = new Employee();
203 | employee.fullName = "Bob Smith";
204 | if (employee.fullName) {
205 | alert(employee.fullName);
206 | }
207 | ```
208 |
209 | #### static
210 |
211 | 到目前为止,我们只讨论了类的实例成员,那些仅当类被实例化的时候才会被初始化的属性。 我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。
212 |
213 | ```typescript
214 | class Router {
215 | static baseRoute = '/basePath';
216 | calculateRoute(path: string) {
217 | return Router.baseRoute + this.commonPrefix + path;
218 | }
219 | constructor (public commonPrefix: string) { }
220 | }
221 |
222 | let route1 = new Router('/api'); // 一级路径为/api
223 | let route2 = new Router('/page'); // 一级路径为/page
224 |
225 | console.log(route1.calculateRoute('/main')); // 最终路径/basePath/api/main
226 | console.log(route2.calculateRoute('/user')); // 最终路径/basePath/page/user
227 | ```
228 |
229 | #### abstract
230 |
231 | 抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 `abstract`关键字是用于定义抽象类和在抽象类内部定义抽象方法。
232 |
233 | ```typescript
234 | abstract class Animal {
235 | abstract makeSound(): void;
236 | move(): void {
237 | console.log('roaming the earch...');
238 | }
239 | }
240 | ```
241 |
242 | ## 下一步
243 |
244 | - [接口和命名空间](/preknowledge/interface-and-namespace) - 进一步了解Typescript所规定的语法
245 |
246 |
--------------------------------------------------------------------------------
/docs/extension-guides/scm-provider.md:
--------------------------------------------------------------------------------
1 | # 源控制API
2 |
3 | VS Code 允许插件创作者通过扩展API去定义*源控制管理*特性(Source Control Management,SCM),VS Code整合了各式各样的SCM体系,而只给用户展现了一组小巧、强大的API接口,还是带用户界面的那种。
4 |
5 | 
6 |
7 |
8 | VS Code自带一个源控制器:Git,它是源控制API的最佳实践。如果你想构建你自己的SCM供应器,那么这是[一个很好的起点](https://github.com/Microsoft/vscode/blob/master/extensions/git/src/repository.ts)。
9 |
10 | > VS Code插件市场还有很多类似的超赞的插件,比如[SVN](https://marketplace.visualstudio.com/items?itemName=johnstoncode.svn-scm)。
11 |
12 | 如果你需要帮助,请查看[vscode命名空间API](https://code.visualstudio.com/api/references/vscode-api#scm)。
13 |
14 | ## 源控制模型
15 | ---
16 | `SourceControl`负责生产源控制模型的实体,它里面有`SourceControlResourceState`实例的**资源状态**,而资源状态又是`SourceControlResourceGroup`实例整理成**组**的。
17 |
18 | 通过`vscode.scm.createSourceControl`创建一个新的*源控制器*。
19 |
20 | 为了更好地理解这几种实体的交互,让我们拿[Git](https://github.com/Microsoft/vscode/tree/master/extensions/git)来做例子,考虑下列`git status`输出:
21 |
22 | ```bash
23 | vsce master* → git status
24 | On branch master
25 | Your branch is up-to-date with 'origin/master'.
26 | Changes to be committed:
27 | (use "git reset HEAD ..." to unstage)
28 |
29 | modified: README.md
30 | renamed: src/api.ts -> src/test/api.ts
31 |
32 | Changes not staged for commit:
33 | (use "git add/rm ..." to update what will be committed)
34 | (use "git checkout -- ..." to discard changes in working directory)
35 |
36 | deleted: .travis.yml
37 | modified: README.md
38 | ```
39 | 这个工作区里面发生了很多事,首先,`README.md`文件已经被修改了但还没有提交,然后立刻又被修改了。 其次,`src/api.ts`文件被移动到了`src/test/api.ts`,这个修改已经存备(staged), 最后,`.travis.yml`文件被删除。
40 |
41 | 对于这个工作区而言,Git定义了两个资源组:**工作中(Working tree)**和**已索引(Index)**,对于单个组而言,每次**文件修改**就会产生一些资源状态:
42 | - **已索引** - 资源组
43 | - 修改`README.md` - 资源状态
44 | - 移动`src/api.ts`到`src/test/api.ts` - 资源状态
45 | - **工作中** - 资源组
46 | - 删除`.travis.yml` - 资源状态
47 | - 修改`README.md` - 资源状态
48 |
49 | 同一个`README.md`是怎么成为两组截然不同的资源状态呢?
50 |
51 | 下面揭秘Git是如何创建这个模型的:
52 |
53 | ```typescript
54 | function createResourceUri(relativePath: string): vscode.Uri {
55 | const absolutePath = path.join(vscode.workspace.rootPath, relativePath);
56 | return vscode.Uri.file(absolutePath);
57 | }
58 |
59 | const gitSCM = vscode.scm.createSourceControl('git', "Git");
60 |
61 | const index = gitSCM.createResourceGroup('index', "Index");
62 | index.resourceStates = [
63 | { resourceUri: createResourceUri('README.md') },
64 | { resourceUri: createResourceUri('src/test/api.ts') }
65 | ];
66 |
67 | const workingTree = gitSCM.createResourceGroup('workingTree', "Changes");
68 | workingTree.resourceStates = [
69 | { resourceUri: createResourceUri('.travis.yml') },
70 | { resourceUri: createResourceUri('README.md') }
71 | ];
72 | ```
73 |
74 | 源变动和最终产生的资源组会传递到源控制视图上。
75 |
76 | ## 源控制视图
77 | ---
78 | 当源变动时,VS Code会生成源控制视图。源状态可通过`SourceControlResourceDecorations`自定义:
79 |
80 | ```typescript
81 | export interface SourceControlResourceState {
82 | readonly decorations?: SourceControlResourceDecorations;
83 | }
84 | ```
85 |
86 | 上述例子已经足以让源控制视图生成一个简单的列表,不过用户可能想要在不同的资源状态上进行不同的操作。比如,当用户点击资源状态时,会发生什么呢?资源状态提供了一个可选命令去处理这类场景:
87 | ```typescript
88 | export interface SourceControlResourceState {
89 | readonly command?: Command;
90 | }
91 | ```
92 |
93 | ### 菜单
94 |
95 | 要想提供更加丰富的交互效果,我们提供了5个源控制菜单项供你使用。
96 |
97 | `scm/title`菜单在源控制视图的顶部右上方,菜单项水平排列在`标题栏`中,另外一些会在`...`下拉菜单中。
98 |
99 | `scm/resourceGroup/context`和`scm/resourceState/context`是类似的,你可以通过前者自定义资源组,后者则是定义资源状态。将菜单项放在`inline`组里,可以水平在视图中展示它们。而其他的菜单项可以通过鼠标右击的形式展示在菜单中。菜单中调用的命令会传入资源状态作为参数。注意SCM视图提供多选,因此命令函数可能一次性会接收一个或多个参数。
100 |
101 | 例如,Git支持往`scm/resourceState/context`菜单中添加`git.stage`命令和使用下列方法,提供多个文件的存备(staged):
102 |
103 | ```typescript
104 | stage(...resourceStates: SourceControlResourceState[]): Promise;
105 | ```
106 |
107 | 创建它们的时候,`SourceControl`和`SourceControlResourceGroup`实例会需要你提供一个string类型的`id`,这些值最终会在`scmProvider`和`scmResourceGroup`以上下文键值的形式出现。在菜单项的`when`语法中使用这些[上下文键值](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts)。看个Git如何通过`git.stage`命令显示菜单项的:
108 |
109 | ```json
110 | {
111 | "command": "git.stage",
112 | "when": "scmProvider == git && scmResourceGroup == merge",
113 | "group": "inline"
114 | }
115 | ```
116 |
117 | `scm/change/title`可以对*行内变动*配置标题栏的命令(contribute commands to the title bar of an inline change)。命令中的参数有文档的URI,变动数组,当前行内变动所在索引。例如下面是一个可以配置菜单的Git`stageChange`命令声明:
118 |
119 | ```typescript
120 | async stageChange(uri: Uri, changes: LineChange[], index: number): Promise;
121 | ```
122 |
123 | `scm/sourceControl`菜单根据环境出现在源控制实例的边上。
124 |
125 | 
126 |
127 | 最后,`scm/change/title`菜单是和快速Diff功能相关联的,越新的文件越靠前,你可以针对特定的代码变动调用命令。
128 |
129 | ### SCM 输入框
130 |
131 | 源控制输入框位于每个源控制视图的顶部,接收用户输入的信息。你可以获取(或设置)这个信息供后续使用。在Git中,比如说,这可以作为一个commit框,用户输入了提交信息后,触发`git commit`命令:
132 |
133 | ```typescript
134 | export interface SourceControlInputBox {
135 | value: string;
136 | }
137 |
138 | export interface SourceControl {
139 | readonly inputBox: SourceControlInputBox;
140 | }
141 | ```
142 |
143 | 用户可以通过Ctrl+Enter(Mac上是Cmd+Enter)接收任意信息,在`SourceControl`中的`acceptInputCommand`处理这类事件。
144 |
145 | ```typescript
146 | export interface SourceControl {
147 | readonly acceptInputCommand?: Command;
148 | }
149 | ```
150 |
151 | ## 快速Diff
152 | ---
153 |
154 | VS Code支持显示**快速Diff**编辑器的高亮槽,点击这些槽会出现一个内部diff交互器,你可以在这里为上下文配置命令。
155 |
156 | 
157 |
158 | 这些高亮槽是VS Code自己计算出来的,你要做的就是根据给定的文件提供原始文件内容
159 |
160 | ```typescript
161 | export interface SourceControl {
162 | quickDiffProvider?: QuickDiffProvider;
163 | }
164 | ```
165 |
166 | 使用`QuickDiffProvider`,你的实现需要告诉VS Code——参数传入的给定资源`Uri`所对应的原始资源`Uri`。
167 |
168 | ?> **提示**: 如果你想在给定`Uri`的情况下,为任意资源提供内容,那么你可以把**源控制API**和**[工作区命名空间的`registerTextDocumentContentProvider`方法](https://code.visualstudio.com/api/references/vscode-api#workspace)**结合起来使用。
169 |
170 | ## 下一步
171 |
172 | 想要学习更多关于VS Code扩展性模型,请参考:
173 |
174 | * [SCM API 参考](https://code.visualstudio.com/docs/extensionAPI/vscode-api#_scm) - 查看完整的SCM API文档
175 | * [Git 插件](https://github.com/Microsoft/vscode/tree/master/extensions/git) - 学习Git插件实现
176 | * [插件API概览](/) - 学习全部的VS Code扩展性模型
177 | * [插件配置清单](/references/extension-manifest) - VS Code package.json插件配置清单参考
178 | * [发布内容配置点](/references/contribution-points) - VS Code发布内容配置点参考
--------------------------------------------------------------------------------
/docs/preknowledge/generics.md:
--------------------------------------------------------------------------------
1 | # 认识Typescript-泛型
2 |
3 | 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
4 |
5 | 在像C#和Java这样的语言中,可以使用**泛型**来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
6 |
7 | ## 泛型之Hello World
8 | ---
9 | 下面来创建第一个使用泛型的例子:identity函数。 这个函数会返回任何传入它的值。 你可以把这个函数当成是 `echo`命令。
10 |
11 | 不用泛型的话,这个函数可能是下面这样:
12 |
13 | ```typescript
14 | function identity(arg: number): number {
15 | return arg;
16 | }
17 | ```
18 |
19 | 或者,我们使用any类型来定义函数:
20 |
21 | ```typescript
22 | function identity(arg: any): any {
23 | return arg;
24 | }
25 | ```
26 |
27 | 使用`any`类型会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。
28 |
29 | 因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了**类型变量**,它是一种特殊的变量,*只用于表示类型而不是值*。
30 |
31 | ```typescript
32 | function identity(arg: T): T {
33 | return arg;
34 | }
35 | ```
36 |
37 | 我们给identity添加了类型变量`T`。 `T`帮助我们捕获用户传入的类型(比如:`number`),之后我们就可以使用这个类型。 之后我们再次使用了`T`当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。
38 |
39 | 我们把这个版本的`identity`函数叫做泛型,因为它可以适用于多个类型。 不同于使用`any`,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。
40 |
41 | 我们定义了泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数:
42 |
43 | ```typescript
44 | let output = identity("myString"); // type of output will be 'string'
45 | ```
46 |
47 | 这里我们明确的指定了`T`是`string`类型,并做为一个参数传给函数,使用了`<>`括起来而不是`()`。
48 |
49 | 第二种方法更普遍。利用了*类型推论* -- 即编译器会根据传入的参数自动地帮助我们确定T的类型:
50 |
51 | ```typescript
52 | let output = identity("myString"); // type of output will be 'string'
53 | ```
54 |
55 | 注意我们没必要使用尖括号(`<>`)来明确地传入类型;编译器可以查看`myString`的值,然后把`T`设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入`T`的类型,在一些复杂的情况下,这是可能出现的。
56 |
57 | ## 使用泛型变量
58 | ---
59 |
60 | 使用泛型创建像`identity`这样的泛型函数时,编译器要求你在函数体必须正确的使用这个通用的类型。 换句话说,你必须把这些参数当做是任意或所有类型。
61 |
62 | 看下之前identity例子:
63 |
64 | ```typescript
65 | function identity(arg: T): T {
66 | return arg;
67 | }
68 | ```
69 |
70 | 如果我们想同时打印出arg的长度。 我们很可能会这样做:
71 |
72 | ```typescript
73 | function loggingIdentity(arg: T): T {
74 | console.log(arg.length); // Error: T doesn't have .length
75 | return arg;
76 | }
77 | ```
78 |
79 | 如果这么做,编译器会报错说我们使用了`arg`的`.length`属性,但是没有地方指明`arg`具有这个属性。 记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有 `.length`属性的。
80 |
81 | 现在假设我们想操作T类型的数组而不直接是T。由于我们操作的是数组,所以`.length`属性是应该存在的。 我们可以像创建其它数组一样创建这个数组:
82 |
83 | ```typescript
84 | function loggingIdentity(arg: T[]): T[] {
85 | console.log(arg.length); // Array has a .length, so no more error
86 | return arg;
87 | }
88 | ```
89 |
90 | 你可以这样理解`loggingIdentity`的类型:泛型函数`loggingIdentity`,接收类型参数`T`和参数`arg`,它是个元素类型是`T`的数组,并返回元素类型是`T`的数组。 如果我们传入数字数组,将返回一个数字数组,因为此时 `T`的的类型为number。 这可以让我们把泛型变量`T`当做类型的一部分使用,而不是整个类型,增加了灵活性。
91 |
92 | 我们也可以这样实现上面的例子:
93 |
94 | ```typescript
95 | function loggingIdentity(arg: Array): Array {
96 | console.log(arg.length); // Array has a .length, so no more error
97 | return arg;
98 | }
99 | ```
100 | 使用过其它语言的话,你可能对这种语法已经很熟悉了。 在下一节,会介绍如何创建自定义泛型像 Array一样。
101 |
102 | ## 泛型类型
103 | ---
104 | 上一节,我们创建了identity通用函数,可以适用于不同的类型。 在这节,我们研究一下函数本身的类型,以及如何创建泛型接口。
105 |
106 | 泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面,像函数声明一样:
107 |
108 | ```typescript
109 | function identity(arg: T): T {
110 | return arg;
111 | }
112 |
113 | let myIdentity: (arg: T) => T = identity;
114 | ```
115 |
116 | 我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。
117 |
118 | ```typescript
119 | function identity(arg: T): T {
120 | return arg;
121 | }
122 |
123 | let myIdentity: (arg: U) => U = identity;
124 | ```
125 |
126 | 我们还可以使用带有调用签名的对象字面量来定义泛型函数:
127 |
128 | ```typescript
129 | function identity(arg: T): T {
130 | return arg;
131 | }
132 |
133 | let myIdentity: {(arg: T): T} = identity;
134 | ```
135 |
136 | 这引导我们去写第一个泛型接口了。 我们把上面例子里的对象字面量拿出来做为一个接口:
137 |
138 | ```typescript
139 | interface GenericIdentityFn {
140 | (arg: T): T;
141 | }
142 |
143 | function identity(arg: T): T {
144 | return arg;
145 | }
146 |
147 | let myIdentity: GenericIdentityFn = identity;
148 | ```
149 |
150 | 一个相似的例子,我们可能想把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的具体是哪个泛型类型(比如: `Dictionary而不只是Dictionary`)。 这样接口里的其它成员也能知道这个参数的类型了。
151 |
152 | ```typescript
153 | interface GenericIdentityFn {
154 | (arg: T): T;
155 | }
156 |
157 | function identity(arg: T): T {
158 | return arg;
159 | }
160 |
161 | let myIdentity: GenericIdentityFn = identity;
162 | ```
163 |
164 | 注意,我们的示例做了少许改动。 不再描述泛型函数,而是把非泛型函数签名作为泛型类型一部分。 当我们使用 `GenericIdentityFn` 的时候,还得传入一个类型参数来指定泛型类型(这里是:`number`),锁定了之后代码里使用的类型。 对于描述哪部分类型属于泛型部分来说,理解何时把参数放在调用签名里和何时放在接口上是很有帮助的。
165 |
166 | 除了泛型接口,我们还可以创建泛型类。 注意,无法创建泛型枚举和泛型命名空间。
167 |
168 | ## 泛型类
169 | ---
170 |
171 | ```typescript
172 | class GenericNumber {
173 | zeroValue: T;
174 | add: (x: T, y: T) => T;
175 | }
176 |
177 | let myGenericNumber = new GenericNumber();
178 | myGenericNumber.zeroValue = 0;
179 | myGenericNumber.add = function(x, y) { return x + y; };
180 | ```
181 |
182 | GenericNumber类的使用是十分直观的,并且你可能已经注意到了,没有什么去限制它只能使用number类型。 也可以使用字符串或其它更复杂的类型。
183 |
184 | ```typescript
185 | let stringNumeric = new GenericNumber();
186 | stringNumeric.zeroValue = "";
187 | stringNumeric.add = function(x, y) { return x + y; };
188 |
189 | console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));
190 | ```
191 |
192 | 与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。
193 |
194 | ## 泛型约束
195 | ---
196 |
197 | 你应该会记得之前的一个例子,我们有时候想操作某类型的一组值,并且我们知道这组值具有什么样的属性。 在 `loggingIdentity` 例子中,我们想访问`arg`的`length`属性,但是编译器并不能证明每种类型都有`length`属性,所以就报错了。
198 |
199 | ```typescript
200 | function loggingIdentity(arg: T): T {
201 | console.log(arg.length); // Error: T doesn't have .length
202 | return arg;
203 | }
204 | ```
205 |
206 | 相比于操作any所有类型,我们想要限制函数去处理任意带有`.length`属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。
207 |
208 | 为此,我们定义一个接口来描述约束条件。 创建一个包含 `.length`属性的接口,使用这个接口和extends关键字来实现约束:
209 |
210 | ```typescript
211 | interface Lengthwise {
212 | length: number;
213 | }
214 |
215 | function loggingIdentity(arg: T): T {
216 | console.log(arg.length); // Now we know it has a .length property, so no more error
217 | return arg;
218 | }
219 | ```
220 |
221 | 现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
222 |
223 | ```typescript
224 | loggingIdentity(3); // Error, number doesn't have a .length property
225 | ```
226 |
227 | 我们需要传入符合约束类型的值,必须包含必须的属性:
228 |
229 | ```typescript
230 | loggingIdentity({length: 10, value: 3});
231 | ```
232 |
233 | ### 在泛型约束中使用类型参数
234 |
235 | 你可以声明一个类型参数,且它被另一个类型参数所约束。 比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj上,因此我们需要在这两个类型之间使用约束。
236 |
237 | ```typescript
238 | function getProperty(obj: T, key: K) {
239 | return obj[key];
240 | }
241 |
242 | let x = { a: 1, b: 2, c: 3, d: 4 };
243 |
244 | getProperty(x, "a"); // okay
245 | getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
246 | ```
--------------------------------------------------------------------------------
/docs/working-with-extensions/bundling-extension.md:
--------------------------------------------------------------------------------
1 | # 打包插件
2 |
3 | VS Code 插件体积常常随着更新越来越大,它会产生很多文件,而且还依赖各种[npm](https://www.npmjs.com/)包。
4 | 程序开发的最佳实践是抽象和重用,但过度拆分和庞大的代码结构产生的代价就是更大的插件体积和更慢的运行效率。加载 100 个小文件远比加载一个大文件来的慢,这就是我们更推荐打包插件的原因。
5 | *打包*是将多个小的源文件打包成单个入口文件的过程。
6 |
7 | 对于 Javascript 而言,可选的构建工具非常多,比较流行的如[rollup.js](https://rollupjs.org/),[parcel](https://parceljs.org/)和[webpack](https://webpack.js.org/)。大部分构建工具的概念和功能都是相似的,本节主要使用**webpack**打包。
8 |
9 | ## 使用 webpack
10 |
11 | ---
12 |
13 | webpack 这个开发工具可以在[npm](https://www.npmjs.com/)里找到,为了获取 webpack 和它的命令行界面,打开终端然后输入:
14 |
15 | ```bash
16 | npm i --save-dev webpack webpack-cli
17 | ```
18 |
19 | 这行命令会先安装 webpack,然后更新你插件里的`package.json`中的`devDependencies`字段。Webpack 是一个 Javascrip 打包工具,但是大部分 VS Code 插件是用 Typescript 写的,所以你需要在 webpack 中配置`ts-loader`,它才能正确编译 Typescript。安装`ts-loader`:
20 |
21 | ```bash
22 | npm i --save-dev ts-loader
23 | ```
24 |
25 | ## 配置 webpack
26 |
27 | ---
28 |
29 | 既然所有的工具都安装好了,我们现在可以开始配置 webpack 了。通常来说,你的项目目录中需要创建一个`webpack.config.js`文件,webpack 才能知道按什么规则打包你的插件。下面的配置示例是 VS Code 插件专用的,让我们来开这个头吧:
30 |
31 | ```javascript
32 | /*---------------------------------------------------------------------------------------------
33 | * Copyright (c) Microsoft Corporation. All rights reserved.
34 | * Licensed under the MIT License. See License.txt in the project root for license information.
35 | *--------------------------------------------------------------------------------------------*/
36 |
37 | //@ts-check
38 |
39 | "use strict";
40 |
41 | const path = require("path");
42 |
43 | /**@type {import('webpack').Configuration}*/
44 | const config = {
45 | target: "node", // vscode插件运行在Node.js环境中 📖 -> https://webpack.js.org/configuration/node/
46 |
47 | entry: "./src/extension.ts", // 插件的入口文件 📖 -> https://webpack.js.org/configuration/entry-context/
48 | output: {
49 | // 打包好的文件储存在'dist'文件夹中 (请参考package.json), 📖 -> https://webpack.js.org/configuration/output/
50 | path: path.resolve(__dirname, "dist"),
51 | filename: "extension.js",
52 | libraryTarget: "commonjs2",
53 | devtoolModuleFilenameTemplate: "../[resource-path]"
54 | },
55 | devtool: "source-map",
56 | externals: {
57 | vscode: "commonjs vscode" // vscode-module是热更新的临时目录,所以要排除掉。 在这里添加其他不应该被webpack打包的文件, 📖 -> https://webpack.js.org/configuration/externals/
58 | },
59 | resolve: {
60 | // 支持读取TypeScript和JavaScript文件, 📖 -> https://github.com/TypeStrong/ts-loader
61 | extensions: [".ts", ".js"]
62 | },
63 | module: {
64 | rules: [
65 | {
66 | test: /\.ts$/,
67 | exclude: /node_modules/,
68 | use: [
69 | {
70 | loader: "ts-loader"
71 | }
72 | ]
73 | }
74 | ]
75 | }
76 | };
77 | module.exports = config;
78 | ```
79 |
80 | 这份文件是[webpack-extension](https://github.com/Microsoft/vscode-extension-samples/blob/master/webpack-sample)中的[一部分](https://github.com/Microsoft/vscode-extension-samples/blob/master/webpack-sample/webpack.config.js)。webpack 配置最后输出的就是 JS 对象。
81 |
82 | 在上面的例子里,我们定义了如下内容:
83 |
84 | - `traget`:'node',因为我们的插件运行在 Node.js 环境中。
85 | - `entry`:webpack 使用的入口文件。这就像是`package.json`中的`main`属性,有点不一样的是你还需要给 webpack 提供"source"—— 一般就是`src/extension.ts`,小心不要配置在"output"里了。webpack 可以解析 Typescript,所以我们不需要再单独执行 Typescript 编译了。
86 | - `output`配置告诉 webpack 应该把打包好的文件放在哪里,一般而言我们会放在`dist`文件夹里。在这个例子里,webpack 最后会产生一个`dist/extension.js`文件。
87 | - 在`resolve`和`module/rules`中配置 Typescript 和 Javascript 的解析器。
88 | - `externals`即排除配置,在这里可以配置打包文件不应包含的文件和模块。`vscode`不需要被打包是因为它并不储存在磁盘上,它是 VS Code 热更新生成的临时文件夹。根据插件依赖的具体 node 模块,你可能需要通过这个配置优化打包文件。
89 |
90 | ## 运行 webpack
91 |
92 | ---
93 |
94 | `webpack.config.js`文件创建好之后,webpack 就可以正式开始工作了。你可以从命令行中运行 webpack,不过为了避免重复工作用 npm script 会更有效率。
95 |
96 | 将下列脚本复制到`package.json`的`scripts`中去:
97 |
98 | ```json
99 | "scripts": {
100 | "vscode:prepublish": "webpack --mode production",
101 | "compile": "webpack --mode none",
102 | "watch": "webpack --mode none --watch",
103 | },
104 | ```
105 |
106 | `compile`和`watch`脚本是开发时使用的,它们会产生构建文件。`vscode:prepublish`是`vsce`使用的,`vsce`是 VS Code 的打包和发布工具,你需要在发布插件之前运行这个命令。webpack 中的[mode](https://webpack.js.org/concepts/mode/)是控制优化级别的配置项,如果你使用`production`字段,那么就会打包出最小的构建文件,但是也会耗费更多时间,所以我们开发中使用`none`。想要运行上述脚本,我们可以打开终端(命令行)输入`npm run compile`或者从*命令面板*(Ctrl+Shift+P)中使用**运行任务**来开始。
107 |
108 | ## 运行插件
109 |
110 | ---
111 |
112 | 运行插件之前,你需要将`package.json`中的`main`属性指向到构建文件上,也就是我们上面提到的[`"./dist/extension"`](https://github.com/Microsoft/vscode-references-view/blob/d649d01d369e338bbe70c86e03f28269cbf87027/package.json#L26),改好之后我们就可以运行和测试插件了。关于调试配置,请注意更新`launch.json`中的`outFiles`属性。
113 |
114 | ## 测试
115 |
116 | ---
117 |
118 | 插件开发者一般都会给插件源代码进行单元测试,但是有了完备的底层架构支持,插件的源代码可以不依赖测试,webpack 产生的构建文件中也不应该包含任何测试代码。如果需要运行单元测试,只需要简单地编译就好了。在上面的例子里,我们有一个`test-compile`脚本,它会把调用 Typescript 编译器将插件编译至`out`目录中。这样一来我们就有了 JS 文件,再使用下面的`launch.json`就足够应付测试了。
119 |
120 | ```json
121 | {
122 | "name": "Extension Tests",
123 | "type": "extensionHost",
124 | "request": "launch",
125 | "runtimeExecutable": "${execPath}",
126 | "args": [
127 | "--extensionDevelopmentPath=${workspaceFolder}",
128 | "--extensionTestsPath=${workspaceFolder}/out/test"
129 | ],
130 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"],
131 | "preLaunchTask": "npm: test-compile"
132 | }
133 | ```
134 |
135 | 这个测试配置对于非 webpack 打包的插件来说也是可以使用的。我们没必要将单元测试打包起来,因为它们不应包含在我们发布的插件里。
136 |
137 | ## 发布
138 |
139 | ---
140 |
141 | 发布前你需要更新`.vscodeignore`文件。现在所有东西都打包到了`dist/extension.js`文件中,所以应该排除这个文件还有`out`文件夹(怕你漏了,特此提醒),以及最重要的`node_modules`文件夹。
142 |
143 | 一般来说,`.vsignore`文件应该是这样的:
144 |
145 | ```bash
146 | .vscode
147 | node_modules
148 | out/
149 | src/
150 | tsconfig.json
151 | webpack.config.js
152 | ```
153 |
154 | ## 迁移插件
155 |
156 | ---
157 |
158 | 用 webpack 迁移现有的插件是很容易的,整个过程就像我们上面的指南一样。真实的例子如 VS Code 的 References 视图就是从这个[pull request](https://github.com/Microsoft/vscode-references-view/pull/50)应用了 webpack 而来的。
159 |
160 | 在里面,你可以看到:
161 |
162 | - `devDependencies`中添加`webpack`,`webpack-cli`和`ts-loader`
163 | - 更新 npm 脚本以便开发时使用 webpack
164 | - 更新调试配置文件`launch.json`
165 | - 添加和修改`webpack.config.js`
166 | - 更新`.vscodeignore`排除`node_modules`和其他开发时产生的临时文件
167 | - 开始享受体积更小、安装更快的插件!
168 |
169 | ## 疑难解答
170 |
171 | ---
172 |
173 | #### 压缩
174 |
175 | 使用`production`模式会执行代码压缩,它会去除源代码中的空格和注释,并把变量名和函数名进行替换——混淆和压缩。不过形如`Function.prototype.name`的代码不会压缩。
176 |
177 | #### webpack critical dependencies
178 |
179 | 当你运行 webpack 时,你可能会碰到像**Critical dependencies: the request of a dependency is an expression**字样的警告。这些警告必须立即处理,一般来说会影响到打包过程。这句话意味着 webpack 不能静态分析某些依赖,一般是由动态使用`require`导致的,比如`require(someDynamicVariable)`。
180 |
181 | 想要处理这类警告,你需要:
182 |
183 | - 将需要打包的部分变成静态的依赖。
184 | - 通过`externals`排除这部分依赖,但是注意它们的 Javascript 文件还是应该保留在我们打包的插件里,在`.vscodeignore`中使用 glob 模式,比如`!node_modules/mySpecialModule`。
185 |
186 | ## 下一步
187 |
188 | - [插件市场](https://code.visualstudio.com/docs/editor/extension-gallery) - 学习更多 VS Code 插件市场的有关内容。
189 | - [测试插件](/working-with-extensions/testing-extension.md) - 测试插件,提高项目质量。
190 | - [持续集成](/working-with-extensions/continuous-integration.md) - 使用 Azure Pipeline 运行插件的 CI 构建。
191 |
--------------------------------------------------------------------------------
/docs/working-with-extensions/publish-extension.md:
--------------------------------------------------------------------------------
1 | # 发布插件
2 |
3 | ## vsce —— 发布工具参阅
4 |
5 | [vsce](https://github.com/Microsoft/vsce)是一个用于将插件发布到[市场](https://code.visualstudio.com/docs/editor/extension-gallery)上的命令行工具。
6 |
7 | ## 安装
8 |
9 | ---
10 |
11 | 请确认本机已经安装了[Node.js](https://nodejs.org/),然后运行:
12 |
13 | ```bash
14 | npm install -g vsce
15 | ```
16 |
17 | ## 使用
18 |
19 | ---
20 |
21 | 然后你就可以在命令行里直接使用`vsce`了。下面是一个快速发布的示例(在你登录和打包好插件之后):
22 |
23 | ```bash
24 | $ vsce publish
25 | Publishing uuid@0.0.1...
26 | Successfully published uuid@0.0.1!
27 | ```
28 |
29 | 更多可用的命令参数,请使用`vsce --help`
30 |
31 | ## 发布教程
32 |
33 | ---
34 |
35 | !> **注意:** 出于安全考虑,`vsce`不会发布包含用户提供 SVG 图片的插件。
36 |
37 | 发布工具会检查以下内容:
38 |
39 | - `pacakge.json`文件中的 icon 不可以是 SVG。
40 | - `pacakge.json`中的徽章不可以是 SVG,除非来自于[可靠的图标来源](/extensibility-reference/extension-manifest.md#使用认证过的标志)
41 | - `README.md`和`CHANGELOG.md`中的图片链接需要使用`https`协议
42 | - `README.md`和`CHANGELOG.md`中的图片不可以是 SVG,除非来自[可靠的图标来源](/extensibility-reference/extension-manifest.md#使用认证过的标志)
43 |
44 | ---
45 |
46 | VS Code 插件市场的服务是[Visual Studio Team Services](https://visualstudio.microsoft.com/team-services)提供的,因此验证、代理、管理插件都是由这个服务提供的。
47 |
48 | `vsce`只能用[Personal Access Tokens](https://docs.microsoft.com/vsts/integrate/get-started/authentication/pats)发布插件,所以至少要创建一个 Token 以便发布插件。
49 |
50 | #### 获取 Personal Access Token
51 |
52 | 首先,你得有一个[Azure DevOps 组织](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization-msa-or-work-student)。
53 |
54 | 下面的例子里,我们假设组织名为`vscode`,从你的组织主页(例如:`https://dev.azure.com/vscode` )进入**安全(Security)**页面:
55 |
56 | 
57 |
58 | 选择 Personal Access Token,点击**New Token**创建一个新的 Personal Access Token
59 |
60 | 
61 |
62 | 给 Personal Access Token 添加描述,过期时间等等,你最好把过期时间设置为 1 年,这样你接下就能方便很多,选择**custom defined(用户自定义)**范围,然后点击**Show all scopes(显示全部)**
63 |
64 | 
65 |
66 | 最后,在这个列表中找到**Marketplace**,并勾选**Acquire**和**Manage**
67 |
68 | 
69 |
70 | 点击**Create**,你就会看到新创建的 Personal Access Token 了,复制好,你接下来就会用到这个 token 来创建一个*发行方*了。
71 |
72 | #### 创建一个发行方
73 |
74 | **发行方**是 VS Code 市场有权发布插件的唯一标识,每个插件的[package.json](/extensibility-reference/extension-manifest.md)文件都包含着`publisher`字段。
75 |
76 | 现在我们已经有了[Personal Access Token](#获取Personal-Access-Token),我们马上可以用`vsce`创建一个发行方:
77 |
78 | ```bash
79 | vsce create-publisher (publisher name)
80 | ```
81 |
82 | `vsce`会记住这个 Personal Access Token,日后你需要再次使用的时候会自动带上。
83 |
84 | ?> 注意:另外,你也可以在市场的发行方[管理页](https://marketplace.visualstudio.com/manage)中创建发行方,然后用这个账号登录`vsce`。
85 |
86 | #### 发行方登录
87 |
88 | 如果你已经有发行方账号了:
89 |
90 | ```bash
91 | vsce login (publisher name)
92 | ```
93 |
94 | 和`create-publisher`命令类似地,`vsce`会要求输入你的 Personal Access Token。
95 |
96 | 你也可以用命令参数`-p `直接登录然后立即发布插件:
97 |
98 | ```bash
99 | vsce publish -p
100 | ```
101 |
102 | #### 增量更新插件版本
103 |
104 | 用 SemVer 语义标识符:`major`,`minor`,`patch`增量更新插件版本号。
105 |
106 | 例如,你想把插件从 1.0.0 更新到 1.1.0,那么加上`minor`:
107 |
108 | ```bash
109 | vsce publish minor
110 | ```
111 |
112 | 插件`package.json`的 version 会先更新,然后才发布插件。
113 |
114 | 你也可以通过命令行指定版本号:
115 |
116 | ```bash
117 | vsce publish 2.0.1
118 | ```
119 |
120 | #### 下架插件
121 |
122 | 通过指定插件 id`publisher.extension`下架插件:
123 |
124 | ```
125 | vsce unpublish (publisher name).(extension name)
126 | ```
127 |
128 | !> **注意:**当你下架插件的时候,市场会移除所有插件的历史统计数据,请在下架前再三考虑,最好还是更新插件吧。
129 |
130 | #### 插件打包
131 |
132 | 你也可能只是想打包一下插件,而不是发布到商店里。用下列命令将插件打包到`.vsix`文件中:
133 |
134 | ```
135 | vsce package
136 | ```
137 |
138 | 这个命令会在当前目录生成一个`.vsix`文件,直接从`.vsix`安装插件是允许的,查看[从 VSIX 安装插件](https://github.com/Microsoft/vscode-docs/blob/master/docs/editor/extension-gallery.md#install-from-a-vsix)了解更多内容。
139 |
140 | #### VS Code 版本兼容性
141 |
142 | 当你制作插件的时候,你需要描述插件对 VS Code 的版本兼容性——修改`package.json`中的`engines.vscode`:
143 |
144 | ```json
145 | {
146 | "engines": {
147 | "vscode": "^1.8.0"
148 | }
149 | }
150 | ```
151 |
152 | `1.8.0`表示你的插件只能兼容`1.8.0`版本的 VS Code,`^1.8.0`则表示你的插件向上兼容,包括`1.8.1, 1.9.0`等等。
153 |
154 | 使用`engines.vscode`可以确保插件安装环境包含了插件依赖的 API。这个机制在稳定版和 Insider 版本都适用。
155 |
156 | 现在我们假设最新的稳定版 API 是`1.8.0`,而`1.9.0`引入了新的 API,所以你可以用`1.9.0-insider`标识插件在 Insider 版中也可正常使用。
157 | 如果你想使用这些刚刚引入的 API,则将依赖版本设置为`^1.9.0`,你的插件则只能安装在`>=1.9.0`的 VS Code 上,也就意味着所有当前的 Insider 版本都可以用得上,而稳定版只有在更新到`1.9.0`才能使用你的插件。
158 |
159 | ## 进阶用法
160 |
161 | ---
162 |
163 | #### 符合市场的插件
164 |
165 | 你可以自定义插件在市场中的外观,查看示例[GO 插件](https://marketplace.visualstudio.com/items/ms-vscode.Go)。
166 |
167 | 下面是一些让你的插件在市场看起来更酷的小提示:
168 |
169 | - 插件根目录下面的`README.md`文件可以用来填充插件市场的页面内容。`vsce`会将 README 中的链接通过下面两种方式修改掉:
170 | - 如果你的`package.json`的`repository`字段是一个 Github 仓库,`vsce`会自动检测,然后相应地调整链接。
171 | - 运行`vsce package`时,加上`--baseContentUrl`和`--baseImagesUrl`标识重载上述行为。
172 | - 插件根目录下的`LICENSE`会成为插件的 license。
173 | - 同样的`CHANGELOG.md`文件会成为插件的发布日志。
174 | - 通过设置`package.json`的`galleryBanner.color`hex 值,修改 banner 的背景色。
175 | - 通过给`package.json`中的`icon`设置一个相对路径,可以将一个`128px`的 PNG 图标放进你的插件中。
176 |
177 | - 参见[插件市场展现小提示](https://github.com/Microsoft/vscode-docs/blob/master/docs/extensionAPI/extension-manifest.md#marketplace-presentation-tips)
178 |
179 | #### `.vscodeignore`
180 |
181 | 创建一个`.vscodeignore`文件可以排除插件目录中的内容。这个文件支持[glob](https://github.com/isaacs/minimatch)模式,每个表达式一行。
182 |
183 | 例如:
184 |
185 | ```
186 | **/*.ts
187 | **/tsconfig.json
188 | !file.ts
189 | ```
190 |
191 | 你应该忽略哪些不必在运行时用到的文件。例如:你的插件是用 Typescript 写的,那么你就应该忽略所有的`**/*.ts`文件。
192 |
193 | ?> **注意:**在`devDependencies`列出的开发依赖会被自动忽略,你不必将他们加入到`.vscodeignore`中。
194 |
195 | ##### 预发布步骤
196 |
197 | 你是可以在清单文件中添加预发布步骤的,下面的命令会在插件每次打包时执行:
198 |
199 | ```json
200 | {
201 | "name": "uuid",
202 | "version": "0.0.1",
203 | "publisher": "joaomoreno",
204 | "engines": {
205 | "vscode": "0.10.x"
206 | },
207 | "scripts": {
208 | "vscode:prepublish": "tsc"
209 | }
210 | }
211 | ```
212 |
213 | 上面的示例会在每次插件打包时调用 Typescript 编译器。
214 |
215 | ## FAQ
216 |
217 | ##### 问:当我发布插件时遇到了 403 Forbidden(或 401 Unauthorized)错误?
218 |
219 | 答:很有可能是你创建 PAT (Personal Access Token) 时没有选择`all accessible accounts`。你还需要将授权范围设置为`All scopes`,这样发布工具才能工作。
220 |
221 | ##### 问:我没办法用`vsce`工具下架插件?
222 |
223 | 答:你可能改变了插件 ID 或者发行方名称。不过你还可以在[管理页面](https://marketplace.visualstudio.com/manage)发布或者下架插件。
224 |
225 | ##### 问:为什么 vsce 不保留文件属性?
226 |
227 | 答:请注意,当你在 Windows 平台构建和发布插件的时候,所有插件包内的文件会丢失 POSIX 文件属性,或称之为执行位(executable bit)的东西。一些基于这些属性的`node_modules`依赖则会调整工作方式。从 Linux 和 macOS 平台构建则会如预期执行。
228 |
229 | ## 下一步
230 |
231 | - [插件市场](https://code.visualstudio.com/docs/editor/extension-gallery) - 学习更多 VS Code 公共插件市场。
232 | - [测试插件](/working-with-extensions/testing-extension.md) - 添加插件测试,提高插件质量。
233 |
--------------------------------------------------------------------------------
/docs/references/commands.md:
--------------------------------------------------------------------------------
1 | # 内置命令
2 |
3 | 这篇文档列出了可能需要与`vscode.commands.executeCommand`一起使用的命令集合.
4 |
5 | 阅读[命令指南]()以了解如何使用`commands`API.
6 |
7 | 下面是一个如何在 VS Code 中打开新文件夹的例子:
8 |
9 | ```javascript
10 | let uri = Uri.file('/some/path/to/folder');
11 | let success = await commands.executeCommand('vscode.openFolder', uri);
12 | ```
13 |
14 | ## 命令
15 |
16 | `vscode.executeWorkspaceSymbolProvider` - 执行工作区所有的**符号**供应器函数
17 |
18 | - *query* - 搜索关键词
19 | - *(returns)* - promise函数, 且参数为具有SymbolInformation和DocumentSymbol的实例数组.
20 |
21 | `vscode.executeDefinitionProvider` - 执行所有的**定义**供应器函数
22 |
23 | - *uri* - 文档的Uri
24 | - *position* - 某个符号的位置
25 | - *(returns)* - promise函数, 且参数为Location实例数组.
26 |
27 | `vscode.executeDeclarationProvider` - 执行所有的**声明**供应器函数.
28 |
29 | - *uri* - 文档的Uri
30 | - *position* - 某个符号的位置
31 | - *(returns)* - promise函数, 且参数为Location实例数组.
32 |
33 | `vscode.executeTypeDefinitionProvider` - 执行所有的**类型定义**供应器函数.
34 |
35 | - *uri* - 文档的Uri
36 | - *position* - 某个符号的位置
37 | - *(returns)* - promise函数, 且参数为Location实例数组.
38 |
39 | `vscode.executeImplementationProvider` - 执行所有的**接口**供应器函数
40 |
41 | - *uri* - 文档的Uri
42 | - *position* - 某个符号的位置
43 | - *(returns)* - promise函数, 且参数为Location实例数组
44 |
45 | `vscode.executeHoverProvider` - 执行所有的**悬停**供应器函数.
46 |
47 | - *uri* - 文档的Uri
48 | - *position* - 某个符号的位置
49 | - *(returns)* - promise函数, 且参数为Hover实例数组
50 |
51 | `vscode.executeDocumentHighlights` - 执行**文档高亮**供应器函数.
52 |
53 | - *uri* - 文档的Uri
54 | - *position* - 在文档中的位置
55 | - *(returns)* - promise函数, 且参数为DocumentHighlight实例数组
56 |
57 | `vscode.executeReferenceProvider` - 执行**引用**供应器函数
58 |
59 | - *uri* - 文档的Uri
60 | - *position* - 在文档中的位置
61 | - *(returns)* - promise函数, 且参数为Location实例数组
62 |
63 | `vscode.executeDocumentRenameProvider` - 执行**重命名**供应器函数
64 |
65 | - *uri* - 文档的Uri
66 | - *position* - 在文档中的位置
67 | - *newName* - 新的符号名称
68 | - *(returns)* - promise函数, 且参数为WorkspaceEdit
69 |
70 | `vscode.executeSignatureHelpProvider` - 执行**符号帮助**供应器函数
71 |
72 | - *uri* - 文档的Uri
73 | - *position* - 在文档中的位置
74 | - *triggerCharacter* - (可选的)当用户输入特定字符时(如`,` 或 `(`)触发符号帮助
75 | - *(returns)* - promise函数, 且参数为SignatureHelp
76 |
77 | `vscode.executeDocumentSymbolProvider` - 执行**文档符号**供应器函数
78 |
79 | - *uri* - 文档的Uri
80 | - *(returns)* - promise函数, 且参数为具有SymbolInformation和DocumentSymbol的实例数组
81 |
82 | `vscode.executeCompletionItemProvider` - 执行**自动补全**供应器函数
83 |
84 | - *uri* - 文档的Uri
85 | - *position* - 在文档中的位置
86 | - *triggerCharacter* - (可选的)当用户输入诸如(`,` `(`)之类的字符时触发
87 | - *itemResolveCount* - (可选的)补全的符号数量(数目太大会减慢补全速度)
88 | - *(returns)* - promise函数, 且参数为CompletionList实例
89 |
90 | `vscode.executeCodeActionProvider` - 执行**代码操作小灯泡提示**供应器函数
91 |
92 | - *uri* - 文档的Uri
93 | - *range* - 在文档中的范围
94 | - *(returns)* - promise函数, 且参数为Command实例数组
95 |
96 | `vscode.executeCodeLensProvider` - 执行**CodeLens**供应器函数
97 |
98 | - *uri* - 文档的Uri
99 | - *itemResolveCount* - (可选的)需要解析的lenses数量, 数目太大会影响性能
100 | - *(returns)* - promise函数, 且参数为CodeLens实例数组
101 |
102 | `vscode.executeFormatDocumentProvider` - 执行**格式化文档**供应器函数
103 |
104 | - *uri* - 文档的Uri
105 | - *options* - 配置项
106 | - *(returns)* - promise函数, 且参数为TextEdits数组
107 |
108 | `vscode.executeFormatRangeProvider` - 执行**局部格式化**供应器函数
109 |
110 | - *uri* - 文档的Uri
111 | - *range* - 限制的范围
112 | - *options* - 配置项
113 | - *(returns)* - promise函数, 且参数为TextEdits数组
114 |
115 | `vscode.executeFormatOnTypeProvider` - 执行**格式化文档**供应器函数
116 |
117 | - *uri* - 文档的Uri
118 | - *position* - 在文档中的位置
119 | - *ch* - 在输入某个字符之后进行格式化
120 | - *options* - 配置项
121 | - *(returns)* - promise函数, 且参数为TextEdits数组
122 |
123 | `vscode.executeLinkProvider` - 执行**文档链接**供应器函数
124 |
125 | - *uri* - 文档的Uri
126 | - *(returns)* - promise函数, 且参数为DocumentLink实例数组
127 |
128 | `vscode.executeDocumentColorProvider` - 执行**文档颜色**供应器函数
129 |
130 | - *uri* - 文档的Uri
131 | - *(returns)* - promise函数, 且参数为ColorInfomation对象数组
132 |
133 | `vscode.executeColorPresentationProvider` - 执行**色彩呈现**供应器函数
134 |
135 | - *color* - 需要展示并插入的颜色
136 | - *context* - 上下文对象, 包括uri和影响范围
137 | - *(returns)* - promise函数, 且参数为ColorPresentation对象数组
138 |
139 | `vscode.openFolder` - 在当前窗口或者新的窗口打开一个文件夹或者工作区
140 |
141 | - *uri* - 被打开的文件夹或工作区Uri. 如果未提供, 会打开一个询问提示框
142 | - *newWindow* - (可选的)是否在新窗口打开. 默认在本窗口
143 |
144 | !> **注意:** 在当前窗口打开, 如果未设置`newWindow = true`, 会在指定的工作区或者文件夹开启新的拓展主机进程, 并且关闭当前拓展主机进程.
145 |
146 | `vscode.diff` - 在diff编辑器中打开指定资源以比较它们的内容
147 |
148 | - *left* diff编辑器左边的文件
149 | - *right* diff编辑器右边的文件
150 | - *title* (可选)diff编辑器标题
151 | - *options* (可选)编辑器配置项, 参考`vscode.TextDocumentShowOptions`
152 |
153 | `vscode.open` - 在编辑器打开指定文件
154 |
155 | - *resource* - 要打开的文件
156 | - *columnOrOptions* - (可选)可以是要打开的编辑器列,也可以是编辑器选项,参考`vscode.TextDocumentShowOptions`
157 |
158 | 可以是文本文件、二进制文件、http(s) url. 如果需要更多的配置项, 使用`vscode.window.showTextDocument`代替.
159 |
160 | `vscode.removeFromRecentlyOpened` - 在最近打开的列表中移除一个路径
161 |
162 | - *path* - 被移除的路径
163 |
164 | `vscode.setEditorLayout` - 设置编辑器布局
165 |
166 | - *layout* - 被设置的布局
167 |
168 | 布局是一个对象,带有初始布局方向(可选,0 = 水平布局,1 = 垂直布局),还有一个包含编辑器组的数组。每个编辑器组又有一个尺寸和另一个数组,其中有矩形布局和方向信息。如果设置了编辑器组的大小,每一行或者每一列的总和必须为1。比如一个2x2的网格:`{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`
169 |
170 | `cursorMove` - 移动光标到视图的合理位置
171 |
172 | - *Cursor move argument object*
173 |
174 | 可以传递的键值对
175 |
176 | - 'to': 必选, 鼠标要移动到的合理位置
177 | ```
178 | 'left', 'right', 'up', 'down'
179 | 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter'
180 | 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter'
181 | 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'
182 | ```
183 | - 'by': 移动的单位. 默认根据'to'来计算.
184 | ```
185 | 'line', 'wrappedLine', 'character', 'halfLine'
186 | ```
187 | - 'value': 单位步数. 默认为'1'.
188 | - 'select': 如果为'true'则会选中. 默认为'false'.
189 |
190 | `editorScroll` - 编辑器滚动方向
191 |
192 | - *Editor scroll argument object*
193 |
194 | 可以传递的键值对
195 |
196 | - 'to': 必须的. 方向值
197 | ```
198 | 'up', 'down'
199 | ```
200 | - 'by': 移动的单位. 默认根据'to'来计算.
201 | ```
202 | 'line', 'wrappedLine', 'page', 'halfPage'
203 | ```
204 | - 'value': 单位步数. 默认为'1'.
205 | - 'revealCursor': 如果为'true', 在超出滚动视图也会显示光标.
206 |
207 | `revealLine` - 在给定的位置显示行
208 |
209 | - *Reveal line argument object*
210 |
211 | 可以传递的键值对
212 |
213 | - 'lineNumber': 必须的. 行号
214 | - 'at': 显示的合理位置
215 | ```
216 | 'top', 'center', 'bottom'
217 | ```
218 |
219 | `editor.unfold` - 展开编辑器内容
220 |
221 | - *Unfold editor argument*
222 |
223 | 可以传递的键值对
224 |
225 | - 'levels': 展开的层级数. 默认为 1.
226 | - 'direction': 如果是'up', 向上展开, 否则向下展开
227 | - 'selectionLines': 要使用展开功能的起始行(从0起)。如果不设置,就会使用当前激活的行(选中区).
228 |
229 | `editor.fold` - 折叠编辑器内容
230 |
231 | - `Fold editor argument`
232 |
233 | 可以传递的键值对
234 |
235 | - 'levels': 折叠的的层级数。默认为1
236 | - 'direction': 如果设置为'up',向上折叠,不然向下折叠
237 | - 'selectionLines': 要使用折叠功能的起始行(从0起)。如果不设置,就会使用当前激活的行(选中区)
238 |
239 | `editor.action.showReferences` - 在文件中显示引用
240 |
241 | - *uri* - 要显示引用的文件
242 | - *position* - 要显示的位置
243 | - *locations* - 位置数组
244 |
245 | `moveActiveEditor` - 通过标签或者组移动激活的编辑器
246 |
247 | - *Active editor move argument*
248 |
249 | 参数
250 |
251 | - 'to': String. 目标位置
252 | - 'by': String. 移动的单位(通过标签或者组).
253 | - 'value': Number. 要移动的位置或者绝对位置值
254 |
255 | ## 简单命令
256 |
257 | 简单的命令不需要参数, 可以在`keybindings.json`的**键盘快捷方式**列表中找到. 在文件底部的注释块中列出了未绑定的命令.
258 |
259 | 查看`keybindings.json`:
260 |
261 | Windows, Linux: **文件** > **首选项** > **键盘快捷方式** > `keybindings.json`
262 |
263 | macOS: **编码** > **首选项** > **键盘快捷方式** > `keybindings.json`
--------------------------------------------------------------------------------
/docs/extension-authoring/example-hello-world.md:
--------------------------------------------------------------------------------
1 | # 示例:Hello World
2 |
3 | ## 你的第一个插件
4 | ---
5 | 本节通过Hello World这个完整的项目手把手教你掌握VS Code扩展性概念。
6 | 在本项目中,我们会给VS Code添加一个新的命令,虽然只是用来显示"Hello World"信息。在本节的最后,你将和编辑器编辑器互动,查找用户选中的文本。
7 | #### 预备工作
8 | 请查看[生成插件-预备工作](/extension-authoring/extension-generator?id=预先准备)
9 |
10 | #### 生成新插件
11 | 请查看[生成插件-运行Yo](/extension-authoring/extension-generator?id=运行yo-code😎)
12 | ## 运行插件
13 | ---
14 | - 打开VS Code,选择`文件`>`打开文件夹`,选择你刚刚生成的项目目录
15 | - 点击`F5`或者`Debug`按钮,然后点击`开始`
16 | - 新的VS Code实例会运行在特殊环境中(`Extension Development Host`
17 | - 按下`⇧⌘P`(windows `shift + ctrl + p`),输入命令`Hello world`
18 | - 恭喜!你的第一个VS Code插件执行成功了
19 |
20 | 
21 | ## 插件的目录结构
22 | ---
23 |
24 | 构建完毕之后,你的插件开发目录看起来应该是这样的
25 | ```
26 | .
27 | ├── .gitignore
28 | ├── .vscode // VS Code 文件
29 | │ ├── launch.json
30 | │ ├── settings.json
31 | │ └── tasks.json
32 | ├── .vscodeignore // 发布插件时忽略的文件
33 | ├── README.md
34 | ├── src
35 | │ └── extension.ts // 插件的入口(源文件)
36 | ├── test // 测试文件夹
37 | │ ├── extension.test.ts // extension.test.js, 如果是 JavaScript 插件的话
38 | │ └── index.ts // index.js, 如果是 JavaScript 插件的话
39 | ├── node_modules
40 | │ ├── vscode // 包含了vscode插件开发时的类型定义文件
41 | │ └── typescript // typescript的编译器 (TypeScript only)
42 | ├── out // 编译出口 (TypeScript only)
43 | │ ├── extension.js // 插件入口
44 | │ ├── extension.js.map
45 | │ └── test
46 | │ ├── extension.test.js
47 | │ ├── extension.test.js.map
48 | │ ├── index.js
49 | │ └── index.js.map
50 | ├── package.json // 插件清单
51 | ├── tsconfig.json // jsconfig.json, 如果是 JavaScript 插件的话
52 | └── vsc-extension-quickstart.md // 快速上手插件开发
53 | ```
54 | 让我们看看这些文件夹都是干什么用的:
55 | #### 插件清单:`package.json`
56 | - 每个VS Code插件都有`package.json`文件,文件内包含了这个插件功能和用处。
57 | - 当项目启动时,VS Code会立即读取这个文件中的每个`配置(contributes)`部分并作出响应。
58 | - 请阅读[package.json插件清单](/extensibility-reference/extension-manifest.md)参考文档
59 | - 更多信息请参阅[package.json发布内容配置](/extensibility-reference/contribution-points.md)参考文档
60 |
61 | ?> 译者注:为了便于理解,`contribution / contributes`在本教程中译为**发布内容配置/配置**,`contribution points`译为**发布内容配置点/配置点**。
62 |
63 | **示例:基于TypeScript的pacakge.json**
64 |
65 | ```json
66 | {
67 | "name": "myFirstExtension",
68 | "description": "",
69 | "version": "0.0.1",
70 | "publisher": "",
71 | "engines": {
72 | "vscode": "^1.5.0"
73 | },
74 | "categories": [
75 | "Other"
76 | ],
77 | "activationEvents": [
78 | "onCommand:extension.sayHello"
79 | ],
80 | "main": "./out/extension",
81 | "contributes": {
82 | "commands": [{
83 | "command": "extension.sayHello",
84 | "title": "Hello World"
85 | }]
86 | },
87 | "scripts": {
88 | "vscode:prepublish": "tsc -p ./",
89 | "compile": "tsc -watch -p ./",
90 | "postinstall": "node ./node_modules/vscode/bin/install",
91 | "test": "node ./node_modules/vscode/bin/test"
92 | },
93 | "devDependencies": {
94 | "typescript": "^2.0.3",
95 | "vscode": "^1.5.0",
96 | "mocha": "^2.3.3",
97 | "@types/node": "^6.0.40",
98 | "@types/mocha": "^2.2.32"
99 | }
100 | }
101 | ```
102 | !> 提示: 基于JavaScript的插件没有scripts部分,因为不需要编译。
103 |
104 | 这份`package.json`文件说了什么呢?
105 | - **配置部分(contributes)**给*命令面板*定义了一个叫做`Hello world`的入口,它会调用'extension.sayHello'。
106 | - 当命令"extension.sayHello"调用时,执行`loaded`(激活事件)请求。
107 | - 在"`./out/extension.js`"中,存放着我们的主文件。
108 |
109 | !> 注意:VS Code **不会一启动就加载插件**。插件必须在`activationEvents`中描述它的启动时机,比如`loaded`事件。
110 | #### 生成的代码
111 | 自动生成的代码存放在**extension.ts**(或者**extension.js**中)。
112 | ```typescript
113 | // 'vscode'模块包含了VS Code extensibility API
114 | // 按下述方式导入这个模块
115 | import * as vscode from 'vscode';
116 |
117 | // 一旦你的插件激活,vscode会立刻调用下述方法
118 | export function activate(context: vscode.ExtensionContext) {
119 |
120 | // 用console输出诊断信息(console.log)和错误(console.error)
121 | // 下面的代码只会在你的插件激活时执行一次
122 | console.log('Congratulations, your extension "my-first-extension" is now active!');
123 |
124 | // 入口命令已经在package.json文件中定义好了,现在调用registerCommand方法
125 | // registerCommand中的参数必须与package.json中的command保持一致
126 | let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
127 | // 把你的代码写在这里,每次命令执行时都会调用这里的代码
128 | // ...
129 | // 给用户显示一个消息提示
130 | vscode.window.showInformationMessage('Hello World!');
131 | });
132 |
133 | context.subscriptions.push(disposable);
134 | }
135 | ```
136 | - 每个插件都应该在主文件中注册一个`activate()`函数,因为这个函数只会调用一次。你只有`在package.json`中注册了`activationEvents`列表中的事件,激活事件才会被调用。
137 | - 如果插件使用了系统资源(如:生成进程),则需要从主文件中导出名为`deactive()`的函数去清理释放这些资源,VS Code会在关闭时调用这个方法。
138 | - 这个插件导入了VS Code API,然后注册了"extension.sayHello"命令和回调函数,执行后在VS Code中显示一条"Hello World!"消息。
139 |
140 | !> 注意: `package.json`的`contributes`部分给*命令面板*添加了一个调用入口。`extension.ts/.js`定义了`extension.sayHello`的实现。对于 Typescript类型的插件来说,生成的`out/extension.js`会在运行时加载。
141 | #### 其他文件
142 | - `vscode/launch.json` 告诉VS Code启用插件开发模式。它也描述了`.vscode/tasks.json`中需要Typescript编译器的预加载任务。
143 | - `vscode/settings.json` 默认排除外部文件夹。你如果想隐藏一些文件,可以修改这个配置。
144 | - `gitignore` - 告诉git不跟踪哪些文件。
145 | - `vscodeignore` - 告诉打包工具,发布插件时应该忽略哪些文件。
146 | - `README.md` - 为插件的使用用户提供良好的文档。
147 | - `vsc-extension-quickstart.md` - 你的快速开始指南。
148 | - `test/extension.test.ts` - 把你的单元测试放在这里,看看和VS Code API有哪些出入。
149 | ## 插件激活过程
150 | ---
151 | 我们刚刚已经了解了开发目录下的每个文件,现在我们看看你的插件是怎么运行起来的:
152 | 1. 插件开发环境发现了这个插件,然后读取它的`package.json`
153 | 2. 你按下`ctrl shift p`时,*命令面板*显示出已注册的命令列表
154 | 3. 在列表中你找到`package.json`中定义的`Hello world`命令入口
155 | 4. 选中并执行`Hello world`,随即执行真实的"`extension.sayHello`"命令
156 | - 创建`"onCommand:extension.sayHello"`激活事件
157 | - 激活所有注册在`activationEvents`的事件
158 | - Javascript虚拟机加载`./out/extension.js`文件
159 | - VS Code查找导出的`activate`函数,并调用
160 | 5. 调用`"extension.sayHello"`注册的函数
161 | 6. 函数执行,显示出"Hello world"消息
162 | ## 调试插件
163 | ---
164 | 直接在你的代码里打上断点就可以调试了,很简单吧。
165 |
166 | 
167 |
168 | !> 注意:VS Code具有解析sourcemap的能力,所以你可以直接在Typescript代码中调试。
169 |
170 | ?>小提示:调试控制台(Debug Console)能输出所有console打印的日志。
171 |
172 | 查看更多关于插件[开发环境](/extension-authoring/developing-extensions.md)的东西。
173 | ## 小小的改造
174 | ---
175 | 试着修改你的`extension.ts`(或者`extension.js`)中`extension.sayHello`的实现,我们把它改造成一个对选中文本计数的功能。
176 | ```typescript
177 | let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
178 | // 每当你执行命令,这里的代码都会执行一次
179 |
180 | let editor = vscode.window.activeTextEditor;
181 | if (!editor) {
182 | return; // 没有打开的编辑器
183 | }
184 |
185 | let selection = editor.selection;
186 | let text = editor.document.getText(selection);
187 |
188 | // 给用户一个消息提示框
189 | vscode.window.showInformationMessage('Selected characters: ' + text.length);
190 | });
191 | ```
192 | !> 当你修改了代码,你需要按Ctrl + R(macOS Cmd + R)重启Extension Development Host,或者直接按VS Code上面的重启按钮
193 |
194 | 新建一个文件,输入一些文本然后选中。当你运行**Hello World**命令,你应该能看到字符计数的消息框。
195 |
196 | 
197 | ## 在本地安装你的插件
198 | ---
199 | 好了,现在你已经完成了一个运行在开发模式下的插件,为了让你的插件运行在所有VS Code实例中,你需要在本地插件目录下新建一个文件夹,然后复制过去:
200 | - Windows: `%USERPROFILE%\.vscode\extensions`
201 | - macOS/Linux: `$HOME\.vscode\extensions`
202 |
203 | ## 发布插件
204 | ---
205 | 参阅[分享插件](/extension-authoring/publish-extension.md)
206 | ## 下一步
207 | ---
208 | 在本篇指引中,我们实现了一个小小的插件。在[示例-Word Count](docs/extension-authoring/example-word-count.md)中你能找到完整的例子,学习如何在Markdown文件中监听编辑器的文档变动事件、显示文本字数。
209 |
210 | 如果你想查看更多extension API的概述,看看这些主题吧:
211 | - [Extension API 概览](/extensibility-reference/overview.md) - 了解完整的VS Code扩展性模型。
212 | - [API原则和模式](/extensibility-reference/principles-patterns.md) - VS Code的扩展性基于这些指导性原则和模式。
213 | - [发布内容配置](/extensibility-reference/contribution-points.md) - 各种各样的VS Code发布内容配置项
214 | - [激活事件](/extensibility-reference/activation-events.md) - VS Code激活事件参考
215 | - [更多插件示例](/extension-authoring/samples.md) - 看看我们的插件示例列表
--------------------------------------------------------------------------------
/docs/extension-authoring/example-word-count.md:
--------------------------------------------------------------------------------
1 | # 示例 Word Count
2 |
3 | 如果你还没有接触过[你的第一个插件](/extension-authoring/example-hello-world.md)章节,我们建议你先去了解一下。
4 |
5 | 本篇示例将会告诉你,如何制作一个Markdown辅助编辑工具。开始之前,我们先了解一下本篇你将接触到的插件功能:当编辑Makrdown文件时状态栏会显示编辑区字数,如果你编辑文档或者打开了另一个md文件字数也会随之改变。
6 |
7 | 
8 |
9 | ?> 小贴士:如果你碰到了什么问题,可以在这里[下载完整的项目](https://github.com/microsoft/vscode-wordcount)进行调试
10 |
11 | ## 本节要点
12 | 本章将通过三个部分让你了解vscode有关的概念:
13 | - [更新状态栏](#更新状态栏) - 在*状态栏*中显示自定义文本
14 | - [订阅事件](#订阅事件) - 通过编辑器事件更新*状态栏*
15 | - [释放插件资源](#释放插件资源) - 比如:释放事件订阅和UI回调函数
16 |
17 | 如果你还不熟悉生成插件的步骤,请先了解之前的章节:[生成插件-运行Yo](/extension-authoring/extension-generator?id=运行yo-code😎)
18 |
19 | 就像你之前在[示例:Hello-world](/extension-authoring/example-hello-world.md)中做的一样,使用`F5`或者`Cmd + R`运行该项目。
20 |
21 | ## 更新状态栏
22 | ---
23 | 将下列代码更新到`extension.ts`中。这段代码声明了一个`WordCounter`类用于控制文本计数并显示到状态栏中,我们依然用了"Hello World"这个命令来执行`updateWordCount`。
24 |
25 | ```typescript
26 | // 'vscode'模块包含了VS Code扩展性API
27 | // 导入你要用到的扩展类
28 | import {
29 | window,
30 | commands,
31 | Disposable,
32 | ExtensionContext,
33 | StatusBarAlignment,
34 | StatusBarItem, TextDocument
35 | } from 'vscode';
36 |
37 | // 当你的插件激活时,会调用这个方法。'activation'是package.json中定义好的activation events
38 | export function activate(context: ExtensionContext) {
39 |
40 | // 用console对象输出诊断和错误信息(console.log / console.error).
41 | // 下面这行代码,只会在你的插件激活时执行一次
42 | console.log('Congratulations, your extension "WordCount" is now active!');
43 |
44 | // 新建一个字数计数器
45 | let wordCounter = new WordCounter();
46 | let disposable = commands.registerCommand('extension.sayHello', () => {
47 | wordCounter.updateWordCount();
48 | });
49 |
50 | // 把disposables添加到一个列表中,以便关闭插件时释放资源
51 | context.subscriptions.push(wordCounter);
52 | context.subscriptions.push(disposable);
53 | }
54 |
55 | class WordCounter {
56 | private _statusBarItem: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left);
57 |
58 | public updateWordCount() {
59 |
60 | // 获取当前编辑器
61 | let editor = window.activeTextEditor;
62 | if (!editor) {
63 | this._statusBarItem.hide();
64 | return;
65 | }
66 |
67 | let doc = editor.document;
68 |
69 | // 只对markdown文件生效
70 | if (doc.languageId === "markdown") {
71 | let wordCount = this._getWordCount(doc);
72 |
73 | // 更新状态栏
74 | this._statusBarItem.text = wordCount !== 1 ? `${wordCount} Words` : '1 Word';
75 | this._statusBarItem.show();
76 | } else {
77 | this._statusBarItem.hide();
78 | }
79 | }
80 |
81 | public _getWordCount(doc: TextDocument): number {
82 | let docContent = doc.getText();
83 |
84 | // 去除多余空格
85 | docContent = docContent.replace(/(< ([^>]+)<)/g, '').replace(/\s+/g, ' ');
86 | docContent = docContent.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
87 | let wordCount = 0;
88 | if (docContent !== "") {
89 | wordCount = docContent.split(" ").length;
90 | }
91 | return wordCount;
92 | }
93 |
94 | dispose() {
95 | this._statusBarItem.dispose();
96 | }
97 | }
98 | ```
99 |
100 | 现在让我们试试更新插件吧。
101 |
102 | 在VS Code中(非开发主机窗口)按下`F5`或者直接按重启按钮。假设你已经打开了一个markdown文件,然后和Hello-World示例一样,我们在*命令面板*中输入`Hello World`启动插件。
103 |
104 | 
105 |
106 | 很棒,我们接下来做更cool的事情——实时更新字数。
107 |
108 | ## 订阅事件
109 | ---
110 |
111 | 我们先来看看事件中的类方法:
112 | - `onDidChangeTextEditorSelection` - 鼠标位置变动时触发。
113 | - `onDidChangeActiveTextEditor` - 激活编辑器(打开的编辑器)切换的时候触发。
114 |
115 | 为了实现这个目标,我们给`extension.ts`添加一个新类,订阅上述事件然后让`WordCounter`更新字数。
116 |
117 | ?>在实现时,你需要注意我们是如何把**消息订阅( subscription )**转换为**释放器( Disposables )**来管理的,它将监听并释放自己。
118 |
119 | 根据下列代码,将`WordCounterController`类添加到`extension.ts`文件底部。
120 | ```typescript
121 | class WordCounterController {
122 |
123 | private _wordCounter: WordCounter;
124 | private _disposable: Disposable;
125 |
126 | constructor(wordCounter: WordCounter) {
127 | this._wordCounter = wordCounter;
128 |
129 | // 订阅 文本选区变更 和 编辑器激活事件
130 | let subscriptions: Disposable[] = [];
131 | window.onDidChangeTextEditorSelection(this._onEvent, this, subscriptions);
132 | window.onDidChangeActiveTextEditor(this._onEvent, this, subscriptions);
133 |
134 | // 为当前文件更新计数器
135 | this._wordCounter.updateWordCount();
136 |
137 | // 把两个事件订阅器整合成一个临时容器
138 | this._disposable = Disposable.from(...subscriptions);
139 | }
140 |
141 | dispose() {
142 | this._disposable.dispose();
143 | }
144 |
145 | private _onEvent() {
146 | this._wordCounter.updateWordCount();
147 | }
148 | }
149 | ```
150 |
151 | 我们不希望通过执行命令才启动词汇计数插件,而是markdown文件一打开插件就应该启动。
152 |
153 | 首先,我们将`active`函数替换成这样:
154 | ```typescript
155 | // 输出诊断信息
156 | console.log('Congratulations, your extension "WordCount" is now active!');
157 |
158 | // 新建一个词汇计数器
159 | let wordCounter = new WordCounter();
160 | let controller = new WordCounterController(wordCounter);
161 |
162 | // 插件关闭时,释放器会自动释放
163 | context.subscriptions.push(controller);
164 | context.subscriptions.push(wordCounter);
165 | ```
166 |
167 | 然后,我们必须保证`Markdown`文件打开时才激活。在之前的示例中,我们通过`extension.sayHello`命令激活插件,而我们现在用不到了,删除`package.json`文件中原来的`contributes`键。
168 | ```json
169 | "contributes": {
170 | "commands":
171 | [{
172 | "command": "extension.sayHello",
173 | "title": "Hello World"
174 | }
175 | ]
176 | }
177 | ```
178 | 用下面的代码替换掉:
179 | ```json
180 | "activationEvents": [
181 | "onLanguage:markdown"
182 | ]
183 | ```
184 | 为[onLanguage: ${language}]()配置一个语言,在这里是"markdown" 。这样一来打开这一类文件就会触发这个事件了。
185 |
186 | 重启插件(按下`Cmd + R`或者重启按钮),然后新建一个`.md`文件,你应该能看到下图的效果。
187 |
188 | 
189 |
190 | 如果你在`active`函数上打了断点,你应该能看到markdown文件被打开时只触发了一次。`WordCountController`构造器运行之后,订阅了编辑器事件,这样我们整个插件就正常运行了。
191 |
192 | ## 自定义状态栏
193 | ---
194 |
195 | VS Code允许你定制状态栏的颜色、图标、提示文本等额外样式。如果你不清楚**状态栏**相关的API,你可以查看该类型的代码提示,你也可以通过`vscode.d.ts`VS Code扩展性API查看,这个文件就在你生成的项目文件夹里,在编辑器中打开`node_modules\vscode\vscode.d.ts`,你能看到完整的扩展性 API和注释。
196 |
197 | 
198 |
199 | 更新StatusBarItem接口的代码
200 | ```typescript
201 | // 更新状态栏
202 | this._statusBarItem.text = wordCount !== 1 ? `$(pencil) ${wordCount} Words` : '$(pencil) 1 Word';
203 | this._statusBarItem.show();
204 | ```
205 | 这样就在计数左边显示了一个[Github Oction](https://octicons.github.com/)的`pencil`图标
206 |
207 | 
208 |
209 | ## 释放插件资源
210 | ---
211 |
212 | 现在,我们来深入了解一下VS Code是怎么通过[释放器(Disposables)](/extensibility-reference/principles-patterns?id=disposables(释放器))控制资源的。
213 |
214 | 当一个插件被激活,它会传入一个`ExtensionContext`对象, 这个对象有一个用于订阅释放器(Disposable)的`subscriptions`方法。插件将=释放器添加到这个订阅列表中,VS Code则会在插件关闭的时候释放这些对象。
215 |
216 | 很多能生成工作区(workspace)或者UI对象的VS Code API(比如:`registerCommand`)会自动返回一个释放器,我们可以直接调用他们的dispose方法释放UI元素。
217 |
218 | 事件则有所不同,比如`onDid*`这类事件订阅器会返回一个释放器。插件通过释放事件的释放器(Disposable)来取消已经订阅的事件。在我们的例子里,`WordCountController`把事件订阅的释放器直接保存到自己的释放器列表中,在插件关闭时释放。
219 | ```typescript
220 | // 订阅选区变动事件和编辑器激活事件
221 | let subscriptions: Disposable[] = [];
222 | window.onDidChangeTextEditorSelection(this._onEvent, this, subscriptions);
223 | window.onDidChangeActiveTextEditor(this._onEvent, this, subscriptions);
224 |
225 | // 把两个事件订阅器生成一个组合的临时容器
226 | this._disposable = Disposable.from(...subscriptions);
227 | ```
228 | ## 在本地安装你的插件
229 | ---
230 |
231 | 到目前为止,你的插件都还跑在插件开发模式中,要想让你的插件在正常的VS Code中运行起来将你的插件复制到`.vscode/extensions`目录下。
232 |
233 | ## 发布插件
234 | ---
235 |
236 | 参阅[分享插件](/extension-authoring/publish-extension.md)
237 |
238 | ## 下一步
239 |
240 | [插件生成器](/extension-authoring/extension-generator.md) - 学习Yo Code插件生成器的更多选项
241 |
242 | [Extenstion API](/extensibility-reference/overview.md) - 插件API概览
243 |
244 | [发布插件](/extension-authoring/publish-extension.md) - 学会如何在应用市场发布一个公共插件
245 |
246 | [编辑器 API](/extensibility-reference/vscode-api.md) - 学习更多有关文档, 文档编辑器和编辑的内容
247 |
248 | [更多插件示例](/extension-authoring/samples.md) - 在插件示例列表学习其他用法
--------------------------------------------------------------------------------
/docs/references/extension-guidelines.md:
--------------------------------------------------------------------------------
1 | # 插件开发准则
2 |
3 | ## 架构
4 |
5 | VS Code UI 包含了两类元素:容器和容器项。容器是指视图的顶层层级,它包括:
6 |
7 | 
8 |
9 | 1. 活动栏([Activiti Bar](/references/extension-guidelines#视图容器))
10 | 2. 侧边栏(Sidebar)
11 | 3. 编辑器(Editor)
12 | 4. 面板(Panel)
13 | 5. 状态栏(Status Bar)
14 |
15 | 容器项则在容器的内部,他们的种类就很丰富了:
16 |
17 | 
18 |
19 | 1. 视图容器(View Container)
20 | 2. 视图([View](/references/extension-guidelines#视图))
21 | 3. 视图工具区(View Toolbar)
22 | 4. 侧边栏工具区(Sidebar Toolbar)
23 | 5. 编辑器工具区(Editor Toolbar)
24 | 6. 面板工具区(Pannel Toolbar)
25 | 7. 状态栏项([Status Bar Item](/references/extension-guidelines#状态栏))
26 |
27 |
28 | ## 通知框
29 |
30 | [通知框](/extension-capabilities/common-capabilities#display-notifications) 会从 VS Code 右下角弹出,它可以展示一些简要的信息。你能够设置的通知类型有三种:
31 |
32 | - [信息提示](https://code.visualstudio.com/api/references/vscode-api#window.showInformationMessage)
33 | - [警告提示](https://code.visualstudio.com/api/references/vscode-api#window.showWarningMessage)
34 | - [错误提示](https://code.visualstudio.com/api/references/vscode-api#window.showErrorMessage)
35 |
36 | 不要过多地发送通知,以免分散用户的注意力。
37 |
38 | 
39 |
40 | *这条通知是用户**更新版本**后弹出的,注意这个通知中仅仅展示了信息不含任何操作。*
41 |
42 | 
43 |
44 | *这个例子中带有一个黄色的高亮警告,附带 3 个按钮——它要求用户进行介入处理。*
45 |
46 | 
47 |
48 | *这个例子是无操作的错误通知。*
49 |
50 | ✔建议
51 | - 发送通知的时候不要过分吸引用户的注意力
52 | - 为每个通知添加 **不要再提示** 按钮
53 | - 短时间内只提示一次
54 |
55 | ❌ 不要
56 | - 发送重复通知
57 | - 用于促销广告
58 | - 插件首次安装后用户收集用户反馈
59 | - 没有必要的操作还硬加按钮
60 |
61 | ### 进度通知
62 |
63 | 当你需要展示一个不知何时才能完成的任务进度时(比如:初始化环境),你可以使用进度通知。这种类型的通知应该作为当前上下文(在视图或者编辑器内)处理进度通知类型的最后手段。
64 |
65 | ✔建议
66 | - 提供一个查看详情的链接(比如进度日志)
67 | - 处理过程中给出信息(比如:初始化、构建,等等)
68 | - 提供取消操作(如果可用的话)
69 | - 如果有超时场景,提供倒计时
70 |
71 | ❌ 不要
72 | - 不销毁进度通知
73 |
74 | 
75 |
76 | *这个例子演示了远程连接初始化时的进度通知,它同时也提供了输出日志(details)*
77 |
78 | ## 视图
79 |
80 | [视图](/references/contribution-points#contributesviews)是内容的容器,它们会出现在注入侧边栏或者面板这样的地方。视图可以包含树视图或者自定义视图,这些视图可以包含视图操作。视图可被用户调整位置,比如放到其他视图、活动栏和面板中。请控制你的视图数量以便其他插件的视图还有空间展示。
81 |
82 | ✔建议
83 | - 如果可以的话,使用现有的图标
84 | - 为语言文件使用文件图标
85 | - 用树视图展示数据
86 | - 为每个视图提供活动栏图标
87 | - 控制视图数量最小化
88 | - 控制名称长度最小化
89 | - 克制地使用自定义 webview 视图
90 |
91 | ❌ 不要
92 | - 对已有功能重新造轮子
93 | - 使用树视图项作为唯一的操作入口(如,搜索栏)
94 | - 非必要的情况下也使用自定义 webview
95 | - 在编辑器中使用视图容器装载 webview
96 |
97 | 
98 |
99 | *这个例子使用了树视图展示一组测试的测试状态。每种测试结果都有其唯一图标类型与之对应。*
100 |
101 | ### 视图位置
102 |
103 | 视图可被放在[现有的视图容器](/references/contribution-points#contributesviews)中,比如文件管理器、源管理器(SCM)和调试视图容器中。你也可以在活动栏中添加自定义视图容器,然后再往里添加视图。另外,视图可被添加到任何面板或者他们自己所属的自定义视图容器中。
104 |
105 | 
106 |
107 | ### 视图容器
108 |
109 | [视图容器](/references/contribution-points#contributesviewsContainers)是活动栏的一部分,每个容器都有其独有的图标。
110 |
111 | 
112 |
113 | 这个例子用了一个边框图标来展示自定义视图容器。
114 |
115 | ### 视图中的进度条
116 |
117 | 如果你的视图在 源(SCM)视图容器中,你也可以[显示进度条](https://code.visualstudio.com/api/references/vscode-api#ProgressLocation)
118 |
119 | ### 欢迎视图
120 |
121 | 当视图没有任何内容时,你可以[添加一些内容来引导用户](/references/contribution-points#contributesviewsWelcome)如何使用你的插件。欢迎视图支持链接和图标。
122 |
123 | ✔建议
124 | - 仅在必要时使用欢迎视图
125 | - 如有需要可使用链接而不是按钮
126 | - 按钮仅用于基础性的操作
127 | - 描述清楚链接指向
128 | - 控制内容的长度
129 | - 控制欢迎视图的数量
130 | - 控制视图中的按钮数量
131 |
132 | ❌ 不要
133 | - 非必要的场景中也使用按钮
134 | - 将欢迎视图当成销售页面
135 | - 每个链接的标题都叫"查看更多"
136 |
137 | 
138 |
139 | 这个例子使用了一个基础按钮,其他视图则包含了期待用户知晓的具体文档链接。
140 |
141 | ## Webviews
142 | 如果你需要展示的自定义功能超出了VS Code API 的能力,那么你可以使用完全可定制的 [webview](/extension-guides/webview)。再次提示开发者:仅在必要时使用 webview。
143 |
144 | ✔建议
145 | - 仅在绝对必要时使用 webview
146 | - 在合适的场景中启动你的插件
147 | - 为激活窗口打开 webview
148 | - 确保视图中的所有元素都是可定制主题的(查看 [webview-view-sample](https://github.com/microsoft/vscode-extension-samples/blob/main/webview-view-sample/media/main.css)和[color tokens](/references/theme-color)文档)
149 | - 确保你的视图遵循[可访问性指南](https://code.visualstudio.com/docs/editor/accessibility)(色彩对比度、ARIA 标签、键盘导航)
150 | - 在视图的工具区使用命令操作
151 |
152 | ❌ 不要
153 | - 用于广告宣传(包括升级、捐助等等)
154 | - 作为插件的向导页面
155 | - 在所有窗口中打开
156 | - 插件升级后打开(请使用通知)
157 | - 添加和编辑器或者工作区无关的功能
158 | - 重复发明轮子(比如:欢迎页、设置、配置等等)
159 |
160 | ### 例子
161 |
162 | **浏览器预览**
163 | 这个插件在编辑器旁边打开了一个浏览器效果预览
164 | 
165 |
166 | **Pull Request**
167 | 这个插件为自定义树视图中的工作区仓库展示了一个 Pull Request 页面,它用 webview 显示了 PR 的详细信息。
168 |
169 | **初次使用培训**
170 | 这个插件打开了一个快速启动页面,它包含了有用的操作、链接和更多信息。这个 Webview 仅仅在用户首次打开特定文件时展示,帮助用户检查是否遵循了特定步骤(比如安装或创建一个文件时)。
171 |
172 | 
173 |
174 | ### webview 视图
175 |
176 | 你可以在任意视图容器(侧边栏和面板)中添加 webview,这样的 webview 被称为 [webview 视图](https://code.visualstudio.com/api/references/vscode-api#WebviewView)。它的使用方式和常规的 webview 是一样的。
177 |
178 | 
179 |
180 | 这个 webview 视图内容展示了创建一个 PR 所需的下拉菜单、输入框和按钮。
181 |
182 | ## 状态栏
183 |
184 | [状态栏](/extension-capabilities/extending-workbench#状态栏项)位于 VS Code 工作台底部,用于展示和你的工作区相关的信息和操作。状态栏项分为两类:基础的(左)和次级的(右)。状态栏中和整个工作区(状态、问题/警告、同步状态)相关的位于左边,次级状态或者上下文相关的(语言、间距、反馈)处于右边。注意控制添加的状态栏项目数,为其他插件腾出空间。
185 |
186 | ✔建议
187 | - 使用短的文本标签
188 | - 仅在必要时使用图标
189 | - 使用语义清晰的图标
190 | - 把首要(全局)项目放在左边
191 | - 把次要(上下文)项目放在右边
192 |
193 | ❌ 不要
194 | - 添加自定义颜色
195 | - 添加的图标数量在 1 个以上(除非必要)
196 | - 添加的状态栏项数量在 1 个以上(除非必要)
197 |
198 | 
199 |
200 | *这个例子中,状态栏项的信息和整个工作区相关,所以展示在左边*
201 |
202 | ### 进度状态栏项目
203 | 当需要展示静默进度(进度在后台处理)时,建议用带有加载图标(你可以添加旋转动画)的状态栏项来展示。如果进度状态是需要用户注意到的,我们建议你将其提升到通知维度。
204 |
205 | 
206 |
207 | *这个例子中,状态栏展示静默进度*
208 |
209 | ### 错误状态栏项
210 | 如果你需要展示的项目出于警示目的需要高亮,你可以使用错误状态栏项。请将错误状态栏项作为特殊场景下的最后手段。
211 | 
212 |
213 | *这个例子中,错误状态栏项展示了文件中的代码错误*
214 |
215 | ## 快速选择
216 |
217 | [快速选择](/extension-capabilities/common-capabilities#quick-pick)是一个展示操作和接受用户输入的简易手段。在选择配置、过滤内容或者从列表中挑选项目时尤为有用。
218 | 
219 |
220 | *快速选择可以包含非常多的选项。而且选项可以由图标、详情和标签组成,而且还包括默认项。在这个例子的顶部,你可以看到多步骤模式下的后退、恢复和前进等操作。*
221 |
222 | ✔建议
223 | - 使用语义清晰的图标
224 | - 使用具体的描述展示当前项(如果需要的话)
225 | - 使用详情提供额外(简短)的说明
226 | - 在需要多选输入的场景中使用多步骤模式(比如:初始化向导)
227 | - 提供一个创建更多的选项(如果需要的话)
228 |
229 | ❌ 不要
230 | - 重复造轮子
231 | - 多种选项公用一个图标
232 | - 列表中超过 6 种图标
233 |
234 | ## 编辑器操作
235 | 编辑器的工具区会展示[编辑器操作](/references/contribution-points#contributescommands)。你可以添加图标作为快速操作,或者在悬浮菜单中添加菜单项(...)。
236 |
237 | ✔建议
238 | - 在特定上下文场景中展示操作
239 | - 尽量使用内置图标库中的图标
240 | - 悬浮菜单中的项目应作为二级操作
241 |
242 | ❌ 不要
243 | - 添加多类图标
244 | - 添加自定义颜色
245 | - 使用 emoji
246 |
247 | 
248 |
249 | *仅在 HTML 页面中展示这个图标来表示加载页面预览*
250 |
251 | ## 上下文菜单
252 |
253 | [菜单项](/references/contribution-points#contributesmenus)会展示在视图、操作、右击菜单中。要额外注意菜单组中的菜单项要保持其一致性。如果你的插件操作和文件相关,请将操作配置到文件管理器菜单中(适时)。如果插件是针对特定文件类型的,请仅仅在此类场景中展示操作。
254 |
255 | ✔建议
256 | - 当场景合适时才展示操作
257 | - 相似操作分组
258 | - 一个组中的操作过多时,使用子菜单
259 |
260 | ❌ 不要
261 | - 在所有场景中都展示操作
262 |
263 | 
264 |
265 | *将 **Copy Github Link** 和其他复制命令放在一起。这个操作只会在 GitHub 仓库项目中可用*
266 |
267 | ## 设置
268 |
269 | [设置](/references/contribution-points#contributesconfiguration)是用户配置插件的入口。设置可以是输入框、布尔值、下拉菜单、列表、键值对。如果你的插件要求用户配置特定设置,你可以打开设置 UI 然后用设置 ID 查询你的插件设置。
270 |
271 | ✔建议
272 | - 为每项设置添加默认值
273 | - 为每项设置添加清晰的描述
274 | - 为复杂设置添加文档链接
275 | - 为相关设置添加链接
276 | - 当用户需要配置特定设置项时,提供设置 ID 链接
277 |
278 | ❌ 不要
279 | - 创建你自己的设置页面/webview
280 | - 使用超长的描述
281 |
282 | 
283 |
284 | *使用设置 ID 链接到特定设置项上*
285 |
286 | ## 命令面板
287 |
288 | [命令面板](/references/contribution-points#contributescommands)可以找到所有命令。注意:为你的命令使用清晰的名称和标签,便于用户查找。
289 |
290 | ✔建议
291 | - 必要时添加快捷键
292 | - 为命令添加清晰的名字
293 | - 为同类命令分组
294 |
295 | ❌ 不要
296 | - 重写已有的快捷键
297 | - 命名命令时使用 emoji
298 |
299 | 
300 |
301 | *命令被分组到 "Debug" 类目中, 而且每个命令都有清晰的名字,少部分命令有其快捷键*
--------------------------------------------------------------------------------