All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 " Author: w0rp <devw0rp@gmail.com>
2 " Description: APIs for working with asynchronous sockets, with an API
3 " normalised between Vim 8 and NeoVim. Socket connections only work in NeoVim
4 " 0.3+, and silently do nothing in earlier NeoVim versions.
6 " Important functions are described below. They are:
8 " ale#socket#Open(address, options) -> channel_id (>= 0 if successful)
9 " ale#socket#IsOpen(channel_id) -> 1 if open, 0 otherwise
10 " ale#socket#Close(channel_id)
11 " ale#socket#Send(channel_id, data)
12 " ale#socket#GetAddress(channel_id) -> Return the address for a job
14 let s:channel_map = get(s:, 'channel_map', {})
16 function! s:VimOutputCallback(channel, data) abort
17 let l:channel_id = ch_info(a:channel).id
19 " Only call the callbacks for jobs which are valid.
20 if l:channel_id >= 0 && has_key(s:channel_map, l:channel_id)
21 call ale#util#GetFunction(s:channel_map[l:channel_id].callback)(l:channel_id, a:data)
25 function! s:NeoVimOutputCallback(channel_id, data, event) abort
26 let l:info = s:channel_map[a:channel_id]
29 let l:info.last_line = ale#util#JoinNeovimOutput(
34 \ ale#util#GetFunction(l:info.callback),
39 " Open a socket for a given address. The following options are accepted:
41 " callback - A callback for receiving input. (required)
43 " A non-negative number representing a channel ID will be returned is the
44 " connection was successful. 0 is a valid channel ID in Vim, so test if the
45 " connection ID is >= 0.
46 function! ale#socket#Open(address, options) abort
47 let l:mode = get(a:options, 'mode', 'raw')
48 let l:Callback = a:options.callback
50 let l:channel_info = {
51 \ 'address': a:address,
53 \ 'callback': a:options.callback,
58 let l:channel_options = {
61 \ 'callback': function('s:VimOutputCallback'),
64 " Use non-blocking writes for Vim versions that support the option.
65 if has('patch-8.1.350')
66 let l:channel_options.noblock = 1
69 let l:channel_info.channel = ch_open(a:address, l:channel_options)
70 let l:vim_info = ch_info(l:channel_info.channel)
71 let l:channel_id = !empty(l:vim_info) ? l:vim_info.id : -1
72 elseif exists('*chansend') && exists('*sockconnect')
75 let l:channel_id = sockconnect(stridx(a:address, ':') != -1 ? 'tcp' : 'pipe',
76 \ a:address, {'on_data': function('s:NeoVimOutputCallback')})
77 let l:channel_info.last_line = ''
78 catch /connection failed/
82 " 0 means the connection failed some times in NeoVim, so make the ID
83 " invalid to match Vim.
88 let l:channel_info.channel = l:channel_id
95 let s:channel_map[l:channel_id] = l:channel_info
101 " Return 1 is a channel is open, 0 otherwise.
102 function! ale#socket#IsOpen(channel_id) abort
103 if !has_key(s:channel_map, a:channel_id)
108 " In NeoVim, we have to check if this channel is in the global list.
109 return index(map(nvim_list_chans(), 'v:val.id'), a:channel_id) >= 0
112 let l:channel = s:channel_map[a:channel_id].channel
114 return ch_status(l:channel) is# 'open'
117 " Close a socket, if it's still open.
118 function! ale#socket#Close(channel_id) abort
119 " IsRunning isn't called here, so we don't check nvim_list_chans()
120 if !has_key(s:channel_map, a:channel_id)
124 let l:channel = remove(s:channel_map, a:channel_id).channel
127 silent! call chanclose(l:channel)
128 elseif ch_status(l:channel) is# 'open'
129 call ch_close(l:channel)
133 " Send some data to a socket.
134 function! ale#socket#Send(channel_id, data) abort
135 if !has_key(s:channel_map, a:channel_id)
139 let l:channel = s:channel_map[a:channel_id].channel
142 call chansend(l:channel, a:data)
144 call ch_sendraw(l:channel, a:data)
148 " Get an address for a channel, or an empty string.
149 function! ale#socket#GetAddress(channel_id) abort
150 return get(get(s:channel_map, a:channel_id, {}), 'address', '')