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
|
local files = require 'files'
local vm = require 'vm'
local hoverLabel = require 'core.hover.label'
local hoverDesc = require 'core.hover.description'
local guide = require 'parser.guide'
local lookback = require 'core.look-backward'
local function findNearCall(uri, ast, pos)
local text = files.getText(uri)
local state = files.getState(uri)
if not state or not text 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
local finishOffset = guide.positionToOffset(state, src.finish)
-- call(),$
if src.finish <= pos
and text:sub(finishOffset, finishOffset) == ')' then
return
end
-- {},$
if src.finish <= pos
and text:sub(finishOffset, finishOffset) == '}' then
return
end
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
---@async
local function makeOneSignature(source, oop, index)
local label = hoverLabel(source, oop)
if not label then
return nil
end
-- 去掉返回值
label = label:gsub('%s*->.+', '')
local params = {}
local i = 0
local argStart, argLabel = label:match '()(%b())$'
local converted = argLabel
: sub(2, -2)
: gsub('%b<>', function (str)
return ('_'):rep(#str)
end)
: gsub('%b()', function (str)
return ('_'):rep(#str)
end)
: gsub('%b{}', function (str)
return ('_'):rep(#str)
end)
: gsub('[%[%]%(%)]', '_')
for start, finish in converted:gmatch '%s*()[^,]+()' do
i = i + 1
params[i] = {
label = {start + argStart - 1, finish - 1 + argStart},
}
end
-- 不定参数
if index and index > i and i > 0 then
local lastLabel = params[i].label
local text = label:sub(lastLabel[1] + 1, lastLabel[2])
if text:sub(1, 3) == '...' then
index = i
end
end
if #params < (index or 0) then
return nil
end
return {
label = label,
params = params,
index = index or 1,
description = hoverDesc(source),
}
end
---@async
local function makeSignatures(text, call, pos)
local func = call.node
local oop = func.type == 'method'
or func.type == 'getmethod'
or func.type == 'setmethod'
local index
if call.args then
local args = {}
for _, arg in ipairs(call.args) do
if arg.type ~= 'self' then
args[#args+1] = arg
end
end
local uri = guide.getUri(call)
local state = files.getState(uri)
for i, arg in ipairs(args) do
local startOffset = guide.positionToOffset(state, arg.start)
startOffset = lookback.findTargetSymbol(text, startOffset, '(')
or lookback.findTargetSymbol(text, startOffset, ',')
or startOffset
local startPos = guide.offsetToPosition(state, startOffset)
if startPos > pos then
index = i - 1
break
end
if pos <= arg.finish then
index = i
break
end
end
if not index then
local offset = guide.positionToOffset(state, pos)
local backSymbol = lookback.findSymbol(text, offset)
if backSymbol == ','
or backSymbol == '(' then
index = #args + 1
else
index = #args
end
end
end
local signs = {}
local node = vm.compileNode(func)
---@type vm.node
node = node:getData 'originNode' or node
local mark = {}
for src in node:eachObject() do
if (src.type == 'function' and not vm.isVarargFunctionWithOverloads(src))
or src.type == 'doc.type.function' then
if not mark[src] then
mark[src] = true
signs[#signs+1] = makeOneSignature(src, oop, index)
end
end
end
return signs
end
---@async
return function (uri, pos)
local state = files.getState(uri)
local text = files.getText(uri)
if not state or not text then
return nil
end
local offset = guide.positionToOffset(state, pos)
pos = guide.offsetToPosition(state, lookback.skipSpace(text, offset))
local call = findNearCall(uri, state, pos)
if not call then
return nil
end
local signs = makeSignatures(text, call, pos)
if not signs or #signs == 0 then
return nil
end
table.sort(signs, function (a, b)
return #a.params < #b.params
end)
return signs
end
|