diff options
author | Michal Liszcz <liszcz.michal@gmail.com> | 2023-03-09 15:12:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-09 06:12:56 -0800 |
commit | 9ef7297ef142354ace8b1f3f277d0eee3cfdc6d4 (patch) | |
tree | 317cfc799191e044e1fc9452ef40ca24376fcbb1 | |
parent | ce0fddf5ae334f0c79dcd95b379999e11df1486b (diff) | |
download | rneovim-9ef7297ef142354ace8b1f3f277d0eee3cfdc6d4.tar.gz rneovim-9ef7297ef142354ace8b1f3f277d0eee3cfdc6d4.tar.bz2 rneovim-9ef7297ef142354ace8b1f3f277d0eee3cfdc6d4.zip |
feat(lsp): overwrite omnifunc/tagfunc set by ftplugin #22267
Problem:
Some built-in ftplugins set omnifunc/tagfunc/formatexpr which causes
lsp.lua:set_defaults() to skip setup of defaults for those filetypes.
For example the C++ ftplugin has:
omnifunc=ccomplete#Complete
Last set from /usr/share/nvim/runtime/ftplugin/c.vim line 30
so the changes done in #95c65a6b221fe6e1cf91e8322e7d7571dc511a71
will always be skipped for C++ files.
Solution:
Overwrite omnifunc/tagfunc/formatexpr options that were set by stock
ftplugin.
Fixes #21001
-rw-r--r-- | runtime/doc/lsp.txt | 7 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 32 | ||||
-rw-r--r-- | test/functional/fixtures/fake-lsp-server.lua | 17 | ||||
-rw-r--r-- | test/functional/plugin/lsp_spec.lua | 103 |
4 files changed, 153 insertions, 6 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index b73e2958ef..b21ef9d7d3 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -51,8 +51,11 @@ Starting a LSP client will automatically report diagnostics via |vim.diagnostic|. Read |vim.diagnostic.config()| to learn how to customize the display. -It also sets some buffer options if the options are otherwise empty and if the -language server supports the functionality. +It also sets some buffer options if the language server supports the +functionality and if the options are otherwise empty or have the default +values set by Nvim runtime files (e.g. a ftplugin). In the latter case, +the default values are not restored when the LSP client is detached from +the buffer. - 'omnifunc' is set to |vim.lsp.omnifunc()|. This allows to trigger completion using |i_CTRL-X_CTRL-O| diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9a0b3f3100..cb77e9636f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1113,18 +1113,42 @@ function lsp.start_client(config) end ---@private + -- Determines whether the given option can be set by `set_defaults`. + local function is_empty_or_default(bufnr, option) + if vim.bo[bufnr][option] == '' then + return true + end + + local old_bufnr = vim.fn.bufnr('') + local last_set_from = vim.fn.gettext('\n\tLast set from ') + local line = vim.fn.gettext(' line ') + + vim.cmd.buffer(bufnr) + local scriptname = vim.fn + .execute('verbose set ' .. option .. '?') + :match(last_set_from .. '(.*)' .. line .. '%d+') + vim.cmd.buffer(old_bufnr) + + if not scriptname then + return false + end + local vimruntime = vim.fn.getenv('VIMRUNTIME') + return vim.startswith(vim.fn.expand(scriptname), vim.fn.expand(vimruntime)) + end + + ---@private local function set_defaults(client, bufnr) local capabilities = client.server_capabilities - if capabilities.definitionProvider and vim.bo[bufnr].tagfunc == '' then + if capabilities.definitionProvider and is_empty_or_default(bufnr, 'tagfunc') then vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc' end - if capabilities.completionProvider and vim.bo[bufnr].omnifunc == '' then + if capabilities.completionProvider and is_empty_or_default(bufnr, 'omnifunc') then vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc' end if capabilities.documentRangeFormattingProvider - and vim.bo[bufnr].formatprg == '' - and vim.bo[bufnr].formatexpr == '' + and is_empty_or_default(bufnr, 'formatprg') + and is_empty_or_default(bufnr, 'formatexpr') then vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()' end diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index dbb66a42e8..9cc2f6fa27 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -932,6 +932,23 @@ function tests.basic_formatting() } end +function tests.set_defaults_all_capabilities() + skeleton { + on_init = function(_) + return { + capabilities = { + definitionProvider = true, + completionProvider = true, + documentRangeFormattingProvider = true, + } + } + end; + body = function() + notify('test') + end; + } +end + -- Tests will be indexed by test_name local test_name = arg[1] local timeout = arg[2] diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 5eb367b0b8..56c53a6fed 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -31,6 +31,11 @@ local fake_lsp_code = lsp_helpers.fake_lsp_code local fake_lsp_logfile = lsp_helpers.fake_lsp_logfile local test_rpc_server = lsp_helpers.test_rpc_server +local function get_buf_option(name, bufnr) + bufnr = bufnr or "BUFFER" + return exec_lua(string.format("return vim.api.nvim_buf_get_option(%s, '%s')", bufnr, name)) +end + -- TODO(justinmk): hangs on Windows https://github.com/neovim/neovim/pull/11837 if skip(is_os('win')) then return end @@ -313,6 +318,104 @@ describe('LSP', function() } end) + it('should set default options on attach', function() + local client + test_rpc_server { + test_name = "set_defaults_all_capabilities"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + ]] + end; + on_init = function(_client) + client = _client + exec_lua("lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)") + end; + on_handler = function(_, _, ctx) + if ctx.method == 'test' then + eq('v:lua.vim.lsp.tagfunc', get_buf_option("tagfunc")) + eq('v:lua.vim.lsp.omnifunc', get_buf_option("omnifunc")) + eq('v:lua.vim.lsp.formatexpr()', get_buf_option("formatexpr")) + client.stop() + end + end; + on_exit = function(_, _) + eq('', get_buf_option("tagfunc")) + eq('', get_buf_option("omnifunc")) + eq('', get_buf_option("formatexpr")) + end; + } + end) + + it('should overwrite options set by ftplugins', function() + local client + test_rpc_server { + test_name = "set_defaults_all_capabilities"; + on_setup = function() + exec_lua [[ + vim.api.nvim_command('filetype plugin on') + BUFFER_1 = vim.api.nvim_create_buf(false, true) + BUFFER_2 = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_option(BUFFER_1, 'filetype', 'man') + vim.api.nvim_buf_set_option(BUFFER_2, 'filetype', 'xml') + ]] + eq('v:lua.require\'man\'.goto_tag', get_buf_option("tagfunc", "BUFFER_1")) + eq('xmlcomplete#CompleteTags', get_buf_option("omnifunc", "BUFFER_2")) + eq('xmlformat#Format()', get_buf_option("formatexpr", "BUFFER_2")) + end; + on_init = function(_client) + client = _client + exec_lua("lsp.buf_attach_client(BUFFER_1, TEST_RPC_CLIENT_ID)") + exec_lua("lsp.buf_attach_client(BUFFER_2, TEST_RPC_CLIENT_ID)") + end; + on_handler = function(_, _, ctx) + if ctx.method == 'test' then + eq('v:lua.vim.lsp.tagfunc', get_buf_option("tagfunc", "BUFFER_1")) + eq('v:lua.vim.lsp.omnifunc', get_buf_option("omnifunc", "BUFFER_2")) + eq('v:lua.vim.lsp.formatexpr()', get_buf_option("formatexpr", "BUFFER_2")) + client.stop() + end + end; + on_exit = function(_, _) + eq('', get_buf_option("tagfunc", "BUFFER_1")) + eq('', get_buf_option("omnifunc", "BUFFER_2")) + eq('', get_buf_option("formatexpr", "BUFFER_2")) + end; + } + end) + + it('should not overwrite user-defined options', function() + local client + test_rpc_server { + test_name = "set_defaults_all_capabilities"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_option(BUFFER, 'tagfunc', 'tfu') + vim.api.nvim_buf_set_option(BUFFER, 'omnifunc', 'ofu') + vim.api.nvim_buf_set_option(BUFFER, 'formatexpr', 'fex') + ]] + end; + on_init = function(_client) + client = _client + exec_lua("lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)") + end; + on_handler = function(_, _, ctx) + if ctx.method == 'test' then + eq('tfu', get_buf_option("tagfunc")) + eq('ofu', get_buf_option("omnifunc")) + eq('fex', get_buf_option("formatexpr")) + client.stop() + end + end; + on_exit = function(_, _) + eq('tfu', get_buf_option("tagfunc")) + eq('ofu', get_buf_option("omnifunc")) + eq('fex', get_buf_option("formatexpr")) + end; + } + end) + it('should detach buffer on bufwipe', function() clear() exec_lua(create_server_definition) |