summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/meta/Lua 5.4/basic.lua106
-rw-r--r--server/meta/Lua 5.4/meta.lua27
-rw-r--r--server/src/core/completion.lua24
-rw-r--r--server/src/core/hover/emmy_function.lua11
-rw-r--r--server/src/core/hover/function.lua116
-rw-r--r--server/src/emmy/funcType.lua9
-rw-r--r--server/src/emmy/manager.lua15
-rw-r--r--server/src/emmy/param.lua16
-rw-r--r--server/src/emmy/return.lua1
-rw-r--r--server/src/method/textDocument/hover.lua5
-rw-r--r--server/src/parser/ast.lua52
-rw-r--r--server/src/parser/grammar.lua31
-rw-r--r--server/src/vm/emmy.lua40
-rw-r--r--server/src/vm/function.lua23
-rw-r--r--server/src/vm/special.lua42
-rw-r--r--server/src/vm/vm.lua5
-rw-r--r--server/test/completion/init.lua21
-rw-r--r--server/test/crossfile/hover.lua51
-rw-r--r--server/test/hover/init.lua46
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)
+]=]