]> git.madduck.net Git - etc/vim.git/blob - autoload/ale/socket.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Squashed '.vim/bundle/ale/' content from commit 22185c4c
[etc/vim.git] / autoload / ale / socket.vim
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.
5 "
6 " Important functions are described below. They are:
7 "
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
13
14 let s:channel_map = get(s:, 'channel_map', {})
15
16 function! s:VimOutputCallback(channel, data) abort
17     let l:channel_id = ch_info(a:channel).id
18
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)
22     endif
23 endfunction
24
25 function! s:NeoVimOutputCallback(channel_id, data, event) abort
26     let l:info = s:channel_map[a:channel_id]
27
28     if a:event is# 'data'
29         let l:info.last_line = ale#util#JoinNeovimOutput(
30         \   a:channel_id,
31         \   l:info.last_line,
32         \   a:data,
33         \   l:info.mode,
34         \   ale#util#GetFunction(l:info.callback),
35         \)
36     endif
37 endfunction
38
39 " Open a socket for a given address. The following options are accepted:
40 "
41 " callback - A callback for receiving input. (required)
42 "
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
49
50     let l:channel_info = {
51     \   'address': a:address,
52     \   'mode': l:mode,
53     \   'callback': a:options.callback,
54     \}
55
56     if !has('nvim')
57         " Vim
58         let l:channel_options = {
59         \   'mode': l:mode,
60         \   'waittime': 0,
61         \   'callback': function('s:VimOutputCallback'),
62         \}
63
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
67         endif
68
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')
73         " NeoVim 0.3+
74         try
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/
79             let l:channel_id = -1
80         endtry
81
82         " 0 means the connection failed some times in NeoVim, so make the ID
83         " invalid to match Vim.
84         if l:channel_id is 0
85             let l:channel_id = -1
86         endif
87
88         let l:channel_info.channel = l:channel_id
89     else
90         " Other Vim versions.
91         let l:channel_id = -1
92     endif
93
94     if l:channel_id >= 0
95         let s:channel_map[l:channel_id] = l:channel_info
96     endif
97
98     return l:channel_id
99 endfunction
100
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)
104         return 0
105     endif
106
107     if has('nvim')
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
110     endif
111
112     let l:channel = s:channel_map[a:channel_id].channel
113
114     return ch_status(l:channel) is# 'open'
115 endfunction
116
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)
121         return 0
122     endif
123
124     let l:channel = remove(s:channel_map, a:channel_id).channel
125
126     if has('nvim')
127         silent! call chanclose(l:channel)
128     elseif ch_status(l:channel) is# 'open'
129         call ch_close(l:channel)
130     endif
131 endfunction
132
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)
136         return
137     endif
138
139     let l:channel = s:channel_map[a:channel_id].channel
140
141     if has('nvim')
142         call chansend(l:channel, a:data)
143     else
144         call ch_sendraw(l:channel, a:data)
145     endif
146 endfunction
147
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', '')
151 endfunction