diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index fcc16069..932465ee 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -569,6 +569,21 @@ function! ale#lsp#StartProgram(conn_id, executable, command) abort return l:job_id > 0 endfunction +" Split an address into [host, port]. +" The port will either be a number or v:null. +function! ale#lsp#SplitAddress(address) abort + let l:port_match = matchlist(a:address, '\v:(\d+)$') + + if !empty(l:port_match) + let l:host = a:address[:-len(l:port_match[1]) - 2] + let l:port = l:port_match[1] + 0 + + return [l:host, l:port ? l:port : v:null] + endif + + return [a:address, v:null] +endfunction + " Connect to an LSP server via TCP. " " 1 will be returned if the connection is running, or 0 if the connection could @@ -577,8 +592,23 @@ function! ale#lsp#ConnectToAddress(conn_id, address) abort let l:conn = s:connections[a:conn_id] let l:started = 0 - " TODO: Start Neovim client here. - if !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id) + if g:ale_use_neovim_lsp_api && !l:conn.is_tsserver + let [l:host, l:port] = ale#lsp#SplitAddress(a:address) + + let l:client_id = luaeval('require("ale.lsp").start(_A)', { + \ 'name': a:conn_id, + \ 'host': l:host, + \ 'port': l:port, + \ 'root_dir': l:conn.root, + \ 'init_options': l:conn.init_options, + \}) + + if l:client_id > 0 + let l:conn.client_id = l:client_id + endif + + return l:client_id > 0 + elseif !has_key(l:conn, 'channel_id') || !ale#socket#IsOpen(l:conn.channel_id) let l:channel_id = ale#socket#Open(a:address, { \ 'callback': {_, mess -> ale#lsp#HandleMessage(a:conn_id, mess)}, \}) diff --git a/doc/ale.txt b/doc/ale.txt index 1d0d5a66..222c48f8 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -476,6 +476,11 @@ functionality for Neovim's LSP client should work as expected, and this ensures ALE integrates well with other plugins that rely on Neovim's LSP client. +NOTE: Neovim versions below `0.11.0` do not support socket connections to +language servers when the `address` defined in ALE uses a hostname instead of +an IP address. To work around this, configure language clients with an IP +address instead of a hostname, or revert back to ALE's custom LSP client. + See |lsp| for information on Neovim's built in LSP client. For diagnostics, for computing problems to show via ALE, ALE overrides the diff --git a/lua/ale/lsp.lua b/lua/ale/lsp.lua index f45f4598..01c59c86 100644 --- a/lua/ale/lsp.lua +++ b/lua/ale/lsp.lua @@ -6,6 +6,31 @@ module.start = function(config) config.init_options[true] = nil end + -- If configuring LSP via a socket connection, then generate the cmd + -- using vim.lsp.rpc.connect(), as defined in Neovim documentation. + if config.host then + local cmd_func = vim.lsp.rpc.connect(config.host, config.port) + config.host = nil + config.port = nil + + -- Wrap the cmd function so we don't throw errors back to the user + -- if the connection to an address fails to start. + -- + -- We will separately log in ALE that we failed to start a connection. + -- + -- In older Neovim versions TCP connections do not function if supplied + -- a hostname instead of an address. + config.cmd = function(dispatch) + local success, result = pcall(cmd_func, dispatch) + + if success then + return result + end + + return nil + end + end + config.handlers = { -- Override Neovim's handling of diagnostics to run through ALE's -- functions so all of the functionality in ALE works. diff --git a/test/lsp/test_lsp_address_split.vader b/test/lsp/test_lsp_address_split.vader new file mode 100644 index 00000000..adc1735a --- /dev/null +++ b/test/lsp/test_lsp_address_split.vader @@ -0,0 +1,9 @@ +Execute(Address splitting should function as intended): + AssertEqual ['foo', v:null], ale#lsp#SplitAddress('foo') + AssertEqual ['foo:', v:null], ale#lsp#SplitAddress('foo:') + AssertEqual ['foo', v:null], ale#lsp#SplitAddress('foo:0') + AssertEqual ['foo', 123], ale#lsp#SplitAddress('foo:123') + AssertEqual ['protocol:/foo:', v:null], ale#lsp#SplitAddress('protocol:/foo:') + AssertEqual ['protocol:/foo', v:null], ale#lsp#SplitAddress('protocol:/foo:0') + AssertEqual ['protocol:/foo', 123], ale#lsp#SplitAddress('protocol:/foo:123') + AssertEqual ['protocol:foo', v:null], ale#lsp#SplitAddress('protocol:foo')