From b0dcd1bb84970aba3d37ac5e48c9705ac3bc13cf Mon Sep 17 00:00:00 2001 From: wsdjeg Date: Sun, 24 Jul 2022 16:26:12 +0800 Subject: [PATCH] chore(bundle): use bundle fugitive & dispatch --- autoload/SpaceVim/layers/git.vim | 4 +- bundle/vim-dispatch/.github/FUNDING.yml | 2 + bundle/vim-dispatch/.gitignore | 1 + bundle/vim-dispatch/CONTRIBUTING.markdown | 1 + bundle/vim-dispatch/README.markdown | 154 + bundle/vim-dispatch/autoload/dispatch.vim | 1399 +++ .../autoload/dispatch/headless.vim | 30 + .../vim-dispatch/autoload/dispatch/iterm.vim | 141 + bundle/vim-dispatch/autoload/dispatch/job.vim | 142 + .../vim-dispatch/autoload/dispatch/screen.vim | 44 + .../autoload/dispatch/terminal.vim | 84 + .../vim-dispatch/autoload/dispatch/tmux.vim | 130 + .../autoload/dispatch/windows.vim | 89 + bundle/vim-dispatch/autoload/dispatch/x11.vim | 56 + bundle/vim-dispatch/doc/dispatch.txt | 226 + bundle/vim-dispatch/plugin/dispatch.vim | 107 + bundle/vim-fugitive/autoload/fugitive.vim | 8118 +++++++++++++++++ bundle/vim-fugitive/doc/fugitive.txt | 730 ++ bundle/vim-fugitive/ftdetect/fugitive.vim | 1 + .../vim-fugitive/ftplugin/fugitiveblame.vim | 6 + bundle/vim-fugitive/plugin/fugitive.vim | 748 ++ bundle/vim-fugitive/syntax/fugitive.vim | 57 + bundle/vim-fugitive/syntax/fugitiveblame.vim | 7 + 23 files changed, 12275 insertions(+), 2 deletions(-) create mode 100644 bundle/vim-dispatch/.github/FUNDING.yml create mode 100644 bundle/vim-dispatch/.gitignore create mode 100644 bundle/vim-dispatch/CONTRIBUTING.markdown create mode 100644 bundle/vim-dispatch/README.markdown create mode 100644 bundle/vim-dispatch/autoload/dispatch.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/headless.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/iterm.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/job.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/screen.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/terminal.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/tmux.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/windows.vim create mode 100644 bundle/vim-dispatch/autoload/dispatch/x11.vim create mode 100644 bundle/vim-dispatch/doc/dispatch.txt create mode 100644 bundle/vim-dispatch/plugin/dispatch.vim create mode 100644 bundle/vim-fugitive/autoload/fugitive.vim create mode 100644 bundle/vim-fugitive/doc/fugitive.txt create mode 100644 bundle/vim-fugitive/ftdetect/fugitive.vim create mode 100644 bundle/vim-fugitive/ftplugin/fugitiveblame.vim create mode 100644 bundle/vim-fugitive/plugin/fugitive.vim create mode 100644 bundle/vim-fugitive/syntax/fugitive.vim create mode 100644 bundle/vim-fugitive/syntax/fugitiveblame.vim diff --git a/autoload/SpaceVim/layers/git.vim b/autoload/SpaceVim/layers/git.vim index 044945e77..ec66a7009 100644 --- a/autoload/SpaceVim/layers/git.vim +++ b/autoload/SpaceVim/layers/git.vim @@ -64,8 +64,8 @@ function! SpaceVim#layers#git#plugins() abort if s:git_plugin ==# 'gina' call add(plugins, [g:_spacevim_root_dir . 'bundle/gina.vim', { 'merged' : 0}]) elseif s:git_plugin ==# 'fugitive' - call add(plugins, ['tpope/vim-fugitive', { 'merged' : 0}]) - call add(plugins, ['tpope/vim-dispatch', { 'merged' : 0}]) + call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-fugitive', { 'merged' : 0}]) + call add(plugins, [g:_spacevim_root_dir . 'bundle/vim-dispatch', { 'merged' : 0}]) elseif s:git_plugin ==# 'gita' call add(plugins, ['lambdalisue/vim-gita', { 'on_cmd' : 'Gita'}]) else diff --git a/bundle/vim-dispatch/.github/FUNDING.yml b/bundle/vim-dispatch/.github/FUNDING.yml new file mode 100644 index 000000000..e2a49d110 --- /dev/null +++ b/bundle/vim-dispatch/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: tpope +custom: ["https://www.paypal.me/vimpope"] diff --git a/bundle/vim-dispatch/.gitignore b/bundle/vim-dispatch/.gitignore new file mode 100644 index 000000000..0a56e3fc5 --- /dev/null +++ b/bundle/vim-dispatch/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/bundle/vim-dispatch/CONTRIBUTING.markdown b/bundle/vim-dispatch/CONTRIBUTING.markdown new file mode 100644 index 000000000..b3f00951c --- /dev/null +++ b/bundle/vim-dispatch/CONTRIBUTING.markdown @@ -0,0 +1 @@ +See the [contribution guidelines for pathogen.vim](https://github.com/tpope/vim-pathogen/blob/master/CONTRIBUTING.markdown). diff --git a/bundle/vim-dispatch/README.markdown b/bundle/vim-dispatch/README.markdown new file mode 100644 index 000000000..376fe4532 --- /dev/null +++ b/bundle/vim-dispatch/README.markdown @@ -0,0 +1,154 @@ +# dispatch.vim + +Leverage the power of Vim's compiler plugins without being bound by +synchronicity. Kick off builds and test suites using one of several +asynchronous adapters (including tmux, screen, iTerm, Windows, and a headless +mode), and when the job completes, errors will be loaded and parsed +automatically. + +If that doesn't excite you, then perhaps [this video][teaser] will change your +mind. + +[teaser]: http://vimeo.com/tpope/vim-dispatch-teaser + +## Installation + +Install using your favorite package manager, or use Vim's built-in package +support: + + mkdir -p ~/.vim/pack/tpope/start + cd ~/.vim/pack/tpope/start + git clone https://tpope.io/vim/dispatch.git + vim -u NONE -c "helptags dispatch/doc" -c q + + +## Usage + +The core of Vim's compiler system is `:make`, a command similar to `:grep` +that runs a build tool and parses the resulting errors. The default build +tool is of course `make`, but you can switch it (and the associated error +parser) with `:compiler`. There are lots of built-in compilers, and they do +more than just compile things. Plus you can make your own. + +We'll start by looking at dispatch.vim's `:make` wrapper `:Make`, and then +move on to higher abstractions. + +### Foreground builds + +Kick off quick tasks with `:Make`. What happens next depends on which adapter +takes charge. + +* If you're in tmux, a small split will be opened at the bottom. +* On Windows, a minimized cmd.exe window is spawned. +* Otherwise, you get a plain old `:make` invocation. + +When the task completes, the window closes, the errors are loaded and parsed, +and the quickfix window automatically opens. At no point will your focus be +stolen. + +### Background builds + +Use `:Make!` for longer running tasks, like "run the entire test suite". + +* If you're in tmux or GNU screen, a new window is created in the background. +* Windows still spawns a minimized cmd.exe window. +* Otherwise, you get a headless invocation. You can't see it, but it's + running in the background. + +You won't be interrupted with a quickfix window for a background build. +Instead, open it at your leisure with `:Copen`. + +You can also use `:Copen` on a build that's still running to retrieve and +parse any errors that have already happened. + +### Compiler switching + +As hinted earlier, it's easy to switch compilers. + + :compiler rubyunit + :make test/models/user_test.rb + +Wait, that's still twice as many commands as it needs to be. Plus, it +requires you to make the leap from `testrb` (the executable) to `rubyunit` +(the compiler plugin). The `:Dispatch` command looks for a compiler for an +executable and sets it up automatically. + + :Dispatch testrb test/models/user_test.rb + +If no compiler plugin is found, `:Dispatch` simply captures all output. + + :Dispatch bundle install + +As with `:make`, you can use `%` expansions for the current filename. + + :Dispatch rspec % + +The `:Dispatch` command switches the compiler back afterwards, so you can pick +a primary compiler for `:Make`, and use `:Dispatch` for secondary concerns. + +### Default dispatch + +With no arguments, `:Dispatch` looks for a `b:dispatch` variable. You +can set it interactively, or in an autocommand: + + autocmd FileType java let b:dispatch = 'javac %' + +If no `b:dispatch` is found, it falls back to `:Make`. + +`:Dispatch` makes a great map. By default dispatch.vim provides `` ``` for +`:Dispatch`. You can find all default maps under `:h dispatch-maps`. + +### Focusing + +Use `:FocusDispatch` (or just `:Focus`) to temporarily, globally override the +default dispatch: + + :Focus rake spec:models + +Now every bare call to `:Dispatch` will call `:Dispatch rake spec:models`. +You'll be getting a lot of mileage out of that `:Dispatch` map. + +Use `:Focus!` to reset back to the default. + +### Spawning interactive processes + +Sometimes you just want to kick off a process without any output capturing or +error parsing. That's what `:Start` is for: + + :Start lein repl + +Unlike `:Make`, the new window will be in focus, since the idea is that you +want to interact with it. Use `:Start!` to launch it in the background. + +### Plugin support + +Using dispatch.vim from a plugin is a simple matter of checking for and using +`:Make` and `:Start` if they're available instead of `:make` and `:!`. Your +favorite plugin already supports it, assuming your favorite plugin is +[rails.vim](https://github.com/tpope/vim-rails). + +## FAQ + +> How can I have `:Dispatch!` or `:Make!` open the quickfix window on +> completion? + +Use `:Dispatch` or `:Make`. The entire point of the `!` is to run in the +background without interrupting you. + +> But that blocks Vim. + +Then the adapter in use doesn't support foreground builds. Adjust your setup. + +## Self-Promotion + +Like dispatch.vim? Follow the repository on +[GitHub](https://github.com/tpope/vim-dispatch) and vote for it on +[vim.org](http://www.vim.org/scripts/script.php?script_id=4504). And if +you're feeling especially charitable, follow [tpope](http://tpo.pe/) on +[Twitter](http://twitter.com/tpope) and +[GitHub](https://github.com/tpope). + +## License + +Copyright © Tim Pope. Distributed under the same terms as Vim itself. +See `:help license`. diff --git a/bundle/vim-dispatch/autoload/dispatch.vim b/bundle/vim-dispatch/autoload/dispatch.vim new file mode 100644 index 000000000..79ffbfdaf --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch.vim @@ -0,0 +1,1399 @@ +" 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 diff --git a/bundle/vim-dispatch/autoload/dispatch/headless.vim b/bundle/vim-dispatch/autoload/dispatch/headless.vim new file mode 100644 index 000000000..da9788169 --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/headless.vim @@ -0,0 +1,30 @@ +" dispatch.vim headless strategy + +if exists('g:autoloaded_dispatch_headless') + finish +endif +let g:autoloaded_dispatch_headless = 1 + +function! dispatch#headless#handle(request) abort + if !a:request.background || &shell !~# 'sh' + return 0 + endif + if a:request.action ==# 'make' + let command = dispatch#prepare_make(a:request) + elseif a:request.action ==# 'start' + let command = dispatch#prepare_start(a:request) + else + return 0 + endif + if &shellredir =~# '%s' + let redir = printf(&shellredir, '/dev/null') + else + let redir = &shellredir . ' ' . '/dev/null' + endif + call system(&shell.' '.&shellcmdflag.' '.shellescape(command).redir.' &') + return !v:shell_error +endfunction + +function! dispatch#headless#activate(pid) abort + return 0 +endfunction diff --git a/bundle/vim-dispatch/autoload/dispatch/iterm.vim b/bundle/vim-dispatch/autoload/dispatch/iterm.vim new file mode 100644 index 000000000..6144c3c26 --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/iterm.vim @@ -0,0 +1,141 @@ +" dispatch.vim iTerm strategy + +if exists('g:autoloaded_dispatch_iterm') + finish +endif +let g:autoloaded_dispatch_iterm = 1 + +function! dispatch#iterm#handle(request) abort + if $TERM_PROGRAM !=# 'iTerm.app' && !((has('gui_macvim') || has('gui_vimr')) && has('gui_running')) + return 0 + endif + if a:request.action ==# 'start' + return dispatch#iterm#spawn(dispatch#prepare_start(a:request), a:request, !a:request.background) + endif +endfunction + +function! dispatch#iterm#is_modern_version() abort + return s:osascript( + \ 'on modernversion(version)', + \ 'set olddelimiters to AppleScript''s text item delimiters', + \ 'set AppleScript''s text item delimiters to "."', + \ 'set thearray to every text item of version', + \ 'set AppleScript''s text item delimiters to olddelimiters', + \ 'set major to item 1 of thearray', + \ 'set minor to item 2 of thearray', + \ 'set veryminor to item 3 of thearray', + \ 'if major < 2 then return false', + \ 'if major > 2 then return true', + \ 'if minor < 9 then return false', + \ 'if minor > 9 then return true', + \ 'if veryminor < 20140903 then return false', + \ 'return true', + \ 'end modernversion', + \ 'tell application "iTerm"', + \ 'if not my modernversion(version) then error', + \ 'end tell') +endfunction + +function! dispatch#iterm#spawn(command, request, activate) abort + if dispatch#iterm#is_modern_version() + return dispatch#iterm#spawn3(a:command, a:request, a:activate) + else + return dispatch#iterm#spawn2(a:command, a:request, a:activate) + endif +endfunction + +function! dispatch#iterm#spawn2(command, request, activate) abort + let script = dispatch#isolate(a:request, [], + \ dispatch#set_title(a:request), a:command) + return s:osascript( + \ 'if application "iTerm" is not running', + \ 'error', + \ 'end if') && s:osascript( + \ 'tell application "iTerm"', + \ 'tell the current terminal', + \ 'set oldsession to the current session', + \ 'tell (make new session)', + \ 'set name to ' . s:escape(a:request.title), + \ 'set title to ' . s:escape(a:request.expanded), + \ 'exec command ' . s:escape(script), + \ a:request.background || !has('gui_running') ? 'select oldsession' : '', + \ 'end tell', + \ 'end tell', + \ a:activate ? 'activate' : '', + \ 'end tell') +endfunction + +function! dispatch#iterm#spawn3(command, request, activate) abort + let script = dispatch#isolate(a:request, [], + \ dispatch#set_title(a:request), a:command) + return s:osascript( + \ 'if application "iTerm" is not running', + \ 'error', + \ 'end if') && s:osascript( + \ 'tell application "iTerm"', + \ 'tell the current window', + \ 'set oldtab to the current tab', + \ 'set newtab to (create tab with default profile command ' . s:escape(script) . ')', + \ 'tell current session of newtab', + \ 'set name to ' . s:escape(a:request.title), + \ 'set title to ' . s:escape(a:request.expanded), + \ 'end tell', + \ a:request.background || !has('gui_running') ? 'select oldtab' : '', + \ 'end tell', + \ a:activate ? 'activate' : '', + \ 'end tell') +endfunction + +function! dispatch#iterm#activate(pid) abort + if dispatch#iterm#is_modern_version() + return dispatch#iterm#activate3(a:pid) + else + return dispatch#iterm#activate2(a:pid) + endif +endfunction + +function! dispatch#iterm#activate2(pid) abort + let tty = matchstr(system('ps -p '.a:pid), 'tty\S\+') + if !empty(tty) + return s:osascript( + \ 'if application "iTerm" is not running', + \ 'error', + \ 'end if') && s:osascript( + \ 'tell application "iTerm"', + \ 'activate', + \ 'tell the current terminal', + \ 'select session id "/dev/'.tty.'"', + \ 'end tell', + \ 'end tell') + endif +endfunction + +function! dispatch#iterm#activate3(pid) abort + let tty = matchstr(system('ps -p '.a:pid), 'tty\S\+') + if !empty(tty) + return s:osascript( + \ 'if application "iTerm" is not running', + \ 'error', + \ 'end if') && s:osascript( + \ 'tell application "iTerm"', + \ 'activate', + \ 'tell the current window', + \ 'repeat with atab in tabs', + \ 'repeat with asession in sessions', + \ 'if (tty) = ' . tty, + \ 'select atab', + \ 'end repeat', + \ 'end repeat', + \ 'end tell', + \ 'end tell') + endif +endfunction + +function! s:osascript(...) abort + call system('osascript'.join(map(copy(a:000), '" -e ".shellescape(v:val)'), '')) + return !v:shell_error +endfunction + +function! s:escape(string) abort + return '"'.escape(a:string, '"\').'"' +endfunction diff --git a/bundle/vim-dispatch/autoload/dispatch/job.vim b/bundle/vim-dispatch/autoload/dispatch/job.vim new file mode 100644 index 000000000..dd6e42b44 --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/job.vim @@ -0,0 +1,142 @@ +" dispatch.vim job strategy + +if exists('g:autoloaded_dispatch_job') + finish +endif +let g:autoloaded_dispatch_job = 1 + +if !exists('s:waiting') + let s:waiting = {} +endif + +let g:dispatch_waiting_jobs = s:waiting + +function! dispatch#job#handle(request) abort + if !get(g:, 'dispatch_experimental', 1) + return 0 + endif + if a:request.action !=# 'make' + return 0 + endif + if exists('*job_start') + if has('win32') + let cmd = &shell . ' ' . &shellcmdflag . ' ' . dispatch#windows#escape(a:request.expanded) + else + let cmd = split(&shell) + split(&shellcmdflag) + [a:request.expanded] + endif + let job = job_start(cmd, { + \ 'mode': 'raw', + \ 'callback': function('s:output'), + \ 'close_cb': function('s:closed'), + \ 'exit_cb': function('s:exit'), + \ }) + call ch_close_in(job) + let a:request.pid = job_info(job).process + let a:request.job = job + let ch_id = ch_info(job_info(job).channel).id + let s:waiting[ch_id] = {'request': a:request, 'output': ['']} + elseif exists('*jobpid') && exists('*jobstart') + let job_id = jobstart(a:request.expanded, { + \ 'on_stdout': function('s:output'), + \ 'on_stderr': function('s:output'), + \ 'on_exit': function('s:complete'), + \ }) + call chanclose(job_id, 'stdin') + let a:request.pid = jobpid(job_id) + let a:request.job = job_id + let s:waiting[job_id] = {'request': a:request, 'output': ['']} + else + return 0 + endif + let a:request.handler = 'job' + call writefile([], a:request.file) + call writefile([a:request.pid], a:request.file . '.pid') + return 2 +endfunction + +function! s:complete(id, status, ...) abort + let waiting = remove(s:waiting, a:id) + call writefile([a:status], waiting.request.file . '.complete') + call DispatchComplete(waiting.request.id) +endfunction + +function! s:closed(ch) abort + let id = ch_info(a:ch).id + if has_key(s:waiting, id) && has_key(s:waiting[id], 'exit_status') + call s:complete(id, s:waiting[id].exit_status) + endif +endfunction + +function! s:exit(job, status) abort + let ch = job_info(a:job).channel + let info = ch_info(ch) + if info.status ==# 'closed' + return s:complete(info.id, a:status) + else + let s:waiting[info.id].exit_status = a:status + endif +endfunction + +function! s:output(ch, output, ...) abort + if a:0 + " nvim + let waiting = s:waiting[a:ch] + let output = a:output + else + " vim + let waiting = s:waiting[ch_info(a:ch).id] + let output = split(a:output, "\n", 1) + endif + let request = waiting.request + call writefile(output, request.file, 'ab') + let waiting.output[-1] .= remove(output, 0) + call extend(waiting.output, output) + + if dispatch#request(get(getqflist({'title': 1}), 'title', '')) is# request && len(waiting.output) > 1 + let lefm = &l:efm + let gefm = &g:efm + let makeprg = &l:makeprg + let compiler = get(b:, 'current_compiler', '') + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd' + let dir = getcwd() + let modelines = &modelines + try + let &modelines = 0 + let b:current_compiler = get(request, 'compiler', '') + if empty(b:current_compiler) + unlet! b:current_compiler + endif + exe cd fnameescape(request.directory) + let &l:efm = request.format + let &g:efm = request.format + let &l:makeprg = request.command + caddexpr remove(waiting.output, 0, -2) + finally + let &modelines = modelines + exe cd fnameescape(dir) + let &l:efm = lefm + let &g:efm = gefm + let &l:makeprg = makeprg + if empty(compiler) + unlet! b:current_compiler + else + let b:current_compiler = compiler + endif + endtry + cbottom + endif +endfunction + +function! dispatch#job#kill(pid, force) abort + let request = dispatch#request('job/' . a:pid) + if exists('*job_stop') + return job_stop(request.job, a:force ? 'kill' : 'hup') + elseif exists('*jobstop') + call jobstop(request.job) + return 1 + endif +endfunction + +function! dispatch#job#activate(pid) abort + return 0 +endfunction diff --git a/bundle/vim-dispatch/autoload/dispatch/screen.vim b/bundle/vim-dispatch/autoload/dispatch/screen.vim new file mode 100644 index 000000000..9f360ca0c --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/screen.vim @@ -0,0 +1,44 @@ +" dispatch.vim GNU Screen strategy + +if exists('g:autoloaded_dispatch_screen') + finish +endif +let g:autoloaded_dispatch_screen = 1 + +function! dispatch#screen#handle(request) abort + if empty($STY) || !executable('screen') + return 0 + endif + if a:request.action ==# 'make' + if !get(a:request, 'background', 0) && !dispatch#has_callback() + return 0 + endif + return dispatch#screen#spawn(dispatch#prepare_make(a:request), a:request) + elseif a:request.action ==# 'start' + return dispatch#screen#spawn(dispatch#prepare_start(a:request), a:request) + endif +endfunction + +function! dispatch#screen#spawn(command, request) abort + let command = 'screen -ln -fn -t '.dispatch#shellescape(a:request.title) + \ . ' ' . &shell . ' ' . &shellcmdflag . ' ' + \ . shellescape('exec ' . dispatch#isolate(a:request, + \ ['STY', 'WINDOW'], dispatch#set_title(a:request), a:command)) + silent execute dispatch#bang(command) + if (a:request.background || a:request.action !=# 'start') && !has('gui_running') && !has('nvim') + silent !screen -X other + endif + return 1 +endfunction + +function! dispatch#screen#activate(pid) abort + let out = system('ps ewww -p '.a:pid) + if empty($STY) || stridx(out, 'STY='.$STY) < 0 + return 0 + endif + let window = matchstr(out, 'WINDOW=\zs\d\+') + if !empty(window) + silent execute '!screen -X select '.window + return !v:shell_error + endif +endfunction diff --git a/bundle/vim-dispatch/autoload/dispatch/terminal.vim b/bundle/vim-dispatch/autoload/dispatch/terminal.vim new file mode 100644 index 000000000..138508faa --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/terminal.vim @@ -0,0 +1,84 @@ +" dispatch.vim terminal strategy + +if exists('g:autoloaded_dispatch_terminal') + finish +endif +let g:autoloaded_dispatch_terminal = 1 + +if !exists('s:waiting') + let s:waiting = {} +endif + +function! dispatch#terminal#handle(request) abort + if !get(g:, 'dispatch_experimental', 1) + return 0 + endif + if !(has('terminal') || has('nvim')) || a:request.action !=# 'start' + return 0 + endif + + let a:request.handler = 'terminal' + let winid = win_getid() + + if has('nvim') + exe a:request.mods 'new' + let options = { + \ 'on_exit': function('s:exit', [a:request]), + \ } + let job = termopen(a:request.expanded, options) + let a:request.bufnr = bufnr('') + let a:request.pid = jobpid(job) + + if !a:request.background + startinsert + endif + else + let winid = win_getid() + exe a:request.mods 'split' + let options = { + \ 'exit_cb': function('s:exit', [a:request]), + \ 'term_name': '!' . a:request.expanded, + \ 'term_finish': 'open', + \ 'curwin': 1, + \ } + let a:request.bufnr = term_start([&shell, &shellcmdflag, a:request.expanded], options) + let job = term_getjob(a:request.bufnr) + let a:request.pid = job_info(job).process + endif + + if a:request.background + call win_gotoid(winid) + endif + + let s:waiting[a:request.pid] = a:request + call writefile([a:request.pid], a:request.file . '.pid') + + return 1 +endfunction + +function! s:exit(request, job, status, ...) abort + unlet! s:waiting[a:request.pid] + call writefile([a:status], a:request.file . '.complete') + + let wait = get(a:request, 'wait', 'error') + if wait ==# 'never' || (wait !=# 'always' && a:status == 0) + silent exec 'bdelete! ' . a:request.bufnr + endif +endfunction + +function! dispatch#terminal#activate(pid) abort + if has_key(s:waiting, a:pid) + let request = s:waiting[a:pid] + let pre = &switchbuf + + try + let &switchbuf = 'useopen,usetab' + silent exe request.mods 'sbuffer' request.bufnr + finally + let &switchbuf = pre + endtry + return 1 + endif + + return 0 +endfunction diff --git a/bundle/vim-dispatch/autoload/dispatch/tmux.vim b/bundle/vim-dispatch/autoload/dispatch/tmux.vim new file mode 100644 index 000000000..a28c3118e --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/tmux.vim @@ -0,0 +1,130 @@ +" dispatch.vim tmux strategy + +if exists('g:autoloaded_dispatch_tmux') + finish +endif +let g:autoloaded_dispatch_tmux = 1 + +let s:waiting = {} +let s:make_pane = tempname() + +function! dispatch#tmux#handle(request) abort + let session = get(g:, 'tmux_session', '') + if empty($TMUX) && empty(''.session) || !executable('tmux') + return 0 + endif + if !empty(system('tmux has-session -t '.shellescape(session))[0:-2]) + return '' + endif + + if a:request.action ==# 'make' + if !get(a:request, 'background', 0) && !dispatch#has_callback() && + \ !empty(''.session) && session !=# system('tmux display-message -p "#S"')[0:-2] + return 0 + endif + return dispatch#tmux#make(a:request) + elseif a:request.action ==# 'start' + let command = 'tmux new-window -P -t '.shellescape(session.':') + let command .= ' -n '.shellescape(a:request.title) + if a:request.background + let command .= ' -d' + endif + let command .= ' ' . shellescape('exec ' . dispatch#isolate( + \ a:request, ['TMUX', 'TMUX_PANE'], + \ dispatch#set_title(a:request), + \ dispatch#prepare_start(a:request))) + call system(command) + return 1 + endif +endfunction + +function! dispatch#tmux#make(request) abort + let pipepane = get(g:, 'dispatch_tmux_pipe_pane', 0) + \ && a:request.format !~# '%\\[er]' + let session = get(g:, 'tmux_session', '') + let script = dispatch#isolate(a:request, ['TMUX', 'TMUX_PANE'], + \ call('dispatch#prepare_make', [a:request] + + \ (pipepane ? [a:request.expanded . '; echo ' . dispatch#status_var() + \ . ' > ' . a:request.file . '.complete'] : []))) + + let title = shellescape(a:request.title) + let height = get(g:, 'dispatch_tmux_height', get(g:, 'dispatch_quickfix_height', 10)) + if get(a:request, 'background', 0) || (height <= 0 && dispatch#has_callback()) + let cmd = 'new-window -d -n '.title + elseif has('gui_running') || empty($TMUX) || (!empty(''.session) && session !=# system('tmux display-message -p "#S"')[0:-2]) + let cmd = 'new-window -n '.title + else + let cmd = 'split-window -l '.(height < 0 ? -height : height).' -d' + endif + + let cmd .= ' ' . dispatch#shellescape('-P', '-t', session.':', 'exec ' . script) + + let filter = 'sed' + let uname = system('uname')[0:-2] + if uname ==# 'Darwin' + let filter = '/usr/bin/sed -l' + elseif uname ==# 'Linux' + let filter .= ' -u' + endif + let filter .= " -e \"s/\r\r*$//\" -e \"s/.*\r//\"" + let filter .= " -e \"s/\e\\[K//g\" " + let filter .= " -e \"s/.*\e\\[2K\e\\[[01]G//g\"" + let filter .= " -e \"s/.*\e\\[?25h\e\\[0G//g\"" + let filter .= " -e \"s/\e\\[[0-9;]*m//g\"" + let filter .= " -e \"s/\017//g\"" + let filter .= " > " . a:request.file . "" + call system('tmux ' . cmd . '|tee ' . s:make_pane . + \ (pipepane ? '|xargs -I {} tmux pipe-pane -t {} '.shellescape(filter) : '')) + + let pane = s:pane_id(get(readfile(s:make_pane, '', 1), 0, '')) + if !empty(pane) + let s:waiting[pane] = a:request + return 1 + endif +endfunction + +function! s:pane_id(pane) abort + if a:pane =~# '\.\d\+$' + let [window, index] = split(a:pane, '\.\%(\d\+$\)\@=') + let out = system('tmux list-panes -F "#P #{pane_id}" -t '.shellescape(window)) + let id = matchstr("\n".out, '\n'.index.' \+\zs%\d\+') + else + let id = system('tmux list-panes -F "#{pane_id}" -t '.shellescape(a:pane))[0:-2] + endif + return id +endfunction + +function! dispatch#tmux#poll() abort + if empty(s:waiting) + return + endif + let panes = split(system('tmux list-panes -a -F "#{pane_id}"'), "\n") + for [pane, request] in items(s:waiting) + if index(panes, pane) < 0 + call remove(s:waiting, pane) + call dispatch#complete(request) + endif + endfor +endfunction + +function! dispatch#tmux#activate(pid) abort + let out = system('ps ewww -p '.a:pid) + let pane = matchstr(out, 'TMUX_PANE=\zs%\d\+') + if empty(pane) + return 0 + endif + let session = get(g:, 'tmux_session', '') + if !empty(session) + let session = ' -t '.shellescape(session) + endif + let panes = split(system('tmux list-panes -s -F "#{pane_id}"'.session), "\n") + if index(panes, pane) >= 0 + call system('tmux select-window -t '.pane.'; tmux select-pane -t '.pane) + return !v:shell_error + endif +endfunction + +augroup dispatch_tmux + autocmd! + autocmd VimResized * nested if !dispatch#has_callback() | call dispatch#tmux#poll() | endif +augroup END diff --git a/bundle/vim-dispatch/autoload/dispatch/windows.vim b/bundle/vim-dispatch/autoload/dispatch/windows.vim new file mode 100644 index 000000000..f5c098f7b --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/windows.vim @@ -0,0 +1,89 @@ +" dispatch.vim Windows strategy + +if exists('g:autoloaded_dispatch_windows') + finish +endif +let g:autoloaded_dispatch_windows = 1 + +function! dispatch#windows#escape(str) abort + if &shellxquote ==# '"' + return '"' . substitute(a:str, '"', '""', 'g') . '"' + else + let esc = exists('+shellxescape') ? &shellxescape : '"&|<>()@^' + return &shellxquote . + \ substitute(a:str, '['.esc.']', '^&', 'g') . + \ get({'(': ')', '"(': ')"'}, &shellxquote, &shellxquote) + endif +endfunction + +function! dispatch#windows#handle(request) abort + if !has('win32') || empty(v:servername) + return 0 + endif + if a:request.action ==# 'make' + return dispatch#windows#make(a:request) + elseif a:request.action ==# 'start' + return dispatch#windows#start(a:request) + endif +endfunction + +function! dispatch#windows#spawn(title, exec, background) abort + let extra = a:background ? ' /min' : '' + silent execute dispatch#bang('start /min cmd.exe /cstart ' . + \ '"' . substitute(a:title, '"', '', 'g') . '"' . extra . ' ' . + \ &shell . ' ' . &shellcmdflag . ' ' . dispatch#windows#escape(a:exec)) + return 1 +endfunction + +let s:pid = "wmic process where ^(Name='WMIC.exe' AND CommandLine LIKE '\\%\\%\\%TIME\\%\\%\\%'^) get ParentProcessId | more +1 > " + +function! dispatch#windows#make(request) abort + if &shellxquote ==# '"' + let exec = dispatch#prepare_make(a:request) + else + let pidfile = a:request.file.'.pid' + let exec = + \ s:pid . pidfile . + \ ' & ' . a:request.expanded . + \ ' > ' . a:request.file . ' 2>&1' . + \ ' & echo %ERRORLEVEL% > ' . a:request.file . '.complete' . + \ ' & ' . dispatch#callback(a:request) + endif + + return dispatch#windows#spawn(a:request.title, exec, 1) +endfunction + +function! dispatch#windows#start(request) abort + if &shellxquote ==# '"' + let exec = dispatch#prepare_start(a:request) + else + let pidfile = a:request.file.'.pid' + let pause = get({'always': ' & pause', 'never': ''}, + \ get(a:request, 'wait'), ' || pause') + let exec = + \ s:pid . pidfile . + \ ' & ' . a:request.expanded . + \ pause . + \ ' & cd . > ' . a:request.file.'.complete' . + \ ' & del ' . pidfile + endif + + return dispatch#windows#spawn(a:request.title, exec, a:request.background) +endfunction + +function! dispatch#windows#activate(pid) abort + let tasklist_cmd = 'tasklist /fi "pid eq '.a:pid.'"' + if &shellxquote ==# '"' + let tasklist_cmd = substitute(tasklist_cmd, '"', "'", "g") + endif + if system(tasklist_cmd) !~# '===' + return 0 + endif + + if !exists('s:activator') + let s:activator = tempname().'.vbs' + call writefile(['WScript.CreateObject("WScript.Shell").AppActivate(WScript.Arguments(0))'], s:activator) + endif + call system('cscript //nologo '.s:activator.' '.a:pid) + return !v:shell_error +endfunction diff --git a/bundle/vim-dispatch/autoload/dispatch/x11.vim b/bundle/vim-dispatch/autoload/dispatch/x11.vim new file mode 100644 index 000000000..0b2dff52a --- /dev/null +++ b/bundle/vim-dispatch/autoload/dispatch/x11.vim @@ -0,0 +1,56 @@ +" dispatch.vim X11 strategy + +if exists('g:autoloaded_dispatch_x11') + finish +endif +let g:autoloaded_dispatch_x11 = 1 + +function! s:windowid() + if executable('xprop') + return matchstr(system("xprop -root _NET_ACTIVE_WINDOW"), '0x\x\+') + endif +endf + +function! dispatch#x11#handle(request) abort + if $DISPLAY !~# '^:' + return 0 + endif + let windowid = s:windowid() + if get(a:request, 'background') && + \ (empty(windowid) || !executable('wmctrl')) + return 0 + endif + if exists('g:dispatch_terminal_exec') + let terminal = g:dispatch_terminal_exec + elseif !empty($TERMINAL) + let terminal = $TERMINAL . ' -e' + elseif executable('xterm') + let terminal = 'xterm -e' + else + return 0 + endif + if a:request.action ==# 'start' + return dispatch#x11#spawn(terminal, dispatch#prepare_start(a:request), a:request, windowid) + else + return 0 + endif +endfunction + +function! dispatch#x11#spawn(terminal, command, request, windowid) abort + let command = dispatch#set_title(a:request) . '; ' . a:command + if a:request.background + let command = 'wmctrl -i -a '. a:windowid . ';' . command + echom command + endif + call system(a:terminal . ' ' . dispatch#shellescape(&shell, &shellcmdflag, command). ' &') + return 1 +endfunction + +function! dispatch#x11#activate(pid) abort + let out = system('ps ewww -p '.a:pid) + let window = matchstr(out, 'WINDOWID=\zs\d\+') + if !empty(window) && executable('wmctrl') + call system('wmctrl -i -a '.window) + return !v:shell_error + endif +endfunction diff --git a/bundle/vim-dispatch/doc/dispatch.txt b/bundle/vim-dispatch/doc/dispatch.txt new file mode 100644 index 000000000..b31ac96cb --- /dev/null +++ b/bundle/vim-dispatch/doc/dispatch.txt @@ -0,0 +1,226 @@ +*dispatch.txt* Asynchronous build and test dispatcher + +Author: Tim Pope +Repo: https://github.com/tpope/vim-dispatch +License: Same terms as Vim itself (see |license|) + +INTRODUCTION *dispatch* + +Leverage the power of Vim's compiler system without being constrained by +synchronicity. + +COMMANDS *dispatch-commands* + + *dispatch-:Make* +:Make [arguments] Using the current |:compiler| settings, dispatch a + build in the foreground. Adapter strategies vary, but + the goal is visibility without stealing focus. When + the build is complete, load the results into the + |quickfix| list and call |:cwindow|. This command is + preferred for shorter tasks like "build this file." + +:Make! [arguments] Using the current compiler settings, dispatch a build + in the background. Use |:Copen| to load the results. + This command is preferred for longer tasks like "run + the entire test suite." + + *dispatch-:Copen* +:Copen Load the latest build into the quickfix list and open + it with |:copen|. You may call this before the + process is finished. + +:Copen! Load the latest build into the quickfix list using a + catch-all parser. This is useful when you can't tell + what went wrong. + + *dispatch-:Dispatch* +:Dispatch[!] [options] {program} [arguments] + Find a compiler plugin that sets 'makeprg' to + {program} and use its 'errorformat' to dispatch a + |:Make| for the given {program} and [arguments]. If + no compiler plugin is found, the generic format + %+I%.%# is used. + + :Dispatch picks a compiler by looking for either + CompilerSet makeprg={program}\ [arguments] or + CompilerSet makeprg={program} in compiler plugins. + To force a given {program} to use a given {compiler}, + create ~/.vim/after/compiler/{compiler}.vim and add to + it a line like the following: > + + " CompilerSet makeprg={program} +< + If you need more control, *g:dispatch_compilers* can + be set to a dictionary with commands for keys and + compiler plugins for values. Use an empty value to + skip the matched string and try again with the rest of + the command. +> + let g:dispatch_compilers = { + \ 'latex': 'tex', + \ 'bundle exec': ''} +< + You can optionally give one or more of the following + options before {program}: + + -compiler=... force use of a compiler plugin + -dir=... run command in given directory + +:Dispatch[!] [options] -- [arguments] + This is equivalent to |:Make| but accepts options. + + *b:dispatch* +:Dispatch[!] [options] Invoke |:Dispatch| with the options, program, and + arguments found in b:dispatch. In the quickfix + window, this reruns the shown dispatch. When no other + default is found, equivalent to |:Make|. + + *dispatch-:FocusDispatch* +:FocusDispatch [options] {program} [arguments] + Set a global default command for |:Dispatch| with no + arguments. Overrides |b:dispatch|. + +:FocusDispatch! [options] {program} [arguments] + Set a window local default command for |:Dispatch| + with no arguments. Overrides |b:dispatch| and the + global default. + +:FocusDispatch! Clear the global and window local defaults for + |:Dispatch|. + +:FocusDispatch Show the task that would run when calling |:Dispatch| + with no arguments. + +:0Dispatch[!] [options] Provide a range to |:Dispatch| skips any focused task + and forces |b:dispatch|. + + *dispatch-:AbortDispatch* +:AbortDispatch Abort the most recent build. + +:AbortDispatch {command} + Abort the most recent build of {command}. + + *dispatch-:Start* +:Start [options] {command} + Start a process in a new, focused window. If + {command} and the directory for execution match a + previous :Start invocation, focus it instead, if + possible. Options: + + -dir=... run command in given directory + -title=... short label for supported adapters + -wait=... when to display press enter prompt: + "always", "never", or "error" + +:Start! [options] {command} + Start a process in a new, unfocused window. If + {command} and the current working directory match + previous :Start invocation, do nothing. + + *b:start* +:Start[!] [options] With no arguments, |:Start| the options and command + given by |b:start|. If not set, falls back to + 'shell'. + + *dispatch-:Spawn* +:Spawn[!] [options] [command] + Like |:Start|, but always spawn a new process rather + than focusing an existing one. The default is always + to spawn a new 'shell'. + +:0Spawn! [options] + Provide a range to |:Spawn| to spawn |b:dispatch|. + Handy for using an interactive debugger. + +MAPS *dispatch-maps* + +The following convenience maps are provided. + +*m* |:Make| +*m* |:Make| +*m!* |:Make|! +*m?* Show 'makeprg' +*`* |:Dispatch| +*`* |:Dispatch| +*`!* |:Dispatch|! +*`?* |:FocusDispatch| +*'* |:Start| +*'* |:Start| +*'!* |:Start|! +*'?* Show |b:start| +*g'* |:Spawn| +*g'* |:Spawn| +*g'!* |:Spawn|! +*g'?* Show 'shell' + +These can be disabled with +> + let g:dispatch_no_maps = 1 +< +STRATEGIES *dispatch-strategies* + +Strategies are listed in order of precedence. The first available one is +used. Some strategies only provide for a subset of tasks. + +Disable :Make/:Dispatch or :Start/:Spawn using a given strategy by assigning +g:dispatch_no_strategyname_make or g:dispatch_no_strategyname_start, +respectively. For example, to prevent :Dispatch from ever using the Job +handler: +> + let g:dispatch_no_job_make = 1 +< +Except for Tmux, which uses |VimResized|, all strategies require either |job| +support plus a file system with FIFOs (basically everything but Windows) or +|clientserver| support to do foreground makes. + +Tmux ~ + +Foreground makes open in a small split at the bottom. The closure of the +pane triggers a |VimResized| event which loads the results into the quickfix +list. + +The tmux strategy can be used from the GUI as well. Either start Vim from +inside of tmux or assign g:tmux_session. This will use a new window for +foreground makes rather than a split. + +Job ~ + +Uses the |job| support in Vim 8 and Neovim to update the quickfix list in real +time. + +GNU Screen ~ + +A new window is always used, since splits in GNU Screen are awkward. + +Terminal ~ + +Uses the respective |:terminal| features of Vim and Neovim. + +Windows ~ + +You can use either the standard cmd.exe or a cygwin shell. Both foreground +and background |:Make| invocations are started minimized to prevent focus +stealing. + +iTerm ~ + +This strategy fires if you're in MacVim with at least one iTerm window open, +or if Vim is running in iTerm itself. Used only for |:Start|. + +X11 ~ + +Uses g:dispatch_terminal_exec, "$TERMINAL -e", or "xterm -e". Used only for +|:Start|. Background invocations require wmctrl to be is installed (which is +used to switch the focus back to Vim). + +Headless ~ + +Forks tasks into the background. It's working, you just can't see it. Don't +forget to check |:Copen|. The presence of this strategy means that |:Make!| +and |:Start!| will never block Vim. + +Synchronous ~ + +When all else fails, a vanilla |:make| or |:!| is performed. + + vim:tw=78:et:ft=help:norl: diff --git a/bundle/vim-dispatch/plugin/dispatch.vim b/bundle/vim-dispatch/plugin/dispatch.vim new file mode 100644 index 000000000..d639e9c61 --- /dev/null +++ b/bundle/vim-dispatch/plugin/dispatch.vim @@ -0,0 +1,107 @@ +" Location: plugin/dispatch.vim +" Maintainer: Tim Pope +" Version: 1.8 +" GetLatestVimScripts: 4504 1 :AutoInstall: dispatch.vim + +if exists("g:loaded_dispatch") || v:version < 700 || &compatible + finish +endif +let g:loaded_dispatch = 1 + +command! -bang -nargs=* -range=-1 -complete=customlist,dispatch#command_complete Dispatch + \ execute dispatch#compile_command(0, , + \ < 0 || == ? : 0, '') + +command! -bang -nargs=* -range=-1 -complete=customlist,dispatch#command_complete FocusDispatch + \ execute dispatch#focus_command(0, , + \ < 0 || == ? : 0, '') + +command! -bang -nargs=* -range=-1 -complete=customlist,dispatch#make_complete Make + \ execute dispatch#compile_command(0, '-- ' . , + \ < 0 || == ? : 0, '') + +command! -bang -nargs=* -range=-1 -complete=customlist,dispatch#command_complete Spawn + \ execute dispatch#spawn_command(0, , + \ < 0 || == ? : 0, '') + +command! -bang -nargs=* -range=-1 -complete=customlist,dispatch#command_complete Start + \ execute dispatch#start_command(0, , + \ < 0 || == ? : 0, '') + +command! -bang -bar Copen call dispatch#copen(0, '') + +command! -bang -bar -nargs=* AbortDispatch + \ execute dispatch#abort_command(0, ) + +function! s:map(mode, lhs, rhs, ...) abort + let flags = (a:0 ? a:1 : '') . (a:rhs =~# '^' ? '' : '