1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 16:40:05 +08:00
SpaceVim/bundle/gina.vim/autoload/gina/process/pipe.vim

203 lines
6.2 KiB
VimL
Vendored

let s:Guard = vital#gina#import('Vim.Guard')
let s:String = vital#gina#import('Data.String')
function! s:extend_content(content, data) abort
unlockvar 1 a:content
let a:content[-1] .= a:data[0]
call extend(a:content, a:data[1:])
lockvar 1 a:content
endfunction
" Default pipe -------------------------------------------------------------
function! gina#process#pipe#default() abort
let pipe = deepcopy(s:default_pipe)
return pipe
endfunction
function! s:_default_pipe_on_start() abort dict
call gina#process#register(self)
endfunction
function! s:_default_pipe_on_exit(exitval) abort dict
call gina#process#unregister(self)
endfunction
let s:default_pipe = {
\ 'on_start': function('s:_default_pipe_on_start'),
\ 'on_exit': function('s:_default_pipe_on_exit'),
\}
" Store pipe ---------------------------------------------------------------
function! gina#process#pipe#store() abort
let pipe = deepcopy(s:store_pipe)
let pipe.stdout = ['']
let pipe.stderr = ['']
let pipe.content = ['']
lockvar 1 pipe.stdout
lockvar 1 pipe.stderr
lockvar 1 pipe.content
return pipe
endfunction
function! s:_store_pipe_on_receive(event, data) abort dict
call map(a:data, 'v:val[-1:] ==# "\r" ? v:val[:-2] : v:val')
call s:extend_content(self[a:event], a:data)
call s:extend_content(self.content, a:data)
endfunction
function! s:_store_pipe_on_exit(exitval) abort dict
" Content may has an extra empty line (POSIX text) so remove it
if has_key(self, 'stdout') && empty(self.stdout[-1])
unlockvar 1 self.stdout
call remove(self.stdout, -1)
lockvar 1 self.stdout
endif
if has_key(self, 'stderr') && empty(self.stderr[-1])
unlockvar 1 self.stderr
call remove(self.stderr, -1)
lockvar 1 self.stderr
endif
if has_key(self, 'content') && empty(self.content[-1])
unlockvar 1 self.content
call remove(self.content, -1)
lockvar 1 self.content
endif
call call('s:_default_pipe_on_exit', [a:exitval], self)
endfunction
let s:store_pipe = {
\ 'on_start': function('s:_default_pipe_on_start'),
\ 'on_stdout': function('s:_store_pipe_on_receive', ['stdout']),
\ 'on_stderr': function('s:_store_pipe_on_receive', ['stderr']),
\ 'on_exit': function('s:_store_pipe_on_exit'),
\}
" Echo pipe ----------------------------------------------------------------
function! gina#process#pipe#echo() abort
let pipe = deepcopy(s:echo_pipe)
let pipe.stdout = ['']
let pipe.stderr = ['']
let pipe.content = ['']
lockvar 1 pipe.stdout
lockvar 1 pipe.stderr
lockvar 1 pipe.content
return pipe
endfunction
function! s:_echo_pipe_on_exit(exitval) abort dict
if len(self.content)
call gina#core#console#message(
\ s:String.remove_ansi_sequences(join(self.content, "\n")),
\)
endif
call call('s:_store_pipe_on_exit', [a:exitval], self)
endfunction
let s:echo_pipe = {
\ 'on_start': function('s:_default_pipe_on_start'),
\ 'on_stdout': function('s:_store_pipe_on_receive', ['stdout']),
\ 'on_stderr': function('s:_store_pipe_on_receive', ['stderr']),
\ 'on_exit': function('s:_echo_pipe_on_exit'),
\}
" Stream pipe --------------------------------------------------------------
function! gina#process#pipe#stream(...) abort
let pipe = deepcopy(s:stream_pipe)
let pipe.writer = gina#core#writer#new(a:0 ? a:1 : s:stream_pipe_writer)
let pipe.stderr = ['']
let pipe.content = ['']
lockvar 1 pipe.stderr
lockvar 1 pipe.content
return pipe
endfunction
function! s:_stream_pipe_on_start() abort dict
call call('s:_default_pipe_on_start', [], self)
let self.writer._job = self
call self.writer.start()
endfunction
function! s:_stream_pipe_on_receive(data) abort dict
call map(a:data, 'v:val[-1:] ==# "\r" ? v:val[:-2] : v:val')
call self.writer.write(a:data)
endfunction
function! s:_stream_pipe_on_exit(data) abort dict
call self.writer.stop()
call call('s:_echo_pipe_on_exit', [a:data], self)
endfunction
let s:stream_pipe = {
\ 'on_start': function('s:_stream_pipe_on_start'),
\ 'on_stdout': function('s:_stream_pipe_on_receive'),
\ 'on_stderr': function('s:_store_pipe_on_receive', ['stderr']),
\ 'on_exit': function('s:_stream_pipe_on_exit'),
\}
" Stream pipe writer -------------------------------------------------------
function! gina#process#pipe#stream_writer() abort
return deepcopy(s:stream_pipe_writer)
endfunction
function! s:_discard_winview() abort
augroup gina_process_pipe_stream_pipe_writer_internal
autocmd! * <buffer=abuf>
autocmd CursorMoved <buffer=abuf>
\ silent! unlet! b:gina_winview |
\ autocmd! gina_process_pipe_stream_pipe_writer_internal * <buffer=abuf>
augroup END
endfunction
function! s:_stream_pipe_writer_on_start() abort dict
let self._spinner = gina#core#spinner#start(self.bufnr)
call gina#process#register('writer:' . self.bufnr, 1)
call gina#core#emitter#emit('writer:started', self.bufnr)
" When user moves cursor, remove 'b:gina_winview' to prevent unwilling
" cursor change after completion
augroup gina_process_pipe_stream_pipe_writer_internal
execute printf('autocmd! * <buffer=%d>', self.bufnr)
execute printf(
\ 'autocmd CursorMoved <buffer=%d> call s:_discard_winview()',
\ self.bufnr
\)
augroup END
endfunction
function! s:_stream_pipe_writer_on_exit() abort dict
call self._job.stop()
call self._spinner.stop()
let focus = gina#core#buffer#focus(self.bufnr)
if empty(focus) || bufnr('%') != self.bufnr
call gina#core#emitter#emit('writer:stopped', self.bufnr)
call gina#process#unregister('writer:' . self.bufnr, 1)
return
endif
try
if exists('b:gina_winview')
silent! call winrestview(b:gina_winview)
endif
finally
call focus.restore()
call gina#core#emitter#emit('writer:stopped', self.bufnr)
call gina#process#unregister('writer:' . self.bufnr, 1)
endtry
endfunction
let s:stream_pipe_writer = {
\ 'on_start': function('s:_stream_pipe_writer_on_start'),
\ 'on_exit': function('s:_stream_pipe_writer_on_exit'),
\}
" Save winview on BufUnload while winsaveview() returns unwilling value
" on BufReadCmd
augroup gina_process_pipe_internal
autocmd! *
autocmd BufUnload gina://* let b:gina_winview = winsaveview()
augroup END