summaryrefslogtreecommitdiff
path: root/script/core/workspace-symbol.lua
blob: f831aa0349746d7c5a2cd7976c4b801507a91236 (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
local files    = require 'files'
local guide    = require 'parser.guide'
local matchKey = require 'core.matchkey'
local define   = require 'proto.define'
local await    = require 'await'
local vm       = require 'vm'

local function buildSource(uri, source, key, results)
    if     source.type == 'local'
    or     source.type == 'setlocal'
    or     source.type == 'setglobal' then
        local name = source[1]
        if matchKey(key, name) then
            results[#results+1] = {
                name   = name,
                skind  = define.SymbolKind.Variable,
                ckind  = define.CompletionItemKind.Variable,
                source = source,
            }
        end
    elseif source.type == 'setfield'
    or     source.type == 'tablefield' then
        local field = source.field
        local name  = field and field[1]
        if name and matchKey(key, name) then
            results[#results+1] = {
                name   = name,
                skind  = define.SymbolKind.Field,
                ckind  = define.CompletionItemKind.Field,
                source = field,
            }
        end
    elseif source.type == 'setmethod' then
        local method = source.method
        local name   = method and method[1]
        if name and matchKey(key, name) then
            results[#results+1] = {
                name   = name,
                skind  = define.SymbolKind.Method,
                ckind  = define.CompletionItemKind.Method,
                source = method,
            }
        end
    end
end

local function searchFile(uri, key, results)
    local ast = files.getState(uri)
    if not ast then
        return
    end

    guide.eachSource(ast.ast, function (source)
        buildSource(uri, source, key, results)
    end)
end

---@param key string
---@param suri? uri
---@param results table[]
local function searchGlobalAndClass(key, suri, results)
    for _, global in pairs(vm.getAllGlobals()) do
        local name = global:getCodeName()
        if matchKey(key, name) then
            local sets
            if suri then
                sets = global:getSets(suri)
            else
                sets = global:getAllSets()
            end
            for _, set in ipairs(sets) do
                local skind, ckind
                if set.type == 'doc.class' then
                    skind = define.SymbolKind.Class
                    ckind = define.CompletionItemKind.Class
                elseif set.type == 'doc.alias' then
                    skind = define.SymbolKind.Struct
                    ckind = define.CompletionItemKind.Struct
                else
                    skind = define.SymbolKind.Variable
                    ckind = define.CompletionItemKind.Variable
                end
                results[#results+1] = {
                    name   = name,
                    skind  = skind,
                    ckind  = ckind,
                    source = set,
                }
            end
        end
    end
end

---@param key string
---@param suri? uri
---@param results table[]
local function searchClassField(key, suri, results)
    local class, inField = key:match('^(.+)%.(.-)$')
    if not class then
        return
    end
    local global = vm.getGlobal('type', class)
    if not global then
        return
    end
    local set
    if suri then
        set = global:getSets(suri)[1]
    else
        set = global:getAllSets()[1]
    end
    if not set then
        return
    end
    suri = suri or guide.getUri(set)
    vm.getClassFields(suri, global, vm.ANY, function (field)
        if field.type == 'generic' then
            return
        end
        ---@cast field -vm.generic
        local keyName = guide.getKeyName(field)
        if not keyName then
            return
        end
        if not matchKey(inField, keyName) then
            return
        end
        results[#results+1] = {
            name   = class .. '.' .. keyName,
            skind  = define.SymbolKind.Field,
            ckind  = define.SymbolKind.Field,
            source = field,
        }
    end)
end

---@param key string
---@param suri? uri
---@param results table[]
local function searchWords(key, suri, results)
    for uri in files.eachFile(suri) do
        searchFile(uri, key, results)
        if #results > 1000 then
            break
        end
    end
end

---@param key string
---@param suri? uri
---@param includeWords? boolean
return function (key, suri, includeWords)
    local results = {}

    searchGlobalAndClass(key, suri, results)
    searchClassField(key, suri, results)
    if includeWords then
        searchWords(key, suri, results)
    end

    return results
end