fix(lua): improve annotations for stricter luals diagnostics (#24609)

Problem: luals returns stricter diagnostics with bundled luarc.json
Solution: Improve some function and type annotations:

* use recognized uv.* types 
* disable diagnostic for global `vim` in shared.lua
* docs: don't start comment lines with taglink (otherwise LuaLS will interpret it as a type)
* add type alias for lpeg pattern
* fix return annotation for `vim.secure.trust`
* rename local Range object in vim.version (shadows `Range` in vim.treesitter)
* fix some "missing fields" warnings
* add missing required fields for test functions in eval.lua
* rename lsp meta files for consistency
This commit is contained in:
Christian Clason 2023-08-09 11:06:13 +02:00 committed by GitHub
parent 8afdc1f386
commit c43c745a14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 111 additions and 95 deletions

View File

@ -119,7 +119,7 @@ These dependencies are "vendored" (inlined), we must update the sources manually
* `runtime/lua/vim/inspect.lua`: [inspect.lua](https://github.com/kikito/inspect.lua)
* `src/nvim/tui/terminfo_defs.h`: terminfo definitions
* Run `scripts/update_terminfo.sh` to update these definitions.
* `runtime/lua/vim/lsp/types/protocol.lua`: LSP specification
* `runtime/lua/vim/lsp/_meta/protocol.lua`: LSP specification
* Run `scripts/gen_lsp.lua` to update.
* `src/bit.c`: only for PUC lua: port of `require'bit'` from luajit https://bitop.luajit.org/
* [treesitter parsers](https://github.com/neovim/neovim/blob/fcc24e43e0b5f9d801a01ff2b8f78ce8c16dd551/cmake.deps/CMakeLists.txt#L197-L210)

View File

@ -1857,6 +1857,7 @@ foldtextresult({lnum}) *foldtextresult()*
line, "'m" mark m, etc.
Useful when exporting folded text, e.g., to HTML.
fullcommand({name}) *fullcommand()*
Get the full command name from a short abbreviated command
name; see |20.2| for details on command abbreviations.
@ -7993,6 +7994,13 @@ termopen({cmd} [, {opts}]) *termopen()*
except $TERM is set to "xterm-256color". Full behavior is
described in |terminal|.
test_garbagecollect_now() *test_garbagecollect_now()*
Like |garbagecollect()|, but executed right away. This must
only be called directly to avoid any structure to exist
internally, and |v:testing| must have been set before calling
any function.
timer_info([{id}]) *timer_info()*
Return a list with information about timers.
When {id} is given only information about this timer is

View File

@ -459,7 +459,7 @@ fromqflist({list}) *vim.diagnostic.fromqflist()*
Convert a list of quickfix items to a list of diagnostics.
Parameters: ~
• {list} (table) A list of quickfix items from |getqflist()| or
• {list} table[] List of quickfix items from |getqflist()| or
|getloclist()|.
Return: ~

View File

@ -2127,7 +2127,8 @@ request({method}, {params}, {callback}, {notify_reply_callback})
• {method} (string) The invoked LSP method
• {params} (table|nil) Parameters for the invoked LSP
method
• {callback} fun(err: lsp.ResponseError | nil, result: any) Callback to invoke
• {callback} fun(err: lsp.ResponseError | nil, result:
any) Callback to invoke
• {notify_reply_callback} (function|nil) Callback to invoke as soon as
a request is no longer pending

View File

@ -2073,7 +2073,7 @@ vim.spairs({t}) *vim.spairs()*
• {t} (table) Dict-like table
Return: ~
iterator over sorted keys and their values
(function) iterator over sorted keys and their values
See also: ~
• Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua
@ -3007,10 +3007,9 @@ vim.secure.trust({opts}) *vim.secure.trust()*
• bufnr (number|nil): Buffer number to update. Mutually
exclusive with {path}.
Return: ~
(boolean, string) success, msg:
• true and full path of target file if operation was successful
• false and error message on failure
Return (multiple): ~
(boolean) success true if operation was successful
(string) msg full path if operation was successful, else error message
==============================================================================

View File

@ -31,11 +31,7 @@ Find more information in the file src/testdir/README.txt.
==============================================================================
2. Test functions *test-functions-details*
test_garbagecollect_now() *test_garbagecollect_now()*
Like garbagecollect(), but executed right away. This must
only be called directly to avoid any structure to exist
internally, and |v:testing| must have been set before calling
any function.
See |test_garbagecollect_now()|.
==============================================================================
3. Assert functions *assert-functions-details*

View File

@ -620,8 +620,8 @@ local on_key_cbs = {}
---
---@param fn fun(key: string) Function invoked on every key press. |i_CTRL-V|
--- Returning nil removes the callback associated with namespace {ns_id}.
---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a new
--- |nvim_create_namespace()| id.
---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a
--- new |nvim_create_namespace()| id.
---
---@return integer Namespace id associated with {fn}. Or count of all callbacks
---if on_key() is called without arguments.

View File

@ -1,14 +1,13 @@
local uv = vim.uv
--- @class SystemOpts
--- @field cmd string[]
--- @field stdin string|string[]|true
--- @field stdout fun(err:string, data: string)|false
--- @field stderr fun(err:string, data: string)|false
--- @field stdin? string|string[]|true
--- @field stdout? fun(err:string, data: string)|false
--- @field stderr? fun(err:string, data: string)|false
--- @field cwd? string
--- @field env? table<string,string|number>
--- @field clear_env? boolean
--- @field text boolean?
--- @field text? boolean
--- @field timeout? integer Timeout in ms
--- @field detach? boolean
@ -19,15 +18,14 @@ local uv = vim.uv
--- @field stderr? string
--- @class SystemState
--- @field handle uv_process_t
--- @field timer uv_timer_t
--- @field pid integer
--- @field handle? uv.uv_process_t
--- @field timer? uv.uv_timer_t
--- @field pid? integer
--- @field timeout? integer
--- @field done boolean
--- @field stdin uv_stream_t?
--- @field stdout uv_stream_t?
--- @field stderr uv_stream_t?
--- @field cmd string[]
--- @field done? boolean
--- @field stdin? uv.uv_stream_t
--- @field stdout? uv.uv_stream_t
--- @field stderr? uv.uv_stream_t
--- @field result? SystemCompleted
---@param state SystemState
@ -128,7 +126,7 @@ function SystemObj:is_closing()
end
---@param output function|'false'
---@return uv_stream_t?
---@return uv.uv_stream_t?
---@return function? Handler
local function setup_output(output)
if output == nil then
@ -144,7 +142,7 @@ local function setup_output(output)
end
---@param input string|string[]|true|nil
---@return uv_stream_t?
---@return uv.uv_stream_t?
---@return string|string[]?
local function setup_input(input)
if not input then
@ -189,7 +187,7 @@ local function setup_env(env, clear_env)
return renv
end
--- @param stream uv_stream_t
--- @param stream uv.uv_stream_t
--- @param text? boolean
--- @param bucket string[]
--- @return fun(err: string?, data: string?)
@ -217,7 +215,7 @@ local M = {}
--- @param opts uv.aliases.spawn_options
--- @param on_exit fun(code: integer, signal: integer)
--- @param on_error fun()
--- @return uv_process_t, integer
--- @return uv.uv_process_t, integer
local function spawn(cmd, opts, on_exit, on_error)
local handle, pid_or_err = uv.spawn(cmd, opts, on_exit)
if not handle then

View File

@ -17,7 +17,7 @@ end
--- Stops and closes a libuv |uv_fs_event_t| or |uv_fs_poll_t| handle
---
---@param handle (uv_fs_event_t|uv_fs_poll_t) The handle to stop
---@param handle (uv.uv_fs_event_t|uv.uv_fs_poll_t) The handle to stop
local function stop(handle)
local _, stop_err = handle:stop()
assert(not stop_err, stop_err)
@ -79,7 +79,7 @@ local default_poll_interval_ms = 2000
--- @field children? table<string,watch.Watches>
--- @field cancel? fun()
--- @field started? boolean
--- @field handle? uv_fs_poll_t
--- @field handle? uv.uv_fs_poll_t
--- @class watch.PollOpts
--- @field interval? integer

View File

@ -562,8 +562,8 @@ end
---@param opts table|nil When omitted or "nil", retrieve the current configuration. Otherwise, a
--- configuration table with the following keys:
--- - underline: (default true) Use underline for diagnostics. Options:
--- * severity: Only underline diagnostics matching the given severity
--- |diagnostic-severity|
--- * severity: Only underline diagnostics matching the given
--- severity |diagnostic-severity|
--- - virtual_text: (default true) Use virtual text for diagnostics. If multiple diagnostics
--- are set for a namespace, one prefix per diagnostic + the last diagnostic
--- message are shown.
@ -596,8 +596,8 @@ end
--- end
--- </pre>
--- - signs: (default true) Use signs for diagnostics. Options:
--- * severity: Only show signs for diagnostics matching the given severity
--- |diagnostic-severity|
--- * severity: Only show signs for diagnostics matching the given
--- severity |diagnostic-severity|
--- * priority: (number, default 10) Base priority to use for signs. When
--- {severity_sort} is used, the priority of a sign is adjusted based on
--- its severity. Otherwise, all signs use the same priority.
@ -723,17 +723,17 @@ function M.get_namespaces()
end
---@class Diagnostic
---@field bufnr integer
---@field bufnr? integer
---@field lnum integer 0-indexed
---@field end_lnum nil|integer 0-indexed
---@field end_lnum? integer 0-indexed
---@field col integer 0-indexed
---@field end_col nil|integer 0-indexed
---@field severity DiagnosticSeverity
---@field end_col? integer 0-indexed
---@field severity? DiagnosticSeverity
---@field message string
---@field source nil|string
---@field code nil|string
---@field _tags { deprecated: boolean, unnecessary: boolean}
---@field user_data nil|any arbitrary data plugins can add
---@field source? string
---@field code? string
---@field _tags? { deprecated: boolean, unnecessary: boolean}
---@field user_data? any arbitrary data plugins can add
--- Get current diagnostics.
---
@ -819,13 +819,13 @@ end
---
---@param opts table|nil Configuration table with the following keys:
--- - namespace: (number) Only consider diagnostics from the given namespace.
--- - cursor_position: (cursor position) Cursor position as a (row, col) tuple. See
--- |nvim_win_get_cursor()|. Defaults to the current cursor position.
--- - cursor_position: (cursor position) Cursor position as a (row, col) tuple.
--- See |nvim_win_get_cursor()|. Defaults to the current cursor position.
--- - wrap: (boolean, default true) Whether to loop around file or not. Similar to 'wrapscan'.
--- - severity: See |diagnostic-severity|.
--- - float: (boolean or table, default true) If "true", call |vim.diagnostic.open_float()|
--- after moving. If a table, pass the table as the {opts} parameter to
--- |vim.diagnostic.open_float()|. Unless overridden, the float will show
--- after moving. If a table, pass the table as the {opts} parameter
--- to |vim.diagnostic.open_float()|. Unless overridden, the float will show
--- diagnostics at the new cursor position (as if "cursor" were passed to
--- the "scope" option).
--- - win_id: (number, default 0) Window ID
@ -1213,8 +1213,8 @@ end
--- Show diagnostics in a floating window.
---
---@param opts table|nil Configuration table with the same keys as
--- |vim.lsp.util.open_floating_preview()| in addition to the following:
---@param opts table|nil Configuration table with the same keys
--- as |vim.lsp.util.open_floating_preview()| in addition to the following:
--- - bufnr: (number) Buffer number to show diagnostics from.
--- Defaults to the current buffer.
--- - namespace: (number) Limit diagnostics to the given namespace
@ -1227,16 +1227,15 @@ end
--- otherwise, a (row, col) tuple.
--- - severity_sort: (default false) Sort diagnostics by severity. Overrides the setting
--- from |vim.diagnostic.config()|.
--- - severity: See |diagnostic-severity|. Overrides the setting from
--- |vim.diagnostic.config()|.
--- - severity: See |diagnostic-severity|. Overrides the setting
--- from |vim.diagnostic.config()|.
--- - header: (string or table) String to use as the header for the floating window. If a
--- table, it is interpreted as a [text, hl_group] tuple. Overrides the setting
--- from |vim.diagnostic.config()|.
--- - source: (boolean or string) Include the diagnostic source in the message.
--- Use "if_many" to only show sources if there is more than one source of
--- diagnostics in the buffer. Otherwise, any truthy value means to always show
--- the diagnostic source. Overrides the setting from
--- |vim.diagnostic.config()|.
--- the diagnostic source. Overrides the setting from |vim.diagnostic.config()|.
--- - format: (function) A function that takes a diagnostic as input and returns a
--- string. The return value is the text used to display the diagnostic.
--- Overrides the setting from |vim.diagnostic.config()|.
@ -1692,8 +1691,7 @@ end
--- Convert a list of quickfix items to a list of diagnostics.
---
---@param list table A list of quickfix items from |getqflist()| or
--- |getloclist()|.
---@param list table[] List of quickfix items from |getqflist()| or |getloclist()|.
---@return Diagnostic[] array of |diagnostic-structure|
function M.fromqflist(list)
vim.validate({

View File

@ -18,7 +18,7 @@ local M = {}
---@class ModuleInfo
---@field modpath string Path of the module
---@field modname string Name of the module
---@field stat? uv_fs_t File stat of the module path
---@field stat? uv.uv_fs_t File stat of the module path
---@alias LoaderStats table<string, {total:number, time:number, [string]:number?}?>

View File

@ -364,7 +364,7 @@ do
--- @field lines string[] snapshot of buffer lines from last didChange
--- @field lines_tmp string[]
--- @field pending_changes table[] List of debounced changes in incremental sync mode
--- @field timer nil|uv_timer_t uv_timer
--- @field timer nil|uv.uv_timer_t uv_timer
--- @field last_flush nil|number uv.hrtime of the last flush/didChange-notification
--- @field needs_flush boolean true if buffer updates haven't been sent to clients/servers yet
--- @field refs integer how many clients are using this group

View File

@ -1,4 +1,5 @@
---@meta
error('Cannot require a meta file')
---@alias lsp-handler fun(err: lsp.ResponseError|nil, result: any, context: lsp.HandlerContext, config: table|nil): any?

View File

@ -1,9 +1,12 @@
--[[
This file is autogenerated from scripts/gen_lsp.lua
Regenerate:
nvim -l scripts/gen_lsp.lua gen --version 3.18 --runtime/lua/vim/lsp/types/protocol.lua
nvim -l scripts/gen_lsp.lua gen --version 3.18 --runtime/lua/vim/lsp/_meta/protocol.lua
--]]
---@meta
error('Cannot require a meta file')
---@alias lsp.null nil
---@alias uinteger integer
---@alias lsp.decimal number

View File

@ -6,11 +6,13 @@ local lpeg = vim.lpeg
local M = {}
---@alias lpeg userdata
--- Parses the raw pattern into an |lpeg| pattern. LPeg patterns natively support the "this" or "that"
--- alternative constructions described in the LSP spec that cannot be expressed in a standard Lua pattern.
---
---@param pattern string The raw glob pattern
---@return userdata An |lpeg| representation of the pattern, or nil if the pattern is invalid.
---@return lpeg An |lpeg| representation of the pattern, or nil if the pattern is invalid.
local function parse(pattern)
local l = lpeg
@ -109,7 +111,7 @@ local to_lsp_change_type = {
--- Default excludes the same as VSCode's `files.watcherExclude` setting.
--- https://github.com/microsoft/vscode/blob/eef30e7165e19b33daa1e15e92fa34ff4a5df0d3/src/vs/workbench/contrib/files/browser/files.contribution.ts#L261
---@type Lpeg pattern
---@type lpeg parsed Lpeg pattern
M._poll_exclude_pattern = parse('**/.git/{objects,subtree-cache}/**')
+ parse('**/node_modules/*/**')
+ parse('**/.hg/store/**')
@ -132,7 +134,7 @@ function M.register(reg, ctx)
if not has_capability or not client.workspace_folders then
return
end
local watch_regs = {} --- @type table<string,{pattern:userdata,kind:integer}>
local watch_regs = {} --- @type table<string,{pattern:lpeg,kind:integer}>
for _, w in ipairs(reg.registerOptions.watchers) do
local relative_pattern = false
local glob_patterns = {} --- @type {baseUri:string, pattern: string}[]

View File

@ -5,8 +5,8 @@ local api = vim.api
local M = {}
---@class lsp.inlay_hint.bufstate
---@field version integer
---@field client_hint table<integer, table<integer, lsp.InlayHint[]>> client_id -> (lnum -> hints)
---@field version? integer
---@field client_hint? table<integer, table<integer, lsp.InlayHint[]>> client_id -> (lnum -> hints)
---@field applied table<integer, integer> Last version of hints applied to this line
---@field enabled boolean Whether inlay hints are enabled for this buffer
---@type table<integer, lsp.inlay_hint.bufstate>

View File

@ -14,11 +14,11 @@ local uv = vim.uv
--- @field marked boolean whether this token has had extmarks applied
---
--- @class STCurrentResult
--- @field version integer document version associated with this result
--- @field result_id string resultId from the server; used with delta requests
--- @field highlights STTokenRange[] cache of highlight ranges for this document version
--- @field tokens integer[] raw token array as received by the server. used for calculating delta responses
--- @field namespace_cleared boolean whether the namespace was cleared for this result yet
--- @field version? integer document version associated with this result
--- @field result_id? string resultId from the server; used with delta requests
--- @field highlights? STTokenRange[] cache of highlight ranges for this document version
--- @field tokens? integer[] raw token array as received by the server. used for calculating delta responses
--- @field namespace_cleared? boolean whether the namespace was cleared for this result yet
---
--- @class STActiveRequest
--- @field request_id integer the LSP request ID of the most recent request sent to the server
@ -717,8 +717,7 @@ end
--- mark will be deleted by the semantic token engine when appropriate; for
--- example, when the LSP sends updated tokens. This function is intended for
--- use inside |LspTokenUpdate| callbacks.
---@param token (table) a semantic token, found as `args.data.token` in
--- |LspTokenUpdate|.
---@param token (table) a semantic token, found as `args.data.token` in |LspTokenUpdate|.
---@param bufnr (integer) the buffer to highlight
---@param client_id (integer) The ID of the |vim.lsp.client|
---@param hl_group (string) Highlight group name

View File

@ -715,8 +715,8 @@ end
--- Turns the result of a `textDocument/completion` request into vim-compatible
--- |complete-items|.
---
---@param result table The result of a `textDocument/completion` call, e.g. from
---|vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`,
---@param result table The result of a `textDocument/completion` call, e.g.
--- from |vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`,
--- `CompletionList` or `null`
---@param prefix (string) the prefix to filter the completion items
---@return table { matches = complete-items table, incomplete = bool }

View File

@ -119,9 +119,8 @@ end
--- - path (string|nil): Path to a file to update. Mutually exclusive with {bufnr}.
--- Cannot be used when {action} is "allow".
--- - bufnr (number|nil): Buffer number to update. Mutually exclusive with {path}.
---@return (boolean, string) success, msg:
--- - true and full path of target file if operation was successful
--- - false and error message on failure
---@return boolean success true if operation was successful
---@return string msg full path if operation was successful, else error message
function M.trust(opts)
vim.validate({
path = { opts.path, 's', true },

View File

@ -6,6 +6,7 @@
-- or the test suite. (Eventually the test suite will be run in a worker process,
-- so this wouldn't be a separate case to consider)
---@diagnostic disable-next-line: lowercase-global
vim = vim or {}
local function _id(v)
@ -533,7 +534,7 @@ end
---@see Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua
---
---@param t table Dict-like table
---@return # iterator over sorted keys and their values
---@return function iterator over sorted keys and their values
function vim.spairs(t)
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))

View File

@ -434,7 +434,7 @@ local predicate_handlers = {
predicate_handlers['vim-match?'] = predicate_handlers['match?']
---@class TSMetadata
---@field range Range
---@field range? Range
---@field [integer] TSMetadata
---@field [string] integer|string

View File

@ -212,15 +212,15 @@ function M.last(versions)
return last
end
---@class Range
---@class VersionRange
---@field from Version
---@field to? Version
local Range = {}
local VersionRange = {}
--- @private
---
---@param version string|Version
function Range:has(version)
function VersionRange:has(version)
if type(version) == 'string' then
---@diagnostic disable-next-line: cast-local-type
version = M.parse(version)
@ -266,7 +266,7 @@ end
--- @param spec string Version range "spec"
function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
if spec == '*' or spec == '' then
return setmetatable({ from = M.parse('0.0.0') }, { __index = Range })
return setmetatable({ from = M.parse('0.0.0') }, { __index = VersionRange })
end
---@type number?
@ -280,7 +280,7 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
return setmetatable({
from = ra and ra.from,
to = rb and (#parts == 3 and rb.from or rb.to),
}, { __index = Range })
}, { __index = VersionRange })
end
---@type string, string
local mods, version = spec:lower():match('^([%^=<>~]*)(.*)$')
@ -326,7 +326,7 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
end
end
end
return setmetatable({ from = from, to = to }, { __index = Range })
return setmetatable({ from = from, to = to }, { __index = VersionRange })
end
end

View File

@ -1,9 +1,9 @@
--[[
Generates lua-ls annotations for lsp
USAGE:
nvim -l scripts/gen_lsp.lua gen # this will overwrite runtime/lua/vim/lsp/types/protocol.lua
nvim -l scripts/gen_lsp.lua gen # this will overwrite runtime/lua/vim/lsp/_meta/protocol.lua
nvim -l scripts/gen_lsp.lua gen --version 3.18 --build/new_lsp_types.lua
nvim -l scripts/gen_lsp.lua gen --version 3.18 --out runtime/lua/vim/lsp/types/protocol.lua
nvim -l scripts/gen_lsp.lua gen --version 3.18 --out runtime/lua/vim/lsp/_meta/protocol.lua
nvim -l scripts/gen_lsp.lua gen --version 3.18 --methods
--]]
@ -110,7 +110,7 @@ function M.gen(opt)
'--[[',
'This file is autogenerated from scripts/gen_lsp.lua',
'Regenerate:',
[=[nvim -l scripts/gen_lsp.lua gen --version 3.18 --runtime/lua/vim/lsp/types/protocol.lua]=],
[=[nvim -l scripts/gen_lsp.lua gen --version 3.18 --runtime/lua/vim/lsp/_meta/protocol.lua]=],
'--]]',
'',
'---@alias lsp.null nil',
@ -264,7 +264,7 @@ function M.gen(opt)
end
local opt = {
output_file = 'runtime/lua/vim/lsp/types/protocol.lua',
output_file = 'runtime/lua/vim/lsp/_meta/protocol.lua',
version = nil,
methods = nil,
}

View File

@ -2898,6 +2898,9 @@ M.funcs = {
signature = 'foldtextresult({lnum})',
},
foreground = {
args = 0,
params = {},
signature = '',
lua = false,
},
fullcommand = {
@ -11326,10 +11329,21 @@ M.funcs = {
signature = 'termopen({cmd} [, {opts}])',
},
test_garbagecollect_now = {
args = 0,
desc = [=[
Like |garbagecollect()|, but executed right away. This must
only be called directly to avoid any structure to exist
internally, and |v:testing| must have been set before calling
any function.
]=],
params = {},
signature = 'test_garbagecollect_now()',
lua = false,
},
test_write_list_log = {
args = 1,
params = { { 'fname' } },
signature = '',
lua = false,
},
timer_info = {

View File

@ -12,7 +12,7 @@ local Set = {}
--- @param items? string[]
function Set:new(items)
local obj = {} --- @ type Set
local obj = {} --- @type Set
setmetatable(obj, self)
self.__index = self
@ -33,10 +33,7 @@ end
--- @return Set
function Set:copy()
local obj = {} --- @ type Set
obj.nelem = self.nelem
obj.tbl = {}
obj.items = {}
local obj = {nelem = self.nelem, tbl = {}, items = {}} --- @type Set
for k, v in pairs(self.tbl) do
obj.tbl[k] = v
end