背景
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 相关的源码主要考察两个位置
- Language Service Plugin 的加载位置
- typescript 内置 refactor 的注册位置
3.1 Language Service Plugin
加载 Language Service Plugin 的位置,
位于 enablePlugin
和 enableProxy
方法中,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
网友评论