diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/meta/Lua 5.4/basic.lua | 106 | ||||
-rw-r--r-- | server/meta/Lua 5.4/meta.lua | 27 | ||||
-rw-r--r-- | server/src/core/completion.lua | 24 | ||||
-rw-r--r-- | server/src/core/hover/emmy_function.lua | 11 | ||||
-rw-r--r-- | server/src/core/hover/function.lua | 116 | ||||
-rw-r--r-- | server/src/emmy/funcType.lua | 9 | ||||
-rw-r--r-- | server/src/emmy/manager.lua | 15 | ||||
-rw-r--r-- | server/src/emmy/param.lua | 16 | ||||
-rw-r--r-- | server/src/emmy/return.lua | 1 | ||||
-rw-r--r-- | server/src/method/textDocument/hover.lua | 5 | ||||
-rw-r--r-- | server/src/parser/ast.lua | 52 | ||||
-rw-r--r-- | server/src/parser/grammar.lua | 31 | ||||
-rw-r--r-- | server/src/vm/emmy.lua | 40 | ||||
-rw-r--r-- | server/src/vm/function.lua | 23 | ||||
-rw-r--r-- | server/src/vm/special.lua | 42 | ||||
-rw-r--r-- | server/src/vm/vm.lua | 5 | ||||
-rw-r--r-- | server/test/completion/init.lua | 21 | ||||
-rw-r--r-- | server/test/crossfile/hover.lua | 51 | ||||
-rw-r--r-- | server/test/hover/init.lua | 46 |
19 files changed, 572 insertions, 69 deletions
diff --git a/server/meta/Lua 5.4/basic.lua b/server/meta/Lua 5.4/basic.lua new file mode 100644 index 00000000..7fa412de --- /dev/null +++ b/server/meta/Lua 5.4/basic.lua @@ -0,0 +1,106 @@ +--- 独立版Lua的启动参数。 +arg = {} + +--- 如果其参数 `v` 的值为假,它就调用 `error`。 +---@overload fun(v:any):any +---@param v any +---@param message any {optional = 'self'} +---@return any +function assert(v, message) end + +---@overload fun() +---@overload fun(opt:GCOption):any +---@param opt GCOption {optional = 'after'} +---@param arg integer {optional = 'self'} +---@return any +function collectgarbage(opt, arg) end + +--- 打开该名字的文件,并执行文件中的 Lua 代码块。 +---@overload fun():any +---@param filename string {optional = 'self', special = 'dofile:1'} +---@return any +function dofile(filename) end + +--- 中止上一次保护函数调用,将错误对象 `message` 返回。 +---@overload fun(message:any) +---@param message any +---@param level integer {optional = 'self'} +function error(message, level) end + +--- 内部储存有全局环境。 +_G = {} + +--- 返回该对象的元表。 +---@param object any +---@return table {name = 'metatable'} +function getmetatable(object) end + +--- 能迭代表 `t` 中序列的键值对。 +---|```lua +---|for i, v in ipairs(t) do +---| -- body +---|end +---|``` +---@param t table +---@return {name = 'iterator'} +---@return table {name = 't'} +---@return integer {name = 'i'} +function ipairs(t) + --- 返回该键的下一个键及其关联的值。 + ---@overload fun(t:table):integer,any + ---@param t table + ---@param index any {optional = 'self'} + ---@return integer {name = 'index'} + ---@return any {name = 'value'} + local function iterator(t, index) end + + return iterator, t, nil +end + +--- 加载一个代码块。 +---@overload fun():function,string +---@overload fun(chunk:string|function):function,string +---@overload fun(chunk:string|function, chunkname:string):function,string +---@overload fun(chunk:string|function, chunkname:string, mode:loadOption):function,string +---@param chunk string|function +---@param chunkname string {optional = 'after'} +---@param mode loadOption {optional = 'after'} +---@param env table {optional = 'self'} +---@return {name = 'init'} +---@return string {name = 'errMessage', optional = 'self'} +function load(chunk, chunkname, mode, env) + return function (...) end +end + +--- 返回该键的下一个键及其关联的值。 +---@overload fun(t:table):any, any +---@param t table +---@param index any {optional = 'self'} +---@return any {name = 'key'} +---@return any {name = 'value'} +function next(t, index) end + +--- 能迭代表 `t` 中的所有键值对。 +---|```lua +---|for k, v in pairs(t) do +---| -- body +---|end +---|``` +---@param t table +---@return {name = 'next'} +---@return table {name = 't'} +---@return any {name = 'key'} +function pairs(t) + --- 返回该键的下一个键及其关联的值。 + ---@overload fun(t:table):any, any + ---@param t table + ---@param index any {optional = 'self'} + ---@return any {name = 'key'} + ---@return any {name = 'value'} + local function next(t, index) end + + return next, t, nil +end + +--- 当前解释器版本号。 +_VERSION = 'Lua 5.4' diff --git a/server/meta/Lua 5.4/meta.lua b/server/meta/Lua 5.4/meta.lua new file mode 100644 index 00000000..4fe28af2 --- /dev/null +++ b/server/meta/Lua 5.4/meta.lua @@ -0,0 +1,27 @@ +---@class any +---@class string any +---@class number any +---@class integer number +---@class boolean any +---@class table any +---@class function any +---@class nil any +---@class userdata any +---@class thread any + +---@alias GCOption string +---| > '"collect"' # 做一次完整的垃圾收集循环。 +---| '"stop"' # 停止垃圾收集器的运行。 +---| '"restart"' # 重启垃圾收集器的自动运行。 +---| '"count"' # 以 K 字节数为单位返回 Lua 使用的总内存数。 +---| '"step"' # 单步运行垃圾收集器。 +---| '"setpause"' # 设置收集器的 `间歇率`。 +---| '"setstepmul"' # 设置收集器的 `步进倍率`。 +---| '"incremental"' # 改变收集器模式为增量模式。 +---| '"generational"' # 改变收集器模式为分代模式。 +---| '"isrunning"' # 返回表示收集器是否在工作的布尔值。 + +---@alias loadOption string +---| "'b'" # 只能是二进制代码块。 +---| "'t'" # 只能是文本代码块。 +---| > "'bt'" # 可以是二进制也可以是文本。 diff --git a/server/src/core/completion.lua b/server/src/core/completion.lua index a0a7fb82..6ab3c565 100644 --- a/server/src/core/completion.lua +++ b/server/src/core/completion.lua @@ -43,7 +43,7 @@ for _, k in ipairs(KEYS) do KEYMAP[k] = true end -local EMMY_KEYWORD = {'class', 'type', 'alias', 'param', 'return', 'field', 'generic', 'vararg', 'language', 'see'} +local EMMY_KEYWORD = {'class', 'type', 'alias', 'param', 'return', 'field', 'generic', 'vararg', 'language', 'see', 'overload'} local function getDucumentation(name, value) if value:getType() == 'function' then @@ -663,6 +663,17 @@ local function searchEnumAsLib(vm, source, word, callback, pos, args, lib) end end +local function buildEmmyEnumComment(enum, data) + if not enum.comment then + return data + end + if not data then + data = {} + end + data.documentation = tostring(enum.comment) + return data +end + local function searchEnumAsEmmyParams(vm, source, word, callback, pos, args, func) local select = #args + 1 for i, arg in ipairs(args) do @@ -678,17 +689,18 @@ local function searchEnumAsEmmyParams(vm, source, word, callback, pos, args, fun end param:eachEnum(function (enum) - if matchKey(word, enum) then - local strSource = parser:ast(tostring(enum), 'String') + local str = enum[1] + if matchKey(word, str) then + local strSource = parser:ast(tostring(str), 'String') if strSource then if source.type == 'string' then local data = buildTextEdit(source.start, source.finish, strSource[1], source[2]) - callback(enum, nil, CompletionItemKind.EnumMember, data) + callback(str, nil, CompletionItemKind.EnumMember, buildEmmyEnumComment(enum, data)) else - callback(enum, nil, CompletionItemKind.EnumMember) + callback(str, nil, CompletionItemKind.EnumMember, buildEmmyEnumComment(enum)) end else - callback(enum, nil, CompletionItemKind.EnumMember) + callback(str, nil, CompletionItemKind.EnumMember, buildEmmyEnumComment(enum)) end end end) diff --git a/server/src/core/hover/emmy_function.lua b/server/src/core/hover/emmy_function.lua index 313b5276..4dd89107 100644 --- a/server/src/core/hover/emmy_function.lua +++ b/server/src/core/hover/emmy_function.lua @@ -41,11 +41,14 @@ local function buildEmmyArgs(emmy, object, select) end local function buildEmmyReturns(emmy) - local rtn = emmy:getReturn() - if rtn then - return '\n -> ' .. rtn:getType() - else + local rtns = {} + emmy:eachReturn(function (rtn) + rtns[#rtns+1] = rtn:getType() + end) + if #rtns == 0 then return '\n -> ' .. 'any' + else + return '\n -> ' .. table.concat(rtns, ', ') end end diff --git a/server/src/core/hover/function.lua b/server/src/core/hover/function.lua index 5358cfaa..5adeeb7e 100644 --- a/server/src/core/hover/function.lua +++ b/server/src/core/hover/function.lua @@ -1,9 +1,12 @@ +local emmyFunction = require 'core.hover.emmy_function' + local function buildValueArgs(func, object, select) if not func then return '', nil end local names = {} local values = {} + local options = {} if func.argValues then for i, value in ipairs(func.argValues) do values[i] = value:getType() @@ -15,6 +18,7 @@ local function buildValueArgs(func, object, select) local param = func:findEmmyParamByName(arg:getName()) if param then values[i] = param:getType() + options[i] = param:getOption() end end end @@ -30,11 +34,20 @@ local function buildValueArgs(func, object, select) max = math.max(#names, #values) end for i = start, max do + local name = names[i] + local value = values[i] or 'any' + local option = options[i] + if option and option.optional then + if i > start then + strs[#strs+1] = ' [' + else + strs[#strs+1] = '[' + end + end if i > start then strs[#strs+1] = ', ' end - local name = names[i] - local value = values[i] or 'any' + if i == select then strs[#strs+1] = '@ARG' end @@ -46,6 +59,10 @@ local function buildValueArgs(func, object, select) if i == select then strs[#strs+1] = '@ARG' end + + if option and option.optional == 'self' then + strs[#strs+1] = ']' + end end if func:hasDots() then if max > 0 then @@ -53,6 +70,15 @@ local function buildValueArgs(func, object, select) end strs[#strs+1] = '...' end + + if options then + for _, option in pairs(options) do + if option.optional == 'after' then + strs[#strs+1] = ']' + end + end + end + local text = table.concat(strs) local argLabel = {} for i = 1, 2 do @@ -80,15 +106,78 @@ local function buildValueReturns(func) return '' end local strs = {} + local emmys = {} + local n = 0 + func:eachEmmyReturn(function (emmy) + n = n + 1 + emmys[n] = emmy + end) if func.returns then for i, rtn in ipairs(func.returns) do - strs[i] = rtn:getType() + local emmy = emmys[i] + local option = emmy and emmy.option + if option and option.optional then + if i > 1 then + strs[#strs+1] = ' [' + else + strs[#strs+1] = '[' + end + end + if i > 1 then + strs[#strs+1] = ', ' + end + if option and option.name then + strs[#strs+1] = ('%s: '):format(option.name) + end + strs[#strs+1] = rtn:getType() + if option and option.optional == 'self' then + strs[#strs+1] = ']' + end + end + for i = 1, #func.returns do + local emmy = emmys[i] + if emmy and emmy.option and emmy.option.optional == 'after' then + strs[#strs+1] = ']' + end end end if #strs == 0 then strs[1] = 'any' end - return '\n -> ' .. table.concat(strs, ', ') + return '\n -> ' .. table.concat(strs) +end + +---@param func emmyFunction +local function buildEnum(func) + if not func then + return nil + end + local params = func:getEmmyParams() + if not params then + return nil + end + local strs = {} + for _, param in ipairs(params) do + local first = true + param:eachEnum(function (enum) + if first then + first = false + strs[#strs+1] = ('\n%s: %s'):format(param:getName(), param:getType()) + end + if enum.default then + strs[#strs+1] = ('\n |>%s'):format(enum[1]) + else + strs[#strs+1] = ('\n | %s'):format(enum[1]) + end + if enum.comment then + strs[#strs+1] = ' -- ' .. enum.comment + end + end) + end + if #strs == 0 then + return nil + end + return table.concat(strs) end local function getComment(func) @@ -98,10 +187,25 @@ local function getComment(func) return func:getComment() end +local function getOverLoads(name, func, object, select) + local overloads = func and func:getEmmyOverLoads() + if not overloads then + return nil + end + local list = {} + for _, ol in ipairs(overloads) do + local hover = emmyFunction(name, ol, object, select) + list[#list+1] = hover.label + end + return table.concat(list, '\n') +end + return function (name, func, object, select) local args, argLabel = buildValueArgs(func, object, select) local returns = buildValueReturns(func) + local enum = buildEnum(func) local comment = getComment(func) + local overloads = getOverLoads(name, func, object, select) local headLen = #('function %s('):format(name) local title = ('function %s(%s)%s'):format(name, args, returns) if argLabel then @@ -110,7 +214,9 @@ return function (name, func, object, select) end return { label = title, - argLabel = argLabel, description = comment, + enum = enum, + argLabel = argLabel, + overloads = overloads, } end diff --git a/server/src/emmy/funcType.lua b/server/src/emmy/funcType.lua index f467f763..2c073fe2 100644 --- a/server/src/emmy/funcType.lua +++ b/server/src/emmy/funcType.lua @@ -30,7 +30,7 @@ function mt:addParam(name, type) end function mt:addReturn(type) - self._return = type + self._returns[#self._returns+1] = type end function mt:eachParam(callback) @@ -39,8 +39,10 @@ function mt:eachParam(callback) end end -function mt:getReturn() - return self._return +function mt:eachReturn(callback) + for _, rtn in ipairs(self._returns) do + callback(rtn) + end end function mt:bindFunction(func) @@ -55,6 +57,7 @@ return function (manager, source) local self = setmetatable({ source = source.id, _params = {}, + _returns = {}, _manager = manager, }, mt) return self diff --git a/server/src/emmy/manager.lua b/server/src/emmy/manager.lua index 11aa899c..658fb56b 100644 --- a/server/src/emmy/manager.lua +++ b/server/src/emmy/manager.lua @@ -136,7 +136,7 @@ function mt:addAlias(source, typeObj) local list = self:getClass(aliasName) list[source.id] = aliasObj for i = 3, #source do - aliasObj:addEnum(source[i][1]) + aliasObj:addEnum(source[i]) end return aliasObj end @@ -156,17 +156,20 @@ function mt:addParam(source, bind) end) end for i = 3, #source do - paramObj:addEnum(source[i][1]) + paramObj:addEnum(source[i]) end + paramObj:setOption(source.option) return paramObj end function mt:addReturn(source, bind) local returnObj = newReturn(self, source) - if bind.type == 'emmy.generic' then - returnObj:bindGeneric(bind) - else - returnObj:bindType(bind) + if bind then + if bind.type == 'emmy.generic' then + returnObj:bindGeneric(bind) + else + returnObj:bindType(bind) + end end return returnObj end diff --git a/server/src/emmy/param.lua b/server/src/emmy/param.lua index 290ab6e4..12ac0633 100644 --- a/server/src/emmy/param.lua +++ b/server/src/emmy/param.lua @@ -37,16 +37,24 @@ function mt:bindGeneric(generic) end end -function mt:addEnum(str) - self._enum[#self._enum+1] = str +function mt:addEnum(enum) + self._enum[#self._enum+1] = enum end function mt:eachEnum(callback) - for _, str in ipairs(self._enum) do - callback(str) + for _, enum in ipairs(self._enum) do + callback(enum) end end +function mt:setOption(option) + self._option = option +end + +function mt:getOption() + return self._option +end + return function (manager, source) local self = setmetatable({ source = source.id, diff --git a/server/src/emmy/return.lua b/server/src/emmy/return.lua index a347267c..a23f3ac9 100644 --- a/server/src/emmy/return.lua +++ b/server/src/emmy/return.lua @@ -28,6 +28,7 @@ end return function (manager, source) local self = setmetatable({ source = source.id, + option = source.option, _manager = manager, }, mt) return self diff --git a/server/src/method/textDocument/hover.lua b/server/src/method/textDocument/hover.lua index 4e4b754d..f127c8a8 100644 --- a/server/src/method/textDocument/hover.lua +++ b/server/src/method/textDocument/hover.lua @@ -23,12 +23,15 @@ return function (lsp, params) ```lua %s ``` +```lua +%s +``` %s ```lua %s ``` %s -]]):format(hover.label or '', hover.description or '', hover.enum or '', hover.doc or '') +]]):format(hover.label or '', hover.overloads or '', hover.description or '', hover.enum or '', hover.doc or '') local response = { contents = { diff --git a/server/src/parser/ast.lua b/server/src/parser/ast.lua index 9903dca2..f2486fd6 100644 --- a/server/src/parser/ast.lua +++ b/server/src/parser/ast.lua @@ -1213,16 +1213,22 @@ local Defs = { [2] = valueType, } end, - EmmyFunctionType = function (start, ...) + EmmyFunctionType = function (start, args, returns, finish) local result = { start = start, + finish = finish - 1, type = 'emmyFunctionType', - ... + args = args, + returns = returns, } - result.finish = result[#result] - 1 - result[#result] = nil return result end, + EmmyFunctionRtns = function (...) + return {...} + end, + EmmyFunctionArgs = function (...) + return {...} + end, EmmyAlias = function (name, emmyName, ...) return { type = 'emmyAlias', @@ -1233,9 +1239,10 @@ local Defs = { ... } end, - EmmyParam = function (argName, emmyName, ...) + EmmyParam = function (argName, emmyName, option, ...) local emmy = { type = 'emmyParam', + option = option, argName, emmyName, ... @@ -1244,13 +1251,14 @@ local Defs = { emmy.finish = emmy[#emmy].finish return emmy end, - EmmyReturn = function (...) + EmmyReturn = function (start, type, finish, option) local emmy = { type = 'emmyReturn', - ... + option = option, + start = start, + finish = finish - 1, + [1] = type, } - emmy.start = emmy[1].start - emmy.finish = emmy[#emmy].finish return emmy end, EmmyField = function (access, fieldName, ...) @@ -1315,9 +1323,33 @@ local Defs = { EmmyComment = function (...) return { type = 'emmyComment', - [1] = table.concat({...}, ' '), + [1] = table.concat({...}), } end, + EmmyOption = function (options) + if not options or options == '' then + return nil + end + local option = {} + for _, pair in ipairs(options) do + if pair.type == 'pair' then + local key = pair[1] + local value = pair[2] + if key.type == 'name' then + option[key[1]] = value[1] + end + end + end + return option + end, + EmmyTypeEnum = function (default, enum, comment) + enum.type = 'emmyEnum' + if default ~= '' then + enum.default = true + end + enum.comment = comment + return enum + end, -- 捕获错误 UnknownSymbol = function (start, symbol) diff --git a/server/src/parser/grammar.lua b/server/src/parser/grammar.lua index 50f76d0b..c20f5b32 100644 --- a/server/src/parser/grammar.lua +++ b/server/src/parser/grammar.lua @@ -517,9 +517,11 @@ grammar 'Emmy' [[ Emmy <- EmmySp '---@' EmmyBody ShortComment / EmmyComments EmmySp <- (!'---@' !'---' Comment / %s / %nl)* -EmmyComments <- (EmmyComment (%nl EmmyComment)*) +EmmyComments <- (EmmyComment (%nl EmmyComMulti / %nl EmmyComSingle)*) -> EmmyComment -EmmyComment <- EmmySp '---' !'@' %s* {(!%nl .)*} +EmmyComment <- EmmySp '---' !'@' %s* {(!%nl .)*} +EmmyComMulti <- EmmySp '---|' {} -> en {(!%nl .)*} +EmmyComSingle <- EmmySp '---' !'@' %s* {} -> ' ' {(!%nl .)*} EmmyBody <- 'class' %s+ EmmyClass -> EmmyClass / 'type' %s+ EmmyType -> EmmyType / 'alias' %s+ EmmyAlias -> EmmyAlias @@ -556,13 +558,19 @@ EmmyTypeName <- EmmyFunctionType / EmmyTableType / EmmyArrayType / MustEmmyName -EmmyTypeEnums <- %s* '|' %s* String +EmmyTypeEnum <- %s* (%nl %s* '---')? '|' EmmyEnum + -> EmmyTypeEnum +EmmyEnum <- %s* {'>'?} %s* String (EmmyEnumComment / (!%nl !'|' .)*) +EmmyEnumComment <- %s* '#' %s* {(!%nl .)*} -EmmyAlias <- MustEmmyName %s* EmmyType EmmyTypeEnums* +EmmyAlias <- MustEmmyName %s* EmmyType EmmyTypeEnum* -EmmyParam <- MustEmmyName %s* EmmyType EmmyTypeEnums* +EmmyParam <- MustEmmyName %s* EmmyType %s* EmmyOption %s* EmmyTypeEnum* +EmmyOption <- Table? + -> EmmyOption -EmmyReturn <- EmmyType +EmmyReturn <- {} %nil {} Table -> EmmyOption + / {} EmmyType {} EmmyOption EmmyField <- (EmmyFieldAccess MustEmmyName %s* EmmyType) EmmyFieldAccess <- ({'public'} Cut %s*) @@ -585,10 +593,15 @@ EmmyArrayType <- (MustEmmyName '[]') EmmyTableType <- ({} 'table' Cut '<' %s* EmmyType %s* ',' %s* EmmyType %s* '>' {}) -> EmmyTableType -EmmyFunctionType<- ({} 'fun' Cut %s* EmmyFunctionArgs? %s* EmmyFunctionRtn? {}) +EmmyFunctionType<- ({} 'fun' Cut %s* EmmyFunctionArgs %s* EmmyFunctionRtns {}) -> EmmyFunctionType -EmmyFunctionArgs<- '(' %s* EmmyFunctionArg %s* (',' %s* EmmyFunctionArg %s*)* ')' -EmmyFunctionRtn <- ':' %s* EmmyType +EmmyFunctionArgs<- ('(' %s* EmmyFunctionArg %s* (',' %s* EmmyFunctionArg %s*)* ')') + -> EmmyFunctionArgs + / '(' %nil ')'? + / %nil +EmmyFunctionRtns<- (':' %s* EmmyType (%s* ',' %s* EmmyType)*) + -> EmmyFunctionRtns + / %nil EmmyFunctionArg <- MustEmmyName %s* ':' %s* EmmyType EmmySee <- {} MustEmmyName %s* '#' %s* MustEmmyName {} diff --git a/server/src/vm/emmy.lua b/server/src/vm/emmy.lua index 3ad0a472..5a15d3b0 100644 --- a/server/src/vm/emmy.lua +++ b/server/src/vm/emmy.lua @@ -196,7 +196,7 @@ function mt:doEmmyReturn(action) ---@type emmyMgr local emmyMgr = self.emmyMgr self:instantSource(action) - local type = self:getGenericByType(action[1]) or self:buildEmmyAnyType(action[1]) + local type = action[1] and (self:getGenericByType(action[1]) or self:buildEmmyAnyType(action[1])) local rtn = emmyMgr:addReturn(action, type) action:set('emmy.return', rtn) self:addEmmyReturn(rtn) @@ -299,23 +299,29 @@ function mt:buildEmmyFunctionType(source) local funcObj = emmyMgr:addFunctionType(source) ---@type emmyFunction local func = functionMgr.create(source) - for i = 1, #source // 2 do - local nameSource = source[i*2-1] - local typeSource = source[i*2] - local paramType = self:buildEmmyAnyType(typeSource) - funcObj:addParam(nameSource[1], paramType) - local value = self:createValue(paramType:getType(), typeSource) - value:setEmmy(paramType) - self:instantSource(nameSource) - func:addArg(nameSource[1], nameSource, value) + local args = source.args + if args then + for i = 1, #args // 2 do + local nameSource = args[i*2-1] + local typeSource = args[i*2] + local paramType = self:buildEmmyAnyType(typeSource) + funcObj:addParam(nameSource[1], paramType) + local value = self:createValue(paramType:getType(), typeSource) + value:setEmmy(paramType) + self:instantSource(nameSource) + func:addArg(nameSource[1], nameSource, value) + end end - local returnSource = source[#source] - if returnSource then - local returnType = self:buildEmmyAnyType(returnSource) - funcObj:addReturn(returnType) - local value = self:createValue(returnType:getType(), returnSource) - value:setEmmy(returnType) - func:setReturn(1, value) + local returns = source.returns + if returns then + for i = 1, #returns do + local returnSource = returns[i] + local returnType = self:buildEmmyAnyType(returnSource) + funcObj:addReturn(returnType) + local value = self:createValue(returnType:getType(), returnSource) + value:setEmmy(returnType) + func:setReturn(i, value) + end end funcObj:bindFunction(func) return funcObj diff --git a/server/src/vm/function.lua b/server/src/vm/function.lua index 8f7ac6c2..b6772cd1 100644 --- a/server/src/vm/function.lua +++ b/server/src/vm/function.lua @@ -162,8 +162,11 @@ function mt:rawSetReturn(index, value) end function mt:setReturn(index, value) - if self._emmyReturns then - return + local emmy = self._emmyReturns and self._emmyReturns[index] + if emmy then + if emmy:bindType() or emmy:bindGeneric() then + return + end end return self:rawSetReturn(index, value) end @@ -172,8 +175,11 @@ function mt:mergeReturn(index, value) if self._removed then return end - if self._emmyReturns then - return + local emmy = self._emmyReturns and self._emmyReturns[index] + if emmy then + if emmy:bindType() or emmy:bindGeneric() then + return + end end self:set('hasReturn', true) if not self.returns then @@ -339,6 +345,15 @@ function mt:run(vm) end end +function mt:eachEmmyReturn(callback) + if not self._emmyReturns then + return + end + for _, rtn in ipairs(self._emmyReturns) do + callback(rtn) + end +end + function mt:setArgs(values) for i = 1, #self.argValues do self.argValues[i] = nil diff --git a/server/src/vm/special.lua b/server/src/vm/special.lua new file mode 100644 index 00000000..b22ed06d --- /dev/null +++ b/server/src/vm/special.lua @@ -0,0 +1,42 @@ +local mt = require 'vm.manager' + +---@param func emmyFunction +---@param values table +function mt:callEmmySpecial(func, values) + local emmyParams = func:getEmmyParams() + for index, param in ipairs(emmyParams) do + local option = param:getOption() + if option and type(option.special) == 'string' then + self:checkEmmyParam(func, values, index, option.special) + end + end +end + +---@param func emmyFunction +---@param values table +---@param index integer +---@param special string +function mt:checkEmmyParam(func, values, index, special) + if special == 'dofile:1' then + self:callEmmyDoFile(func, values, index) + end +end + +---@param func emmyFunction +---@param values table +---@param index integer +function mt:callEmmyDoFile(func, values, index) + if not values[index] then + values[index] = self:createValue('any', self:getDefaultSource()) + end + local str = values[index]:getLiteral() + if type(str) ~= 'string' then + return + end + local requireValue = self:tryRequireOne(values[index], 'dofile') + if not requireValue then + requireValue = self:createValue('any', self:getDefaultSource()) + requireValue.isRequire = true + end + func:setReturn(1, requireValue) +end diff --git a/server/src/vm/vm.lua b/server/src/vm/vm.lua index f0e30537..bafd5841 100644 --- a/server/src/vm/vm.lua +++ b/server/src/vm/vm.lua @@ -16,6 +16,7 @@ require 'vm.raw' require 'vm.pcall' require 'vm.ipairs' require 'vm.emmy' +require 'vm.special' -- TODO source测试 --rawset(_G, 'CachedSource', setmetatable({}, { __mode = 'kv' })) @@ -325,6 +326,7 @@ end function mt:call(value, values, source) local lib = value:getLib() + ---@type emmyFunction local func = value:getFunction() value:setType('function', 0.5) if not func then @@ -343,6 +345,9 @@ function mt:call(value, values, source) else func:mergeReturn(1, self:createValue('any', source)) end + if func:getEmmyParams() then + self:callEmmySpecial(func, values) + end end return func:getReturn() diff --git a/server/test/completion/init.lua b/server/test/completion/init.lua index 5f43b8d2..bc17901e 100644 --- a/server/test/completion/init.lua +++ b/server/test/completion/init.lua @@ -1338,3 +1338,24 @@ zz$ } } } + +TEST [[ +---@param x string +---| "'选项1'" # 注释1 +---| "'选项2'" # 注释2 +function f(x) end + +f($) +]] +{ + { + label = "'选项1'", + kind = CompletionItemKind.EnumMember, + documentation = '注释1', + }, + { + label = "'选项2'", + kind = CompletionItemKind.EnumMember, + documentation = '注释2', + }, +} diff --git a/server/test/crossfile/hover.lua b/server/test/crossfile/hover.lua index b0b8ab61..19e8912f 100644 --- a/server/test/crossfile/hover.lua +++ b/server/test/crossfile/hover.lua @@ -259,3 +259,54 @@ TEST { description = 'abc', } } + +TEST { + { + path = 'a.lua', + content = '', + }, + { + path = 'b.lua', + content = [[ + ---@param x string + ---| "'选项1'" # 注释1 + ---| > "'选项2'" # 注释2 + function <?f?>(x) end + ]] + }, + hover = { + label = 'function f(x: string)', + name = 'f', + enum = [[ + +x: string + | '选项1' -- 注释1 + |>'选项2' -- 注释2]] + } +} + +TEST { + { + path = 'a.lua', + content = '', + }, + { + path = 'b.lua', + content = [[ + ---@alias option + ---| "'选项1'" # 注释1 + ---| > "'选项2'" # 注释2 + ---@param x option + function <?f?>(x) end + ]] + }, + hover = { + label = 'function f(x: option)', + name = 'f', + enum = [[ + +x: option + | '选项1' -- 注释1 + |>'选项2' -- 注释2]] + } +} diff --git a/server/test/hover/init.lua b/server/test/hover/init.lua index f1a18c06..e77d4ea3 100644 --- a/server/test/hover/init.lua +++ b/server/test/hover/init.lua @@ -812,3 +812,49 @@ local <toclose> <const> <?x?> = 1 [[ local <toclose> <const> x: number = 1 ]] + +TEST [[ +---@param x number {optional = 'after'} +---@param y boolean {optional = 'self'} +---@param z string +function <?f?>(x, y, z) end +]] +[=[ +function f([x: number [, y: boolean], z: string]) +]=] + +TEST [[ +---@return string {name = 'key'} +---@return string {name = 'value'} +function <?f?>() end +]] +[=[ +function f() + -> key: string, value: string +]=] + +TEST [[ +---@return {name = 'x', optional = 'after'} +---@return string {name = 'y', optional = 'self'} +---@return string {name = 'z'} +function <?f?>() end +]] +[=[ +function f() + -> [x: any [, y: string], z: string] +]=] + +TEST [[ +---@return {name = 'x', optional = 'after'} +---@return string {name = 'y', optional = 'self'} +---@return string {name = 'z'} +function f() + return function (a, b) + end +end + +<?f2?> = f() +]] +[=[ +function f2(a: any, b: any) +]=] |