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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
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
local function isEventNotMatch(call, src)
if not call.args or not src.args then
return false
end
local literal, index
for i = 1, 2 do
if not call.args[i] then
break
end
literal = guide.getLiteral(call.args[i])
if literal then
index = i
break
end
end
if not literal then
return false
end
local event = src.args[index]
if not event or event.type ~= 'doc.type.arg' then
return false
end
if not event.extends
or #event.extends.types ~= 1 then
return false
end
local eventLiteral = event.extends.types[1] and guide.getLiteral(event.extends.types[1])
if eventLiteral == nil then
return false
end
return eventLiteral ~= literal
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.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]
and not isEventNotMatch(call, src) then
mark[src] = true
signs[#signs+1] = makeOneSignature(src, oop, index)
end
elseif src.type == 'global' and src.cate == 'type' then
---@cast src vm.global
for _, set in ipairs(src:getSets(guide.getUri(call))) do
if set.type == 'doc.class' then
for _, overload in ipairs(set.calls) do
local f = overload.overload
if not mark[f]
and not isEventNotMatch(call, src) then
mark[f] = true
signs[#signs+1] = makeOneSignature(f, oop, index)
end
end
end
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
|