--[[ Generates lua-ls annotations for lsp USAGE: nvim -l scripts/lsp_types.lua gen --runtime/lua/vim/lsp/types/protocol.lua --]] 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.loop.fs_stat('./lsp.json') then vim.fn.delete('./lsp.json') end vim.fn.system({ 'curl', 'https://raw.githubusercontent.com/microsoft/lsprotocol/main/generator/lsp.json', '-o', './lsp.json', }) local protocol = vim.fn.json_decode(vim.fn.readfile('./lsp.json')) vim.fn.delete('./lsp.json') local output_file = opt[1] 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', '---@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(output_file, table.concat(output, '\n')) end local opt = {} local index = 1 for _, a in ipairs(arg) do if vim.startswith(a, '--') then local name = a:sub(3) opt[index] = name index = index + 1 end end for _, a in ipairs(arg) do if M[a] then M[a](opt) end end return M