" 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\@\|%\|#<\=\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 ==# '' ? '' : 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 ' . 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 ==# '' ? '' : 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, '\= 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 ==# '' ? '' : 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 =~# '\\\@ 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 =~# '\' 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 =~# '\' 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 ==# '' ? '' : 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