nixos/lua-lsp/script/core/hover/table.lua

226 lines
6 KiB
Lua

local vm = require 'vm'
local util = require 'utility'
local config = require 'config'
local await = require 'await'
local guide = require 'parser.guide'
---@param uri uri
---@param keys string[]
---@param nodeMap table<string, vm.node>
---@param reachMax integer
local function buildAsHash(uri, keys, nodeMap, reachMax)
local lines = {}
lines[#lines+1] = '{'
for _, key in ipairs(keys) do
local node = nodeMap[key]
local isOptional = node:isOptional()
if isOptional then
node = node:copy()
node:removeOptional()
end
local ifr = vm.getInfer(node)
local typeView = ifr:view(uri, 'unknown')
local literalView = ifr:viewLiterals()
if literalView then
lines[#lines+1] = (' %s%s: %s = %s,'):format(
key,
isOptional and '?' or '',
typeView,
literalView
)
else
lines[#lines+1] = (' %s%s: %s,'):format(
key,
isOptional and '?' or '',
typeView
)
end
end
if reachMax > 0 then
lines[#lines+1] = (' ...(+%d)'):format(reachMax)
end
lines[#lines+1] = '}'
return table.concat(lines, '\n')
end
---@param uri uri
---@param keys string[]
---@param nodeMap table<string, vm.node>
---@param reachMax integer
local function buildAsConst(uri, keys, nodeMap, reachMax)
local literalMap = {}
for _, key in ipairs(keys) do
literalMap[key] = vm.getInfer(nodeMap[key]):viewLiterals()
end
table.sort(keys, function (a, b)
return tonumber(literalMap[a]) < tonumber(literalMap[b])
end)
local lines = {}
lines[#lines+1] = '{'
for _, key in ipairs(keys) do
local node = nodeMap[key]
local isOptional = node:isOptional()
if isOptional then
node = node:copy()
node:removeOptional()
end
local typeView = vm.getInfer(node):view(uri, 'unknown')
local literalView = literalMap[key]
if literalView then
lines[#lines+1] = (' %s%s: %s = %s,'):format(
key,
isOptional and '?' or '',
typeView,
literalView
)
else
lines[#lines+1] = (' %s%s: %s,'):format(
key,
isOptional and '?' or '',
typeView
)
end
end
if reachMax > 0 then
lines[#lines+1] = (' ...(+%d)'):format(reachMax)
end
lines[#lines+1] = '}'
return table.concat(lines, '\n')
end
---@param source parser.object
---@param fields parser.object[]
local function getVisibleKeyMap(source, fields)
local uri = guide.getUri(source)
local keys = {}
local map = {}
local ignored = {}
for _, field in ipairs(fields) do
local key = vm.viewKey(field, uri)
local rawKey = guide.getKeyName(field)
if rawKey and rawKey ~= key then
ignored[rawKey] = true
map[rawKey] = nil
end
if not ignored[key]
and vm.isVisible(source, field) then
if key and not map[key] then
map[key] = true
end
end
end
for key in pairs(map) do
keys[#keys+1] = key
end
table.sort(keys, function (a, b)
if a == b then
return false
end
local s1 = 0
local s2 = 0
if a:sub(1, 1) == '_' then
s1 = s1 + 10
end
if b:sub(1, 1) == '_' then
s2 = s2 + 10
end
if a:sub(1, 1) == '[' then
s1 = s1 + 1
end
if b:sub(1, 1) == '[' then
s2 = s2 + 1
end
if s1 == s2 then
return a < b
end
return s1 < s2
end)
return keys, map
end
---@async
local function getNodeMap(uri, fields, keyMap)
---@type table<string, vm.node>
local nodeMap = {}
for _, field in ipairs(fields) do
local key = vm.viewKey(field, uri)
if not key or not keyMap[key] then
goto CONTINUE
end
await.delay()
local node = vm.compileNode(field)
if nodeMap[key] then
nodeMap[key]:merge(node)
else
nodeMap[key] = node:copy()
end
::CONTINUE::
end
return nodeMap
end
---@async
---@return string?
return function (source)
local uri = guide.getUri(source)
local maxFields = config.get(uri, 'Lua.hover.previewFields')
if maxFields <= 0 then
return nil
end
local node = vm.compileNode(source)
for n in node:eachObject() do
if n.type == 'global' and n.cate == 'type' then
if n.name == 'string'
or (n.name ~= 'unknown' and n.name ~= 'any' and vm.isSubType(uri, n.name, 'string')) then
return nil
end
elseif n.type == 'doc.type.string'
or n.type == 'string' then
return nil
elseif n.type == 'doc.type.sign' then
return nil
end
end
local fields = vm.getFields(source)
local keys, map = getVisibleKeyMap(source, fields)
if #keys == 0 then
return nil
end
local reachMax = #keys - maxFields
if #keys > maxFields then
for i = maxFields + 1, #keys do
map[keys[i]] = nil
keys[i] = nil
end
end
local nodeMap = getNodeMap(uri, fields, map)
local isConsts = true
for i = 1, #keys do
await.delay()
local key = keys[i]
local literal = vm.getInfer(nodeMap[key]):viewLiterals()
if not tonumber(literal) then
isConsts = false
end
end
local result
if isConsts then
result = buildAsConst(uri, keys, nodeMap, reachMax)
else
result = buildAsHash(uri, keys, nodeMap, reachMax)
end
--if timeUp then
-- result = ('\n--%s\n%s'):format(lang.script.HOVER_TABLE_TIME_UP, result)
--end
return result
end