summaryrefslogtreecommitdiff
path: root/script-beta/core/hover
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2020-11-20 21:57:09 +0800
committer最萌小汐 <sumneko@hotmail.com>2020-11-20 21:57:09 +0800
commit4ca61ec457822dd14966afa0752340ae8ce180a1 (patch)
treeae8adb1ad82c717868e551e699fd3cf3bb290089 /script-beta/core/hover
parentc63b2e404d8d2bb984afe3678a5ba2b2836380cc (diff)
downloadlua-language-server-4ca61ec457822dd14966afa0752340ae8ce180a1.zip
no longer beta
Diffstat (limited to 'script-beta/core/hover')
-rw-r--r--script-beta/core/hover/arg.lua71
-rw-r--r--script-beta/core/hover/description.lua204
-rw-r--r--script-beta/core/hover/init.lua164
-rw-r--r--script-beta/core/hover/label.lua211
-rw-r--r--script-beta/core/hover/name.lua101
-rw-r--r--script-beta/core/hover/return.lua125
-rw-r--r--script-beta/core/hover/table.lua257
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