summaryrefslogtreecommitdiff
path: root/server/src/core/document_symbol.lua
blob: 8d5059b3bda2b689acf0a5fc1bcc11fce8bedb55 (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
local hoverFunction = require 'core.hover_function'
local hoverName = require 'core.hover_name'
local hover = require 'core.hover'

local SymbolKind = {
    File = 1,
    Module = 2,
    Namespace = 3,
    Package = 4,
    Class = 5,
    Method = 6,
    Property = 7,
    Field = 8,
    Constructor = 9,
    Enum = 10,
    Interface = 11,
    Function = 12,
    Variable = 13,
    Constant = 14,
    String = 15,
    Number = 16,
    Boolean = 17,
    Array = 18,
    Object = 19,
    Key = 20,
    Null = 21,
    EnumMember = 22,
    Struct = 23,
    Event = 24,
    Operator = 25,
    TypeParameter = 26,
}

local function buildFunction(vm, func)
    local source = func.source
    local declarat = func:getDeclarat()
    local name
    local var
    if declarat then
        if declarat.type == 'function' then
            var = declarat.name and declarat.name.bind
        else
            var = declarat.bind
        end
    end
    if var then
        name = hoverName(var, declarat)
    else
        name = ''
    end
    local hvr = hoverFunction(name, func, declarat and declarat.object)
    if not hvr then
        return nil
    end
    local selectionRange
    local range
    local kind = SymbolKind.Function
    if var then
        range = { math.min(source.start, declarat.start), source.finish }
        selectionRange = { declarat.start, declarat.finish }
        if var.parent and var.parent.value and not var.parent.value.GLOBAL then
            kind = SymbolKind.Field
        end
    else
        range = { source.start, source.finish }
        selectionRange = { source.start, source.start }
    end

    return {
        name = name,
        -- 前端不支持多行
        detail = hvr.label:gsub('[\r\n]', ''),
        kind = kind,
        range = range,
        selectionRange = selectionRange,
    }
end

local function isLocalTable(var, source)
    if not var.value or var.value:getType() ~= 'table' then
        return false
    end
    if var.value.source.start == 0 then
        return false
    end
    if source ~= var.value:getDeclarat() then
        return false
    end
    if var.value.source.finish < source.finish then
        return false
    end
    return true
end

local function buildVar(vm, var, source)
    if source.start == 0 then
        return nil
    end
    if var.value and var.value:getType() == 'function' and var.value.uri == vm.uri then
        return nil
    end
    if var.hide then
        return nil
    end
    local key = var.key
    if key == '_' then
        return nil
    end
    if type(key) ~= 'string' then
        key = ('[%s]'):format(key)
    end
    local range
    if isLocalTable(var, source) then
        range = { source.start, var.value.source.finish }
    else
        range = { source.start, source.finish }
    end
    local hvr = hover(var, source)
    if not hvr then
        return nil
    end
    local kind
    if source.isIndex then
        kind = SymbolKind.Class
    else
        kind = SymbolKind.Variable
    end
    return {
        name = key,
        -- 前端不支持多行
        detail = hvr.label:gsub('[\r\n]', ''),
        kind = kind,
        range = range,
        selectionRange = { source.start, source.finish },
    }
end

local function packChild(symbols, finish, kind)
    local t
    while true do
        local symbol = symbols[#symbols]
        if not symbol then
            break
        end
        if symbol.range[1] > finish then
            break
        end
        symbols[#symbols] = nil
        symbol.children = packChild(symbols, symbol.range[2], symbol.kind)
        if symbol.kind == SymbolKind.Class and kind == SymbolKind.Function then
        else
            if not t then
                t = {}
            end
            t[#t+1] = symbol
        end
    end
    return t
end

local function packSymbols(symbols)
    -- 按照start位置反向排序
    table.sort(symbols, function (a, b)
        return a.range[1] > b.range[1]
    end)
    -- 处理嵌套
    return packChild(symbols, math.maxinteger, SymbolKind.Function)
end

return function (vm)
    local symbols = {}

    for _, func in ipairs(vm.results.funcs) do
        symbols[#symbols+1] = buildFunction(vm, func)
    end
    for _, source in ipairs(vm.results.sources) do
        if source.bind then
            if source.isLocal or source.isIndex then
                symbols[#symbols+1] = buildVar(vm, source.bind, source)
            end
        end
    end

    local packedSymbols = packSymbols(symbols)

    return packedSymbols
end