summaryrefslogtreecommitdiff
path: root/script/core/workspace-symbol.lua
blob: 6501708d0f4f029ec3f8ce15dc826107df02ce50 (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
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,
                kind  = define.SymbolKind.Variable,
                uri   = uri,
                range = { source.start, source.finish },
            }
        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,
                kind  = define.SymbolKind.Field,
                uri   = uri,
                range = { field.start, field.finish },
            }
        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,
                kind  = define.SymbolKind.Method,
                uri   = uri,
                range = { method.start, method.finish },
            }
        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

---@async
---@param key string
---@param results table[]
local function searchGlobalAndClass(key, results)
    for _, global in pairs(vm.getAllGlobals()) do
        local name = global:getCodeName()
        if matchKey(key, name) then
            for _, set in ipairs(global:getAllSets()) do
                local kind
                if set.type == 'doc.class' then
                    kind = define.SymbolKind.Class
                elseif set.type == 'doc.alias' then
                    kind = define.SymbolKind.Namespace
                else
                    kind = define.SymbolKind.Variable
                end
                results[#results+1] = {
                    name  = name,
                    kind  = kind,
                    uri   = guide.getUri(set),
                    range = { set.start, set.finish },
                }
            end
            await.delay()
        end
    end
end

---@async
---@param key string
---@param results table[]
local function searchClassField(key, 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 = global:getAllSets()[1]
    if not set then
        return
    end
    local suri = guide.getUri(set)
    vm.getClassFields(suri, global, nil, false, function (field, isMark)
        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,
            kind  = define.SymbolKind.Field,
            uri   = guide.getUri(field),
            range = { field.start, field.finish },
        }
    end)
end

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

---@async
return function (key)
    local results = {}

    searchGlobalAndClass(key, results)
    searchClassField(key, results)
    searchWords(key, results)

    return results
end