lua-tags 是一个 VSCode LSP 插件项目,为 Lua 语言提供智能感知(IntelliSense)功能。
- 代码自动补全(Auto Completion)
- 跳转到定义(Go To Definition)
- 鼠标悬停提示(Hover)
- 函数签名帮助(Signature Help)
- 文档符号和工作空间符号(Document/Workspace Symbol)
- 代码检查(通过 luacheck 集成)
- 注解支持(@class、@field、@param、@return、@type、@alias)
- 架构: 客户端-服务器(Client-Server)架构
- 协议: 使用 vscode-languageserver 和 vscode-languageclient 实现 LSP
- Lua版本支持: 5.1、5.2、5.3、5.4、LuaJIT
- 核心依赖:
- luaparse: Lua 代码解析器
- fuzzysort: 模糊搜索库
- luacheck: Lua 代码检查工具
- 注解系统: 兼容 emmylua 和 luals 的注解语法
LSP 客户端,负责与 VSCode 编辑器通信。
LSP 服务器,核心功能实现。所有符号解析、补全、跳转等逻辑都在这里。
测试文件目录。采用 @vscode/test-cli 管理,.vscode-test.js 中通过 defineConfig 定义多组测试配置,一次命令自动运行所有测试。
src/test/
├── helper.ts # 公共测试接口(testHover, testCompletion, testGoToDefinition 等)
├── fixture/ # Lua 样本文件(作为 VSCode 工作区打开)
│ ├── core/ # 原 sample,测试核心 IntelliSense 功能
│ └── annotation/ # 原 sample2,测试注解系统
└── suit/ # 测试用例
├── core/ # 核心功能测试(completion, definition, hover, signature 等)
└── annotation/ # 注解功能测试
运行测试:npm test(先编译,再通过 vscode-test 依次运行 core 和 annotation 两组测试)。
如需只跑某一组:npx vscode-test --label core 或 npx vscode-test --label annotation。
工具脚本,目前只有 autoSTL.ts 用于自动生成 Lua 标准库符号。
Lua 标准库符号定义文件(JSON 格式),按版本分文件。
语法高亮定义文件,用于注入式语法高亮。
luacheck 可执行文件,用于代码检查。
- 作用: LSP 客户端入口文件
- 职责:
- 激活插件时启动语言服务器
- 配置服务器选项和客户端选项
- 注册命令(如导出全局符号)
- 处理自定义通知消息
- 作用: LSP 服务器主文件
- 职责:
- 创建 LSP 连接和文档管理器
- 处理各种 LSP 请求(补全、跳转、悬停等)
- 初始化服务器和工作区
- 管理文件变更事件
- 作用: 符号管理核心类(SymbolEx)
- 职责:
- 管理全局符号缓存(globalSymbol、globalModule)
- 管理文档符号缓存(documentSymbol、documentModule)
- 解析变量类型注解
- 格式化类型描述
- 符号模糊匹配
- 作用: Lua 代码解析器(ParseSymbol)
- 职责:
- 使用 luaparse 解析 Lua 代码
- 提取符号信息(函数、变量、表等)
- 处理作用域和局部变量
- 关联注释到符号
- 解析注解并存储到注册表
- 作用: 自动补全功能(AutoCompletion)
- 职责:
- 处理代码自动补全请求
- 搜索局部变量和全局符号
- 处理 require 路径补全
- 生成补全项
- 作用: 跳转到定义功能(GoToDefinition)
- 职责:
- 处理跳转到定义请求
- 支持 require 路径跳转
- 支持注解类型跳转
- 作用: 鼠标悬停提示功能(HoverProvider)
- 职责:
- 处理鼠标悬停请求
- 生成符号的 Markdown 格式提示
- 显示函数签名、常量值、注释等
- 作用: 符号搜索功能(Search)
- 职责:
- 搜索局部变量(通过 AST 遍历)
- 搜索全局模块和文档模块
- 搜索注解类型
- 位置过滤和本地化检测
- 作用: 函数签名帮助功能(SignatureProvider)
- 职责:
- 处理函数签名帮助请求
- 扫描当前输入的函数调用
- 显示函数参数信息
- 作用: 代码检查功能(DiagnosticProvider)
- 职责:
- 集成 luacheck 进行代码检查
- 管理检查队列和延迟检查
- 解析 luacheck 输出并发送诊断信息
- 作用: 配置管理(Setting)
- 职责:
- 管理插件配置项
- 判断文件类型和解析规则
- 管理 luacheck 相关配置
- 作用: 工具类(Utils、DirWalker)
- 职责:
- 日志记录和错误处理
- 目录遍历和文件读取
- 发送诊断信息到 VSCode
- 作用: 符号缓存(CacheSymbol)
- 职责:
- 缓存已解析的文档符号(用于局部变量查找)
- 实现 LRU 缓存策略
- 作用: 注解注册表(AnnotationRegistry)
- 职责:
- 存储所有文档的注解数据
- 提供注解查询接口
- 解析类型表达式和字段
- 管理全局类和别名缓存
- 作用: 注解解析器
- 职责:
- 从注释中解析注解(@class、@field、@param 等)
- 解析类型表达式(数组、泛型、函数类型)
- 关联注解到对应符号
- 作用: 全局符号导出
- 职责:
- 将全局符号导出到文件
- 用于 luacheck 配置生成
- 作用: Lua 标准库符号加载
- 职责:
- 从 JSON 文件加载 Lua 标准库符号
- 解析并组织标准库符号结构
- 作用: 标准库符号自动生成工具
- 职责:
- 从 Lua 官方文档 HTML 中提取符号
- 生成标准库符号 JSON 文件
支持的注解类型:
@class ClassName: 声明一个类@field fieldName type: 定义类字段@param paramName type: 定义函数参数类型@return type: 定义函数返回类型@type type: 指定变量类型@alias AliasName type: 定义类型别名
支持的类型语法:
- 基本类型:
string,number,boolean,function,table - 数组:
Foo[] - 泛型table:
table<K, V> - 函数类型:
func(a:number, b:string):boolean - 自定义类: 通过
@class声明
lua-tags.luaVersion: Lua 版本(5.1/5.2/5.3/5.4/LuaJIT)lua-tags.maxFileSize: 最大文件大小限制lua-tags.excludeDir: 排除的目录lua-tags.luacheck: 启用 luachecklua-tags.checkOnInit: 初始化时检查所有文件lua-tags.checkHow: 检查时机(typing/save)lua-tags.exportPath: 全局符号导出路径lua-tags.rpcPrefix: RPC 前缀正则数组(TypeScript 正则,带 /g),跳转/hover 时 忽略匹配到的前缀(如RPC[addr].),仅提取前缀后的符号;光标落在前缀本身上时 仍正常搜索。实现在setting.ts(parseRegexList/getRpcPrefixes)与server.ts(getQuerySymbol 的 effectiveStart 逻辑)。lua-tags.defaultFileMode: 默认文件加载方式load/module,默认load。module 方式下顶层全局符号挂到按路径推导的模块名下。lua-tags.fileMode: 数组,每项{module:boolean, files:glob},按 glob 覆盖单个 文件的加载方式,首个匹配生效。glob 在setting.tsglobToRegex 中编译。lua-tags.customLoadFunc: 自定义加载函数名数组(如["import","include"]), 配置后与require一视同仁——Setting.isLoadFunc(name)同时匹配 require 与自定义 函数,parseSymbol.tstoSym 的 CallExpression/StringCallExpression 分支用统一的isLoadFunc判断并走toModulePath(去.lua后缀、//\归一为.)。local M = import("a.b.c")会把 M 绑定到 a.b.c 模块;include("a.b.c.lua")同理。 路径补全/跳转见autoCompletion.ts与goToDefinition.ts,二者均用Setting.getLoadFuncs()(require+自定义)构造正则,并用toModulePath归一化路径。
parseSymbol.ts新增defaultModuleName:以 module 方式加载时,parse()开头把parseModuleName初始化为defaultModuleName,使pushParseSymbol把顶层全局 符号挂到该模块名下(设置baseModule,非全局)。- 必要性说明:原有的
module("name")处理只在parseCallStatement里、即代码里 显式写了module(...)调用时才设置parseModuleName。file mode 针对的是没有 显式module()调用、模块名由文件路径推导的文件,原逻辑覆盖不到,所以必须新增defaultModuleName在parse()入口处主动初始化parseModuleName。 - 若文件内同时有显式
module("name"),parseCallStatement会覆盖parseModuleName,以显式名为准。 - module 方式下顶层符号的
baseModule被设置(非全局),但仍可通过 require 引用的 文件符号列表被M.field解析到(不需要额外合成模块符号)。