summaryrefslogtreecommitdiff
path: root/server/src/core/definition.lua
blob: 87bdc0975d987bb660bfbb88d75d5c3fdb8874ce (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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
local function parseValueSimily(vm, source, lsp)
    local key = source[1]
    if not key then
        return nil
    end
    local positions = {}
    for _, other in ipairs(vm.sources) do
        if other == source then
            goto CONTINUE
        end
        if      other[1] == key
            and not other:bindLocal()
            and other:bindValue()
            and other:action() == 'set'
            and source:bindValue() ~= other:bindValue()
        then
            positions[#positions+1] = {
                other.start,
                other.finish,
            }
        end
        :: CONTINUE ::
    end
    if #positions == 0 then
        return nil
    end
    return positions
end

local function parseValueCrossFile(vm, source, lsp)
    local value = source:bindValue()
    local positions = {}
    value:eachInfo(function (info, src)
        if info.type == 'local' and src.uri == value.uri then
            positions[#positions+1] = {
                src.start,
                src.finish,
                value.uri,
            }
            return true
        end
    end)
    if #positions > 0 then
        return positions
    end

    value:eachInfo(function (info, src)
        if info.type == 'set' and src.uri == value.uri  then
            positions[#positions+1] = {
                src.start,
                src.finish,
                value.uri,
            }
        end
    end)
    if #positions > 0 then
        return positions
    end

    value:eachInfo(function (info, src)
        if info.type == 'return' and src.uri == value.uri then
            positions[#positions+1] = {
                src.start,
                src.finish,
                value.uri,
            }
        end
    end)
    if #positions > 0 then
        return positions
    end

    local destVM = lsp:getVM(value.uri)
    if not destVM then
        positions[#positions+1] = {
            0, 0, value.uri,
        }
        return positions
    end

    local result = parseValueSimily(destVM, source, lsp)
    if result then
        for _, position in ipairs(result) do
            positions[#positions+1] = position
            position[3] = value.uri
        end
    end
    if #positions > 0 then
        return positions
    end

    return positions
end

local function parseLocal(vm, source, lsp)
    local loc = source:bindLocal()
    local value = source:bindValue()
    if value.uri ~= vm.uri then
        return parseValueCrossFile(vm, source, lsp)
    end
    local positions = {}
    positions[#positions+1] = {
        loc:getSource().start,
        loc:getSource().finish,
        loc:getSource():getUri(),
    }
    if #positions == 0 then
        return nil
    end
    return positions
end

local function parseValue(vm, source, lsp)
    local positions = {}
    local mark = {}

    local function callback(src)
        if mark[src] then
            return
        end
        mark[src] = true
        if src.start == 0 then
            return
        end
        local uri = src.uri
        if uri == '' then
            uri = nil
        end
        positions[#positions+1] = {
            src.start,
            src.finish,
            uri,
        }
    end

    if source:bindValue() then
        source:bindValue():eachInfo(function (info, src)
            if info.type == 'set' or info.type == 'local' then
                callback(src)
            end
        end)
    end
    local parent = source:get 'parent'
    if parent then
        parent:eachInfo(function (info, src)
            if info[1] == source[1] then
                if info.type == 'set child' then
                    callback(src)
                end
            end
        end)
    end
    if #positions == 0 then
        return nil
    end
    return positions
end

local function parseLabel(vm, label, lsp)
    local positions = {}
    label:eachInfo(function (info, src)
        if info.type == 'set' then
            positions[#positions+1] = {
                src.start,
                src.finish,
            }
        end
    end)
    if #positions == 0 then
        return nil
    end
    return positions
end

local function jumpUri(vm, source, lsp)
    local uri = source:get 'target uri'
    local positions = {}
    positions[#positions+1] = {
        0, 0, uri,
    }
    return positions
end

return function (vm, source, lsp)
    if not source then
        return nil
    end
    if source:bindLocal() then
        return parseLocal(vm, source, lsp)
    end
    if source:bindValue() then
        return parseValue(vm, source, lsp)
            or parseValueSimily(vm, source, lsp)
    end
    if source:bindLabel() then
        return parseLabel(vm, source:bindLabel(), lsp)
    end
    if source:get 'target uri' then
        return jumpUri(vm, source, lsp)
    end
    if source:get 'in index' then
        return parseValue(vm, source, lsp)
            or parseValueSimily(vm, source, lsp)
    end
end