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