diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/autoload/man.vim | 12 | ||||
-rw-r--r-- | runtime/doc/api.txt | 105 | ||||
-rw-r--r-- | runtime/doc/diagnostic.txt | 113 | ||||
-rw-r--r-- | runtime/doc/editing.txt | 11 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 11 | ||||
-rw-r--r-- | runtime/doc/lsp.txt | 6 | ||||
-rw-r--r-- | runtime/doc/lua.txt | 14 | ||||
-rw-r--r-- | runtime/doc/options.txt | 6 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 16 | ||||
-rw-r--r-- | runtime/lua/vim/diagnostic.lua | 289 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/codelens.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 21 |
13 files changed, 356 insertions, 256 deletions
diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 4f132b6121..90d353f9de 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -197,13 +197,21 @@ function! s:extract_sect_and_name_ref(ref) abort if empty(name) throw 'manpage reference cannot contain only parentheses' endif - return ['', name] + return ['', s:spaces_to_underscores(name)] endif let left = split(ref, '(') " see ':Man 3X curses' on why tolower. " TODO(nhooyr) Not sure if this is portable across OSs " but I have not seen a single uppercase section. - return [tolower(split(left[1], ')')[0]), left[0]] + return [tolower(split(left[1], ')')[0]), s:spaces_to_underscores(left[0])] +endfunction + +" replace spaces in a man page name with underscores +" intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)'; +" while editing SQL source code, it's nice to visually select 'CREATE TABLE' +" and hit 'K', which requires this transformation +function! s:spaces_to_underscores(str) + return substitute(a:str, ' ', '_', 'g') endfunction function! s:get_path(sect, name) abort diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 1ad0a6883e..1573bec7ac 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -531,6 +531,20 @@ nvim__get_hl_defs({ns_id}) *nvim__get_hl_defs()* nvim__get_lib_dir() *nvim__get_lib_dir()* TODO: Documentation +nvim__get_runtime({pat}, {all}, {*opts}) *nvim__get_runtime()* + Find files in runtime directories + + Attributes: ~ + {fast} + + Parameters: ~ + {pat} pattern of files to search for + {all} whether to return all matches or only the first + {options} is_lua: only search lua subdirs + + Return: ~ + list of absolute paths to the found files + nvim__id({obj}) *nvim__id()* Returns object given as argument. @@ -582,6 +596,9 @@ nvim__id_float({flt}) *nvim__id_float()* nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* TODO: Documentation +nvim__runtime_inspect() *nvim__runtime_inspect()* + TODO: Documentation + nvim__screenshot({path}) *nvim__screenshot()* TODO: Documentation @@ -802,6 +819,39 @@ nvim_eval({expr}) *nvim_eval()* Return: ~ Evaluation result or expanded object +nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()* + Evaluates statusline string. + + Attributes: ~ + {fast} + + Parameters: ~ + {str} Statusline string (see 'statusline'). + {opts} Optional parameters. + • winid: (number) |window-ID| of the window to use + as context for statusline. + • maxwidth: (number) Maximum width of statusline. + • fillchar: (string) Character to fill blank + spaces in the statusline (see 'fillchars'). + • highlights: (boolean) Return highlight + information. + • use_tabline: (boolean) Evaluate tabline instead + of statusline. When |TRUE|, {winid} is ignored. + + Return: ~ + Dictionary containing statusline information, with these + keys: + • str: (string) Characters that will be displayed on the + statusline. + • width: (number) Display width of the statusline. + • highlights: Array containing highlight information of + the statusline. Only included when the "highlights" key + in {opts} is |TRUE|. Each element of the array is a + |Dictionary| with these keys: + • start: (number) Byte index (0-based) of first + character that uses the highlight. + • group: (string) Name of highlight group. + nvim_exec({src}, {output}) *nvim_exec()* Executes Vimscript (multiline block of Ex-commands), like anonymous |:source|. @@ -1827,7 +1877,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* callbacks. {opts} Optional parameters. • on_lines: Lua callback invoked on change. - Return`true`to detach. Args: + Return `true` to detach. Args: • the string "lines" • buffer handle • b:changedtick @@ -1843,7 +1893,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* • on_bytes: lua callback invoked on change. This callback receives more granular information about the change compared to - on_lines. Return`true`to detach. Args: + on_lines. Return `true` to detach. Args: • the string "bytes" • buffer handle • b:changedtick @@ -2282,7 +2332,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) • hl_mode : control how highlights are combined with the highlights of the text. Currently only affects virt_text highlights, but might - affect`hl_group`in later versions. + affect `hl_group` in later versions. • "replace": only show the virt_text color. This is the default • "combine": combine with background text @@ -2692,28 +2742,29 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* {buffer} Buffer to display, or 0 for current buffer {enter} Enter the window (make it the current window) {config} Map defining the window configuration. Keys: - • `relative`: Sets the window layout to "floating", placed - at (row,col) coordinates relative to: + • relative: Sets the window layout to + "floating", placed at (row,col) coordinates + relative to: • "editor" The global editor grid • "win" Window given by the `win` field, or current window. • "cursor" Cursor position in current window. - • `win` : |window-ID| for relative="win". - • `anchor`: Decides which corner of the float to place - at (row,col): + • win: |window-ID| for relative="win". + • anchor: Decides which corner of the float to + place at (row,col): • "NW" northwest (default) • "NE" northeast • "SW" southwest • "SE" southeast - • `width` : Window width (in character cells). + • width: Window width (in character cells). Minimum of 1. - • `height` : Window height (in character cells). + • height: Window height (in character cells). Minimum of 1. - • `bufpos`: Places float relative to buffer text (only - when relative="win"). Takes a tuple of - zero-indexed [line, column].`row`and`col`if given are applied relative to this + • bufpos: Places float relative to buffer text + (only when relative="win"). Takes a tuple of + zero-indexed [line, column]. `row` and `col` if given are applied relative to this position, else they default to: • `row=1` and `col=0` if `anchor` is "NW" or "NE" @@ -2721,19 +2772,19 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* "SE" (thus like a tooltip near the buffer text). - • `row` : Row position in units of "screen cell + • row: Row position in units of "screen cell height", may be fractional. - • `col` : Column position in units of "screen - cell width", may be fractional. - • `focusable` : Enable focus by user actions + • col: Column position in units of "screen cell + width", may be fractional. + • focusable: Enable focus by user actions (wincmds, mouse events). Defaults to true. Non-focusable windows can be entered by |nvim_set_current_win()|. - • `external` : GUI should display the window as - an external top-level window. Currently - accepts no other positioning configuration - together with this. - • `zindex`: Stacking order. floats with higher`zindex`go on top on floats with lower indices. Must + • external: GUI should display the window as an + external top-level window. Currently accepts + no other positioning configuration together + with this. + • zindex: Stacking order. floats with higher `zindex` go on top on floats with lower indices. Must be larger than zero. The following screen elements have hard-coded z-indices: • 100: insert completion popupmenu @@ -2744,7 +2795,7 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* are recommended, unless there is a good reason to overshadow builtin elements. - • `style`: Configure the appearance of the window. + • style: Configure the appearance of the window. Currently only takes one non-empty value: • "minimal" Nvim will display the window with many UI options disabled. This is useful @@ -2759,9 +2810,9 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* and clearing the |EndOfBuffer| region in 'winhighlight'. - • `border`: Style of (optional) window border. This can - either be a string or an array. The string - values are + • border: Style of (optional) window border. + This can either be a string or an array. The + string values are • "none": No border (default). • "single": A single line box. • "double": A double line box. @@ -2791,7 +2842,7 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* It could also be specified by character: [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. - • `noautocmd` : If true then no buffer-related + • noautocmd: If true then no buffer-related autocommand events such as |BufEnter|, |BufLeave| or |BufWinEnter| may fire from calling this function. diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 17d317522b..4d1e5ac997 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -262,12 +262,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()* For example, if a user enables virtual text globally with > - vim.diagnostic.config({virt_text = true}) + vim.diagnostic.config({virtual_text = true}) < and a diagnostic producer sets diagnostics with > - vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false}) + vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false}) < then virtual text will not be enabled for those diagnostics. @@ -302,7 +302,7 @@ config({opts}, {namespace}) *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. Example:> + to display the diagnostic. Example: > function(diagnostic) if diagnostic.severity == vim.diagnostic.severity.ERROR then @@ -324,6 +324,18 @@ config({opts}, {namespace}) *vim.diagnostic.config()* Otherwise, all signs use the same priority. + • float: Options for floating windows: + • severity: See |diagnostic-severity|. + • show_header: (boolean, default true) Show + "Diagnostics:" header + • source: (string) Include the diagnostic + source in the message. One of "always" or + "if_many". + • 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. + • update_in_insert: (default false) Update diagnostics in Insert mode (if false, diagnostics are updated on InsertLeave) @@ -442,12 +454,10 @@ goto_next({opts}) *vim.diagnostic.goto_next()* • wrap: (boolean, default true) Whether to loop around file or not. Similar to 'wrapscan'. • severity: See |diagnostic-severity|. - • enable_popup: (boolean, default true) Call - |vim.diagnostic.show_line_diagnostics()| on - jump. - • popup_opts: (table) Table to pass as {opts} - parameter to - |vim.diagnostic.show_line_diagnostics()| + • 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()|. • win_id: (number, default 0) Window ID goto_prev({opts}) *vim.diagnostic.goto_prev()* @@ -508,6 +518,46 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults}) diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}. +open_float({bufnr}, {opts}) *vim.diagnostic.open_float()* + Show diagnostics in a floating window. + + Parameters: ~ + {bufnr} number|nil Buffer number. Defaults to the current + buffer. + {opts} table|nil Configuration table with the same keys + as |vim.lsp.util.open_floating_preview()| in + addition to the following: + • namespace: (number) Limit diagnostics to the + given namespace + • scope: (string, default "buffer") Show + diagnostics from the whole buffer ("buffer"), + the current cursor line ("line"), or the + current cursor position ("cursor"). + • pos: (number or table) If {scope} is "line" or + "cursor", use this position rather than the + cursor position. If a number, interpreted as a + line number; 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()|. + • show_header: (boolean, default true) Show + "Diagnostics:" header. Overrides the setting + from |vim.diagnostic.config()|. + • source: (string) Include the diagnostic source + in the message. One of "always" or "if_many". + 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()|. + + Return: ~ + tuple ({float_bufnr}, {win_id}) + reset({namespace}, {bufnr}) *vim.diagnostic.reset()* Remove all diagnostics from the given namespace. @@ -581,51 +631,6 @@ show({namespace}, {bufnr}, {diagnostics}, {opts}) {opts} table|nil Display options. See |vim.diagnostic.config()|. - *vim.diagnostic.show_line_diagnostics()* -show_line_diagnostics({opts}, {bufnr}, {lnum}) - Open a floating window with the diagnostics from the given - line. - - Parameters: ~ - {opts} table Configuration table. See - |vim.diagnostic.show_position_diagnostics()|. - {bufnr} number|nil Buffer number. Defaults to the current - buffer. - {lnum} number|nil Line number. Defaults to line number - of cursor. - - Return: ~ - tuple ({popup_bufnr}, {win_id}) - - *vim.diagnostic.show_position_diagnostics()* -show_position_diagnostics({opts}, {bufnr}, {position}) - Open a floating window with the diagnostics at the given - position. - - Parameters: ~ - {opts} table|nil Configuration table with the same - keys as |vim.lsp.util.open_floating_preview()| - in addition to the following: - • namespace: (number) Limit diagnostics to the - given namespace - • severity: See |diagnostic-severity|. - • show_header: (boolean, default true) Show - "Diagnostics:" header - • source: (string) Include the diagnostic - source in the message. One of "always" or - "if_many". - • 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. - {bufnr} number|nil Buffer number. Defaults to the - current buffer. - {position} table|nil The (0,0)-indexed position. Defaults - to the current cursor position. - - Return: ~ - tuple ({popup_bufnr}, {win_id}) - toqflist({diagnostics}) *vim.diagnostic.toqflist()* Convert a list of diagnostics to a list of quickfix items that can be passed to |setqflist()| or |setloclist()|. diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index efba9020f9..4e3173cfa9 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1331,11 +1331,12 @@ window the current directory is changed to the last specified local current directory. If none was specified, the global or tab-local directory is used. When changing tabs the same behaviour applies. If the current tab has no -local working directory the global working directory is used. When a |:cd| -command is used, the current window and tab will lose their local current -directories and will use the global current directory from now on. When -a |:tcd| command is used, only the current window will lose its local working -directory. +local working directory the global working directory is used. + +When a |:cd| command is used, the current window and tab will lose their local +current directories and will use the global current directory from now on. +When a |:tcd| command is used, only the current window will lose its local +working directory. After using |:cd| the full path name will be used for reading and writing files. On some networked file systems this may cause problems. The result of diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4467afaa16..6549d0b5f3 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2461,8 +2461,8 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) has({feature}) Number |TRUE| if feature {feature} supported has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key} haslocaldir([{winnr} [, {tabnr}]]) - Number |TRUE| if current window executed |:lcd| - or |:tcd| + Number |TRUE| if the window executed |:lcd| or + the tab executed |:tcd| hasmapto({what} [, {mode} [, {abbr}]]) Number |TRUE| if mapping to {what} exists histadd({history}, {item}) String add an item to a history @@ -5012,6 +5012,7 @@ getcwd([{winnr}[, {tabnr}]]) *getcwd()* {winnr} can be the window number or the |window-ID|. If both {winnr} and {tabnr} are -1 the global working directory is returned. + Throw error if the arguments are invalid. |E5000| |E5001| |E5002| Can also be used as a |method|: > GetWinnr()->getcwd() @@ -5774,8 +5775,9 @@ has_key({dict}, {key}) *has_key()* mydict->has_key(key) haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()* - The result is a Number, which is 1 when the tabpage or window - has set a local path via |:tcd| or |:lcd|, otherwise 0. + The result is a Number, which is 1 when the window has set a + local path via |:lcd| or when {winnr} is -1 and the tabpage + has set a local path via |:tcd|, otherwise 0. Tabs and windows are identified by their respective numbers, 0 means current tab or window. Missing argument implies 0. @@ -5787,6 +5789,7 @@ haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()* With {winnr} and {tabnr} use the window in that tabpage. {winnr} can be the window number or the |window-ID|. If {winnr} is -1 it is ignored, only the tab is resolved. + Throw error if the arguments are invalid. |E5000| |E5001| |E5002| Can also be used as a |method|: > GetWinnr()->haslocaldir() diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index ac797de92d..5549d3c180 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -628,6 +628,10 @@ client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* flush({client}) *vim.lsp.flush()* TODO: Documentation + *vim.lsp.for_each_buffer_client()* +for_each_buffer_client({bufnr}, {fn}) + TODO: Documentation + get_active_clients() *vim.lsp.get_active_clients()* Gets all active clients. @@ -666,7 +670,7 @@ omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* {base} If findstart=0, text to match against Return: ~ - (number) Decided by`findstart`: + (number) Decided by {findstart}: • findstart=0: column where the completion starts, or -2 or -3 • findstart=1: list of matches (actually just calls diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index a6ccf9f35c..fe94e89e2a 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -928,6 +928,7 @@ Example: > vim.g.foo = 5 -- Set the g:foo Vimscript variable. print(vim.g.foo) -- Get and print the g:foo Vimscript variable. vim.g.foo = nil -- Delete (:unlet) the Vimscript variable. + vim.b[2].foo = 6 -- Set b:foo for buffer 2 vim.g *vim.g* Global (|g:|) editor variables. @@ -935,15 +936,18 @@ vim.g *vim.g* vim.b *vim.b* Buffer-scoped (|b:|) variables for the current buffer. - Invalid or unset key returns `nil`. + Invalid or unset key returns `nil`. Can be indexed with + an integer to access variables for a specific buffer. vim.w *vim.w* Window-scoped (|w:|) variables for the current window. - Invalid or unset key returns `nil`. + Invalid or unset key returns `nil`. Can be indexed with + an integer to access variables for a specific window. vim.t *vim.t* Tabpage-scoped (|t:|) variables for the current tabpage. - Invalid or unset key returns `nil`. + Invalid or unset key returns `nil`. Can be indexed with + an integer to access variables for a specific tabpage. vim.v *vim.v* |v:| variables. @@ -1518,7 +1522,7 @@ tbl_flatten({t}) *vim.tbl_flatten()* Flattened copy of the given list-like table. See also: ~ - Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + From https://github.com/premake/premake-core/blob/master/src/base/table.lua tbl_isempty({t}) *vim.tbl_isempty()* Checks if a table is empty. @@ -1554,7 +1558,7 @@ tbl_keys({t}) *vim.tbl_keys()* list of keys See also: ~ - Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + From https://github.com/premake/premake-core/blob/master/src/base/table.lua tbl_map({func}, {t}) *vim.tbl_map()* Apply a function to all values of a table. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 3520d40591..be397117b2 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1159,9 +1159,9 @@ A jump table for the options with a short description can be found at |Q_op|. 'cdpath' 'cd' string (default: equivalent to $CDPATH or ",,") global This is a list of directories which will be searched when using the - |:cd| and |:lcd| commands, provided that the directory being searched - for has a relative path, not an absolute part starting with "/", "./" - or "../", the 'cdpath' option is not used then. + |:cd|, |:tcd| and |:lcd| commands, provided that the directory being + searched for has a relative path, not an absolute part starting with + "/", "./" or "../", the 'cdpath' option is not used then. The 'cdpath' option's value has the same form and semantics as |'path'|. Also see |file-searching|. The default value is taken from $CDPATH, with a "," prepended to look diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 7e7bb7d62f..77bf1d29eb 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -180,7 +180,6 @@ Commands: |:match| can be invoked before highlight group is defined Events: - |DirChanged| can be triggered when switching to another window |Signal| |TabNewEntered| |TermClose| @@ -197,10 +196,6 @@ Functions: |stdpath()| |system()|, |systemlist()| can run {cmd} directly (without 'shell') |matchadd()| can be called before highlight group is defined - |getcwd()| and |haslocaldir()| may throw errors. *E5000* *E5001* *E5002* - |haslocaldir()|'s only possible return values are 0 and 1, it never returns 2. - `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global - working directory. Use `getcwd(-1, -1)` to get the global working directory. Highlight groups: |highlight-blend| controls blend level for a highlight group @@ -253,6 +248,8 @@ Variables: Nvim always builds with all features, in contrast to Vim which may have certain features removed/added at compile-time. |feature-compile| +Some Vim features were changed in Nvim, and vice versa. + If a Python interpreter is available on your `$PATH`, |:python| and |:python3| are always available and may be used simultaneously. See |provider-python|. @@ -428,6 +425,15 @@ Vimscript compatibility: `shell_error` does not alias to |v:shell_error| `this_session` does not alias to |v:this_session| +Working directory (Vim implemented some of these later than Nvim): +- |DirChanged| can be triggered when switching to another window. +- |getcwd()| and |haslocaldir()| may throw errors if the tab page or window + cannot be found. *E5000* *E5001* *E5002* +- |haslocaldir()| only checks for tab-local directory when -1 is passed as + window number, and its only possible returns values are 0 and 1. +- `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global + working directory. Use `getcwd(-1, -1)` to get the global working directory. + ============================================================================== 5. Missing legacy features *nvim-features-missing* diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 326932d982..4cf22282a2 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -19,6 +19,7 @@ local global_diagnostic_options = { signs = true, underline = true, virtual_text = true, + float = true, update_in_insert = false, severity_sort = false, } @@ -119,8 +120,8 @@ end ---@private local function enabled_value(option, namespace) - local ns = get_namespace(namespace) - if type(ns.opts[option]) == "table" then + local ns = namespace and get_namespace(namespace) or {} + if ns.opts and type(ns.opts[option]) == "table" then return ns.opts[option] end @@ -153,8 +154,9 @@ end ---@private local function get_resolved_options(opts, namespace, bufnr) - local ns = get_namespace(namespace) - local resolved = vim.tbl_extend('keep', opts or {}, ns.opts, global_diagnostic_options) + local ns = namespace and get_namespace(namespace) or {} + -- Do not use tbl_deep_extend so that an empty table can be used to reset to default values + local resolved = vim.tbl_extend('keep', opts or {}, ns.opts or {}, global_diagnostic_options) for k in pairs(global_diagnostic_options) do if resolved[k] ~= nil then resolved[k] = resolve_optional_value(k, resolved[k], namespace, bufnr) @@ -354,19 +356,15 @@ local function schedule_display(namespace, bufnr, args) local key = make_augroup_key(namespace, bufnr) if not registered_autocmds[key] then - vim.cmd(string.format("augroup %s", key)) - vim.cmd(" au!") - vim.cmd( - string.format( - [[autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)]], - table.concat(insert_leave_auto_cmds, ","), - bufnr, - namespace, - bufnr - ) - ) - vim.cmd("augroup END") - + vim.cmd(string.format([[augroup %s + au! + autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s) + augroup END]], + key, + table.concat(insert_leave_auto_cmds, ","), + bufnr, + namespace, + bufnr)) registered_autocmds[key] = true end end @@ -376,77 +374,14 @@ local function clear_scheduled_display(namespace, bufnr) local key = make_augroup_key(namespace, bufnr) if registered_autocmds[key] then - vim.cmd(string.format("augroup %s", key)) - vim.cmd(" au!") - vim.cmd("augroup END") - + vim.cmd(string.format([[augroup %s + au! + augroup END]], key)) registered_autocmds[key] = nil end end ---@private ---- Open a floating window with the provided diagnostics ----@param opts table Configuration table ---- - show_header (boolean, default true): Show "Diagnostics:" header ---- - all opts for |vim.util.open_floating_preview()| can be used here ----@param diagnostics table: The diagnostics to display ----@return table {popup_bufnr, win_id} -local function show_diagnostics(opts, diagnostics) - if not diagnostics or vim.tbl_isempty(diagnostics) then - return - end - local lines = {} - local highlights = {} - local show_header = vim.F.if_nil(opts.show_header, true) - if show_header then - table.insert(lines, "Diagnostics:") - table.insert(highlights, {0, "Bold"}) - end - - if opts.format then - diagnostics = reformat_diagnostics(opts.format, diagnostics) - end - - if opts.source then - diagnostics = prefix_source(opts.source, diagnostics) - end - - -- Use global setting for severity_sort since 'show_diagnostics' is namespace - -- independent - local severity_sort = global_diagnostic_options.severity_sort - if severity_sort then - if type(severity_sort) == "table" and severity_sort.reverse then - table.sort(diagnostics, function(a, b) return a.severity > b.severity end) - else - table.sort(diagnostics, function(a, b) return a.severity < b.severity end) - end - end - - for i, diagnostic in ipairs(diagnostics) do - local prefix = string.format("%d. ", i) - local hiname = floating_highlight_map[diagnostic.severity] - assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity)) - - local message_lines = vim.split(diagnostic.message, '\n', true) - table.insert(lines, prefix..message_lines[1]) - table.insert(highlights, {#prefix, hiname}) - for j = 2, #message_lines do - table.insert(lines, string.rep(' ', #prefix) .. message_lines[j]) - table.insert(highlights, {0, hiname}) - end - end - - local popup_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts) - for i, hi in ipairs(highlights) do - local prefixlen, hiname = unpack(hi) - -- Start highlight after the prefix - vim.api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1) - end - - return popup_bufnr, winnr -end - ----@private local function set_list(loclist, opts) opts = opts or {} local open = vim.F.if_nil(opts.open, true) @@ -469,6 +404,7 @@ local function set_list(loclist, opts) end ---@private +--- To (slightly) improve performance, modifies diagnostics in place. local function clamp_line_numbers(bufnr, diagnostics) local buf_line_count = vim.api.nvim_buf_line_count(bufnr) if buf_line_count == 0 then @@ -526,7 +462,7 @@ end local function diagnostic_move_pos(opts, pos) opts = opts or {} - local enable_popup = vim.F.if_nil(opts.enable_popup, true) + local float = vim.F.if_nil(opts.float, true) local win_id = opts.win_id or vim.api.nvim_get_current_win() if not pos then @@ -539,10 +475,13 @@ local function diagnostic_move_pos(opts, pos) vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) - if enable_popup then - -- This is a bit weird... I'm surprised that we need to wait til the next tick to do this. + if float then + local float_opts = type(float) == "table" and float or {} vim.schedule(function() - M.show_position_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id)) + M.open_float( + vim.api.nvim_win_get_buf(win_id), + vim.tbl_extend("keep", float_opts, {scope="cursor"}) + ) end) end end @@ -561,12 +500,12 @@ end --- --- For example, if a user enables virtual text globally with --- <pre> ---- vim.diagnostic.config({virt_text = true}) +--- vim.diagnostic.config({virtual_text = true}) --- </pre> --- --- and a diagnostic producer sets diagnostics with --- <pre> ---- vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false}) +--- vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false}) --- </pre> --- --- then virtual text will not be enabled for those diagnostics. @@ -603,6 +542,13 @@ end --- * 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. +--- - float: Options for floating windows: +--- * severity: See |diagnostic-severity|. +--- * show_header: (boolean, default true) Show "Diagnostics:" header +--- * source: (string) Include the diagnostic source in +--- the message. One of "always" or "if_many". +--- * 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. --- - update_in_insert: (default false) Update diagnostics in Insert mode (if false, --- diagnostics are updated on InsertLeave) --- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in @@ -825,10 +771,9 @@ end --- |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|. ---- - enable_popup: (boolean, default true) Call |vim.diagnostic.show_line_diagnostics()| ---- on jump. ---- - popup_opts: (table) Table to pass as {opts} parameter to ---- |vim.diagnostic.show_line_diagnostics()| +--- - 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()|. --- - win_id: (number, default 0) Window ID function M.goto_next(opts) return diagnostic_move_pos( @@ -1144,68 +1089,136 @@ function M.show(namespace, bufnr, diagnostics, opts) save_extmarks(namespace, bufnr) end ---- Open a floating window with the diagnostics at the given position. +--- Show diagnostics in a floating window. --- +---@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param opts table|nil Configuration table with the same keys as --- |vim.lsp.util.open_floating_preview()| in addition to the following: --- - namespace: (number) Limit diagnostics to the given namespace ---- - severity: See |diagnostic-severity|. ---- - show_header: (boolean, default true) Show "Diagnostics:" header ---- - source: (string) Include the diagnostic source in ---- the message. One of "always" or "if_many". +--- - scope: (string, default "buffer") Show diagnostics from the whole buffer ("buffer"), +--- the current cursor line ("line"), or the current cursor position ("cursor"). +--- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather +--- than the cursor position. If a number, interpreted as a line number; +--- 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()|. +--- - show_header: (boolean, default true) Show "Diagnostics:" header. Overrides the +--- setting from |vim.diagnostic.config()|. +--- - source: (string) Include the diagnostic source in the message. One of "always" or +--- "if_many". 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. ----@param bufnr number|nil Buffer number. Defaults to the current buffer. ----@param position table|nil The (0,0)-indexed position. Defaults to the current cursor position. ----@return tuple ({popup_bufnr}, {win_id}) -function M.show_position_diagnostics(opts, bufnr, position) +--- Overrides the setting from |vim.diagnostic.config()|. +---@return tuple ({float_bufnr}, {win_id}) +function M.open_float(bufnr, opts) vim.validate { - opts = { opts, 't', true }, bufnr = { bufnr, 'n', true }, - position = { position, 't', true }, + opts = { opts, 't', true }, } opts = opts or {} - - opts.focus_id = "position_diagnostics" bufnr = get_bufnr(bufnr) - if not position then - local curr_position = vim.api.nvim_win_get_cursor(0) - curr_position[1] = curr_position[1] - 1 - position = curr_position - end - local match_position_predicate = function(diag) - return position[1] == diag.lnum and - position[2] >= diag.col and - (position[2] <= diag.end_col or position[1] < diag.end_lnum) + local scope = opts.scope or "buffer" + local lnum, col + if scope == "line" or scope == "cursor" then + if not opts.pos then + local pos = vim.api.nvim_win_get_cursor(0) + lnum = pos[1] - 1 + col = pos[2] + elseif type(opts.pos) == "number" then + lnum = opts.pos + elseif type(opts.pos) == "table" then + lnum, col = unpack(opts.pos) + else + error("Invalid value for option 'pos'") + end + elseif scope ~= "buffer" then + error("Invalid value for option 'scope'") end + local diagnostics = M.get(bufnr, opts) clamp_line_numbers(bufnr, diagnostics) - local position_diagnostics = vim.tbl_filter(match_position_predicate, diagnostics) - return show_diagnostics(opts, position_diagnostics) -end ---- Open a floating window with the diagnostics from the given line. ---- ----@param opts table Configuration table. See |vim.diagnostic.show_position_diagnostics()|. ----@param bufnr number|nil Buffer number. Defaults to the current buffer. ----@param lnum number|nil Line number. Defaults to line number of cursor. ----@return tuple ({popup_bufnr}, {win_id}) -function M.show_line_diagnostics(opts, bufnr, lnum) - vim.validate { - opts = { opts, 't', true }, - bufnr = { bufnr, 'n', true }, - lnum = { lnum, 'n', true }, - } + if scope == "line" then + diagnostics = vim.tbl_filter(function(d) + return d.lnum == lnum + end, diagnostics) + elseif scope == "cursor" then + -- LSP servers can send diagnostics with `end_col` past the length of the line + local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1] + diagnostics = vim.tbl_filter(function(d) + return d.lnum == lnum + and math.min(d.col, line_length - 1) <= col + and (d.end_col >= col or d.end_lnum > lnum) + end, diagnostics) + end - opts = opts or {} - opts.focus_id = "line_diagnostics" - bufnr = get_bufnr(bufnr) - local diagnostics = M.get(bufnr, opts) - clamp_line_numbers(bufnr, diagnostics) - lnum = lnum or (vim.api.nvim_win_get_cursor(0)[1] - 1) - local line_diagnostics = diagnostic_lines(diagnostics)[lnum] - return show_diagnostics(opts, line_diagnostics) + if vim.tbl_isempty(diagnostics) then + return + end + + local severity_sort = vim.F.if_nil(opts.severity_sort, global_diagnostic_options.severity_sort) + if severity_sort then + if type(severity_sort) == "table" and severity_sort.reverse then + table.sort(diagnostics, function(a, b) return a.severity > b.severity end) + else + table.sort(diagnostics, function(a, b) return a.severity < b.severity end) + end + end + + do + -- Resolve options with user settings from vim.diagnostic.config + -- Unlike the other decoration functions (e.g. set_virtual_text, set_signs, etc.) `open_float` + -- does not have a dedicated table for configuration options; instead, the options are mixed in + -- with its `opts` table which also includes "keyword" parameters. So we create a dedicated + -- options table that inherits missing keys from the global configuration before resolving. + local t = global_diagnostic_options.float + local float_opts = vim.tbl_extend("keep", opts, type(t) == "table" and t or {}) + opts = get_resolved_options({ float = float_opts }, nil, bufnr).float + end + + local lines = {} + local highlights = {} + local show_header = vim.F.if_nil(opts.show_header, true) + if show_header then + table.insert(lines, "Diagnostics:") + table.insert(highlights, {0, "Bold"}) + end + + if opts.format then + diagnostics = reformat_diagnostics(opts.format, diagnostics) + end + + if opts.source then + diagnostics = prefix_source(opts.source, diagnostics) + end + + for i, diagnostic in ipairs(diagnostics) do + local prefix = string.format("%d. ", i) + local hiname = floating_highlight_map[diagnostic.severity] + local message_lines = vim.split(diagnostic.message, '\n') + table.insert(lines, prefix..message_lines[1]) + table.insert(highlights, {#prefix, hiname}) + for j = 2, #message_lines do + table.insert(lines, string.rep(' ', #prefix) .. message_lines[j]) + table.insert(highlights, {0, hiname}) + end + end + + -- Used by open_floating_preview to allow the float to be focused + if not opts.focus_id then + opts.focus_id = scope + end + local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts) + for i, hi in ipairs(highlights) do + local prefixlen, hiname = unpack(hi) + -- Start highlight after the prefix + vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i-1, prefixlen, -1) + end + + return float_bufnr, winnr end --- Remove all diagnostics from the given namespace. diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9c35351608..ad6c90cd03 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1430,7 +1430,7 @@ end ---@param findstart 0 or 1, decides behavior ---@param base If findstart=0, text to match against --- ----@returns (number) Decided by `findstart`: +---@returns (number) Decided by {findstart}: --- - findstart=0: column where the completion starts, or -2 or -3 --- - findstart=1: list of matches (actually just calls |complete()|) function lsp.omnifunc(findstart, base) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 6d3912781e..63fcbe430b 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -138,7 +138,8 @@ function M.display(lenses, bufnr, client_id) end end if #chunks > 0 then - api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks }) + api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks, + hl_mode="combine" }) end end end @@ -199,7 +200,8 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) ns, lens.range.start.line, 0, - { virt_text = {{ lens.command.title, 'LspCodeLens' }} } + { virt_text = {{ lens.command.title, 'LspCodeLens' }}, + hl_mode="combine" } ) end countdown() diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 0e63c0dd29..8fb7971c5a 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -551,14 +551,15 @@ end ---@param position table|nil The (0,0)-indexed position ---@return table {popup_bufnr, win_id} function M.show_position_diagnostics(opts, buf_nr, position) - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} - end + opts = opts or {} + opts.where = "cursor" + opts.pos = position + if opts.severity then + opts.severity = severity_lsp_to_vim(opts.severity) + elseif opts.severity_limit then + opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} end - return vim.diagnostic.show_position_diagnostics(opts, buf_nr, position) + return vim.diagnostic.open_float(buf_nr, opts) end --- Open a floating window with the diagnostics from {line_nr} @@ -573,11 +574,13 @@ end ---@param client_id number|nil the client id ---@return table {popup_bufnr, win_id} function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) + opts = opts or {} + opts.where = "line" + opts.pos = line_nr if client_id then - opts = opts or {} opts.namespace = M.get_namespace(client_id) end - return vim.diagnostic.show_line_diagnostics(opts, buf_nr, line_nr) + return vim.diagnostic.open_float(buf_nr, opts) end --- Redraw diagnostics for the given buffer and client |