mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-03 17:24:29 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
				
					committed by
					
						
						Mathias Fußenegger
					
				
			
			
				
	
			
			
			
						parent
						
							2f43af6423
						
					
				
				
					commit
					6c35fb421e
				
			
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user