mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 06:20:05 +08:00
1400 lines
42 KiB
VimL
Vendored
1400 lines
42 KiB
VimL
Vendored
" Location: autoload/dispatch.vim
|
|
|
|
if exists('g:autoloaded_dispatch')
|
|
finish
|
|
endif
|
|
|
|
let g:autoloaded_dispatch = 1
|
|
|
|
" Section: Utility
|
|
|
|
function! dispatch#tempname() abort
|
|
let temp = tempname()
|
|
if has('win32')
|
|
return fnamemodify(fnamemodify(temp, ':h'), ':p').fnamemodify(temp, ':t')
|
|
endif
|
|
return temp
|
|
endfunction
|
|
|
|
function! dispatch#uniq(list) abort
|
|
let i = 0
|
|
let seen = {}
|
|
while i < len(a:list)
|
|
if (a:list[i] ==# '' && exists('empty')) || has_key(seen,a:list[i])
|
|
call remove(a:list,i)
|
|
elseif a:list[i] ==# ''
|
|
let i += 1
|
|
let empty = 1
|
|
else
|
|
let seen[a:list[i]] = 1
|
|
let i += 1
|
|
endif
|
|
endwhile
|
|
return a:list
|
|
endfunction
|
|
|
|
function! dispatch#fnameescape(file) abort
|
|
if exists('*fnameescape')
|
|
return fnameescape(a:file)
|
|
elseif a:file ==# '-'
|
|
return '\-'
|
|
else
|
|
return substitute(escape(a:file, " \t\n*?[{`$\\%#'\"|!<"), '^[+>]', '\\&', '')
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#shellescape(...) abort
|
|
let args = []
|
|
for arg in a:000
|
|
if arg =~# '^[A-Za-z0-9_/.-]\+$'
|
|
let args += [arg]
|
|
elseif &shell =~# 'c\@<!sh'
|
|
let args += [substitute(shellescape(arg), '\\\n', '\n', 'g')]
|
|
else
|
|
let args += [shellescape(arg)]
|
|
endif
|
|
endfor
|
|
return join(args, ' ')
|
|
endfunction
|
|
|
|
let s:var = '\%(<\%(cword\|cWORD\|cexpr\|cfile\|sfile\|slnum\|afile\|abuf\|amatch' . (has('clientserver') ? '\|client' : '') . '\)>\|%\|#<\=\d\+\|##\=\)'
|
|
function! dispatch#escape(string) abort
|
|
return substitute(a:string, s:var, '\\&', 'g')
|
|
endfunction
|
|
|
|
function! dispatch#bang(string) abort
|
|
return '!' . substitute(a:string, '!\|' . s:var, '\\&', 'g')
|
|
endfunction
|
|
|
|
function! s:expand(expr, dispatch_opts) abort
|
|
if a:expr =~# '^\\\+`[-+]\=='
|
|
return a:expr[1:-1]
|
|
elseif a:expr =~# '^`='
|
|
sandbox let v = eval(a:expr[2:-2])
|
|
return v
|
|
elseif a:expr =~# '^`[-+]='
|
|
return ''
|
|
endif
|
|
call extend(l:, a:dispatch_opts)
|
|
sandbox let v = expand(substitute(a:expr, ':S$', '', ''))
|
|
if has('win32') && a:expr =~# '^\\[%#]' && v =~# '^\\[%#]'
|
|
let v = v[1:-1]
|
|
endif
|
|
if a:expr =~# ':S$'
|
|
let v = shellescape(v)
|
|
endif
|
|
if len(v) && len(expand(matchstr(a:expr, '^[%#][^:]*\%(:p:h\)\=\|^[^:]\+')))
|
|
return v
|
|
else
|
|
return a:expr
|
|
endif
|
|
endfunction
|
|
|
|
let s:flags = '<\=\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)*\%(:S\)\='
|
|
let s:expandable = '\C\\*\%(`[+-]\==[^`]*`\|' . s:var . s:flags . '\)'
|
|
function! dispatch#expand(string, ...) abort
|
|
let opts = {}
|
|
if a:0 && a:1 > 0
|
|
let opts['l#'] = a:1
|
|
let opts._l = a:1
|
|
endif
|
|
let lnum = v:lnum
|
|
try
|
|
let v:lnum = get(opts, 'l#', 0)
|
|
let string = substitute(a:string, s:expandable, '\=s:expand(submatch(0), opts)', 'g')
|
|
finally
|
|
let v:lnum = lnum
|
|
endtry
|
|
return string
|
|
endfunction
|
|
|
|
function! s:command_lnum(string, lnum) abort
|
|
return a:lnum > 0 ? substitute(a:string, '^:[%0]\=\ze\a', ':' . a:lnum, '') : a:string
|
|
endfunction
|
|
|
|
function! s:build_make(program, args) abort
|
|
if a:program =~# '\$\*'
|
|
return substitute(a:program, '\$\*', a:args, 'g')
|
|
elseif empty(a:args)
|
|
return a:program
|
|
else
|
|
return a:program . ' ' . a:args
|
|
endif
|
|
endfunction
|
|
|
|
function! s:efm_query(key, format) abort
|
|
let matches = []
|
|
let efm = ',' . a:format
|
|
let pattern = '\c,%\\&\(' . substitute(
|
|
\ type(a:key) == type('') ? a:key : join(a:key, '\|'), '_', '_\=', 'g') .
|
|
\ '\)=\(\%(\\.\|[^\,]\)*\)'
|
|
let pos = 0
|
|
while 1
|
|
let match = matchlist(efm, pattern, pos)
|
|
let pos = matchend(efm, pattern, pos)
|
|
if pos < 0
|
|
return matches
|
|
endif
|
|
call add(matches, [match[1], substitute(match[2], '\\\ze[\,]', '', 'g')])
|
|
endwhile
|
|
endfunction
|
|
|
|
function! s:efm_literal(key, format, ...) abort
|
|
let subs = {'%': '%', 'f': '%:S'}
|
|
for [key, raw] in s:efm_query(a:key, a:format)
|
|
let value = substitute(raw, '%\(.\)', '\=get(subs,submatch(1),"\030")', 'g')
|
|
if len(value) && value !~# "\030"
|
|
return value
|
|
endif
|
|
endfor
|
|
return ''
|
|
endfunction
|
|
|
|
function! s:efm_to_regexp(pattern) abort
|
|
return '\c^\%(' . substitute(a:pattern,
|
|
\ '\(%\=\)\([%*\\.#^$[~]\)',
|
|
\ '\=empty(submatch(1)) ? "\\".submatch(2) : submatch(2)==#"#"?"*":submatch(2)',
|
|
\ 'g') . '\)$'
|
|
endfunction
|
|
|
|
function! s:efm_regexps(key, ...) abort
|
|
let matches = s:efm_query(a:key, a:0 ? a:1 : &errorformat)
|
|
call map(matches, '[v:val[0], s:efm_to_regexp(v:val[1])]')
|
|
return matches
|
|
endfunction
|
|
|
|
function! s:escape_path(path) abort
|
|
return substitute(dispatch#fnameescape(a:path), '^\\\~', '\~', '')
|
|
endfunction
|
|
|
|
function! dispatch#dir_opt(...) abort
|
|
let dir = fnamemodify(a:0 ? a:1 : getcwd(), ':p:~:s?[^:]\zs[\\/]$??')
|
|
return '-dir=' . s:escape_path(dir) . ' '
|
|
endfunction
|
|
|
|
function! s:cd_command() abort
|
|
return exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'
|
|
endfunction
|
|
|
|
function! dispatch#cd_helper(dir) abort
|
|
let back = s:cd_command() . ' ' . dispatch#fnameescape(getcwd())
|
|
return 'let g:dispatch_back = '.string(back).'|lcd '.dispatch#fnameescape(a:dir)
|
|
endfunction
|
|
|
|
function! s:wrapcd(dir, cmd) abort
|
|
if a:dir ==# getcwd()
|
|
return a:cmd
|
|
endif
|
|
return 'try|execute dispatch#cd_helper('.string(a:dir).')|execute '.string(a:cmd).'|finally|execute remove(g:, "dispatch_back")|endtry'
|
|
endfunction
|
|
|
|
function! dispatch#slash() abort
|
|
return !exists("+shellslash") || &shellslash ? '/' : '\'
|
|
endfunction
|
|
|
|
function! dispatch#shellpipe(file) abort
|
|
if &shellpipe =~# '%s'
|
|
return ' ' . printf(&shellpipe, dispatch#shellescape(a:file))
|
|
else
|
|
return ' ' . &shellpipe . ' ' . dispatch#shellescape(a:file)
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#vim_executable() abort
|
|
if !exists('s:vim')
|
|
if has('win32')
|
|
let roots = [fnamemodify($VIMRUNTIME, ':8') . dispatch#slash(),
|
|
\ fnamemodify($VIM, ':8') . dispatch#slash()]
|
|
elseif has('gui_macvim')
|
|
let roots = [fnamemodify($VIM, ':h:h') . '/MacOS/']
|
|
else
|
|
let roots = [fnamemodify($VIM, ':h:h') . '/bin/']
|
|
endif
|
|
for root in roots
|
|
if executable(root . v:progname)
|
|
let s:vim = root . v:progname
|
|
break
|
|
endif
|
|
endfor
|
|
if !exists('s:vim')
|
|
if executable(v:progname)
|
|
let s:vim = v:progname
|
|
else
|
|
let s:vim = 'vim'
|
|
endif
|
|
endif
|
|
endif
|
|
return s:vim
|
|
endfunction
|
|
|
|
function! dispatch#has_callback() abort
|
|
if has('clientserver') && !empty(v:servername)
|
|
return 1
|
|
elseif !exists('*job_start') && !exists('*jobstart') || !get(g:, 'dispatch_fifo_callback', 1)
|
|
return 0
|
|
endif
|
|
if !exists('s:has_temp_fifo')
|
|
let fifo = tempname()
|
|
call system('mkfifo ' . dispatch#shellescape(fifo))
|
|
let s:has_temp_fifo = (getftype(fifo) ==# 'fifo')
|
|
call delete(fifo)
|
|
endif
|
|
return s:has_temp_fifo
|
|
endfunction
|
|
|
|
function! dispatch#callback(request) abort
|
|
let request = s:request(a:request)
|
|
if !has_key(request, 'id') || !dispatch#has_callback()
|
|
return ''
|
|
endif
|
|
if has('clientserver') && !empty(v:servername)
|
|
return dispatch#shellescape(dispatch#vim_executable()) .
|
|
\ ' --servername ' . dispatch#shellescape(v:servername) .
|
|
\ ' --remote-expr "' . 'DispatchComplete(' . request.id . ')' . '"'
|
|
endif
|
|
call system('mkfifo ' . dispatch#shellescape(request.file . '.callback'))
|
|
let cmd = ['head', '-1', request.file . '.callback']
|
|
let Cb = { ... -> dispatch#complete(request.id) }
|
|
if exists('*job_start')
|
|
call job_start(cmd, {'exit_cb': Cb})
|
|
elseif exists('*jobstart')
|
|
call jobstart(cmd, {'on_exit': Cb})
|
|
else
|
|
return ''
|
|
endif
|
|
return 'echo > ' . request.file . '.callback'
|
|
endfunction
|
|
|
|
function! dispatch#autowrite() abort
|
|
if &autowrite || &autowriteall
|
|
try
|
|
if &confirm
|
|
let reconfirm = 1
|
|
setglobal noconfirm
|
|
endif
|
|
silent! wall
|
|
finally
|
|
if exists('reconfirm')
|
|
setglobal confirm
|
|
endif
|
|
endtry
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
function! dispatch#status_var() abort
|
|
if &shellxquote ==# '"'
|
|
return '%ERRORLEVEL%'
|
|
elseif &shell =~# 'csh\|fish'
|
|
return '$status'
|
|
else
|
|
return '$?'
|
|
endif
|
|
endfunction
|
|
|
|
function! s:subshell(cmds) abort
|
|
if &shell =~# 'fish'
|
|
return 'begin; ' . a:cmds . '; end'
|
|
else
|
|
return '(' . a:cmds . ')'
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#prepare_start(request, ...) abort
|
|
let status = dispatch#status_var()
|
|
let exec = 'echo ' . (&shell =~# 'fish' ? '%self' : '$$') . ' > ' . a:request.file . '.pid; '
|
|
if executable('perl')
|
|
let exec .= 'sync; perl -e "select(undef,undef,undef,0.1)" 2>/dev/null; '
|
|
else
|
|
let exec .= 'sleep 1; '
|
|
endif
|
|
let exec .= a:0 ? a:1 : a:request.expanded
|
|
let wait = a:0 > 1 ? a:2 : get(a:request, 'wait', 'error')
|
|
let pause = s:subshell("printf '\e[1m--- Press ENTER to continue ---\e[0m\\n'; exec head -1")
|
|
if wait ==# 'always'
|
|
let exec .= '; ' . pause
|
|
elseif wait !=# 'never' && wait !=# 'make'
|
|
let exec .= "; test ".status." = 0 -o ".status." = 130" .
|
|
\ (&shell =~# 'fish' ? '; or ' : ' || ') . pause
|
|
endif
|
|
if wait !=# 'make'
|
|
let exec .= '; touch ' .a:request.file . '.complete'
|
|
endif
|
|
let callback = dispatch#callback(a:request)
|
|
return exec . (empty(callback) ? '' : '; ' . callback)
|
|
endfunction
|
|
|
|
function! dispatch#prepare_make(request, ...) abort
|
|
let exec = a:0 ? a:1 : s:subshell(a:request.expanded . '; echo ' .
|
|
\ dispatch#status_var() . ' > ' . a:request.file . '.complete') .
|
|
\ dispatch#shellpipe(a:request.file)
|
|
return dispatch#prepare_start(a:request, exec, 'make')
|
|
endfunction
|
|
|
|
function! dispatch#set_title(request) abort
|
|
return dispatch#shellescape('printf',
|
|
\ '\033]1;%s\007\033]2;%s\007',
|
|
\ a:request.title,
|
|
\ a:request.expanded)
|
|
endfunction
|
|
|
|
function! dispatch#isolate(request, keep, ...) abort
|
|
let keep = ['SHELL', 'HOME'] + a:keep
|
|
let command = ['cd ' . shellescape(getcwd())]
|
|
for line in split(system('env'), "\n")
|
|
let var = matchstr(line, '^\w\+\ze=')
|
|
if !empty(var) && var !~# '^\%(_\|SHLVL\|PWD\|VIM\|VIMRUNTIME\|MYG\=VIMRC\)$' && index(keep, var) < 0
|
|
if &shell =~# 'csh'
|
|
let command += split('setenv '.var.' '.shellescape(eval('$'.var)), "\n")
|
|
else
|
|
let command += split('export '.var.'='.dispatch#shellescape(eval('$'.var)), "\n")
|
|
endif
|
|
endif
|
|
endfor
|
|
let command += a:000
|
|
let temp = type(a:request) == type({}) ? a:request.file . '.dispatch' : dispatch#tempname()
|
|
call writefile(command, temp)
|
|
return 'env -i ' . join(map(copy(keep), 'v:val."=". dispatch#shellescape(eval("$".v:val))." "'), '') . &shell . ' ' . temp
|
|
endfunction
|
|
|
|
function! s:current_compiler(...) abort
|
|
return get((empty(&l:makeprg) ? g: : b:), 'current_compiler', a:0 ? a:1 : '')
|
|
endfunction
|
|
|
|
function! s:set_current_compiler(name) abort
|
|
if empty(a:name)
|
|
unlet! b:current_compiler
|
|
else
|
|
let b:current_compiler = a:name
|
|
endif
|
|
endfunction
|
|
|
|
function! s:postfix(request) abort
|
|
let pid = dispatch#pid(a:request)
|
|
return '(' . a:request.handler.'/'.(!empty(pid) ? pid : '?') . ')'
|
|
endfunction
|
|
|
|
function! s:echo_truncated(left, right) abort
|
|
if exists('v:echospace')
|
|
let max_len = (&cmdheight - 1) * &columns + v:echospace
|
|
else
|
|
let max_len = &cmdheight * &columns - 1
|
|
let last_has_status = (&laststatus == 2 || (&laststatus == 1 && winnr('$') != 1))
|
|
|
|
if &ruler && !last_has_status
|
|
if empty(&rulerformat)
|
|
" Default ruler is 17 chars wide.
|
|
let max_len -= 17
|
|
elseif exists('g:rulerwidth')
|
|
" User specified width of custom ruler.
|
|
let max_len -= g:rulerwidth
|
|
else
|
|
" Don't know width of custom ruler, make a conservative guess.
|
|
let max_len -= &columns / 2
|
|
endif
|
|
let max_len -= 1
|
|
endif
|
|
if &showcmd
|
|
let max_len -= 10
|
|
if !&ruler || last_has_status
|
|
let max_len -= 1
|
|
endif
|
|
endif
|
|
endif
|
|
let msg = a:left . a:right
|
|
if len(substitute(msg, '.', '.', 'g')) > max_len
|
|
let msg = a:left . '<' . matchstr(a:right, '\v.{'.(max_len - len(substitute(a:left, '.', '.', 'g')) - 1).'}$')
|
|
endif
|
|
echo msg
|
|
endfunction
|
|
|
|
function! s:dispatch(request) abort
|
|
for handler in g:dispatch_handlers
|
|
if get(g:, 'dispatch_no_' . handler . '_' . get(a:request, 'action')) ||
|
|
\ get(g:, 'dispatch_no_' . handler . '_' . (get(a:request, 'action') ==# 'start' ? 'spawn' : 'dispatch'))
|
|
continue
|
|
endif
|
|
let response = call('dispatch#'.handler.'#handle', [a:request])
|
|
if !empty(response)
|
|
let a:request.handler = handler
|
|
|
|
" Display command, avoiding hit-enter prompt.
|
|
redraw
|
|
let msg = ':!'
|
|
let suffix = s:postfix(a:request)
|
|
let cmd = a:request.expanded . ' ' . suffix
|
|
call s:echo_truncated(':!', a:request.expanded . ' ' . s:postfix(a:request))
|
|
return response
|
|
endif
|
|
endfor
|
|
return 0
|
|
endfunction
|
|
|
|
function! s:extract_opts(command, ...) abort
|
|
let command = a:command
|
|
let opts = {}
|
|
while command =~# '^\%(-\|++\)\%(\w\+\)\%([= ]\|$\)'
|
|
let opt = matchstr(command, '\zs\w\+')
|
|
if command =~# '^\%(-\|++\)\w\+='
|
|
let val = matchstr(command, '\w\+=\zs\%(\\.\|\S\)*')
|
|
else
|
|
let val = 1
|
|
endif
|
|
if opt ==# 'dir' || opt ==# 'directory'
|
|
let opts.directory = fnamemodify(expand(val), ':p:s?[^:]\zs[\\/]$??')
|
|
elseif index(['compiler', 'title', 'wait'], opt) >= 0
|
|
let opts[opt] = substitute(val, '\\\(\s\)', '\1', 'g')
|
|
endif
|
|
let command = substitute(command, '^\%(-\|++\)\w\+\%(=\%(\\.\|\S\)*\)\=\s*', '', '')
|
|
endwhile
|
|
return [command, extend(opts, a:0 ? a:1 : {})]
|
|
endfunction
|
|
|
|
function! s:default_args(args, count, ...) abort
|
|
let args = matchstr(a:args, '\s\+\zs.*')
|
|
let format = a:0 ? a:1 : &errorformat
|
|
if a:count >= 0
|
|
let prefix = s:efm_literal('buffer', format, a:count)
|
|
if len(prefix)
|
|
let args = prefix . substitute(args, '^\ze.', ' ', '')
|
|
endif
|
|
endif
|
|
if empty(args)
|
|
let args = s:efm_literal('default', format, a:count)
|
|
endif
|
|
return args
|
|
endfunction
|
|
|
|
function! s:make_focus(count) abort
|
|
return s:build_make(&makeprg, s:default_args('', a:count))
|
|
endfunction
|
|
|
|
" Section: :Start, :Spawn
|
|
|
|
function! s:focus(count) abort
|
|
if type(get(b:, 'dispatch')) == type('')
|
|
return b:dispatch
|
|
else
|
|
return s:make_focus(a:count)
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#spawn_command(bang, command, count, mods, ...) abort
|
|
let [command, opts] = s:extract_opts(a:command)
|
|
if empty(command) && a:count >= 0
|
|
let command = s:focus(a:count)
|
|
call extend(opts, {'wait': 'always'}, 'keep')
|
|
let [command, opts] = s:extract_opts(command, opts)
|
|
endif
|
|
let opts.background = a:bang
|
|
let opts.mods = a:mods ==# '<mods>' ? '' : a:mods
|
|
call dispatch#spawn(command, opts, a:count)
|
|
return ''
|
|
endfunction
|
|
|
|
function! s:doautocmd(event) abort
|
|
if v:version >= 704 || (v:version == 703 && has('patch442'))
|
|
return 'doautocmd <nomodeline> ' . a:event
|
|
elseif &modelines == 0 || !&modeline
|
|
return 'doautocmd ' . a:event
|
|
else
|
|
return 'try|set modelines=0|doautocmd ' . a:event . '|finally|set modelines=' . &modelines . '|endtry'
|
|
endif
|
|
endfunction
|
|
|
|
function! s:compiler_getcwd() abort
|
|
try
|
|
exe s:doautocmd('QuickFixCmdPre dispatch-make-complete')
|
|
return getcwd()
|
|
finally
|
|
exe s:doautocmd('QuickFixCmdPost dispatch-make-complete')
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:parse_start(command, count) abort
|
|
let [command, opts] = s:extract_opts(a:command)
|
|
if empty(command) && a:count >= 0
|
|
let command = s:focus(a:count)
|
|
call extend(opts, {'wait': 'always'}, 'keep')
|
|
let [command, opts] = s:extract_opts(command, opts)
|
|
endif
|
|
if empty(command) && type(get(b:, 'start')) == type('')
|
|
let command = b:start
|
|
let [command, opts] = s:extract_opts(command, opts)
|
|
elseif empty(command) && len(s:efm_literal(['start', 'default_start'], &errorformat))
|
|
let task = s:efm_literal(['start', 'default_start'], &errorformat)
|
|
let command = &makeprg . ' ' . task
|
|
if !has_key(opts, 'title') && task =~# '^\w\S\{,19\}$'
|
|
let opts.title = s:current_compiler('make') . ' ' . task
|
|
endif
|
|
if !has_key(opts, 'directory')
|
|
let opts.directory = s:compiler_getcwd()
|
|
endif
|
|
endif
|
|
return [command, opts]
|
|
endfunction
|
|
|
|
function! dispatch#start_command(bang, command, count, mods, ...) abort
|
|
let [command, opts] = s:parse_start(a:command, a:count)
|
|
let opts.background = get(opts, 'background') || a:bang
|
|
let opts.mods = a:mods ==# '<mods>' ? '' : a:mods
|
|
if command =~# '^:\S'
|
|
unlet! g:dispatch_last_start
|
|
return s:wrapcd(get(opts, 'directory', getcwd()),
|
|
\ substitute(command, '\>', get(opts, 'background', 0) ? '!' : '', ''))
|
|
endif
|
|
call dispatch#start(command, opts, a:count)
|
|
return ''
|
|
endfunction
|
|
|
|
if type(get(g:, 'DISPATCH_STARTS')) != type({})
|
|
unlet! g:DISPATCH_STARTS
|
|
let g:DISPATCH_STARTS = {}
|
|
endif
|
|
|
|
function! dispatch#start(command, ...) abort
|
|
return dispatch#spawn(a:command, extend({'manage': 1}, a:0 ? a:1 : {}), a:0 > 1 ? a:2 : -1)
|
|
endfunction
|
|
|
|
function! dispatch#spawn(command, ...) abort
|
|
let command = empty(a:command) ? &shell : a:command
|
|
let request = extend({
|
|
\ 'action': 'start',
|
|
\ 'background': 0,
|
|
\ 'command': command,
|
|
\ 'directory': getcwd(),
|
|
\ 'title': '',
|
|
\ 'mods': '',
|
|
\ }, a:0 ? a:1 : {})
|
|
if empty(a:command)
|
|
call extend(request, {'wait': 'never'}, 'keep')
|
|
endif
|
|
let g:dispatch_last_start = request
|
|
if empty(request.title)
|
|
let request.title = substitute(fnamemodify(matchstr(request.command, '\%(\\.\|\S\)\+'), ':t:r'), '\\\(\s\)', '\1', 'g')
|
|
endif
|
|
let cd = s:cd_command()
|
|
try
|
|
if request.directory !=# getcwd()
|
|
let cwd = getcwd()
|
|
execute cd dispatch#fnameescape(request.directory)
|
|
endif
|
|
let request.expanded = dispatch#expand(request.command, a:0 > 1 ? a:2 : -1)
|
|
if get(request, 'manage')
|
|
let key = request.directory."\t".substitute(request.expanded, '\s*$', '', '')
|
|
let i = 0
|
|
while i < len(get(g:DISPATCH_STARTS, key, []))
|
|
let [handler, pid] = split(g:DISPATCH_STARTS[key][i], '[@/]')
|
|
if !s:running(pid, handler)
|
|
call remove(g:DISPATCH_STARTS[key], i)
|
|
continue
|
|
endif
|
|
try
|
|
if request.background || dispatch#{handler}#activate(pid)
|
|
let request.handler = handler
|
|
let request.pid = pid
|
|
return request
|
|
endif
|
|
catch
|
|
endtry
|
|
let i += 1
|
|
endwhile
|
|
endif
|
|
call dispatch#autowrite()
|
|
let request.file = dispatch#tempname()
|
|
let s:files[request.file] = request
|
|
if exists('cwd')
|
|
execute cd dispatch#fnameescape(request.directory)
|
|
endif
|
|
if s:dispatch(request)
|
|
if get(request, 'manage')
|
|
if !has_key(g:DISPATCH_STARTS, key)
|
|
let g:DISPATCH_STARTS[key] = []
|
|
endif
|
|
call add(g:DISPATCH_STARTS[key], request.handler.'/'.dispatch#pid(request))
|
|
endif
|
|
else
|
|
let request.handler = 'sync'
|
|
execute dispatch#bang(request.expanded)
|
|
endif
|
|
finally
|
|
if exists('cwd')
|
|
execute cd dispatch#fnameescape(cwd)
|
|
endif
|
|
endtry
|
|
return request
|
|
endfunction
|
|
|
|
" Section: :Dispatch, :Make
|
|
|
|
let g:dispatch_compilers = get(g:, 'dispatch_compilers', {})
|
|
|
|
function! s:compiler_split(args) abort
|
|
let remove = keys(filter(copy(g:dispatch_compilers), 'empty(v:val)'))
|
|
let pattern = '\%('.join(map(remove, 'substitute(escape(v:val, ".*^$~[]\\"), "\\w\\zs$", " ", "")'), '\s*\|').'\)'
|
|
let args = substitute(a:args, '\s\+', ' ', 'g')
|
|
let prefix = matchstr(args, '^\s*'.pattern.'*')
|
|
let args = substitute(args, '^\s*'.pattern.'*', '', '')
|
|
let rtp = escape(&runtimepath, ' ')
|
|
for [command, plugin] in items(g:dispatch_compilers)
|
|
if strpart(args.' ', 0, len(command)+1) ==# command.' ' && !empty(plugin)
|
|
\ && !empty(findfile('compiler/'.plugin.'.vim', rtp))
|
|
return [plugin, prefix, command, args[len(command) : -1]]
|
|
endif
|
|
endfor
|
|
let program = matchstr(args, '\S\+')
|
|
let rest = matchstr(args, '\s.*')
|
|
if fnamemodify(program, ':t') ==# 'make'
|
|
return ['make', prefix, program, rest]
|
|
endif
|
|
let plugins = map(reverse(split(globpath(rtp, 'compiler/*.vim'), "\n")), '[fnamemodify(v:val, ":t:r"), readfile(v:val)]')
|
|
for [plugin, lines] in plugins
|
|
for line in lines
|
|
let full = substitute(substitute(
|
|
\ matchstr(line, '\<CompilerSet\s\+makeprg=\zs\a\%(\\.\|[^[:space:]"]\)*'),
|
|
\ '\\\(.\)', '\1', 'g'),
|
|
\ ' \=["'']\=\%(%\|\$\*\|--\w\@!\).*', '', '')
|
|
if !empty(full) && strpart(args.' ', 0, len(full)+1) ==# full.' '
|
|
return [plugin, prefix, full, args[len(full) : -1]]
|
|
endif
|
|
endfor
|
|
endfor
|
|
for [plugin, lines] in plugins
|
|
for line in lines
|
|
if matchstr(line, '\<CompilerSet\s\+makeprg=\zs[[:alnum:]_.-]\+') ==# fnamemodify(program, ':t')
|
|
return [plugin, prefix, program, rest]
|
|
endif
|
|
endfor
|
|
endfor
|
|
return ['', prefix, program, rest]
|
|
endfunction
|
|
|
|
function! dispatch#compiler_for_program(args) abort
|
|
return get(s:compiler_split(a:args), 0, '')
|
|
endfunction
|
|
|
|
function! dispatch#compiler_options(compiler) abort
|
|
let current_compiler = get(b:, 'current_compiler', '')
|
|
let makeprg = &l:makeprg
|
|
let efm = &l:efm
|
|
if empty(a:compiler)
|
|
return {}
|
|
endif
|
|
|
|
try
|
|
if a:compiler ==# 'make'
|
|
if &makeprg !=# 'make'
|
|
setlocal errorformat<
|
|
endif
|
|
return {'program': 'make', 'format': &errorformat}
|
|
endif
|
|
let &l:makeprg = ''
|
|
try
|
|
execute 'compiler' dispatch#fnameescape(a:compiler)
|
|
catch /^Vim(compiler):E666:/
|
|
return {}
|
|
endtry
|
|
let options = {'format': &errorformat}
|
|
if !empty(&l:makeprg)
|
|
let options.program = &l:makeprg
|
|
endif
|
|
return options
|
|
finally
|
|
let &l:makeprg = makeprg
|
|
let &l:efm = efm
|
|
call s:set_current_compiler(current_compiler)
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:completion_filter(results, query) abort
|
|
if type(get(g:, 'completion_filter')) == type({})
|
|
return g:completion_filter.Apply(a:results, a:query)
|
|
else
|
|
return filter(a:results, 'strpart(v:val, 0, len(a:query)) ==# a:query')
|
|
endif
|
|
endfunction
|
|
|
|
function! s:file_complete(A) abort
|
|
return map(split(glob(substitute(a:A, '.\@<=\ze[\\/]\|$', '*', 'g')), "\n"),
|
|
\ 'dispatch#fnameescape(isdirectory(v:val) ? v:val . dispatch#slash() : v:val)')
|
|
endfunction
|
|
|
|
function! s:compiler_complete(format, compiler, A, L, P) abort
|
|
let compiler = empty(a:compiler) ? 'make' : a:compiler
|
|
|
|
let fn = s:efm_literal('completion', a:format)
|
|
if empty(fn)
|
|
let fn = s:efm_literal('complete', a:format)
|
|
endif
|
|
if empty(fn)
|
|
for file in findfile('compiler/'.compiler.'.vim', escape(&runtimepath, ' '), -1)
|
|
for line in readfile(file)
|
|
let fn = matchstr(line, '\C-complete=\zscustom\%(list\)\=,\%(s:\)\@!\S\+')
|
|
if !empty(fn)
|
|
break
|
|
endif
|
|
endfor
|
|
endfor
|
|
endif
|
|
let fn = substitute(fn, '\C^custom\%(list\)\=,', '', '')
|
|
|
|
if fn =~# '[#A-Z]' && exists('*' . fn)
|
|
let results = call(fn, [a:A, a:L, a:P])
|
|
elseif exists('*CompilerComplete_' . compiler)
|
|
let results = call('CompilerComplete_' . compiler, [a:A, a:L, a:P])
|
|
else
|
|
let results = -1
|
|
endif
|
|
|
|
if type(results) == type([])
|
|
return results
|
|
elseif type(results) != type('')
|
|
unlet! results
|
|
let results = join(s:file_complete(a:A), "\n")
|
|
endif
|
|
|
|
return s:completion_filter(split(results, "\n"), a:A)
|
|
endfunction
|
|
|
|
function! dispatch#command_complete(A, L, P) abort
|
|
let L = strpart(a:L, 0, a:P)
|
|
let args = matchstr(L, '\s\zs.*')
|
|
let [cmd, opts] = s:extract_opts(args)
|
|
let P = a:P + len(cmd) - len(L)
|
|
let len = matchend(cmd, '\S\+\s')
|
|
if len >= 0 && P >= 0
|
|
let cd = s:cd_command()
|
|
try
|
|
if get(opts, 'directory', getcwd()) !=# getcwd()
|
|
let cwd = getcwd()
|
|
execute cd dispatch#fnameescape(opts.directory)
|
|
endif
|
|
if has_key(opts, 'compiler')
|
|
let compiler = opts.compiler
|
|
let efm = get(dispatch#compiler_options(compiler), 'format', '')
|
|
elseif cmd !~# '^--\S\@!'
|
|
let compiler = dispatch#compiler_for_program(cmd)
|
|
let efm = get(dispatch#compiler_options(compiler), 'format', '')
|
|
else
|
|
let compiler = s:current_compiler()
|
|
let efm = &errorformat
|
|
endif
|
|
return s:compiler_complete(efm, compiler, a:A, 'Make '.strpart(cmd, len), P+5)
|
|
finally
|
|
if exists('cwd')
|
|
execute cd dispatch#fnameescape(cwd)
|
|
endif
|
|
endtry
|
|
elseif a:A =~# '^-dir='
|
|
let results = map(filter(s:file_complete(a:A[5:-1]), 'isdirectory(v:val)'), '"-dir=".v:val')
|
|
elseif a:A =~# '^-compiler='
|
|
let results = map(reverse(split(globpath(escape(&runtimepath, ' '), 'compiler/*.vim'), "\n")), '"-compiler=".fnamemodify(v:val, ":t:r")')
|
|
elseif a:A =~# '^-'
|
|
let as = {'dir': 'directory'}
|
|
let results = filter(['-compiler=', '-dir='],
|
|
\ '!has_key(opts, get(as, v:val[1:-2], v:val[1:-2]))')
|
|
elseif a:A =~# '^:' && exists('*getcompletion')
|
|
let matches = matchlist(a:A, '^:\([.$]\|\d\+\)\=\(\a.*\)')
|
|
if len(matches)
|
|
let results = map(getcompletion(matches[2], 'command'), '":".matches[1].v:val')
|
|
else
|
|
let results = []
|
|
endif
|
|
elseif a:A =~# '[\/]'
|
|
let results = s:file_complete(a:A)
|
|
else
|
|
let results = []
|
|
for dir in split($PATH, has('win32') ? ';' : ':')
|
|
let results += map(split(glob(dir.'/'.substitute(a:A, '.', '*&', 'g').'*'), "\n"), 'v:val[strlen(dir)+1 : -1]')
|
|
endfor
|
|
endif
|
|
return s:completion_filter(sort(dispatch#uniq(results)), a:A)
|
|
endfunction
|
|
|
|
function! dispatch#make_complete(A, L, P) abort
|
|
try
|
|
exe s:doautocmd('QuickFixCmdPre dispatch-make-complete')
|
|
return s:compiler_complete(&errorformat, s:current_compiler(), a:A, a:L, a:P)
|
|
finally
|
|
exe s:doautocmd('QuickFixCmdPost dispatch-make-complete')
|
|
endtry
|
|
endfunction
|
|
|
|
if !exists('s:makes')
|
|
let s:makes = []
|
|
let s:files = {}
|
|
endif
|
|
|
|
function! dispatch#compile_command(bang, args, count, mods, ...) abort
|
|
let [args, request] = s:extract_opts(a:args, {'mods': a:mods ==# '<mods>' ? '' : a:mods})
|
|
|
|
if empty(args)
|
|
let default_dispatch = 1
|
|
if type(get(b:, 'dispatch')) == type('')
|
|
unlet! default_dispatch
|
|
let args = b:dispatch
|
|
endif
|
|
for vars in a:count < 0 ? [g:, t:, w:, b:] : []
|
|
if type(get(vars, 'Dispatch')) == type('')
|
|
unlet! default_dispatch
|
|
let args = vars.Dispatch
|
|
endif
|
|
endfor
|
|
let [args, request] = s:extract_opts(args, request)
|
|
endif
|
|
if empty(args)
|
|
let args = '--'
|
|
endif
|
|
|
|
if args =~# '^!'
|
|
return 'Start' . (a:bang ? '!' : '') . ' ' . args[1:-1]
|
|
endif
|
|
|
|
if args =~# '^:\S'
|
|
call dispatch#autowrite()
|
|
let args = s:command_lnum(args, a:count)
|
|
return s:wrapcd(get(request, 'directory', getcwd()),
|
|
\ substitute(args[1:-1], '\>', (a:bang ? '!' : ''), ''))
|
|
endif
|
|
|
|
let executable = matchstr(args, '\S\+')
|
|
|
|
call extend(request, {
|
|
\ 'action': 'make',
|
|
\ 'background': a:bang,
|
|
\ 'format': '%+I%.%#'
|
|
\ }, 'keep')
|
|
|
|
if executable ==# '_' || executable ==# '--'
|
|
if !empty(get(request, 'compiler', ''))
|
|
let compiler_options = dispatch#compiler_options(request.compiler)
|
|
if !has_key(compiler_options, 'format')
|
|
return 'compiler ' . dispatch#fnameescape(request.compiler)
|
|
endif
|
|
call extend(request, compiler_options)
|
|
else
|
|
let request.compiler = s:current_compiler()
|
|
let request.program = &makeprg
|
|
let request.format = &errorformat
|
|
endif
|
|
let request.args = s:default_args(args, exists('default_dispatch') && a:count < 0 ? 0 : a:count, request.format)
|
|
let request.command = s:build_make(get(request, 'program', get(request, 'compiler', '--')), request.args)
|
|
else
|
|
let [compiler, prefix, program, rest] = s:compiler_split(args)
|
|
let request.compiler = get(request, 'compiler', compiler)
|
|
if !empty(request.compiler)
|
|
let compiler_options = dispatch#compiler_options(request.compiler)
|
|
if !has_key(compiler_options, 'format')
|
|
return 'compiler ' . dispatch#fnameescape(request.compiler)
|
|
endif
|
|
call extend(request, compiler_options)
|
|
if request.compiler ==# compiler
|
|
let request.program = prefix . program
|
|
let request.args = rest[1:-1]
|
|
endif
|
|
endif
|
|
let request.command = args
|
|
endif
|
|
let request.format = substitute(request.format, ',%-G%\.%#\%($\|,\@=\)', '', '')
|
|
|
|
for [key, regexp] in s:efm_regexps(['terminal', 'force_start', 'force_spawn'], request.format)
|
|
if has_key(request, 'args') && request.args =~# regexp
|
|
let title = request.compiler
|
|
if regexp =~# '\\\@<!\\ze'
|
|
let title .= ' ' . matchstr(request.args, regexp)
|
|
endif
|
|
let title = get(request, 'title', title)
|
|
return (key =~? 'spawn' ? 'Spawn' : 'Start') . (a:bang ? '!' : '') .
|
|
\ ' -title=' . escape(title, '\ ') .
|
|
\ ' ' . request.command
|
|
endif
|
|
endfor
|
|
|
|
if empty(request.compiler)
|
|
unlet request.compiler
|
|
endif
|
|
let request.title = get(request, 'title', get(request, 'compiler', 'make'))
|
|
|
|
call dispatch#autowrite()
|
|
if winnr('$') > 1
|
|
cclose
|
|
endif
|
|
let request.file = dispatch#tempname()
|
|
let &errorfile = request.file
|
|
|
|
let lnum = v:lnum
|
|
let efm = &l:efm
|
|
let makeprg = &l:makeprg
|
|
let compiler = get(b:, 'current_compiler', '')
|
|
let after = ''
|
|
let cd = s:cd_command()
|
|
try
|
|
call s:set_current_compiler(get(request, 'compiler', ''))
|
|
let v:lnum = a:count > 0 ? a:count : 0
|
|
let &l:efm = request.format
|
|
let &l:makeprg = request.command
|
|
exe s:doautocmd('QuickFixCmdPre dispatch-make')
|
|
let request.directory = get(request, 'directory', getcwd())
|
|
if request.directory !=# getcwd()
|
|
let cwd = getcwd()
|
|
execute cd dispatch#fnameescape(request.directory)
|
|
endif
|
|
let request.expanded = dispatch#expand(request.command, a:count)
|
|
call extend(s:makes, [request])
|
|
let request.id = len(s:makes)
|
|
let s:files[request.file] = request
|
|
|
|
call writefile([], request.file)
|
|
|
|
if exists(':chistory')
|
|
let result = s:dispatch(request)
|
|
else
|
|
let result = 0
|
|
endif
|
|
if result
|
|
if !get(request, 'background')
|
|
call s:cgetfile(request, '')
|
|
if result is 2
|
|
exe 'botright copen' get(g:, 'dispatch_quickfix_height', '')
|
|
wincmd p
|
|
endif
|
|
endif
|
|
else
|
|
let request.handler = 'sync'
|
|
let after = 'call dispatch#complete('.request.id.',0)'
|
|
redraw!
|
|
let sp = dispatch#shellpipe(request.file)
|
|
let dest = request.file . '.complete'
|
|
if !exists(':chistory') && request.background
|
|
echohl WarningMsg
|
|
echo "Asynchronous dispatch requires Vim 8 or higher\n"
|
|
echohl NONE
|
|
endif
|
|
if &shellxquote ==# '"'
|
|
silent execute dispatch#bang(request.expanded . ' ' . sp . ' & echo %ERRORLEVEL% > ' . dest)
|
|
else
|
|
silent execute dispatch#bang('(' . request.expanded . '; echo ' .
|
|
\ dispatch#status_var() . ' > ' . dest . ')' . ' ' . sp)
|
|
endif
|
|
redraw!
|
|
endif
|
|
finally
|
|
exe s:doautocmd('QuickFixCmdPost dispatch-make')
|
|
let v:lnum = lnum
|
|
let &l:efm = efm
|
|
let &l:makeprg = makeprg
|
|
call s:set_current_compiler(compiler)
|
|
if exists('cwd')
|
|
execute cd dispatch#fnameescape(cwd)
|
|
endif
|
|
endtry
|
|
execute after
|
|
return ''
|
|
endfunction
|
|
|
|
" Section: :FocusDispatch
|
|
|
|
function! dispatch#focus(...) abort
|
|
let haslnum = a:0 && a:1 >= 0
|
|
if exists('b:Dispatch') && !haslnum
|
|
let [what, why] = [b:Dispatch, 'Buffer local focus']
|
|
elseif exists('w:Dispatch') && !haslnum
|
|
let [what, why] = [w:Dispatch, 'Window local focus']
|
|
elseif exists('t:Dispatch') && !haslnum
|
|
let [what, why] = [t:Dispatch, 'Tab local focus']
|
|
elseif exists('g:Dispatch') && !haslnum
|
|
let [what, why] = [g:Dispatch, 'Global focus']
|
|
elseif exists('b:dispatch')
|
|
let [what, why] = [b:dispatch, 'Buffer default']
|
|
else
|
|
let [what, why] = ['--', (len(&l:makeprg) ? 'Buffer' : 'Global') . ' default']
|
|
let default = 1
|
|
endif
|
|
if what =~# '^--\S\@!'
|
|
let args = s:default_args(what[2:-1], haslnum ? a:1 : exists('default') ? 0 : -1)
|
|
let what = len(args) ? '-- ' . args : args
|
|
endif
|
|
if haslnum
|
|
let [what, opts] = s:extract_opts(what)
|
|
if what =~# '^:'
|
|
let what = s:command_lnum(what, a:1)
|
|
else
|
|
let what = dispatch#expand(what, a:1)
|
|
endif
|
|
if a:0 > 1
|
|
return [what, extend(opts, a:2)]
|
|
endif
|
|
if has_key(opts, 'compiler') && opts.compiler !=# dispatch#compiler_for_program(what)
|
|
let what = '-compiler=' . opts.compiler . ' ' . what
|
|
endif
|
|
if has_key(opts, 'directory') && opts.directory !=# getcwd()
|
|
let what = '-dir=' .
|
|
\ s:escape_path(fnamemodify(opts.directory, ':~:.')) .
|
|
\ ' ' . what
|
|
endif
|
|
endif
|
|
if what =~# '^--\S\@!'
|
|
return [':Make' . what[2:-1], why]
|
|
elseif what =~# '^!'
|
|
return [':Start ' . what[1:-1], why]
|
|
elseif what =~# '^:\S'
|
|
return [what, why]
|
|
else
|
|
return [':Dispatch ' . what, why]
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#focus_command(bang, args, count, ...) abort
|
|
let [args, opts] = s:extract_opts(a:args)
|
|
if args =~# '^:[.$%]Dispatch$'
|
|
let [args, opts] = dispatch#focus(line(a:args[1]), opts)
|
|
elseif args =~# '^:\d*Dispatch$'
|
|
let [args, opts] = dispatch#focus(+matchstr(a:args, '\d\+'), opts)
|
|
elseif args =~# '^--\S\@!' && !has_key(opts, 'compiler')
|
|
let args = s:default_args(args, -1)
|
|
let args = s:build_make(&makeprg, args)
|
|
let args = dispatch#expand(args, 0)
|
|
else
|
|
let args = args =~# '^:' ? args : dispatch#expand(args, -1)
|
|
endif
|
|
let args = dispatch#escape(args)
|
|
if has_key(opts, 'compiler')
|
|
let args = '-compiler=' . opts.compiler . ' ' . args
|
|
endif
|
|
if has_key(opts, 'directory')
|
|
let args = dispatch#dir_opt(opts.directory) . args
|
|
endif
|
|
if empty(a:args) && a:bang
|
|
unlet! b:Dispatch w:Dispatch t:Dispatch g:Dispatch
|
|
let [what, why] = dispatch#focus(a:count)
|
|
echo 'Reverted default to ' . what
|
|
elseif empty(a:args)
|
|
let [what, why] = dispatch#focus(a:count)
|
|
echo a:count < 0 ? printf('%s is %s', why, what) : what
|
|
elseif a:count >= 0
|
|
let b:Dispatch = args
|
|
let [what, why] = dispatch#focus()
|
|
echo 'Set buffer local focus to ' . what
|
|
elseif a:0 && a:1 =~# '\<tab\>'
|
|
let t:Dispatch = args
|
|
unlet! b:Dispatch w:Dispatch
|
|
let [what, why] = dispatch#focus(a:count)
|
|
echo 'Set tab local focus to ' . what
|
|
elseif a:bang || a:0 && a:1 =~# '\<vert\>'
|
|
let w:Dispatch = args
|
|
unlet! b:Dispatch
|
|
let [what, why] = dispatch#focus(a:count)
|
|
echo 'Set window local focus to ' . what
|
|
else
|
|
let g:Dispatch = args
|
|
unlet! b:Dispatch w:Dispatch t:Dispatch
|
|
let [what, why] = dispatch#focus(a:count)
|
|
echo 'Set global focus to ' . what
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
function! dispatch#make_focus(count) abort
|
|
let cmd = s:make_focus(a:count)
|
|
if a:count >= 0
|
|
return dispatch#expand(cmd, a:count)
|
|
else
|
|
return cmd
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#spawn_focus(count) abort
|
|
if a:count < 0
|
|
return &shell
|
|
else
|
|
return dispatch#expand(s:focus(a:count), a:count)
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#start_focus(count) abort
|
|
let [command, opts] = s:parse_start('', a:count)
|
|
if a:count >= 0
|
|
let command = dispatch#expand(command, a:count)
|
|
endif
|
|
if empty(command)
|
|
let command = &shell
|
|
endif
|
|
if get(opts, 'wait', 'error') !=# 'error'
|
|
let command = '-wait=' . escape(opts.wait, '\ ') . ' ' . command
|
|
endif
|
|
if has_key(opts, 'title')
|
|
let command = '-title=' . escape(opts.title, '\ ') . ' ' . command
|
|
endif
|
|
if has_key(opts, 'directory') && opts.directory !=# getcwd()
|
|
let command = '-dir=' .
|
|
\ s:escape_path(fnamemodify(opts.directory, ':~:.')) . ' ' .
|
|
\ command
|
|
endif
|
|
return command
|
|
endfunction
|
|
|
|
" Section: Requests
|
|
|
|
function! s:file(request) abort
|
|
if type(a:request) == type('')
|
|
return a:request
|
|
elseif type(a:request) == type({})
|
|
return get(a:request, 'file', '')
|
|
else
|
|
return get(get(s:makes, a:request-1, {}), 'file', '')
|
|
endif
|
|
endfunction
|
|
|
|
function! s:request(request) abort
|
|
if type(a:request) == type({})
|
|
return a:request
|
|
elseif type(a:request) == type(0) && a:request >= 0
|
|
return get(s:makes, a:request-1, {})
|
|
elseif type(a:request) == type('') && a:request =~# '^\w\+/\d\+$'
|
|
let i = len(s:makes)
|
|
while i
|
|
let i -= 1
|
|
if get(s:makes[i], 'handler') . '/' . dispatch#pid(s:makes[i]) ==# a:request
|
|
return s:makes[i]
|
|
endif
|
|
endwhile
|
|
return {}
|
|
elseif type(a:request) == type('') && !empty(a:request)
|
|
let id = matchstr(a:request, '^:noautocmd cgetfile \zs.*\|^:Dispatch.*(\zs\w\+/\d\+\ze)$')
|
|
if empty(id)
|
|
return get(s:files, a:request, {})
|
|
else
|
|
return s:request(id)
|
|
endif
|
|
else
|
|
return {}
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#request(...) abort
|
|
return s:request(a:0 ? a:1 : 0)
|
|
endfunction
|
|
|
|
function! s:running(pid, ...) abort
|
|
if empty(a:pid)
|
|
return 0
|
|
elseif a:0 && exists('*dispatch#'.a:1.'#running')
|
|
return dispatch#{a:1}#running(a:pid)
|
|
elseif has('win32')
|
|
let tasklist_cmd = 'tasklist /fi "pid eq '.a:pid.'"'
|
|
if &shellxquote ==# '"'
|
|
let tasklist_cmd = substitute(tasklist_cmd, '"', "'", "g")
|
|
endif
|
|
return system(tasklist_cmd) =~# '==='
|
|
else
|
|
call system('kill -0 '.a:pid)
|
|
return !v:shell_error
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#pid(request) abort
|
|
let request = s:request(a:request)
|
|
if !has_key(request, 'pid')
|
|
if has('win32') && !executable('wmic')
|
|
let request.pid = 0
|
|
return 0
|
|
endif
|
|
let file = request.file
|
|
for i in range(50)
|
|
if getfsize(file.'.pid') > 0 || filereadable(file.'.complete')
|
|
break
|
|
endif
|
|
sleep 10m
|
|
endfor
|
|
try
|
|
let request.pid = +readfile(file.'.pid')[0]
|
|
catch
|
|
let request.pid = 0
|
|
endtry
|
|
endif
|
|
return request.pid
|
|
endfunction
|
|
|
|
function! dispatch#completed(request) abort
|
|
return get(s:request(a:request), 'completed', 0)
|
|
endfunction
|
|
|
|
function! dispatch#complete(file, ...) abort
|
|
if !dispatch#completed(a:file)
|
|
let request = s:request(a:file)
|
|
let request.completed = 1
|
|
try
|
|
let status = readfile(request.file . '.complete', 1)[0]
|
|
catch
|
|
let status = -1
|
|
call writefile([-1], request.file . '.complete')
|
|
endtry
|
|
if !a:0
|
|
silent doautocmd ShellCmdPost
|
|
endif
|
|
if !request.background && !get(request, 'aborted')
|
|
call s:cwindow(request, 0, status, '', 'make')
|
|
redraw!
|
|
endif
|
|
if has_key(request, 'aborted')
|
|
echohl DispatchAbortedMsg
|
|
let label = 'Aborted:'
|
|
elseif status > 0
|
|
echohl DispatchFailureMsg
|
|
let label = 'Failure:'
|
|
elseif status == 0
|
|
echohl DispatchSuccessMsg
|
|
let label = 'Success:'
|
|
else
|
|
echohl DispatchCompleteMsg
|
|
let label = 'Complete:'
|
|
endif
|
|
call s:echo_truncated(label . ' !', request.expanded . ' ' . s:postfix(request))
|
|
echohl NONE
|
|
if !a:0
|
|
checktime
|
|
endif
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
" Section: :AbortDispatch
|
|
|
|
function! dispatch#abort_command(bang, query, ...) abort
|
|
if !exists(':chistory')
|
|
return 'echoerr ' .string('Asynchronous dispatch requires Vim 8 or higher')
|
|
endif
|
|
let i = len(s:makes) - 1
|
|
while i >= 0
|
|
let request = s:makes[i]
|
|
if strpart(request.command, 0, len(a:query)) ==# a:query
|
|
break
|
|
endif
|
|
let i -= 1
|
|
endwhile
|
|
if i < 0
|
|
return 'echomsg '.string('No running dispatch found')
|
|
endif
|
|
let request.aborted = 1
|
|
let pid = dispatch#pid(request)
|
|
if !pid
|
|
return 'echoerr '.string('No pid file')
|
|
endif
|
|
if exists('*dispatch#'.get(request, 'handler').'#kill')
|
|
call dispatch#{request.handler}#kill(pid, a:bang)
|
|
elseif has('win32')
|
|
call system('taskkill /PID ' . (a:bang ? '/F ' : '') . pid)
|
|
else
|
|
call system('kill -' . (a:bang ? 'KILL' : 'HUP') . ' ' . pid)
|
|
endif
|
|
return 'call dispatch#complete('.request.id.')'
|
|
endfunction
|
|
|
|
" Section: Quickfix window
|
|
|
|
function! dispatch#copen(bang, mods, ...) abort
|
|
if empty(s:makes)
|
|
return 'echoerr ' . string('No dispatches yet')
|
|
endif
|
|
let request = dispatch#request()
|
|
if !dispatch#completed(request) && filereadable(request.file . '.complete')
|
|
let request.completed = 1
|
|
endif
|
|
call s:cwindow(request, a:bang, -2, a:mods ==# '<mods>' ? '' : a:mods, 'cgetfile')
|
|
endfunction
|
|
|
|
function! s:is_quickfix(...) abort
|
|
let nr = a:0 ? a:1 : winnr()
|
|
return getwinvar(nr, '&buftype') ==# 'quickfix' && empty(getloclist(nr))
|
|
endfunction
|
|
|
|
function! s:cgetfile(request, event, ...) abort
|
|
let request = s:request(a:request)
|
|
if !has_key(request, 'handler')
|
|
throw 'Bad request ' . string(request)
|
|
endif
|
|
let efm = &l:efm
|
|
let makeprg = &l:makeprg
|
|
let compiler = get(b:, 'current_compiler', '')
|
|
let cd = s:cd_command()
|
|
let dir = getcwd()
|
|
try
|
|
call s:set_current_compiler(get(request, 'compiler', ''))
|
|
exe cd dispatch#fnameescape(request.directory)
|
|
if a:0 && a:1
|
|
let &l:efm = '%+G%.%#'
|
|
else
|
|
let &l:efm = request.format
|
|
endif
|
|
let &l:makeprg = dispatch#escape(request.expanded)
|
|
let title = ':Dispatch '.dispatch#escape(request.expanded) . ' ' . s:postfix(request)
|
|
if len(a:event)
|
|
exe s:doautocmd('QuickFixCmdPre ' . a:event)
|
|
endif
|
|
if exists(':chistory') && get(getqflist({'title': 1}), 'title', '') ==# title
|
|
call setqflist([], 'r')
|
|
execute 'noautocmd caddfile' dispatch#fnameescape(request.file)
|
|
else
|
|
execute 'noautocmd cgetfile' dispatch#fnameescape(request.file)
|
|
endif
|
|
if exists(':chistory')
|
|
call setqflist([], 'r', {'title': title})
|
|
endif
|
|
if len(a:event)
|
|
exe s:doautocmd('QuickFixCmdPost ' . a:event)
|
|
endif
|
|
finally
|
|
exe cd dispatch#fnameescape(dir)
|
|
let &l:efm = efm
|
|
let &l:makeprg = makeprg
|
|
call s:set_current_compiler(compiler)
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:cwindow(request, all, copen, mods, event) abort
|
|
call s:cgetfile(a:request, a:event, a:all)
|
|
let height = get(g:, 'dispatch_quickfix_height', 10)
|
|
if height <= 0
|
|
return
|
|
endif
|
|
let was_qf = s:is_quickfix()
|
|
let mods = a:mods
|
|
if mods !~# 'aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright'
|
|
let mods = 'botright ' . mods
|
|
endif
|
|
if a:copen
|
|
execute (mods) 'copen' height
|
|
elseif !was_qf || winnr('$') > 1
|
|
execute (mods) 'cwindow' height
|
|
endif
|
|
if !was_qf && s:is_quickfix() && a:copen !=# -2
|
|
wincmd p
|
|
endif
|
|
endfunction
|
|
|
|
function! dispatch#quickfix_init() abort
|
|
let request = s:request(w:quickfix_title)
|
|
if empty(request)
|
|
return
|
|
endif
|
|
let w:quickfix_title = ':Dispatch ' . dispatch#escape(request.expanded) .
|
|
\ ' ' . s:postfix(request)
|
|
let b:dispatch = dispatch#dir_opt(request.directory) .
|
|
\ dispatch#escape(request.expanded)
|
|
if has_key(request, 'compiler')
|
|
let b:dispatch = '-compiler=' . request.compiler . ' ' . b:dispatch
|
|
endif
|
|
if has_key(request, 'program')
|
|
let &l:efm = request.format
|
|
let &l:makeprg = request.program
|
|
if has_key(request, 'compiler')
|
|
let b:current_compiler = request.compiler
|
|
else
|
|
unlet! b:current_compiler
|
|
endif
|
|
endif
|
|
exe 'lcd' dispatch#fnameescape(request.directory)
|
|
endfunction
|
|
|
|
" Section: End
|