123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- scriptencoding utf-8
- let s:root = expand('<sfile>:h:h:h')
- let s:is_vim = !has('nvim')
- let s:is_win = has("win32") || has("win64")
- let s:clients = {}
- if get(g:, 'node_client_debug', 0)
- echohl WarningMsg | echo '[coc.nvim] Enable g:node_client_debug could impact your vim experience' | echohl None
- let $NODE_CLIENT_LOG_LEVEL = 'debug'
- if exists('$NODE_CLIENT_LOG_FILE')
- let s:logfile = resolve($NODE_CLIENT_LOG_FILE)
- else
- let s:logfile = tempname()
- let $NODE_CLIENT_LOG_FILE = s:logfile
- endif
- endif
- " create a client
- function! coc#client#create(name, command)
- let client = {}
- let client['command'] = a:command
- let client['name'] = a:name
- let client['running'] = 0
- let client['async_req_id'] = 1
- let client['async_callbacks'] = {}
- " vim only
- let client['channel'] = v:null
- " neovim only
- let client['chan_id'] = 0
- let client['start'] = function('s:start', [], client)
- let client['request'] = function('s:request', [], client)
- let client['notify'] = function('s:notify', [], client)
- let client['request_async'] = function('s:request_async', [], client)
- let client['on_async_response'] = function('s:on_async_response', [], client)
- let s:clients[a:name] = client
- return client
- endfunction
- function! s:start() dict
- if self.running | return | endif
- if !isdirectory(getcwd())
- echohl Error | echon '[coc.nvim] Current cwd is not a valid directory.' | echohl None
- return
- endif
- let timeout = string(get(g:, 'coc_channel_timeout', 30))
- let tmpdir = fnamemodify(tempname(), ':p:h')
- if s:is_vim
- if get(g:, 'node_client_debug', 0)
- let file = tmpdir . '/coc.log'
- call ch_logfile(file, 'w')
- echohl MoreMsg | echo '[coc.nvim] channel log to '.file | echohl None
- endif
- let options = {
- \ 'in_mode': 'json',
- \ 'out_mode': 'json',
- \ 'err_mode': 'nl',
- \ 'err_cb': {channel, message -> s:on_stderr(self.name, split(message, "\n"))},
- \ 'exit_cb': {channel, code -> s:on_exit(self.name, code)},
- \ 'env': {
- \ 'NODE_NO_WARNINGS': '1',
- \ 'VIM_NODE_RPC': '1',
- \ 'COC_NVIM': '1',
- \ 'COC_CHANNEL_TIMEOUT': timeout,
- \ 'TMPDIR': tmpdir,
- \ }
- \}
- if has("patch-8.1.350")
- let options['noblock'] = 1
- endif
- let job = job_start(self.command, options)
- let status = job_status(job)
- if status !=# 'run'
- let self.running = 0
- echohl Error | echom 'Failed to start '.self.name.' service' | echohl None
- return
- endif
- let self['running'] = 1
- let self['channel'] = job_getchannel(job)
- else
- let original = {}
- let opts = {
- \ 'rpc': 1,
- \ 'on_stderr': {channel, msgs -> s:on_stderr(self.name, msgs)},
- \ 'on_exit': {channel, code -> s:on_exit(self.name, code)},
- \ }
- if has('nvim-0.5.0')
- " could use env option
- let opts['env'] = {
- \ 'COC_NVIM': '1',
- \ 'NODE_NO_WARNINGS': '1',
- \ 'COC_CHANNEL_TIMEOUT': timeout,
- \ 'TMPDIR': tmpdir
- \ }
- else
- if exists('*getenv')
- let original = {
- \ 'NODE_NO_WARNINGS': getenv('NODE_NO_WARNINGS'),
- \ 'TMPDIR': getenv('TMPDIR'),
- \ }
- endif
- if exists('*setenv')
- call setenv('COC_NVIM', '1')
- call setenv('NODE_NO_WARNINGS', '1')
- call setenv('COC_CHANNEL_TIMEOUT', timeout)
- call setenv('TMPDIR', tmpdir)
- else
- let $NODE_NO_WARNINGS = 1
- let $TMPDIR = tmpdir
- endif
- endif
- let chan_id = jobstart(self.command, opts)
- if !empty(original)
- if exists('*setenv')
- for key in keys(original)
- call setenv(key, original[key])
- endfor
- else
- let $TMPDIR = original['TMPDIR']
- endif
- endif
- if chan_id <= 0
- echohl Error | echom 'Failed to start '.self.name.' service' | echohl None
- return
- endif
- let self['chan_id'] = chan_id
- let self['running'] = 1
- endif
- endfunction
- function! s:on_stderr(name, msgs)
- if get(g:, 'coc_vim_leaving', 0) | return | endif
- if get(g:, 'coc_disable_uncaught_error', 0) | return | endif
- let data = filter(copy(a:msgs), '!empty(v:val)')
- if empty(data) | return | endif
- let client = a:name ==# 'coc' ? '[coc.nvim]' : '['.a:name.']'
- let data[0] = client.': '.data[0]
- call coc#ui#echo_messages('Error', data)
- endfunction
- function! s:on_exit(name, code) abort
- if get(g:, 'coc_vim_leaving', 0) | return | endif
- let client = get(s:clients, a:name, v:null)
- if empty(client) | return | endif
- if client['running'] != 1 | return | endif
- let client['running'] = 0
- let client['chan_id'] = 0
- let client['channel'] = v:null
- let client['async_req_id'] = 1
- if a:code != 0 && a:code != 143
- echohl Error | echom 'client '.a:name. ' abnormal exit with: '.a:code | echohl None
- endif
- endfunction
- function! coc#client#get_client(name) abort
- return get(s:clients, a:name, v:null)
- endfunction
- function! coc#client#get_channel(client)
- if s:is_vim
- return a:client['channel']
- endif
- return a:client['chan_id']
- endfunction
- function! s:request(method, args) dict
- let channel = coc#client#get_channel(self)
- if empty(channel) | return '' | endif
- try
- if s:is_vim
- let res = ch_evalexpr(channel, [a:method, a:args], {'timeout': 60 * 1000})
- if type(res) == 1 && res ==# ''
- throw 'request '.a:method. ' '.string(a:args).' timeout after 60s'
- endif
- let [l:errmsg, res] = res
- if !empty(l:errmsg)
- throw l:errmsg
- else
- return res
- endif
- else
- return call('rpcrequest', [channel, a:method] + a:args)
- endif
- catch /.*/
- if v:exception =~# 'E475'
- if get(g:, 'coc_vim_leaving', 0) | return | endif
- echohl Error | echom '['.self.name.'] server connection lost' | echohl None
- let name = self.name
- call s:on_exit(name, 0)
- execute 'silent do User ConnectionLost'.toupper(name[0]).name[1:]
- elseif v:exception =~# 'E12'
- " neovim's bug, ignore it
- else
- echohl Error | echo 'Error on request ('.a:method.'): '.v:exception | echohl None
- endif
- endtry
- endfunction
- function! s:notify(method, args) dict
- let channel = coc#client#get_channel(self)
- if empty(channel)
- return ''
- endif
- try
- if s:is_vim
- call ch_sendraw(channel, json_encode([0, [a:method, a:args]])."\n")
- else
- call call('rpcnotify', [channel, a:method] + a:args)
- endif
- catch /.*/
- if v:exception =~# 'E475'
- if get(g:, 'coc_vim_leaving', 0)
- return
- endif
- echohl Error | echom '['.self.name.'] server connection lost' | echohl None
- let name = self.name
- call s:on_exit(name, 0)
- execute 'silent do User ConnectionLost'.toupper(name[0]).name[1:]
- elseif v:exception =~# 'E12'
- " neovim's bug, ignore it
- else
- echohl Error | echo 'Error on notify ('.a:method.'): '.v:exception | echohl None
- endif
- endtry
- endfunction
- function! s:request_async(method, args, cb) dict
- let channel = coc#client#get_channel(self)
- if empty(channel) | return '' | endif
- if type(a:cb) != 2
- echohl Error | echom '['.self['name'].'] Callback should be function' | echohl None
- return
- endif
- let id = self.async_req_id
- let self.async_req_id = id + 1
- let self.async_callbacks[id] = a:cb
- call self['notify']('nvim_async_request_event', [id, a:method, a:args])
- endfunction
- function! s:on_async_response(id, resp, isErr) dict
- let Callback = get(self.async_callbacks, a:id, v:null)
- if empty(Callback)
- " should not happen
- echohl Error | echom 'callback not found' | echohl None
- return
- endif
- call remove(self.async_callbacks, a:id)
- if a:isErr
- call call(Callback, [a:resp, v:null])
- else
- call call(Callback, [v:null, a:resp])
- endif
- endfunction
- function! coc#client#is_running(name) abort
- let client = get(s:clients, a:name, v:null)
- if empty(client) | return 0 | endif
- if !client['running'] | return 0 | endif
- if s:is_vim
- let status = job_status(ch_getjob(client['channel']))
- return status ==# 'run'
- else
- let chan_id = client['chan_id']
- let [code] = jobwait([chan_id], 10)
- return code == -1
- endif
- endfunction
- function! coc#client#stop(name) abort
- let client = get(s:clients, a:name, v:null)
- if empty(client) | return 1 | endif
- let running = coc#client#is_running(a:name)
- if !running
- echohl WarningMsg | echom 'client '.a:name. ' not running.' | echohl None
- return 1
- endif
- if s:is_vim
- call job_stop(ch_getjob(client['channel']), 'term')
- else
- call jobstop(client['chan_id'])
- endif
- sleep 200m
- if coc#client#is_running(a:name)
- echohl Error | echom 'client '.a:name. ' stop failed.' | echohl None
- return 0
- endif
- call s:on_exit(a:name, 0)
- echohl MoreMsg | echom 'client '.a:name.' stopped!' | echohl None
- return 1
- endfunction
- function! coc#client#request(name, method, args)
- let client = get(s:clients, a:name, v:null)
- if !empty(client)
- return client['request'](a:method, a:args)
- endif
- endfunction
- function! coc#client#notify(name, method, args)
- let client = get(s:clients, a:name, v:null)
- if !empty(client)
- call client['notify'](a:method, a:args)
- endif
- endfunction
- function! coc#client#request_async(name, method, args, cb)
- let client = get(s:clients, a:name, v:null)
- if !empty(client)
- call client['request_async'](a:method, a:args, a:cb)
- endif
- endfunction
- function! coc#client#on_response(name, id, resp, isErr)
- let client = get(s:clients, a:name, v:null)
- if !empty(client)
- call client['on_async_response'](a:id, a:resp, a:isErr)
- endif
- endfunction
- function! coc#client#restart(name) abort
- let stopped = coc#client#stop(a:name)
- if !stopped | return | endif
- let client = get(s:clients, a:name, v:null)
- if !empty(client)
- call client['start']()
- endif
- endfunction
- function! coc#client#restart_all()
- for key in keys(s:clients)
- call coc#client#restart(key)
- endfor
- endfunction
- function! coc#client#open_log()
- if !get(g:, 'node_client_debug', 0)
- echohl Error | echon '[coc.nvim] use let g:node_client_debug = 1 in your vimrc to enabled debug mode.' | echohl None
- return
- endif
- execute 'vs '.s:logfile
- endfunction
|