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