From 65ef6fdaeef86f8341293942c076b320acf03fd1 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 7 Sep 2026 11:43:51 +0100 Subject: [PATCH] build: replace LuaLS with EmmyLua Problem: LuaLS struggles with the generics used in Nvim's runtime, requiring broad diagnostic suppressions. Indexing is also slow. Solution: Use EmmyLua for type checks in the build and CI. It offers more sophisticated type checking, substantially better support for generics, and much better flow analysis. Correct the affected annotations. Use `@internal`, supported directly by EmmyLua, instead of `@nodoc` for shared internal declarations, and support it in the help parser. AI-assisted --- .emmyrc.json | 46 ++++++++++++++- .github/workflows/test.yml | 4 +- .luarc.json | 56 ++++++++++--------- CMakeLists.txt | 53 +++++++++++------- CONTRIBUTING.md | 9 ++- Makefile | 8 +-- runtime/doc/dev.txt | 4 +- runtime/doc/dev_test.txt | 11 ++-- runtime/doc/lua.txt | 12 ++-- runtime/lua/nvim/dir.lua | 2 +- runtime/lua/vim/_core/editor.lua | 6 +- runtime/lua/vim/_core/shared.lua | 9 +-- runtime/lua/vim/_core/ui2/messages.lua | 8 +-- runtime/lua/vim/async.lua | 2 +- runtime/lua/vim/async/_core.lua | 3 - runtime/lua/vim/async/_event.lua | 3 - runtime/lua/vim/async/_future.lua | 13 ++++- runtime/lua/vim/async/_queue.lua | 3 - runtime/lua/vim/async/_runtime.lua | 3 - runtime/lua/vim/async/_semaphore.lua | 3 - runtime/lua/vim/filetype.lua | 2 +- runtime/lua/vim/fs.lua | 2 +- runtime/lua/vim/iter.lua | 12 ++-- runtime/lua/vim/loader.lua | 2 +- runtime/lua/vim/log.lua | 2 +- runtime/lua/vim/lsp/_capability.lua | 4 +- runtime/lua/vim/lsp/_folding_range.lua | 5 +- runtime/lua/vim/lsp/_snippet_grammar.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 7 ++- runtime/lua/vim/lsp/client.lua | 2 +- runtime/lua/vim/lsp/codelens.lua | 2 +- runtime/lua/vim/lsp/completion.lua | 2 +- runtime/lua/vim/lsp/diagnostic.lua | 2 +- runtime/lua/vim/lsp/inlay_hint.lua | 4 +- runtime/lua/vim/lsp/log.lua | 2 +- runtime/lua/vim/lsp/util.lua | 14 +++-- runtime/lua/vim/net/_transport.lua | 6 +- runtime/lua/vim/range.lua | 7 ++- runtime/lua/vim/treesitter/dev.lua | 2 + runtime/lua/vim/treesitter/languagetree.lua | 4 +- runtime/lua/vim/treesitter/query.lua | 3 +- runtime/lua/vim/tty.lua | 2 +- .../pack/dist/opt/nvim.tohtml/lua/tohtml.lua | 3 +- runtime/plugin/shada.lua | 2 +- src/gen/luacats_grammar.lua | 6 +- src/gen/luacats_parser.lua | 4 +- test/functional/plugin/lsp/snippet_spec.lua | 5 +- .../functional/script/luacats_parser_spec.lua | 22 ++++++++ 48 files changed, 231 insertions(+), 159 deletions(-) diff --git a/.emmyrc.json b/.emmyrc.json index 8ddce1f0da..dbfbe96446 100644 --- a/.emmyrc.json +++ b/.emmyrc.json @@ -1,5 +1,19 @@ { - "$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json", + "$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/0.25.1/crates/emmylua_code_analysis/resources/schema.json", + "runtime": { + "version": "LuaJIT" + }, + "workspace": { + "ignoreDir": [ + ".deps", + "build" + ], + "ignoreGlobs": [ + "**/example_init.lua", + "test/**/fixtures/**" + ], + "preloadFileSize": 1000 + }, "format": { "externalTool": { "program": "stylua", @@ -12,9 +26,35 @@ }, "diagnostics": { "disable": [ + "annotation-usage-error", + "assign-type-mismatch", + "await-in-sync", + "call-non-callable", + "code-style-check", + "deprecated", + "global-in-non-module", + "incomplete-signature-doc", + "invert-if", + "iter-variable-reassign", + "missing-fields", + "missing-global-doc", + "need-check-nil", + "non-literal-expressions-in-assert", + "param-type-mismatch", "preferred-local-alias", - "unnecessary-if" - ] + "redefined-label", + "redefined-local", + "return-type-mismatch", + "undefined-field", + "unknown-doc-tag", + "unnecessary-assert", + "unnecessary-if", + "unreachable-code", + "unused" + ], + "severity": { + "duplicate-require": "warning" + } }, "codeAction": { "insertSpace": true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f42e8a8747..c26b16eadc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -105,8 +105,8 @@ jobs: run: cmake --build build --target lintlua-stylua - if: success() || failure() && steps.abort_job.outputs.status == 'success' - name: luals - run: cmake --build build --target luals + name: emmylua + run: cmake --build build --target emmylua-check - if: success() || failure() && steps.abort_job.outputs.status == 'success' name: lintsh diff --git a/.luarc.json b/.luarc.json index bca1c08b37..dbf4c4ff88 100644 --- a/.luarc.json +++ b/.luarc.json @@ -1,32 +1,34 @@ { - "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", - "runtime": { - "version": "LuaJIT" - }, - "workspace": { - "ignoreDir": [ - "/lua/vim/async.lua", - ".deps", - "build" - ], - "ignoreGlobs": [ - "test/**/fixtures/**" - ], - "preloadFileSize": 1000, - "checkThirdParty": "Disable" - }, - "diagnostics": { - "groupFileStatus": { - "strict": "Opened", - "strong": "Opened" + "$comment": "Keep LuaLS settings under Lua so EmmyLua ignores them.", + "Lua": { + "runtime": { + "version": "LuaJIT" }, - "groupSeverity": { - "strong": "Warning", - "strict": "Warning" + "workspace": { + "ignoreDir": [ + "/lua/vim/async.lua", + ".deps", + "build" + ], + "ignoreGlobs": [ + "test/**/fixtures/**" + ], + "preloadFileSize": 1000, + "checkThirdParty": "Disable" }, - "unusedLocalExclude": [ "_*" ], - "disable": [ - "luadoc-miss-see-name" - ] + "diagnostics": { + "groupFileStatus": { + "strict": "Opened", + "strong": "Opened" + }, + "groupSeverity": { + "strong": "Warning", + "strict": "Warning" + }, + "unusedLocalExclude": [ "_*" ], + "disable": [ + "luadoc-miss-see-name" + ] + } } } diff --git a/CMakeLists.txt b/CMakeLists.txt index 72e7427393..3ab07c2666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -331,21 +331,34 @@ ExternalProject_Add(uncrustify EXCLUDE_FROM_ALL TRUE ${EXTERNALPROJECT_OPTIONS}) -if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm|aarch") - set(LUALS_ARCH arm64) +if (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm|aarch|ARM") + set(EMMYLUA_ARCH arm64) else() - set(LUALS_ARCH x64) + set(EMMYLUA_ARCH x64) endif() -set(LUALS_VERSION 3.15.0) -set(LUALS "lua-language-server-${LUALS_VERSION}-${CMAKE_SYSTEM_NAME}-${LUALS_ARCH}") -set(LUALS_TARBALL ${LUALS}.tar.gz) -set(LUALS_URL https://github.com/LuaLS/lua-language-server/releases/download/${LUALS_VERSION}/${LUALS_TARBALL}) +string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} EMMYLUA_SYSTEM) +set(EMMYLUA_ARCHIVE_EXT tar.gz) +set(EMMYLUA_EXE_EXT "") +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + if(EMMYLUA_ARCH STREQUAL "arm64") + set(EMMYLUA_ARCH aarch64) + endif() + string(APPEND EMMYLUA_ARCH "-glibc.2.17") +elseif(CMAKE_HOST_WIN32) + set(EMMYLUA_SYSTEM win32) + set(EMMYLUA_ARCHIVE_EXT zip) + set(EMMYLUA_EXE_EXT .exe) +endif() -ExternalProject_Add(download_luals - URL ${LUALS_URL} - DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luals - SOURCE_DIR ${DEPS_BIN_DIR}/luals +set(EMMYLUA_VERSION 0.25.1) +set(EMMYLUA_ARCHIVE "emmylua_check-${EMMYLUA_SYSTEM}-${EMMYLUA_ARCH}.${EMMYLUA_ARCHIVE_EXT}") +set(EMMYLUA_URL https://github.com/EmmyLuaLs/emmylua-analyzer-rust/releases/download/${EMMYLUA_VERSION}/${EMMYLUA_ARCHIVE}) + +ExternalProject_Add(download_emmylua + URL ${EMMYLUA_URL} + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/emmylua + SOURCE_DIR ${DEPS_BIN_DIR}/emmylua CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" @@ -353,13 +366,13 @@ ExternalProject_Add(download_luals DOWNLOAD_NO_PROGRESS TRUE CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS}) -file(GLOB_RECURSE LUAFILES runtime/*.lua) -add_target(luals - COMMAND ${DEPS_BIN_DIR}/luals/bin/lua-language-server - --configpath=${PROJECT_SOURCE_DIR}/.luarc.json - --check=${PROJECT_SOURCE_DIR}/runtime - --checklevel=Hint - DEPENDS ${LUAFILES} - CUSTOM_COMMAND_ARGS USES_TERMINAL) +add_custom_target(emmylua-check + COMMAND ${DEPS_BIN_DIR}/emmylua/emmylua_check${EMMYLUA_EXE_EXT} + ${PROJECT_SOURCE_DIR}/runtime + --config ${PROJECT_SOURCE_DIR}/.emmyrc.json + --warnings-as-errors + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + USES_TERMINAL + VERBATIM) -add_dependencies(luals download_luals) +add_dependencies(emmylua-check download_emmylua) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f95c3aa3b7..fe72f2c81e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -300,9 +300,12 @@ If you need to modify or debug the documentation flow, these are the main files: ### Lua docstrings -Use [LuaLS] annotations in Lua docstrings to annotate parameter types, return +Use [LuaCATS] annotations in Lua docstrings to annotate parameter types, return types, etc. See [:help dev-lua-doc][dev-lua-doc]. +Run `make emmylua-check` to check the runtime with [EmmyLua]. The build downloads +the pinned checker automatically. Settings are in `.emmyrc.json`. + Third-party dependencies ------------------------ @@ -353,7 +356,8 @@ as context, use the `-W` argument as well. [conventional_commits]: https://www.conventionalcommits.org [dev-doc-guide]: https://neovim.io/doc/user/dev.html#dev-doc [dev-lua-doc]: https://neovim.io/doc/user/dev.html#dev-lua-doc -[LuaLS]: https://luals.github.io/wiki/annotations/ +[LuaCATS]: https://luals.github.io/wiki/annotations/ +[EmmyLua]: https://github.com/EmmyLuaLs/emmylua-analyzer-rust [gcc-warnings]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html [gh]: https://cli.github.com/ [git-bisect]: http://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git @@ -362,7 +366,6 @@ as context, use the `-W` argument as well. [github-issues]: https://github.com/neovim/neovim/issues [include-what-you-use-install]: https://github.com/include-what-you-use/include-what-you-use#how-to-install [include-what-you-use]: https://github.com/include-what-you-use/include-what-you-use#using-with-cmake -[lua-language-server]: https://github.com/sumneko/lua-language-server/ [nvim-lspconfig/clangd]: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#clangd [pr-draft]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request [pr-ready]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request diff --git a/Makefile b/Makefile index 30f1e6285d..46c2bdb3de 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ functionaltest-lua: | nvim $(CMAKE) --build build --target functionaltest FORMAT=formatc formatlua formatquery format -LINT=lintlua lintsh lintc clang-analyzer lintcommit lintdoc lintdocurls lint luals lintquery linterrcodes +LINT=lintlua lintsh lintc clang-analyzer lintcommit lintdoc lintdocurls lint emmylua-check lintquery linterrcodes TEST=functionaltest unittest generated-sources benchmark $(FORMAT) $(LINT) $(TEST) doc: | build/.ran-cmake $(CMAKE) --build build --target $@ @@ -194,9 +194,3 @@ appimage-%: bash scripts/genappimage.sh $* .PHONY: test clean distclean nvim libnvim cmake deps install appimage checkprefix benchmark $(FORMAT) $(LINT) $(TEST) - -.PHONY: emmylua-check -emmylua-check: - -emmylua_check runtime/lua \ - --config .luarc.json \ - --config .emmyrc.json diff --git a/runtime/doc/dev.txt b/runtime/doc/dev.txt index c56a53e9da..128d4a4ece 100644 --- a/runtime/doc/dev.txt +++ b/runtime/doc/dev.txt @@ -249,9 +249,11 @@ Docstring format: - See scripts/util.lua for the mapping of api-level to Nvim version. - Use `@deprecated` to mark deprecated functions. - Use `@nodoc` to prevent documentation generation. -- Use `@private` if the function is internal or otherwise non-public. +- Use `@private` for private functions. - Naming a function with an underscore prefix ("_foo", not "foo") implies `@nodoc` and `@private`. +- Use `@internal` for non-public functions shared across Nvim modules, or + `@class (internal)` for shared internal types. Both hide generated docs. - Use `@inlinedoc` to inline `@class` blocks into `@param` blocks. E.g. >lua --- Object with fields: diff --git a/runtime/doc/dev_test.txt b/runtime/doc/dev_test.txt index e911a76d38..3d3290e338 100644 --- a/runtime/doc/dev_test.txt +++ b/runtime/doc/dev_test.txt @@ -419,14 +419,15 @@ Example: https://github.com/neovim/neovim/pull/35768 FIXING LINT FAILURES -`make lint` (and `make lintlua`) runs [LuaLS](https://github.com/LuaLS/lua-language-server/wiki/Annotations) -on the test code. +`make emmylua-check` runs EmmyLua on the runtime Lua code, using `.emmyrc.json`. +The checker is downloaded automatically. Enable more checks incrementally by +removing codes from `diagnostics.disable` and fixing the resulting diagnostics. -If a LuaLS/EmmyLS warning must be ignored, specify the warning code. Example: >lua +If an EmmyLua warning must be ignored, specify the warning code. Example: >lua - ---@diagnostic disable-next-line: unused-vararg + ---@diagnostic disable-next-line: unused -https://github.com/LuaLS/lua-language-server/wiki/Annotations#diagnostic +https://github.com/EmmyLuaLs/emmylua-analyzer-rust Ignore the smallest applicable scope (e.g. inside a function, not at the top of the file). diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 36c7fa587e..af28ea63a0 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1650,19 +1650,19 @@ Ringbuf:peek() *Ringbuf:peek()* Returns the first unread item without removing it Return: ~ - (`any?`) + (`T?`) Ringbuf:pop() *Ringbuf:pop()* Removes and returns the first unread item Return: ~ - (`any?`) + (`T?`) Ringbuf:push({item}) *Ringbuf:push()* Adds an item, overriding the oldest item if the buffer is full. Parameters: ~ - • {item} (`any`) + • {item} (`T`) vim.deep_equal({a}, {b}) *vim.deep_equal()* Deep compare values for equality @@ -2034,7 +2034,7 @@ vim.ringbuf({size}) *vim.ringbuf()* • {size} (`integer`) Return: ~ - (`vim.Ringbuf`) ringbuf + (`vim.Ringbuf`) ringbuf vim.spairs({t}) *vim.spairs()* Enumerates key-value pairs of a table, ordered by key. @@ -2982,7 +2982,7 @@ vim.fs.slug({path}, {opts}) *vim.fs.slug()* Parameters: ~ • {path} (`string`) Filepath (or other identity string). - • {opts} (`table?`) + • {opts} (`{ maxlen?: integer }?`) • maxlen: (integer, default: 180) Max length (bytes) of the result. @@ -3573,7 +3573,7 @@ Iter:totable() *Iter:totable()* Since: 0.10.0 Overloads: ~ - • `fun(self: vim.Iter): T[]` + • `fun(self: vim.Iter): T[]` • `fun(self: vim.Iter): [V1, V2, V...][]` Return: ~ diff --git a/runtime/lua/nvim/dir.lua b/runtime/lua/nvim/dir.lua index c3de19396d..ccce98e98c 100644 --- a/runtime/lua/nvim/dir.lua +++ b/runtime/lua/nvim/dir.lua @@ -6,7 +6,7 @@ local api = vim.api local M = {} --- An entry rendered as one line in a listing buffer. ----@class (private) nvim.dir.Entry +---@class (internal) nvim.dir.Entry ---@field name string ---@field dir boolean diff --git a/runtime/lua/vim/_core/editor.lua b/runtime/lua/vim/_core/editor.lua index 3fde7b4ef3..12b772d047 100644 --- a/runtime/lua/vim/_core/editor.lua +++ b/runtime/lua/vim/_core/editor.lua @@ -493,9 +493,8 @@ vim.cmd = setmetatable({}, { --- @param t table __index = function(t, cmd) t[cmd] = function(...) - local opts --- @type vim.api.keyset.cmd + local opts --- @type vim.api.keyset.cmd & { [integer]: any } if select('#', ...) == 1 and type(select(1, ...)) == 'table' then - --- @type vim.api.keyset.cmd opts = select(1, ...) -- Move indexed positions in opts to opt.args @@ -506,7 +505,6 @@ vim.cmd = setmetatable({}, { break end opts.args[i] = opts[i] - --- @diagnostic disable-next-line: no-unknown opts[i] = nil end end @@ -565,7 +563,7 @@ end ---@param bufnr integer Buffer number, or 0 for current buffer ---@param pos1 integer[]|string Start of region as a (line, column) tuple or |getpos()|-compatible string ---@param pos2 integer[]|string End of region as a (line, column) tuple or |getpos()|-compatible string ----@param regtype string [setreg()]-style selection type +---@param regtype string # [setreg()]-style selection type ---@param inclusive boolean Controls whether the ending column is inclusive (see also 'selection'). ---@return table region Dict of the form `{linenr = {startcol,endcol}}`. `endcol` is exclusive, and ---whole lines are returned as `{startcol,endcol} = {0,-1}`. diff --git a/runtime/lua/vim/_core/shared.lua b/runtime/lua/vim/_core/shared.lua index 00e7f39520..6e62db8015 100644 --- a/runtime/lua/vim/_core/shared.lua +++ b/runtime/lua/vim/_core/shared.lua @@ -1331,11 +1331,11 @@ end do ---@class vim.Ringbuf - ---@field private _items table[] + ---@field private _items table ---@field private _idx_read integer ---@field private _idx_write integer ---@field private _size integer - ---@overload fun(self): table? + ---@overload fun(self: vim.Ringbuf): T? local Ringbuf = {} --- Clear all items @@ -1346,7 +1346,6 @@ do end --- Adds an item, overriding the oldest item if the buffer is full. - ---@generic T ---@param item T function Ringbuf.push(self, item) self._items[self._idx_write] = item @@ -1357,7 +1356,6 @@ do end --- Removes and returns the first unread item - ---@generic T ---@return T? function Ringbuf.pop(self) local idx_read = self._idx_read @@ -1371,7 +1369,6 @@ do end --- Returns the first unread item without removing it - ---@generic T ---@return T? function Ringbuf.peek(self) if self._idx_read == self._idx_write then @@ -1407,7 +1404,7 @@ do --- - |Ringbuf:clear()| --- ---@param size integer - ---@return vim.Ringbuf ringbuf + ---@return vim.Ringbuf ringbuf function vim.ringbuf(size) local ringbuf = { _items = {}, diff --git a/runtime/lua/vim/_core/ui2/messages.lua b/runtime/lua/vim/_core/ui2/messages.lua index 8c06133e16..33048ac9c1 100644 --- a/runtime/lua/vim/_core/ui2/messages.lua +++ b/runtime/lua/vim/_core/ui2/messages.lua @@ -33,10 +33,10 @@ local M = { }, virt = { -- Stored virt_text state. last = { {}, {}, {}, {} }, ---@type MsgContent[] status in last cmdline row. - cmd = { {}, {} }, ---@type MsgContent[] [(x)] indicators in cmd window. - msg = { {}, {} }, ---@type MsgContent[] [(x)] indicators in msg window. - top = { {} }, ---@type MsgContent[] [+x] top indicator in dialog window. - bot = { {} }, ---@type MsgContent[] [+x] bottom indicator in dialog window. + cmd = { {}, {} }, ---@type MsgContent[] # [(x)] indicators in cmd window. + msg = { {}, {} }, ---@type MsgContent[] # [(x)] indicators in msg window. + top = { {} }, ---@type MsgContent[] # [+x] top indicator in dialog window. + bot = { {} }, ---@type MsgContent[] # [+x] bottom indicator in dialog window. idx = { mode = 1, search = 2, cmd = 3, ruler = 4, spill = 1, dupe = 2 }, ids = {}, ---@type { ['last'|'cmd'|'msg'|'top'|'bot']: integer? } Table of mark IDs. delayed = false, -- Whether placement of 'last' virt_text is delayed. diff --git a/runtime/lua/vim/async.lua b/runtime/lua/vim/async.lua index 8483b45add..b261e04664 100644 --- a/runtime/lua/vim/async.lua +++ b/runtime/lua/vim/async.lua @@ -288,7 +288,7 @@ function M.timeout(duration, task) timed_out = true task:close() end) - --- @diagnostic disable-next-line: invisible + --- @diagnostic disable-next-line: access-invisible timer._hidden = true local result = F.pack_len(M.pawait(task)) diff --git a/runtime/lua/vim/async/_core.lua b/runtime/lua/vim/async/_core.lua index 25837598a4..f2c0352869 100644 --- a/runtime/lua/vim/async/_core.lua +++ b/runtime/lua/vim/async/_core.lua @@ -1,6 +1,3 @@ --- LuaLS cannot model the generic annotations used by this vendored implementation. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field, need-check-nil, await-in-sync - local util = require('vim._core.util') local future = require('vim.async._future') local runtime = require('vim.async._runtime') diff --git a/runtime/lua/vim/async/_event.lua b/runtime/lua/vim/async/_event.lua index 376def201b..676ef0f8d0 100644 --- a/runtime/lua/vim/async/_event.lua +++ b/runtime/lua/vim/async/_event.lua @@ -1,6 +1,3 @@ --- LuaLS cannot model the generic annotations used by this vendored implementation. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field, need-check-nil, await-in-sync - local async = require('vim.async._core') local runtime = require('vim.async._runtime') diff --git a/runtime/lua/vim/async/_future.lua b/runtime/lua/vim/async/_future.lua index 46b036b88c..b0d51433aa 100644 --- a/runtime/lua/vim/async/_future.lua +++ b/runtime/lua/vim/async/_future.lua @@ -1,9 +1,11 @@ --- LuaLS cannot model the generic annotations used by this vendored implementation. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field, need-check-nil, await-in-sync - local F = vim.F local util = require('vim._core.util') +--- @class (internal) vim.async.Future +--- @field private _callbacks table +--- @field private _callback_pos integer +--- @field private _err? any +--- @field private _result? R[] & { n: integer } local Future = {} Future.__index = Future @@ -22,6 +24,8 @@ function Future:result() end end +--- @param callback fun(err?: any, ...: R...) +--- @return fun() function Future:on_complete(callback) if self:completed() then -- Already completed or closed @@ -42,6 +46,8 @@ function Future:on_complete(callback) end end +--- @param err? any +--- @param ... R... function Future:complete(err, ...) if self:completed() then error('Future is already completed', 2) @@ -69,6 +75,7 @@ function Future:complete(err, ...) end end +--- @return vim.async.Future return function() return setmetatable({ _callbacks = {}, diff --git a/runtime/lua/vim/async/_queue.lua b/runtime/lua/vim/async/_queue.lua index 5a2d9b4952..2476f8f3a3 100644 --- a/runtime/lua/vim/async/_queue.lua +++ b/runtime/lua/vim/async/_queue.lua @@ -1,6 +1,3 @@ --- LuaLS cannot model the generic annotations used by this vendored implementation. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field, need-check-nil, await-in-sync - local new_event = require('vim.async._event') --- An optionally bounded FIFO queue for passing values between async tasks. diff --git a/runtime/lua/vim/async/_runtime.lua b/runtime/lua/vim/async/_runtime.lua index d7a84f65af..43e58108af 100644 --- a/runtime/lua/vim/async/_runtime.lua +++ b/runtime/lua/vim/async/_runtime.lua @@ -1,6 +1,3 @@ --- LuaLS cannot model the generic annotations used by this vendored implementation. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field, need-check-nil, await-in-sync - local validate = vim.validate --- @class vim.async.Timer: vim.async.Closable diff --git a/runtime/lua/vim/async/_semaphore.lua b/runtime/lua/vim/async/_semaphore.lua index 79091f42b9..ddd942441f 100644 --- a/runtime/lua/vim/async/_semaphore.lua +++ b/runtime/lua/vim/async/_semaphore.lua @@ -1,6 +1,3 @@ --- LuaLS cannot model the generic annotations used by this vendored implementation. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field, need-check-nil, await-in-sync - local F = vim.F local new_event = require('vim.async._event') diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e7f9f701fd..2ba238085e 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -3,7 +3,7 @@ local fn = vim.fn local M = {} ---- @alias vim.filetype.mapfn fun(path:string,bufnr:integer, ...):string?, fun(b:integer)? +--- @alias vim.filetype.mapfn fun(path:string,bufnr:integer, ...):string?, fun(b:integer)?, boolean? --- @alias vim.filetype.mapopts { priority: number } --- @alias vim.filetype.maptbl [string|vim.filetype.mapfn, vim.filetype.mapopts] --- @alias vim.filetype.mapping.value string|vim.filetype.mapfn|vim.filetype.maptbl diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 986d328c06..c412e9d714 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -173,7 +173,7 @@ end --- ---@since 15 ---@param path string Filepath (or other identity string). ----@param opts? table +---@param opts? { maxlen?: integer } # --- - maxlen: (integer, default: 180) Max length (bytes) of the result. ---@return string # Filesystem-safe, mnemonic slug. function M.slug(path, opts) diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua index b0e00d8dd4..136941e233 100644 --- a/runtime/lua/vim/iter.lua +++ b/runtime/lua/vim/iter.lua @@ -64,13 +64,11 @@ --- -- { "a", "b" } --- ``` --- LuaLS cannot model the variadic EmmyLua generics used by this module. ----@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field - +-- `never` represents an empty tail for single-value iterators. --- @nodoc --- @class vim.IterModule --- @operator call: vim.Iter ---- @overload fun(src: T[]): vim.IterArray +--- @overload fun(src: T[]): vim.IterArray --- @overload fun(src: table): vim.Iter --- @overload fun(src: table, ...): vim.Iter --- @overload fun(src: function, ...): vim.Iter @@ -468,7 +466,7 @@ end --- --- --- @since 12 ---- @overload fun(self: vim.Iter): T[] +--- @overload fun(self: vim.Iter): T[] --- @overload fun(self: vim.Iter): [V1, V2, V...][] --- @return any[] function Iter:totable() @@ -486,7 +484,7 @@ function Iter:totable() end --- @nodoc ---- @overload fun(self: vim.IterArray): T[] +--- @overload fun(self: vim.IterArray): T[] --- @overload fun(self: vim.IterArray): [V1, V2, V...][] --- @return any[] function IterArray:totable() @@ -1250,7 +1248,7 @@ end --- @generic R1, R... --- @param src table|fun(s: table, v: any): R1, R... Table or iterator to drain values from --- @return vim.Iter ---- @overload fun(src: T[]): vim.IterArray +--- @overload fun(src: T[]): vim.IterArray --- @overload fun(src: table): vim.Iter --- @private function Iter.new(src, ...) diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 924503514b..2ab8d451a2 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -60,7 +60,7 @@ local stats = { find = { total = 0, time = 0, not_found = 0 } } --- @type table? local fs_stat_cache ---- @type table> +--- @type table?> local indexed = {} --- @param path string diff --git a/runtime/lua/vim/log.lua b/runtime/lua/vim/log.lua index 416ff29fa3..84fcdd0713 100644 --- a/runtime/lua/vim/log.lua +++ b/runtime/lua/vim/log.lua @@ -41,7 +41,7 @@ ---@field private filename string --- --- Internal state for the log file handle. `nil` until the file is opened. ----@field private logfile file*? +---@field private logfile file? --- --- Internal state for the log file open error. ---@field private openerr string? diff --git a/runtime/lua/vim/lsp/_capability.lua b/runtime/lua/vim/lsp/_capability.lua index 208fcc72a7..22d5f0b560 100644 --- a/runtime/lua/vim/lsp/_capability.lua +++ b/runtime/lua/vim/lsp/_capability.lua @@ -48,10 +48,8 @@ local buf_capabilities = {} local M = {} M.__index = M ----@generic T : vim.lsp.Capability ----@param self T ---@param bufnr integer ----@return T +---@return self function M:new(bufnr) -- `self` in the `new()` function refers to the concrete type (i.e., the metatable). -- `Class` may be a subtype of `Capability`, as it supports inheritance. diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua index 2d3d951425..bf97a7a8fe 100644 --- a/runtime/lua/vim/lsp/_folding_range.lua +++ b/runtime/lua/vim/lsp/_folding_range.lua @@ -203,6 +203,7 @@ end ---@return vim.lsp.folding_range.State function State:new(bufnr) self = Capability.new(self, bufnr) + ---@cast self vim.lsp.folding_range.State self.lang = vim.treesitter.language.get_lang(vim.bo[self.bufnr].filetype) self.row_level = {} self.row_kinds = {} @@ -325,8 +326,8 @@ end --- Split `line` into highlighted virt_text chunks from `spans`. --- ---@param line string ----@param spans [integer, integer, string][] [start_col, end_col, highlight] ----@return [string, string[]?][] [text, highlight[]?][] +---@param spans [integer, integer, string][] # [start_col, end_col, highlight] +---@return [string, string[]?][] # [text, highlight[]?][] local function spans_to_virt_text(line, spans) local boundaries = { 0, #line } for _, span in ipairs(spans) do diff --git a/runtime/lua/vim/lsp/_snippet_grammar.lua b/runtime/lua/vim/lsp/_snippet_grammar.lua index 7828b2a994..5424c41955 100644 --- a/runtime/lua/vim/lsp/_snippet_grammar.lua +++ b/runtime/lua/vim/lsp/_snippet_grammar.lua @@ -174,7 +174,7 @@ local G = P({ --- @param input string --- @return vim.snippet.Node function M.parse(input) - return assert(G:match(input), 'snippet parsing failed') + return (assert(G:match(input), 'snippet parsing failed')) end return M diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 64360a88e9..7a041a7dab 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -675,14 +675,15 @@ function M.format(opts) return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range end - local ret = params --[[@as lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams]] + --- @type lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams + local ret = params if passed_multiple_ranges then --- @cast range {start:[integer,integer],end:[integer, integer]}[] - ret = params --[[@as lsp.DocumentRangesFormattingParams]] + --- @cast ret lsp.DocumentRangesFormattingParams ret.ranges = vim.tbl_map(to_lsp_range, range) elseif range then --- @cast range {start:[integer,integer],end:[integer, integer]} - ret = params --[[@as lsp.DocumentRangeFormattingParams]] + --- @cast ret lsp.DocumentRangeFormattingParams ret.range = to_lsp_range(range) end return ret diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 3a66a1e635..0ce4087f77 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -416,7 +416,7 @@ function Client.create(config) local id = client_index local name = get_name(id, config) - --- @class vim.lsp.Client + --- @type vim.lsp.Client local self = { id = id, config = config, diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index ae1d30b69a..17f40fa298 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -428,7 +428,7 @@ end --- |lsp-handler| for the method `workspace/codeLens/refresh` --- ----@private +---@internal ---@type lsp.Handler function M.on_refresh(err, _, ctx) if err then diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 644f1db870..46e22964c7 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -511,7 +511,7 @@ function M._lsp_to_complete_items( return {} end - ---@type fun(item: lsp.CompletionItem, item_prefix: string):boolean + ---@type fun(item: lsp.CompletionItem, item_prefix: string): boolean, integer? local matches if not prefix:find('%w') then matches = function(_, _) diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index c683c07b89..6f4cdbc693 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -384,7 +384,7 @@ end --- |lsp-handler| for the method `workspace/diagnostic/refresh` ---@param ctx lsp.HandlerContext ----@private +---@internal function M.on_refresh(err, _, ctx) if err then return vim.NIL diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 6728876160..9d2d0f9548 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -137,7 +137,7 @@ end --- Store hints for a specific buffer and client ---@param result lsp.InlayHint[]? ---@param ctx lsp.HandlerContext ----@private +---@internal function M.on_inlayhint(err, result, ctx) local bufnr = assert(ctx.bufnr) local provider = InlayHint.active[bufnr] @@ -223,7 +223,7 @@ end --- |lsp-handler| for the method `workspace/inlayHint/refresh` ---@param ctx lsp.HandlerContext ----@private +---@internal function M.on_refresh(err, _, ctx) if err then return vim.NIL diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index d5cc246b90..e34d0aef7d 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -42,7 +42,7 @@ M._self = log --- Returns the log filename. ---@return string log filename function M.get_filename() - ---@diagnostic disable-next-line: invisible + ---@diagnostic disable-next-line: access-invisible return log.filename end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index dc057f1f47..0da99e70bf 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -8,6 +8,7 @@ local uv = vim.uv local M = {} --- @param border string|(string|[string,string])[] +--- @return never local function border_error(border) error( string.format( @@ -69,8 +70,7 @@ local function get_border_size(opts) -- border specified as a list of border characters return e end - --- @diagnostic disable-next-line:missing-return - border_error(border) + return border_error(border) end --- @param e string @@ -179,7 +179,7 @@ function M.apply_text_edits(text_edits, bufnr, position_encoding, change_annotat local function apply_text_edits() -- Fix reversed range and indexing each text_edits for index, text_edit in ipairs(text_edits) do - --- @cast text_edit lsp.TextEdit|{_index: integer} + --- @cast text_edit lsp.TextEdit & { _index?: integer } -- XXX: Preserve existing _index to avoid surprises if the same edit is reapplied. #39344 if text_edit._index == nil then text_edit._index = index @@ -668,7 +668,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers if active_signature >= #signature_help.signatures or active_signature < 0 then active_signature = 0 end - local signature = vim.deepcopy(signature_help.signatures[active_signature + 1]) + local signature = signature_help.signatures[active_signature + 1] + if not signature then + return + end + signature = vim.deepcopy(signature) local label = signature.label if ft then -- wrap inside a code block for proper rendering @@ -1410,7 +1414,7 @@ function M._make_floating_popup_size(contents, opts) local title_length = 0 local chunks = type(opts.title) == 'string' and { { opts.title } } or opts.title or {} for _, chunk in - ipairs(chunks --[=[@as [string, string][]]=]) + ipairs(chunks --[=[@as [string, string][] ]=]) do title_length = title_length + vim.fn.strdisplaywidth(chunk[1]) end diff --git a/runtime/lua/vim/net/_transport.lua b/runtime/lua/vim/net/_transport.lua index ba604470fd..55dff381c5 100644 --- a/runtime/lua/vim/net/_transport.lua +++ b/runtime/lua/vim/net/_transport.lua @@ -3,7 +3,7 @@ local strbuffer = require('vim._core.stringbuffer') --- Interface for transport implementations. --- ---- @class (private, exact) vim.net.Transport +--- @class (internal, exact) vim.net.Transport --- @field listen fun(self: vim.net.Transport, on_read: fun(err: any, data: string), on_exit: fun(code: integer, signal: integer)) --- @field write fun(self: vim.net.Transport, msg: string) --- @field is_closing fun(self: vim.net.Transport): boolean @@ -101,7 +101,7 @@ end --- These messages are buffered in `msgbuf`. --- @field private connected boolean --- @field private closing boolean ---- @field private msgbuf vim.Ringbuf +--- @field private msgbuf vim.Ringbuf --- @field private on_exit? fun(code: integer, signal: integer) --- @field new fun(host_or_path: string, port?: integer, log: vim.Log): vim.net.TransportConnect local TransportConnect = {} @@ -193,7 +193,7 @@ end --- `nil` means it needs more transport data. --- decoder errors are reported through `on_error`. --- ----@class (private, exact) vim.net.MessageStream +---@class (internal, exact) vim.net.MessageStream ---@field private strbuf string.buffer ---@field private decode fun(strbuf: string.buffer): string? ---@field private on_read fun(err: string?, data: string?) diff --git a/runtime/lua/vim/range.lua b/runtime/lua/vim/range.lua index aafc8da65f..0689b15d81 100644 --- a/runtime/lua/vim/range.lua +++ b/runtime/lua/vim/range.lua @@ -113,7 +113,8 @@ function M.new(...) if start.buf ~= end_.buf then error('start and end positions must belong to the same buffer') end - start_row, start_col, end_row, end_col, buf = start[1], start[2], end_[1], end_[2], start.buf + start_row, start_col, end_row, end_col, buf = + start.row, start.col, end_.row, end_.col, start.buf elseif nargs == 5 then ---@type integer, integer, integer, integer, integer buf, start_row, start_col, end_row, end_col = ... @@ -225,8 +226,8 @@ function M.has(outer, inner) if getmetatable(inner) == vim.pos then ---@cast inner -vim.Range - return util.cmp_pos.le(outer[1], outer[2], inner[1], inner[2]) - and util.cmp_pos.ge(outer[3], outer[4], inner[1], inner[2]) + return util.cmp_pos.le(outer[1], outer[2], inner.row, inner.col) + and util.cmp_pos.ge(outer[3], outer[4], inner.row, inner.col) end ---@cast inner -vim.Pos diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 0eaff2e554..aa50696238 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -314,6 +314,8 @@ end ---@return integer ---@package function TSTreeView:iter() + -- TODO(lewis6991): EmmyLua 0.25.1's ipairs annotation omits the table and initial index. + --- @diagnostic disable-next-line: missing-return-value return ipairs(self.opts.anon and self.nodes or self.named) end diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 9a0378fd11..7b287f26c3 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -109,7 +109,7 @@ local TSCallbackNames = { ---@field private _num_valid_regions integer Number of valid regions ---@field private _is_entirely_valid boolean Whether the entire tree (excluding children) is valid. ---@field private _logger? fun(logtype: string, msg: string) ----@field private _logfile? file* +---@field private _logfile? file local LanguageTree = {} ---Optional arguments: @@ -139,7 +139,7 @@ function LanguageTree.new(source, lang, opts) local injections = opts.injections or {} - --- @class vim.treesitter.LanguageTree + --- @type vim.treesitter.LanguageTree local self = { _source = source, _lang = lang, diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index a612870475..73d4ee7991 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -548,6 +548,7 @@ local predicate_handlers = { return impl['contains'](match, source, predicate, true) end, + --- @param predicate any[] & { string_set?: table } ['any-of?'] = function(match, _, source, predicate) local nodes = match[predicate[2]] if not nodes or #nodes == 0 then @@ -559,7 +560,7 @@ local predicate_handlers = { -- Since 'predicate' will not be used by callers of this function, use it -- to store a string set built from the list of words to check against. - local string_set = predicate['string_set'] --- @type table + local string_set = predicate['string_set'] if not string_set then string_set = {} for i = 3, #predicate do diff --git a/runtime/lua/vim/tty.lua b/runtime/lua/vim/tty.lua index 73de366bc1..d9c2a119e9 100644 --- a/runtime/lua/vim/tty.lua +++ b/runtime/lua/vim/tty.lua @@ -10,7 +10,7 @@ local M = {} --- ---@param payload string Sequence to send via nvim_ui_send(). Use empty string ('') to just register --- a listener (no sending). ----@param opts? { timeout?: integer, on_timeout?: fun(), group?: integer|string, chan?: integer } +---@param opts? { timeout?: integer, on_timeout?: fun(), group?: integer|string, chan?: integer } # --- - `timeout` (default: 1000) ms to wait before giving up, or 0 for never (caller must remove the autocmd). --- - `on_timeout` optional fn called when the timeout fires. --- - `group`: augroup for the TermResponse autocmd. diff --git a/runtime/pack/dist/opt/nvim.tohtml/lua/tohtml.lua b/runtime/pack/dist/opt/nvim.tohtml/lua/tohtml.lua index d509a3d036..7daa62597b 100644 --- a/runtime/pack/dist/opt/nvim.tohtml/lua/tohtml.lua +++ b/runtime/pack/dist/opt/nvim.tohtml/lua/tohtml.lua @@ -465,8 +465,7 @@ local function styletable_treesitter(state) query:iter_captures(root, buf_highlighter.bufnr, state.start - 1, state.end_) do local srow, scol, erow, ecol = node:range() - --- @diagnostic disable-next-line: invisible - local c = q._query.captures[capture] + local c = query.captures[capture] if c ~= nil then local hlid = register_hl(state, '@' .. c .. '.' .. tree:lang()) if metadata.conceal and state.opt.conceallevel ~= 0 then diff --git a/runtime/plugin/shada.lua b/runtime/plugin/shada.lua index 48aa6d2244..ee56b3b023 100644 --- a/runtime/plugin/shada.lua +++ b/runtime/plugin/shada.lua @@ -69,7 +69,7 @@ def_autocmd({ 'FileWriteCmd', 'FileAppendCmd' }, {}, function(ev) vim.fn.getline( math.min(vim.fn.line("'["), vim.fn.line("']")), math.max(vim.fn.line("'["), vim.fn.line("']")) - ) --[=[@as string[]]=] + ) --[=[@as string[] ]=] ), ev.file, ev.event == 'FileAppendCmd' and 'ab' or 'b' diff --git a/src/gen/luacats_grammar.lua b/src/gen/luacats_grammar.lua index 151e8103d3..02d086435f 100644 --- a/src/gen/luacats_grammar.lua +++ b/src/gen/luacats_grammar.lua @@ -91,14 +91,14 @@ local v = setmetatable({}, { --- @field generics? string[] --- @field parent? string --- @field parent_generics? string[] ---- @field access? 'private'|'protected'|'package' +--- @field access? 'private'|'protected'|'package'|'internal' --- @class nvim.luacats.Field --- @field kind 'field' --- @field name string --- @field type string --- @field desc? string ---- @field access? 'private'|'protected'|'package' +--- @field access? 'private'|'protected'|'package'|'internal' --- @class nvim.luacats.Note --- @field desc? string @@ -173,7 +173,7 @@ local function generic_opt(name) return (Pf('<') * Cg(Ct(comma1(typedef)), name) * Plf('>')) + -Plf('<') end -local access = P('private') + P('protected') + P('package') +local access = P('private') + P('protected') + P('package') + P('internal') local caccess = Cg(access, 'access') local cattr = Cg(comma(access + P('exact')), 'access') local desc_delim = Sf '#:' + ws diff --git a/src/gen/luacats_parser.lua b/src/gen/luacats_parser.lua index 8fabd1e6aa..c56bdc1593 100644 --- a/src/gen/luacats_parser.lua +++ b/src/gen/luacats_parser.lua @@ -25,7 +25,7 @@ local luacats_grammar = require('gen.luacats_grammar') --- @field overloads string[] --- @field returns nvim.luacats.parser.return[] --- @field desc string ---- @field access? 'private'|'package'|'protected' +--- @field access? 'private'|'package'|'protected'|'internal' --- @field class? string --- @field module? string --- @field modvar? string @@ -198,6 +198,8 @@ local function process_doc_line(line, state) cur_obj.access = 'package' elseif kind == 'protected' then cur_obj.access = 'protected' + elseif kind == 'internal' then + cur_obj.access = 'internal' elseif kind == 'deprecated' then cur_obj.deprecated = true elseif kind == 'inlinedoc' then diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua index ded79eadf9..27f95f8204 100644 --- a/test/functional/plugin/lsp/snippet_spec.lua +++ b/test/functional/plugin/lsp/snippet_spec.lua @@ -12,8 +12,9 @@ describe('vim.lsp._snippet_grammar', function() before_each(n.clear) local parse = function(...) - local res = exec_lua('return require("vim.lsp._snippet_grammar").parse(...)', ...) - return res.data.children + local results = exec_lua('return { require("vim.lsp._snippet_grammar").parse(...) }', ...) + eq(1, #results) + return results[1].data.children end it('parses only text', function() diff --git a/test/functional/script/luacats_parser_spec.lua b/test/functional/script/luacats_parser_spec.lua index 98b6d08ca4..ce18634cb0 100644 --- a/test/functional/script/luacats_parser_spec.lua +++ b/test/functional/script/luacats_parser_spec.lua @@ -107,6 +107,28 @@ describe('luacats parser', function() exp ) + for _, access in ipairs({ 'internal', 'internal, exact' }) do + it('tracks internal visibility with (' .. access .. ')', function() + local classes, funs = parser.parse_str( + dedent([[ + --- @class (%s) vim.MyClass + --- @field internal value string + local MyClass = {} + + --- @internal + function MyClass.get() end + + return MyClass + ]]):format(access), + 'runtime/lua/vim/myclass.lua' + ) + + eq(access, classes['vim.MyClass'].access) + eq('internal', classes['vim.MyClass'].fields[1].access) + eq('internal', funs[1].access) + end) + end + it('tracks class member declaration style', function() local classes, funs = parser.parse_str( dedent([[ --- @class vim.MyClass