diff options
| author | Ori Perry <48057913+oriori1703@users.noreply.github.com> | 2026-10-05 14:02:24 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-10-05 07:02:24 -0400 |
| commit | 0f9e596d896c4260cd7f6a59e90b04713eb948db (patch) | |
| tree | 8c6a98b4869c456ec99861e76fbe2239690b0876 | |
| parent | docs: update configs.md (diff) | |
| download | nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.tar nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.tar.gz nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.tar.bz2 nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.tar.lz nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.tar.xz nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.tar.zst nvim-lspconfig-0f9e596d896c4260cd7f6a59e90b04713eb948db.zip | |
build(gen): type annotations for emmylua_ls #4534
| -rw-r--r-- | lua/lspconfig/types/lsp/emmylua_ls.lua | 681 | ||||
| -rw-r--r-- | scripts/gen_annotations.lua | 30 | ||||
| -rw-r--r-- | scripts/gen_json_schemas.lua | 81 |
3 files changed, 779 insertions, 13 deletions
diff --git a/lua/lspconfig/types/lsp/emmylua_ls.lua b/lua/lspconfig/types/lsp/emmylua_ls.lua new file mode 100644 index 00000000..332d682b --- /dev/null +++ b/lua/lspconfig/types/lsp/emmylua_ls.lua @@ -0,0 +1,681 @@ +---@meta + +---```lua +---default = {} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.CodeAction +---Add space after `---` comments when inserting `@diagnostic disable-next-line`. +--- +---When omitted, this follows the formatter's resolved +---`emmy_doc.space_between_tag_columns` setting. +---@field insertSpace? boolean + +---```lua +---default = { +--- enable = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.CodeLens +---Enable code lens. +--- +---```lua +---default = true +---``` +---@field enable? boolean + +---Configuration for EmmyLua code completion. +--- +---```lua +---default = { +--- autoRequire = true, +--- autoRequireFunction = "require", +--- autoRequireNamingConvention = "keep", +--- autoRequireSeparator = ".", +--- baseFunctionIncludesName = true, +--- callSnippet = false, +--- enable = true, +--- postfix = "@" +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Completion +---Automatically insert call to `require` when autocompletion +---inserts objects from other modules. +--- +---```lua +---default = true +---``` +---@field autoRequire? boolean +---The function used for auto-requiring modules. +--- +---```lua +---default = "require" +---``` +---@field autoRequireFunction? string +---The naming convention for auto-required filenames. +--- +---```lua +---default = "keep" +---``` +---@field autoRequireNamingConvention? "keep"|"snake-case"|"pascal-case"|"camel-case"|"keep-class" +---A separator used in auto-require paths. +--- +---```lua +---default = "." +---``` +---@field autoRequireSeparator? string +---Whether to include the name in the base function completion. Effect: `function () end` -> `function name() end`. +--- +---```lua +---default = true +---``` +---@field baseFunctionIncludesName? boolean +---Whether to use call snippets in completions. +---@field callSnippet? boolean +---Enable autocompletion. +--- +---```lua +---default = true +---``` +---@field enable? boolean +---Symbol that's used to trigger postfix autocompletion. +--- +---```lua +---default = "@" +---``` +---@field postfix? string + +---Represents the diagnostic configuration for Emmyrc. +--- +---```lua +---default = { +--- diagnosticInterval = 500, +--- disable = {}, +--- enable = true, +--- enables = {}, +--- globals = {}, +--- globalsRegex = {}, +--- severity = vim.empty_dict() +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Diagnostics +---Delay between opening/changing a file and scanning it for errors, in milliseconds. +---@field diagnosticInterval? integer +---A list of diagnostic codes that are disabled. +--- +---```lua +---default = {} +---``` +---@field disable? ("none"|"syntax-error"|"doc-syntax-error"|"type-not-found"|"missing-return"|"param-type-mismatch"|"missing-parameter"|"redundant-parameter"|"unreachable-code"|"unused"|"undefined-global"|"deprecated"|"access-invisible"|"discard-returns"|"undefined-field"|"local-const-reassign"|"iter-variable-reassign"|"duplicate-type"|"redefined-local"|"redefined-label"|"code-style-check"|"need-check-nil"|"await-in-sync"|"annotation-usage-error"|"return-type-mismatch"|"missing-return-value"|"redundant-return-value"|"undefined-doc-param"|"duplicate-doc-field"|"unknown-doc-tag"|"missing-fields"|"inject-field"|"circle-doc-class"|"incomplete-signature-doc"|"missing-global-doc"|"assign-type-mismatch"|"duplicate-require"|"non-literal-expressions-in-assert"|"unbalanced-assignments"|"unnecessary-assert"|"unnecessary-if"|"duplicate-set-field"|"duplicate-index"|"generic-constraint-mismatch"|"cast-type-mismatch"|"unresolved-require"|"require-module-not-visible"|"enum-value-mismatch"|"preferred-local-alias"|"read-only"|"global-in-non-module"|"attribute-param-type-mismatch"|"attribute-missing-parameter"|"attribute-redundant-parameter"|"invert-if"|"call-non-callable"|"inconsistent-type-access-modifier"|"missing-type-argument")[] +---A flag indicating whether diagnostics are enabled. +--- +---```lua +---default = true +---``` +---@field enable? boolean +---A list of diagnostic codes that are enabled. +--- +---```lua +---default = {} +---``` +---@field enables? ("none"|"syntax-error"|"doc-syntax-error"|"type-not-found"|"missing-return"|"param-type-mismatch"|"missing-parameter"|"redundant-parameter"|"unreachable-code"|"unused"|"undefined-global"|"deprecated"|"access-invisible"|"discard-returns"|"undefined-field"|"local-const-reassign"|"iter-variable-reassign"|"duplicate-type"|"redefined-local"|"redefined-label"|"code-style-check"|"need-check-nil"|"await-in-sync"|"annotation-usage-error"|"return-type-mismatch"|"missing-return-value"|"redundant-return-value"|"undefined-doc-param"|"duplicate-doc-field"|"unknown-doc-tag"|"missing-fields"|"inject-field"|"circle-doc-class"|"incomplete-signature-doc"|"missing-global-doc"|"assign-type-mismatch"|"duplicate-require"|"non-literal-expressions-in-assert"|"unbalanced-assignments"|"unnecessary-assert"|"unnecessary-if"|"duplicate-set-field"|"duplicate-index"|"generic-constraint-mismatch"|"cast-type-mismatch"|"unresolved-require"|"require-module-not-visible"|"enum-value-mismatch"|"preferred-local-alias"|"read-only"|"global-in-non-module"|"attribute-param-type-mismatch"|"attribute-missing-parameter"|"attribute-redundant-parameter"|"invert-if"|"call-non-callable"|"inconsistent-type-access-modifier"|"missing-type-argument")[] +---A list of global variables. +--- +---```lua +---default = {} +---``` +---@field globals? string[] +---A list of regular expressions for global variables. +--- +---```lua +---default = {} +---``` +---@field globalsRegex? string[] +---A map of diagnostic codes to their severity settings. +--- +---```lua +---default = {} +---``` +---@field severity? table + +---```lua +---default = { +--- knownTags = {}, +--- privateName = {}, +--- syntax = "md" +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Doc +---List of known documentation tags. +--- +---```lua +---default = {} +---``` +---@field knownTags? string[] +---Treat specific field names as private, e.g. `m_*` means `XXX.m_id` and `XXX.m_type` are private, witch can only be accessed in the class where the definition is located. +--- +---```lua +---default = {} +---``` +---@field privateName? string[] +---When `syntax` is `Myst` or `Rst`, specifies default role used +---with RST processor. +---@field rstDefaultRole? string +---When `syntax` is `Myst` or `Rst`, specifies primary domain used +---with RST processor. +---@field rstPrimaryDomain? string +---Syntax for highlighting documentation. +--- +---```lua +---default = "md" +---``` +---@field syntax? "none" | "md" | "myst" | "rst" + +---```lua +---default = { +--- enable = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.DocumentColor +---Enable parsing strings for color tags and showing a color picker next to them. +--- +---```lua +---default = true +---``` +---@field enable? boolean + +---```lua +---default = { +--- useDiff = false +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Format +---Whether to enable external tool formatting. +---@field externalTool? table +---Whether to enable external tool range formatting. +---@field externalToolRangeFormat? table +---Whether to use the diff algorithm for formatting. +---@field useDiff? boolean + +---```lua +---default = { +--- enable = true, +--- enumParamHint = false, +--- indexHint = true, +--- localHint = true, +--- metaCallHint = true, +--- overrideHint = true, +--- paramHint = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Hint +---Enable inlay hints. +--- +---```lua +---default = true +---``` +---@field enable? boolean +---Show name of enumerator when passing a literal value to a function +---that expects an enum. +--- +---Example: +--- +---```lua +------ @enum Level +---local Foo = { +--- Info = 1, +--- Error = 2, +---} +--- +------ @param l Level +---function print_level(l) end +--- +---print_level(1 --[[ Hint: Level.Info ]]) +---``` +---@field enumParamHint? boolean +---Show named array indexes. +--- +---Example: +--- +---```lua +---local array = { +--- [1] = 1, -- [name] +---} +--- +---print(array[1] --[[ Hint: name ]]) +---``` +--- +---```lua +---default = true +---``` +---@field indexHint? boolean +---Show types of local variables. +--- +---```lua +---default = true +---``` +---@field localHint? boolean +---Show hint when calling an object results in a call to +---its meta table's `__call` function. +--- +---```lua +---default = true +---``` +---@field metaCallHint? boolean +---Show methods that override functions from base class. +--- +---```lua +---default = true +---``` +---@field overrideHint? boolean +---Show parameter names in function calls and parameter types in function definitions. +--- +---```lua +---default = true +---``` +---@field paramHint? boolean + +---```lua +---default = { +--- enable = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Hover +---The detail number of hover information. +---Default is `None`, which means using the default detail level. +---You can set it to a number between `1` and `255` to customize +---@field customDetail? integer +---Enable showing documentation on hover. +--- +---```lua +---default = true +---``` +---@field enable? boolean + +---```lua +---default = { +--- enable = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.InlineValues +---Show inline values during debug. +--- +---```lua +---default = true +---``` +---@field enable? boolean + +---```lua +---default = { +--- enable = true, +--- fuzzySearch = true, +--- shortStringSearch = false +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.References +---Enable searching for symbol usages. +--- +---```lua +---default = true +---``` +---@field enable? boolean +---Use fuzzy search when searching for symbol usages +---and normal search didn't find anything. +--- +---```lua +---default = true +---``` +---@field fuzzySearch? boolean +---Also search for usages in strings. +---@field shortStringSearch? boolean + +---```lua +---default = { +--- paths = {} +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Resource +---```lua +---default = {} +---``` +---@field paths? string[] + +---```lua +---default = { +--- extensions = {}, +--- frameworkVersions = {}, +--- nonstandardSymbol = {}, +--- requireLikeFunction = {}, +--- requirePattern = {}, +--- special = vim.empty_dict(), +--- version = "LuaLatest" +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Runtime +---file Extensions. eg: .lua, .lua.txt +--- +---```lua +---default = {} +---``` +---@field extensions? string[] +---Framework versions. +--- +---```lua +---default = {} +---``` +---@field frameworkVersions? string[] +---Non-standard symbols. +--- +---```lua +---default = {} +---``` +---@field nonstandardSymbol? ("//" | "/**/" | "`" | "+=" | "-=" | "*=" | "/=" | "%=" | "^=" | "//=" | "|=" | "&=" | "<<=" | ">>=" | "||" | "&&" | "!" | "!=" | "continue")[] +---Functions that like require. +--- +---```lua +---default = {} +---``` +---@field requireLikeFunction? string[] +---Require pattern. eg. "?.lua", "?/init.lua" +--- +---```lua +---default = {} +---``` +---@field requirePattern? string[] +---Special symbols. +--- +---```lua +---default = {} +---``` +---@field special? table +---Lua version. +--- +---```lua +---default = "LuaLatest" +---``` +---@field version? "LuaJIT2" | "LuaJIT3"|"Lua5.1"|"LuaJIT"|"Lua5.2"|"Lua5.3"|"Lua5.4"|"Lua5.5"|"LuaLatest" + +---```lua +---default = { +--- enable = true, +--- renderDocumentationMarkup = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.SemanticTokens +---Enable semantic tokens. +--- +---```lua +---default = true +---``` +---@field enable? boolean +---Render Markdown/RST in documentation. Set `doc.syntax` for this option to have effect. +---@field renderDocumentationMarkup? boolean + +---```lua +---default = { +--- detailSignatureHelper = true +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Signature +---Whether to enable signature help. +--- +---```lua +---default = true +---``` +---@field detailSignatureHelper? boolean + +---```lua +---default = { +--- arrayIndex = true, +--- docBaseConstMatchBaseType = true, +--- metaOverrideFileDefine = true, +--- requirePath = false, +--- typeCall = false +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Strict +---Whether to enable strict mode array indexing. +--- +---```lua +---default = true +---``` +---@field arrayIndex? boolean +---Base constant types defined in doc can match base types, allowing int to match `---@alias id 1|2|3`, same for string. +---@field docBaseConstMatchBaseType? boolean +---meta define overrides file define +--- +---```lua +---default = true +---``` +---@field metaOverrideFileDefine? boolean +---Whether to enable strict mode require path. +---@field requirePath? boolean +---@field typeCall? boolean + +---```lua +---default = { +--- enableReindex = false, +--- encoding = "utf-8", +--- ignoreDir = {}, +--- ignoreGlobs = {}, +--- library = {}, +--- moduleMap = {}, +--- packages = {}, +--- preloadFileSize = 0, +--- reindexDuration = 5000, +--- workspaceRoots = {} +---} +---``` +---@class _.lspconfig.settings.emmylua_ls.Emmylua.Workspace +---Enable full project reindex after changing a file. +---@field enableReindex? boolean +---Encoding. eg: "utf-8" +--- +---```lua +---default = "utf-8" +---``` +---@field encoding? string +---Ignore directories. +--- +---```lua +---default = {} +---``` +---@field ignoreDir? string[] +---Ignore globs. eg: ["**/*.lua"] +--- +---```lua +---default = {} +---``` +---@field ignoreGlobs? string[] +---Library paths. Can be a string path or an object with path and ignore rules. +---eg: ["/usr/local/share/lua/5.1"] or [{"path": "/usr/local/share/lua/5.1", "ignoreDir": ["test"], "ignoreGlobs": ["**/*.spec.lua"]}] +--- +---```lua +---default = {} +---``` +---@field library? (string|table)[] +---Module map. key is regex, value is new module regex +---eg: { +--- "^(.*)$": "module_$1" +--- "^lib(.*)$": "script$1" +---} +--- +---```lua +---default = {} +---``` +---@field moduleMap? table[] +---Package directories. Can be a string path or an object with path and ignore rules. +---Treat the parent directory as a `library`, but only add files from the specified directory. +---eg: ["/usr/local/share/lua/5.1/module"] or [{"path": "/usr/local/share/lua/5.1/module", "ignoreDir": ["test"], "ignoreGlobs": ["**/*.spec.lua"]}] +--- +---```lua +---default = {} +---``` +---@field packages? (string|table)[] +---```lua +---default = 0 +---``` +---@field preloadFileSize? integer +---Delay between changing a file and full project reindex, in milliseconds. +--- +---```lua +---default = 5000 +---``` +---@field reindexDuration? integer +---Workspace roots. eg: ["src", "test"] +--- +---```lua +---default = {} +---``` +---@field workspaceRoots? string[] + +---@class _.lspconfig.settings.emmylua_ls.Emmylua +---```lua +---default = {} +---``` +---@field codeAction? _.lspconfig.settings.emmylua_ls.Emmylua.CodeAction +---```lua +---default = { +--- enable = true +---} +---``` +---@field codeLens? _.lspconfig.settings.emmylua_ls.Emmylua.CodeLens +---Configuration for EmmyLua code completion. +--- +---```lua +---default = { +--- autoRequire = true, +--- autoRequireFunction = "require", +--- autoRequireNamingConvention = "keep", +--- autoRequireSeparator = ".", +--- baseFunctionIncludesName = true, +--- callSnippet = false, +--- enable = true, +--- postfix = "@" +---} +---``` +---@field completion? _.lspconfig.settings.emmylua_ls.Emmylua.Completion +---Represents the diagnostic configuration for Emmyrc. +--- +---```lua +---default = { +--- diagnosticInterval = 500, +--- disable = {}, +--- enable = true, +--- enables = {}, +--- globals = {}, +--- globalsRegex = {}, +--- severity = vim.empty_dict() +---} +---``` +---@field diagnostics? _.lspconfig.settings.emmylua_ls.Emmylua.Diagnostics +---```lua +---default = { +--- knownTags = {}, +--- privateName = {}, +--- syntax = "md" +---} +---``` +---@field doc? _.lspconfig.settings.emmylua_ls.Emmylua.Doc +---```lua +---default = { +--- enable = true +---} +---``` +---@field documentColor? _.lspconfig.settings.emmylua_ls.Emmylua.DocumentColor +---```lua +---default = { +--- useDiff = false +---} +---``` +---@field format? _.lspconfig.settings.emmylua_ls.Emmylua.Format +---```lua +---default = { +--- enable = true, +--- enumParamHint = false, +--- indexHint = true, +--- localHint = true, +--- metaCallHint = true, +--- overrideHint = true, +--- paramHint = true +---} +---``` +---@field hint? _.lspconfig.settings.emmylua_ls.Emmylua.Hint +---```lua +---default = { +--- enable = true +---} +---``` +---@field hover? _.lspconfig.settings.emmylua_ls.Emmylua.Hover +---```lua +---default = { +--- enable = true +---} +---``` +---@field inlineValues? _.lspconfig.settings.emmylua_ls.Emmylua.InlineValues +---```lua +---default = { +--- enable = true, +--- fuzzySearch = true, +--- shortStringSearch = false +---} +---``` +---@field references? _.lspconfig.settings.emmylua_ls.Emmylua.References +---```lua +---default = { +--- paths = {} +---} +---``` +---@field resource? _.lspconfig.settings.emmylua_ls.Emmylua.Resource +---```lua +---default = { +--- extensions = {}, +--- frameworkVersions = {}, +--- nonstandardSymbol = {}, +--- requireLikeFunction = {}, +--- requirePattern = {}, +--- special = vim.empty_dict(), +--- version = "LuaLatest" +---} +---``` +---@field runtime? _.lspconfig.settings.emmylua_ls.Emmylua.Runtime +---```lua +---default = { +--- enable = true, +--- renderDocumentationMarkup = true +---} +---``` +---@field semanticTokens? _.lspconfig.settings.emmylua_ls.Emmylua.SemanticTokens +---```lua +---default = { +--- detailSignatureHelper = true +---} +---``` +---@field signature? _.lspconfig.settings.emmylua_ls.Emmylua.Signature +---```lua +---default = { +--- arrayIndex = true, +--- docBaseConstMatchBaseType = true, +--- metaOverrideFileDefine = true, +--- requirePath = false, +--- typeCall = false +---} +---``` +---@field strict? _.lspconfig.settings.emmylua_ls.Emmylua.Strict +---```lua +---default = { +--- enableReindex = false, +--- encoding = "utf-8", +--- ignoreDir = {}, +--- ignoreGlobs = {}, +--- library = {}, +--- moduleMap = {}, +--- packages = {}, +--- preloadFileSize = 0, +--- reindexDuration = 5000, +--- workspaceRoots = {} +---} +---``` +---@field workspace? _.lspconfig.settings.emmylua_ls.Emmylua.Workspace + +---@class lspconfig.settings.emmylua_ls +---@field emmylua? _.lspconfig.settings.emmylua_ls.Emmylua diff --git a/scripts/gen_annotations.lua b/scripts/gen_annotations.lua index 1612d82b..1ca3d100 100644 --- a/scripts/gen_annotations.lua +++ b/scripts/gen_annotations.lua @@ -255,25 +255,33 @@ local function lua_type_for(prop) ' | ' ) end + if prop.const ~= nil then + return vim.inspect(prop.const) + end local types = type(prop.type) == 'table' and prop.type or { prop.type } - if vim.tbl_isempty(types) and type(prop.anyOf) == 'table' then - return table.concat( - vim.tbl_map(function(p) - return lua_type_for(p) - end, prop.anyOf), - '|' - ) + -- Convert `anyOf`/`oneOf` to a union, excluding null: + -- `{ anyOf = { { type = 'string' }, { type = 'number' }, { type = 'null' } } }` => `string|number`. + local alternatives = prop.anyOf or prop.oneOf + if vim.tbl_isempty(types) and type(alternatives) == 'table' then + local alternative_types = {} + for _, alternative in ipairs(alternatives) do + if alternative.type ~= 'null' then + table.insert(alternative_types, lua_type_for(alternative)) + end + end + return #alternative_types > 0 and table.concat(alternative_types, '|') or 'any' end types = vim.tbl_map(function(t) if t == 'null' then return end if t == 'array' then - if prop.items and prop.items.type then - if type(prop.items.type) == 'table' then - return 'any[]' + if type(prop.items) == 'table' then + local item_type = lua_type_for(prop.items) + if item_type:find('|', 1, true) then + item_type = '(' .. item_type .. ')' end - return (prop.items.type == 'object' and 'table' or prop.items.type) .. '[]' + return item_type .. '[]' end return 'any[]' end diff --git a/scripts/gen_json_schemas.lua b/scripts/gen_json_schemas.lua index 3d6f2999..c1a9b109 100644 --- a/scripts/gen_json_schemas.lua +++ b/scripts/gen_json_schemas.lua @@ -13,6 +13,7 @@ local index = { denols = 'https://raw.githubusercontent.com/denoland/vscode_deno/main/package.json', elixirls = 'https://raw.githubusercontent.com/elixir-lsp/vscode-elixir-ls/master/package.json', elmls = 'https://raw.githubusercontent.com/elm-tooling/elm-language-client-vscode/master/package.json', + emmylua_ls = 'https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/main/crates/emmylua_code_analysis/resources/schema.json', eslint = 'https://raw.githubusercontent.com/microsoft/vscode-eslint/main/package.json', flow = 'https://raw.githubusercontent.com/flowtype/flow-for-vscode/master/package.json', fsautocomplete = 'https://raw.githubusercontent.com/ionide/ionide-vscode-fsharp/main/release/package.json', @@ -132,6 +133,9 @@ local overrides = { cssls = { translate = true, }, + emmylua_ls = { + prefix = 'emmylua.', + }, nixd = { prefix = 'nixd.', }, @@ -156,6 +160,73 @@ local function resolve_schema_configs() return vim.tbl_deep_extend('force', schemas, overrides) end +---Resolve local JSON pointer references in a schema node. +---For root `{"$defs":{"port":{"type":"integer"}}}`, `{"$ref":"#/$defs/port"}` => `{"type":"integer"}`. +---`#` => `root`; `#/items/0` => `root.items[1]`. +---`#/foo//bar` => `root.foo[''].bar`; `#/%24defs/port` => `root['$defs'].port`. +---@param node any +---@param root table +---@param resolving? table<string, true> +---@return any +local function resolve_local_refs(node, root, resolving) + if type(node) ~= 'table' then + return node + end + + local ref = node['$ref'] + local pointer + if type(ref) == 'string' and vim.startswith(ref, '#') then + pointer = vim.uri_decode(ref:sub(2)) + end + if pointer == nil or (pointer ~= '' and not vim.startswith(pointer, '/')) then + for key, value in pairs(node) do + node[key] = resolve_local_refs(value, root, resolving) + end + return node + end + + resolving = resolving or {} + if resolving[ref] then + error('Cyclic schema reference: ' .. ref) + end + + local target = root + -- `#/$defs/foo~1bar~0baz` resolves to `root['$defs']['foo/bar~baz']` (`~1` escapes `/`, `~0` escapes `~`). + for part in pointer:gmatch('/([^/]*)') do + local token = part:gsub('~1', '/'):gsub('~0', '~') + ---@type string|integer + local key = token + if type(target) ~= 'table' then + error('Could not resolve schema reference: ' .. ref) + end + if vim.islist(target) then + if token ~= '0' and not token:match('^[1-9]%d*$') then + error('Could not resolve schema reference: ' .. ref) + end + key = assert(tonumber(token)) + 1 + end + if target[key] == nil then + error('Could not resolve schema reference: ' .. ref) + end + target = target[key] + end + + resolving[ref] = true + local resolved = resolve_local_refs(vim.deepcopy(target), root, resolving) + resolving[ref] = nil + for key, value in pairs(node) do + if key ~= '$ref' then + if type(resolved) ~= 'table' or resolved[key] ~= nil then + -- TODO: Preserve conflicting constraints with `allOf` once annotation generation supports it. + -- See https://json-schema.org/draft/2020-12/json-schema-core#section-8.2.3.1. + error(('Cannot merge schema reference sibling %q: %s'):format(key, ref)) + end + resolved[key] = resolve_local_refs(value, root, resolving) + end + end + return resolved +end + ---Replaces localized documentation placeholders in a schema tree in place. --- ---This is used for schemas whose documentation strings are stored in a @@ -218,12 +289,18 @@ local function generate_server_schema(schema) for _, config_section in pairs(config_schema) do if config_section.properties then for k, v in pairs(config_section.properties) do - properties[k] = v + if k ~= '$schema' then + properties[k] = resolve_local_refs(v, config_section) + end end end end elseif config_schema.properties then - properties = config_schema.properties + for k, v in pairs(config_schema.properties) do + if k ~= '$schema' then + properties[k] = resolve_local_refs(v, config_schema) + end + end end -- `properties["enable_snippets"]` => `properties["zls.enable_snippets"]` |
