summaryrefslogtreecommitdiff
path: root/script/core/hover/hover.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/core/hover/hover.lua')
-rw-r--r--script/core/hover/hover.lua416
1 files changed, 0 insertions, 416 deletions
diff --git a/script/core/hover/hover.lua b/script/core/hover/hover.lua
deleted file mode 100644
index 7b99f8c0..00000000
--- a/script/core/hover/hover.lua
+++ /dev/null
@@ -1,416 +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 lang = require 'language'
-local config = require 'config'
-local uric = require 'uri'
-
-local function utf8Len(str, start, finish)
- local len, pos = utf8.len(str, start, finish, true)
- if len then
- return len
- end
- return 1 + utf8Len(str, start, pos-1) + utf8Len(str, pos+1, finish)
-end
-
-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 then
- return nil
- end
- local ws = lsp:findWorkspaceFor(uri)
- if ws then
- local path = ws:relativePathByUri(uri)
- if not path then
- return nil
- end
- return {
- description = ('[%s](%s)'):format(path:string(), uri),
- }
- else
- return {
- description = ('[%s](%s)'):format(uric.decode(uri):string(), uri),
- }
- end
-end
-
-local function hoverAsString(source)
- local str = source[1]
- if type(str) ~= 'string' then
- return nil
- end
- local len = #str
- local charLen = utf8Len(str, 1, -1)
- local lines = {}
- if len == charLen then
- lines[#lines+1] = lang.script('HOVER_STRING_BYTES', len)
- else
- lines[#lines+1] = lang.script('HOVER_STRING_CHARACTERS', len, charLen)
- end
- -- 内部包含转义符?
- local rawLen = source.finish - source.start - 2 * #source[2] + 1
- if config.config.hover.viewString
- and (source[2] == '"' or source[2] == "'")
- and rawLen > #str then
- local view = str
- local max = config.config.hover.viewStringMax
- if #view > max then
- view = view:sub(1, max) .. '...'
- end
- lines[#lines+1] = ([[
-
-------------------
-```txt
-%s
-```]]):format(view)
- end
- return {
- description = table.concat(lines, '\n'),
- range = {
- start = source.start,
- finish = source.finish,
- },
- }
-end
-
-local function formatNumber(n)
- local str = ('%.10f'):format(n)
- str = str:gsub('%.?0*$', '')
- return str
-end
-
-local function hoverAsNumber(source)
- if not config.config.hover.viewNumber then
- return nil
- end
- local num = source[1]
- if type(num) ~= 'number' then
- return nil
- end
- local raw = source[2]
- if not raw or not raw:find '[^%-%d%.]' then
- return nil
- end
- return {
- description = formatNumber(num),
- range = {
- start = source.start,
- finish = source.finish,
- },
- }
-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
- if source.type == 'string' then
- return hoverAsString(source)
- end
- if source.type == 'number' then
- return hoverAsNumber(source)
- end
- return nil
-end