Skip to content

Commit

Permalink
lsp: vim.lsp.diagnostic (#12655)
Browse files Browse the repository at this point in the history
Breaking Changes:
- Deprecated all `vim.lsp.util.{*diagnostics*}()` functions.
    - Instead, all functions must be found in vim.lsp.diagnostic
    - For now, they issue a warning ONCE per neovim session. In a
      "little while" we will remove them completely.
- `vim.lsp.callbacks` has moved to `vim.lsp.handlers`.
    - For a "little while" we will just redirect `vim.lsp.callbacks` to
      `vim.lsp.handlers`. However, we will remove this at some point, so
      it is recommended that you change all of your references to
      `callbacks` into `handlers`.
    - This also means that for functions like |vim.lsp.start_client()|
      and similar, keyword style arguments have moved from "callbacks"
      to "handlers". Once again, these are currently being forward, but
      will cease to be forwarded in a "little while".
- Changed the highlight groups for LspDiagnostic highlight as they were
  inconsistently named.
    - For more information, see |lsp-highlight-diagnostics|
- Changed the sign group names as well, to be consistent with
  |lsp-highlight-diagnostics|

General Enhancements:
- Rewrote much of the getting started help document for lsp. It also
  provides a much nicer configuration strategy, so as to not recommend
  globally overwriting builtin neovim mappings.

LSP Enhancements:
- Introduced the concept of |lsp-handlers| which will allow much better
  customization for users without having to copy & paste entire files /
  functions / etc.

Diagnostic Enhancements:
- "goto next diagnostic" |vim.lsp.diagnostic.goto_next()|
- "goto prev diagnostic" |vim.lsp.diagnostic.goto_prev()|
    - For each of the gotos, auto open diagnostics is available as a
      configuration option
- Configurable diagnostic handling:
    - See |vim.lsp.diagnostic.on_publish_diagnostics()|
    - Delay display until after insert mode
    - Configure signs
    - Configure virtual text
    - Configure underline
- Set the location list with the buffers diagnostics.
    - See |vim.lsp.diagnostic.set_loclist()|
- Better performance for getting counts and line diagnostics
    - They are now cached on save, to enhance lookups.
    - Particularly useful for checking in statusline, etc.
- Actual testing :)
    - See ./test/functional/plugin/lsp/diagnostic_spec.lua
- Added `guisp` for underline highlighting

NOTE: "a little while" means enough time to feel like most plugins and
plugin authors have had a chance to refactor their code to use the
updated calls. Then we will remove them completely. There is no need to
keep them, because we don't have any released version of neovim that
exposes these APIs. I'm trying to be nice to people following HEAD :)

Co-authored: [Twitch Chat 2020](https://twitch.tv/teej_dv)
  • Loading branch information
tjdevries committed Nov 13, 2020
1 parent 4ae31c4 commit f75be5e
Show file tree
Hide file tree
Showing 22 changed files with 3,744 additions and 1,359 deletions.
53 changes: 42 additions & 11 deletions runtime/doc/api.txt
Expand Up @@ -475,6 +475,9 @@ created for extmark changes.
==============================================================================
Global Functions *api-global*

nvim__get_hl_defs({ns_id}) *nvim__get_hl_defs()*
TODO: Documentation

nvim__get_lib_dir() *nvim__get_lib_dir()*
TODO: Documentation

Expand Down Expand Up @@ -952,6 +955,9 @@ nvim_get_runtime_file({name}, {all}) *nvim_get_runtime_file()*
It is not an error to not find any files. An empty array is
returned then.

Attributes: ~
{fast}

Parameters: ~
{name} pattern of files to search for
{all} whether to return all matches or only the first
Expand Down Expand Up @@ -987,6 +993,7 @@ nvim_input({keys}) *nvim_input()*
Note:
|keycodes| like <CR> are translated, so "<" is special. To
input a literal "<", send <LT>.

Note:
For mouse events use |nvim_input_mouse()|. The pseudokey
form "<LeftMouse><col,row>" is deprecated since
Expand Down Expand Up @@ -1378,8 +1385,7 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
{opts} Optional parameters. Reserved for future use.

*nvim_set_client_info()*
nvim_set_client_info({name}, {version}, {type}, {methods},
{attributes})
nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
Self-identifies the client.

The client/plugin/application should call this after
Expand Down Expand Up @@ -1491,7 +1497,7 @@ nvim_set_decoration_provider({ns_id}, {opts})
disable the provider until the next redraw. Similarily, return
`false` in `on_win` will skip the `on_lines` calls for that
window (but any extmarks set in `on_win` will still be used).
A plugin managing multiple sources of decorations should
A plugin managing multiple sources of decoration should
ideally only set one provider, and merge the sources
internally. You can use multiple `ns_id` for the extmarks
set/modified inside the callback anyway.
Expand Down Expand Up @@ -1519,6 +1525,33 @@ nvim_set_decoration_provider({ns_id}, {opts})
• on_end: called at the end of a redraw cycle
["end", tick]

nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
Set a highlight group.

TODO: ns_id = 0, should modify :highlight namespace TODO val
should take update vs reset flag

Parameters: ~
{ns_id} number of namespace for this highlight
{name} highlight group name, like ErrorMsg
{val} highlight definiton map, like
|nvim_get_hl_by_name|.

nvim_set_hl_ns({ns_id}) *nvim_set_hl_ns()*
Set active namespace for highlights.

NB: this function can be called from async contexts, but the
semantics are not yet well-defined. To start with
|nvim_set_decoration_provider| on_win and on_line callbacks
are explicitly allowed to change the namespace during a redraw
cycle.

Attributes: ~
{fast}

Parameters: ~
{ns_id} the namespace to activate

nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()*
Sets a global |mapping| for the given mode.

Expand Down Expand Up @@ -1618,8 +1651,8 @@ nvim__buf_stats({buffer}) *nvim__buf_stats()*
TODO: Documentation

*nvim_buf_add_highlight()*
nvim_buf_add_highlight({buffer}, {src_id}, {hl_group}, {line},
{col_start}, {col_end})
nvim_buf_add_highlight({buffer}, {src_id}, {hl_group}, {line}, {col_start},
{col_end})
Adds a highlight to buffer.

Useful for plugins that dynamically generate highlights to a
Expand Down Expand Up @@ -2067,8 +2100,7 @@ nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {opts})
|nvim_set_keymap()|

*nvim_buf_set_lines()*
nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing},
{replacement})
nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement})
Sets (replaces) a line-range in the buffer.

Indexing is zero-based, end-exclusive. Negative indices are
Expand Down Expand Up @@ -2116,8 +2148,7 @@ nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
{value} Variable value

*nvim_buf_set_virtual_text()*
nvim_buf_set_virtual_text({buffer}, {src_id}, {line}, {chunks},
{opts})
nvim_buf_set_virtual_text({buffer}, {src_id}, {line}, {chunks}, {opts})
Set the virtual text (annotation) for a buffer line.

By default (and currently the only option) the text will be
Expand Down Expand Up @@ -2449,8 +2480,8 @@ nvim_ui_pum_set_bounds({width}, {height}, {row}, {col})
Note that this method is not to be confused with
|nvim_ui_pum_set_height()|, which sets the number of visible
items in the popup menu, while this function sets the bounding
box of the popup menu, including visual decorations such as
boarders and sliders. Floats need not use the same font size,
box of the popup menu, including visual elements such as
borders and sliders. Floats need not use the same font size,
nor be anchored to exact grid corners, so one can set
floating-point numbers to the popup menu geometry.

Expand Down
129 changes: 129 additions & 0 deletions runtime/doc/lsp-extension.txt
@@ -0,0 +1,129 @@
*lsp-extension.txt* LSP Extension

NVIM REFERENCE MANUAL


The `vim.lsp` Lua module is a framework for building LSP plugins.

1. Start with |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|.
2. Peek at the API: >
:lua print(vim.inspect(vim.lsp))
< 3. See |lsp-extension-example| for a full example.

================================================================================
LSP EXAMPLE *lsp-extension-example*

This example is for plugin authors or users who want a lot of control. If you
are just getting started see |lsp-quickstart|.

For more advanced configurations where just filtering by filetype isn't
sufficient, you can use the `vim.lsp.start_client()` and
`vim.lsp.buf_attach_client()` commands to easily customize the configuration
however you please. For example, if you want to do your own filtering, or
start a new LSP client based on the root directory for working with multiple
projects in a single session. To illustrate, the following is a fully working
Lua example.

The example will:
1. Check for each new buffer whether or not we want to start an LSP client.
2. Try to find a root directory by ascending from the buffer's path.
3. Create a new LSP for that root directory if one doesn't exist.
4. Attach the buffer to the client for that root directory.

>
-- Some path manipulation utilities
local function is_dir(filename)
local stat = vim.loop.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
-- Assumes filepath is a file.
local function dirname(filepath)
local is_changed = false
local result = filepath:gsub(path_sep.."([^"..path_sep.."]+)$", function()
is_changed = true
return ""
end)
return result, is_changed
end
local function path_join(...)
return table.concat(vim.tbl_flatten {...}, path_sep)
end
-- Ascend the buffer's path until we find the rootdir.
-- is_root_path is a function which returns bool
local function buffer_find_root_dir(bufnr, is_root_path)
local bufname = vim.api.nvim_buf_get_name(bufnr)
if vim.fn.filereadable(bufname) == 0 then
return nil
end
local dir = bufname
-- Just in case our algo is buggy, don't infinite loop.
for _ = 1, 100 do
local did_change
dir, did_change = dirname(dir)
if is_root_path(dir, bufname) then
return dir, bufname
end
-- If we can't ascend further, then stop looking.
if not did_change then
return nil
end
end
end
-- A table to store our root_dir to client_id lookup. We want one LSP per
-- root directory, and this is how we assert that.
local javascript_lsps = {}
-- Which filetypes we want to consider.
local javascript_filetypes = {
["javascript.jsx"] = true;
["javascript"] = true;
["typescript"] = true;
["typescript.jsx"] = true;
}
-- Create a template configuration for a server to start, minus the root_dir
-- which we will specify later.
local javascript_lsp_config = {
name = "javascript";
cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") };
}
-- This needs to be global so that we can call it from the autocmd.
function check_start_javascript_lsp()
local bufnr = vim.api.nvim_get_current_buf()
-- Filter which files we are considering.
if not javascript_filetypes[vim.api.nvim_buf_get_option(bufnr, 'filetype')] then
return
end
-- Try to find our root directory. We will define this as a directory which contains
-- node_modules. Another choice would be to check for `package.json`, or for `.git`.
local root_dir = buffer_find_root_dir(bufnr, function(dir)
return is_dir(path_join(dir, 'node_modules'))
-- return vim.fn.filereadable(path_join(dir, 'package.json')) == 1
-- return is_dir(path_join(dir, '.git'))
end)
-- We couldn't find a root directory, so ignore this file.
if not root_dir then return end
-- Check if we have a client already or start and store it.
local client_id = javascript_lsps[root_dir]
if not client_id then
local new_config = vim.tbl_extend("error", javascript_lsp_config, {
root_dir = root_dir;
})
client_id = vim.lsp.start_client(new_config)
javascript_lsps[root_dir] = client_id
end
-- Finally, attach to the buffer to track changes. This will do nothing if we
-- are already attached.
vim.lsp.buf_attach_client(bufnr, client_id)
end
vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]]
<

vim:tw=78:ts=8:ft=help:norl:

0 comments on commit f75be5e

Please sign in to comment.