summaryrefslogtreecommitdiff
path: root/server/src/core/hover/hover.lua
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/core/hover/hover.lua')
-rw-r--r--server/src/core/hover/hover.lua326
1 files changed, 0 insertions, 326 deletions
diff --git a/server/src/core/hover/hover.lua b/server/src/core/hover/hover.lua
deleted file mode 100644
index 2ee5cf46..00000000
--- a/server/src/core/hover/hover.lua
+++ /dev/null
@@ -1,326 +0,0 @@
-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 = {
- ['any'] = true,
- ['nil'] = true,
- ['integer'] = true,
- ['number'] = true,
- ['boolean'] = true,
- ['string'] = true,
- ['thread'] = true,
- ['userdata'] = true,
- ['table'] = true,
- ['function'] = true,
-}
-
-local function longString(str)
- for i = 0, 10 do
- local finish = ']' .. ('='):rep(i) .. ']'
- if not str:find(finish, 1, true) then
- return ('[%s[\n%s%s'):format(('='):rep(i), str, finish)
- end
- end
- return ('%q'):format(str)
-end
-
-local function formatString(str)
- if #str > 1000 then
- str = str:sub(1000)
- end
- if str:find('[\r\n]') then
- str = str:gsub('[\000-\008\011-\012\014-\031\127]', '')
- return longString(str)
- else
- str = str:gsub('[\000-\008\011-\012\014-\031\127]', function (char)
- return ('\\%03d'):format(char:byte())
- end)
- local single = str:find("'", 1, true)
- local double = str:find('"', 1, true)
- if single and double then
- return longString(str)
- elseif double then
- return ("'%s'"):format(str)
- else
- return ('"%s"'):format(str)
- end
- end
-end
-
-local function formatLiteral(v)
- if math.type(v) == 'float' then
- return ('%.10f'):format(v):gsub('[0]*$', ''):gsub('%.$', '.0')
- elseif type(v) == 'string' then
- return formatString(v)
- else
- return ('%q'):format(v)
- end
-end
-
-local function findClass(value)
- -- 检查是否有emmy
- local emmy = value:getEmmy()
- if emmy then
- return emmy:getType()
- end
- -- 检查对象元表
- local metaValue = value:getMetaTable()
- if not metaValue then
- return nil
- end
- -- 检查元表中的 __name
- local metaName = metaValue:rawGet('__name')
- if metaName and type(metaName:getLiteral()) == 'string' then
- return metaName:getLiteral()
- end
- -- 检查元表的 __index
- local indexValue = metaValue:rawGet('__index')
- if not indexValue then
- return nil
- end
- -- 查找index方法中的以下字段: type name class
- -- 允许多重继承
- return indexValue:eachChild(function (k, v)
- -- 键值类型必须均为字符串
- if type(k) ~= 'string' then
- return
- end
- if type(v:getLiteral()) ~= 'string' then
- return
- end
- local lKey = k:lower()
- if lKey == 'type'
- or lKey == 'name'
- or lKey == 'class'
- then
- -- 必须只有过一次赋值
- local hasSet = false
- local ok = v:eachInfo(function (info)
- if info.type == 'set' then
- if hasSet then
- return false
- else
- hasSet = true
- end
- end
- end)
- if ok == false then
- return false
- end
- return v:getLiteral()
- end
- end)
-end
-
-local function formatKey(key)
- local kType = type(key)
- if kType == 'table' then
- key = ('[*%s]'):format(key:getType())
- elseif math.type(key) == 'integer' then
- key = ('[%03d]'):format(key)
- elseif kType == 'string' then
- if key:find '^%d' or key:find '[^%w_]' then
- key = ('[%s]'):format(formatString(key))
- end
- elseif key == '' then
- key = '[*any]'
- else
- key = ('[%s]'):format(key)
- end
- return key
-end
-
-local function unpackTable(value)
- local lines = {}
- value:eachChild(function (key, child)
- key = formatKey(key)
-
- local vType = type(child:getLiteral())
- if vType == 'boolean'
- or vType == 'integer'
- or vType == 'number'
- or vType == 'string'
- then
- lines[#lines+1] = ('%s: %s = %s'):format(key, child:getType(), formatLiteral(child:getLiteral()))
- else
- lines[#lines+1] = ('%s: %s'):format(key, child:getType())
- end
- end)
- local emmy = value:getEmmy()
- if emmy then
- if emmy.type == 'emmy.arrayType' then
- lines[#lines+1] = ('[*integer]: %s'):format(emmy:getName())
- elseif emmy.type == 'emmy.tableType' then
- lines[#lines+1] = ('[*%s]: %s'):format(emmy:getKeyType():getType(), emmy:getValueType():getType())
- end
- end
- if #lines == 0 then
- return '{}'
- end
-
- -- 整理一下表
- local cleaned = {}
- local used = {}
- for _, line in ipairs(lines) do
- if used[line] then
- goto CONTINUE
- end
- used[line] = true
- if line == '[*any]: any' then
- goto CONTINUE
- end
- cleaned[#cleaned+1] = ' ' .. line .. ','
- :: CONTINUE ::
- end
-
- table.sort(cleaned)
- table.insert(cleaned, 1, '{')
- cleaned[#cleaned+1] = '}'
- return table.concat(cleaned, '\r\n')
-end
-
-local function getValueHover(source, name, value, lib)
- local valueType = value:getType()
- local class = findClass(value)
-
- if class then
- valueType = class
- lib = nil
- end
-
- if not OriginTypes[valueType] then
- valueType = '*' .. valueType
- end
-
- local tips = {}
- local literal
- if lib then
- literal = lib.code or (lib.value and formatLiteral(lib.value))
- tips[#tips+1] = lib.description
- else
- literal = value:getLiteral() and formatLiteral(value:getLiteral())
- end
-
- tips[#tips+1] = value:getComment()
-
- local tp
- if source:bindLocal() then
- tp = 'local'
- local loc = source:bindLocal()
- if loc.tags then
- local mark = {}
- local tagBufs = {}
- for _, tag in ipairs(loc.tags) do
- local tagName = tag[1]
- if not mark[tagName] then
- mark[tagName] = true
- tagBufs[#tagBufs+1] = ('<%s>'):format(tagName)
- end
- end
- name = name .. ' ' .. table.concat(tagBufs, ' ')
- end
- tips[#tips+1] = loc:getComment()
- elseif source:get 'global' then
- tp = 'global'
- elseif source:get 'simple' then
- local simple = source:get 'simple'
- if simple[1]:get 'global' then
- tp = 'global'
- else
- tp = 'field'
- end
- else
- tp = 'field'
- end
-
- local text
- if valueType == 'table' then
- text = ('%s %s: %s'):format(tp, name, unpackTable(value))
- else
- if literal == nil then
- if class and not OriginTypes[class] then
- text = ('%s %s: %s %s'):format(tp, name, valueType, unpackTable(value))
- else
- text = ('%s %s: %s'):format(tp, name, valueType)
- end
- else
- text = ('%s %s: %s = %s'):format(tp, name, valueType, literal)
- end
- end
-
- local tip
- if #tips > 0 then
- tip = table.concat(tips, '\n\n-------------\n\n')
- end
- return {
- label = text,
- description = tip,
- }
-end
-
-local function hoverAsValue(source, lsp, select)
- local lib, fullkey = findLib(source)
- ---@type value
- local value = source:findValue()
- local name = fullkey or buildValueName(source)
-
- local hover
- if value:getType() == 'function' then
- local object = source:get 'object'
- if lib then
- hover = getFunctionHoverAsLib(name, lib, object, select)
- else
- 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)
- end
-
- if not hover then
- return nil
- end
- hover.name = name
- return hover
-end
-
-local function hoverAsTargetUri(source, lsp)
- local uri = source:get 'target uri'
- if not lsp or not lsp.workspace then
- return nil
- end
- local path = lsp.workspace:relativePathByUri(uri)
- if not path then
- return nil
- end
- return {
- description = ('[%s](%s)'):format(path:string(), uri),
- }
-end
-
-return function (source, lsp, select)
- if not source then
- return nil
- end
- if source:get 'target uri' then
- return hoverAsTargetUri(source, lsp)
- end
- if source.type == 'name' and source:bindValue() then
- return hoverAsValue(source, lsp, select)
- end
- if source.type == 'simple' then
- source = source[#source]
- if source.type == 'name' and source:bindValue() then
- return hoverAsValue(source, lsp, select)
- end
- end
- return nil
-end