aboutsummaryrefslogtreecommitdiff
path: root/scripts/gen_lsp.lua
diff options
context:
space:
mode:
authorJongwook Choi <wookayin@gmail.com>2023-12-11 02:25:17 -0500
committerMathias Fußenegger <mfussenegger@users.noreply.github.com>2023-12-27 10:48:06 +0100
commit6c35fb421e888d0cbdfac07a5ff4579c9be7f0ec (patch)
tree03662f39cca7bcc11c7d1d8bb862dc9864828e87 /scripts/gen_lsp.lua
parent2f43af6423193eb52e9a6635034f8c79f40f1706 (diff)
downloadrneovim-6c35fb421e888d0cbdfac07a5ff4579c9be7f0ec.tar.gz
rneovim-6c35fb421e888d0cbdfac07a5ff4579c9be7f0ec.tar.bz2
rneovim-6c35fb421e888d0cbdfac07a5ff4579c9be7f0ec.zip
fix(gen_lsp.lua): improve type name, and fix wrong type inheritance
Style improvements: 1. Anonymous classes derived from `StructureLiteralType` should have a better name. The class name can be also nested. Examples: ```diff ----@field serverInfo? anonym1 +---@field serverInfo? lsp._anonym1.serverInfo ``` ```diff ----@field insertTextModeSupport? anonym26 +---@field insertTextModeSupport? lsp._anonym26.completionItem.insertTextModeSupport ``` 2. Add one separate empty line before each `@field` definition. Without these, empty lines the doc can look confusing because descriptions also may contain empty lines. See `lsp.CompletionItem` for example: ```lua ---The kind of this completion item. Based of the kind ---an icon is chosen by the editor. ---@field kind? lsp.CompletionItemKind ---Tags for this completion item. --- ---@since 3.15.0 ---@field tags? lsp.CompletionItemTag[] ``` It might feel like "Tags for this completion item" belongs to `kind`, not `tags` due to the lack of separator blank lines. The following (after this commit) should look much better: ```diff ---The kind of this completion item. Based of the kind ---an icon is chosen by the editor. ---@field kind? lsp.CompletionItemKind +--- ---Tags for this completion item. --- ---@since 3.15.0 ---@field tags? lsp.CompletionItemTag[] ``` 3. Escape some LSP-specific annotations that can't be recognized by lua-ls. It'd be better to make them visible in LSP hover doc windows. Example: `@sample ...`. Fixes: 1. A type may extend from more than one base types (as well as mixin types). Previously only the first base class was being considered, resulting incomplete base classes for `@class` definitions. Example: `InlayHintOptions` (should have both of `resolveProvider` and `workDoneProgress`, the latter is from `WorkDoneProgressOptions`) ```diff ----@class lsp.InlayHintOptions +---@class lsp.InlayHintOptions: lsp.WorkDoneProgressOptions ``` 2. Remove `<200b>` (zero-width space) unicode characters. 3. Add the missing newline at EOF.
Diffstat (limited to 'scripts/gen_lsp.lua')
-rw-r--r--scripts/gen_lsp.lua98
1 files changed, 60 insertions, 38 deletions
diff --git a/scripts/gen_lsp.lua b/scripts/gen_lsp.lua
index 943e88896b..0e7eb38cca 100644
--- a/scripts/gen_lsp.lua
+++ b/scripts/gen_lsp.lua
@@ -51,7 +51,7 @@ local function read_json(opt)
end
-- Gets the Lua symbol for a given fully-qualified LSP method name.
-local function name(s)
+local function to_luaname(s)
-- "$/" prefix is special: https://microsoft.github.io/language-server-protocol/specification/#dollarRequests
return s:gsub('^%$', 'dollar'):gsub('/', '_')
end
@@ -95,7 +95,7 @@ local function gen_methods(protocol)
---@type (vim._gen_lsp.Request|vim._gen_lsp.Notification)[]
local all = vim.list_extend(protocol.requests, protocol.notifications)
table.sort(all, function(a, b)
- return name(a.method) < name(b.method)
+ return to_luaname(a.method) < to_luaname(b.method)
end)
for _, item in ipairs(all) do
if item.method then
@@ -105,7 +105,7 @@ local function gen_methods(protocol)
output[#output + 1] = indent .. '--- ' .. docstring
end
end
- output[#output + 1] = ("%s%s = '%s',"):format(indent, name(item.method), item.method)
+ output[#output + 1] = ("%s%s = '%s',"):format(indent, to_luaname(item.method), item.method)
end
end
output[#output + 1] = '}'
@@ -193,6 +193,16 @@ function M.gen(opt)
'decimal',
}
+ ---@param documentation string
+ local _process_documentation = function(documentation)
+ documentation = documentation:gsub('\n', '\n---')
+ -- Remove <200b> (zero-width space) unicode characters: e.g., `**/<200b>*`
+ documentation = documentation:gsub('\226\128\139', '')
+ -- Escape annotations that are not recognized by lua-ls
+ documentation = documentation:gsub('%^---@sample', '---\\@sample')
+ return '---' .. documentation
+ end
+
--- @class vim._gen_lsp.Type
--- @field kind string a common field for all Types.
--- @field name? string for ReferenceType, BaseType
@@ -202,8 +212,10 @@ function M.gen(opt)
--- @field value? string|vim._gen_lsp.Type for StringLiteralType, MapType, StructureLiteralType
---@param type vim._gen_lsp.Type
+ ---@param prefix? string Optional prefix associated with the this type, made of (nested) field name.
+ --- Used to generate class name for structure literal types.
---@return string
- local function parse_type(type)
+ local function parse_type(type, prefix)
-- ReferenceType | BaseType
if type.kind == 'reference' or type.kind == 'base' then
if vim.tbl_contains(simple_types, type.name) then
@@ -213,13 +225,13 @@ function M.gen(opt)
-- ArrayType
elseif type.kind == 'array' then
- return parse_type(type.element) .. '[]'
+ return parse_type(type.element, prefix) .. '[]'
-- OrType
elseif type.kind == 'or' then
local val = ''
for _, item in ipairs(type.items) do
- val = val .. parse_type(item) .. '|' --[[ @as string ]]
+ val = val .. parse_type(item, prefix) .. '|' --[[ @as string ]]
end
val = val:sub(0, -2)
return val
@@ -232,7 +244,7 @@ function M.gen(opt)
elseif type.kind == 'map' then
local key = assert(type.key)
local value = type.value --[[ @as vim._gen_lsp.Type ]]
- return 'table<' .. parse_type(key) .. ', ' .. parse_type(value) .. '>'
+ return 'table<' .. parse_type(key, prefix) .. ', ' .. parse_type(value, prefix) .. '>'
-- StructureLiteralType
elseif type.kind == 'literal' then
@@ -240,7 +252,14 @@ function M.gen(opt)
-- 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 }
+ local anonymous_classname = 'lsp._anonym' .. anonymous_num
+ if prefix then
+ anonymous_classname = anonymous_classname .. '.' .. prefix
+ end
+ local anonym = vim.tbl_flatten { -- remove nil
+ anonymous_num > 1 and '' or nil,
+ '---@class ' .. anonymous_classname,
+ }
--- @class vim._gen_lsp.StructureLiteral translated to anonymous @class.
--- @field deprecated? string
@@ -252,27 +271,29 @@ function M.gen(opt)
---@type vim._gen_lsp.StructureLiteral
local structural_literal = assert(type.value) --[[ @as vim._gen_lsp.StructureLiteral ]]
for _, field in ipairs(structural_literal.properties) do
+ anonym[#anonym + 1] = '---'
if field.documentation then
- field.documentation = field.documentation:gsub('\n', '\n---')
- anonym[#anonym + 1] = '---' .. field.documentation
+ anonym[#anonym + 1] = _process_documentation(field.documentation)
end
anonym[#anonym + 1] = '---@field '
.. field.name
.. (field.optional and '?' or '')
.. ' '
- .. parse_type(field.type)
+ .. parse_type(field.type, prefix .. '.' .. field.name)
end
- anonym[#anonym + 1] = ''
+ -- anonym[#anonym + 1] = ''
for _, line in ipairs(anonym) do
- anonym_classes[#anonym_classes + 1] = line
+ if line then
+ anonym_classes[#anonym_classes + 1] = line
+ end
end
- return 'anonym' .. anonymous_num
+ return anonymous_classname
-- TupleType
elseif type.kind == 'tuple' then
local tuple = '{ '
for i, value in ipairs(type.items) do
- tuple = tuple .. '[' .. i .. ']: ' .. parse_type(value) .. ', '
+ tuple = tuple .. '[' .. i .. ']: ' .. parse_type(value, prefix) .. ', '
end
-- remove , at the end
tuple = tuple:sub(0, -3)
@@ -293,22 +314,22 @@ function M.gen(opt)
--- @field proposed? boolean
--- @field since? string
for _, structure in ipairs(protocol.structures) do
+ -- output[#output + 1] = ''
if structure.documentation then
- structure.documentation = structure.documentation:gsub('\n', '\n---')
- output[#output + 1] = '---' .. structure.documentation
+ output[#output + 1] = _process_documentation(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
+ local class_string = ('---@class lsp.%s'):format(structure.name)
+ if structure.extends or structure.mixins then
+ local inherits_from = table.concat(
+ vim.list_extend(
+ vim.tbl_map(parse_type, structure.extends or {}),
+ vim.tbl_map(parse_type, structure.mixins or {})
+ ),
+ ', '
+ )
+ class_string = class_string .. ': ' .. inherits_from
end
+ output[#output + 1] = class_string
--- @class vim._gen_lsp.Property translated to @field
--- @field deprecated? string
@@ -319,15 +340,15 @@ function M.gen(opt)
--- @field since? string
--- @field type { kind: string, name: string }
for _, field in ipairs(structure.properties or {}) do
+ output[#output + 1] = '---' -- Insert a single newline between @fields (and after @class)
if field.documentation then
- field.documentation = field.documentation:gsub('\n', '\n---')
- output[#output + 1] = '---' .. field.documentation
+ output[#output + 1] = _process_documentation(field.documentation)
end
output[#output + 1] = '---@field '
.. field.name
.. (field.optional and '?' or '')
.. ' '
- .. parse_type(field.type)
+ .. parse_type(field.type, field.name)
end
output[#output + 1] = ''
end
@@ -342,8 +363,7 @@ function M.gen(opt)
--- @field values { name: string, value: string, documentation?: string, since?: string }[]
for _, enum in ipairs(protocol.enumerations) do
if enum.documentation then
- enum.documentation = enum.documentation:gsub('\n', '\n---')
- output[#output + 1] = '---' .. enum.documentation
+ output[#output + 1] = _process_documentation(enum.documentation)
end
local enum_type = '---@alias lsp.' .. enum.name
for _, value in ipairs(enum.values) do
@@ -366,18 +386,20 @@ function M.gen(opt)
--- @field type vim._gen_lsp.Type
for _, alias in ipairs(protocol.typeAliases) do
if alias.documentation then
- alias.documentation = alias.documentation:gsub('\n', '\n---')
- output[#output + 1] = '---' .. alias.documentation
+ output[#output + 1] = _process_documentation(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) .. '|'
+ alias_type = alias_type .. parse_type(item, alias.name) .. '|'
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)
+ output[#output + 1] = '---@alias lsp.'
+ .. alias.name
+ .. ' '
+ .. parse_type(alias.type, alias.name)
end
output[#output + 1] = ''
end
@@ -387,7 +409,7 @@ function M.gen(opt)
output[#output + 1] = line
end
- tofile(opt.output_file, table.concat(output, '\n'))
+ tofile(opt.output_file, table.concat(output, '\n') .. '\n')
end
---@type vim._gen_lsp.opt