diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/src/core/completion.lua | 121 | ||||
-rw-r--r-- | server/src/core/document_symbol.lua | 4 | ||||
-rw-r--r-- | server/src/core/hover/function.lua (renamed from server/src/core/hover_function.lua) | 0 | ||||
-rw-r--r-- | server/src/core/hover/hover.lua (renamed from server/src/core/hover.lua) | 199 | ||||
-rw-r--r-- | server/src/core/hover/init.lua | 1 | ||||
-rw-r--r-- | server/src/core/hover/lib_function.lua | 194 | ||||
-rw-r--r-- | server/src/core/hover/name.lua (renamed from server/src/core/hover_name.lua) | 0 | ||||
-rw-r--r-- | server/test/completion/init.lua | 26 |
8 files changed, 326 insertions, 219 deletions
diff --git a/server/src/core/completion.lua b/server/src/core/completion.lua index a4913ad8..a04ffc52 100644 --- a/server/src/core/completion.lua +++ b/server/src/core/completion.lua @@ -1,5 +1,6 @@ local findSource = require 'core.find_source' -local hover = require 'core.hover' +local getFunctionHover = require 'core.hover.function' +local getFunctionHoverAsLib = require 'core.hover.lib_function' local CompletionItemKind = { Text = 1, @@ -98,6 +99,86 @@ local function matchKey(me, other) return true end +local function getDucumentation(name, value) + if value:getType() == 'function' then + local lib = value:getLib() + local hover + if lib then + hover = getFunctionHoverAsLib(name, lib) + else + hover = getFunctionHover(name, value:getFunction()) + end + if not hover then + return nil + end + local text = ([[ +```lua +%s +``` +%s +```lua +%s +``` +]]):format(hover.label or '', hover.description or '', hover.enum or '') + return { + kind = 'markdown', + value = text, + } + end + return nil +end + +local function getDetail(value) + local literal = value:getLiteral() + local tp = type(literal) + if tp == 'boolean' then + return ('= %q'):format(literal) + elseif tp == 'string' then + return ('= %q'):format(literal) + elseif tp == 'number' then + if math.type(literal) == 'integer' then + return ('= %q'):format(literal) + else + local str = ('= %.16f'):format(literal) + local dot = str:find('.', 1, true) + local suffix = str:find('[0]+$', dot + 2) + if suffix then + return str:sub(1, suffix - 1) + else + return str + end + end + end + return nil +end + +local function getKind(cata, value) + if value:getType() == 'function' then + local func = value:getFunction() + if func:getObject() then + return CompletionItemKind.Method + else + return CompletionItemKind.Function + end + end + if cata == 'field' then + local literal = value:getLiteral() + local tp = type(literal) + if tp == 'number' or tp == 'integer' or tp == 'string' then + return CompletionItemKind.Enum + end + end + return nil +end + +local function getValueData(cata, name, value) + return { + documentation = getDucumentation(name, value), + detail = getDetail(value), + kind = getKind(cata, value), + } +end + local function searchLocals(vm, source, word, callback) for _, src in ipairs(vm.sources) do local loc = src:bindLocal() @@ -109,12 +190,26 @@ local function searchLocals(vm, source, word, callback) and loc:close() >= source.finish and matchKey(word, loc:getName()) then - callback(loc:getName(), src, CompletionItemKind.Variable) + callback(loc:getName(), src, CompletionItemKind.Variable, getValueData('local', loc:getName(), loc:getValue())) end :: CONTINUE :: end end +local function sortPairs(t) + local keys = {} + for k in pairs(t) do + keys[#keys+1] = k + end + table.sort(keys) + local i = 0 + return function () + i = i + 1 + local k = keys[i] + return k, t[k] + end +end + local function searchFields(vm, source, word, callback) local parent = source:get 'parent' if not parent then @@ -129,13 +224,12 @@ local function searchFields(vm, source, word, callback) goto CONTINUE end if matchKey(word, k) then - map[#map+1] = k + map[k] = v end :: CONTINUE :: end) - table.sort(map) - for _, k in ipairs(map) do - callback(k, nil, CompletionItemKind.Field) + for k, v in sortPairs(map) do + callback(k, nil, CompletionItemKind.Field, getValueData('field', k, v)) end end @@ -147,6 +241,10 @@ local function searchAsGlobal(vm, source, word, callback) searchFields(vm, source, word, callback) end +local function searchAsSuffix(vm, source, word, callback) + searchFields(vm, source, word, callback) +end + local function searchSource(vm, source, word, callback) if source:get 'global' then searchAsGlobal(vm, source, word, callback) @@ -156,6 +254,9 @@ local function searchSource(vm, source, word, callback) searchAsGlobal(vm, source, word, callback) return end + if source:get 'simple' then + searchAsSuffix(vm, source, word, callback) + end end local function searchAllWords(vm, source, word, callback) @@ -188,8 +289,12 @@ local function makeList(source, word) if not data then data = {} end - data.label = name - data.kind = kind + if not data.label then + data.label = name + end + if not data.kind then + data.kind = kind + end list[#list+1] = data end, list end diff --git a/server/src/core/document_symbol.lua b/server/src/core/document_symbol.lua index 199aab7b..8ca39bdd 100644 --- a/server/src/core/document_symbol.lua +++ b/server/src/core/document_symbol.lua @@ -1,5 +1,5 @@ -local hoverFunction = require 'core.hover_function' -local hoverName = require 'core.hover_name' +local hoverFunction = require 'core.hover.function' +local hoverName = require 'core.hover.name' local hover = require 'core.hover' local SymbolKind = { diff --git a/server/src/core/hover_function.lua b/server/src/core/hover/function.lua index 6be90b06..6be90b06 100644 --- a/server/src/core/hover_function.lua +++ b/server/src/core/hover/function.lua diff --git a/server/src/core/hover.lua b/server/src/core/hover/hover.lua index e99becdc..e07acf4a 100644 --- a/server/src/core/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 buildValueName = require 'core.hover_name' +local getFunctionHover = require 'core.hover.function' +local getFunctionHoverAsLib = require 'core.hover.lib_function' +local buildValueName = require 'core.hover.name' local OriginTypes = { ['any'] = true, @@ -15,200 +16,6 @@ local OriginTypes = { ['function'] = true, } -local function buildLibArgs(lib, object, select) - if not lib.args then - return '' - end - local start - if object then - start = 2 - if select then - select = select + 1 - end - else - start = 1 - end - local strs = {} - for i = start, #lib.args do - local arg = lib.args[i] - if arg.optional then - if i > start then - strs[#strs+1] = ' [' - else - strs[#strs+1] = '[' - end - end - if i > start then - strs[#strs+1] = ', ' - end - - local argStr = {} - if i == select then - argStr[#argStr+1] = '@ARG' - end - if arg.name then - argStr[#argStr+1] = ('%s: '):format(arg.name) - end - if type(arg.type) == 'table' then - argStr[#argStr+1] = table.concat(arg.type, '/') - else - argStr[#argStr+1] = arg.type or 'any' - end - if arg.default then - argStr[#argStr+1] = ('(%q)'):format(arg.default) - end - if i == select then - argStr[#argStr+1] = '@ARG' - end - - for _, str in ipairs(argStr) do - strs[#strs+1] = str - end - if arg.optional == 'self' then - strs[#strs+1] = ']' - end - end - for _, arg in ipairs(lib.args) do - if arg.optional == 'after' then - strs[#strs+1] = ']' - 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 buildLibReturns(lib) - if not lib.returns then - return '' - end - local strs = {} - for i, rtn in ipairs(lib.returns) do - if rtn.optional then - if i > 1 then - strs[#strs+1] = ' [' - else - strs[#strs+1] = '[' - end - end - if i > 1 then - strs[#strs+1] = ', ' - end - if rtn.name then - strs[#strs+1] = ('%s: '):format(rtn.name) - end - if type(rtn.type) == 'table' then - strs[#strs+1] = table.concat(rtn.type, '/') - else - strs[#strs+1] = rtn.type or 'any' - end - if rtn.default then - strs[#strs+1] = ('(%q)'):format(rtn.default) - end - if rtn.optional == 'self' then - strs[#strs+1] = ']' - end - end - for _, rtn in ipairs(lib.returns) do - if rtn.optional == 'after' then - strs[#strs+1] = ']' - end - end - return '\n -> ' .. table.concat(strs) -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 - -local function getFunctionHoverAsLib(name, lib, object, select) - local args, argLabel = buildLibArgs(lib, object, select) - local returns = buildLibReturns(lib) - local enum = buildEnum(lib) - local tip = lib.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 - local function findClass(value) -- 检查对象元表 local metaValue = value:getMetaTable() diff --git a/server/src/core/hover/init.lua b/server/src/core/hover/init.lua new file mode 100644 index 00000000..be5b5632 --- /dev/null +++ b/server/src/core/hover/init.lua @@ -0,0 +1 @@ +return require 'core.hover.hover' diff --git a/server/src/core/hover/lib_function.lua b/server/src/core/hover/lib_function.lua new file mode 100644 index 00000000..cb67c7f9 --- /dev/null +++ b/server/src/core/hover/lib_function.lua @@ -0,0 +1,194 @@ + +local function buildLibArgs(lib, object, select) + if not lib.args then + return '' + end + local start + if object then + start = 2 + if select then + select = select + 1 + end + else + start = 1 + end + local strs = {} + for i = start, #lib.args do + local arg = lib.args[i] + if arg.optional then + if i > start then + strs[#strs+1] = ' [' + else + strs[#strs+1] = '[' + end + end + if i > start then + strs[#strs+1] = ', ' + end + + local argStr = {} + if i == select then + argStr[#argStr+1] = '@ARG' + end + if arg.name then + argStr[#argStr+1] = ('%s: '):format(arg.name) + end + if type(arg.type) == 'table' then + argStr[#argStr+1] = table.concat(arg.type, '/') + else + argStr[#argStr+1] = arg.type or 'any' + end + if arg.default then + argStr[#argStr+1] = ('(%q)'):format(arg.default) + end + if i == select then + argStr[#argStr+1] = '@ARG' + end + + for _, str in ipairs(argStr) do + strs[#strs+1] = str + end + if arg.optional == 'self' then + strs[#strs+1] = ']' + end + end + for _, arg in ipairs(lib.args) do + if arg.optional == 'after' then + strs[#strs+1] = ']' + 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 buildLibReturns(lib) + if not lib.returns then + return '' + end + local strs = {} + for i, rtn in ipairs(lib.returns) do + if rtn.optional then + if i > 1 then + strs[#strs+1] = ' [' + else + strs[#strs+1] = '[' + end + end + if i > 1 then + strs[#strs+1] = ', ' + end + if rtn.name then + strs[#strs+1] = ('%s: '):format(rtn.name) + end + if type(rtn.type) == 'table' then + strs[#strs+1] = table.concat(rtn.type, '/') + else + strs[#strs+1] = rtn.type or 'any' + end + if rtn.default then + strs[#strs+1] = ('(%q)'):format(rtn.default) + end + if rtn.optional == 'self' then + strs[#strs+1] = ']' + end + end + for _, rtn in ipairs(lib.returns) do + if rtn.optional == 'after' then + strs[#strs+1] = ']' + end + end + return '\n -> ' .. table.concat(strs) +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, lib, object, select) + local args, argLabel = buildLibArgs(lib, object, select) + local returns = buildLibReturns(lib) + local enum = buildEnum(lib) + local tip = lib.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_name.lua b/server/src/core/hover/name.lua index 4faab3f1..4faab3f1 100644 --- a/server/src/core/hover_name.lua +++ b/server/src/core/hover/name.lua diff --git a/server/test/completion/init.lua b/server/test/completion/init.lua index 3e7f9792..874b0948 100644 --- a/server/test/completion/init.lua +++ b/server/test/completion/init.lua @@ -182,40 +182,40 @@ ass@ } TEST [[ -local t = { - abc = 1, -} -t.a@ +local zabc = 1 +z@ ]] { { - label = 'abc', - kind = CompletionItemKind.Enum, + label = 'zabc', + kind = CompletionItemKind.Variable, detail = '= 1', } } TEST [[ -local zabc = 1 +local zabc = 1.0 z@ ]] { { label = 'zabc', kind = CompletionItemKind.Variable, - detail = '= 1', + detail = '= 1.0', } } TEST [[ -local zabc = 1.0 -z@ +local t = { + abc = 1, +} +t.a@ ]] { { - label = 'zabc', - kind = CompletionItemKind.Variable, - detail = '= 1.0', + label = 'abc', + kind = CompletionItemKind.Enum, + detail = '= 1', } } |