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
|
--[[
Generates lua-ls annotations for lsp
USAGE:
nvim -l scripts/lsp_types.lua gen # this will overwrite runtime/lua/vim/lsp/types/protocol.lua
nvim -l scripts/lsp_types.lua gen --build/new_lsp_types.lua
nvim -l scripts/lsp_types.lua gen --out runtime/lua/vim/lsp/types/protocol.lua --ref 2023.0.0a2 # specify a git reference from microsoft/lsprotocol
--]]
local M = {}
local function tofile(fname, text)
local f = io.open(fname, 'w')
if not f then
error(('failed to write: %s'):format(f))
else
f:write(text)
f:close()
end
end
function M.gen(opt)
if vim.uv.fs_stat('./lsp.json') then
vim.fn.delete('./lsp.json')
end
vim.fn.system({
'curl',
'https://raw.githubusercontent.com/microsoft/lsprotocol/' .. opt.ref .. '/generator/lsp.json',
'-o',
'./lsp.json',
})
local protocol = vim.fn.json_decode(vim.fn.readfile('./lsp.json'))
vim.fn.delete('./lsp.json')
protocol = protocol or {}
local output = {
'--[[',
'This file is autogenerated from scripts/lsp_types.lua',
'Regenerate:',
[=[nvim -l scripts/lsp_types.lua gen --runtime/lua/vim/lsp/types/protocol.lua]=],
'--]]',
'',
'---@alias lsp.null nil',
'---@alias uinteger integer',
'---@alias lsp.decimal number',
'---@alias lsp.DocumentUri string',
'---@alias lsp.URI string',
'---@alias lsp.LSPObject table<string, lsp.LSPAny>',
'---@alias lsp.LSPArray lsp.LSPAny[]',
'---@alias lsp.LSPAny lsp.LSPObject|lsp.LSPArray|string|number|boolean|nil',
'',
}
local anonymous_num = 0
local anonym_classes = {}
local simple_types = {
'string',
'boolean',
'integer',
'uinteger',
'decimal',
}
local function parse_type(type)
if type.kind == 'reference' or type.kind == 'base' then
if vim.tbl_contains(simple_types, type.name) then
return type.name
end
return 'lsp.' .. type.name
elseif type.kind == 'array' then
return parse_type(type.element) .. '[]'
elseif type.kind == 'or' then
local val = ''
for _, item in ipairs(type.items) do
val = val .. parse_type(item) .. '|'
end
val = val:sub(0, -2)
return val
elseif type.kind == 'stringLiteral' then
return '"' .. type.value .. '"'
elseif type.kind == 'map' then
return 'table<' .. parse_type(type.key) .. ', ' .. parse_type(type.value) .. '>'
elseif type.kind == 'literal' then
-- can I use ---@param disabled? {reason: string}
-- use | to continue the inline class to be able to add docs
-- https://github.com/LuaLS/lua-language-server/issues/2128
anonymous_num = anonymous_num + 1
local anonym = { '---@class anonym' .. anonymous_num }
for _, field in ipairs(type.value.properties) do
if field.documentation then
field.documentation = field.documentation:gsub('\n', '\n---')
anonym[#anonym + 1] = '---' .. field.documentation
end
anonym[#anonym + 1] = '---@field '
.. field.name
.. (field.optional and '?' or '')
.. ' '
.. parse_type(field.type)
end
anonym[#anonym + 1] = ''
for _, line in ipairs(anonym) do
anonym_classes[#anonym_classes + 1] = line
end
return 'anonym' .. anonymous_num
elseif type.kind == 'tuple' then
local tuple = '{ '
for i, value in ipairs(type.items) do
tuple = tuple .. '[' .. i .. ']: ' .. parse_type(value) .. ', '
end
-- remove , at the end
tuple = tuple:sub(0, -3)
return tuple .. ' }'
end
vim.print(type)
return ''
end
for _, structure in ipairs(protocol.structures) do
if structure.documentation then
structure.documentation = structure.documentation:gsub('\n', '\n---')
output[#output + 1] = '---' .. structure.documentation
end
if structure.extends then
local class_string = '---@class lsp.'
.. structure.name
.. ': '
.. parse_type(structure.extends[1])
for _, mixin in ipairs(structure.mixins or {}) do
class_string = class_string .. ', ' .. parse_type(mixin)
end
output[#output + 1] = class_string
else
output[#output + 1] = '---@class lsp.' .. structure.name
end
for _, field in ipairs(structure.properties or {}) do
if field.documentation then
field.documentation = field.documentation:gsub('\n', '\n---')
output[#output + 1] = '---' .. field.documentation
end
output[#output + 1] = '---@field '
.. field.name
.. (field.optional and '?' or '')
.. ' '
.. parse_type(field.type)
end
output[#output + 1] = ''
end
for _, enum in ipairs(protocol.enumerations) do
if enum.documentation then
enum.documentation = enum.documentation:gsub('\n', '\n---')
output[#output + 1] = '---' .. enum.documentation
end
local enum_type = '---@alias lsp.' .. enum.name
for _, value in ipairs(enum.values) do
enum_type = enum_type
.. '\n---| '
.. (type(value.value) == 'string' and '"' .. value.value .. '"' or value.value)
.. ' # '
.. value.name
end
output[#output + 1] = enum_type
output[#output + 1] = ''
end
for _, alias in ipairs(protocol.typeAliases) do
if alias.documentation then
alias.documentation = alias.documentation:gsub('\n', '\n---')
output[#output + 1] = '---' .. alias.documentation
end
if alias.type.kind == 'or' then
local alias_type = '---@alias lsp.' .. alias.name .. ' '
for _, item in ipairs(alias.type.items) do
alias_type = alias_type .. parse_type(item) .. '|'
end
alias_type = alias_type:sub(0, -2)
output[#output + 1] = alias_type
else
output[#output + 1] = '---@alias lsp.' .. alias.name .. ' ' .. parse_type(alias.type)
end
output[#output + 1] = ''
end
for _, line in ipairs(anonym_classes) do
output[#output + 1] = line
end
tofile(opt.output_file, table.concat(output, '\n'))
end
local opt = {
output_file = 'runtime/lua/vim/lsp/types/protocol.lua',
ref = 'main',
}
for i = 1, #_G.arg do
if _G.arg[i] == '--out' then
opt.output_file = _G.arg[i+1]
elseif _G.arg[i] == '--ref' then
opt.ref = _G.arg[i+1]
elseif vim.startswith(_G.arg[i], '--') then
opt.output_file = _G.arg[i]:sub(3)
end
end
for _, a in ipairs(arg) do
if M[a] then
M[a](opt)
end
end
return M
|