最近在使用 Midway 框架写一个小项目, 虽然装饰器在很多时候提供了非常多辅助作用, 但是用多了会发现一件事情, 很多的装饰器参数都是字符串, 这就导致了编辑器完全没有办法做语法补全🤷♀️, 非常的蛋疼.
为了缓解这个问题我打算写一个关于 Midway 框架的 LSP
我这边使用的是
ts-node
调试的时候用是 `node --inspect -r ts-node/register <filename.ts>
目前在找工作, 如果这家公司用的是 midway 我就把这个插件补完, 发布出来并补上其他功能 😄
测试文件
首先为了编写的简单, 先约定在src/config/config.d.ts
里面一个 继承 EggAppConfig
结构的声明
效果
初始化 Typescript 的 LanguageService
这个不多说, 不知道的看 ts 的文档, 那个文件的地址就是我们约定的 types 的存放位置, 可自己计算出来, 当然搜索也是没问题的
搜索继承自EggAppConfig
的 interface
遍历整个文件的节点, 如果找到了 interface 类型的, 那么就看他有没有继承, 如果又继承就看他的继承里面有没有一个叫做 EggAppConfig
搜索到 Config 节点之后, 遍历节点
遍历节点的时候, 针对不同的类型做不同的处理是, 这里写的有点粗糙, 可能有点细节没有考虑到,比如带复杂类型的数组(没处理)
- 如果是 Interface 类型的数据, 就先寻找他的父类, 然后再计算他的成员
- 如果已经是
PropertySignature
那么就记录他的 kind 和名字, 跳转 - 如果是
TypeReference
的类型, 就要去寻找他的真实的定义,和位置 - 如果 kind 的是
TypeLiteralNode
说明是一个数组结构, 需要记录为数组 并且记录他的类型
寻找申明的真实位置
很多时候都会遇到这个蛋疼的问题, ts 的 getDefinitionAtPosition
的这个函数并不准确, 比如搜索EggAppConfig
的 ImportDeclaration, 会返回给你 3 个定义,分别是
- egg
- egg-view
- egg-multipart
实际上你阅读以下源码可以清楚的知道是egg
包里面的定义, 但是这玩意就给你返回了 3 个, webStorm 是 0 个 (MDZZ)
这里只是测试代码, 所以选择三个都返回, 在mapConfig 里面也是 3 个都合并
getDefinitionAtPosition
只会返回一个这样的数据结构
所以我们需要吧 文件名, 位置, 拿去再搜索一次, 由于 ts.forEachChild
只能遍历一层, 增加了辅助函数遍历深层的Node,即 如果还有子节点就递归调用 deepNodeMap 直到没有子节点为止, 对比的时候要注意
- 如果是 Interface 类型的数据, 那么就去读取他的
name.pos
- 如果是普通的直接对比
pos
数据即可
拿到了准确的位置信息和 Node 节点, 然后在 mapConfig 函数里面递归就行了
结束
如果拿到了这份配置表, 梳理一下结构, 非常容易的就可以根据输入的字符预测用户需要完成的配置项, 并且可以提前将 private | <Type>
这种结构帮他写好. 包括其他的 Service
,Controller
,Middleware
都是同理, 搜索对应的就行