nixos/lua-lsp/script/core/view/psi-view.lua

97 lines
2.6 KiB
Lua

local files = require("files")
local guide = require("parser.guide")
local converter = require("proto.converter")
local subString = require 'core.substring'
---@class psi.view.node
---@field name string
---@field attr? psi.view.attr
---@field children? psi.view.node[]
---@class psi.view.attr
---@field range psi.view.range
---@class psi.view.range
---@field start integer
---@field end integer
---@param astNode parser.object
---@param state parser.state
---@return psi.view.node | nil
local function toPsiNode(astNode, state)
if not astNode or not astNode.start then
return
end
local startOffset = guide.positionToOffset(state, astNode.start)
local finishOffset = guide.positionToOffset(state, astNode.finish)
local startPosition = converter.packPosition(state, astNode.start)
local finishPosition = converter.packPosition(state, astNode.finish)
return {
name = string.format("%s@[%d:%d .. %d:%d]",
astNode.type,
startPosition.line + 1, startPosition.character + 1,
finishPosition.line + 1, finishPosition.character + 1),
attr = {
range = {
start = startOffset,
["end"] = finishOffset
}
}
}
end
---@param astNode parser.object
---@return psi.view.node | nil
local function collectPsi(astNode, state)
local psiNode = toPsiNode(astNode, state)
if not psiNode then
return
end
guide.eachChild(astNode, function(child)
if psiNode.children == nil then
psiNode.children = {}
end
local psi = collectPsi(child, state)
if psi then
psiNode.children[#psiNode.children+1] = psi
end
end)
if psiNode.children and #psiNode.children > 0 and psiNode.attr then
local range = psiNode.attr.range
if range.start > psiNode.children[1].attr.range.start then
range.start = psiNode.children[1].attr.range.start
end
if range["end"] < psiNode.children[#psiNode.children].attr.range["end"] then
range["end"] = psiNode.children[#psiNode.children].attr.range["end"]
end
end
if not psiNode.children then
local subber = subString(state)
local showText = subber(astNode.start + 1, astNode.finish)
if string.len(showText) > 30 then
showText = showText:sub(0, 30).. " ... "
end
psiNode.name = psiNode.name .. " " .. showText
end
return psiNode
end
return function(uri)
local state = files.getState(uri)
if not state then
return
end
return { data = collectPsi(state.ast, state) }
end