美文网首页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