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
|
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)
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)
-- 去掉返回值
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('[%[%]%(%)]', '_')
for start, finish in converted:gmatch '%s*()[^,]+()' do
i = i + 1
params[i] = {
label = {start + argStart - 1, finish - 1 + argStart},
}
end
-- 不定参数
if 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
return {
label = label,
params = params,
index = index,
description = hoverDesc(source),
}
end
---@async
local function makeSignatures(text, call, pos)
local node = call.node
local oop = node.type == 'method'
or node.type == 'getmethod'
or node.type == 'setmethod'
local index
if call.args then
local args = {}
for _, arg in ipairs(call.args) do
if not arg.dummy 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
else
index = 1
end
local signs = {}
local defs = vm.getDefs(node)
local mark = {}
for _, src in ipairs(defs) do
if src.type == 'function'
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)
if not state then
return nil
end
local text = files.getText(uri)
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
return signs
end
|