Merge #15585 refactor: move vim.lsp.diagnostic to vim.diagnostic

## Overview

- Move vim.lsp.diagnostic to vim.diagnostic
- Refactor client ids to diagnostic namespaces
- Update tests
- Write/update documentation and function signatures

Currently, non-LSP diagnostics in Neovim must hook into the LSP subsystem. This
is what e.g. null-ls and nvim-lint do. This is necessary because none of the
diagnostic API is exposed separately from the LSP subsystem.

This commit addresses this by generalizing the diagnostic subsystem beyond the
scope of LSP. The `vim.lsp.diagnostic` module is now simply a specific
diagnostic producer and primarily maintains the interface between LSP clients
and the broader diagnostic API.

The current diagnostic API uses "client ids" which only makes sense in the
context of LSP. We replace "client ids" with standard API namespaces generated
from `nvim_create_namespace`.

This PR is *mostly* backward compatible (so long as plugins are only using the
publicly documented API): LSP diagnostics will continue to work as usual, as
will pseudo-LSP clients like null-ls and nvim-lint. However, the latter can now
use the new interface, which looks something like this:

```lua
-- The namespace *must* be given a name. Anonymous namespaces will not work with diagnostics
local ns = vim.api.nvim_create_namespace("foo")

-- Generate diagnostics
local diagnostics = generate_diagnostics()

-- Set diagnostics for the current buffer
vim.diagnostic.set(ns, diagnostics, bufnr)
```

Some public facing API utility methods were removed and internalized directly in `vim.diagnostic`:

* `vim.lsp.util.diagnostics_to_items`

## API Design

`vim.diagnostic` contains most of the same API as `vim.lsp.diagnostic` with
`client_id` simply replaced with `namespace`, with some differences:

* Generally speaking, functions that modify or add diagnostics require a namespace as their first argument, e.g.

  ```lua
  vim.diagnostic.set({namespace}, {bufnr}, {diagnostics}[, {opts}])
  ```

   while functions that read or query diagnostics do not (although in many cases one may be supplied optionally):

   ```lua
   vim.diagnostic.get({bufnr}[, {namespace}])
   ```

* We use our own severity levels to decouple `vim.diagnostic` from LSP. These
  are designed similarly to `vim.log.levels` and currently include:

  ```lua
  vim.diagnostic.severity.ERROR
  vim.diagnostic.severity.WARN
  vim.diagnostic.severity.INFO
  vim.diagnostic.severity.HINT
  ```

  In practice, these match the LSP diagnostic severity levels exactly, but we
  should treat this as an interface and not assume that they are the same. The
  "translation" between the two severity types is handled transparently in
  `vim.lsp.diagnostic`.

* The actual "diagnostic" data structure is: (**EDIT:** Updated 2021-09-09):

  ```lua
  {
    lnum = <number>,
    col = <number>,
    end_lnum = <number>,
    end_col = <number>,
    severity = <vim.diagnostic.severity>,
    message = <string>
  }
  ```

This differs from the LSP definition of a diagnostic, so we transform them in
the handler functions in vim.lsp.diagnostic.

## Configuration

The `vim.lsp.with` paradigm still works for configuring how LSP diagnostics are
displayed, but this is a specific use-case for the `publishDiagnostics` handler.
Configuration with `vim.diagnostic` is instead done with the
`vim.diagnostic.config` function:

```lua
vim.diagnostic.config({
    virtual_text = true,
    signs = false,
    underline = true,
    update_in_insert = true,
    severity_sort = false,
}[, namespace])
```

(or alternatively passed directly to `set()` or `show()`.)

When the `namespace` argument is `nil`, settings are set globally (i.e. for
*all* diagnostic namespaces). This is what user's will typically use for their
local configuration. Diagnostic producers can also set configuration options for
their specific namespace, although this is generally discouraged in order to
respect the user's global settings. All of the values in the table passed to
`vim.diagnostic.config()` are resolved in the same way that they are in
`on_publish_diagnostics`; that is, the value can be a boolean, a table, or
a function:

```lua
vim.diagnostic.config({
    virtual_text = function(namespace, bufnr)
        -- Only enable virtual text in buffer 3
        return bufnr == 3
    end,
})
```

## Misc Notes

* `vim.diagnostic` currently depends on `vim.lsp.util` for floating window
  previews. I think this is okay for now, although ideally we'd want to decouple
  these completely.
This commit is contained in:
Justin M. Keyes 2021-09-16 14:23:42 -07:00 committed by GitHub
commit 2e8103475e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 3207 additions and 1959 deletions

View File

@ -55,6 +55,50 @@ Functions ~
without stopping the job. Use chanclose(id) to close
any socket.
LSP Diagnostics ~
For each of the functions below, use the corresponding function in
|vim.diagnostic| instead (unless otherwise noted). For example, use
|vim.diagnostic.get()| instead of |vim.lsp.diagnostic.get()|.
*vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead.
*vim.lsp.diagnostic.disable()*
*vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead.
*vim.lsp.diagnostic.enable()*
*vim.lsp.diagnostic.get()*
*vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead.
*vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.get()| instead.
*vim.lsp.diagnostic.get_line_diagnostics()*
Use |vim.diagnostic.get()| instead.
*vim.lsp.diagnostic.get_next()*
*vim.lsp.diagnostic.get_next_pos()*
*vim.lsp.diagnostic.get_prev()*
*vim.lsp.diagnostic.get_prev_pos()*
*vim.lsp.diagnostic.get_virtual_text_chunks_for_line()*
Use |vim.diagnostic.get_virt_text_chunks()| instead.
*vim.lsp.diagnostic.goto_next()*
*vim.lsp.diagnostic.goto_prev()*
*vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead.
*vim.lsp.diagnostic.reset()*
*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.
*vim.lsp.diagnostic.show_line_diagnostics()*
*vim.lsp.diagnostic.show_position_diagnostics()*
The following are deprecated without replacement. These functions are moved
internally and are no longer exposed as part of the API. Instead, use
|vim.diagnostic.config()| and |vim.diagnostic.show()|.
*vim.lsp.diagnostic.set_signs()*
*vim.lsp.diagnostic.set_underline()*
*vim.lsp.diagnostic.set_virtual_text()*
LSP Utility Functions ~
*vim.lsp.util.set_qflist()* Use |setqflist()| instead.
*vim.lsp.util.set_loclist()* Use |setloclist()| instead.
Lua ~
*vim.register_keystroke_callback()* Use |vim.on_key()| instead.

483
runtime/doc/diagnostic.txt Normal file
View File

@ -0,0 +1,483 @@
*diagnostic.txt* Diagnostics
NVIM REFERENCE MANUAL
Diagnostic framework *vim.diagnostic*
Nvim provides a framework for displaying errors or warnings from external
tools, otherwise known as "diagnostics". These diagnostics can come from a
variety of sources, such as linters or LSP servers. The diagnostic framework
is an extension to existing error handling functionality such as the
|quickfix| list.
Type |gO| to see the table of contents.
==============================================================================
QUICKSTART *diagnostic-quickstart*
Anything that reports diagnostics is referred to below as a "diagnostic
producer". Diagnostic producers need only follow a few simple steps to
report diagnostics:
1. Create a namespace |nvim_create_namespace()|. Note that the namespace must
have a name. Anonymous namespaces WILL NOT WORK.
2. (Optional) Configure options for the diagnostic namespace
|vim.diagnostic.config()|.
3. Generate diagnostics.
4. Set the diagnostics for the buffer |vim.diagnostic.set()|.
5. Repeat from step 3.
Generally speaking, the API is split between functions meant to be used by
diagnostic producers and those meant for diagnostic consumers (i.e. end users
who want to read and view the diagnostics for a buffer). The APIs for
producers require a {namespace} as their first argument, while those for
consumers generally do not require a namespace (though often one may be
optionally supplied). A good rule of thumb is that if a method is meant to
modify the diagnostics for a buffer (e.g. |vim.diagnostic.set()|) then it
requires a namespace.
*diagnostic-structure*
A diagnostic is a Lua table with the following keys:
lnum: The starting line of the diagnostic
end_lnum: The final line of the diagnostic
col: The starting column of the diagnostic
end_col: The final column of the diagnostic
severity: The severity of the diagnostic |vim.diagnostic.severity|
message: The diagnostic text
Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based
rows and columns). |api-indexing|
*vim.diagnostic.severity* *diagnostic-severity*
The "severity" key in a diagnostic is one of the values defined in
`vim.diagnostic.severity`:
vim.diagnostic.severity.ERROR
vim.diagnostic.severity.WARN
vim.diagnostic.severity.INFO
vim.diagnostic.severity.HINT
Functions that take a severity as an optional parameter (e.g.
|vim.diagnostic.get()|) accept one of two forms:
1. A single |vim.diagnostic.severity| value: >
vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN })
2. A table with a "min" or "max" key (or both): >
vim.diagnostic.get(0, { severity = {min=vim.diagnostic.severity.WARN})
The latter form allows users to specify a range of severities.
==============================================================================
HIGHLIGHTS *diagnostic-highlights*
All highlights defined for diagnostics begin with `Diagnostic` followed by
the type of highlight (e.g., `Sign`, `Underline`, etc.) and the severity (e.g.
`Error`, `Warn`, etc.)
Sign, underline and virtual text highlights (by default) are linked to their
corresponding default highlight.
For example, the default highlighting for |hl-DiagnosticSignError| is linked
to |hl-DiagnosticError|. To change the default (and therefore the linked
highlights), use the |:highlight| command: >
highlight DiagnosticError guifg="BrightRed"
<
*hl-DiagnosticError*
DiagnosticError
Used as the base highlight group.
Other Diagnostic highlights link to this by default (except Underline)
*hl-DiagnosticWarn*
DiagnosticWarn
Used as the base highlight group.
Other Diagnostic highlights link to this by default (except Underline)
*hl-DiagnosticInfo*
DiagnosticInfo
Used as the base highlight group.
Other Diagnostic highlights link to this by default (except Underline)
*hl-DiagnosticHint*
DiagnosticHint
Used as the base highlight group.
Other Diagnostic highlights link to this by default (except Underline)
*hl-DiagnosticVirtualTextError*
DiagnosticVirtualTextError
Used for "Error" diagnostic virtual text.
*hl-DiagnosticVirtualTextWarn*
DiagnosticVirtualTextWarn
Used for "Warn" diagnostic virtual text.
*hl-DiagnosticVirtualTextInfo*
DiagnosticVirtualTextInfo
Used for "Info" diagnostic virtual text.
*hl-DiagnosticVirtualTextHint*
DiagnosticVirtualTextHint
Used for "Hint" diagnostic virtual text.
*hl-DiagnosticUnderlineError*
DiagnosticUnderlineError
Used to underline "Error" diagnostics.
*hl-DiagnosticUnderlineWarn*
DiagnosticUnderlineWarn
Used to underline "Warn" diagnostics.
*hl-DiagnosticUnderlineInfo*
DiagnosticUnderlineInfo
Used to underline "Info" diagnostics.
*hl-DiagnosticUnderlineHint*
DiagnosticUnderlineHint
Used to underline "Hint" diagnostics.
*hl-DiagnosticFloatingError*
DiagnosticFloatingError
Used to color "Error" diagnostic messages in diagnostics float.
See |vim.diagnostic.show_line_diagnostics()|
*hl-DiagnosticFloatingWarn*
DiagnosticFloatingWarn
Used to color "Warn" diagnostic messages in diagnostics float.
*hl-DiagnosticFloatingInfo*
DiagnosticFloatingInfo
Used to color "Info" diagnostic messages in diagnostics float.
*hl-DiagnosticFloatingHint*
DiagnosticFloatingHint
Used to color "Hint" diagnostic messages in diagnostics float.
*hl-DiagnosticSignError*
DiagnosticSignError
Used for "Error" signs in sign column.
*hl-DiagnosticSignWarn*
DiagnosticSignWarn
Used for "Warn" signs in sign column.
*hl-DiagnosticSignInfo*
DiagnosticSignInfo
Used for "Info" signs in sign column.
*hl-DiagnosticSignHint*
DiagnosticSignHint
Used for "Hint" signs in sign column.
==============================================================================
SIGNS *diagnostic-signs*
Signs are defined for each diagnostic severity. The default text for each sign
is the first letter of the severity name (for example, "E" for ERROR). Signs
can be customized using the following: >
sign define DiagnosticSignError text=E texthl=DiagnosticSignError linehl= numhl=
sign define DiagnosticSignWarning text=W texthl=DiagnosticSignWarning linehl= numhl=
sign define DiagnosticSignInformation text=I texthl=DiagnosticSignInformation linehl= numhl=
sign define DiagnosticSignHint text=H texthl=DiagnosticSignHint linehl= numhl=
==============================================================================
EVENTS *diagnostic-events*
*DiagnosticsChanged*
DiagnosticsChanged After diagnostics have changed.
Example: >
autocmd User DiagnosticsChanged lua vim.diagnostic.setqflist({open = false })
<
==============================================================================
Lua module: vim.diagnostic *diagnostic-api*
config({opts}, {namespace}) *vim.diagnostic.config()*
Configure diagnostic options globally or for a specific
diagnostic namespace.
Note:
Each of the configuration options below accepts one of the
following:
• `false` : Disable this feature
• `true` : Enable this feature, use default settings.
• `table` : Enable this feature with overrides.
• `function` : Function with signature (namespace, bufnr)
that returns any of the above.
Parameters: ~
{opts} table Configuration table with the following
keys:
• underline: (default true) Use underline for
diagnostics
• virtual_text: (default true) Use virtual
text for diagnostics
• signs: (default true) Use signs for
diagnostics
• 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 which signs and virtual text are
displayed
{namespace} number|nil Update the options for the given
namespace. When omitted, update the global
diagnostic options.
disable({bufnr}, {namespace}) *vim.diagnostic.disable()*
Disable diagnostics in the given buffer.
Parameters: ~
{bufnr} number|nil Buffer number. Defaults to the
current buffer.
{namespace} number|nil Only disable diagnostics for the
given namespace.
enable({bufnr}, {namespace}) *vim.diagnostic.enable()*
Enable diagnostics in the given buffer.
Parameters: ~
{bufnr} number|nil Buffer number. Defaults to the
current buffer.
{namespace} number|nil Only enable diagnostics for the
given namespace.
get({bufnr}, {opts}) *vim.diagnostic.get()*
Get current diagnostics.
Parameters: ~
{bufnr} number|nil Buffer number to get diagnistics from.
Use 0 for current buffer or nil for all buffers.
{opts} table|nil A table with the following keys:
• namespace: (number) Limit diagnostics to the
given namespace.
• lnum: (number) Limit diagnostics to the given
line number.
• severity: See |diagnostic-severity|.
Return: ~
table A list of diagnostic items |diagnostic-structure|.
get_next({opts}) *vim.diagnostic.get_next()*
Get the next diagnostic closest to the cursor position.
Parameters: ~
{opts} table See |vim.diagnostic.goto_next()|
Return: ~
table Next diagnostic
get_next_pos({opts}) *vim.diagnostic.get_next_pos()*
Return the position of the next diagnostic in the current
buffer.
Parameters: ~
{opts} table See |vim.diagnostic.goto_next()|
Return: ~
table Next diagnostic position as a (row, col) tuple.
get_prev({opts}) *vim.diagnostic.get_prev()*
Get the previous diagnostic closest to the cursor position.
Parameters: ~
{opts} table See |vim.diagnostic.goto_next()|
Return: ~
table Previous diagnostic
get_prev_pos({opts}) *vim.diagnostic.get_prev_pos()*
Return the position of the previous diagnostic in the current
buffer.
Parameters: ~
{opts} table See |vim.diagnostic.goto_next()|
Return: ~
table Previous diagnostic position as a (row, col) tuple.
*vim.diagnostic.get_virt_text_chunks()*
get_virt_text_chunks({line_diags}, {opts})
Get virtual text chunks to display using
|nvim_buf_set_extmark()|.
Parameters: ~
{line_diags} table The diagnostics associated with the
line.
{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.
Return: ~
an array of [text, hl_group] arrays. This can be passed
directly to the {virt_text} option of
|nvim_buf_set_extmark()|.
goto_next({opts}) *vim.diagnostic.goto_next()*
Move to the next diagnostic.
Parameters: ~
{opts} table|nil Configuration table with the following
keys:
• namespace: (number) Only consider diagnostics
from the given namespace.
• cursor_position: (cursor position) Cursor
position as a (row, col) tuple. See
|nvim_win_get_cursor()|. Defaults to the current
cursor position.
• 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()|
• win_id: (number, default 0) Window ID
goto_prev({opts}) *vim.diagnostic.goto_prev()*
Move to the previous diagnostic in the current buffer.
Parameters: ~
{opts} table See |vim.diagnostic.goto_next()|
hide({namespace}, {bufnr}) *vim.diagnostic.hide()*
Hide currently displayed diagnostics.
This only clears the decorations displayed in the buffer.
Diagnostics can be redisplayed with |vim.diagnostic.show()|.
To completely remove diagnostics, use
|vim.diagnostic.reset()|.
To hide diagnostics and prevent them from re-displaying, use
|vim.diagnostic.disable()|.
Parameters: ~
{namespace} number The diagnostic namespace
{bufnr} number|nil Buffer number. Defaults to the
current buffer.
reset({namespace}, {bufnr}) *vim.diagnostic.reset()*
Remove all diagnostics from the given namespace.
Unlike |vim.diagnostic.hide()|, this function removes all
saved diagnostics. They cannot be redisplayed using
|vim.diagnostic.show()|. To simply remove diagnostic
decorations in a way that they can be re-displayed, use
|vim.diagnostic.hide()|.
Parameters: ~
{namespace} number
{bufnr} number|nil Remove diagnostics for the given
buffer. When omitted, diagnostics are removed
for all buffers.
set({namespace}, {bufnr}, {diagnostics}, {opts}) *vim.diagnostic.set()*
Set diagnostics for the given namespace and buffer.
Parameters: ~
{namespace} number The diagnostic namespace
{bufnr} number Buffer number
{diagnostics} table A list of diagnostic items
|diagnostic-structure|
{opts} table|nil Display options to pass to
|vim.diagnostic.show()|
setloclist({opts}) *vim.diagnostic.setloclist()*
Add buffer diagnostics to the location list.
Parameters: ~
{opts} table|nil Configuration table with the following
keys:
• namespace: (number) Only add diagnostics from
the given namespace.
• winnr: (number, default 0) Window number to set
location list for.
• open: (boolean, default true) Open the location
list after setting.
• title: (string) Title of the location list.
Defaults to "Diagnostics".
• severity: See |diagnostic-severity|.
setqflist({opts}) *vim.diagnostic.setqflist()*
Add all diagnostics to the quickfix list.
Parameters: ~
{opts} table|nil Configuration table with the following
keys:
• namespace: (number) Only add diagnostics from
the given namespace.
• open: (boolean, default true) Open quickfix list
after setting.
• title: (string) Title of quickfix list. Defaults
to "Diagnostics".
• severity: See |diagnostic-severity|.
*vim.diagnostic.show()*
show({namespace}, {bufnr}, {diagnostics}, {opts})
Display diagnostics for the given namespace and buffer.
Parameters: ~
{namespace} number Diagnostic namespace
{bufnr} number|nil Buffer number. Defaults to the
current buffer.
{diagnostics} table|nil The diagnostics to display. When
omitted, use the saved diagnostics for the
given namespace and buffer. This can be
used to display a list of diagnostics
without saving them or to display only a
subset of diagnostics.
{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: ~
A ({popup_bufnr}, {win_id}) tuple
*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_floatin_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
{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: ~
A ({popup_bufnr}, {win_id}) tuple
vim:tw=78:ts=8:ft=help:norl:

View File

@ -424,121 +424,6 @@ LspReferenceRead used for highlighting "read" references
LspReferenceWrite used for highlighting "write" references
*lsp-highlight-diagnostics*
All highlights defined for diagnostics begin with `LspDiagnostics` followed by
the type of highlight (e.g., `Sign`, `Underline`, etc.) and then the Severity
of the highlight (e.g. `Error`, `Warning`, etc.)
Sign, underline and virtual text highlights (by default) are linked to their
corresponding LspDiagnosticsDefault highlight.
For example, the default highlighting for |hl-LspDiagnosticsSignError| is
linked to |hl-LspDiagnosticsDefaultError|. To change the default (and
therefore the linked highlights), use the |:highlight| command: >
highlight LspDiagnosticsDefaultError guifg="BrightRed"
<
*hl-LspDiagnosticsDefaultError*
LspDiagnosticsDefaultError
Used as the base highlight group.
Other LspDiagnostic highlights link to this by default (except Underline)
*hl-LspDiagnosticsDefaultWarning*
LspDiagnosticsDefaultWarning
Used as the base highlight group.
Other LspDiagnostic highlights link to this by default (except Underline)
*hl-LspDiagnosticsDefaultInformation*
LspDiagnosticsDefaultInformation
Used as the base highlight group.
Other LspDiagnostic highlights link to this by default (except Underline)
*hl-LspDiagnosticsDefaultHint*
LspDiagnosticsDefaultHint
Used as the base highlight group.
Other LspDiagnostic highlights link to this by default (except Underline)
*hl-LspDiagnosticsVirtualTextError*
LspDiagnosticsVirtualTextError
Used for "Error" diagnostic virtual text.
See |vim.lsp.diagnostic.set_virtual_text()|
*hl-LspDiagnosticsVirtualTextWarning*
LspDiagnosticsVirtualTextWarning
Used for "Warning" diagnostic virtual text.
See |vim.lsp.diagnostic.set_virtual_text()|
*hl-LspDiagnosticsVirtualTextInformation*
LspDiagnosticsVirtualTextInformation
Used for "Information" diagnostic virtual text.
See |vim.lsp.diagnostic.set_virtual_text()|
*hl-LspDiagnosticsVirtualTextHint*
LspDiagnosticsVirtualTextHint
Used for "Hint" diagnostic virtual text.
See |vim.lsp.diagnostic.set_virtual_text()|
*hl-LspDiagnosticsUnderlineError*
LspDiagnosticsUnderlineError
Used to underline "Error" diagnostics.
See |vim.lsp.diagnostic.set_underline()|
*hl-LspDiagnosticsUnderlineWarning*
LspDiagnosticsUnderlineWarning
Used to underline "Warning" diagnostics.
See |vim.lsp.diagnostic.set_underline()|
*hl-LspDiagnosticsUnderlineInformation*
LspDiagnosticsUnderlineInformation
Used to underline "Information" diagnostics.
See |vim.lsp.diagnostic.set_underline()|
*hl-LspDiagnosticsUnderlineHint*
LspDiagnosticsUnderlineHint
Used to underline "Hint" diagnostics.
See |vim.lsp.diagnostic.set_underline()|
*hl-LspDiagnosticsFloatingError*
LspDiagnosticsFloatingError
Used to color "Error" diagnostic messages in diagnostics float.
See |vim.lsp.diagnostic.show_line_diagnostics()|
*hl-LspDiagnosticsFloatingWarning*
LspDiagnosticsFloatingWarning
Used to color "Warning" diagnostic messages in diagnostics float.
See |vim.lsp.diagnostic.show_line_diagnostics()|
*hl-LspDiagnosticsFloatingInformation*
LspDiagnosticsFloatingInformation
Used to color "Information" diagnostic messages in diagnostics float.
See |vim.lsp.diagnostic.show_line_diagnostics()|
*hl-LspDiagnosticsFloatingHint*
LspDiagnosticsFloatingHint
Used to color "Hint" diagnostic messages in diagnostics float.
See |vim.lsp.diagnostic.show_line_diagnostics()|
*hl-LspDiagnosticsSignError*
LspDiagnosticsSignError
Used for "Error" signs in sign column.
See |vim.lsp.diagnostic.set_signs()|
*hl-LspDiagnosticsSignWarning*
LspDiagnosticsSignWarning
Used for "Warning" signs in sign column.
See |vim.lsp.diagnostic.set_signs()|
*hl-LspDiagnosticsSignInformation*
LspDiagnosticsSignInformation
Used for "Information" signs in sign column.
See |vim.lsp.diagnostic.set_signs()|
*hl-LspDiagnosticsSignHint*
LspDiagnosticsSignHint
Used for "Hint" signs in sign column.
See |vim.lsp.diagnostic.set_signs()|
*lsp-highlight-codelens*
Highlight groups related to |lsp-codelens| functionality.
@ -560,13 +445,6 @@ LspSignatureActiveParameter
Used to highlight the active parameter in the signature help. See
|vim.lsp.handlers.signature_help()|.
==============================================================================
AUTOCOMMANDS *lsp-autocommands*
*LspDiagnosticsChanged*
LspDiagnosticsChanged After receiving publishDiagnostics server response
==============================================================================
Lua module: vim.lsp *lsp-core*
@ -1207,220 +1085,20 @@ workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()*
==============================================================================
Lua module: vim.lsp.diagnostic *lsp-diagnostic*
*vim.lsp.diagnostic.clear()*
clear({bufnr}, {client_id}, {diagnostic_ns}, {sign_ns})
Clears the currently displayed diagnostics
get_namespace({client_id}) *vim.lsp.diagnostic.get_namespace()*
Get the diagnostic namespace associated with an LSP client
|vim.diagnostic|.
Parameters: ~
{bufnr} number The buffer number
{client_id} number the client id
{diagnostic_ns} number|nil Associated diagnostic
namespace
{sign_ns} number|nil Associated sign namespace
disable({bufnr}, {client_id}) *vim.lsp.diagnostic.disable()*
Disable diagnostics for the given buffer and client
Parameters: ~
{bufnr} (optional, number): Buffer handle, defaults
to current
{client_id} (optional, number): Disable diagnostics for
the given client. The default is to disable
diagnostics for all attached clients.
enable({bufnr}, {client_id}) *vim.lsp.diagnostic.enable()*
Enable diagnostics for the given buffer and client
Parameters: ~
{bufnr} (optional, number): Buffer handle, defaults
to current
{client_id} (optional, number): Enable diagnostics for
the given client. The default is to enable
diagnostics for all attached clients.
get({bufnr}, {client_id}, {predicate}) *vim.lsp.diagnostic.get()*
Return associated diagnostics for bufnr
Parameters: ~
{bufnr} number
{client_id} number|nil If nil, then return all of the
diagnostics. Else, return just the
diagnostics associated with the client_id.
{predicate} function|nil Optional function for filtering
diagnostics
get_all({client_id}) *vim.lsp.diagnostic.get_all()*
Get all diagnostics for clients
Parameters: ~
{client_id} number Restrict included diagnostics to the
client If nil, diagnostics of all clients are
included.
Return: ~
table with diagnostics grouped by bufnr (bufnr:Diagnostic[])
*vim.lsp.diagnostic.get_count()*
get_count({bufnr}, {severity}, {client_id})
Get the counts for a particular severity
Useful for showing diagnostic counts in statusline. eg:
>
function! LspStatus() abort
let sl = ''
if luaeval('not vim.tbl_isempty(vim.lsp.buf_get_clients(0))')
let sl.='%#MyStatuslineLSP#E:'
let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.diagnostic.get_count(0, [[Error]])")}'
let sl.='%#MyStatuslineLSP# W:'
let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.diagnostic.get_count(0, [[Warning]])")}'
else
let sl.='%#MyStatuslineLSPErrors#off'
endif
return sl
endfunction
let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus()
<
Parameters: ~
{bufnr} number The buffer number
{severity} DiagnosticSeverity
{client_id} number the client id
*vim.lsp.diagnostic.get_line_diagnostics()*
get_line_diagnostics({bufnr}, {line_nr}, {opts}, {client_id})
Get the diagnostics by line
Parameters: ~
{bufnr} number|nil The buffer number
{line_nr} number|nil The line number
{opts} table|nil Configuration keys
• severity: (DiagnosticSeverity, default nil)
• Only return diagnostics with this
severity. Overrides severity_limit
• severity_limit: (DiagnosticSeverity, default nil)
• Limit severity of diagnostics found.
E.g. "Warning" means { "Error",
"Warning" } will be valid.
{client_id|nil} number the client id
Return: ~
table Table with map of line number to list of
diagnostics.
get_next({opts}) *vim.lsp.diagnostic.get_next()*
Get the next diagnostic closest to the cursor_position
Parameters: ~
{opts} table See |vim.lsp.diagnostic.goto_next()|
Return: ~
table Next diagnostic
get_next_pos({opts}) *vim.lsp.diagnostic.get_next_pos()*
Return the pos, {row, col}, for the next diagnostic in the
current buffer.
Parameters: ~
{opts} table See |vim.lsp.diagnostic.goto_next()|
Return: ~
table Next diagnostic position
get_prev({opts}) *vim.lsp.diagnostic.get_prev()*
Get the previous diagnostic closest to the cursor_position
Parameters: ~
{opts} table See |vim.lsp.diagnostic.goto_next()|
Return: ~
table Previous diagnostic
get_prev_pos({opts}) *vim.lsp.diagnostic.get_prev_pos()*
Return the pos, {row, col}, for the prev diagnostic in the
current buffer.
Parameters: ~
{opts} table See |vim.lsp.diagnostic.goto_next()|
Return: ~
table Previous diagnostic position
*vim.lsp.diagnostic.get_virtual_text_chunks_for_line()*
get_virtual_text_chunks_for_line({bufnr}, {line}, {line_diags}, {opts})
Default function to get text chunks to display using
|nvim_buf_set_extmark()|.
Parameters: ~
{bufnr} number The buffer to display the virtual
text in
{line} number The line number to display the
virtual text on
{line_diags} Diagnostic [] The diagnostics associated with the line
{opts} table See {opts} from
|vim.lsp.diagnostic.set_virtual_text()|
Return: ~
an array of [text, hl_group] arrays. This can be passed
directly to the {virt_text} option of
|nvim_buf_set_extmark()|.
goto_next({opts}) *vim.lsp.diagnostic.goto_next()*
Move to the next diagnostic
Parameters: ~
{opts} table|nil Configuration table. Keys:
• {client_id}: (number)
• If nil, will consider all clients attached to
buffer.
• {cursor_position}: (Position, default current
position)
• See |nvim_win_get_cursor()|
• {wrap}: (boolean, default true)
• Whether to loop around file or not. Similar to
'wrapscan'
• {severity}: (DiagnosticSeverity)
• Exclusive severity to consider. Overrides
{severity_limit}
• {severity_limit}: (DiagnosticSeverity)
• Limit severity of diagnostics found. E.g.
"Warning" means { "Error", "Warning" } will be
valid.
• {enable_popup}: (boolean, default true)
• Call
|vim.lsp.diagnostic.show_line_diagnostics()|
on jump
• {popup_opts}: (table)
• Table to pass as {opts} parameter to
|vim.lsp.diagnostic.show_line_diagnostics()|
• {win_id}: (number, default 0)
• Window ID
goto_prev({opts}) *vim.lsp.diagnostic.goto_prev()*
Move to the previous diagnostic
Parameters: ~
{opts} table See |vim.lsp.diagnostic.goto_next()|
{client_id} number The id of the LSP client
*vim.lsp.diagnostic.on_publish_diagnostics()*
on_publish_diagnostics({_}, {result}, {ctx}, {config})
|lsp-handler| for the method "textDocument/publishDiagnostics"
Note:
Each of the configuration options accepts:
• `false` : Disable this feature
• `true` : Enable this feature, use default settings.
• `table` : Enable this feature, use overrides.
• `function`: Function with signature (bufnr, client_id) that
returns any of the above.>
See |vim.diagnostic.config()| for configuration options.
Handler-specific configuration can be set using
|vim.lsp.with()|: >
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
@ -1442,229 +1120,8 @@ on_publish_diagnostics({_}, {result}, {ctx}, {config})
<
Parameters: ~
{config} table Configuration table.
• underline: (default=true)
• Apply underlines to diagnostics.
• See |vim.lsp.diagnostic.set_underline()|
• virtual_text: (default=true)
• Apply virtual text to line endings.
• See |vim.lsp.diagnostic.set_virtual_text()|
• signs: (default=true)
• Apply signs for diagnostics.
• See |vim.lsp.diagnostic.set_signs()|
• update_in_insert: (default=false)
• Update diagnostics in InsertMode or wait
until InsertLeave
• severity_sort: (default=false)
• Sort diagnostics (and thus signs and virtual
text)
redraw({bufnr}, {client_id}) *vim.lsp.diagnostic.redraw()*
Redraw diagnostics for the given buffer and client
This calls the "textDocument/publishDiagnostics" handler
manually using the cached diagnostics already received from
the server. This can be useful for redrawing diagnostics after
making changes in diagnostics configuration.
|lsp-handler-configuration|
Parameters: ~
{bufnr} (optional, number): Buffer handle, defaults
to current
{client_id} (optional, number): Redraw diagnostics for
the given client. The default is to redraw
diagnostics for all attached clients.
reset({client_id}, {buffer_client_map}) *vim.lsp.diagnostic.reset()*
Clear diagnotics and diagnostic cache
Handles saving diagnostics from multiple clients in the same
buffer.
Parameters: ~
{client_id} number
{buffer_client_map} table map of buffers to active
clients
save({diagnostics}, {bufnr}, {client_id}) *vim.lsp.diagnostic.save()*
Save diagnostics to the current buffer.
Handles saving diagnostics from multiple clients in the same
buffer.
Parameters: ~
{diagnostics} Diagnostic []
{bufnr} number
{client_id} number
set_loclist({opts}) *vim.lsp.diagnostic.set_loclist()*
Sets the location list
Parameters: ~
{opts} table|nil Configuration table. Keys:
• {open}: (boolean, default true)
• Open loclist after set
• {client_id}: (number)
• If nil, will consider all clients attached to
buffer.
• {severity}: (DiagnosticSeverity)
• Exclusive severity to consider. Overrides
{severity_limit}
• {severity_limit}: (DiagnosticSeverity)
• Limit severity of diagnostics found. E.g.
"Warning" means { "Error", "Warning" } will be
valid.
• {workspace}: (boolean, default false)
• Set the list with workspace diagnostics
set_qflist({opts}) *vim.lsp.diagnostic.set_qflist()*
Sets the quickfix list
Parameters: ~
{opts} table|nil Configuration table. Keys:
• {open}: (boolean, default true)
• Open quickfix list after set
• {client_id}: (number)
• If nil, will consider all clients attached to
buffer.
• {severity}: (DiagnosticSeverity)
• Exclusive severity to consider. Overrides
{severity_limit}
• {severity_limit}: (DiagnosticSeverity)
• Limit severity of diagnostics found. E.g.
"Warning" means { "Error", "Warning" } will be
valid.
• {workspace}: (boolean, default true)
• Set the list with workspace diagnostics
*vim.lsp.diagnostic.set_signs()*
set_signs({diagnostics}, {bufnr}, {client_id}, {sign_ns}, {opts})
Set signs for given diagnostics
Sign characters can be customized with the following commands:
>
sign define LspDiagnosticsSignError text=E texthl=LspDiagnosticsSignError linehl= numhl=
sign define LspDiagnosticsSignWarning text=W texthl=LspDiagnosticsSignWarning linehl= numhl=
sign define LspDiagnosticsSignInformation text=I texthl=LspDiagnosticsSignInformation linehl= numhl=
sign define LspDiagnosticsSignHint text=H texthl=LspDiagnosticsSignHint linehl= numhl=
<
Parameters: ~
{diagnostics} Diagnostic []
{bufnr} number The buffer number
{client_id} number the client id
{sign_ns} number|nil
{opts} table Configuration for signs. Keys:
• priority: Set the priority of the signs.
• severity_limit (DiagnosticSeverity):
• Limit severity of diagnostics found.
E.g. "Warning" means { "Error",
"Warning" } will be valid.
*vim.lsp.diagnostic.set_underline()*
set_underline({diagnostics}, {bufnr}, {client_id}, {diagnostic_ns}, {opts})
Set underline for given diagnostics
Underline highlights can be customized by changing the
following |:highlight| groups.
>
LspDiagnosticsUnderlineError
LspDiagnosticsUnderlineWarning
LspDiagnosticsUnderlineInformation
LspDiagnosticsUnderlineHint
<
Parameters: ~
{diagnostics} Diagnostic []
{bufnr} number: The buffer number
{client_id} number: The client id
{diagnostic_ns} number|nil: The namespace
{opts} table: Configuration table:
• severity_limit (DiagnosticSeverity):
• Limit severity of diagnostics found.
E.g. "Warning" means { "Error",
"Warning" } will be valid.
*vim.lsp.diagnostic.set_virtual_text()*
set_virtual_text({diagnostics}, {bufnr}, {client_id}, {diagnostic_ns}, {opts})
Set virtual text given diagnostics
Virtual text highlights can be customized by changing the
following |:highlight| groups.
>
LspDiagnosticsVirtualTextError
LspDiagnosticsVirtualTextWarning
LspDiagnosticsVirtualTextInformation
LspDiagnosticsVirtualTextHint
<
Parameters: ~
{diagnostics} Diagnostic []
{bufnr} number
{client_id} number
{diagnostic_ns} number
{opts} table Options on how to display virtual
text. Keys:
• prefix (string): Prefix to display
before virtual text on line
• spacing (number): Number of spaces to
insert before virtual text
• severity_limit (DiagnosticSeverity):
• Limit severity of diagnostics found.
E.g. "Warning" means { "Error",
"Warning" } will be valid.
*vim.lsp.diagnostic.show_line_diagnostics()*
show_line_diagnostics({opts}, {buf_nr}, {line_nr}, {client_id})
Parameters: ~
{opts} table Configuration table
• all opts for
|vim.lsp.diagnostic.get_line_diagnostics()|
and |show_diagnostics()| can be used here
{buf_nr} number|nil The buffer number
{line_nr} number|nil The line number
{client_id} number|nil the client id
Return: ~
table {popup_bufnr, win_id}
*vim.lsp.diagnostic.show_position_diagnostics()*
show_position_diagnostics({opts}, {buf_nr}, {position})
Open a floating window with the diagnostics from {position}
Parameters: ~
{opts} table|nil Configuration keys
• severity: (DiagnosticSeverity, default nil)
• Only return diagnostics with this
severity. Overrides severity_limit
• severity_limit: (DiagnosticSeverity, default nil)
• Limit severity of diagnostics found. E.g.
"Warning" means { "Error", "Warning" }
will be valid.
• all opts for |show_diagnostics()| can be
used here
{buf_nr} number|nil The buffer number
{position} table|nil The (0,0)-indexed position
Return: ~
table {popup_bufnr, win_id}
{config} table Configuration table (see
|vim.diagnostic.config()|).
==============================================================================
@ -1903,21 +1360,6 @@ create_file({change}) *vim.lsp.util.create_file()*
delete_file({change}) *vim.lsp.util.delete_file()*
TODO: Documentation
*vim.lsp.util.diagnostics_to_items()*
diagnostics_to_items({diagnostics_by_bufnr}, {predicate})
Convert diagnostics grouped by bufnr to a list of items for
use in the quickfix or location list.
Parameters: ~
{diagnostics_by_bufnr} table bufnr -> Diagnostic []
{predicate} an optional function to filter the
diagnostics. If present, only
diagnostic items matching will be
included.
Return: ~
table (A list of items)
*vim.lsp.util.extract_completion_items()*
extract_completion_items({result})
Can be used to extract the completion items from a `textDocument/completion` request, which may return one of `CompletionItem[]` , `CompletionList` or null.
@ -1982,6 +1424,9 @@ locations_to_items({locations}) *vim.lsp.util.locations_to_items()*
and in sorted order, for display in quickfix and location
lists.
The result can be passed to the {list} argument of
|setqflist()| or |setloclist()|.
Parameters: ~
{locations} (table) list of `Location` s or
`LocationLink` s
@ -2166,21 +1611,6 @@ set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()*
Return: ~
(table) The modified {lines} object
set_loclist({items}, {win_id}) *vim.lsp.util.set_loclist()*
Fills target window's location list with given list of items.
Can be obtained with e.g. |vim.lsp.util.locations_to_items()|.
Defaults to current window.
Parameters: ~
{items} (table) list of items
set_qflist({items}) *vim.lsp.util.set_qflist()*
Fills quickfix list with given list of items. Can be obtained
with e.g. |vim.lsp.util.locations_to_items()|.
Parameters: ~
{items} (table) list of items
*vim.lsp.util.stylize_markdown()*
stylize_markdown({bufnr}, {contents}, {opts})
Converts markdown into syntax highlighted regions by stripping

File diff suppressed because it is too large Load Diff

View File

@ -1534,8 +1534,5 @@ function lsp._with_extend(name, options, user_config)
return resulting_config
end
-- Define the LspDiagnostics signs if they're not defined already.
require('vim.lsp.diagnostic')._define_default_signs_and_highlights()
return lsp
-- vim:sw=2 ts=2 et

File diff suppressed because it is too large Load Diff

View File

@ -210,10 +210,16 @@ local function response_to_list(map_result, entity)
else
config = config or {}
if config.loclist then
util.set_loclist(map_result(result, ctx.bufnr))
vim.fn.setloclist(0, {}, ' ', {
title = 'Language Server';
items = map_result(result, ctx.bufnr);
})
api.nvim_command("lopen")
else
util.set_qflist(map_result(result, ctx.bufnr))
vim.fn.setqflist({}, ' ', {
title = 'Language Server';
items = map_result(result, ctx.bufnr);
})
api.nvim_command("copen")
end
end

View File

@ -31,16 +31,6 @@ local default_border = {
{" ", "NormalFloat"},
}
local DiagnosticSeverity = protocol.DiagnosticSeverity
local loclist_type_map = {
[DiagnosticSeverity.Error] = 'E',
[DiagnosticSeverity.Warning] = 'W',
[DiagnosticSeverity.Information] = 'I',
[DiagnosticSeverity.Hint] = 'I',
}
---@private
--- Check the border given by opts or the default border for the additional
--- size it adds to a float.
@ -1543,6 +1533,9 @@ end
--- Returns the items with the byte position calculated correctly and in sorted
--- order, for display in quickfix and location lists.
---
--- The result can be passed to the {list} argument of |setqflist()| or
--- |setloclist()|.
---
---@param locations (table) list of `Location`s or `LocationLink`s
---@returns (table) list of items
function M.locations_to_items(locations)
@ -1601,6 +1594,8 @@ end
--- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|.
--- Defaults to current window.
---
---@deprecated Use |setloclist()|
---
---@param items (table) list of items
function M.set_loclist(items, win_id)
vim.fn.setloclist(win_id or 0, {}, ' ', {
@ -1612,6 +1607,8 @@ end
--- Fills quickfix list with given list of items.
--- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|.
---
---@deprecated Use |setqflist()|
---
---@param items (table) list of items
function M.set_qflist(items)
vim.fn.setqflist({}, ' ', {
@ -1869,40 +1866,6 @@ function M.lookup_section(settings, section)
return settings
end
--- Convert diagnostics grouped by bufnr to a list of items for use in the
--- quickfix or location list.
---
---@param diagnostics_by_bufnr table bufnr -> Diagnostic[]
---@param predicate an optional function to filter the diagnostics.
--- If present, only diagnostic items matching will be included.
---@return table (A list of items)
function M.diagnostics_to_items(diagnostics_by_bufnr, predicate)
local items = {}
for bufnr, diagnostics in pairs(diagnostics_by_bufnr or {}) do
for _, d in pairs(diagnostics) do
if not predicate or predicate(d) then
table.insert(items, {
bufnr = bufnr,
lnum = d.range.start.line + 1,
col = d.range.start.character + 1,
text = d.message,
type = loclist_type_map[d.severity or DiagnosticSeverity.Error] or 'E'
})
end
end
end
table.sort(items, function(a, b)
if a.bufnr == b.bufnr then
return a.lnum < b.lnum
else
return a.bufnr < b.bufnr
end
end)
return items
end
M._get_line_byte_from_position = get_line_byte_from_position
M._warn_once = warn_once

View File

@ -0,0 +1,48 @@
" :help vim.diagnostic
hi DiagnosticError ctermfg=1 guifg=Red
hi DiagnosticWarn ctermfg=3 guifg=Orange
hi DiagnosticInfo ctermfg=4 guifg=LightBlue
hi DiagnosticHint ctermfg=7 guifg=LightGrey
hi DiagnosticUnderlineError cterm=underline gui=underline guisp=Red
hi DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange
hi DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue
hi DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey
hi link DiagnosticVirtualTextError DiagnosticError
hi link DiagnosticVirtualTextWarn DiagnosticWarn
hi link DiagnosticVirtualTextInfo DiagnosticInfo
hi link DiagnosticVirtualTextHint DiagnosticHint
hi link DiagnosticFloatingError DiagnosticError
hi link DiagnosticFloatingWarn DiagnosticWarn
hi link DiagnosticFloatingInfo DiagnosticInfo
hi link DiagnosticFloatingHint DiagnosticHint
hi link DiagnosticSignError DiagnosticError
hi link DiagnosticSignWarn DiagnosticWarn
hi link DiagnosticSignInfo DiagnosticInfo
hi link DiagnosticSignHint DiagnosticHint
" Link LspDiagnostics for backward compatibility
hi link LspDiagnosticsDefaultHint DiagnosticHint
hi link LspDiagnosticsVirtualTextHint DiagnosticVirtualTextHint
hi link LspDiagnosticsFloatingHint DiagnosticFloatingHint
hi link LspDiagnosticsSignHint DiagnosticSignHint
hi link LspDiagnosticsDefaultError DiagnosticError
hi link LspDiagnosticsVirtualTextError DiagnosticVirtualTextError
hi link LspDiagnosticsFloatingError DiagnosticFloatingError
hi link LspDiagnosticsSignError DiagnosticSignError
hi link LspDiagnosticsDefaultWarning DiagnosticWarn
hi link LspDiagnosticsVirtualTextWarning DiagnosticVirtualTextWarn
hi link LspDiagnosticsFloatingWarning DiagnosticFloatingWarn
hi link LspDiagnosticsSignWarning DiagnosticSignWarn
hi link LspDiagnosticsDefaultInformation DiagnosticInfo
hi link LspDiagnosticsVirtualTextInformation DiagnosticVirtualTextInfo
hi link LspDiagnosticsFloatingInformation DiagnosticFloatingInfo
hi link LspDiagnosticsSignInformation DiagnosticSignInfo
hi link LspDiagnosticsUnderlineError DiagnosticUnderlineError
hi link LspDiagnosticsUnderlineWarning DiagnosticUnderlineWarn
hi link LspDiagnosticsUnderlineInformation DiagnosticUnderlineInfo
hi link LspDiagnosticsUnderlineHint DiagnosticUnderlineHint

View File

@ -187,6 +187,23 @@ CONFIG = {
'module_override': {},
'append_only': [],
},
'diagnostic': {
'mode': 'lua',
'filename': 'diagnostic.txt',
'section_start_token': '*diagnostic-api*',
'section_order': [
'diagnostic.lua',
],
'files': os.path.join(base_dir, 'runtime/lua/vim/diagnostic.lua'),
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {'diagnostic.lua': 'diagnostic'},
'section_fmt': lambda _: 'Lua module: vim.diagnostic',
'helptag_fmt': lambda _: '*diagnostic-api*',
'fn_helptag_fmt': lambda fstem, name: f'*vim.{fstem}.{name}()*',
'module_override': {},
'append_only': [],
},
'treesitter': {
'mode': 'lua',
'filename': 'treesitter.txt',

View File

@ -105,6 +105,9 @@ setmetatable(vim, {
elseif key == 'highlight' then
t.highlight = require('vim.highlight')
return t.highlight
elseif key == 'diagnostic' then
t.diagnostic = require('vim.diagnostic')
return t.diagnostic
end
end
})

View File

@ -263,6 +263,8 @@ Number; !must be defined to function properly):
- `TEST_TIMEOUT` (FU) (I): specifies maximum time, in seconds, before the test
suite run is killed
- `NVIM_LUA_NOTRACK` (F) (D): disable reference counting of Lua objects
- `NVIM_PROG`, `NVIM_PRG` (F) (S): override path to Neovim executable (default
to `build/bin/nvim`).

View File

@ -0,0 +1,831 @@
local helpers = require('test.functional.helpers')(after_each)
local command = helpers.command
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local eq = helpers.eq
local nvim = helpers.nvim
describe('vim.diagnostic', function()
before_each(function()
clear()
exec_lua [[
require('vim.diagnostic')
function make_error(msg, x1, y1, x2, y2)
return {
lnum = x1,
col = y1,
end_lnum = x2,
end_col = y2,
message = msg,
severity = vim.diagnostic.severity.ERROR,
}
end
function make_warning(msg, x1, y1, x2, y2)
return {
lnum = x1,
col = y1,
end_lnum = x2,
end_col = y2,
message = msg,
severity = vim.diagnostic.severity.WARN,
}
end
function make_information(msg, x1, y1, x2, y2)
return {
lnum = x1,
col = y1,
end_lnum = x2,
end_col = y2,
message = msg,
severity = vim.diagnostic.severity.INFO,
}
end
function make_hint(msg, x1, y1, x2, y2)
return {
lnum = x1,
col = y1,
end_lnum = x2,
end_col = y2,
message = msg,
severity = vim.diagnostic.severity.HINT,
}
end
function count_diagnostics(bufnr, severity, namespace)
return #vim.diagnostic.get(bufnr, {severity = severity, namespace = namespace})
end
function count_extmarks(bufnr, namespace)
return #vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, {})
end
]]
exec_lua([[
diagnostic_ns = vim.api.nvim_create_namespace("diagnostic_spec")
other_ns = vim.api.nvim_create_namespace("other_namespace")
diagnostic_bufnr = vim.api.nvim_create_buf(true, false)
local lines = {"1st line of text", "2nd line of text", "wow", "cool", "more", "lines"}
vim.fn.bufload(diagnostic_bufnr)
vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, 1, false, lines)
return diagnostic_bufnr
]])
end)
after_each(function()
clear()
end)
it('creates highlight groups', function()
command('runtime plugin/diagnostic.vim')
eq({
'DiagnosticError',
'DiagnosticFloatingError',
'DiagnosticFloatingHint',
'DiagnosticFloatingInfo',
'DiagnosticFloatingWarn',
'DiagnosticHint',
'DiagnosticInfo',
'DiagnosticSignError',
'DiagnosticSignHint',
'DiagnosticSignInfo',
'DiagnosticSignWarn',
'DiagnosticUnderlineError',
'DiagnosticUnderlineHint',
'DiagnosticUnderlineInfo',
'DiagnosticUnderlineWarn',
'DiagnosticVirtualTextError',
'DiagnosticVirtualTextHint',
'DiagnosticVirtualTextInfo',
'DiagnosticVirtualTextWarn',
'DiagnosticWarn',
}, exec_lua([[return vim.fn.getcompletion('Diagnostic', 'highlight')]]))
end)
it('retrieves diagnostics from all buffers and namespaces', function()
local result = exec_lua [[
vim.diagnostic.set(diagnostic_ns, 1, {
make_error('Diagnostic #1', 1, 1, 1, 1),
make_error('Diagnostic #2', 2, 1, 2, 1),
})
vim.diagnostic.set(other_ns, 2, {
make_error('Diagnostic #3', 3, 1, 3, 1),
})
return vim.diagnostic.get()
]]
eq(3, #result)
eq(2, exec_lua([[return #vim.tbl_filter(function(d) return d.bufnr == 1 end, ...)]], result))
eq('Diagnostic #1', result[1].message)
end)
it('saves and count a single error', function()
eq(1, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
})
return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)
]])
end)
it('saves and count multiple errors', function()
eq(2, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
make_error('Diagnostic #2', 2, 1, 2, 1),
})
return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)
]])
end)
it('saves and count from multiple namespaces', function()
eq({1, 1, 2}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic From Server 1', 1, 1, 1, 1),
})
vim.diagnostic.set(other_ns, diagnostic_bufnr, {
make_error('Diagnostic From Server 2', 1, 1, 1, 1),
})
return {
-- First namespace
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
-- Second namespace
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, other_ns),
-- All namespaces
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR),
}
]])
end)
it('saves and count from multiple namespaces with respect to severity', function()
eq({3, 0, 3}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
make_error('Diagnostic From Server 1:2', 2, 2, 2, 2),
make_error('Diagnostic From Server 1:3', 2, 3, 3, 2),
})
vim.diagnostic.set(other_ns, diagnostic_bufnr, {
make_warning('Warning From Server 2', 3, 3, 3, 3),
})
return {
-- Namespace 1
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
-- Namespace 2
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, other_ns),
-- All namespaces
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR),
}
]])
end)
it('handles one namespace clearing highlights while the other still has highlights', function()
-- 1 Error (1)
-- 1 Warning (2)
-- 1 Warning (2) + 1 Warning (1)
-- 2 highlights and 2 underlines (since error)
-- 1 highlight + 1 underline
local all_highlights = {1, 1, 2, 4, 2}
eq(all_highlights, exec_lua [[
local ns_1_diags = {
make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 2, 1, 2, 5),
}
local ns_2_diags = {
make_warning("Warning 1", 2, 1, 2, 5),
}
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags)
return {
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN),
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
}
]])
-- Clear diagnostics from namespace 1, and make sure we have the right amount of stuff for namespace 2
eq({1, 1, 2, 0, 2}, exec_lua [[
vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns)
return {
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN),
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
}
]])
-- Show diagnostics from namespace 1 again
eq(all_highlights, exec_lua([[
vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns)
return {
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN),
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
}
]]))
end)
it('does not display diagnostics when disabled', function()
eq({0, 2}, exec_lua [[
local ns_1_diags = {
make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 2, 1, 2, 5),
}
local ns_2_diags = {
make_warning("Warning 1", 2, 1, 2, 5),
}
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags)
vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns)
return {
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
}
]])
eq({4, 0}, exec_lua [[
vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns)
vim.diagnostic.disable(diagnostic_bufnr, other_ns)
return {
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
}
]])
end)
describe('reset()', function()
it('diagnostic count is 0 and displayed diagnostics are 0 after call', function()
-- 1 Error (1)
-- 1 Warning (2)
-- 1 Warning (2) + 1 Warning (1)
-- 2 highlights and 2 underlines (since error)
-- 1 highlight + 1 underline
local all_highlights = {1, 1, 2, 4, 2}
eq(all_highlights, exec_lua [[
local ns_1_diags = {
make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 2, 1, 2, 5),
}
local ns_2_diags = {
make_warning("Warning 1", 2, 1, 2, 5),
}
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags)
vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags)
return {
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN),
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
}
]])
-- Reset diagnostics from namespace 1
exec_lua([[ vim.diagnostic.reset(diagnostic_ns) ]])
-- Make sure we have the right diagnostic count
eq({0, 1, 1, 0, 2} , exec_lua [[
local diagnostic_count = {}
vim.wait(100, function () diagnostic_count = {
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN),
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
} end )
return diagnostic_count
]])
-- Reset diagnostics from namespace 2
exec_lua([[ vim.diagnostic.reset(other_ns) ]])
-- Make sure we have the right diagnostic count
eq({0, 0, 0, 0, 0}, exec_lua [[
local diagnostic_count = {}
vim.wait(100, function () diagnostic_count = {
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns),
count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN),
count_extmarks(diagnostic_bufnr, diagnostic_ns),
count_extmarks(diagnostic_bufnr, other_ns),
} end )
return diagnostic_count
]])
end)
end)
describe('get_next_pos()', function()
it('can find the next pos with only one namespace', function()
eq({1, 1}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
return vim.diagnostic.get_next_pos()
]])
end)
it('can find next pos with two errors', function()
eq({4, 4}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_next_pos { namespace = diagnostic_ns }
]])
end)
it('can cycle when position is past error', function()
eq({1, 1}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_next_pos { namespace = diagnostic_ns }
]])
end)
it('will not cycle when wrap is off', function()
eq(false, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_next_pos { namespace = diagnostic_ns, wrap = false }
]])
end)
it('can cycle even from the last line', function()
eq({4, 4}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {vim.api.nvim_buf_line_count(0), 1})
return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns }
]])
end)
end)
describe('get_prev_pos()', function()
it('can find the prev pos with only one namespace', function()
eq({1, 1}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_prev_pos()
]])
end)
it('can find prev pos with two errors', function()
eq({1, 1}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #1', 1, 1, 1, 1),
make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns }
]])
end)
it('can cycle when position is past error', function()
eq({4, 4}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns }
]])
end)
it('respects wrap parameter', function()
eq(false, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic #2', 4, 4, 4, 4),
})
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.api.nvim_win_set_cursor(0, {3, 1})
return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns, wrap = false}
]])
end)
end)
describe('get()', function()
it('returns an empty table when no diagnostics are present', function()
eq({}, exec_lua [[return vim.diagnostic.get(diagnostic_bufnr, {namespace=diagnostic_ns})]])
end)
it('returns all diagnostics when no severity is supplied', function()
eq(2, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 1, 1, 2, 5),
})
return #vim.diagnostic.get(diagnostic_bufnr)
]])
end)
it('returns only requested diagnostics when severity is supplied', function()
eq({2, 3, 2}, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 1, 1, 2, 5),
make_information("Ignored information", 1, 1, 2, 5),
make_hint("Here's a hint", 1, 1, 2, 5),
})
return {
#vim.diagnostic.get(diagnostic_bufnr, { severity = {min=vim.diagnostic.severity.WARN} }),
#vim.diagnostic.get(diagnostic_bufnr, { severity = {max=vim.diagnostic.severity.WARN} }),
#vim.diagnostic.get(diagnostic_bufnr, {
severity = {
min=vim.diagnostic.severity.INFO,
max=vim.diagnostic.severity.WARN,
}
}),
}
]])
end)
it('allows filtering by line', function()
eq(1, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error("Error 1", 1, 1, 1, 5),
make_warning("Warning on Server 1", 1, 1, 2, 5),
make_information("Ignored information", 1, 1, 2, 5),
make_error("Error On Other Line", 2, 1, 1, 5),
})
return #vim.diagnostic.get(diagnostic_bufnr, {lnum = 2})
]])
end)
end)
describe('config()', function()
it('can use functions for config values', function()
exec_lua [[
vim.diagnostic.config({
virtual_text = function() return true end,
}, diagnostic_ns)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
]]
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
-- Now, don't enable virtual text.
-- We should have one less extmark displayed.
exec_lua [[
vim.diagnostic.config({
virtual_text = function() return false end,
}, diagnostic_ns)
]]
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
end)
it('allows filtering by severity', function()
local get_extmark_count_with_severity = function(min_severity)
return exec_lua([[
vim.diagnostic.config({
underline = false,
virtual_text = {
severity = {min=...},
},
})
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_warning('Delayed Diagnostic', 4, 4, 4, 4),
})
return count_extmarks(diagnostic_bufnr, diagnostic_ns)
]], min_severity)
end
-- No messages with Error or higher
eq(0, get_extmark_count_with_severity("ERROR"))
-- But now we don't filter it
eq(1, get_extmark_count_with_severity("WARN"))
eq(1, get_extmark_count_with_severity("HINT"))
end)
end)
describe('set()', function()
it('can perform updates after insert_leave', function()
exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]]
nvim("input", "o")
eq({mode='i', blocking=false}, nvim("get_mode"))
-- Save the diagnostics
exec_lua [[
vim.diagnostic.config({
update_in_insert = false,
})
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
]]
-- No diagnostics displayed yet.
eq({mode='i', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
nvim("input", "<esc>")
eq({mode='n', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
end)
it('does not perform updates when not needed', function()
exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]]
nvim("input", "o")
eq({mode='i', blocking=false}, nvim("get_mode"))
-- Save the diagnostics
exec_lua [[
vim.diagnostic.config({
update_in_insert = false,
virtual_text = true,
})
-- Count how many times we call display.
SetVirtualTextOriginal = vim.diagnostic._set_virtual_text
DisplayCount = 0
vim.diagnostic._set_virtual_text = function(...)
DisplayCount = DisplayCount + 1
return SetVirtualTextOriginal(...)
end
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
]]
-- No diagnostics displayed yet.
eq({mode='i', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
eq(0, exec_lua [[return DisplayCount]])
nvim("input", "<esc>")
eq({mode='n', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
eq(1, exec_lua [[return DisplayCount]])
-- Go in and out of insert mode one more time.
nvim("input", "o")
eq({mode='i', blocking=false}, nvim("get_mode"))
nvim("input", "<esc>")
eq({mode='n', blocking=false}, nvim("get_mode"))
-- Should not have set the virtual text again.
eq(1, exec_lua [[return DisplayCount]])
end)
it('never sets virtual text, in combination with insert leave', function()
exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]]
nvim("input", "o")
eq({mode='i', blocking=false}, nvim("get_mode"))
-- Save the diagnostics
exec_lua [[
vim.diagnostic.config({
update_in_insert = false,
virtual_text = false,
})
-- Count how many times we call display.
SetVirtualTextOriginal = vim.diagnostic._set_virtual_text
DisplayCount = 0
vim.diagnostic._set_virtual_text = function(...)
DisplayCount = DisplayCount + 1
return SetVirtualTextOriginal(...)
end
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
]]
-- No diagnostics displayed yet.
eq({mode='i', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
eq(0, exec_lua [[return DisplayCount]])
nvim("input", "<esc>")
eq({mode='n', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
eq(0, exec_lua [[return DisplayCount]])
-- Go in and out of insert mode one more time.
nvim("input", "o")
eq({mode='i', blocking=false}, nvim("get_mode"))
nvim("input", "<esc>")
eq({mode='n', blocking=false}, nvim("get_mode"))
-- Should not have set the virtual text still.
eq(0, exec_lua [[return DisplayCount]])
end)
it('can perform updates while in insert mode, if desired', function()
exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]]
nvim("input", "o")
eq({mode='i', blocking=false}, nvim("get_mode"))
-- Save the diagnostics
exec_lua [[
vim.diagnostic.config({
update_in_insert = true,
})
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Delayed Diagnostic', 4, 4, 4, 4),
})
]]
-- Diagnostics are displayed, because the user wanted them that way!
eq({mode='i', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
nvim("input", "<esc>")
eq({mode='n', blocking=false}, nvim("get_mode"))
eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]])
eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]])
end)
it('can set diagnostics without displaying them', function()
eq(0, exec_lua [[
vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
})
return count_extmarks(diagnostic_bufnr, diagnostic_ns)
]])
eq(2, exec_lua [[
vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns)
return count_extmarks(diagnostic_bufnr, diagnostic_ns)
]])
end)
it('can set display options', function()
eq(0, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
}, { virtual_text = false, underline = false })
return count_extmarks(diagnostic_bufnr, diagnostic_ns)
]])
eq(1, exec_lua [[
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic From Server 1:1', 1, 1, 1, 1),
}, { virtual_text = true, underline = false })
return count_extmarks(diagnostic_bufnr, diagnostic_ns)
]])
end)
end)
describe('show_line_diagnostics()', function()
it('creates floating window and returns popup bufnr and winnr if current line contains diagnostics', function()
-- Two lines:
-- Diagnostic:
-- 1. <msg>
eq(2, exec_lua [[
local diagnostics = {
make_error("Syntax error", 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics()
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
]])
end)
it('creates floating window and returns popup bufnr and winnr without header, if requested', function()
-- One line (since no header):
-- 1. <msg>
eq(1, exec_lua [[
local diagnostics = {
make_error("Syntax error", 0, 1, 0, 3),
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {show_header = false}
return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
]])
end)
end)
describe('set_signs()', function()
-- TODO(tjdevries): Find out why signs are not displayed when set from Lua...??
pending('sets signs by default', function()
exec_lua [[
vim.diagnostic.config({
update_in_insert = true,
signs = true,
})
local diagnostics = {
make_error('Delayed Diagnostic', 1, 1, 1, 2),
make_error('Delayed Diagnostic', 3, 3, 3, 3),
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
vim.diagnostic._set_signs(diagnostic_ns, diagnostic_bufnr, diagnostics)
-- return vim.fn.sign_getplaced()
]]
nvim("input", "o")
nvim("input", "<esc>")
-- TODO(tjdevries): Find a way to get the signs to display in the test...
eq(nil, exec_lua [[
return im.fn.sign_getplaced()[1].signs
]])
end)
end)
describe('setloclist()', function()
it('sets diagnostics in lnum order', function()
local loc_list = exec_lua [[
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Farther Diagnostic', 4, 4, 4, 4),
make_error('Lower Diagnostic', 1, 1, 1, 1),
})
vim.diagnostic.setloclist()
return vim.fn.getloclist(0)
]]
assert(loc_list[1].lnum < loc_list[2].lnum)
end)
it('sets diagnostics in lnum order, regardless of namespace', function()
local loc_list = exec_lua [[
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Lower Diagnostic', 1, 1, 1, 1),
})
vim.diagnostic.set(other_ns, diagnostic_bufnr, {
make_warning('Farther Diagnostic', 4, 4, 4, 4),
})
vim.diagnostic.setloclist()
return vim.fn.getloclist(0)
]]
assert(loc_list[1].lnum < loc_list[2].lnum)
end)
end)
end)

View File

@ -1,5 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
local command = helpers.command
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local eq = helpers.eq
@ -9,7 +10,10 @@ describe('vim.lsp.diagnostic', function()
local fake_uri
before_each(function()
clear()
clear {env={
NVIM_LUA_NOTRACK="1";
VIMRUNTIME=os.getenv"VIMRUNTIME";
}}
exec_lua [[
require('vim.lsp')
@ -44,7 +48,7 @@ describe('vim.lsp.diagnostic', function()
count_of_extmarks_for_client = function(bufnr, client_id)
return #vim.api.nvim_buf_get_extmarks(
bufnr, vim.lsp.diagnostic._get_diagnostic_namespace(client_id), 0, -1, {}
bufnr, vim.lsp.diagnostic.get_namespace(client_id), 0, -1, {}
)
end
]]
@ -86,39 +90,6 @@ describe('vim.lsp.diagnostic', function()
eq(2, #result[1])
eq('Diagnostic #1', result[1][1].message)
end)
it('Can convert diagnostic to quickfix items format', function()
local bufnr = exec_lua([[
local fake_uri = ...
return vim.uri_to_bufnr(fake_uri)
]], fake_uri)
local result = exec_lua([[
local bufnr = ...
vim.lsp.diagnostic.save(
{
make_error('Diagnostic #1', 1, 1, 1, 1),
make_error('Diagnostic #2', 2, 1, 2, 1),
}, bufnr, 1
)
return vim.lsp.util.diagnostics_to_items(vim.lsp.diagnostic.get_all())
]], bufnr)
local expected = {
{
bufnr = bufnr,
col = 2,
lnum = 2,
text = 'Diagnostic #1',
type = 'E'
},
{
bufnr = bufnr,
col = 2,
lnum = 3,
text = 'Diagnostic #2',
type = 'E'
},
}
eq(expected, result)
end)
it('should be able to save and count a single client error', function()
eq(1, exec_lua [[
vim.lsp.diagnostic.save(
@ -218,7 +189,7 @@ describe('vim.lsp.diagnostic', function()
-- Clear diagnostics from server 1, and make sure we have the right amount of stuff for client 2
eq({1, 1, 2, 0, 2}, exec_lua [[
vim.lsp.diagnostic.clear(diagnostic_bufnr, 1)
vim.lsp.diagnostic.disable(diagnostic_bufnr, 1)
return {
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1),
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2),
@ -230,7 +201,7 @@ describe('vim.lsp.diagnostic', function()
-- Show diagnostics from server 1 again
eq(all_highlights, exec_lua([[
vim.lsp.diagnostic.display(nil, diagnostic_bufnr, 1)
vim.lsp.diagnostic.enable(diagnostic_bufnr, 1)
return {
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1),
vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2),
@ -575,10 +546,10 @@ describe('vim.lsp.diagnostic', function()
})
-- Count how many times we call display.
SetVirtualTextOriginal = vim.lsp.diagnostic.set_virtual_text
SetVirtualTextOriginal = vim.diagnostic._set_virtual_text
DisplayCount = 0
vim.lsp.diagnostic.set_virtual_text = function(...)
vim.diagnostic._set_virtual_text = function(...)
DisplayCount = DisplayCount + 1
return SetVirtualTextOriginal(...)
end
@ -719,7 +690,7 @@ describe('vim.lsp.diagnostic', function()
return vim.api.nvim_buf_get_extmarks(
diagnostic_bufnr,
vim.lsp.diagnostic._get_diagnostic_namespace(1),
vim.lsp.diagnostic.get_namespace(1),
0,
-1,
{ details = true }
@ -756,7 +727,7 @@ describe('vim.lsp.diagnostic', function()
return vim.api.nvim_buf_get_extmarks(
diagnostic_bufnr,
vim.lsp.diagnostic._get_diagnostic_namespace(1),
vim.lsp.diagnostic.get_namespace(1),
0,
-1,
{ details = true }
@ -798,6 +769,40 @@ describe('vim.lsp.diagnostic', function()
eq(1, get_extmark_count_with_severity("Warning"))
eq(1, get_extmark_count_with_severity("Hint"))
end)
it('correctly handles UTF-16 offsets', function()
local line = "All 💼 and no 🎉 makes Jack a dull 👦"
local result = exec_lua([[
local line = ...
local client_id = vim.lsp.start_client {
cmd_env = {
NVIM_LUA_NOTRACK = "1";
};
cmd = {
vim.v.progpath, '-es', '-u', 'NONE', '--headless'
};
offset_encoding = "utf-16";
}
vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, -1, false, {line})
vim.lsp.diagnostic.on_publish_diagnostics(nil, {
uri = fake_uri,
diagnostics = {
make_error('UTF-16 Diagnostic', 0, 7, 0, 8),
}
}, {client_id=client_id}
)
local diags = vim.diagnostic.get(diagnostic_bufnr)
vim.lsp.stop_client(client_id)
vim.lsp._vim_exit_handler()
return diags
]], line)
eq(1, #result)
eq(exec_lua([[return vim.str_byteindex(..., 7, true)]], line), result[1].col)
eq(exec_lua([[return vim.str_byteindex(..., 8, true)]], line), result[1].end_col)
end)
end)
describe('lsp.util.show_line_diagnostics', function()
@ -940,4 +945,31 @@ describe('vim.lsp.diagnostic', function()
assert(loc_list[1].lnum < loc_list[2].lnum)
end)
end)
it('highlight groups', function()
command('runtime plugin/diagnostic.vim')
eq({
'LspDiagnosticsDefaultError',
'LspDiagnosticsDefaultHint',
'LspDiagnosticsDefaultInformation',
'LspDiagnosticsDefaultWarning',
'LspDiagnosticsFloatingError',
'LspDiagnosticsFloatingHint',
'LspDiagnosticsFloatingInformation',
'LspDiagnosticsFloatingWarning',
'LspDiagnosticsSignError',
'LspDiagnosticsSignHint',
'LspDiagnosticsSignInformation',
'LspDiagnosticsSignWarning',
'LspDiagnosticsUnderlineError',
'LspDiagnosticsUnderlineHint',
'LspDiagnosticsUnderlineInformation',
'LspDiagnosticsUnderlineWarning',
'LspDiagnosticsVirtualTextError',
'LspDiagnosticsVirtualTextHint',
'LspDiagnosticsVirtualTextInformation',
'LspDiagnosticsVirtualTextWarning',
}, exec_lua([[return vim.fn.getcompletion('Lsp', 'highlight')]]))
end)
end)

View File

@ -1017,31 +1017,6 @@ describe('LSP', function()
}
end
it('highlight groups', function()
eq({
'LspDiagnosticsDefaultError',
'LspDiagnosticsDefaultHint',
'LspDiagnosticsDefaultInformation',
'LspDiagnosticsDefaultWarning',
'LspDiagnosticsFloatingError',
'LspDiagnosticsFloatingHint',
'LspDiagnosticsFloatingInformation',
'LspDiagnosticsFloatingWarning',
'LspDiagnosticsSignError',
'LspDiagnosticsSignHint',
'LspDiagnosticsSignInformation',
'LspDiagnosticsSignWarning',
'LspDiagnosticsUnderlineError',
'LspDiagnosticsUnderlineHint',
'LspDiagnosticsUnderlineInformation',
'LspDiagnosticsUnderlineWarning',
'LspDiagnosticsVirtualTextError',
'LspDiagnosticsVirtualTextHint',
'LspDiagnosticsVirtualTextInformation',
'LspDiagnosticsVirtualTextWarning',
}, exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]]))
end)
describe('apply_text_edits', function()
before_each(function()
insert(dedent([[