aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Liszcz <liszcz.michal@gmail.com>2023-03-09 15:12:56 +0100
committerGitHub <noreply@github.com>2023-03-09 06:12:56 -0800
commit9ef7297ef142354ace8b1f3f277d0eee3cfdc6d4 (patch)
tree317cfc799191e044e1fc9452ef40ca24376fcbb1
parentce0fddf5ae334f0c79dcd95b379999e11df1486b (diff)
downloadrneovim-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.txt7
-rw-r--r--runtime/lua/vim/lsp.lua32
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua17
-rw-r--r--test/functional/plugin/lsp_spec.lua103
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)