summaryrefslogtreecommitdiff
path: root/script/core/signature.lua
blob: d1fdd2463edc169641d1bd7a71fe90ecdd6eae08 (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
local files      = require 'files'
local guide      = require 'parser.guide'
local vm         = require 'vm'
local hoverLabel = require 'core.hover.label'
local hoverDesc  = require 'core.hover.description'

local function findNearCall(uri, ast, pos)
    local text = files.getText(uri)
    -- 检查 `f()$` 的情况,注意要区别于 `f($`
    if text:sub(pos, pos) == ')' then
        return nil
    end

    local nearCall
    guide.eachSourceContain(ast.ast, pos, function (src)
        if src.type == 'call'
        or src.type == 'table'
        or src.type == 'function' then
            if not nearCall or nearCall.start < src.start then
                nearCall = src
            end
        end
    end)
    if not nearCall then
        return nil
    end
    if nearCall.type ~= 'call' then
        return nil
    end
    return nearCall
end

local function makeOneSignature(source, oop, index)
    local label = hoverLabel(source, oop)
    -- 去掉返回值
    label = label:gsub('%s*->.+', '')
    local params = {}
    local i = 0
    for start, finish in label:gmatch '[%(%)%,]%s*().-()%s*%f[%(%)%,%[%]]' do
        i = i + 1
        params[i] = {
            label = {start, finish-1},
        }
    end
    -- 不定参数
    if index > i and i > 0 then
        local lastLabel = params[i].label
        local text = label:sub(lastLabel[1], lastLabel[2])
        if text == '...' then
            index = i
        end
    end
    return {
        label       = label,
        params      = params,
        index       = index,
        description = hoverDesc(source),
    }
end

local function makeSignatures(call, pos)
    local node = call.node
    local oop = node.type == 'method'
             or node.type == 'getmethod'
             or node.type == 'setmethod'
    local index
    local args = call.args
    if args then
        for i, arg in ipairs(args) do
            if arg.start <= pos and arg.finish >= pos then
                index = i
                break
            end
        end
        if not index then
            index = #args + 1
        end
    else
        index = 1
    end
    local signs = {}
    local defs = vm.getDefs(node, 0)
    for _, src in ipairs(defs) do
        if src.type == 'function'
        or src.type == 'doc.type.function' then
            signs[#signs+1] = makeOneSignature(src, oop, index)
        end
    end
    return signs
end

return function (uri, pos)
    local ast = files.getAst(uri)
    if not ast then
        return nil
    end
    local call = findNearCall(uri, ast, pos)
    if not call then
        return nil
    end
    local signs = makeSignatures(call, pos)
    if not signs or #signs == 0 then
        return nil
    end
    return signs
end