写这篇文章是因为最近一段时间的工作涉及到 Cloud Studio 插件这一块的内容,旧的插件系统在面向用户开放后暴露了安全性、扩展性等诸多问题。调研了几个不同架构下 IDE 的插件系统实现( Theia, VSCode 等),也大致阅读了一遍 VSCode 插件系统相关的源码,在这里做一个简单的分享,个人水平有限,如有错误之处还请观众老爷们指点一下。


从加载一个插件开始

以我们熟悉的 vscode-eslint 为例,查看源码会发现入口是 extension.ts 文件里的 activate 函数,它的函数签名像这样:

activate

需要了解的一点是, package.json 里的 activationEvents 字段定义了插件的激活事件,考虑到性能问题,我们并不需要一启动 VSCode 就立即激活所有的插件。activation-events 定义了一组事件,当 activationEvents 字段指定的事件被触发时才会激活相应的插件。包含了特定语言的文件被打开,或者特定的【命令】被触发,以及某些视图被切换甚至是一些自定义命令被触发等等事件。

例如在 vscode-java 中,activationEvents 字段的值为

"activationEvents"

其中包含 languageId 为 java 的文件被打开,以及由该插件自定义的几个 JDT 语言服务命令被触发,和【工作空间】包含 pom.xml/buld.gradle 这些事件。在以上事件被触发时插件将会被激活。

这段逻辑被定义在 src/vs/workbench/api/node/extHostExtensionService.ts 中

// 由 ExtensionHostProcessManager 调用并传入相应事件作为参数

其中 ExtensionsActivator 定义在 src/vs/workbench/api/node/extHostExtensionActivator.ts 中

export 

当调用 activator.activateByEvent 方法时(既某个事件被触发),activator 会获取所有符合该事件的插件并逐一执行 extHostExtensionService._activateExtension 方法(也就是 activator.actualActivateExtension) ,中间省去获取上下文,记录日志等一通操作后调用了 extHostExtensionService._callActivateOptional 静态方法

/* 省略部分代码 */

至此,插件被成功激活。

插件如何运行

再来看插件的代码,插件中需要引入一个叫 vscode 的模块

import 

熟悉 TypeScript 的朋友都知道这实际上只是引入了一个 vscode.d.ts 类型声明文件而已,这个文件包含了所有插件可用的 API 及类型定义。

这些 API 在插件 import 时就被注入到了插件的运行环境中,它们定义在源码 src/vs/workbench/api/node/extHost.api.impl.ts 文件 createApiFactory 函数中,通过 defineAPI 函数统一被注入到插件运行环境。

function 

实际上也很简单,这里的 require 已经被 Microsoft/vscode-loader 劫持了,所以在插件代码中所有通过 import (运行时会被编译为 require) 引入的模块都会经过这里,通过这种方式将 API 注入到了插件执行环境中。

一般我们查看资源管理器或者进程会发现 VSCode 创建了很多个子进程,且所有插件都在一个独立的 Extension Host 进程在运行,这是考虑到插件需要在一个与主线程完全隔离的环境下运行,保证安全性。那么问题来了,我们调用 vscode.window.setStatusBarMessage('Hello World') 时是怎么在编辑器状态栏插入消息的?前文我们提到所有的 API 被定义在 extHost.api.impl.ts 文件的 createApiFactory 里,例如 vscode.window.setStatusBarMessage 的实现

const 

实际调用的是 extHostStatusBar.setStatusBarMessage 函数,而 extHostStatusBar 则是 ExtHostStatusBar 的实例

const 

ExtHostStatusBar 包含了两个方法 createStatusBarEntry 和 setStatusBarMessage,createStatusBarEntry 返回了一个 ExtHostStatusBarEntry ,它被包装了一层代理,在 ExtHostStatusBar 被实例化化的同时也会产生一个 ExtHostStatusBarEntry 实例

export 

所以当我们调用 setStatusBarMessage 时,先是调用了 this._statusMessage.setMessage 方法

// setStatusBarMessage 方法

而 this._statusMessage.setMessage 方法经过层层调用,最终调用了 ExtHostStatusBarEntry 实例的 update 方法,也就是前面的 StatusBarMessage 构造函数中的 this._item.update,而这里就到了重头戏,update 方法中包含了一个 延时为 0 的 setTimeout :

this

这里的 this.proxy 就是 ExtHostStatusBar 构造函数中的 this.proxy

constructor

这里的 IMainContext 其实就是继承了 IRPCProtocol 的一个别名而已,new ExtHostStatusBar 的参数是一个 rpcProtocol 实例,它被定义在 src/vs/workbench/services/extensions/node/rpcProtocol.ts 中,我们重点看一下 getProxy 的实现

// 我错了,这里才是重头戏,VSCode 源码太绕了 /(ㄒoㄒ)/~~

_createProxy 返回的是一个代理对象,即它代理了主线程中真正实现这些 API 的对象,例如 'MainThreadStatusBar' 返回的是一个 MainThreadStatusBarShape 类型的代理。

export 

插件 API 定义中并没有实现这个接口,它只需要被主线程中对应的模块实现即可,前面我们说到 setStatusMessage 最终调用了 this._proxy.$setEntry。

_remoteCall 里会调用 RPCProcotol 的静态方法 serializeRequest 将 rpcId 方法名以及参数序列化成一个 Buffer 并发送给主线程。

const 

关于主线程中接收到消息如何处理其实已经不用多说了,根据 rpcId 找到对应的 Services 以及方法,传入参数即可。

在写这篇文章的同时也在思考如何在浏览器与服务器端实现这样一个插件加载和运行机制,顺便写了一个 Demo extensions-example 相比 VSCode 非常非常简单,只是大致模拟了整个过程而已,实际还有很多需要完善的地方,有兴趣的可以参考一下。

vscode 在标签的src引入别名路径_从零开始 - VSCode 插件运行机制相关推荐

  1. VSCode Run code插件运行机制和配置文件的意思解释

    文章目录 一.Run code运行机制 二.Run code的变量 三.说明 1.编译器路径 2."code-runner.executorMap" 四.c++和python配置的 ...

  2. 关于img标签的src的绝对路径问题

    html页面中图片路径写为如下,则图片不能够显示. <img src="E:\myWeb1\before.png" alt=""> 会报错: &qu ...

  3. qemu 规范路径_在 QEMU 上运行 RISC-V 64 位版本的 Linux

    前言 参考[参考 1]网页版 "Running 64- and 32-bit RISC-V Linux on QEMU"Running 64- and 32-bit RISC-V ...

  4. qemu 规范路径_基于qemu-riscv64模拟器运行debian

    Debian Port已经可以提供risc-v体系结构的软件包集合[https://wiki.debian.org/RISC-V]. 以下记录基于qemu模拟risc-v平台环境并运行debian的过 ...

  5. 取 java 程序运行的路径_获取Java程序运行的路径 | 获取当前jar包的路径

    经过试验,不管是否是Jar包,不管是否是Tomcat部署,以下三个方法均可实现. package test; public class MyPath { public static String ge ...

  6. vscode import 自动引入文件路径

    设置vscode import 自动引入文件路径 在根目录下创建jsconfig.json文件 配置文件,记得每次修改之后都要重新启动vscode,才能生效 {"compilerOption ...

  7. img标签地址src路径拼接_img标签的src属性的用法是什么?

    首先我们来看看img标签的定义: 图片也是网页中最常见的html元素,而且是相当重要的一部分.在html网页中,图像由标签定义,是空html标签或说是单标签,它只包含属性,没有闭合标签. img标签的 ...

  8. 解决vscode红色波浪线的ts报错:找不到模块“store” ts(2307),不识别@别名路径

    使用TS构建vue3项目时,如果使用例如 import { store } from '@/store/user' 发生红色波浪线报错,说明ts不识别@别名,可以修改 tsconfig.json 文件 ...

  9. HTML中img标签的src属性为绝对路径时,在IE中图片可显示,在firefox中不行

    HTML中img标签的src属性为绝对路径时,在IE中图片可显示,在firefox中不行 下图,src中为图片的绝对路径,此时在IE浏览器中图片可显示出来,firefox浏览器中图片不能显示出来 代码 ...

最新文章

  1. 阿里云API网关(8)开发指南-SDK下载
  2. java调用微信扫一扫
  3. 在SD/MMC卡上实现hive (Implement WinCE HIVEROM system on NAND or SD system )
  4. 下列属于计算机人工智能应用领域的是多选题,每天五道选择题(10)
  5. 小熊派开发实践丨漫谈LiteOS之传感器移植
  6. ext.net 2.5 导出excel的使用方法
  7. 转载浅谈MFC内存泄露检测及内存越界访问保护机制
  8. java jpa自身关联_java-如何通过JPA / Hibernate加入获取两个关联
  9. struts2源码系列(3)--拦截器
  10. 【转载】如何统计分析网站的访问量
  11. 【转】ACM各种WA的说明及可能的原因
  12. python怎么取百位_#python计算结果百位500向下取整,(0-499取000,500-999取500)
  13. matlab2c使用c++实现matlab函数系列教程-imag函数
  14. 用Python代码来下载任意指定网易云歌曲
  15. attribute与parameter区别
  16. Oracle数据库建表 Oracle数据库的统一命名与编码规范
  17. python-18-正则表达式
  18. iOS 汉字转换为拼音
  19. 哈希表(解决哈希冲突)
  20. Codeforces Round #549 (Div. 2) D. The Beatles

热门文章

  1. 怎么样设置关闭网页再次登录网页是正常登录状态_学籍系统出现“该账号已登录,不能重复登录”怎么办?...
  2. 电力系统继电保护原理及仿真_电力系统继电保护(529页)
  3. python中的缩进问题_Python中的缩进问题
  4. numpy.argsort详解
  5. 老司机也晕车--java字符串String晕车之旅
  6. lucene-solr源码编译导入eclipse--转
  7. Java内存模型深度解析:总结--转
  8. Writing Images to the Excel Sheet using PHPExcel--转载
  9. Spring MVC 中 HandlerInterceptorAdapter的使用--转载
  10. spring mvc使用的一些注意事项