Appearance
代码提示
在vscode中写lua代码,安装插件lua,这个插件是采用类似JSDoc的注释方式实现代码提示。
对于require导入的脚本都能得到有效提示,但有时候我们2个脚本之间调用没有产生关系,就无法进行跳转,这个时候可以手动添加@see标签解决。 例如在skynet中服务之间的调用:
lua
--这是gate服务,首先要在gate中添加@class标签,表明gate类型
---@class gate
local CMD = {}
function CMD.kick()
end
--这是调用gate服务的地方,添加@see标签
---@see gate.kick
skynet.call('gate', 'lua', 'kick', uid)
这个插件的注释功能很强大也很好用,不多说,详见文档。
protobuf中实现代码提示
做网络通讯常用protobuf,下面代码是我通过AI和修改实现的,为proto文件生成lua代码注释
lua
--- 解析 proto 文件并生成注解
local function parse_proto(proto_file)
local annotations = {}
local enums = {}
local class_definitions = {}
local current_class = nil
local proto_lines = {}
local proto_to_lua_types = {
int32 = "number", uint32 = "number", sint32 = "number",
int64 = "number", uint64 = "number", sint64 = "number",
fixed32 = "number", sfixed32 = "number", fixed64 = "number", sfixed64 = "number",
float = "number", double = "number",
bool = "boolean",
string = "string",
bytes = "string"
}
-- 读取 proto 文件的所有行
for line in io.lines(proto_file) do
table.insert(proto_lines, line)
end
-- 第一阶段: 处理所有的 enum 定义
for _, line in ipairs(proto_lines) do
if not line:match("^%s*$") and not line:match("^%s*//") then
local enum_name = line:match("^%s*enum%s+(%w+)")
if enum_name then
current_class = enum_name
enums[current_class] = {}
class_definitions[current_class] = "enum"
goto continue
end
local enum_key, enum_value, enum_comment = line:match("^%s*(%w+)%s*=%s*(%d+);%s*///?%s*(.*)")
if not enum_key and not enum_value and not enum_comment then
enum_key, enum_value, enum_comment = line:match("^%s*(%w+)%s*=%s*(%d+);%s*/-%s*(.*)")
end
if enum_key and enum_value then
if enums[current_class] then
enums[current_class][tonumber(enum_value)] = { key = enum_key, comment = enum_comment }
end
goto continue
end
if line:match("^%s*}") and class_definitions[current_class] == "enum" then
current_class = nil
goto continue
end
end
::continue::
end
-- 第二阶段: 处理 message 和字段
current_class = nil
for _, line in ipairs(proto_lines) do
if not line:match("^%s*$") and not line:match("^%s*//") then
local message_name = line:match("^%s*message%s+(%w+)")
if message_name then
current_class = message_name
table.insert(annotations, string.format("---@class %s", current_class))
class_definitions[current_class] = "message"
goto continue
end
if current_class then
local repeated_type, repeated_name, repeated_comment = line:match("^%s*repeated%s+(%w+)%s+(%w+)%s*=%s*%d+;?%s*///?%s*(.*)")
if not repeated_type and not repeated_name and not repeated_comment then
repeated_type, repeated_name, repeated_comment = line:match("^%s*repeated%s+(%w+)%s+(%w+)%s*=%s*%d+;?%s*/-%s*(.*)")
end
if repeated_type and repeated_name then
local lua_type = proto_to_lua_types[repeated_type] or repeated_type
if enums[lua_type] then
lua_type = 'number'
end
table.insert(annotations, string.format("---@field %s? %s[] %s", repeated_name, lua_type, repeated_comment and ("@ " .. repeated_comment) or ""))
goto continue
end
local key_type, value_type, map_name, map_comment = line:match("^%s*map<(%w+),%s*(%w+)>%s+(%w+)%s*=%s*%d+;?%s*///?%s*(.*)")
if not key_type and not value_type and not map_name and not map_comment then
key_type, value_type, map_name, map_comment = line:match("^%s*map<(%w+),%s*(%w+)>%s+(%w+)%s*=%s*%d+;?%s*/-%s*(.*)")
end
if key_type and value_type and map_name then
local lua_key_type = proto_to_lua_types[key_type] or key_type
local lua_value_type = proto_to_lua_types[value_type] or value_type
if enums[lua_key_type] then
lua_key_type = 'number'
end
if enums[lua_value_type] then
lua_value_type = 'number'
end
table.insert(annotations, string.format("---@field %s? table<%s, %s> %s", map_name, lua_key_type, lua_value_type, map_comment and ("@ " .. map_comment) or ""))
goto continue
end
local field_type, field_name, field_comment = line:match("^%s*(%w+)%s+(%w+)%s*=%s*%d+;?%s*///?%s*(.*)")
if not field_type and not field_name and not field_comment then
field_type, field_name, field_comment = line:match("^%s*(%w+)%s+(%w+)%s*=%s*%d+;?%s*/-%s*(.*)")
end
if field_type and field_name then
local lua_type
if enums[field_type] then
local enum_values = {}
local enum_comments = {}
for value, enum_data in pairs(enums[field_type]) do
table.insert(enum_values, tostring(value))
table.insert(enum_comments, tostring(value) .. ':' .. enum_data.comment)
end
lua_type = table.concat(enum_values, "|")
field_comment = field_comment .. ' ' .. table.concat(enum_comments, "|")
else
lua_type = proto_to_lua_types[field_type] or field_type
end
table.insert(annotations, string.format("---@field %s? %s @ %s", field_name, lua_type, field_comment or ""))
goto continue
end
end
if line:match("^%s*}") and class_definitions[current_class] == "message" then
current_class = nil
table.insert(annotations, "")
end
end
::continue::
end
-- 输出注解
local output = {}
for class, type in pairs(class_definitions) do
if type == "enum" then
if enums[class] then
table.insert(output, string.format("---@class %s", class))
for value, enum_data in pairs(enums[class]) do
table.insert(output, string.format("---@field %s %d @ %s", enum_data.key, value, enum_data.comment))
end
table.insert(output, "")
end
end
end
local final_annotations = table.concat(annotations, "\n") .. "\n" .. table.concat(output, "\n")
return final_annotations
end
-- 使用该函数生成注解并打印
local proto_file = "./comm.proto" -- 请确保此文件路径正确
local annotations = parse_proto(proto_file)
file = io.open('./annotation.lua', 'w+')
if not file then return end
file:write(annotations)
file:close()
print('----over')
示例
下面给出部分proto文件对应生成的lua代码注释
lua
--[[
message GameStart{
uint32 index = 2; ///编号
repeated uint32 cards = 3; ///卡牌
GtType type = 4; ///类型
map<uint32, string> desc = 6;
map<string, GtType> mapEnum = 8; ///怎样
repeated GtType reEnum = 10; ///
}
enum GtType {
idle = 1; ///待机
begin = 2; ///开始
over = 3; ///结束
}
]]
--生成的注释如下
---@class GameStart
---@field index? number @ 编号
---@field cards? number[] @ 卡牌
---@field type? 1|2|3 @ 类型 1:待机|2:开始|3:结束
---@field desc? table<number, string> @
---@field mapEnum? table<string, number> @ 怎样
---@field reEnum? number[] @
--使用时进行类型标注就可以有代码提示了
---@type GameStart
local data = {}