summaryrefslogtreecommitdiff
path: root/script/core/view/psi-view.lua
blob: 86ed1403d1c77ecbfa4f57fac02b2b54161b8216 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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.uri, astNode.start)
    local finishPosition = converter.packPosition(state.uri, 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