nixos/lua-lsp/script/vm/local-id.lua

243 lines
6.1 KiB
Lua
Raw Normal View History

local util = require 'utility'
local guide = require 'parser.guide'
---@class vm
local vm = require 'vm.vm'
---@class parser.object
---@field package _localID string|false
---@field package _localIDs table<string, { sets: parser.object[], gets: parser.object[] }>
local compileLocalID, getLocal
local compileSwitch = util.switch()
: case 'local'
: case 'self'
: call(function (source)
source._localID = ('%d'):format(source.start)
if not source.ref then
return
end
for _, ref in ipairs(source.ref) do
compileLocalID(ref)
end
end)
: case 'setlocal'
: case 'getlocal'
: call(function (source)
source._localID = ('%d'):format(source.node.start)
compileLocalID(source.next)
end)
: case 'getfield'
: case 'setfield'
: call(function (source)
local parentID = source.node._localID
if not parentID then
return
end
local key = guide.getKeyName(source)
if type(key) ~= 'string' then
return
end
source._localID = parentID .. vm.ID_SPLITE .. key
source.field._localID = source._localID
if source.type == 'getfield' then
compileLocalID(source.next)
end
end)
: case 'getmethod'
: case 'setmethod'
: call(function (source)
local parentID = source.node._localID
if not parentID then
return
end
local key = guide.getKeyName(source)
if type(key) ~= 'string' then
return
end
source._localID = parentID .. vm.ID_SPLITE .. key
source.method._localID = source._localID
if source.type == 'getmethod' then
compileLocalID(source.next)
end
end)
: case 'getindex'
: case 'setindex'
: call(function (source)
local parentID = source.node._localID
if not parentID then
return
end
local key = guide.getKeyName(source)
if type(key) ~= 'string' then
return
end
source._localID = parentID .. vm.ID_SPLITE .. key
source.index._localID = source._localID
if source.type == 'setindex' then
compileLocalID(source.next)
end
end)
local leftSwitch = util.switch()
: case 'field'
: case 'method'
: call(function (source)
return getLocal(source.parent)
end)
: case 'getfield'
: case 'setfield'
: case 'getmethod'
: case 'setmethod'
: case 'getindex'
: case 'setindex'
: call(function (source)
return getLocal(source.node)
end)
: case 'getlocal'
: call(function (source)
return source.node
end)
: case 'local'
: case 'self'
: call(function (source)
return source
end)
---@param source parser.object
---@return parser.object?
function getLocal(source)
return leftSwitch(source.type, source)
end
---@param id string
---@param source parser.object
function vm.insertLocalID(id, source)
local root = guide.getRoot(source)
if not root._localIDs then
root._localIDs = util.multiTable(2, function ()
return {
sets = {},
gets = {},
}
end)
end
local sources = root._localIDs[id]
if guide.isSet(source) then
sources.sets[#sources.sets+1] = source
else
sources.gets[#sources.gets+1] = source
end
end
function compileLocalID(source)
if not source then
return
end
source._localID = false
if not compileSwitch:has(source.type) then
return
end
compileSwitch(source.type, source)
local id = source._localID
if not id then
return
end
vm.insertLocalID(id, source)
end
---@param source parser.object
---@return string|false
function vm.getLocalID(source)
if source._localID ~= nil then
return source._localID
end
source._localID = false
local loc = getLocal(source)
if not loc then
return source._localID
end
compileLocalID(loc)
return source._localID
end
---@param source parser.object
---@param key? string
---@return parser.object[]?
function vm.getLocalSourcesSets(source, key)
local id = vm.getLocalID(source)
if not id then
return nil
end
local root = guide.getRoot(source)
if not root._localIDs then
return nil
end
if key then
if type(key) ~= 'string' then
return nil
end
id = id .. vm.ID_SPLITE .. key
end
return root._localIDs[id].sets
end
---@param source parser.object
---@param key? string
---@return parser.object[]?
function vm.getLocalSourcesGets(source, key)
local id = vm.getLocalID(source)
if not id then
return nil
end
local root = guide.getRoot(source)
if not root._localIDs then
return nil
end
if key then
if type(key) ~= 'string' then
return nil
end
id = id .. vm.ID_SPLITE .. key
end
return root._localIDs[id].gets
end
---@param source parser.object
---@param includeGets boolean
---@return parser.object[]?
function vm.getLocalFields(source, includeGets)
local id = vm.getLocalID(source)
if not id then
return nil
end
local root = guide.getRoot(source)
if not root._localIDs then
return nil
end
-- TODOoptimize
local clock = os.clock()
local fields = {}
for lid, sources in pairs(root._localIDs) do
if lid ~= id
and util.stringStartWith(lid, id)
and lid:sub(#id + 1, #id + 1) == vm.ID_SPLITE
-- only one field
and not lid:find(vm.ID_SPLITE, #id + 2) then
for _, src in ipairs(sources.sets) do
fields[#fields+1] = src
end
if includeGets then
for _, src in ipairs(sources.gets) do
fields[#fields+1] = src
end
end
end
end
local cost = os.clock() - clock
if cost > 1.0 then
log.warn('local-id getFields takes %.3f seconds', cost)
end
return fields
end