diff options
-rw-r--r-- | runtime/doc/lsp.txt | 30 | ||||
-rw-r--r-- | runtime/doc/news.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/rpc.lua | 95 | ||||
-rw-r--r-- | test/functional/plugin/lsp_spec.lua | 4 |
4 files changed, 41 insertions, 90 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 70b8878837..0ca5240da0 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -2229,32 +2229,20 @@ Lua module: vim.lsp.rpc *lsp-rpc* • {terminate} (`fun()`) -connect({host}, {port}) *vim.lsp.rpc.connect()* - Create a LSP RPC client factory that connects via TCP to the given host - and port. +connect({host_or_path}, {port}) *vim.lsp.rpc.connect()* + Create a LSP RPC client factory that connects to either: + • a named pipe (windows) + • a domain socket (unix) + • a host and port via TCP Return a function that can be passed to the `cmd` field for |vim.lsp.start_client()| or |vim.lsp.start()|. Parameters: ~ - • {host} (`string`) host to connect to - • {port} (`integer`) port to connect to - - Return: ~ - (`fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) - - *vim.lsp.rpc.domain_socket_connect()* -domain_socket_connect({pipe_path}) - Create a LSP RPC client factory that connects via named pipes (Windows) or - unix domain sockets (Unix) to the given pipe_path (file path on Unix and - name on Windows). - - Return a function that can be passed to the `cmd` field for - |vim.lsp.start_client()| or |vim.lsp.start()|. - - Parameters: ~ - • {pipe_path} (`string`) file path of the domain socket (Unix) or name - of the named pipe (Windows) to connect to + • {host_or_path} (`string`) host to connect to or path to a pipe/domain + socket + • {port} (`integer?`) TCP port to connect to. If absent the + first argument must be a pipe Return: ~ (`fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 966d50c382..8d51d9d849 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -225,7 +225,7 @@ The following new APIs and features were added. • |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to the original LSP `Location` or `LocationLink`. • Added support for connecting to servers using named pipes (Windows) or - unix domain sockets (Unix) via |vim.lsp.rpc.domain_socket_connect()|. + unix domain sockets (Unix) via |vim.lsp.rpc.connect()|. • Added support for `completionList.itemDefaults`, reducing overhead when computing completion items where properties often share the same value (e.g. `commitCharacters`). Note that this might affect plugins and diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 984e4f040a..6748b32ec0 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -621,95 +621,53 @@ local function merge_dispatchers(dispatchers) return merged end ---- Create a LSP RPC client factory that connects via TCP to the given host and port. +--- Create a LSP RPC client factory that connects to either: +--- +--- - a named pipe (windows) +--- - a domain socket (unix) +--- - a host and port via TCP --- --- Return a function that can be passed to the `cmd` field for --- |vim.lsp.start_client()| or |vim.lsp.start()|. --- ----@param host string host to connect to ----@param port integer port to connect to +---@param host_or_path string host to connect to or path to a pipe/domain socket +---@param port integer? TCP port to connect to. If absent the first argument must be a pipe ---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient -function M.connect(host, port) +function M.connect(host_or_path, port) return function(dispatchers) dispatchers = merge_dispatchers(dispatchers) - local tcp = assert(uv.new_tcp()) + local handle = ( + port == nil + and assert( + uv.new_pipe(false), + string.format('Pipe with name %s could not be opened.', host_or_path) + ) + or assert(uv.new_tcp(), 'Could not create new TCP socket') + ) local closing = false local transport = { write = function(msg) - tcp:write(msg) - end, - is_closing = function() - return closing - end, - terminate = function() - if not closing then - closing = true - tcp:shutdown() - tcp:close() - dispatchers.on_exit(0, 0) - end + handle:write(msg) end, - } - local client = new_client(dispatchers, transport) - tcp:connect(host, port, function(err) - if err then - vim.schedule(function() - vim.notify( - string.format('Could not connect to %s:%s, reason: %s', host, port, vim.inspect(err)), - vim.log.levels.WARN - ) - end) - return - end - local handle_body = function(body) - client:handle_body(body) - end - tcp:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) - client:on_error(M.client_errors.READ_ERROR, read_err) - end)) - end) - - return public_client(client) - end -end - ---- Create a LSP RPC client factory that connects via named pipes (Windows) ---- or unix domain sockets (Unix) to the given pipe_path (file path on ---- Unix and name on Windows). ---- ---- Return a function that can be passed to the `cmd` field for ---- |vim.lsp.start_client()| or |vim.lsp.start()|. ---- ----@param pipe_path string file path of the domain socket (Unix) or name of the named pipe (Windows) to connect to ----@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient -function M.domain_socket_connect(pipe_path) - return function(dispatchers) - dispatchers = merge_dispatchers(dispatchers) - local pipe = - assert(uv.new_pipe(false), string.format('pipe with name %s could not be opened.', pipe_path)) - local closing = false - local transport = { - write = vim.schedule_wrap(function(msg) - pipe:write(msg) - end), is_closing = function() return closing end, terminate = function() if not closing then closing = true - pipe:shutdown() - pipe:close() + handle:shutdown() + handle:close() dispatchers.on_exit(0, 0) end end, } local client = new_client(dispatchers, transport) - pipe:connect(pipe_path, function(err) + local function on_connect(err) if err then + local address = port == nil and host_or_path or (host_or_path .. ':' .. port) vim.schedule(function() vim.notify( - string.format('Could not connect to :%s, reason: %s', pipe_path, vim.inspect(err)), + string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)), vim.log.levels.WARN ) end) @@ -718,10 +676,15 @@ function M.domain_socket_connect(pipe_path) local handle_body = function(body) client:handle_body(body) end - pipe:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) + handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) client:on_error(M.client_errors.READ_ERROR, read_err) end)) - end) + end + if port == nil then + handle:connect(host_or_path, on_connect) + else + handle:connect(host_or_path, port, on_connect) + end return public_client(client) end diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1ff29bf74f..feca31c01b 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -4311,7 +4311,7 @@ describe('LSP', function() ]] eq('initialize', result.method) end) - it('can connect to lsp server via rpc.domain_socket_connect', function() + it('can connect to lsp server via pipe or domain_socket', function() local tmpfile --- @type string if is_os('win') then tmpfile = '\\\\.\\\\pipe\\pipe.test' @@ -4336,7 +4336,7 @@ describe('LSP', function() client:close() end)) end) - vim.lsp.start({ name = "dummy", cmd = vim.lsp.rpc.domain_socket_connect(SOCK) }) + vim.lsp.start({ name = "dummy", cmd = vim.lsp.rpc.connect(SOCK) }) vim.wait(1000, function() return init ~= nil end) assert(init, "server must receive `initialize` request") server:close() |