diff options
| -rw-r--r-- | .github/ci/lint.sh (renamed from .github/ci/run_sanitizer.sh) | 12 | ||||
| -rw-r--r-- | .github/workflows/lint.yml | 2 | ||||
| -rw-r--r-- | CONTRIBUTING.md | 49 | ||||
| -rw-r--r-- | lsp/basedpyright.lua | 24 | ||||
| -rw-r--r-- | lsp/denols.lua | 28 | ||||
| -rw-r--r-- | lsp/pyright.lua | 26 | ||||
| -rw-r--r-- | lsp/texlab.lua | 10 | ||||
| -rw-r--r-- | lsp/zk.lua | 18 | ||||
| -rwxr-xr-x | scripts/docgen.lua | 3 |
9 files changed, 87 insertions, 85 deletions
diff --git a/.github/ci/run_sanitizer.sh b/.github/ci/lint.sh index 6f1577da..38ea57eb 100644 --- a/.github/ci/run_sanitizer.sh +++ b/.github/ci/lint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # USAGE: To run locally: -# bash .github/ci/run_sanitizer.sh origin/master HEAD +# bash .github/ci/lint.sh origin/master HEAD set -e @@ -27,6 +27,15 @@ _check_lsp_cmd_prefix() { fi } +# Enforce client:exec_cmd(). +_check_exec_cmd() { + if git grep -P 'workspace.executeCommand' -- 'lsp/*.lua' ; then + echo + echo 'Use client:exec_cmd() instead of calling request("workspace/executeCommand") directly. Example: lsp/pyright.lua' + exit 1 + fi +} + _check_deprecated_utils() { # checks for added lines that contain search pattern and prints them SEARCH_PATTERN='(path\.dirname|fn\.cwd)' @@ -50,4 +59,5 @@ _check_deprecated_utils() { _check_cmd_buflocal _check_lsp_cmd_prefix +_check_exec_cmd _check_deprecated_utils diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 346ad478..20b9b6ae 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - run: | - if ! bash .github/ci/run_sanitizer.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}; then + if ! bash .github/ci/lint.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}; then exit 1 fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 44c394f9..4c3e8e1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,5 @@ +# Contributing to nvim-lspconfig + ## Requirements - [Lint requirements](#lint) @@ -6,7 +8,8 @@ ## Scope of nvim-lspconfig -The point of lspconfig is to provide the minimal configuration necessary for a server to act in compliance with the language server protocol. In general, if a server requires custom client-side commands or off-spec handlers, then the server configuration should be added *without* those in lspconfig and receive a dedicated plugin such as nvim-jdtls, nvim-metals, etc. +The purpose of nvim-lspconfig is to provide configuration so that users can activate LSP with a single `vim.lsp.enable('foo')` call. +It must not provide its own "framework". Any "framework" or "util" code must be upstreamed to Nvim core. ## Pull requests (PRs) @@ -15,7 +18,9 @@ The point of lspconfig is to provide the minimal configuration necessary for a s - Use a **rebase workflow** for small PRs. - After addressing review comments, it's fine to rebase and force-push. -## Adding a server to lspconfig +## New config + +### Criteria New configs must meet these criteria (to avoid spam/quasi-marketing/vanity projects): @@ -25,28 +30,37 @@ New configs must meet these criteria (to avoid spam/quasi-marketing/vanity proje This helps ensure that we only include actively maintained and widely used servers to provide a better experience for the community. -To add a new language server, start with a minimal skeleton. See `:help lspconfig-new` and other configurations in `lsp/`. +### Walkthrough + +To add a new config, copy an existing config from `lsp/`. Start with `lsp/lua_ls.lua` for a simple a config, or `lsp/jdtls.lua` or `lsp/pyright.lua` for more complex examples. When choosing a config name, convert dashes (`-`) to underscores (`_`). If the name of the server is a unique name (`pyright`, `clangd`) or a commonly used abbreviation (`zls`), prefer this as the server name. If the server instead follows the pattern x-language-server, prefer the convention `x_ls` (`jsonnet_ls`). -`default_config` should include: +The minimal config properties are: -* `cmd`: a list which includes the executable name as the first entry, with arguments constituting subsequent list elements (`--stdio` is common). +* `cmd`: command defined as a string list, where the first item is an executable and following items are its arguments (`--stdio` is common). ```lua cmd = { 'typescript-language-server', '--stdio' } ``` * `filetypes`: list of filetypes that should activate this config. -* `root_markers`: a list of files that mark the root of the project. - * See `:help lspconfig-new`. +* `root_markers`: a list of files that mark the root of the project/workspace. + * See `:help lsp-config`. * See `vim.fs.root()` -An example for adding a new config `lsp/pyright.lua` is shown below: +### Commands -```lua -local function organize_imports() - -- executes lsp command. See `lsp/pyright.lua` for the full example. -end +LSP servers may provide custom `workspace/executeCommand` commands. Because LSP does not provide any way for clients to programmatically discover/list these commands, configs may define Nvim commands which invoke the `workspace/executeCommand` commands. To keep things maintainable and discoverable, configs must follow these guidelines: +- Commands must be buffer-local. +- Commands must be prefixed with `:Lsp`. This is a crude way to improve "discoverability". +- Do NOT create commands that merely alias existing *code-actions* or *code-lenses*, which are *already* auto-discoverable via the ["gra" keymap](https://neovim.io/doc/user/lsp.html#gra) (or `vim.lsp.buf.code_action()`) +- Use `client:exec_cmd()` (instead of `request(..., 'workspace/executeCommand')`) + +### Example + +Following is an example new config for `lsp/pyright.lua`: + +```lua ---@brief --- --- https://github.com/microsoft/pyright @@ -72,8 +86,15 @@ return { }, }, }, - on_attach = function() - vim.api.nvim_buf_create_user_command(0, 'LspPyrightOrganizeImports', organize_imports, {}) + on_attach = function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + client:exec_cmd({ + command = 'pyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { + desc = 'Organize Imports', + }) end, } ``` diff --git a/lsp/basedpyright.lua b/lsp/basedpyright.lua index f78bbd21..8872ea14 100644 --- a/lsp/basedpyright.lua +++ b/lsp/basedpyright.lua @@ -4,21 +4,6 @@ --- --- `basedpyright`, a static type checker and language server for python -local function organize_imports() - local params = { - command = 'basedpyright.organizeimports', - arguments = { vim.uri_from_bufnr(0) }, - } - - local clients = vim.lsp.get_clients { - bufnr = vim.api.nvim_get_current_buf(), - name = 'basedpyright', - } - for _, client in ipairs(clients) do - client.request('workspace/executeCommand', params, nil, 0) - end -end - local function set_python_path(path) local clients = vim.lsp.get_clients { bufnr = vim.api.nvim_get_current_buf(), @@ -55,8 +40,13 @@ return { }, }, }, - on_attach = function() - vim.api.nvim_buf_create_user_command(0, 'LspPyrightOrganizeImports', organize_imports, { + on_attach = function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + client:exec_cmd({ + command = 'basedpyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { desc = 'Organize Imports', }) diff --git a/lsp/denols.lua b/lsp/denols.lua index 80ff679e..b7a6466a 100644 --- a/lsp/denols.lua +++ b/lsp/denols.lua @@ -15,19 +15,6 @@ local lsp = vim.lsp -local function buf_cache(bufnr, client) - local params = { - command = 'deno.cache', - arguments = { {}, vim.uri_from_bufnr(bufnr) }, - } - client.request('workspace/executeCommand', params, function(err, _result, ctx) - if err then - local uri = ctx.params.arguments[2] - vim.api.nvim_err_writeln('cache command failed for ' .. vim.uri_to_fname(uri)) - end - end, bufnr) -end - local function virtual_text_document_handler(uri, res, client) if not res then return nil @@ -105,12 +92,17 @@ return { ['textDocument/typeDefinition'] = denols_handler, ['textDocument/references'] = denols_handler, }, - on_attach = function() + on_attach = function(client, bufnr) vim.api.nvim_buf_create_user_command(0, 'LspDenolsCache', function() - local clients = vim.lsp.get_clients { bufnr = 0, name = 'denols' } - if #clients > 0 then - buf_cache(0, clients[#clients]) - end + client:exec_cmd({ + command = 'deno.cache', + arguments = { {}, vim.uri_from_bufnr(bufnr) }, + }, { bufnr = bufnr }, function(err, _result, ctx) + if err then + local uri = ctx.params.arguments[2] + vim.api.nvim_err_writeln('cache command failed for ' .. vim.uri_to_fname(uri)) + end + end) end, { desc = 'Cache a module and all of its dependencies.', }) diff --git a/lsp/pyright.lua b/lsp/pyright.lua index 161b8e0e..c44fecfa 100644 --- a/lsp/pyright.lua +++ b/lsp/pyright.lua @@ -4,21 +4,6 @@ --- --- `pyright`, a static type checker and language server for python -local function organize_imports() - local params = { - command = 'pyright.organizeimports', - arguments = { vim.uri_from_bufnr(0) }, - } - - local clients = vim.lsp.get_clients { - bufnr = vim.api.nvim_get_current_buf(), - name = 'pyright', - } - for _, client in ipairs(clients) do - client.request('workspace/executeCommand', params, nil, 0) - end -end - local function set_python_path(path) local clients = vim.lsp.get_clients { bufnr = vim.api.nvim_get_current_buf(), @@ -55,11 +40,16 @@ return { }, }, }, - on_attach = function() - vim.api.nvim_buf_create_user_command(0, 'LspPyrightOrganizeImports', organize_imports, { + on_attach = function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightOrganizeImports', function() + client:exec_cmd({ + command = 'pyright.organizeimports', + arguments = { vim.uri_from_bufnr(bufnr) }, + }) + end, { desc = 'Organize Imports', }) - vim.api.nvim_buf_create_user_command(0, 'LspPyrightSetPythonPath', set_python_path, { + vim.api.nvim_buf_create_user_command(bufnr, 'LspPyrightSetPythonPath', set_python_path, { desc = 'Reconfigure pyright with the provided python path', nargs = 1, complete = 'file', diff --git a/lsp/texlab.lua b/lsp/texlab.lua index 8123d5b7..5636b031 100644 --- a/lsp/texlab.lua +++ b/lsp/texlab.lua @@ -63,12 +63,12 @@ local function buf_cancel_build(client, bufnr) end local function dependency_graph(client) - client.request('workspace/executeCommand', { command = 'texlab.showDependencyGraph' }, function(err, result) + client:exec_cmd({ command = 'texlab.showDependencyGraph' }, { bufnr = 0 }, function(err, result) if err then return vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) end vim.notify('The dependency graph has been generated:\n' .. result, vim.log.levels.INFO) - end, 0) + end) end local function command_factory(cmd) @@ -102,10 +102,10 @@ end local function buf_find_envs(client, bufnr) local win = vim.api.nvim_get_current_win() - client.request('workspace/executeCommand', { + client:exec_cmd({ command = 'texlab.findEnvironments', arguments = { vim.lsp.util.make_position_params(win, client.offset_encoding) }, - }, function(err, result) + }, { bufnr = bufnr }, function(err, result) if err then return vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) end @@ -126,7 +126,7 @@ local function buf_find_envs(client, bufnr) border = 'single', title = 'Environments', }) - end, bufnr) + end) end local function buf_change_env(client, bufnr) @@ -16,11 +16,11 @@ return { cmd = { 'zk', 'lsp' }, filetypes = { 'markdown' }, root_markers = { '.zk' }, - on_attach = function() - vim.api.nvim_buf_create_user_command(0, 'LspZkIndex', function() + on_attach = function(client, bufnr) + vim.api.nvim_buf_create_user_command(bufnr, 'LspZkIndex', function() vim.lsp.buf.execute_command { command = 'zk.index', - arguments = { vim.api.nvim_buf_get_name(0) }, + arguments = { vim.api.nvim_buf_get_name(bufnr) }, } end, { desc = 'ZkIndex', @@ -30,10 +30,10 @@ return { local bufpath = vim.api.nvim_buf_get_name(0) local root = find_zk_root(bufpath) - vim.lsp.buf_request(0, 'workspace/executeCommand', { + client:exec_cmd({ command = 'zk.list', arguments = { root, { select = { 'path' } } }, - }, function(_, result, _, _) + }, { bufnr = bufnr }, function(_err, result) if not result then return end @@ -48,14 +48,14 @@ return { desc = 'ZkList', }) - vim.api.nvim_buf_create_user_command(0, 'LspZkNew', function(...) - vim.lsp.buf_request(0, 'workspace/executeCommand', { + vim.api.nvim_buf_create_user_command(bufnr, 'LspZkNew', function(...) + client:exec_cmd({ command = 'zk.new', arguments = { - vim.api.nvim_buf_get_name(0), + vim.api.nvim_buf_get_name(bufnr), ..., }, - }, function(_, result, _, _) + }, { bufnr = bufnr }, function(_err, result) if not (result and result.path) then return end diff --git a/scripts/docgen.lua b/scripts/docgen.lua index 956541ca..8695e209 100755 --- a/scripts/docgen.lua +++ b/scripts/docgen.lua @@ -3,7 +3,6 @@ local root = vim.trim(vim.system({ 'git', 'rev-parse', '--show-toplevel' }):wait vim.opt.rtp:append(root) local util = require 'lspconfig.util' -local inspect = vim.inspect local function template(s, params) return (s:gsub('{{([^{}]+)}}', params)) @@ -147,7 +146,7 @@ local function make_lsp_sections(is_markdown) if type(v) == 'boolean' then return ('- `%s` : `%s`'):format(k, v) elseif type(v) ~= 'function' and k ~= 'root_dir' then - return ('- `%s` :\n ```lua\n%s\n ```'):format(k, indent(2, inspect(v))) + return ('- `%s` :\n ```lua\n%s\n ```'):format(k, indent(2, vim.inspect(v))) end local file = assert(io.open(config_file, 'r')) |
