mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	fix(lsp): don't register didChangeWatchedFiles when capability not set (#23689)
Some LSP servers (tailwindcss, rome) are known to request registration for `workspace/didChangeWatchedFiles` even when the corresponding client capability does not advertise support. This change adds an extra check in the `client/registerCapability` handler not to start a watch unless the client capability is set appropriately.
This commit is contained in:
		@@ -193,7 +193,12 @@ local to_lsp_change_type = {
 | 
			
		||||
function M.register(reg, ctx)
 | 
			
		||||
  local client_id = ctx.client_id
 | 
			
		||||
  local client = vim.lsp.get_client_by_id(client_id)
 | 
			
		||||
  if not client.workspace_folders then
 | 
			
		||||
  if
 | 
			
		||||
    -- Ill-behaved servers may not honor the client capability and try to register
 | 
			
		||||
    -- anyway, so ignore requests when the user has opted out of the feature.
 | 
			
		||||
    not client.config.capabilities.workspace.didChangeWatchedFiles.dynamicRegistration
 | 
			
		||||
    or not client.workspace_folders
 | 
			
		||||
  then
 | 
			
		||||
    return
 | 
			
		||||
  end
 | 
			
		||||
  local watch_regs = {}
 | 
			
		||||
 
 | 
			
		||||
@@ -3778,6 +3778,13 @@ describe('LSP', function()
 | 
			
		||||
          name = 'watchfiles-test',
 | 
			
		||||
          cmd = server.cmd,
 | 
			
		||||
          root_dir = root_dir,
 | 
			
		||||
          capabilities = {
 | 
			
		||||
            workspace = {
 | 
			
		||||
              didChangeWatchedFiles = {
 | 
			
		||||
                dynamicRegistration = true,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        local expected_messages = 2 -- initialize, initialized
 | 
			
		||||
@@ -3865,6 +3872,13 @@ describe('LSP', function()
 | 
			
		||||
          name = 'watchfiles-test',
 | 
			
		||||
          cmd = server.cmd,
 | 
			
		||||
          root_dir = root_dir,
 | 
			
		||||
          capabilities = {
 | 
			
		||||
            workspace = {
 | 
			
		||||
              didChangeWatchedFiles = {
 | 
			
		||||
                dynamicRegistration = true,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        local expected_messages = 2 -- initialize, initialized
 | 
			
		||||
@@ -3982,6 +3996,13 @@ describe('LSP', function()
 | 
			
		||||
          name = 'watchfiles-test',
 | 
			
		||||
          cmd = server.cmd,
 | 
			
		||||
          root_dir = root_dir,
 | 
			
		||||
          capabilities = {
 | 
			
		||||
            workspace = {
 | 
			
		||||
              didChangeWatchedFiles = {
 | 
			
		||||
                dynamicRegistration = true,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        local expected_messages = 2 -- initialize, initialized
 | 
			
		||||
@@ -4116,6 +4137,13 @@ describe('LSP', function()
 | 
			
		||||
          name = 'watchfiles-test',
 | 
			
		||||
          cmd = server.cmd,
 | 
			
		||||
          root_dir = root_dir,
 | 
			
		||||
          capabilities = {
 | 
			
		||||
            workspace = {
 | 
			
		||||
              didChangeWatchedFiles = {
 | 
			
		||||
                dynamicRegistration = true,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        local expected_messages = 2 -- initialize, initialized
 | 
			
		||||
@@ -4186,5 +4214,61 @@ describe('LSP', function()
 | 
			
		||||
        },
 | 
			
		||||
      }, result[3].params)
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it("ignores registrations by servers when the client doesn't advertise support", function()
 | 
			
		||||
      exec_lua(create_server_definition)
 | 
			
		||||
      local result = exec_lua([[
 | 
			
		||||
        local server = _create_server()
 | 
			
		||||
        local client_id = vim.lsp.start({
 | 
			
		||||
          name = 'watchfiles-test',
 | 
			
		||||
          cmd = server.cmd,
 | 
			
		||||
          root_dir = 'some_dir',
 | 
			
		||||
          capabilities = {
 | 
			
		||||
            workspace = {
 | 
			
		||||
              didChangeWatchedFiles = {
 | 
			
		||||
                dynamicRegistration = false,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        local watching = false
 | 
			
		||||
        require('vim.lsp._watchfiles')._watchfunc = function(_, _, callback)
 | 
			
		||||
          -- Since the registration is ignored, this should not execute and `watching` should stay false
 | 
			
		||||
          watching = true
 | 
			
		||||
          return function() end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        vim.lsp.handlers['client/registerCapability'](nil, {
 | 
			
		||||
          registrations = {
 | 
			
		||||
            {
 | 
			
		||||
              id = 'watchfiles-test-kind',
 | 
			
		||||
              method = 'workspace/didChangeWatchedFiles',
 | 
			
		||||
              registerOptions = {
 | 
			
		||||
                watchers = {
 | 
			
		||||
                  {
 | 
			
		||||
                    globPattern = '**/*',
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        }, { client_id = client_id })
 | 
			
		||||
 | 
			
		||||
        -- Ensure no errors occur when unregistering something that was never really registered.
 | 
			
		||||
        vim.lsp.handlers['client/unregisterCapability'](nil, {
 | 
			
		||||
          unregisterations = {
 | 
			
		||||
            {
 | 
			
		||||
              id = 'watchfiles-test-kind',
 | 
			
		||||
              method = 'workspace/didChangeWatchedFiles',
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        }, { client_id = client_id })
 | 
			
		||||
 | 
			
		||||
        return watching
 | 
			
		||||
      ]])
 | 
			
		||||
 | 
			
		||||
      eq(false, result)
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user