summaryrefslogtreecommitdiff
path: root/server/src/core/signature.lua
blob: 62aa5b3c707c4f1886f43881d82aa171eff7132a (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
local getFunctionHover = require 'core.hover.function'
local getFunctionHoverAsLib = require 'core.hover.lib_function'
local getFunctionHoverAsEmmy = require 'core.hover.emmy_function'
local findLib = require 'core.find_lib'
local buildValueName = require 'core.hover.name'
local findSource = require 'core.find_source'

local function findCall(vm, pos)
    local results = {}
    vm:eachSource(function (src)
        if      src.type == 'call'
            and src.start <= pos
            and src.finish >= pos
        then
            results[#results+1] = src
        end
    end)
    if #results == 0 then
        return nil
    end
    -- 可能处于 'func1(func2(' 的嵌套中,将最近的call放到最前面
    table.sort(results, function (a, b)
        return a.start > b.start
    end)
    return results
end

local function getSelect(args, pos)
    if not args then
        return 1
    end
    for i, arg in ipairs(args) do
        if arg.start <= pos and arg.finish >= pos - 1 then
            return i
        end
    end
    return #args + 1
end

local function getFunctionSource(call)
    local simple = call:get 'simple'
    for i, source in ipairs(simple) do
        if source == call then
            return simple[i-1]
        end
    end
    return nil
end

local function hovers(call, pos)
    local args = call:bindCall()
    if not args then
        return nil
    end

    local value = call:findCallFunction()
    if not value then
        return nil
    end

    local select = getSelect(args, pos)
    local source = getFunctionSource(call)
    local object = source:get 'object'
    local lib, fullkey = findLib(source)
    local name = fullkey or buildValueName(source)
    local hovers = {}
    if lib then
        hovers[#hovers+1] = getFunctionHoverAsLib(name, lib, object, select)
    else
        local emmy = value:getEmmy()
        if emmy and emmy.type == 'emmy.functionType' then
            hovers[#hovers+1] = getFunctionHoverAsEmmy(name, emmy, object, select)
        else
            ---@type emmyFunction
            local func = value:getFunction()
            hovers[#hovers+1] = getFunctionHover(name, func, object, select)
            local overLoads = func and func:getEmmyOverLoads()
            if overLoads then
                for _, ol in ipairs(overLoads) do
                    hovers[#hovers+1] = getFunctionHoverAsEmmy(name, ol, object, select)
                end
            end
        end
    end
    if #hovers == 0 then
        return nil
    end
    return hovers
end

local function isInFunctionOrTable(call, pos)
    local args = call:bindCall()
    if not args then
        return false
    end
    local select = getSelect(args, pos)
    local arg = args[select]
    if not arg then
        return false
    end
    if arg.type == 'function' or arg.type == 'table' then
        return true
    end
    return false
end

return function (vm, pos)
    local source = findSource(vm, pos) or findSource(vm, pos-1)
    if not source or source.type == 'string' then
        return
    end
    local calls = findCall(vm, pos)
    if not calls or #calls == 0 then
        return nil
    end

    local nearCall = calls[1]
    if isInFunctionOrTable(nearCall, pos) then
        return nil
    end

    local hovers = hovers(nearCall, pos)

    return hovers
end