diff options
-rw-r--r-- | server/src/core/hover/emmy_function.lua | 123 | ||||
-rw-r--r-- | server/src/core/hover/hover.lua | 11 | ||||
-rw-r--r-- | server/src/emmy/funcType.lua | 49 | ||||
-rw-r--r-- | server/src/emmy/manager.lua | 6 | ||||
-rw-r--r-- | server/src/vm/emmy.lua | 46 | ||||
-rw-r--r-- | server/src/vm/local.lua | 17 | ||||
-rw-r--r-- | server/src/vm/value.lua | 5 | ||||
-rw-r--r-- | server/test/hover/init.lua | 9 |
8 files changed, 254 insertions, 12 deletions
diff --git a/server/src/core/hover/emmy_function.lua b/server/src/core/hover/emmy_function.lua new file mode 100644 index 00000000..6306957f --- /dev/null +++ b/server/src/core/hover/emmy_function.lua @@ -0,0 +1,123 @@ +---@param emmy EmmyFunctionType +local function buildEmmyArgs(emmy, object, select) + local start + if object then + start = 2 + else + start = 1 + end + local strs = {} + local i = 0 + emmy:eachParam(function (name, typeObj) + i = i + 1 + if i > start then + strs[#strs+1] = ', ' + end + if i == select then + strs[#strs+1] = '@ARG' + end + strs[#strs+1] = name .. ': ' .. typeObj:getType() + if i == select then + strs[#strs+1] = '@ARG' + end + end) + local text = table.concat(strs) + local argLabel = {} + for i = 1, 2 do + local pos = text:find('@ARG', 1, true) + if pos then + if i == 1 then + argLabel[i] = pos + else + argLabel[i] = pos - 1 + end + text = text:sub(1, pos-1) .. text:sub(pos+4) + end + end + if #argLabel == 0 then + argLabel = nil + end + return text, argLabel +end + +local function buildEmmyReturns(emmy) + return '\n -> ' .. emmy:getReturn():getType() +end + +local function buildEnum(lib) + if not lib.enums then + return '' + end + local container = table.container() + for _, enum in ipairs(lib.enums) do + if not enum.name or (not enum.enum and not enum.code) then + goto NEXT_ENUM + end + if not container[enum.name] then + container[enum.name] = {} + if lib.args then + for _, arg in ipairs(lib.args) do + if arg.name == enum.name then + container[enum.name].type = arg.type + break + end + end + end + if lib.returns then + for _, rtn in ipairs(lib.returns) do + if rtn.name == enum.name then + container[enum.name].type = rtn.type + break + end + end + end + end + table.insert(container[enum.name], enum) + ::NEXT_ENUM:: + end + local strs = {} + for name, enums in pairs(container) do + local tp + if type(enums.type) == 'table' then + tp = table.concat(enums.type, '/') + else + tp = enums.type + end + strs[#strs+1] = ('\n%s: %s'):format(name, tp or 'any') + for _, enum in ipairs(enums) do + if enum.default then + strs[#strs+1] = '\n -> ' + else + strs[#strs+1] = '\n | ' + end + if enum.code then + strs[#strs+1] = tostring(enum.code) + else + strs[#strs+1] = ('%q'):format(enum.enum) + end + if enum.description then + strs[#strs+1] = ' -- ' .. enum.description + end + end + end + return table.concat(strs) +end + +return function (name, emmy, object, select) + local args, argLabel = buildEmmyArgs(emmy, object, select) + local returns = buildEmmyReturns(emmy) + local enum = buildEnum(emmy) + local tip = emmy.description + local headLen = #('function %s('):format(name) + local title = ('function %s(%s)%s'):format(name, args, returns) + if argLabel then + argLabel[1] = argLabel[1] + headLen + argLabel[2] = argLabel[2] + headLen + end + return { + label = title, + description = tip, + enum = enum, + argLabel = argLabel, + } +end diff --git a/server/src/core/hover/hover.lua b/server/src/core/hover/hover.lua index c0f62f24..31699d07 100644 --- a/server/src/core/hover/hover.lua +++ b/server/src/core/hover/hover.lua @@ -1,6 +1,7 @@ local findLib = require 'core.find_lib' local getFunctionHover = require 'core.hover.function' local getFunctionHoverAsLib = require 'core.hover.lib_function' +local getFunctionHoverAsEmmy = require 'core.hover.emmy_function' local buildValueName = require 'core.hover.name' local OriginTypes = { @@ -198,6 +199,7 @@ end local function hoverAsValue(source, lsp, select) local lib, fullkey = findLib(source) + ---@type value local value = source:findValue() local name = fullkey or buildValueName(source) @@ -207,8 +209,13 @@ local function hoverAsValue(source, lsp, select) if lib then hover = getFunctionHoverAsLib(name, lib, object, select) else - local func = value:getFunction() - hover = getFunctionHover(name, func, object, select) + local emmy = value:getEmmy() + if emmy and emmy.type == 'emmy.functionType' then + hover = getFunctionHoverAsEmmy(name, emmy, object, select) + else + local func = value:getFunction() + hover = getFunctionHover(name, func, object, select) + end end else hover = getValueHover(source, name, value, lib) diff --git a/server/src/emmy/funcType.lua b/server/src/emmy/funcType.lua new file mode 100644 index 00000000..405de594 --- /dev/null +++ b/server/src/emmy/funcType.lua @@ -0,0 +1,49 @@ +local listMgr = require 'vm.list' + +---@class EmmyFunctionType +local mt = {} +mt.__index = mt +mt.type = 'emmy.functionType' + +function mt:getType() + return 'function' +end + +function mt:getSource() + return listMgr.get(self.source) +end + +function mt:setValue(value) + self.value = value +end + +function mt:getValue() + return self.value +end + +function mt:addParam(name, type) + self._params[#self._params+1] = { name, type } +end + +function mt:addReturn(type) + self._return = type +end + +function mt:eachParam(callback) + for _, data in ipairs(self._params) do + callback(data[1], data[2]) + end +end + +function mt:getReturn() + return self._return +end + +return function (manager, source) + local self = setmetatable({ + source = source.id, + _params = {}, + _manager = manager, + }, mt) + return self +end diff --git a/server/src/emmy/manager.lua b/server/src/emmy/manager.lua index dad456a8..128ed029 100644 --- a/server/src/emmy/manager.lua +++ b/server/src/emmy/manager.lua @@ -9,6 +9,7 @@ local newField = require 'emmy.field' local newGeneric = require 'emmy.generic' local newArrayType = require 'emmy.arrayType' local newTableType = require 'emmy.tableType' +local newFuncType = require 'emmy.funcType' local mt = {} mt.__index = mt @@ -116,6 +117,11 @@ function mt:addTableType(source, keyType, valueType) return typeObj end +function mt:addFunctionType(source) + local typeObj = newFuncType(self, source) + return typeObj +end + function mt:addAlias(source, typeObj) local aliasName = source[1][1] local aliasObj = newAlias(self, source) diff --git a/server/src/vm/emmy.lua b/server/src/vm/emmy.lua index a66738e5..cdbbd3b7 100644 --- a/server/src/vm/emmy.lua +++ b/server/src/vm/emmy.lua @@ -31,6 +31,7 @@ function mt:doEmmy(action) elseif tp == 'emmyTableType' then self:doEmmyTableType(action) elseif tp == 'emmyFunctionType' then + self:doEmmyFunctionType(action) elseif tp == 'emmySee' then elseif tp == 'emmyIncomplete' then self:doEmmyIncomplete(action) @@ -225,12 +226,17 @@ function mt:doEmmyVararg(action) end end -function mt:doEmmyArrayType(action) +function mt:buildEmmyArrayType(action) ---@type emmyMgr local emmyMgr = self.emmyMgr self:instantSource(action) action:set('emmy class', action[1]) local type = emmyMgr:addArrayType(action) + return type +end + +function mt:doEmmyArrayType(action) + local type = self:buildEmmyArrayType(action) self._emmy = type if self.lsp then self.lsp.global:markGet(self:getUri()) @@ -238,13 +244,18 @@ function mt:doEmmyArrayType(action) return type end -function mt:doEmmyTableType(action) +function mt:buildEmmyTableType(action) ---@type emmyMgr local emmyMgr = self.emmyMgr self:instantSource(action) local keyType = self:buildEmmyType(action[1]) local valueType = self:buildEmmyType(action[2]) local type = emmyMgr:addTableType(action, keyType, valueType) + return type +end + +function mt:doEmmyTableType(action) + local type = self:buildEmmyTableType(action) self._emmy = type if self.lsp then self.lsp.global:markGet(self:getUri()) @@ -252,6 +263,37 @@ function mt:doEmmyTableType(action) return type end +function mt:doEmmyFunctionType(action) + ---@type emmyMgr + local emmyMgr = self.emmyMgr + self:instantSource(action) + local func = emmyMgr:addFunctionType(action) + for i = 1, #action // 2 do + local nameSource = action[i*2-1] + local typeSource = action[i*2] + local paramType = self:buildEmmyAnyType(typeSource) + func:addParam(nameSource[1], paramType) + end + local returnType = self:buildEmmyAnyType(action[#action]) + func:addReturn(returnType) + self._emmy = func + return func +end + +function mt:buildEmmyAnyType(source) + if source.type == 'emmyType' then + return self:buildEmmyType(source) + elseif source.type == 'emmyArrayType' then + return self:buildEmmyArrayType(source) + elseif source.type == 'emmyTableType' then + return self:buildEmmyTableType(source) + elseif source.type == 'emmyFunctionType' then + return self:buildEmmyFunctionType(source) + else + error('Unknown emmy type: ' .. table.dump(source)) + end +end + function mt:doEmmyIncomplete(action) self:instantSource(action) end diff --git a/server/src/vm/local.lua b/server/src/vm/local.lua index ce47414a..5b936625 100644 --- a/server/src/vm/local.lua +++ b/server/src/vm/local.lua @@ -136,18 +136,19 @@ function mt:getSource() return listMgr.get(self.source) end +local EMMY_TYPE = { + ['emmy.class'] = true, + ['emmy.type'] = true, + ['emmy.arrayType'] = true, + ['emmy.tableType'] = true, + ['emmy.functionType'] = true, +} + function mt:setEmmy(emmy) if not emmy then return end - if emmy.type ~= 'emmy.class' - and emmy.type ~= 'emmy.type' - and emmy.type ~= 'emmy.arrayType' - and emmy.type ~= 'emmy.tableType' - then - return - end - if self.value then + if self.value and EMMY_TYPE[emmy.type] then self.value:setEmmy(emmy) end end diff --git a/server/src/vm/value.lua b/server/src/vm/value.lua index 6cfaec4b..b43530d0 100644 --- a/server/src/vm/value.lua +++ b/server/src/vm/value.lua @@ -13,6 +13,7 @@ local TypeLevel = { ['number'] = 0.6, } +---@class value local mt = {} mt.__index = mt mt.type = 'value' @@ -595,6 +596,10 @@ function mt:setEmmy(emmy) ---@type EmmyTableType local emmyTableType = emmy emmyTableType:setValue(self) + elseif emmy.type == 'emmy.functionType' then + ---@type EmmyFunctionType + local emmyFuncType = emmy + emmyFuncType:setValue(self) else return end diff --git a/server/test/hover/init.lua b/server/test/hover/init.lua index 1c4809a6..baed3912 100644 --- a/server/test/hover/init.lua +++ b/server/test/hover/init.lua @@ -721,3 +721,12 @@ end [[ local k: *ClassA ]] + +TEST [[ +---@type fun(x: number, y: number):boolean +local <?f?> +]] +[[ +function f(x: number, y: number) + -> boolean +]] |