aboutsummaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/autoload/man.vim12
-rw-r--r--runtime/doc/api.txt111
-rw-r--r--runtime/doc/deprecated.txt3
-rw-r--r--runtime/doc/diagnostic.txt263
-rw-r--r--runtime/doc/editing.txt11
-rw-r--r--runtime/doc/eval.txt11
-rw-r--r--runtime/doc/lsp.txt31
-rw-r--r--runtime/doc/lua.txt38
-rw-r--r--runtime/doc/map.txt3
-rw-r--r--runtime/doc/options.txt6
-rw-r--r--runtime/doc/vim_diff.txt16
-rw-r--r--runtime/ftplugin/changelog.vim26
-rw-r--r--runtime/ftplugin/nsis.vim4
-rw-r--r--runtime/indent/dosbatch.vim4
-rw-r--r--runtime/indent/nsis.vim4
-rw-r--r--runtime/indent/teraterm.vim4
-rw-r--r--runtime/lua/vim/diagnostic.lua643
-rw-r--r--runtime/lua/vim/lsp.lua147
-rw-r--r--runtime/lua/vim/lsp/buf.lua5
-rw-r--r--runtime/lua/vim/lsp/codelens.lua32
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua31
-rw-r--r--runtime/lua/vim/lsp/handlers.lua2
-rw-r--r--runtime/lua/vim/lsp/rpc.lua29
-rw-r--r--runtime/lua/vim/lsp/util.lua38
-rw-r--r--runtime/lua/vim/ui.lua5
-rw-r--r--runtime/syntax/arduino.vim99
-rw-r--r--runtime/syntax/debchangelog.vim5
-rw-r--r--runtime/syntax/debsources.vim5
-rw-r--r--runtime/syntax/nsis.vim42
29 files changed, 1026 insertions, 604 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..bb8e83f84a 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|.
@@ -1028,7 +1078,7 @@ nvim_get_keymap({mode}) *nvim_get_keymap()*
Array of maparg()-like dictionaries describing mappings.
The "buffer" key is always zero.
-nvim_get_mark({name}) *nvim_get_mark()*
+nvim_get_mark({name}, {opts}) *nvim_get_mark()*
Return a tuple (row, col, buffer, buffername) representing the
position of the uppercase/file named mark. See |mark-motions|.
@@ -1040,6 +1090,7 @@ nvim_get_mark({name}) *nvim_get_mark()*
Parameters: ~
{name} Mark name
+ {opts} Optional parameters. Reserved for future use.
Return: ~
4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if
@@ -1827,7 +1878,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 +1894,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 +2333,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
@@ -2370,7 +2421,7 @@ nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement})
{replacement} Array of lines to use as replacement
*nvim_buf_set_mark()*
-nvim_buf_set_mark({buffer}, {name}, {line}, {col})
+nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts})
Sets a named mark in the given buffer, all marks are allowed
file/uppercase, visual, last change, etc. See |mark-motions|.
@@ -2384,6 +2435,7 @@ nvim_buf_set_mark({buffer}, {name}, {line}, {col})
{name} Mark name
{line} Line number
{col} Column/row number
+ {opts} Optional parameters. Reserved for future use.
Return: ~
true if the mark was set, else false.
@@ -2692,28 +2744,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 +2774,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 +2797,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 +2812,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 +2844,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/deprecated.txt b/runtime/doc/deprecated.txt
index a7ce4135af..21a34178b3 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -85,6 +85,9 @@ For each of the functions below, use the corresponding function in
*vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead.
*vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead.
*vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead.
+
+The following have been replaced by |vim.diagnostic.open_float()|.
+
*vim.lsp.diagnostic.show_line_diagnostics()*
*vim.lsp.diagnostic.show_position_diagnostics()*
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index 17d317522b..d121dba435 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -75,6 +75,100 @@ Functions that take a severity as an optional parameter (e.g.
The latter form allows users to specify a range of severities.
==============================================================================
+HANDLERS *diagnostic-handlers*
+
+Diagnostics are shown to the user with |vim.diagnostic.show()|. The display of
+diagnostics is managed through handlers. A handler is a table with a "show"
+and (optionally) a "hide" function. The "show" function has the signature
+>
+ function(namespace, bufnr, diagnostics, opts)
+<
+and is responsible for displaying or otherwise handling the given
+diagnostics. The "hide" function takes care of "cleaning up" any actions taken
+by the "show" function and has the signature
+>
+ function(namespace, bufnr)
+<
+Handlers can be configured with |vim.diagnostic.config()| and added by
+creating a new key in `vim.diagnostic.handlers` (see
+|diagnostic-handlers-example|).
+
+The {opts} table passed to a handler is the full set of configuration options
+(that is, it is not limited to just the options for the handler itself). The
+values in the table are already resolved (i.e. if a user specifies a
+function for a config option, the function has already been evaluated).
+
+Nvim provides these handlers by default: "virtual_text", "signs", and
+"underline".
+
+ *diagnostic-handlers-example*
+The example below creates a new handler that notifies the user of diagnostics
+with |vim.notify()|: >
+
+ -- It's good practice to namespace custom handlers to avoid collisions
+ vim.diagnostic.handlers["my/notify"] = {
+ show = function(namespace, bufnr, diagnostics, opts)
+ -- In our example, the opts table has a "log_level" option
+ local level = opts["my/notify"].log_level
+
+ local name = vim.diagnostic.get_namespace(namespace).name
+ local msg = string.format("%d diagnostics in buffer %d from %s",
+ #diagnostics,
+ bufnr,
+ name)
+ vim.notify(msg, level)
+ end,
+ }
+
+ -- Users can configure the handler
+ vim.diagnostic.config({
+ ["my/notify"] = {
+ log_level = vim.log.levels.INFO
+ }
+ })
+<
+In this example, there is nothing to do when diagnostics are hidden, so we
+omit the "hide" function.
+
+Existing handlers can be overriden. For example, use the following to only
+show a sign for the highest severity diagnostic on a given line: >
+
+ -- Create a custom namespace. This will aggregate signs from all other
+ -- namespaces and only show the one with the highest severity on a
+ -- given line
+ local ns = vim.api.nvim_create_namespace("my_namespace")
+
+ -- Get a reference to the original signs handler
+ local orig_signs_handler = vim.diagnostic.handlers.signs
+
+ -- Override the built-in signs handler
+ vim.diagnostic.handlers.signs = {
+ show = function(_, bufnr, _, opts)
+ -- Get all diagnostics from the whole buffer rather than just the
+ -- diagnostics passed to the handler
+ local diagnostics = vim.diagnostic.get(bufnr)
+
+ -- Find the "worst" diagnostic per line
+ local max_severity_per_line = {}
+ for _, d in pairs(diagnostics) do
+ local m = max_severity_per_line[d.lnum]
+ if not m or d.severity < m.severity then
+ max_severity_per_line[d.lnum] = d
+ end
+ end
+
+ -- Pass the filtered diagnostics (with our custom namespace) to
+ -- the original handler
+ local filtered_diagnostics = vim.tbl_values(max_severity_per_line)
+ orig_signs_handler.show(ns, bufnr, filtered_diagnostics, opts)
+ end,
+ hide = function(_, bufnr)
+ orig_signs_handler.hide(ns, bufnr)
+ end,
+ }
+<
+
+==============================================================================
HIGHLIGHTS *diagnostic-highlights*
All highlights defined for diagnostics begin with `Diagnostic` followed by
@@ -202,51 +296,6 @@ Example: >
autocmd User DiagnosticsChanged lua vim.diagnostic.setqflist({open = false })
<
==============================================================================
-CUSTOMIZATION *diagnostic-config*
-
-If you need more customization over the way diagnostics are displayed than the
-built-in configuration options provide, you can override the display handler
-explicitly. For example, use the following to only show a sign for the highest
-severity diagnostic on a given line: >
-
- -- Disable the default signs handler
- vim.diagnostic.config({signs = false})
-
- -- Create a namespace. This won't be used to add any diagnostics,
- -- only to display them.
- local ns = vim.api.nvim_create_namespace("my_namespace")
-
- -- Create a reference to the original function
- local orig_show = vim.diagnostic.show
-
- local function set_signs(bufnr)
- -- Get all diagnostics from the current buffer
- local diagnostics = vim.diagnostic.get(bufnr)
-
- -- Find the "worst" diagnostic per line
- local max_severity_per_line = {}
- for _, d in pairs(diagnostics) do
- local m = max_severity_per_line[d.lnum]
- if not m or d.severity < m.severity then
- max_severity_per_line[d.lnum] = d
- end
- end
-
- -- Show the filtered diagnostics using the custom namespace. Use the
- -- reference to the original function to avoid a loop.
- local filtered_diagnostics = vim.tbl_values(max_severity_per_line)
- orig_show(ns, bufnr, filtered_diagnostics, {
- virtual_text=false,
- underline=false,
- signs=true
- })
- end
-
- function vim.diagnostic.show(namespace, bufnr, ...)
- orig_show(namespace, bufnr, ...)
- set_signs(bufnr)
- end
-<
==============================================================================
Lua module: vim.diagnostic *diagnostic-api*
@@ -262,12 +311,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 +351,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 +373,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)
@@ -382,6 +443,15 @@ get({bufnr}, {opts}) *vim.diagnostic.get()*
Return: ~
table A list of diagnostic items |diagnostic-structure|.
+get_namespace({namespace}) *vim.diagnostic.get_namespace()*
+ Get namespace metadata.
+
+ Parameters: ~
+ {ns} number Diagnostic namespace
+
+ Return: ~
+ table Namespace metadata
+
get_namespaces() *vim.diagnostic.get_namespaces()*
Get current diagnostic namespaces.
@@ -442,12 +512,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 +576,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.
@@ -569,7 +677,7 @@ show({namespace}, {bufnr}, {diagnostics}, {opts})
Display diagnostics for the given namespace and buffer.
Parameters: ~
- {namespace} number Diagnostic namespace
+ {namespace} number Diagnostic namespace.
{bufnr} number|nil Buffer number. Defaults to the
current buffer.
{diagnostics} table|nil The diagnostics to display. When
@@ -581,51 +689,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..80ac762792 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -61,6 +61,10 @@ Example config (in init.vim): >
-- See `:help omnifunc` and `:help ins-completion` for more information.
vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
+ -- Use LSP as the handler for formatexpr.
+ -- See `:help formatexpr` for more information.
+ vim.api.nvim_buf_set_option(0, 'formatexpr', 'v:lua.vim.lsp.formatexpr()')
+
-- For plugins with an `on_attach` callback, call them here. For example:
-- require('completion').on_attach()
end
@@ -452,6 +456,22 @@ LspSignatureActiveParameter
|vim.lsp.handlers.signature_help()|.
==============================================================================
+EVENTS *lsp-events*
+
+LspProgressUpdate *LspProgressUpdate*
+ Upon receipt of a progress notification from the server. See
+ |vim.lsp.util.get_progress_messages()|.
+
+LspRequest *LspRequest*
+ After a change to the active set of pending LSP requests. See {requests}
+ in |vim.lsp.client|.
+
+Example: >
+ autocmd User LspProgressUpdate redrawstatus
+ autocmd User LspRequest redrawstatus
+<
+
+==============================================================================
Lua module: vim.lsp *lsp-core*
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
@@ -608,6 +628,11 @@ client() *vim.lsp.client*
server.
• {handlers} (table): The handlers used by the client as
described in |lsp-handler|.
+ • {requests} (table): The current pending requests in flight
+ to the server. Entries are key-value pairs with the key
+ being the request ID while the value is a table with `type`,
+ `bufnr`, and `method` key-value pairs. `type` is either "pending"
+ for an active request, or "cancel" for a cancel request.
• {config} (table): copy of the table that was passed by the
user to |vim.lsp.start_client()|.
• {server_capabilities} (table): Response from the server
@@ -628,6 +653,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 +695,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..1e058874bd 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -393,6 +393,14 @@ where the args are converted to Lua values. The expression >
is equivalent to the Lua chunk >
return somemod.func(...)
+In addition, functions of packages can be accessed like >
+ v:lua.require'mypack'.func(arg1, arg2)
+ v:lua.require'mypack.submod'.func(arg1, arg2)
+Note: only single quote form without parens is allowed. Using
+`require"mypack"` or `require('mypack')` as prefixes do NOT work (the latter
+is still valid as a function call of itself, in case require returns a useful
+value).
+
The `v:lua` prefix may be used to call Lua functions as |method|s. For
example: >
arg1->v:lua.somemod.func(arg2)
@@ -409,7 +417,8 @@ For example consider the following Lua omnifunc handler: >
end
vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc')
-Note: the module ("mymod" in the above example) must be a Lua global.
+Note: the module ("mymod" in the above example) must either be a Lua global,
+or use the require syntax as specified above to access it from a package.
Note: `v:lua` without a call is not allowed in a Vimscript expression:
|Funcref|s cannot represent Lua functions. The following are errors: >
@@ -689,15 +698,14 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
------------------------------------------------------------------------------
VIM.MPACK *lua-mpack*
-The *vim.mpack* module provides packing and unpacking of lua objects to
-msgpack encoded strings. |vim.NIL| and |vim.empty_dict()| are supported.
+The *vim.mpack* module provides encoding and decoding of Lua objects to and
+from msgpack-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|.
-vim.mpack.pack({obj}) *vim.mpack.pack*
- Packs a lua object {obj} and returns the msgpack representation as
- a string
+vim.mpack.encode({obj}) *vim.mpack.encode*
+ Encodes (or "packs") Lua object {obj} as msgpack in a Lua string.
-vim.mpack.unpack({str}) *vim.mpack.unpack*
- Unpacks the msgpack encoded {str} and returns a lua object
+vim.mpack.decode({str}) *vim.mpack.decode*
+ Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object.
------------------------------------------------------------------------------
VIM *lua-builtin*
@@ -928,6 +936,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 +944,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 +1530,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 +1566,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/map.txt b/runtime/doc/map.txt
index 64c0d96aed..90d4c4de93 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -877,6 +877,9 @@ Also note that the 'clipboard' option is temporarily emptied to avoid
clobbering the `"*` or `"+` registers, if its value contains the item `unnamed`
or `unnamedplus`.
+The `mode()` function will return the state as it will be after applying the
+operator.
+
==============================================================================
2. Abbreviations *abbreviations* *Abbreviations*
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/ftplugin/changelog.vim b/runtime/ftplugin/changelog.vim
index 257e9cd9d4..e9df63f8c9 100644
--- a/runtime/ftplugin/changelog.vim
+++ b/runtime/ftplugin/changelog.vim
@@ -2,7 +2,7 @@
" Language: generic Changelog file
" Maintainer: Martin Florian <marfl@posteo.de>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2015-10-25
+" Latest Revision: 2021-10-17
" Variables:
" g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) -
" description: the timeformat used in ChangeLog entries.
@@ -55,7 +55,7 @@ if &filetype == 'changelog'
elseif $EMAIL_ADDRESS != ""
return $EMAIL_ADDRESS
endif
-
+
let login = s:login()
return printf('%s <%s@%s>', s:name(login), login, s:hostname())
endfunction
@@ -223,12 +223,6 @@ if &filetype == 'changelog'
let &paste = save_paste
endfunction
- if exists(":NewChangelogEntry") != 2
- nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
- xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
- command! -nargs=0 NewChangelogEntry call s:new_changelog_entry('')
- endif
-
let b:undo_ftplugin = "setl com< fo< et< ai<"
setlocal comments=
@@ -241,14 +235,26 @@ if &filetype == 'changelog'
let b:undo_ftplugin .= " tw<"
endif
+ if !exists("no_plugin_maps") && !exists("no_changelog_maps") && exists(":NewChangelogEntry") != 2
+ nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
+ xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
+ command! -buffer -nargs=0 NewChangelogEntry call s:new_changelog_entry('')
+ let b:undo_ftplugin .= " | sil! exe 'nunmap <buffer> <Leader>o'" .
+ \ " | sil! exe 'vunmap <buffer> <Leader>o'" .
+ \ " | sil! delc NewChangelogEntry"
+ endif
+
let &cpo = s:cpo_save
unlet s:cpo_save
else
let s:cpo_save = &cpo
set cpo&vim
- " Add the Changelog opening mapping
- nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>
+ if !exists("no_plugin_maps") && !exists("no_changelog_maps")
+ " Add the Changelog opening mapping
+ nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>
+ let b:undo_ftplugin .= " | silent! exe 'nunmap <buffer> <Leader>o"
+ endif
function! s:open_changelog()
let path = expand('%:p:h')
diff --git a/runtime/ftplugin/nsis.vim b/runtime/ftplugin/nsis.vim
index 1a35127c86..3dc0d6318a 100644
--- a/runtime/ftplugin/nsis.vim
+++ b/runtime/ftplugin/nsis.vim
@@ -3,7 +3,7 @@
" Maintainer: Ken Takata
" URL: https://github.com/k-takata/vim-nsis
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Last Change: 2018-01-26
+" Last Change: 2021-10-18
if exists("b:did_ftplugin")
finish
@@ -15,7 +15,6 @@ set cpo&vim
let b:did_ftplugin = 1
let b:undo_ftplugin = "setl com< cms< fo< def< inc<"
- \ " | unlet! b:match_ignorecase b:match_words"
setlocal comments=s1:/*,mb:*,ex:*/,b:#,:; commentstring=;\ %s
setlocal formatoptions-=t formatoptions+=croql
@@ -37,6 +36,7 @@ if exists("loaded_matchit")
\ '\${MementoSection}:\${MementoSectionEnd},' .
\ '!if\%(\%(macro\)\?n\?def\)\?\>:!else\>:!endif\>,' .
\ '!macro\>:!macroend\>'
+ let b:undo_ftplugin .= " | unlet! b:match_ignorecase b:match_words"
endif
let &cpo = s:cpo_save
diff --git a/runtime/indent/dosbatch.vim b/runtime/indent/dosbatch.vim
index aea2a184d4..d24b139242 100644
--- a/runtime/indent/dosbatch.vim
+++ b/runtime/indent/dosbatch.vim
@@ -2,7 +2,7 @@
" Language: MSDOS batch file (with NT command extensions)
" Maintainer: Ken Takata
" URL: https://github.com/k-takata/vim-dosbatch-indent
-" Last Change: 2017 May 10
+" Last Change: 2021-10-18
" Filenames: *.bat
" License: VIM License
@@ -17,6 +17,8 @@ setlocal indentexpr=GetDosBatchIndent(v:lnum)
setlocal indentkeys=!^F,o,O
setlocal indentkeys+=0=)
+let b:undo_indent = "setl ai< inde< indk< si<"
+
if exists("*GetDosBatchIndent")
finish
endif
diff --git a/runtime/indent/nsis.vim b/runtime/indent/nsis.vim
index 223f4fa28e..5d3decca37 100644
--- a/runtime/indent/nsis.vim
+++ b/runtime/indent/nsis.vim
@@ -2,7 +2,7 @@
" Language: NSIS script
" Maintainer: Ken Takata
" URL: https://github.com/k-takata/vim-nsis
-" Last Change: 2018-01-21
+" Last Change: 2021-10-18
" Filenames: *.nsi
" License: VIM License
@@ -17,6 +17,8 @@ setlocal indentexpr=GetNsisIndent(v:lnum)
setlocal indentkeys=!^F,o,O
setlocal indentkeys+==~${Else,=~${EndIf,=~${EndUnless,=~${AndIf,=~${AndUnless,=~${OrIf,=~${OrUnless,=~${Case,=~${Default,=~${EndSelect,=~${EndSwith,=~${Loop,=~${Next,=~${MementoSectionEnd,=~FunctionEnd,=~SectionEnd,=~SectionGroupEnd,=~PageExEnd,0=~!macroend,0=~!if,0=~!else,0=~!endif
+let b:undo_indent = "setl ai< inde< indk< si<"
+
if exists("*GetNsisIndent")
finish
endif
diff --git a/runtime/indent/teraterm.vim b/runtime/indent/teraterm.vim
index 35d7354290..181c9a343f 100644
--- a/runtime/indent/teraterm.vim
+++ b/runtime/indent/teraterm.vim
@@ -3,7 +3,7 @@
" Based on Tera Term Version 4.100
" Maintainer: Ken Takata
" URL: https://github.com/k-takata/vim-teraterm
-" Last Change: 2018-08-31
+" Last Change: 2021-10-18
" Filenames: *.ttl
" License: VIM License
@@ -18,6 +18,8 @@ setlocal indentexpr=GetTeraTermIndent(v:lnum)
setlocal indentkeys=!^F,o,O,e
setlocal indentkeys+==elseif,=endif,=loop,=next,=enduntil,=endwhile
+let b:undo_indent = "setl ai< inde< indk< si<"
+
if exists("*GetTeraTermIndent")
finish
endif
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 326932d982..19cad3cec8 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -19,10 +19,21 @@ local global_diagnostic_options = {
signs = true,
underline = true,
virtual_text = true,
+ float = true,
update_in_insert = false,
severity_sort = false,
}
+M.handlers = setmetatable({}, {
+ __newindex = function(t, name, handler)
+ vim.validate { handler = {handler, "t" } }
+ rawset(t, name, handler)
+ if not global_diagnostic_options[name] then
+ global_diagnostic_options[name] = true
+ end
+ end,
+})
+
-- Local functions {{{
---@private
@@ -96,31 +107,9 @@ end
local all_namespaces = {}
---@private
-local function get_namespace(ns)
- if not all_namespaces[ns] then
- local name
- for k, v in pairs(vim.api.nvim_get_namespaces()) do
- if ns == v then
- name = k
- break
- end
- end
-
- assert(name, "namespace does not exist or is anonymous")
-
- all_namespaces[ns] = {
- name = name,
- sign_group = string.format("vim.diagnostic.%s", name),
- opts = {}
- }
- end
- return all_namespaces[ns]
-end
-
----@private
local function enabled_value(option, namespace)
- local ns = get_namespace(namespace)
- if type(ns.opts[option]) == "table" then
+ local ns = namespace and M.get_namespace(namespace) or {}
+ if ns.opts and type(ns.opts[option]) == "table" then
return ns.opts[option]
end
@@ -153,8 +142,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 M.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)
@@ -341,7 +331,7 @@ local registered_autocmds = {}
---@private
local function make_augroup_key(namespace, bufnr)
- local ns = get_namespace(namespace)
+ local ns = M.get_namespace(namespace)
return string.format("DiagnosticInsertLeave:%s:%s", bufnr, ns.name)
end
@@ -354,19 +344,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 +362,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 +392,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 +450,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 +463,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 +488,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 +530,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
@@ -620,7 +554,7 @@ function M.config(opts, namespace)
local t
if namespace then
- local ns = get_namespace(namespace)
+ local ns = M.get_namespace(namespace)
t = ns.opts
else
t = global_diagnostic_options
@@ -687,6 +621,32 @@ function M.set(namespace, bufnr, diagnostics, opts)
vim.api.nvim_command("doautocmd <nomodeline> User DiagnosticsChanged")
end
+--- Get namespace metadata.
+---
+---@param ns number Diagnostic namespace
+---@return table Namespace metadata
+function M.get_namespace(namespace)
+ vim.validate { namespace = { namespace, 'n' } }
+ if not all_namespaces[namespace] then
+ local name
+ for k, v in pairs(vim.api.nvim_get_namespaces()) do
+ if namespace == v then
+ name = k
+ break
+ end
+ end
+
+ assert(name, "namespace does not exist or is anonymous")
+
+ all_namespaces[namespace] = {
+ name = name,
+ opts = {},
+ user_data = {},
+ }
+ end
+ return all_namespaces[namespace]
+end
+
--- Get current diagnostic namespaces.
---
---@return table A list of active diagnostic namespaces |vim.diagnostic|.
@@ -825,10 +785,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(
@@ -837,156 +796,167 @@ function M.goto_next(opts)
)
end
--- Diagnostic Setters {{{
-
---- Set signs for given diagnostics.
----
----@param namespace number The diagnostic namespace
----@param bufnr number Buffer number
----@param diagnostics table A list of diagnostic items |diagnostic-structure|. When omitted the
---- current diagnostics in the given buffer are used.
----@param opts table Configuration table with the following keys:
---- - priority: Set the priority of the signs |sign-priority|.
----@private
-function M._set_signs(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
- opts = {opts, 't', true},
- }
-
- bufnr = get_bufnr(bufnr)
- opts = get_resolved_options({ signs = opts }, namespace, bufnr)
+M.handlers.signs = {
+ show = function(namespace, bufnr, diagnostics, opts)
+ vim.validate {
+ namespace = {namespace, 'n'},
+ bufnr = {bufnr, 'n'},
+ diagnostics = {diagnostics, 't'},
+ opts = {opts, 't', true},
+ }
- if opts.signs and opts.signs.severity then
- diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
- end
+ bufnr = get_bufnr(bufnr)
- local ns = get_namespace(namespace)
+ if opts.signs and opts.signs.severity then
+ diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
+ end
- define_default_signs()
+ define_default_signs()
- -- 10 is the default sign priority when none is explicitly specified
- local priority = opts.signs and opts.signs.priority or 10
- local get_priority
- if opts.severity_sort then
- if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then
- get_priority = function(severity)
- return priority + (severity - vim.diagnostic.severity.ERROR)
+ -- 10 is the default sign priority when none is explicitly specified
+ local priority = opts.signs and opts.signs.priority or 10
+ local get_priority
+ if opts.severity_sort then
+ if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then
+ get_priority = function(severity)
+ return priority + (severity - vim.diagnostic.severity.ERROR)
+ end
+ else
+ get_priority = function(severity)
+ return priority + (vim.diagnostic.severity.HINT - severity)
+ end
end
else
- get_priority = function(severity)
- return priority + (vim.diagnostic.severity.HINT - severity)
+ get_priority = function()
+ return priority
end
end
- else
- get_priority = function()
- return priority
- end
- end
- for _, diagnostic in ipairs(diagnostics) do
- vim.fn.sign_place(
- 0,
- ns.sign_group,
- sign_highlight_map[diagnostic.severity],
- bufnr,
- {
- priority = get_priority(diagnostic.severity),
- lnum = diagnostic.lnum + 1
- }
- )
- end
-end
-
---- Set underline for given diagnostics.
----
----@param namespace number The diagnostic namespace
----@param bufnr number Buffer number
----@param diagnostics table A list of diagnostic items |diagnostic-structure|. When omitted the
---- current diagnostics in the given buffer are used.
----@param opts table Configuration table. Currently unused.
----@private
-function M._set_underline(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
- opts = {opts, 't', true},
- }
+ local ns = M.get_namespace(namespace)
+ if not ns.user_data.sign_group then
+ ns.user_data.sign_group = string.format("vim.diagnostic.%s", ns.name)
+ end
- bufnr = get_bufnr(bufnr)
- opts = get_resolved_options({ underline = opts }, namespace, bufnr).underline
+ local sign_group = ns.user_data.sign_group
+ for _, diagnostic in ipairs(diagnostics) do
+ vim.fn.sign_place(
+ 0,
+ sign_group,
+ sign_highlight_map[diagnostic.severity],
+ bufnr,
+ {
+ priority = get_priority(diagnostic.severity),
+ lnum = diagnostic.lnum + 1
+ }
+ )
+ end
+ end,
+ hide = function(namespace, bufnr)
+ local ns = M.get_namespace(namespace)
+ if ns.user_data.sign_group then
+ vim.fn.sign_unplace(ns.user_data.sign_group, {buffer=bufnr})
+ end
+ end,
+}
- if opts and opts.severity then
- diagnostics = filter_by_severity(opts.severity, diagnostics)
- end
+M.handlers.underline = {
+ show = function(namespace, bufnr, diagnostics, opts)
+ vim.validate {
+ namespace = {namespace, 'n'},
+ bufnr = {bufnr, 'n'},
+ diagnostics = {diagnostics, 't'},
+ opts = {opts, 't', true},
+ }
- for _, diagnostic in ipairs(diagnostics) do
- local higroup = underline_highlight_map[diagnostic.severity]
+ bufnr = get_bufnr(bufnr)
- if higroup == nil then
- -- Default to error if we don't have a highlight associated
- higroup = underline_highlight_map.Error
+ if opts.underline and opts.underline.severity then
+ diagnostics = filter_by_severity(opts.underline.severity, diagnostics)
end
- vim.highlight.range(
- bufnr,
- namespace,
- higroup,
- { diagnostic.lnum, diagnostic.col },
- { diagnostic.end_lnum, diagnostic.end_col }
- )
- end
-end
+ local ns = M.get_namespace(namespace)
+ if not ns.user_data.underline_ns then
+ ns.user_data.underline_ns = vim.api.nvim_create_namespace("")
+ end
---- Set virtual text for given diagnostics.
----
----@param namespace number The diagnostic namespace
----@param bufnr number Buffer number
----@param diagnostics table A list of diagnostic items |diagnostic-structure|. When omitted the
---- current diagnostics in the given buffer are used.
----@param opts table|nil Configuration table with the following keys:
---- - prefix: (string) Prefix to display before virtual text on line.
---- - spacing: (number) Number of spaces to insert before virtual text.
---- - source: (string) Include the diagnostic source in virtual text. One of "always" or
---- "if_many".
----@private
-function M._set_virtual_text(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
- diagnostics = {diagnostics, 't'},
- opts = {opts, 't', true},
- }
+ local underline_ns = ns.user_data.underline_ns
+ for _, diagnostic in ipairs(diagnostics) do
+ local higroup = underline_highlight_map[diagnostic.severity]
- bufnr = get_bufnr(bufnr)
- opts = get_resolved_options({ virtual_text = opts }, namespace, bufnr).virtual_text
+ if higroup == nil then
+ -- Default to error if we don't have a highlight associated
+ higroup = underline_highlight_map.Error
+ end
- if opts and opts.format then
- diagnostics = reformat_diagnostics(opts.format, diagnostics)
+ vim.highlight.range(
+ bufnr,
+ underline_ns,
+ higroup,
+ { diagnostic.lnum, diagnostic.col },
+ { diagnostic.end_lnum, diagnostic.end_col }
+ )
+ end
+ end,
+ hide = function(namespace, bufnr)
+ local ns = M.get_namespace(namespace)
+ if ns.user_data.underline_ns then
+ vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
+ end
end
+}
- if opts and opts.source then
- diagnostics = prefix_source(opts.source, diagnostics)
- end
+M.handlers.virtual_text = {
+ show = function(namespace, bufnr, diagnostics, opts)
+ vim.validate {
+ namespace = {namespace, 'n'},
+ bufnr = {bufnr, 'n'},
+ diagnostics = {diagnostics, 't'},
+ opts = {opts, 't', true},
+ }
+
+ bufnr = get_bufnr(bufnr)
- local buffer_line_diagnostics = diagnostic_lines(diagnostics)
- for line, line_diagnostics in pairs(buffer_line_diagnostics) do
- if opts and opts.severity then
- line_diagnostics = filter_by_severity(opts.severity, line_diagnostics)
+ local severity
+ if opts.virtual_text then
+ if opts.virtual_text.format then
+ diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics)
+ end
+ if opts.virtual_text.source then
+ diagnostics = prefix_source(opts.virtual_text.source, diagnostics)
+ end
+ if opts.virtual_text.severity then
+ severity = opts.virtual_text.severity
+ end
end
- local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts)
- if virt_texts then
- vim.api.nvim_buf_set_extmark(bufnr, namespace, line, 0, {
- hl_mode = "combine",
- virt_text = virt_texts,
- })
+ local ns = M.get_namespace(namespace)
+ if not ns.user_data.virt_text_ns then
+ ns.user_data.virt_text_ns = vim.api.nvim_create_namespace("")
end
- end
-end
+
+ local virt_text_ns = ns.user_data.virt_text_ns
+ local buffer_line_diagnostics = diagnostic_lines(diagnostics)
+ for line, line_diagnostics in pairs(buffer_line_diagnostics) do
+ if severity then
+ line_diagnostics = filter_by_severity(severity, line_diagnostics)
+ end
+ local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text)
+
+ if virt_texts then
+ vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
+ hl_mode = "combine",
+ virt_text = virt_texts,
+ })
+ end
+ end
+ end,
+ hide = function(namespace, bufnr)
+ local ns = M.get_namespace(namespace)
+ if ns.user_data.virt_text_ns then
+ vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
+ end
+ end,
+}
--- Get virtual text chunks to display using |nvim_buf_set_extmark()|.
---
@@ -1066,19 +1036,16 @@ function M.hide(namespace, bufnr)
bufnr = get_bufnr(bufnr)
diagnostic_cache_extmarks[bufnr][namespace] = {}
- local ns = get_namespace(namespace)
-
- -- clear sign group
- vim.fn.sign_unplace(ns.sign_group, {buffer=bufnr})
-
- -- clear virtual text namespace
- vim.api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
+ for _, handler in pairs(M.handlers) do
+ if handler.hide then
+ handler.hide(namespace, bufnr)
+ end
+ end
end
-
--- Display diagnostics for the given namespace and buffer.
---
----@param namespace number Diagnostic namespace
+---@param namespace number Diagnostic namespace.
---@param bufnr number|nil Buffer number. Defaults to the current buffer.
---@param diagnostics table|nil The diagnostics to display. When omitted, use the
--- saved diagnostics for the given namespace and
@@ -1129,83 +1096,145 @@ function M.show(namespace, bufnr, diagnostics, opts)
clamp_line_numbers(bufnr, diagnostics)
- if opts.underline then
- M._set_underline(namespace, bufnr, diagnostics, opts.underline)
- end
-
- if opts.virtual_text then
- M._set_virtual_text(namespace, bufnr, diagnostics, opts.virtual_text)
- end
-
- if opts.signs then
- M._set_signs(namespace, bufnr, diagnostics, opts.signs)
+ for handler_name, handler in pairs(M.handlers) do
+ if handler.show and opts[handler_name] then
+ handler.show(namespace, bufnr, diagnostics, opts)
+ end
end
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..fb4718c1bb 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -122,9 +122,6 @@ local active_clients = {}
local all_buffer_active_clients = {}
local uninitialized_clients = {}
--- Tracks all buffers attached to a client.
-local all_client_active_buffers = {}
-
---@private
--- Invokes a function for each LSP client attached to the buffer {bufnr}.
---
@@ -242,6 +239,7 @@ local function validate_client_config(config)
on_exit = { config.on_exit, "f", true };
on_init = { config.on_init, "f", true };
settings = { config.settings, "t", true };
+ commands = { config.commands, 't', true };
before_init = { config.before_init, "f", true };
offset_encoding = { config.offset_encoding, "s", true };
flags = { config.flags, "t", true };
@@ -593,6 +591,11 @@ end
--- returned to the language server if requested via `workspace/configuration`.
--- Keys are case-sensitive.
---
+---@param commands table Table that maps string of clientside commands to user-defined functions.
+--- Commands passed to start_client take precedence over the global command registry. Each key
+--- must be a unique comand name, and the value is a function which is called if any LSP action
+--- (code action, code lenses, ...) triggers the command.
+---
---@param init_options Values to pass in the initialization request
--- as `initializationOptions`. See `initialize` in the LSP spec.
---
@@ -647,7 +650,9 @@ end
--- - debounce_text_changes (number, default nil): Debounce didChange
--- notifications to the server by the given number in milliseconds. No debounce
--- occurs if nil
----
+--- - exit_timeout (number, default 500): Milliseconds to wait for server to
+-- exit cleanly after sending the 'shutdown' request before sending kill -15.
+-- If set to false, nvim exits immediately after sending the 'shutdown' request to the server.
---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be
--- fully initialized. Use `on_init` to do any actions once
--- the client has been initialized.
@@ -742,7 +747,6 @@ function lsp.start_client(config)
lsp.diagnostic.reset(client_id, all_buffer_active_clients)
changetracking.reset(client_id)
- all_client_active_buffers[client_id] = nil
for _, client_ids in pairs(all_buffer_active_clients) do
client_ids[client_id] = nil
end
@@ -771,10 +775,14 @@ function lsp.start_client(config)
rpc = rpc;
offset_encoding = offset_encoding;
config = config;
+ attached_buffers = {};
handlers = handlers;
+ commands = config.commands or {};
+
+ requests = {};
-- for $/progress report
- messages = { name = name, messages = {}, progress = {}, status = {} }
+ messages = { name = name, messages = {}, progress = {}, status = {} };
}
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
@@ -907,11 +915,21 @@ function lsp.start_client(config)
end
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
changetracking.flush(client)
-
+ bufnr = resolve_bufnr(bufnr)
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
- return rpc.request(method, params, function(err, result)
+ local success, request_id = rpc.request(method, params, function(err, result)
handler(err, result, {method=method, client_id=client_id, bufnr=bufnr, params=params})
+ end, function(request_id)
+ client.requests[request_id] = nil
+ nvim_command("doautocmd <nomodeline> User LspRequest")
end)
+
+ if success then
+ client.requests[request_id] = { type='pending', bufnr=bufnr, method=method }
+ nvim_command("doautocmd <nomodeline> User LspRequest")
+ end
+
+ return success, request_id
end
---@private
@@ -971,6 +989,11 @@ function lsp.start_client(config)
---@see |vim.lsp.client.notify()|
function client.cancel_request(id)
validate{id = {id, 'n'}}
+ local request = client.requests[id]
+ if request and request.type == 'pending' then
+ request.type = 'cancel'
+ nvim_command("doautocmd <nomodeline> User LspRequest")
+ end
return rpc.notify("$/cancelRequest", { id = id })
end
@@ -989,7 +1012,6 @@ function lsp.start_client(config)
lsp.diagnostic.reset(client_id, all_buffer_active_clients)
changetracking.reset(client_id)
- all_client_active_buffers[client_id] = nil
for _, client_ids in pairs(all_buffer_active_clients) do
client_ids[client_id] = nil
end
@@ -1032,6 +1054,7 @@ function lsp.start_client(config)
-- TODO(ashkan) handle errors.
pcall(config.on_attach, client, bufnr)
end
+ client.attached_buffers[bufnr] = true
end
initialize()
@@ -1142,12 +1165,6 @@ function lsp.buf_attach_client(bufnr, client_id)
})
end
- if not all_client_active_buffers[client_id] then
- all_client_active_buffers[client_id] = {}
- end
-
- table.insert(all_client_active_buffers[client_id], bufnr)
-
if buffer_client_ids[client_id] then return end
-- This is our first time attaching this client to this buffer.
buffer_client_ids[client_id] = true
@@ -1172,7 +1189,7 @@ end
--- Gets a client by id, or nil if the id is invalid.
--- The returned client may not yet be fully initialized.
--
----@param client_id client id number
+---@param client_id number client id
---
---@returns |vim.lsp.client| object, or nil
function lsp.get_client_by_id(client_id)
@@ -1181,15 +1198,11 @@ end
--- Returns list of buffers attached to client_id.
--
----@param client_id client id
+---@param client_id number client id
---@returns list of buffer ids
function lsp.get_buffers_by_client_id(client_id)
- local active_client_buffers = all_client_active_buffers[client_id]
- if active_client_buffers then
- return active_client_buffers
- else
- return {}
- end
+ local client = lsp.get_client_by_id(client_id)
+ return client and vim.tbl_keys(client.attached_buffers) or {}
end
--- Stops a client(s).
@@ -1239,9 +1252,41 @@ function lsp._vim_exit_handler()
client.stop()
end
- if not vim.wait(500, function() return tbl_isempty(active_clients) end, 50) then
- for _, client in pairs(active_clients) do
- client.stop(true)
+ local timeouts = {}
+ local max_timeout = 0
+ local send_kill = false
+
+ for client_id, client in pairs(active_clients) do
+ local timeout = if_nil(client.config.flags.exit_timeout, 500)
+ if timeout then
+ send_kill = true
+ timeouts[client_id] = timeout
+ max_timeout = math.max(timeout, max_timeout)
+ end
+ end
+
+ local poll_time = 50
+
+ local function check_clients_closed()
+ for client_id, timeout in pairs(timeouts) do
+ timeouts[client_id] = timeout - poll_time
+ end
+
+ for client_id, _ in pairs(active_clients) do
+ if timeouts[client_id] ~= nil and timeouts[client_id] > 0 then
+ return false
+ end
+ end
+ return true
+ end
+
+ if send_kill then
+ if not vim.wait(max_timeout, check_clients_closed, poll_time) then
+ for client_id, client in pairs(active_clients) do
+ if timeouts[client_id] ~= nil then
+ client.stop(true)
+ end
+ end
end
end
end
@@ -1282,7 +1327,7 @@ function lsp.buf_request(bufnr, method, params, handler)
if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then
vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
vim.api.nvim_command("redraw")
- return
+ return {}, function() end
end
local client_request_ids = {}
@@ -1430,7 +1475,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)
@@ -1494,6 +1539,52 @@ function lsp.omnifunc(findstart, base)
return -2
end
+--- Provides an interface between the built-in client and a `formatexpr` function.
+---
+--- Currently only supports a single client. This can be set via
+--- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach`
+--- via `vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`.
+---
+---@param opts table options for customizing the formatting expression which takes the
+--- following optional keys:
+--- * timeout_ms (default 500ms). The timeout period for the formatting request.
+function lsp.formatexpr(opts)
+ opts = opts or {}
+ local timeout_ms = opts.timeout_ms or 500
+
+ if vim.tbl_contains({'i', 'R', 'ic', 'ix'}, vim.fn.mode()) then
+ -- `formatexpr` is also called when exceeding `textwidth` in insert mode
+ -- fall back to internal formatting
+ return 1
+ end
+
+ local start_line = vim.v.lnum
+ local end_line = start_line + vim.v.count - 1
+
+ if start_line > 0 and end_line > 0 then
+ local params = {
+ textDocument = util.make_text_document_params();
+ range = {
+ start = { line = start_line - 1; character = 0; };
+ ["end"] = { line = end_line - 1; character = 0; };
+ };
+ };
+ params.options = util.make_formatting_params().options
+ local client_results = vim.lsp.buf_request_sync(0, "textDocument/rangeFormatting", params, timeout_ms)
+
+ -- Apply the text edits from one and only one of the clients.
+ for _, response in pairs(client_results) do
+ if response.result then
+ vim.lsp.util.apply_text_edits(response.result, 0)
+ return 0
+ end
+ end
+ end
+
+ -- do not run builtin formatter.
+ return 0
+end
+
---Checks whether a client is stopped.
---
---@param client_id (Number)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 245f29943e..128f0b01ad 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -480,11 +480,11 @@ local function on_code_action_results(results, ctx)
end
if action.command then
local command = type(action.command) == 'table' and action.command or action
- local fn = vim.lsp.commands[command.command]
+ local fn = client.commands[command.command] or vim.lsp.commands[command.command]
if fn then
local enriched_ctx = vim.deepcopy(ctx)
enriched_ctx.client_id = client.id
- fn(command, ctx)
+ fn(command, enriched_ctx)
else
M.execute_command(command)
end
@@ -529,6 +529,7 @@ local function on_code_action_results(results, ctx)
vim.ui.select(action_tuples, {
prompt = 'Code actions:',
+ kind = 'codeaction',
format_item = function(action_tuple)
local title = action_tuple[2].title:gsub('\r\n', '\\r\\n')
return title:gsub('\n', '\\n')
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index 20b203fe99..9eb64c9a2e 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -31,15 +31,15 @@ local function execute_lens(lens, bufnr, client_id)
local line = lens.range.start.line
api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1)
+ local client = vim.lsp.get_client_by_id(client_id)
+ assert(client, 'Client is required to execute lens, client_id=' .. client_id)
local command = lens.command
- local fn = vim.lsp.commands[command.command]
+ local fn = client.commands[command.command] or vim.lsp.commands[command.command]
if fn then
fn(command, { bufnr = bufnr, client_id = client_id })
return
end
-- Need to use the client that returned the lens → must not use buf_request
- local client = vim.lsp.get_client_by_id(client_id)
- assert(client, 'Client is required to execute lens, client_id=' .. client_id)
local command_provider = client.server_capabilities.executeCommandProvider
local commands = type(command_provider) == 'table' and command_provider.commands or {}
if not vim.tbl_contains(commands, command.command) then
@@ -91,16 +91,16 @@ function M.run()
local option = options[1]
execute_lens(option.lens, bufnr, option.client)
else
- local options_strings = {"Code lenses:"}
- for i, option in ipairs(options) do
- table.insert(options_strings, string.format('%d. %s', i, option.lens.command.title))
- end
- local choice = vim.fn.inputlist(options_strings)
- if choice < 1 or choice > #options then
- return
- end
- local option = options[choice]
- execute_lens(option.lens, bufnr, option.client)
+ vim.ui.select(options, {
+ prompt = 'Code lenses:',
+ format_item = function(option)
+ return option.lens.command.title
+ end,
+ }, function(option)
+ if option then
+ execute_lens(option.lens, bufnr, option.client)
+ end
+ end)
end
end
@@ -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 c6c08a15d3..6b856a52a5 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -146,7 +146,8 @@ local _client_namespaces = {}
function M.get_namespace(client_id)
vim.validate { client_id = { client_id, 'n' } }
if not _client_namespaces[client_id] then
- local name = string.format("vim.lsp.client-%d", client_id)
+ local client = vim.lsp.get_client_by_id(client_id)
+ local name = string.format("vim.lsp.%s.%d", client.name, client_id)
_client_namespaces[client_id] = vim.api.nvim_create_namespace(name)
end
return _client_namespaces[client_id]
@@ -211,9 +212,14 @@ function M.on_publish_diagnostics(_, result, ctx, config)
end
end
end
+
+ -- Persist configuration to ensure buffer reloads use the same
+ -- configuration. To make lsp.with configuration work (See :help
+ -- lsp-handler-configuration)
+ vim.diagnostic.config(config, namespace)
end
- vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), config)
+ vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
-- Keep old autocmd for back compat. This should eventually be removed.
vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged")
@@ -546,14 +552,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.scope = "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}
@@ -568,11 +575,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.scope = "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
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index eff27807be..01d7102e8f 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -185,7 +185,7 @@ local function response_to_list(map_result, entity)
title = 'Language Server';
items = map_result(result, ctx.bufnr);
})
- api.nvim_command("copen")
+ api.nvim_command("botright copen")
end
end
end
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index d9a684a738..bce1e9f35d 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -297,6 +297,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
local message_index = 0
local message_callbacks = {}
+ local notify_reply_callbacks = {}
local handle, pid
do
@@ -309,8 +310,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
stdout:close()
stderr:close()
handle:close()
- -- Make sure that message_callbacks can be gc'd.
+ -- Make sure that message_callbacks/notify_reply_callbacks can be gc'd.
message_callbacks = nil
+ notify_reply_callbacks = nil
dispatchers.on_exit(code, signal)
end
local spawn_params = {
@@ -375,10 +377,12 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param method (string) The invoked LSP method
---@param params (table) Parameters for the invoked LSP method
---@param callback (function) Callback to invoke
+ ---@param notify_reply_callback (function) Callback to invoke as soon as a request is no longer pending
---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
- local function request(method, params, callback)
+ local function request(method, params, callback, notify_reply_callback)
validate {
callback = { callback, 'f' };
+ notify_reply_callback = { notify_reply_callback, 'f', true };
}
message_index = message_index + 1
local message_id = message_index
@@ -388,8 +392,15 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
method = method;
params = params;
}
- if result and message_callbacks then
- message_callbacks[message_id] = schedule_wrap(callback)
+ if result then
+ if message_callbacks then
+ message_callbacks[message_id] = schedule_wrap(callback)
+ else
+ return false
+ end
+ if notify_reply_callback and notify_reply_callbacks then
+ notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
+ end
return result, message_id
else
return false
@@ -466,6 +477,16 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
-- We sent a number, so we expect a number.
local result_id = tonumber(decoded.id)
+ -- Notify the user that a response was received for the request
+ local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
+ if notify_reply_callback then
+ validate {
+ notify_reply_callback = { notify_reply_callback, 'f' };
+ }
+ notify_reply_callback(result_id)
+ notify_reply_callbacks[result_id] = nil
+ end
+
-- Do not surface RequestCancelled to users, it is RPC-internal.
if decoded.error then
local mute_error = false
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 3751f94caf..a5bf0efcb1 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -152,6 +152,7 @@ end
--- Returns a zero-indexed column, since set_lines() does the conversion to
--- 1-indexed
local function get_line_byte_from_position(bufnr, position)
+ -- TODO handle offset_encoding
-- LSP's line and characters are 0-indexed
-- Vim's line and columns are 1-indexed
local col = position.character
@@ -165,7 +166,7 @@ local function get_line_byte_from_position(bufnr, position)
local line = position.line
local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false)
if #lines > 0 then
- local ok, result = pcall(vim.str_byteindex, lines[1], col)
+ local ok, result = pcall(vim.str_byteindex, lines[1], col, true)
if ok then
return result
@@ -226,9 +227,10 @@ function M.get_progress_messages()
table.remove(client.messages, item.idx)
end
- for _, item in ipairs(progress_remove) do
- client.messages.progress[item.token] = nil
- end
+ end
+
+ for _, item in ipairs(progress_remove) do
+ item.client.messages.progress[item.token] = nil
end
return new_messages
@@ -275,7 +277,8 @@ function M.apply_text_edits(text_edits, bufnr)
-- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
local has_eol_text_edit = false
local max = vim.api.nvim_buf_line_count(bufnr)
- local len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '')
+ -- TODO handle offset_encoding
+ local _, len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '')
text_edits = vim.tbl_map(function(text_edit)
if max <= text_edit.range.start.line then
text_edit.range.start.line = max - 1
@@ -335,10 +338,12 @@ function M.apply_text_edits(text_edits, bufnr)
end
if is_cursor_fixed then
- vim.api.nvim_win_set_cursor(0, {
- cursor.row + 1,
- math.min(cursor.col, #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or ''))
- })
+ local is_valid_cursor = true
+ is_valid_cursor = is_valid_cursor and cursor.row < vim.api.nvim_buf_line_count(bufnr)
+ is_valid_cursor = is_valid_cursor and cursor.col <= #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or '')
+ if is_valid_cursor then
+ vim.api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col })
+ end
end
-- Remove final line if needed
@@ -1717,7 +1722,9 @@ function M.symbols_to_items(symbols, bufnr)
})
if symbol.children then
for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do
- vim.list_extend(_items, v)
+ for _, s in ipairs(v) do
+ table.insert(_items, s)
+ end
end
end
end
@@ -1785,7 +1792,9 @@ local function make_position_param()
if not line then
return { line = 0; character = 0; }
end
- col = str_utfindex(line, col)
+ -- TODO handle offset_encoding
+ local _
+ _, col = str_utfindex(line, col)
return { line = row; character = col; }
end
@@ -1835,11 +1844,14 @@ function M.make_given_range_params(start_pos, end_pos)
A[1] = A[1] - 1
B[1] = B[1] - 1
-- account for encoding.
+ -- TODO handle offset_encoding
if A[2] > 0 then
- A = {A[1], M.character_offset(0, A[1], A[2])}
+ local _, char = M.character_offset(0, A[1], A[2])
+ A = {A[1], char}
end
if B[2] > 0 then
- B = {B[1], M.character_offset(0, B[1], B[2])}
+ local _, char = M.character_offset(0, B[1], B[2])
+ B = {B[1], char}
end
-- we need to offset the end character position otherwise we loose the last
-- character of the selection, as LSP end position is exclusive
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 5eab20fc54..adc1e16759 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -9,6 +9,11 @@ local M = {}
--- - format_item (function item -> text)
--- Function to format an
--- individual item from `items`. Defaults to `tostring`.
+--- - kind (string|nil)
+--- Arbitrary hint string indicating the item shape.
+--- Plugins reimplementing `vim.ui.select` may wish to
+--- use this to infer the structure or semantics of
+--- `items`, or the context in which select() was called.
---@param on_choice function ((item|nil, idx|nil) -> ())
--- Called once the user made a choice.
--- `idx` is the 1-based index of `item` within `item`.
diff --git a/runtime/syntax/arduino.vim b/runtime/syntax/arduino.vim
index 4a4ef82072..1c9618ff0f 100644
--- a/runtime/syntax/arduino.vim
+++ b/runtime/syntax/arduino.vim
@@ -1,50 +1,79 @@
" Vim syntax file
" Language: Arduino
" Maintainer: Johannes Hoff <johannes@johanneshoff.com>
-" Last Change: 2011 June 3
+" Last Change: 21 October 2021
" License: VIM license (:help license, replace vim by arduino.vim)
" Syntax highlighting like in the Arduino IDE
-" Keywords extracted from <arduino>/build/shared/lib/keywords.txt (arduino
-" version 0021)
+" Automatically generated by the script available at
+" https://bitbucket.org/johannes/arduino-vim-syntax
+" Using keywords from <arduino>/build/shared/lib/keywords.txt
+" From version: 1.8.16
-" Thanks to Rik, Erik Nomitch, Adam Obeng and Graeme Cross for helpful feedback!
+" Thanks to Rik, Erik Nomitch, Adam Obeng, Graeme Cross and Niall Parker
+" for helpful feedback!
-" quit when a syntax file was already loaded
-if exists("b:current_syntax")
+" For version 5.x: Clear all syntax items
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+ syntax clear
+elseif exists("b:current_syntax")
finish
endif
" Read the C syntax to start with
-runtime! syntax/cpp.vim
-
-syn keyword arduinoConstant HIGH LOW INPUT OUTPUT
-syn keyword arduinoConstant DEC BIN HEX OCT BYTE
-syn keyword arduinoConstant PI HALF_PI TWO_PI
-syn keyword arduinoConstant LSBFIRST MSBFIRST
-syn keyword arduinoConstant CHANGE FALLING RISING
-syn keyword arduinoConstant SERIAL DISPLAY
-syn keyword arduinoConstant DEFAULT EXTERNAL INTERNAL INTERNAL1V1 INTERNAL2V56
-
-syn keyword arduinoStdFunc abs acos asin atan atan2 ceil constrain
-syn keyword arduinoStdFunc cos degrees exp floor log
-syn keyword arduinoStdFunc map max min pow radians
-syn keyword arduinoStdFunc round sin sq sqrt tan
-syn keyword arduinoStdFunc randomSeed random
-
-syn keyword arduinoFunc analogReference analogRead analogWrite
-syn keyword arduinoFunc attachInterrupt detachInterrupt interrupts noInterrupts
-syn keyword arduinoFunc lowByte highByte bitRead bitWrite bitSet bitClear
-syn keyword arduinoFunc millis micros delay delayMicroseconds
-syn keyword arduinoFunc pinMode digitalWrite digitalRead
-syn keyword arduinoFunc tone noTone pulseIn shiftOut
-
-syn keyword arduinoMethod setup loop
-syn keyword arduinoMethod begin end available read flush print println write peek
-
-syn keyword arduinoType boolean byte word String
-
-syn keyword arduinoModule Serial Serial1 Serial2 Serial3
+if version < 600
+ so <sfile>:p:h/cpp.vim
+else
+ runtime! syntax/cpp.vim
+endif
+
+syn keyword arduinoConstant BIN CHANGE DEC DEFAULT EXTERNAL FALLING HALF_PI HEX
+syn keyword arduinoConstant HIGH INPUT INPUT_PULLUP INTERNAL INTERNAL1V1
+syn keyword arduinoConstant INTERNAL2V56 LED_BUILTIN LED_BUILTIN_RX
+syn keyword arduinoConstant LED_BUILTIN_TX LOW LSBFIRST MSBFIRST OCT OUTPUT PI
+syn keyword arduinoConstant RISING TWO_PI
+
+syn keyword arduinoFunc analogRead analogReadResolution analogReference
+syn keyword arduinoFunc analogWrite analogWriteResolution attachInterrupt
+syn keyword arduinoFunc bit bitClear bitRead bitSet bitWrite delay
+syn keyword arduinoFunc delayMicroseconds detachInterrupt
+syn keyword arduinoFunc digitalPinToInterrupt digitalRead digitalWrite
+syn keyword arduinoFunc highByte interrupts lowByte micros millis
+syn keyword arduinoFunc noInterrupts noTone pinMode pulseIn pulseInLong
+syn keyword arduinoFunc shiftIn shiftOut tone yield
+
+syn keyword arduinoMethod available availableForWrite begin charAt compareTo
+syn keyword arduinoMethod concat end endsWith equals equalsIgnoreCase export
+syn keyword arduinoMethod final find findUntil flush getBytes indexOf
+syn keyword arduinoMethod lastIndexOf length loop override parseFloat
+syn keyword arduinoMethod parseInt peek print println read readBytes
+syn keyword arduinoMethod readBytesUntil readString readStringUntil replace
+syn keyword arduinoMethod setCharAt setTimeout setup startsWith Stream
+syn keyword arduinoMethod substring toCharArray toInt toLowerCase toUpperCase
+syn keyword arduinoMethod trim
+
+syn keyword arduinoModule Keyboard Mouse Serial Serial1 Serial2 Serial3
+syn keyword arduinoModule SerialUSB
+
+syn keyword arduinoStdFunc abs accept acos acosf asin asinf atan atan2 atan2f
+syn keyword arduinoStdFunc atanf cbrt cbrtf ceil ceilf click constrain
+syn keyword arduinoStdFunc copysign copysignf cos cosf cosh coshf degrees exp
+syn keyword arduinoStdFunc expf fabs fabsf fdim fdimf floor floorf fma fmaf
+syn keyword arduinoStdFunc fmax fmaxf fmin fminf fmod fmodf hypot hypotf
+syn keyword arduinoStdFunc isfinite isinf isnan isPressed ldexp ldexpf log
+syn keyword arduinoStdFunc log10 log10f logf lrint lrintf lround lroundf map
+syn keyword arduinoStdFunc max min move pow powf press radians random
+syn keyword arduinoStdFunc randomSeed release releaseAll round roundf signbit
+syn keyword arduinoStdFunc sin sinf sinh sinhf sq sqrt sqrtf tan tanf tanh
+syn keyword arduinoStdFunc tanhf trunc truncf
+
+syn keyword arduinoType _Bool _Complex _Imaginary array atomic_bool
+syn keyword arduinoType atomic_char atomic_int atomic_llong atomic_long
+syn keyword arduinoType atomic_schar atomic_short atomic_uchar atomic_uint
+syn keyword arduinoType atomic_ullong atomic_ulong atomic_ushort boolean
+syn keyword arduinoType byte char16_t char32_t complex NULL null PROGMEM
+syn keyword arduinoType String word
hi def link arduinoType Type
hi def link arduinoConstant Constant
diff --git a/runtime/syntax/debchangelog.vim b/runtime/syntax/debchangelog.vim
index 93b03ae06d..79352c0827 100644
--- a/runtime/syntax/debchangelog.vim
+++ b/runtime/syntax/debchangelog.vim
@@ -3,7 +3,7 @@
" Maintainer: Debian Vim Maintainers
" Former Maintainers: Gerfried Fuchs <alfie@ist.org>
" Wichert Akkerman <wakkerma@debian.org>
-" Last Change: 2021 Aug 03
+" Last Change: 2021 Oct 19
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debchangelog.vim
" Standard syntax initialization
@@ -24,7 +24,8 @@ let s:supported = [
\ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm',
\ 'trixie', 'sid', 'rc-buggy',
\
- \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'devel'
+ \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy',
+ \ 'devel'
\ ]
let s:unsupported = [
\ 'frozen', 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato',
diff --git a/runtime/syntax/debsources.vim b/runtime/syntax/debsources.vim
index 8aa96fcb58..4b4c497f18 100644
--- a/runtime/syntax/debsources.vim
+++ b/runtime/syntax/debsources.vim
@@ -2,7 +2,7 @@
" Language: Debian sources.list
" Maintainer: Debian Vim Maintainers
" Former Maintainer: Matthijs Mohlmann <matthijs@cacholong.nl>
-" Last Change: 2021 Aug 03
+" Last Change: 2021 Oct 19
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debsources.vim
" Standard syntax initialization
@@ -26,7 +26,8 @@ let s:supported = [
\ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm',
\ 'trixie', 'sid', 'rc-buggy',
\
- \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'devel'
+ \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy',
+ \ 'devel'
\ ]
let s:unsupported = [
\ 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato',
diff --git a/runtime/syntax/nsis.vim b/runtime/syntax/nsis.vim
index 3389771b78..3a73fe0989 100644
--- a/runtime/syntax/nsis.vim
+++ b/runtime/syntax/nsis.vim
@@ -1,9 +1,9 @@
" Vim syntax file
-" Language: NSIS script, for version of NSIS 3.03 and later
+" Language: NSIS script, for version of NSIS 3.08 and later
" Maintainer: Ken Takata
" URL: https://github.com/k-takata/vim-nsis
" Previous Maintainer: Alex Jakushev <Alex.Jakushev@kemek.lt>
-" Last Change: 2018-10-02
+" Last Change: 2020-10-18
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -97,6 +97,8 @@ syn match nsisSysVar "$RESOURCES_LOCALIZED"
syn match nsisSysVar "$CDBURN_AREA"
syn match nsisSysVar "$HWNDPARENT"
syn match nsisSysVar "$PLUGINSDIR"
+syn match nsisSysVar "$\%(USERTEMPLATES\|USERSTARTMENU\|USERSMPROGRAMS\|USERDESKTOP\)"
+syn match nsisSysVar "$\%(COMMONTEMPLATES\|COMMONSTARTMENU\|COMMONSMPROGRAMS\|COMMONDESKTOP\|COMMONPROGRAMDATA\)"
syn match nsisSysVar "$\\r"
syn match nsisSysVar "$\\n"
syn match nsisSysVar "$\\t"
@@ -149,7 +151,7 @@ syn keyword nsisStatement contained Section nextgroup=nsisSectionOpt skipwhite
syn region nsisSectionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisSectionKwd
syn match nsisSectionKwd contained "/o\>"
-syn keyword nsisStatement contained SectionIn nextgroup=nsisSectionInOpt skipwhite
+syn keyword nsisStatement contained SectionInstType SectionIn nextgroup=nsisSectionInOpt skipwhite
syn region nsisSectionInOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisSectionInKwd
syn keyword nsisSectionInKwd contained RO
@@ -269,10 +271,22 @@ syn keyword nsisAttribute contained ManifestDPIAware nextgroup=nsisManifestDPIAw
syn region nsisManifestDPIAwareOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisManifestDPIAwareKwd
syn keyword nsisManifestDPIAwareKwd contained notset true false
+syn keyword nsisAttribute contained ManifestLongPathAware nextgroup=nsisManifestLongPathAwareOpt skipwhite
+syn region nsisManifestLongPathAwareOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisManifestLongPathAwareKwd
+syn match nsisManifestLongPathAwareKwd contained "\<\%(notset\|true\|false\)\>"
+
syn keyword nsisAttribute contained ManifestSupportedOS nextgroup=nsisManifestSupportedOSOpt skipwhite
syn region nsisManifestSupportedOSOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisManifestSupportedOSKwd
syn match nsisManifestSupportedOSKwd contained "\<\%(none\|all\|WinVista\|Win7\|Win8\|Win8\.1\|Win10\)\>"
+syn keyword nsisAttribute contained PEAddResource nextgroup=nsisPEAddResourceOpt skipwhite
+syn region nsisPEAddResourceOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisPEAddResourceKwd
+syn match nsisPEAddResourceKwd contained "/\%(OVERWRITE\|REPLACE\)\>"
+
+syn keyword nsisAttribute contained PERemoveResource nextgroup=nsisPERemoveResourceOpt skipwhite
+syn region nsisPERemoveResourceOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisPERemoveResourceKwd
+syn match nsisPERemoveResourceKwd contained "/NOERRORS\>"
+
syn keyword nsisAttribute contained RequestExecutionLevel nextgroup=nsisRequestExecutionLevelOpt skipwhite
syn region nsisRequestExecutionLevelOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisRequestExecutionLevelKwd
syn keyword nsisRequestExecutionLevelKwd contained none user highest admin
@@ -353,7 +367,7 @@ syn keyword nsisInstruction contained ExpandEnvStrings ReadEnvStr
syn keyword nsisInstruction contained DeleteRegKey nextgroup=nsisDeleteRegKeyOpt skipwhite
syn region nsisDeleteRegKeyOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisDeleteRegKeyKwd,nsisRegistry
-syn match nsisDeleteRegKeyKwd contained "/ifempty\>"
+syn match nsisDeleteRegKeyKwd contained "/\%(ifempty\|ifnosubkeys\|ifnovalues\)\>"
syn keyword nsisInstruction contained nextgroup=nsisRegistryOpt skipwhite
\ DeleteRegValue EnumRegKey EnumRegValue ReadRegDWORD ReadRegStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr
@@ -368,8 +382,8 @@ syn region nsisSetRegViewOpt contained start="" end="$" transparent keepend cont
syn keyword nsisSetRegViewKwd contained default lastused
"FUNCTIONS - general purpose (4.9.3)
-syn keyword nsisInstruction contained CallInstDLL CreateDirectory GetDLLVersion
-syn keyword nsisInstruction contained GetDLLVersionLocal GetFileTime GetFileTimeLocal
+syn keyword nsisInstruction contained CallInstDLL CreateDirectory GetWinVer
+syn keyword nsisInstruction contained GetFileTime GetFileTimeLocal GetKnownFolderPath
syn keyword nsisInstruction contained GetTempFileName SearchPath RegDLL UnRegDLL
syn keyword nsisInstruction contained CopyFiles nextgroup=nsisCopyFilesOpt skipwhite
@@ -380,6 +394,10 @@ syn keyword nsisInstruction contained CreateShortcut nextgroup=nsisCreateShortcu
syn region nsisCreateShortcutOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisCreateShortcutKwd
syn match nsisCreateShortcutKwd contained "/NoWorkingDir\>"
+syn keyword nsisInstruction contained GetDLLVersion GetDLLVersionLocal nextgroup=nsisGetDLLVersionOpt skipwhite
+syn region nsisGetDLLVersionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetDLLVersionKwd
+syn match nsisGetDLLVersionKwd contained "/ProductVersion\>"
+
syn keyword nsisInstruction contained GetFullPathName nextgroup=nsisGetFullPathNameOpt skipwhite
syn region nsisGetFullPathNameOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetFullPathNameKwd
syn match nsisGetFullPathNameKwd contained "/SHORT\>"
@@ -395,6 +413,7 @@ syn keyword nsisFileAttrib contained FILE_ATTRIBUTE_TEMPORARY
syn keyword nsisInstruction contained Abort Call ClearErrors GetCurrentAddress
syn keyword nsisInstruction contained GetFunctionAddress GetLabelAddress Goto
syn keyword nsisInstruction contained IfAbort IfErrors IfFileExists IfRebootFlag IfSilent
+syn keyword nsisInstruction contained IfShellVarContextAll IfRtlLanguage
syn keyword nsisInstruction contained IntCmp IntCmpU Int64Cmp Int64CmpU IntPtrCmp IntPtrCmpU
syn keyword nsisInstruction contained Return Quit SetErrors StrCmp StrCmpS
@@ -460,6 +479,10 @@ syn keyword nsisInstruction contained CreateFont nextgroup=nsisFontOpt skipwhite
syn keyword nsisInstruction contained nextgroup=nsisBooleanOpt skipwhite
\ LockWindow SetAutoClose
+syn keyword nsisInstruction contained LoadAndSetImage nextgroup=nsisLoadAndSetImageOpt skipwhite
+syn region nsisLoadAndSetImageOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisLoadAndSetImageKwd
+syn match nsisLoadAndSetImageKwd contained "/\%(EXERESOURCE\|STRINGID\|RESIZETOFIT\%(WIDTH\|HEIGHT\)\)\>"
+
syn keyword nsisInstruction contained SendMessage nextgroup=nsisSendMessageOpt skipwhite
syn region nsisSendMessageOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisSendMessageKwd
syn match nsisSendMessageKwd contained "/TIMEOUT\>"
@@ -556,7 +579,7 @@ syn keyword nsisVerboseKwd contained push pop
"PREPROCESSOR (5.4)
syn match nsisDefine contained "!define\>" nextgroup=nsisDefineOpt skipwhite
syn region nsisDefineOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisDefineKwd
-syn match nsisDefineKwd contained "/\%(ifndef\|redef\|date\|utcdate\|math\|file\)\>"
+syn match nsisDefineKwd contained "/\%(ifndef\|redef\|date\|utcdate\|file\|intfmt\|math\)\>"
syn match nsisDefine contained "!undef\>"
syn match nsisPreCondit contained "!ifdef\>"
@@ -615,7 +638,10 @@ hi def link nsisInstTypeKwd Constant
hi def link nsisLicenseBkColorKwd Constant
hi def link nsisLicenseForceSelectionKwd Constant
hi def link nsisManifestDPIAwareKwd Constant
+hi def link nsisManifestLongPathAwareKwd Constant
hi def link nsisManifestSupportedOSKwd Constant
+hi def link nsisPEAddResourceKwd Constant
+hi def link nsisPERemoveResourceKwd Constant
hi def link nsisRequestExecutionLevelKwd Constant
hi def link nsisShowInstDetailsKwd Constant
hi def link nsisSilentInstallKwd Constant
@@ -633,11 +659,13 @@ hi def link nsisWriteRegMultiStrKwd Constant
hi def link nsisSetRegViewKwd Constant
hi def link nsisCopyFilesKwd Constant
hi def link nsisCreateShortcutKwd Constant
+hi def link nsisGetDLLVersionKwd Constant
hi def link nsisGetFullPathNameKwd Constant
hi def link nsisFileAttrib Constant
hi def link nsisMessageBox Constant
hi def link nsisFileWriteUTF16LEKwd Constant
hi def link nsisSetShellVarContextKwd Constant
+hi def link nsisLoadAndSetImageKwd Constant
hi def link nsisSendMessageKwd Constant
hi def link nsisSetBrandingImageKwd Constant
hi def link nsisSetDetailsViewKwd Constant