- vscode 源码本地运行和打包
- vscode 源码分析
- 技术架构
- vscode魔改
- 扩展ActivityBar
- 1. 准备工作
- 2. 创建新的插件项目
- 3. 定义Activity Bar图标和侧边栏视图
- 4. 显示“hello”字符串
- 1. 安装“Custom CSS and JS Loader”插件
- 2. 创建自定义CSS文件
- 3. 配置VS Code使用自定义CSS
- 4. 重新加载自定义样式
- 1. 定义树节点
- 2. 创建树数据
- 3. 实现TreeDataProvider
- 4. 注册TreeView
- 1. 类定义和属性
- 2. 事件发射器和事件
- 3. getTreeItem 方法
- 4. getChildren 方法
- 1.
vscode.window.registerTreeDataProvider
: - 2.
vscode.window.createTreeView
: - 总结:
- 1. 在
package.json
中定义你的视图 - 2. 在
extension.ts
中使用registerTreeDataProvider
- 1. 在
package.json
中定义命令和按钮 - 2. 在
extension.ts
中实现命令 - 3. 添加图标(可选)
- 1. 在
TreeDataProviderExample
类中添加refresh
方法 - 2. 在树的数据真正更改时调用
refresh
方法
- 扩展StatusBar
- 欢迎页
vscode 源码本地运行和打包
先决条件
下载源码
// 只现在最近一次 commit 的代码
git clone --depth 1 https://github.com/microsoft/vscode.git
遇到的问题
1、Nodejs 开启 ‘Tools for Native Modules’ 后,安装报错
Error 0x8013153b: [0e54:001a][2023-08-28T14:32:58] Could not update channel "<https://aka.ms/vs/16/release/channel>"
解决方案:
- https://segmentfault.com/q/1010000042607907
- https://stackoverflow.com/questions/67171333/error-when-installing-necessary-packages-on-node-js
2、安装 tree-sitter 失败
error <https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz:> connect ETIMEDOUT 192.30.255.113:443
解决办法:registry 切换到 npm,关闭代理,清空 chche 缓存,重新安装
3、使用 yarn gulp vscode-win32-x64
可以打包,没有失败,但是在 vscode 项目中只发现了out-vscode
文件夹。
解决办法:其实已经生成了VSCode-win32-x64
文件夹,只不过不在 vscode 文件夹里面,而是跟 vscode 同级。。。
Ref:https://github.com/microsoft/vscode/issues/49424
参考文档
vscode 源码分析
- https://www.barretlee.com/blog/2019/11/01/vscode-study-02-debugging/
- vscode 二次开发界面篇:https://blog.csdn.net/zhouhangzooo/article/details/90180720
- https://xie.infoq.cn/article/be35607adb680eec093546904
- https://zhaomenghuan.js.org/note/vscode/awesome-vscode.html
项目结构
Visual Studio Code 的源代码组织结构由多个层次和模块组成,其核心位于 src/vs
目录下。这个核心可以通过扩展来进行扩展。扩展在一个称为扩展主机的单独进程中运行。扩展是通过使用扩展 API 来实现的。内置的扩展可以在 extensions
文件夹中找到。
核心层次
- base:提供通用的实用程序和用户界面构建块,可以在任何其他层中使用。
- platform:定义服务注入支持和 VS Code 的基础服务,这些服务在诸如工作台和代码的各个层之间共享。
- editor:”Monaco” 代码编辑器层。作为一个单独的可下载组件提供。
- workbench:托管“Monaco”编辑器、notebooks 和自定义编辑器,并为资源管理器、状态栏或菜单栏等面板提供框架,利用 Electron 实现 VS Code 桌面应用程序和浏览器 API 来提供用于 Web 的 VS 代码。
- code:桌面应用的入口点,将所有内容整合在一起。例如包括 Electron 主文件、共享进程和 CLI
- server:我们的远程开发服务器应用的入口点。
目标环境:
- common:只需要基本的 JavaScript API 的源代码。
- browser:需要 Web API 的源代码,例如 DOM。
- 可以使用
common
代码
- 可以使用
- node:需要 Node.JS API 的源代码。
- 可以使用
common
代码
- 可以使用
- electron-sandbox:需要浏览器 API 和一部分与 Electron 主进程通信的 API 的源代码。
- 可以使用
common
、browser
、electron-sandbox
代码
- 可以使用
- electron-main:需要 Electron 主进程 API 的源代码。
- 可以使用
common
、node
代码
- 可以使用
├── build # gulp编译构建脚本
├── extensions # 内置插件
├── gulpfile.js # gulp task
├── out # 编译输出目录
├── resources # 平台相关静态资源,图标等
├── scripts # 工具脚本,开发/测试
├── src # 源码目录
├── test # 测试套件
└── product.json # App meta信息
src 下文件目录结构,如下图:
├── bootstrap-amd.js # 子进程实际入口
├── bootstrap-fork.js #
├── bootstrap-window.js #
├── bootstrap.js # 子进程环境初始化
├── buildfile.js # 构建config
├── cli.js # CLI入口
├── main.js # 主进程入口
├── paths.js # AppDataPath与DefaultUserDataPath
├── typings # 这个目录包含了VS Code项目中使用的所有TypeScript类型定义文件
└── vs # 分层且模块化的 core
├── base # 提供可在任何其他层中使用的基础的工具方法和基础的 DOM UI 控件
│ ├── browser # 基础UI组件,DOM操作、交互事件、DnD等
│ ├── common # diff描述,markdown解析器,worker协议,各种工具函数
│ ├── node # Node工具函数
│ ├── parts # IPC协议(Electron、Node),quickopen、tree组件
│ ├── test # base单测用例
│ └── worker # Worker factory 和 main Worker(运行IDE Core:Monaco)
├── code # VSCode 主运行窗口:桌面应用程序的入口点,将所有内容缝合在一起,例如包括 Electron 主文件、共享进程和 CLI
│ ├── browser # 需要 Electron 渲染器处理API的源代码(可以使用 common, browser, node)
│ ├── electron-main # 需要Electron主进程API的源代码(可以使用 common, node)
│ ├── electron-sandbox
│ ├── node # 需要Electron主进程API的源代码(可以使用 common, node)
│ ├── test
│ └── code.main.ts
├── editor # Monaco Editor Web代码编辑器:作为单独的可下载组件提供。
│ ├── browser # 代码编辑器核心
│ ├── common # 代码编辑器核心
│ ├── contrib # vscode 与独立 IDE共享的代码
│ ├── standalone # 独立 IDE 独有的代码
│ ├── test
│ ├── editor.all.ts
│ ├── editor.api.ts
│ ├── editor.main.ts
│ └── editor.worker.ts
├── platform # 定义服务注入支持和跨层(如workbench 和 code)共享的 VS Code 基本服务
├── workbench # 托管“Monaco”编辑器、notebooks 和自定义编辑器,并为资源管理器、状态栏或菜单栏等面板提供框架,利用 Electron 实现 VS Code 桌面应用程序和浏览器 API 来提供用于 Web 的 VS 代码。
│ ├── api # 工作区与服务端调用的接口
│ ├── browser # 工作区布局 UI 界面
│ ├── common # 工作区 UI 通用部件
│ ├── contrib #
│ ├── electron-sandbox # 桌面端沙盒渲染进程
│ ├── services # 工作区依赖和使用的对象及服务接口定义
│ └── test # 工作区相关的测试用例
├── server/ # 用于远程开发的服务器应用程序的入口点。
├── buildunit.json
├── css.build.js # 用于插件构建的CSS loader
├── css.js # CSS loader
├── loader.js # AMD loader(用于异步加载AMD模块,类似于require.js)
├── nls.build.js # 用于插件构建的 NLS loader
└── nls.js # NLS(National Language Support)多语言loader
依赖注入
依赖注入是一种设计模式,它允许一个对象从外部接收其依赖项,而不是自己创建它们。这种方法有助于实现反转控制原则,其中对象不再负责查找其依赖项或管理它们的生命周期。
在 Visual Studio Code 中,代码是围绕服务组织的,其中大多数服务在platform
层中定义。这些服务通过构造函数注入的方式传递给它们的客户端。
消费服务:
- 服务定义分为两部分:(1) 服务的接口,和 (2) 服务标识符。后者是必需的,因为 TypeScript 使用结构化而不是名义化的类型系统。服务标识符是一个装饰器,并应该与服务接口同名。
- 通过在构造函数参数上添加相应的装饰来声明服务依赖。例如,
@IModelService
是服务标识符装饰,而IModelService
是此参数的类型注解。 - 使用实例化服务来为服务消费者创建实例,例如
instantiationService.createInstance(Client)
。通常,当您作为贡献(如视图或语言)注册时,这会为您完成。
提供服务:
- 向其他人或您自己的组件提供服务的最佳方式是使用
registerSingleton
函数。它接受一个服务标识符和一个服务构造函数。 - 在模块范围内添加此调用,以便在启动时执行。然后,工作台将知道此服务并能够将其传递给消费者。
VS Code 编辑器源代码组织:
vs/editor
文件夹不应有任何 node 或 electron-* 依赖。vs/editor/common
和vs/editor/browser
- 代码编辑器核心。vs/editor/contrib
- 代码编辑器贡献。vs/editor/standalone
- 仅与独立编辑器一起提供的代码。vs/workbench/contrib/codeEditor
- 在 VS Code 中提供的代码编辑器贡献。
VS Code 工作台源代码组织
在 Visual Studio Code 的 “Workbench source organisation” 中,工作台(vs/workbench
)是为了提供丰富的开发体验而组成的,例如全文搜索、集成的 git 和调试。
以下是文件夹的组织方式:
-
**vs/workbench/{common browser electron-sandbox}**:这是工作台的核心,尽可能地保持最小化。 - vs/workbench/api:这是
vscode.d.ts
API 的提供者,包括扩展主机和工作台实现。 - vs/workbench/services:工作台核心服务。这里不应该包括只在
vs/workbench/contrib
中使用的服务。 - vs/workbench/contrib:工作台贡献。这是大部分代码应该存在的地方。
vs/workbench/contrib
文件夹有一些规则:- 不能有任何从
vs/workbench/contrib
外部到vs/workbench/contrib
内部的依赖。 - 每个贡献都应该有一个单独的
.contribution.ts
文件,例如vs/workbench/contrib/search/browser/search.contribution.ts
,这应该被添加到产品的主要入口点。- 如果你添加了一个新的服务,它只从一个贡献中使用,而不是从其他组件或工作台核心中使用,建议从贡献入口文件中注册该服务。
- 每个贡献都应该从一个单独的文件中公开其内部 API,例如
vs/workbench/contrib/search/common/search.ts
。 - 贡献可以依赖于另一个贡献的内部 API。
- 贡献不应该深入到另一个贡献的内部(内部是贡献内部不在单一公共 API 文件中的任何内容)。
- 在让一个贡献依赖于另一个贡献之前要三思:这真的是必要的吗?这有意义吗?是否可以通过使用工作台的可扩展性故事来避免这种依赖?
- 不能有任何从
这种组织方式确保了工作台的核心不直接依赖于所有这些贡献。相反,它使用了一个内部机制(与真正的扩展 API 相对)来为工作台贡献这些内容。
VS Code for Desktop / VS Code for Web
我们既向桌面提供 Electron,也向 Web 提供,目标是在两种环境中尽可能多地共享代码。
- 两种环境:
- VS Code for Desktop:这是大多数人熟悉的版本,它是一个桌面应用程序,使用 Electron 构建,可以在 Windows、macOS 和 Linux 上运行。
- VS Code for Web:这是一个运行在浏览器中的版本,允许用户在没有安装任何东西的情况下访问和使用 VS Code。
- 代码共享:VS Code 的目标是在这两种环境中尽可能多地共享代码。只在一个环境中运行的代码应该是例外,而不是常规。理想情况下,相同的代码可以在这两种环境中运行。
- 入口文件:为了区分在产品构建中的环境,有几个入口文件定义了所有的依赖,这些依赖取决于环境:
- src/vs/workbench/workbench.sandbox.main.ts:仅用于桌面的依赖。
- src/vs/workbench/workbench.web.main.ts:仅用于Web的依赖。
- src/vs/workbench/workbench.common.main.ts:共享的依赖,无论是桌面还是Web。
- 一些规则:
- 共享的代码应该放入
workbench.common.main.ts
。 - 仅用于Web的代码应该放入
workbench.web.main.ts
。 - 仅用于桌面的代码应该放入
workbench.desktop.main.ts
。
当为桌面引入一个服务而不为Web引入时,需要特别小心:在Web中运行需要该服务的代码将无法执行,除非你为Web提供了一个相关的服务。如果根据环境使用不同的策略,可以为一个服务提供两种不同的实现。
- 共享的代码应该放入
- 注意事项:只有从主入口文件引用的代码才会加载到产品中。如果你有一个文件在你的代码中没有被引用,确保将导入添加到你的
.contribution.ts
文件中。
技术架构
首先,Electron 应用的运行原理与浏览器类似,它有一个主进程和多个渲染进程。
- 主进程:负责运行 Electron 应用的主要代码,它可以创建和控制浏览器窗口、处理系统事件等。(可以访问 Node 及 Native API;)
- 渲染进程:每个 Electron 的浏览器窗口都在其自己的渲染进程中运行。这些进程负责运行窗口的 Web 页面。(可以访问 Browser API 和 Node API 及一部分 Native API。)
主进程和渲染进程之间可以通过 IPC(Inter-Process Communication,进程间通信)进行通信。这意味着你可以在渲染进程中执行某些操作(例如点击按钮),然后通知主进程来执行其他任务(例如打开一个新窗口)
VSC 采用多进程架构,VSC 启动后主要有下面的几个进程:
- 主进程
- 渲染进程
- 插件进程
- Debug进程
- Search进程
主进程
相当于后台服务,后台进程是 VSC 的入口,主要负责多窗体管理(创建/切换)、编辑器生命周期管理,进程间通信(IPC Server),自动更新,工具条菜单栏注册等。
我们启动 VSC 的时候,后台进程会首先启动,读取各种配置信息和历史记录,然后将这些信息和主窗口 UI 的 HTML 主文件路径整合成一个 URL,启动一个浏览器窗口来显示编辑器的 UI。后台进程会一直关注 UI 进程的状态,当所有 UI 进程被关闭的时候,整个编辑器退出。
此外后台进程还会开启一个本地的 Socket,当有新的 VSC 进程启动的时候,会尝试连接这个 Socket,并将启动的参数信息传递给它,由已经存在的 VSC 来执行相关的动作,这样能够保证 VSC 的唯一性,避免出现多开文件夹带来的问题。
渲染进程
编辑器窗口进程负责整个 UI 的展示,也就是我们所见的部分。UI 全部用 HTML 编写没有太多需要介绍的部分。
项目文件的读取和保存由主进程的 NodeJS API 完成,因为全部是异步操作,即便有比较大的文件,也不会对 UI 造成阻塞。IO 跟 UI 在一个进程,并采用异步操作,在保证 IO 性能的基础上也保证了 UI 的响应速度。
插件进程
每一个 UI 窗口会启动一个 NodeJS 子进程作为插件的宿主进程。所有的插件会共同运行在这个进程中。
Debug 进程
Debugger 插件跟普通的插件有一点区别,它不运行在插件进程中,而是在每次 debug 的时候由 UI 单独新开一个进程。
搜索进程
搜索是一个十分耗时的任务,VSC 也使用的单独的进程来实现这个功能,保证主窗口的效率。将耗时的任务分到多个进程中,有效的保证了主进程的响应速度。
vscode魔改
扩展ActivityBar
ActivityBar显示一个icon,点击后,在侧边栏显示树形视图,并且有刷新按钮。
Person:
- 你现在是一名经验丰富的vscode extension工程师。我现在想要编写一个插件,在ActivityBar显示一个icon,当点击icon时,侧边栏显示“hello”字符串。
请从创建工程开始,教我一步步如何操作。
ChatGPT:
- 当然可以!以下是创建一个VS Code插件的步骤,该插件会在Activity Bar上显示一个图标,点击后在侧边栏显示“hello”字符串:
1. 准备工作
确保你已经安装了以下工具:
- Node.js
- VS Code
- VS Code Extension Generator 。你可以使用npm来安装它:
npm install -g yo generator-code
2. 创建新的插件项目
- 打开终端或命令提示符。
- 输入以下命令来创建一个新的插件项目:
yo code
- 选择
New Extension (TypeScript)
。 - 输入你的插件名称,例如:
HelloSidebar
。 - 选择一个位置来保存你的插件项目。
- 打开该项目文件夹在VS Code中。
3. 定义Activity Bar图标和侧边栏视图
- 打开
package.json
文件。 - 在
contributes
部分,添加以下代码来定义一个Activity Bar图标和一个侧边栏视图:
"viewsContainers": {
"activitybar": [
{
"id": "helloSidebar",
"title": "Hello Sidebar",
"icon": "media/icon.svg"
}
]
},
"views": {
"helloSidebar": [ // 叫做helloSidebar的视图容器
{
"id": "helloView", // 这个视图的唯一标识。其他部分的代码可能会使用这个ID来引用这个视图。
"name": "Hello View" // 该视图的显示名称。在VS Code中,当用户看到这个视图时,他们会看到“主界面”作为视图的名称。
"icon": "./resource/images/CEC-IDE.svg": // 这是该视图的图标路径。该图标会在VS Code的边栏或其他地方展示给用户。
"type": "webview": 这表示这个视图的类型是webview。Webview在VS Code中是一个特殊的视图,它允许开发者在视图内部加载一个完整的Web页面或应用。当你在views配置中省略type字段时,它默认会被视为一个文件树视图(tree view)
"contextualTitle": "主界面": contextualTitle是VS Code视图定义中的一个可选字段,用于提供备用标题,该标题仅在特定上下文中使用,而不是通常的侧边栏或面板视图。
}
]
}
- 在
media
文件夹中,添加一个名为icon.svg
的SVG图标。这将是Activity Bar上的图标。
4. 显示“hello”字符串
- 打开
src/extension.ts
文件。 - 在
activate
函数中,添加以下代码来创建一个新的TreeView并显示“hello”字符串:
const helloDataProvider = {
getTreeItem: (element: any) => element,
getChildren: () => {
return [new vscode.TreeItem('hello')];
}
};
vscode.window.createTreeView('helloView', { treeDataProvider: helloDataProvider });
Person:
- 我看有人通过下面这种方式增加图标文字的,那么我应该在哪里修改:
.activitybar .composite-bar li.action-item[title^="资源管理器"]::after,
.activitybar .composite-bar li.action-item[title^="Explorer"]::after {
content: "代码";
}
ChatGPT:
- 这段代码是使用CSS伪元素来在Activity Bar的图标后添加文字的方法。这种方法是通过修改VS Code的自定义样式来实现的。但是,VS Code默认不支持自定义样式和JS,所以你需要使用一个名为“Custom CSS and JS Loader”的插件来加载自定义样式。
以下是步骤:
1. 安装“Custom CSS and JS Loader”插件
在VS Code中打开扩展视图并搜索“Custom CSS and JS Loader”,然后安装它。
2. 创建自定义CSS文件
- 在你的插件项目或其他位置创建一个新的CSS文件,例如
custom-style.css
。 - 将上面提供的CSS代码复制到这个文件中。
- 修改
content
属性的值为你想要的文字,例如”HELLO”:
content: "HELLO";
3. 配置VS Code使用自定义CSS
- 打开VS Code的设置(可以使用快捷键
Ctrl + ,
)。 - 在设置中搜索“Custom CSS and JS”。
- 在“Custom CSS Path”设置中,点击“编辑”并输入你的CSS文件的路径,例如:
file:///path_to_your_file/custom-style.css
。 - 保存设置。
4. 重新加载自定义样式
为了让自定义样式生效,你需要使用命令面板:
- 打开命令面板(
Ctrl + Shift + P
)。 - 输入“Reload Custom CSS and JS”并选择这个命令。
- VS Code可能会提示你重新启动编辑器,确认并重新启动。
现在,你应该可以在Activity Bar的图标上看到你添加的文字了。
注意 :使用“Custom CSS and JS Loader”插件和自定义样式可能会影响VS Code的稳定性和性能,而且在VS Code更新后可能需要重新应用自定义样式。
Person:
- 什么是TreeView?
ChatGPT:
TreeView
是 Visual Studio Code (VS Code) 扩展API中的一个组件,它允许开发者在VS Code的侧边栏(Side Bar)中创建自定义的树形视图。这种树形视图可以用来展示分层的数据、文件结构、项目结构或其他任何需要树状表示的信息。
TreeView
在 VS Code 中的常见应用包括:
- 资源管理器 :默认的文件和文件夹视图就是一个
TreeView
。 - 源代码管理 :例如 Git 插件中展示的改动文件列表。
- 其他扩展 :例如 Docker 插件中展示的容器和镜像列表,或者数据库插件中展示的表和查询结果。
要在VS Code扩展中实现自定义的 TreeView
,你需要:
- 定义数据 :创建一个实现了
TreeDataProvider
接口的对象。这个对象负责提供树的数据和响应数据变化。 - 注册视图 :使用
vscode.window.createTreeView
方法来创建并注册你的TreeView
。
一旦你的 TreeView
被创建和注册,VS Code 就会自动处理大部分的用户交互,例如展开和折叠树的节点。但你也可以为特定的节点或事件添加自定义的交互逻辑。
Person:
- 将“hello”字符串改成一个tree示例
ChatGPT:
- 当然可以。下面是如何将简单的“hello”字符串转换为一个树形结构示例的步骤:
1. 定义树节点
首先,我们需要定义一个表示树节点的类。这个类将包含节点的标签和子节点。
class TreeNode {
constructor(
public readonly label: string,
public readonly children?: TreeNode[]
) {}
}
2. 创建树数据
接下来,我们创建一个简单的树数据示例:
const rootNode = new TreeNode('Root', [
new TreeNode('Child 1'),
new TreeNode('Child 2', [
new TreeNode('Grandchild 1'),
new TreeNode('Grandchild 2')
])
]);
这将创建一个树,其中有一个根节点,根节点有两个子节点,其中一个子节点还有两个子节点。
3. 实现TreeDataProvider
现在,我们需要实现TreeDataProvider
接口来为TreeView
提供数据:
class TreeDataProviderExample implements vscode.TreeDataProvider<TreeNode> {
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
getTreeItem(element: TreeNode): vscode.TreeItem {
const treeItem = new vscode.TreeItem(element.label);
treeItem.collapsibleState = element.children && element.children.length > 0 ?
vscode.TreeItemCollapsibleState.Collapsed :
vscode.TreeItemCollapsibleState.None;
return treeItem;
}
getChildren(element?: TreeNode): TreeNode[] {
if (element) {
return element.children || [];
} else {
return [rootNode];
}
}
}
4. 注册TreeView
最后,在activate
函数中,我们需要创建和注册TreeView
:
export function activate(context: vscode.ExtensionContext) {
const treeDataProvider = new TreeDataProviderExample();
vscode.window.createTreeView('helloView', { treeDataProvider });
}
现在,当你运行你的插件并点击Activity Bar上的图标时,你应该会看到一个树形结构,其中包含一个根节点、两个子节点以及一个子节点的两个子节点。
Person:
- 详细解释一下TreeDataProviderExample类
ChatGPT:
- 当然可以。
TreeDataProviderExample
类是一个实现了vscode.TreeDataProvider
接口的类,用于为TreeView
提供数据。这个类是创建自定义树形视图的核心部分。
让我们逐步深入了解 TreeDataProviderExample
类的每个部分:
1. 类定义和属性
class TreeDataProviderExample implements vscode.TreeDataProvider<TreeNode> {
这里,我们定义了一个名为 TreeDataProviderExample
的类,它实现了 vscode.TreeDataProvider
接口。这个接口期望一个泛型参数,我们使用了我们自定义的 TreeNode
类型。
2. 事件发射器和事件
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
_onDidChangeTreeData
是一个私有的事件发射器,用于在树数据发生变化时通知 VS Code。onDidChangeTreeData
是一个公开的只读事件,它是TreeDataProvider
接口的一部分。当你想要通知 VS Code 树的数据已经更改时,你可以使用_onDidChangeTreeData
事件发射器来触发这个事件。
3. getTreeItem 方法
getTreeItem(element: TreeNode): vscode.TreeItem {
const treeItem = new vscode.TreeItem(element.label);
treeItem.collapsibleState = element.children && element.children.length > 0 ?
vscode.TreeItemCollapsibleState.Collapsed :
vscode.TreeItemCollapsibleState.None;
return treeItem;
}
getTreeItem
方法是用于将我们的自定义TreeNode
对象转换为 VS Code 可以理解的TreeItem
对象。- 这个方法接收一个
TreeNode
元素作为参数,并返回一个TreeItem
对象。 - 我们设置
TreeItem
的collapsibleState
属性,以确定该项是否可以折叠和展开。如果该节点有子节点,我们将其设置为Collapsed
,否则设置为None
。
4. getChildren 方法
getChildren(element?: TreeNode): TreeNode[] {
if (element) {
return element.children || [];
} else {
return [rootNode];
}
}
getChildren
方法用于获取给定TreeNode
元素的子节点。- 如果提供了一个
TreeNode
元素,该方法将返回其子节点;如果没有提供元素(即请求根节点),它将返回我们之前定义的rootNode
。 - 这个方法是递归的,因为当用户点击一个可折叠的节点时,VS Code 会再次调用这个方法,传入被点击的节点,以获取其子节点。
总的来说,TreeDataProviderExample
类为 VS Code 提供了一个自定义的树形数据结构,使得我们可以在 TreeView
中展示我们自己的数据。
Person:
- 是否可以通过registerTreeDataProvider 替代createTreeView?有啥区别?
ChatGPT:
- 是的,你可以使用
vscode.window.registerTreeDataProvider
方法来注册一个TreeDataProvider
,而不是使用vscode.window.createTreeView
。这两种方法都允许你在 VS Code 中创建和显示一个树形视图,但它们的用途和功能有所不同。
1. vscode.window.registerTreeDataProvider
:
这是一个更简单的方法,用于将你的 TreeDataProvider
与一个在 package.json
文件中预定义的视图关联起来。
优点 :
- 更简单,只需一行代码。
- 不需要手动管理
TreeView
实例。
缺点 :
- 提供的控制较少。你不能动态地更新或刷新整个视图,除非通过触发
onDidChangeTreeData
事件。
2. vscode.window.createTreeView
:
这个方法不仅注册了你的 TreeDataProvider
,还返回了一个 TreeView
对象,该对象提供了更多的控制和管理视图的方法。
优点 :
- 提供了更多的控制。例如,你可以使用
TreeView.reveal
方法来滚动到并高亮显示特定的节点。 - 允许你动态地更新或刷新整个视图。
缺点 :
- 使用起来稍微复杂一些,因为你需要管理返回的
TreeView
实例。
总结:
- 如果你只是想简单地显示一个树形视图,并且不需要额外的控制或功能,那么
registerTreeDataProvider
可能是更好的选择。 - 如果你需要更多的控制,例如动态地滚动到特定的节点或刷新整个视图,那么
createTreeView
会更有用。
在大多数情况下,registerTreeDataProvider
足以满足需求。但如果你的扩展需要更复杂的交互或功能,createTreeView
可能是更好的选择。
Person:
- 如果将上面使用createTreeView 的树形视图改成使用registerTreeDataProvider 应该怎么改?
ChatGPT:
- 如果你想使用
registerTreeDataProvider
替代createTreeView
,你需要进行以下更改:
1. 在 package.json
中定义你的视图
首先,确保你在 package.json
文件的 contributes
部分中已经定义了你的视图。这是 registerTreeDataProvider
方法所需的。
例如:
"contributes": {
"views": {
"helloSidebar": [
{
"id": "helloView",
"name": "Hello View"
}
]
}
}
这里,helloSidebar
是你的 Activity Bar 的 ID,而 helloView
是你的树形视图的 ID。
2. 在 extension.ts
中使用 registerTreeDataProvider
替换 createTreeView
的调用为 registerTreeDataProvider
的调用:
export function activate(context: vscode.ExtensionContext) {
const treeDataProvider = new TreeDataProviderExample();
vscode.window.registerTreeDataProvider('helloView', treeDataProvider);
}
注意:你不再需要保存 createTreeView
返回的 TreeView
实例,因为 registerTreeDataProvider
不返回任何内容。
Person:
- 我想要在侧边栏树形视图的右上角增加一个刷新按钮,应该怎么做?
ChatGPT:
- 要在侧边栏的树形视图的右上角添加一个刷新按钮,你需要在你的扩展的
package.json
文件中使用view/title
贡献点来定义一个命令。以下是具体步骤:
1. 在 package.json
中定义命令和按钮
在 package.json
的 contributes
部分,你需要定义一个命令和一个与该命令关联的按钮:
"contributes": {
"commands": [
{
"command": "extension.refreshTreeView",
"title": "Refresh TreeView"
}
],
"views": {
"helloSidebar": [
{
"id": "helloView",
"name": "Hello View"
}
]
},
"menus": {
"view/title": [
{
"command": "extension.refreshTreeView",
"when": "view == helloView",
"group": "navigation"
}
]
}
}
这里,我们定义了一个命令 extension.refreshTreeView
,并将其添加到 helloView
视图的标题栏中。
2. 在 extension.ts
中实现命令
接下来,你需要在 extension.ts
文件中实现这个命令,例如:
export function activate(context: vscode.ExtensionContext) {
const treeDataProvider = new TreeDataProviderExample();
vscode.window.registerTreeDataProvider('helloView', treeDataProvider);
context.subscriptions.push(vscode.commands.registerCommand('extension.refreshTreeView', () => {
// 这里实现刷新逻辑
// 例如,你可以触发 TreeDataProvider 的 onDidChangeTreeData 事件来刷新整个树
treeDataProvider.refresh();
}));
}
在这个示例中,当用户点击刷新按钮时,我们调用 treeDataProvider
的 refresh
方法。你可能需要在 TreeDataProviderExample
类中实现这个 refresh
方法,它通常会触发 onDidChangeTreeData
事件来通知 VS Code 刷新树的数据。
3. 添加图标(可选)
如果你想为你的刷新按钮添加一个图标,你可以在 package.json
的 contributes.commands
部分为你的命令添加 icon
属性,并提供一个图标的路径。
"commands": [
{
"command": "extension.refreshTreeView",
"title": "Refresh TreeView",
"icon": {
"light": "path_to_light_theme_icon.svg",
"dark": "path_to_dark_theme_icon.svg"
}
}
]
这样,当用户点击侧边栏的树形视图的右上角的刷新按钮时,你的扩展就会执行相应的刷新逻辑。
Person:
- refresh 方法如何写?
ChatGPT:
refresh
方法的主要目的是通知 VS Code 树的数据已经更改,从而触发树的刷新。为此,我们通常使用onDidChangeTreeData
事件发射器来发送一个事件。
以下是如何在 TreeDataProviderExample
类中实现 refresh
方法的步骤:
1. 在 TreeDataProviderExample
类中添加 refresh
方法
首先,确保你的类中有一个 onDidChangeTreeData
事件发射器。我们将使用这个发射器来触发树的刷新。
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
接下来,添加 refresh
方法:
refresh(): void {
this._onDidChangeTreeData.fire();
}
这个方法简单地触发了 onDidChangeTreeData
事件,通知 VS Code 树的数据可能已经更改,从而刷新整个树。
2. 在树的数据真正更改时调用 refresh
方法
当你的数据源(例如一个数组、数据库或外部API)发生更改时,你可以调用这个 refresh
方法来更新树的显示。
例如,如果你有一个方法来添加一个新的树节点,你可以在添加节点后调用 refresh
方法:
addNode(newNode: TreeNode): void {
// 假设你有一个表示树数据的数组
this.data.push(newNode);
this.refresh();
}
这样,每当你添加一个新的节点时,树就会自动刷新以显示新的数据。
总之,refresh
方法提供了一种简单的方式来通知 VS Code 树的数据已经更改,从而触发树的刷新。你可以根据你的数据源和应用逻辑来决定何时调用这个方法。
扩展StatusBar
扩展StatusBar,增加一个debug按钮,点击后执行F5指令
要在 VS Code 的 StatusBar 中添加一个 “debug” 按钮并使其执行 F5
指令(即启动调试),你可以按照以下步骤操作:
1. 在 package.json
中定义命令和按钮
首先,在你的扩展的 package.json
文件中,定义一个新的命令和一个状态栏按钮:
"contributes": {
"commands": [
{
"command": "extension.startDebugging",
"title": "Start Debugging"
}
],
"menus": {
"statusBar": [
{
"command": "extension.startDebugging",
"when": "debuggersAvailable"
}
]
}
}
这里,我们定义了一个命令 extension.startDebugging
并将其添加到状态栏。when
条件确保只有当有可用的调试器时,按钮才会显示。
2. 在 extension.ts
中实现命令
接下来,在 extension.ts
文件中,你需要实现这个命令:
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('extension.startDebugging', () => {
vscode.commands.executeCommand('workbench.action.debug.start');
}));
}
这里,当 extension.startDebugging
命令被触发时,我们简单地执行内置的 workbench.action.debug.start
命令,这与按 F5
键的效果相同。
3. 设置按钮的外观(可选)
如果你想为你的状态栏按钮设置一个特定的文本、颜色或图标,你可以使用 VS Code 的 StatusBarItem
API。例如:
export function activate(context: vscode.ExtensionContext) {
const statusBarBtn = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
statusBarBtn.text = "$(debug-start) Debug";
statusBarBtn.command = 'extension.startDebugging';
statusBarBtn.show();
context.subscriptions.push(statusBarBtn);
context.subscriptions.push(vscode.commands.registerCommand('extension.startDebugging', () => {
vscode.commands.executeCommand('workbench.action.debug.start');
}));
}
在上述代码中,我们创建了一个新的 StatusBarItem
,设置了其文本、图标和命令,并使其显示在状态栏中。$(debug-start)
是一个内置的图标,表示一个调试图标。
4. 测试你的扩展
保存你的更改并运行你的扩展(通常是按 F5
键)。你应该会在 VS Code 的状态栏中看到一个新的 “Debug” 按钮。点击这个按钮应该会启动调试会话,就像按 F5
键一样。
欢迎页
修改路径:E:\whty-code\vscode\src\vs\workbench\contrib\welcomeGettingStarted\browser\gettingStarted.ts