diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2020-11-20 21:57:09 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2020-11-20 21:57:09 +0800 |
commit | 4ca61ec457822dd14966afa0752340ae8ce180a1 (patch) | |
tree | ae8adb1ad82c717868e551e699fd3cf3bb290089 /script-beta/core/hover | |
parent | c63b2e404d8d2bb984afe3678a5ba2b2836380cc (diff) | |
download | lua-language-server-4ca61ec457822dd14966afa0752340ae8ce180a1.zip |
no longer beta
Diffstat (limited to 'script-beta/core/hover')
-rw-r--r-- | script-beta/core/hover/arg.lua | 71 | ||||
-rw-r--r-- | script-beta/core/hover/description.lua | 204 | ||||
-rw-r--r-- | script-beta/core/hover/init.lua | 164 | ||||
-rw-r--r-- | script-beta/core/hover/label.lua | 211 | ||||
-rw-r--r-- | script-beta/core/hover/name.lua | 101 | ||||
-rw-r--r-- | script-beta/core/hover/return.lua | 125 | ||||
-rw-r--r-- | script-beta/core/hover/table.lua | 257 |
7 files changed, 0 insertions, 1133 deletions
diff --git a/script-beta/core/hover/arg.lua b/script-beta/core/hover/arg.lua deleted file mode 100644 index 9cd19f02..00000000 --- a/script-beta/core/hover/arg.lua +++ /dev/null @@ -1,71 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm' - -local function optionalArg(arg) - if not arg.bindDocs then - return false - end - local name = arg[1] - for _, doc in ipairs(arg.bindDocs) do - if doc.type == 'doc.param' and doc.param[1] == name then - return doc.optional - end - end -end - -local function asFunction(source, oop) - if not source.args then - return '' - end - local args = {} - for i = 1, #source.args do - local arg = source.args[i] - local name = arg.name or guide.getName(arg) - if name then - args[i] = ('%s%s: %s'):format( - name, - optionalArg(arg) and '?' or '', - vm.getInferType(arg) - ) - else - args[i] = ('%s'):format(vm.getInferType(arg)) - end - end - local methodDef - local parent = source.parent - if parent and parent.type == 'setmethod' then - methodDef = true - end - if not methodDef and oop then - return table.concat(args, ', ', 2) - else - return table.concat(args, ', ') - end -end - -local function asDocFunction(source) - if not source.args then - return '' - end - local args = {} - for i = 1, #source.args do - local arg = source.args[i] - local name = arg.name[1] - args[i] = ('%s%s: %s'):format( - name, - arg.optional and '?' or '', - vm.getInferType(arg.extends) - ) - end - return table.concat(args, ', ') -end - -return function (source, oop) - if source.type == 'function' then - return asFunction(source, oop) - end - if source.type == 'doc.type.function' then - return asDocFunction(source) - end - return '' -end diff --git a/script-beta/core/hover/description.lua b/script-beta/core/hover/description.lua deleted file mode 100644 index 7d89ee6c..00000000 --- a/script-beta/core/hover/description.lua +++ /dev/null @@ -1,204 +0,0 @@ -local vm = require 'vm' -local ws = require 'workspace' -local furi = require 'file-uri' -local files = require 'files' -local guide = require 'parser.guide' -local markdown = require 'provider.markdown' -local config = require 'config' -local lang = require 'language' - -local function asStringInRequire(source, literal) - local rootPath = ws.path or '' - local parent = source.parent - if parent and parent.type == 'callargs' then - local result, searchers - local call = parent.parent - local func = call.node - local libName = vm.getLibraryName(func) - if not libName then - return - end - if libName == 'require' then - result, searchers = ws.findUrisByRequirePath(literal) - elseif libName == 'dofile' - or libName == 'loadfile' then - result = ws.findUrisByFilePath(literal) - end - if result and #result > 0 then - for i, uri in ipairs(result) do - local searcher = searchers and furi.decode(searchers[uri]) - uri = files.getOriginUri(uri) - local path = furi.decode(uri) - if files.eq(path:sub(1, #rootPath), rootPath) then - path = path:sub(#rootPath + 1) - end - path = path:gsub('^[/\\]*', '') - if vm.isMetaFile(uri) then - result[i] = ('* [[meta]](%s)'):format(uri) - elseif searcher then - searcher = searcher:sub(#rootPath + 1) - searcher = ws.normalize(searcher) - result[i] = ('* [%s](%s) %s'):format(path, uri, lang.script('HOVER_USE_LUA_PATH', searcher)) - else - result[i] = ('* [%s](%s)'):format(path, uri) - end - end - table.sort(result) - local md = markdown() - md:add('md', table.concat(result, '\n')) - return md:string() - end - end -end - -local function asStringView(source, literal) - -- 内部包含转义符? - local rawLen = source.finish - source.start - 2 * #source[2] + 1 - if config.config.hover.viewString - and (source[2] == '"' or source[2] == "'") - and rawLen > #literal then - local view = literal - local max = config.config.hover.viewStringMax - if #view > max then - view = view:sub(1, max) .. '...' - end - local md = markdown() - md:add('txt', view) - return md:string() - end -end - -local function asString(source) - local literal = guide.getLiteral(source) - if type(literal) ~= 'string' then - return nil - end - return asStringInRequire(source, literal) - or asStringView(source, literal) -end - -local function getBindComment(docGroup, base) - local lines = {} - for _, doc in ipairs(docGroup) do - if doc.type == 'doc.comment' then - lines[#lines+1] = doc.comment.text:sub(2) - elseif #lines > 0 and not base then - break - elseif doc == base then - break - else - lines = {} - end - end - if #lines == 0 then - return nil - end - return table.concat(lines, '\n') -end - -local function buildEnumChunk(docType, name) - local enums = vm.getDocEnums(docType) - if #enums == 0 then - return - end - local types = {} - for _, tp in ipairs(docType.types) do - types[#types+1] = tp[1] - end - local lines = {} - lines[#lines+1] = ('%s: %s'):format(name, table.concat(types)) - for _, enum in ipairs(enums) do - lines[#lines+1] = (' %s %s%s'):format( - (enum.default and '->') - or (enum.additional and '+>') - or ' |', - enum[1], - enum.comment and (' -- %s'):format(enum.comment) or '' - ) - end - return table.concat(lines, '\n') -end - -local function getBindEnums(docGroup) - local mark = {} - local chunks = {} - local returnIndex = 0 - for _, doc in ipairs(docGroup) do - if doc.type == 'doc.param' then - local name = doc.param[1] - if mark[name] then - goto CONTINUE - end - mark[name] = true - chunks[#chunks+1] = buildEnumChunk(doc.extends, name) - elseif doc.type == 'doc.return' then - for _, rtn in ipairs(doc.returns) do - returnIndex = returnIndex + 1 - local name = rtn.name and rtn.name[1] or ('(return %d)'):format(returnIndex) - if mark[name] then - goto CONTINUE - end - mark[name] = true - chunks[#chunks+1] = buildEnumChunk(rtn, name) - end - end - ::CONTINUE:: - end - if #chunks == 0 then - return nil - end - return table.concat(chunks, '\n\n') -end - -local function tryDocFieldUpComment(source) - if source.type ~= 'doc.field' then - return - end - if not source.bindGroup then - return - end - local comment = getBindComment(source.bindGroup, source) - return comment -end - -local function tryDocComment(source) - if not source.bindDocs then - return - end - local comment = getBindComment(source.bindDocs) - local enums = getBindEnums(source.bindDocs) - local md = markdown() - if comment then - md:add('md', comment) - end - if enums then - md:add('lua', enums) - end - return md:string() -end - -local function tryDocOverloadToComment(source) - if source.type ~= 'doc.type.function' then - return - end - local doc = source.parent - if doc.type ~= 'doc.overload' - or not doc.bindSources then - return - end - for _, src in ipairs(doc.bindSources) do - local md = tryDocComment(src) - if md then - return md - end - end -end - -return function (source) - if source.type == 'string' then - return asString(source) - end - return tryDocOverloadToComment(source) - or tryDocFieldUpComment(source) - or tryDocComment(source) -end diff --git a/script-beta/core/hover/init.lua b/script-beta/core/hover/init.lua deleted file mode 100644 index 96e01ab5..00000000 --- a/script-beta/core/hover/init.lua +++ /dev/null @@ -1,164 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local vm = require 'vm' -local getLabel = require 'core.hover.label' -local getDesc = require 'core.hover.description' -local util = require 'utility' -local findSource = require 'core.find-source' -local lang = require 'language' - -local function eachFunctionAndOverload(value, callback) - callback(value) - if not value.bindDocs then - return - end - for _, doc in ipairs(value.bindDocs) do - if doc.type == 'doc.overload' then - callback(doc.overload) - end - end -end - -local function getHoverAsFunction(source) - local values = vm.getDefs(source, 'deep') - local desc = getDesc(source) - local labels = {} - local defs = 0 - local protos = 0 - local other = 0 - local oop = source.type == 'method' - or source.type == 'getmethod' - or source.type == 'setmethod' - local mark = {} - for _, def in ipairs(values) do - def = guide.getObjectValue(def) or def - if def.type == 'function' - or def.type == 'doc.type.function' then - eachFunctionAndOverload(def, function (value) - if mark[value] then - return - end - mark[value] =true - local label = getLabel(value, oop) - if label then - defs = defs + 1 - labels[label] = (labels[label] or 0) + 1 - if labels[label] == 1 then - protos = protos + 1 - end - end - desc = desc or getDesc(value) - end) - elseif def.type == 'table' - or def.type == 'boolean' - or def.type == 'string' - or def.type == 'number' then - other = other + 1 - desc = desc or getDesc(def) - end - end - - if defs == 1 and other == 0 then - return { - label = next(labels), - source = source, - description = desc, - } - end - - local lines = {} - if defs > 1 then - lines[#lines+1] = lang.script('HOVER_MULTI_DEF_PROTO', defs, protos) - end - if other > 0 then - lines[#lines+1] = lang.script('HOVER_MULTI_PROTO_NOT_FUNC', other) - end - if defs > 1 then - for label, count in util.sortPairs(labels) do - lines[#lines+1] = ('(%d) %s'):format(count, label) - end - else - lines[#lines+1] = next(labels) - end - local label = table.concat(lines, '\n') - return { - label = label, - source = source, - description = desc, - } -end - -local function getHoverAsValue(source) - local oop = source.type == 'method' - or source.type == 'getmethod' - or source.type == 'setmethod' - local label = getLabel(source, oop) - local desc = getDesc(source) - if not desc then - local values = vm.getDefs(source, 'deep') - for _, def in ipairs(values) do - desc = getDesc(def) - if desc then - break - end - end - end - return { - label = label, - source = source, - description = desc, - } -end - -local function getHoverAsDocName(source) - local label = getLabel(source) - local desc = getDesc(source) - return { - label = label, - source = source, - description = desc, - } -end - -local function getHover(source) - if source.type == 'doc.type.name' then - return getHoverAsDocName(source) - end - local isFunction = vm.hasInferType(source, 'function', 'deep') - if isFunction then - return getHoverAsFunction(source) - else - return getHoverAsValue(source) - end -end - -local accept = { - ['local'] = true, - ['setlocal'] = true, - ['getlocal'] = true, - ['setglobal'] = true, - ['getglobal'] = true, - ['field'] = true, - ['method'] = true, - ['string'] = true, - ['number'] = true, - ['doc.type.name'] = true, -} - -local function getHoverByUri(uri, offset) - local ast = files.getAst(uri) - if not ast then - return nil - end - local source = findSource(ast, offset, accept) - if not source then - return nil - end - local hover = getHover(source) - return hover -end - -return { - get = getHover, - byUri = getHoverByUri, -} diff --git a/script-beta/core/hover/label.lua b/script-beta/core/hover/label.lua deleted file mode 100644 index d785bc27..00000000 --- a/script-beta/core/hover/label.lua +++ /dev/null @@ -1,211 +0,0 @@ -local buildName = require 'core.hover.name' -local buildArg = require 'core.hover.arg' -local buildReturn = require 'core.hover.return' -local buildTable = require 'core.hover.table' -local vm = require 'vm' -local util = require 'utility' -local guide = require 'parser.guide' -local lang = require 'language' -local config = require 'config' -local files = require 'files' - -local function asFunction(source, oop) - local name = buildName(source, oop) - local arg = buildArg(source, oop) - local rtn = buildReturn(source) - local lines = {} - lines[1] = ('function %s(%s)'):format(name, arg) - lines[2] = rtn - return table.concat(lines, '\n') -end - -local function asDocFunction(source) - local name = buildName(source) - local arg = buildArg(source) - local rtn = buildReturn(source) - local lines = {} - lines[1] = ('function %s(%s)'):format(name, arg) - lines[2] = rtn - return table.concat(lines, '\n') -end - -local function asDocTypeName(source) - for _, doc in ipairs(vm.getDocTypes(source[1])) do - if doc.type == 'doc.class.name' then - return 'class ' .. source[1] - end - if doc.type == 'doc.alias.name' then - local extends = doc.parent.extends - return lang.script('HOVER_EXTENDS', vm.getInferType(extends)) - end - end -end - -local function asValue(source, title) - local name = buildName(source) - local infers = vm.getInfers(source, 'deep') - local type = vm.getInferType(source, 'deep') - local class = vm.getClass(source, 'deep') - local literal = vm.getInferLiteral(source, 'deep') - local cont - if type ~= 'string' and not type:find('%[%]$') then - if #vm.getFields(source, 'deep') > 0 - or vm.hasInferType(source, 'table', 'deep') then - cont = buildTable(source) - end - end - local pack = {} - pack[#pack+1] = title - pack[#pack+1] = name .. ':' - if cont and type == 'table' then - type = nil - end - if class then - pack[#pack+1] = class - else - pack[#pack+1] = type - end - if literal then - pack[#pack+1] = '=' - pack[#pack+1] = literal - end - if cont then - pack[#pack+1] = cont - end - return table.concat(pack, ' ') -end - -local function asLocal(source) - return asValue(source, 'local') -end - -local function asGlobal(source) - return asValue(source, 'global') -end - -local function isGlobalField(source) - if source.type == 'field' - or source.type == 'method' then - source = source.parent - end - if source.type == 'setfield' - or source.type == 'getfield' - or source.type == 'setmethod' - or source.type == 'getmethod' then - local node = source.node - if node.type == 'setglobal' - or node.type == 'getglobal' then - return true - end - return isGlobalField(node) - elseif source.type == 'tablefield' then - local parent = source.parent - if parent.type == 'setglobal' - or parent.type == 'getglobal' then - return true - end - return isGlobalField(parent) - else - return false - end -end - -local function asField(source) - if isGlobalField(source) then - return asGlobal(source) - end - return asValue(source, 'field') -end - -local function asDocField(source) - local name = source.field[1] - local class - for _, doc in ipairs(source.bindGroup) do - if doc.type == 'doc.class' then - class = doc - break - end - end - if not class then - return ('field ?.%s: %s'):format( - name, - vm.getInferType(source.extends) - ) - end - return ('field %s.%s: %s'):format( - class.class[1], - name, - vm.getInferType(source.extends) - ) -end - -local function asString(source) - local str = source[1] - if type(str) ~= 'string' then - return '' - end - local len = #str - local charLen = util.utf8Len(str, 1, -1) - if len == charLen then - return lang.script('HOVER_STRING_BYTES', len) - else - return lang.script('HOVER_STRING_CHARACTERS', len, charLen) - end -end - -local function formatNumber(n) - local str = ('%.10f'):format(n) - str = str:gsub('%.?0*$', '') - return str -end - -local function asNumber(source) - if not config.config.hover.viewNumber then - return nil - end - local num = source[1] - if type(num) ~= 'number' then - return nil - end - local uri = guide.getUri(source) - local text = files.getText(uri) - if not text then - return nil - end - local raw = text:sub(source.start, source.finish) - if not raw or not raw:find '[^%-%d%.]' then - return nil - end - return formatNumber(num) -end - -return function (source, oop) - if source.type == 'function' then - return asFunction(source, oop) - elseif source.type == 'local' - or source.type == 'getlocal' - or source.type == 'setlocal' then - return asLocal(source) - elseif source.type == 'setglobal' - or source.type == 'getglobal' then - return asGlobal(source) - elseif source.type == 'getfield' - or source.type == 'setfield' - or source.type == 'getmethod' - or source.type == 'setmethod' - or source.type == 'tablefield' - or source.type == 'field' - or source.type == 'method' then - return asField(source) - elseif source.type == 'string' then - return asString(source) - elseif source.type == 'number' then - return asNumber(source) - elseif source.type == 'doc.type.function' then - return asDocFunction(source) - elseif source.type == 'doc.type.name' then - return asDocTypeName(source) - elseif source.type == 'doc.field' then - return asDocField(source) - end -end diff --git a/script-beta/core/hover/name.lua b/script-beta/core/hover/name.lua deleted file mode 100644 index 9ad32e09..00000000 --- a/script-beta/core/hover/name.lua +++ /dev/null @@ -1,101 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm' - -local buildName - -local function asLocal(source) - local name = guide.getName(source) - if not source.attrs then - return name - end - local label = {} - label[#label+1] = name - for _, attr in ipairs(source.attrs) do - label[#label+1] = ('<%s>'):format(attr[1]) - end - return table.concat(label, ' ') -end - -local function asField(source, oop) - local class - if source.node.type ~= 'getglobal' then - class = vm.getClass(source.node, 'deep') - end - local node = class or guide.getName(source.node) or '?' - local method = guide.getName(source) - if oop then - return ('%s:%s'):format(node, method) - else - return ('%s.%s'):format(node, method) - end -end - -local function asTableField(source) - if not source.field then - return - end - return guide.getName(source.field) -end - -local function asGlobal(source) - return guide.getName(source) -end - -local function asDocFunction(source) - local doc = guide.getParentType(source, 'doc.type') - or guide.getParentType(source, 'doc.overload') - if not doc or not doc.bindSources then - return '' - end - for _, src in ipairs(doc.bindSources) do - local name = buildName(src) - if name ~= '' then - return name - end - end - return '' -end - -local function asDocField(source) - return source.field[1] -end - -function buildName(source, oop) - if oop == nil then - oop = source.type == 'setmethod' - or source.type == 'getmethod' - end - if source.type == 'local' - or source.type == 'getlocal' - or source.type == 'setlocal' then - return asLocal(source) or '' - end - if source.type == 'setglobal' - or source.type == 'getglobal' then - return asGlobal(source) or '' - end - if source.type == 'setmethod' - or source.type == 'getmethod' then - return asField(source, true) or '' - end - if source.type == 'setfield' - or source.type == 'getfield' then - return asField(source, oop) or '' - end - if source.type == 'tablefield' then - return asTableField(source) or '' - end - if source.type == 'doc.type.function' then - return asDocFunction(source) - end - if source.type == 'doc.field' then - return asDocField(source) - end - local parent = source.parent - if parent then - return buildName(parent, oop) - end - return '' -end - -return buildName diff --git a/script-beta/core/hover/return.lua b/script-beta/core/hover/return.lua deleted file mode 100644 index 3829dbed..00000000 --- a/script-beta/core/hover/return.lua +++ /dev/null @@ -1,125 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm' - -local function mergeTypes(returns) - if type(returns) == 'string' then - return returns - end - return guide.mergeTypes(returns) -end - -local function getReturnDualByDoc(source) - local docs = source.bindDocs - if not docs then - return - end - local dual - for _, doc in ipairs(docs) do - if doc.type == 'doc.return' then - for _, rtn in ipairs(doc.returns) do - if not dual then - dual = {} - end - dual[#dual+1] = { rtn } - end - end - end - return dual -end - -local function getReturnDualByGrammar(source) - if not source.returns then - return nil - end - local dual - for _, rtn in ipairs(source.returns) do - if not dual then - dual = {} - end - for n = 1, #rtn do - if not dual[n] then - dual[n] = {} - end - dual[n][#dual[n]+1] = rtn[n] - end - end - return dual -end - -local function asFunction(source) - local dual = getReturnDualByDoc(source) - or getReturnDualByGrammar(source) - if not dual then - return - end - local returns = {} - for i, rtn in ipairs(dual) do - local line = {} - local types = {} - if i == 1 then - line[#line+1] = ' -> ' - else - line[#line+1] = ('% 3d. '):format(i) - end - for n = 1, #rtn do - local values = vm.getInfers(rtn[n]) - for _, value in ipairs(values) do - if value.type then - for tp in value.type:gmatch '[^|]+' do - types[#types+1] = tp - end - end - end - end - if #types > 0 or rtn[1] then - local tp = mergeTypes(types) or 'any' - if rtn[1].name then - line[#line+1] = ('%s%s: %s'):format( - rtn[1].name[1], - rtn[1].optional and '?' or '', - tp - ) - else - line[#line+1] = ('%s%s'):format( - tp, - rtn[1].optional and '?' or '' - ) - end - else - break - end - returns[i] = table.concat(line) - end - if #returns == 0 then - return nil - end - return table.concat(returns, '\n') -end - -local function asDocFunction(source) - if not source.returns or #source.returns == 0 then - return nil - end - local returns = {} - for i, rtn in ipairs(source.returns) do - local rtnText = ('%s%s'):format( - vm.getInferType(rtn), - rtn.optional and '?' or '' - ) - if i == 1 then - returns[#returns+1] = (' -> %s'):format(rtnText) - else - returns[#returns+1] = ('% 3d. %s'):format(i, rtnText) - end - end - return table.concat(returns, '\n') -end - -return function (source) - if source.type == 'function' then - return asFunction(source) - end - if source.type == 'doc.type.function' then - return asDocFunction(source) - end -end diff --git a/script-beta/core/hover/table.lua b/script-beta/core/hover/table.lua deleted file mode 100644 index 02be5271..00000000 --- a/script-beta/core/hover/table.lua +++ /dev/null @@ -1,257 +0,0 @@ -local vm = require 'vm' -local util = require 'utility' -local guide = require 'parser.guide' -local config = require 'config' -local lang = require 'language' - -local function getKey(src) - local key = vm.getKeyName(src) - if not key or #key <= 2 then - if not src.index then - return '[any]' - end - local class = vm.getClass(src.index) - if class then - return ('[%s]'):format(class) - end - local tp = vm.getInferType(src.index) - if tp then - return ('[%s]'):format(tp) - end - return '[any]' - end - local ktype = key:sub(1, 2) - key = key:sub(3) - if ktype == 's|' then - if key:match '^[%a_][%w_]*$' then - return key - else - return ('[%s]'):format(util.viewLiteral(key)) - end - end - return ('[%s]'):format(key) -end - -local function getFieldFast(src) - local value = guide.getObjectValue(src) or src - if not value then - return 'any' - end - if value.type == 'boolean' then - return value.type, util.viewLiteral(value[1]) - end - if value.type == 'number' - or value.type == 'integer' then - if math.tointeger(value[1]) then - if config.config.runtime.version == 'Lua 5.3' - or config.config.runtime.version == 'Lua 5.4' then - return 'integer', util.viewLiteral(value[1]) - end - end - return value.type, util.viewLiteral(value[1]) - end - if value.type == 'table' - or value.type == 'function' then - return value.type - end - if value.type == 'string' then - local literal = value[1] - if type(literal) == 'string' and #literal >= 50 then - literal = literal:sub(1, 47) .. '...' - end - return value.type, util.viewLiteral(literal) - end -end - -local function getFieldFull(src) - local tp = vm.getInferType(src) - --local class = vm.getClass(src) - local literal = vm.getInferLiteral(src) - if type(literal) == 'string' and #literal >= 50 then - literal = literal:sub(1, 47) .. '...' - end - return tp, literal -end - -local function getField(src, timeUp, mark, key) - if src.type == 'table' - or src.type == 'function' then - return nil - end - if src.parent then - if src.type == 'string' - or src.type == 'boolean' - or src.type == 'number' - or src.type == 'integer' then - if src.parent.type == 'tableindex' - or src.parent.type == 'setindex' - or src.parent.type == 'getindex' then - if src.parent.index == src then - src = src.parent - end - end - end - end - local tp, literal - tp, literal = getFieldFast(src) - if tp then - return tp, literal - end - if timeUp or mark[key] then - return nil - end - mark[key] = true - tp, literal = getFieldFull(src) - if tp then - return tp, literal - end - return nil -end - -local function buildAsHash(classes, literals) - local keys = {} - for k in pairs(classes) do - keys[#keys+1] = k - end - table.sort(keys) - local lines = {} - lines[#lines+1] = '{' - for _, key in ipairs(keys) do - local class = classes[key] - local literal = literals[key] - if literal then - lines[#lines+1] = (' %s: %s = %s,'):format(key, class, literal) - else - lines[#lines+1] = (' %s: %s,'):format(key, class) - end - end - lines[#lines+1] = '}' - return table.concat(lines, '\n') -end - -local function buildAsConst(classes, literals) - local keys = {} - for k in pairs(classes) do - keys[#keys+1] = k - end - table.sort(keys, function (a, b) - return tonumber(literals[a]) < tonumber(literals[b]) - end) - local lines = {} - lines[#lines+1] = '{' - for _, key in ipairs(keys) do - local class = classes[key] - local literal = literals[key] - if literal then - lines[#lines+1] = (' %s: %s = %s,'):format(key, class, literal) - else - lines[#lines+1] = (' %s: %s,'):format(key, class) - end - end - lines[#lines+1] = '}' - return table.concat(lines, '\n') -end - -local function mergeLiteral(literals) - local results = {} - local mark = {} - for _, value in ipairs(literals) do - if not mark[value] then - mark[value] = true - results[#results+1] = value - end - end - if #results == 0 then - return nil - end - table.sort(results) - return table.concat(results, '|') -end - -local function mergeTypes(types) - local results = {} - local mark = { - -- 讲道理table的keyvalue不会是nil - ['nil'] = true, - } - for _, tv in ipairs(types) do - for tp in tv:gmatch '[^|]+' do - if not mark[tp] then - mark[tp] = true - results[#results+1] = tp - end - end - end - return guide.mergeTypes(results) -end - -local function clearClasses(classes) - classes['[nil]'] = nil - classes['[any]'] = nil - classes['[string]'] = nil -end - -return function (source) - local literals = {} - local classes = {} - local clock = os.clock() - local timeUp - local mark = {} - local fields = vm.getFields(source, 'deep') - local keyCount = 0 - for _, src in ipairs(fields) do - local key = getKey(src) - if not key then - goto CONTINUE - end - if not classes[key] then - classes[key] = {} - keyCount = keyCount + 1 - end - if not literals[key] then - literals[key] = {} - end - if not TEST and os.clock() - clock > config.config.hover.fieldInfer / 1000.0 then - timeUp = true - end - local class, literal = getField(src, timeUp, mark, key) - if literal == 'nil' then - literal = nil - end - classes[key][#classes[key]+1] = class - literals[key][#literals[key]+1] = literal - if keyCount >= 1000 then - break - end - ::CONTINUE:: - end - - clearClasses(classes) - - for key, class in pairs(classes) do - literals[key] = mergeLiteral(literals[key]) - classes[key] = mergeTypes(class) - end - - if not next(classes) then - return '{}' - end - - local intValue = true - for key, class in pairs(classes) do - if class ~= 'integer' or not tonumber(literals[key]) then - intValue = false - break - end - end - local result - if intValue then - result = buildAsConst(classes, literals) - else - result = buildAsHash(classes, literals) - end - if timeUp then - result = ('\n--%s\n%s'):format(lang.script.HOVER_TABLE_TIME_UP, result) - end - return result -end |