-
本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
-
这是源码共读的第44期,链接:传送门。
-
**撰写日期 2023-06-20,源码 open-in-github-button v0.1.1
open-in-github-button
// 核心源码
import { StatusBarAlignment, window } from 'vscode'
export function activate() {
// 添加一个状态栏按钮
const statusBar = window.createStatusBarItem(StatusBarAlignment.Left, 0)
// 按钮添加调用命令
statusBar.command = 'openInGitHub.openProject'
// 按钮 icon
statusBar.text = '$(github)'
statusBar.tooltip = 'Open in GitHub'
statusBar.show()
}
export function deactivate() {
}
从以上极简的源码可以看出,关键源码在 statusBar.command = 'openInGithub.openProject',那么这个命令从哪里来的呢?答案是:open-in-github
open-in-github 源码初探
下面主要分析 open-in-github 的源码,以及如何调试该 vscode 插件源码
打开 open-in-github 的 package.json 文件,可以看到入口是 out/extension.js
注意:这是发布打包后的入口,先执行 package.json - script task 下的 vscode:prepublish (这一步是为后面源码调试做准备,src/out 会生成 extension.js 以及其 sourcemap),实际原始入口为 src/extension.ts,
// src/extension.ts
/* IMPORT */
import Utils from './utils';
/* ACTIVATE */
const activate = Utils.initCommands;
/* EXPORT */
export {activate};
可以看到,细节再 Utils.initCommands 里面
// src/utils.ts
import * as Commands from './commands';
const Utils = {
initCommands ( context: vscode.ExtensionContext ) {
// 根据 vscode 插件 id,获取其 package.json 下 contributes 定义
const {commands} = vscode.extensions.getExtension ( 'fabiospampinato.vscode-open-in-github' ).packageJSON.contributes;
// 注册 vscode 命令,并给命令订阅对应匹配的回调操作
commands.forEach ( ({ command, title }) => {
const commandName = _.last ( command.split ( '.' ) ) as string,
handler = Commands[commandName],
disposable = vscode.commands.registerCommand ( command, () => handler () );
context.subscriptions.push ( disposable );
});
return Commands;
},
// more...
};
export default Utils;
packageJSON.contributes
"contributes": {
"configuration": {/*...*/},
"commands": [
{
"command": "openInGitHub.openProject",
"title": "Open in GitHub: Project"
},
{
"command": "openInGitHub.openFile",
"title": "Open in GitHub: File"
},
// more...
]
},
那么对应命令出发后,匹配什么样回调呢?我们根据上面 src/utils.ts 源码,可以顺藤摸瓜找到
// src/commands.ts
import URL from './url';
/* COMMANDS */
function openProject () {
return URL.open ();
}
function openFile () {
return URL.open ( true, false, 'blob' );
}
// more...
清晰了,openInGitHub.openProject 都应订阅的是 openProject 函数,里面调用了 URL.open()
源码调试,探索本质
梳理了 open-in-github 插件的启动入口以及其中一个命令的脉络,开始源码的调试探索;这里借助另一个 vscode 插件 - Debug Launcher,同样有 open-in-github 的作者 Fabio Spampinato 开发,可零配置地方便调试 vscode 插件。
Debug Launcher 使用方式
打开 package.json 文件,鼠标聚焦到文件任意一处,ctrl+shift+p 打开 vscode 快捷命令,输入 debug 选择 Debug Launcher: Auto,按 enter 回车。以 open-in-github 项目为例:
开始调试
首先,我们在 open-in-github 的源代码这几处打上断点
- src/utils.ts
- src/commands.ts
- src/url.ts
下面我们按照以上 Debug Launcher 的使用方式,调用 Debug auto,然后选择在新打开的 vscode 窗口里,选择一个 github 库的文件夹,ctrl+shift+p 打开 vscode 快捷命令,输入 open in gihutb,点击下面红色框的选项即可定位到相应断点
如前文分析一样,initCommands 方法里注册命令并订阅对应匹配的回调操作
接着,F8/点击 Continue,断点来到 URL.open() 处,这时可以 F11/点击 Step into ,因为我们想看其中细节:
由截图可知,实际会调用一个 URL.get() 的异步方法,异步返回 github 网络地址后,使用 vscode 内置的方法,即可唤起浏览器打开对应的 github 仓库。继续 Step into 看看 URL.get() 是如何得知 github 地址的?
Soga,明显需要我们再钻入 Utils.repo 探秘,继续 Step into... 略过不重要的过程,最终找到了!真相就是 simple-git,这是一个使用 node.js 实现且轻量的 git 接口,可以解析 git 版本管理的系列信息,异常强大。
总结
最后,我们来做个简单整理归纳,open-in-github-button 实际只是给 vscode 注册了一个底部菜单按钮,调用命令是 open-in-gitutb 提供的 openInGitHub.openProject,而 open-in-github 内部的实现依赖于 simple-git 解析 git 版本管理信息而得到 gitRemotes 远程仓库地址,最终由 vscode 内置的 vscode.env.openExternal 唤起浏览器打开 github 仓库地址。通过对 open-in-github-button 和 open-in-github 的源码实现分析,不仅了解到了 vscode 插件的调试方式,还知悉其他很好的第三方库使用(如:simple-git/pify/mkdirp/find-up...),受益匪浅 🎉🎉🎉