diff options
Diffstat (limited to 'script/core/hover/function.lua')
-rw-r--r-- | script/core/hover/function.lua | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/script/core/hover/function.lua b/script/core/hover/function.lua new file mode 100644 index 00000000..3865f602 --- /dev/null +++ b/script/core/hover/function.lua @@ -0,0 +1,243 @@ +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() + end + end + if func.args then + for i, arg in ipairs(func.args) do + names[#names+1] = arg:getName() + local param = func:findEmmyParamByName(arg:getName()) + if param then + values[i] = param:getType() + options[i] = param:getOption() + end + end + end + local strs = {} + local start = 1 + if object then + start = 2 + end + local max + if func:getSource() then + max = #names + else + max = math.max(#names, #values) + end + local args = {} + 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 + + if i == select then + strs[#strs+1] = '@ARG' + end + if name then + strs[#strs+1] = name .. ': ' .. value + else + strs[#strs+1] = value + end + args[#args+1] = strs[#strs] + 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 + strs[#strs+1] = ', ' + 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 + 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, args +end + +local function buildValueReturns(func) + if not func then + return '\n -> any' + end + if not func:get 'hasReturn' then + 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 + 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] = ('\n% 3d. '):format(i) + end + if emmy and emmy.name then + strs[#strs+1] = ('%s: '):format(emmy.name) + elseif 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) +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 = {} + local raw = {} + for _, param in ipairs(params) do + local first = true + local name = param:getName() + raw[name] = {} + 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 + raw[name][#raw[name]+1] = enum[1] + end) + end + if #strs == 0 then + return nil + end + return table.concat(strs), raw +end + +local function getComment(func) + if not func then + return nil + end + local comments = {} + local params = func:getEmmyParams() + if params then + for _, param in ipairs(params) do + local option = param:getOption() + if option and option.comment then + comments[#comments+1] = ('+ `%s`*(%s)*: %s'):format(param:getName(), param:getType(), option.comment) + end + end + end + comments[#comments+1] = func:getComment() + if #comments == 0 then + return nil + end + return table.concat(comments, '\n\n') +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 argStr, argLabel, args = buildValueArgs(func, object, select) + local returns = buildValueReturns(func) + local enum, rawEnum = buildEnum(func) + local comment = getComment(func) + local overloads = getOverLoads(name, func, object, select) + return { + label = ('function %s(%s)%s'):format(name, argStr, returns), + name = name, + argStr = argStr, + returns = returns, + description = comment, + enum = enum, + rawEnum = rawEnum, + argLabel = argLabel, + overloads = overloads, + args = args, + } +end |