美文网首页Node.js
[Node] Language Service Plugin:

[Node] Language Service Plugin:

作者: 何幻 | 来源:发表于2020-06-23 19:17 被阅读0次

    背景

    typescript 源码中内置了一些 refactor,
    位于 typescript/src/services/refactors 这个目录中。

    VSCode 的代码重构功能,正是用到了这些 refactor。
    我们在 VSCode 中打开一个 index.ts 文件,输入以下内容,

    const a = 1;
    

    全选,然后按快捷键 ⌘ + .,就可以将表达式提取到全局函数中,


    类似这样的重构能力是 typescript 内置的,
    幸运的是,用户可以通过编写 Language Service Plugin 进行扩展。

    本文用来记录相关的细节。

    1. 示例

    我编写了一个简单的示例,可以用来说明问题,
    源码位于:github: ts-refactor
    可根据 README 中的介绍,来查看效果。



    2. 原理

    参考 Enabling a plugin
    TypeScript 项目的 tsconfig.json 支持配置 compilerOptions.plugins 字段,
    用于加载用户自定义的 Language Service Plugin

    {
      "compilerOptions": {
        "plugins": [
          {
            "name": "/Users/.../ts-refactor/"
          }
        ]
      }
    }
    

    这个 Language Service Plugin 是具有如下格式的模块,

    const pluginMoudleFactory = ({ typescript }) => {
      const pluginModule = {
        create: ({ config, project, languageService, languageServiceHost, serverHost }) => {
          ... // 这里写业务逻辑
    
          return languageService;
        },
      };
      return pluginModule;
    };
    
    module.exports = pluginMoudleFactory;
    

    上述编写业务逻辑的位置,我们可以调用 typescript.refactor.registerRefactor
    为 typescript 扩展新的 refactor。

    3. 源码

    typescript 相关的源码主要考察两个位置

    3.1 Language Service Plugin

    加载 Language Service Plugin 的位置,
    位于 enablePluginenableProxy 方法中,src/server/project.ts#L1516

    protected enablePlugin(...) {
      ...
      const resolvedModule = firstDefined(searchPaths, searchPath =>
        <PluginModuleFactory | undefined>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log, logError));
      if (resolvedModule) {
          ...
        this.enableProxy(resolvedModule, pluginConfigEntry);
      }
      else {
          ...
      }
    }
    
    private enableProxy(...) {
      try {
        ...
        const pluginModule = pluginModuleFactory({ typescript: ts });
        const newLS = pluginModule.create(info);
        ...
      }
      catch (e) {
        ...
      }
    }
    

    pluginModuleFactory 就是 Language Service Plugin 模块导出的对象。

    3.2 typescript 内置 refactor

    typescript 内置的 refactor 位于,typescript/src/services/refactors 目录,
    这个目录下的 refactor 都具有相同的结构,

    /* @internal */
    namespace ts.refactor.... {
      const refactorName = ...;
      registerRefactor(refactorName, { getAvailableActions, getEditsForAction });
    
      ...
    }
    

    其中,/* @internal */ 所示的类型(或 namespace),并不会导出到最终的 .d.ts 中。
    每一个 refactor 都调用了 registerRefactor 进行注册。

    每个 refactor 的 getAvailableActions⌘ + . 按下时都会触发,
    typescript 会汇总所有的结果,返回给 VSCode 展示有哪些 refactor 可用。

    用户选择使用某个 refactor 关联的某个 action 之后,
    就会触发 getEditsForAction,计算重构结果。

    我们自己编写的 refactor,正是模拟了这个过程,
    Language Service Plugin 中手动调用了 registerRefactor 注册新的 refactor。

    registerRefactor 位于 src/services/refactorProvider.ts#L8

    /* @internal */
    namespace ts.refactor {
      ...
      export function registerRefactor(name: string, refactor: Refactor) {
        ...
      }
      ...
    }
    

    参考

    typescript v3.9.5
    Language Service Plugin
    github: ts-refactor

    相关文章

      网友评论

        本文标题:[Node] Language Service Plugin:

        本文链接:https://www.haomeiwen.com/subject/qyghfktx.html