diff options
Diffstat (limited to 'script/core/hover/hover.lua')
-rw-r--r-- | script/core/hover/hover.lua | 416 |
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 |