├── LICENSE.txt ├── README.rst ├── autoload ├── airline │ └── extensions │ │ └── asynccommand.vim ├── asynccommand.vim └── asynchandler.vim ├── doc └── asynccommand.txt └── plugin └── asynccommand.vim /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 David Briscoe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | AsyncCommand 2 | ==== 3 | 4 | AsyncCommand allows you to execute shell commands without waiting for them 5 | to complete. When the application terminates, its output can be loaded into 6 | a vim buffer. AsyncCommand is written to be compatible with Windows and 7 | Linux (tested on Win7 and Ubuntu 11.10). 8 | 9 | Currently three types of commands are supported: 10 | 11 | AsyncGrep 12 | grep for files and load results in quickfix 13 | AsyncShell 14 | run any program and load results in a split 15 | AsyncCscopeFindSymbol, AsyncCscopeFindCalls, AsyncCscopeFindX 16 | cscope commands 17 | AsyncMake 18 | run your makeprg with input arguments and load the results in quickfix 19 | 20 | For more information, see :help asynccommand 21 | -------------------------------------------------------------------------------- /autoload/airline/extensions/asynccommand.vim: -------------------------------------------------------------------------------- 1 | " Show AsyncCommand in vim-airline 2 | 3 | function! airline#extensions#asynccommand#init(ext) 4 | if !exists("g:loaded_asynccommand") || !g:loaded_asynccommand || exists("g:asynccommand_no_airline") 5 | return 6 | endif 7 | call a:ext.add_statusline_funcref(function('airline#extensions#asynccommand#apply')) 8 | endfunction 9 | 10 | function! airline#extensions#asynccommand#apply(...) 11 | let w:airline_section_y = get(w:, 'airline_section_y', g:airline_section_y) 12 | let w:airline_section_y = '%{asynccommand#statusline()} ' . g:airline_left_sep . w:airline_section_y 13 | endfunction 14 | -------------------------------------------------------------------------------- /autoload/asynccommand.vim: -------------------------------------------------------------------------------- 1 | " AsyncCommand implementation 2 | " 3 | " This is the core implementation of AsyncCommand. In general, these functions 4 | " should only be called from AsyncCommand handlers. For handlers to use in 5 | " Async commands, see asynccommand_handlers.vim 6 | 7 | if exists("g:loaded_autoload_asynccommand") || &cp || !has('clientserver') 8 | finish 9 | endif 10 | let g:loaded_autoload_asynccommand = 1 11 | 12 | let s:receivers = {} 13 | 14 | if !exists("g:asynccommand_statusline") 15 | let g:asynccommand_statusline = 'Pending:%d' 16 | endif 17 | 18 | if !exists("g:asynccommand_statusline_autohide") 19 | let g:asynccommand_statusline_autohide = 0 20 | endif 21 | 22 | " Basic background task running is different on each platform 23 | if has("win32") 24 | " Works in Windows (Win7 x64) 25 | function! s:Async_Impl(tool_cmd, vim_cmd) 26 | silent exec "!start /min cmd /c \"".a:tool_cmd." & ".a:vim_cmd." >NUL\"" 27 | endfunction 28 | function! s:Async_Single_Impl(tool_cmd) 29 | silent exec "!start /min cmd /c \"".a:tool_cmd."\"" 30 | endfunction 31 | let s:result_var = '\%ERRORLEVEL\%' 32 | else 33 | " Works in linux (Ubuntu 10.04) 34 | function! s:Async_Impl(tool_cmd, vim_cmd) 35 | silent exec "! ( ".a:tool_cmd." ; ".a:vim_cmd." >/dev/null ) &" 36 | endfunction 37 | function! s:Async_Single_Impl(tool_cmd) 38 | silent exec "! ".a:tool_cmd." &" 39 | endfunction 40 | let s:result_var = '$?' 41 | endif 42 | 43 | function! asynccommand#run(command, ...) 44 | " asynccommand#run(command, [function], [dict]) 45 | " - command is the shell command to execute asynchronously. 46 | " - [function] will be called when the command completes. Function 47 | " signature should be func(filename) or func(filename, dict) depending 48 | " on whether you provide dict. If function is not provided, nothing is 49 | " done on command completion. 50 | " - [dict] will be passed to function. 51 | if len(v:servername) == 0 52 | echohl Error 53 | echo "Error: AsyncCommand requires +clientserver and needs a value for v:servername." 54 | echo " See :help --servername" 55 | let server = 'x11' 56 | if has("win32") 57 | let server = 'w32' 58 | endif 59 | echo " and :help ". server ."-clientserver" 60 | echohl 61 | return "" 62 | endif 63 | if a:0 == 1 64 | let Fn = a:1 65 | let env = 0 66 | elseif a:0 == 2 67 | let Fn = a:1 68 | let env = a:2 69 | else 70 | " execute in background 71 | call s:Async_Single_Impl(a:command) 72 | if !has("gui_running") 73 | " In console vim, clear and redraw after running a background program 74 | " to remove screen clear from running external program. (Vim stops 75 | " being visible.) 76 | redraw! 77 | endif 78 | return "" 79 | endif 80 | 81 | " String together and execute. 82 | let temp_file = tempname() 83 | " Avoid backslashes to ensure they're not treated as escapes. Vim is smart 84 | " enough to find files with either slash direction. 85 | let temp_file = substitute(temp_file, '\\', '/', 'g') 86 | 87 | let shellredir = &shellredir 88 | if match( shellredir, '%s') == -1 89 | " ensure shellredir has a %s so printf works 90 | let shellredir .= '%s' 91 | endif 92 | 93 | " Grab output and error in case there's something we should see 94 | let tool_cmd = '(' . a:command . ') ' . printf(shellredir, temp_file) 95 | 96 | if type(Fn) == type({}) 97 | \ && has_key(Fn, 'get') 98 | \ && type(Fn.get) == type(function('asynccommand#run')) 99 | " Fn is a dictionary and Fn.get is the function we should execute on 100 | " completion. 101 | let s:receivers[temp_file] = {'func': Fn.get, 'dict': Fn} 102 | else 103 | let s:receivers[temp_file] = {'func': Fn, 'dict': env} 104 | endif 105 | 106 | " Use the configured command if available, otherwise try to guess what's 107 | " running. 108 | if exists('g:asynccommand_prg') 109 | let prg = g:asynccommand_prg 110 | elseif has("gui_running") && has("gui_macvim") && executable('mvim') 111 | let prg = "mvim" 112 | elseif has("gui_running") && executable('gvim') 113 | let prg = "gvim" 114 | elseif executable('vim') 115 | let prg = "vim" 116 | else 117 | echohl Error 118 | echo "AsyncCommand failed to find Vim: Neither vim, gvim, nor mvim are in your path." 119 | echo "Update your PATH or set g:asynccommand_prg to your vim executable." 120 | echohl 121 | return "" 122 | endif 123 | 124 | let vim_cmd = prg . " --servername " . v:servername . " --remote-expr \"AsyncCommandDone('" . temp_file . "', " . s:result_var . ")\" " 125 | 126 | call s:Async_Impl(tool_cmd, vim_cmd) 127 | if !has("gui_running") 128 | " In console vim, clear and redraw after running a background program 129 | " to remove screen clear from running external program. (Vim stops 130 | " being visible.) 131 | redraw! 132 | endif 133 | return "" 134 | endfunction 135 | 136 | function! asynccommand#done(temp_file_name, return_code) 137 | " Called on completion of the task 138 | let r = s:receivers[a:temp_file_name] 139 | if type(r.dict) == type({}) 140 | let r.dict.return_code = a:return_code 141 | call call(r.func, [a:temp_file_name], r.dict) 142 | else 143 | call call(r.func, [a:temp_file_name]) 144 | endif 145 | unlet s:receivers[a:temp_file_name] 146 | call delete(a:temp_file_name) 147 | endfunction 148 | 149 | function! asynccommand#tab_restore(env) 150 | " Open the tab the command was run from, load the results there, and then 151 | " return to the current tab. This ensures that our results are visible where 152 | " they'll be useful, but we don't jump between tabs. 153 | " 154 | " We also use lazyredraw to ensure that the user doesn't see their cursor 155 | " jumping around. 156 | " 157 | " TODO: We probably want notification when the task completes since we won't 158 | " see it from our current tab. 159 | let env = { 160 | \ 'tab': tabpagenr(), 161 | \ 'env': a:env, 162 | \ } 163 | function env.get(temp_file_name) dict 164 | let lazyredraw = &lazyredraw 165 | let &lazyredraw = 1 166 | let current_tab = tabpagenr() 167 | try 168 | silent! exec "tabnext " . self.tab 169 | " pass our return code on to our sub-function 170 | let self.env.return_code = self.return_code 171 | " self.env.get is not this function -- it's the function passed to 172 | " tab_restore() 173 | call call(self.env.get, [a:temp_file_name], self.env) 174 | silent! exe "tabnext " . current_tab 175 | redraw 176 | finally 177 | let &lazyredraw = lazyredraw 178 | endtry 179 | endfunction 180 | return env 181 | endfunction 182 | 183 | function! asynccommand#statusline() 184 | let n_pending_jobs = len(s:receivers) 185 | if g:asynccommand_statusline_autohide && n_pending_jobs == 0 186 | return '' 187 | endif 188 | 189 | return printf(g:asynccommand_statusline, n_pending_jobs) 190 | endfunction 191 | 192 | " Return a string with a header and list of the output files and title (if 193 | " available) for all pending commands. 194 | function! s:create_pending_listing() 195 | let out = "Pending Output Files\n" 196 | let out .= "====================\n" 197 | let out .= "Commands (identified by window title if available) are writing to these files.\n" 198 | let out .= "\n" 199 | for [fname, handler] in items(s:receivers) 200 | let out .= fname 201 | try 202 | let id = handler.dict.env.title 203 | catch /Key not present in Dictionary/ 204 | let id = 'untitled' 205 | endtry 206 | let out .= printf("\t- %s\n", id) 207 | endfor 208 | return out 209 | endfunction 210 | 211 | function! asynccommand#open_pending() 212 | silent pedit _AsyncPending_ 213 | wincmd P 214 | setlocal modifiable 215 | 1,$delete 216 | 217 | silent 0put =s:create_pending_listing() 218 | 219 | " Map q for easy quit if not already mapped. 220 | silent! nnoremap q :bdelete 221 | 222 | " Prevent use of the buffer as a file (to ensure if a file matching this 223 | " name exists, we don't touch it). We don't support cancelling so 224 | " modification is meaningless. 225 | setlocal buftype=nofile 226 | setlocal bufhidden=wipe 227 | setlocal noswapfile 228 | setlocal nobuflisted 229 | setlocal nomodifiable 230 | setlocal readonly 231 | endfunction 232 | 233 | " vi: et sw=4 ts=4 234 | -------------------------------------------------------------------------------- /autoload/asynchandler.vim: -------------------------------------------------------------------------------- 1 | " AsyncCommand Handlers 2 | " 3 | " Various handlers for processing Async results. When an AsyncCommand 4 | " completes, the function provided to asynccommand#run is called. You can use 5 | " these functions, or use them as a guide to define your own handlers. 6 | 7 | if exists("g:loaded_autoload_asynchandler") || &cp || !has('clientserver') 8 | " requires nocompatible and clientserver 9 | " also, don't double load 10 | finish 11 | endif 12 | let g:loaded_autoload_asynchandler = 1 13 | 14 | function! asynchandler#rename(path) 15 | " Move the output file to somewhere permanent. 16 | let env = {'path': a:path} 17 | function env.get(temp_file_name) dict 18 | silent! let ret = rename(a:temp_file_name, self.path) 19 | if ret != 0 20 | echohl WarningMsg 21 | echo "Async rename failed: " . escape(self.path) 22 | echohl NONE 23 | endif 24 | endfunction 25 | return env 26 | endfunction 27 | 28 | " Convenience functions for loading the result in the quickfix/locationlist 29 | " or adding to the window's contents. 30 | " 31 | " format - The errorformat applied to the quickfix results. 32 | " title - The window title expects a %d to list the number of results. See 33 | " w:quickfix_title 34 | " 35 | function! asynchandler#quickfix(format, title) 36 | return asynchandler#qf("cgetfile", "quickfix", a:format, a:title) 37 | endfunction 38 | 39 | function! asynchandler#quickfix_add(format, title) 40 | return asynchandler#qf("caddfile", "quickfix", a:format, a:title) 41 | endfunction 42 | 43 | function! asynchandler#loclist(format, title) 44 | return asynchandler#qf("lgetfile", "location-list", a:format, a:title) 45 | endfunction 46 | 47 | function! asynchandler#loclist_add(format, title) 48 | return asynchandler#qf("laddfile", "location-list", a:format, a:title) 49 | endfunction 50 | 51 | 52 | function! asynchandler#qf(command, list, format, title) 53 | " Load the result in the quickfix/locationlist 54 | let env = { 55 | \ 'title': a:title, 56 | \ 'command': a:command, 57 | \ 'list': a:list, 58 | \ 'format': a:format, 59 | \ 'mode': a:list == 'quickfix' ? 'c' : 'l', 60 | \ } 61 | function env.get(temp_file_name) dict 62 | let errorformat=&errorformat 63 | let &errorformat=self.format 64 | try 65 | exe 'botright ' . self.mode . "open" 66 | let cmd = self.command . ' ' . a:temp_file_name 67 | exe cmd 68 | if type(self.title) == type("") && self.title != "" 69 | let w:quickfix_title = printf(self.title, len(self.mode == 'c' ? getqflist() : getloclist())) 70 | endif 71 | silent! wincmd p 72 | finally 73 | let &errorformat = errorformat 74 | endtry 75 | endfunction 76 | return asynccommand#tab_restore(env) 77 | endfunction 78 | 79 | function! asynchandler#split(is_const_preview) 80 | " Load the result in a split 81 | let env = { 82 | \ 'is_const_preview' : a:is_const_preview 83 | \ } 84 | function env.get(temp_file_name) dict 85 | if self.is_const_preview 86 | exec "pedit " . a:temp_file_name 87 | wincmd P 88 | setlocal nomodifiable 89 | silent! nnoremap q :bdelete 90 | else 91 | exec "split " . a:temp_file_name 92 | endif 93 | silent! wincmd p 94 | endfunction 95 | return asynccommand#tab_restore(env) 96 | endfunction 97 | 98 | " vi: et sw=4 ts=4 99 | -------------------------------------------------------------------------------- /doc/asynccommand.txt: -------------------------------------------------------------------------------- 1 | *asynccommand.txt* Run commands without waiting. 2 | 3 | ============================================================================== 4 | 5 | *asynccommand* 6 | Introduction~ 7 | 8 | AsyncCommand allows you to execute shell commands without waiting for them 9 | to complete. When the application terminates, its output can be loaded into 10 | a vim buffer. 11 | 12 | You can define your own commands by following the same format: 13 | Define a launch function that passes the external command and result handler 14 | (like asynchandler#quickfix) to asynccommand#run. You can use the handlers in 15 | autoload/asynchandler.vim or define your own. 16 | 17 | *asynccommand-requirements* 18 | AsyncCommand Requirements~ 19 | 20 | AsyncCommand needs vim compiled with |+clientserver|. Check your vim version 21 | with |:version|. Vim must have a |v:servername| (see |--servername|). On unix, 22 | an X11 server is required to use |+clientserver|. See |x11-clientserver| and 23 | |w32-clientserver|. 24 | 25 | *asynccommand-commands* 26 | Commands~ 27 | 28 | :AsyncPending *:AsyncPending* 29 | Open the preview window with a listing of the output files for 30 | the currently active asynccommands. Run again to refresh the 31 | listing. Commands like AsyncCommand that don't have a 32 | completion handler are not included. 33 | 34 | :AsyncCommand {cmd} *:AsyncCommand* 35 | Execute shell {cmd} in the background. 36 | 37 | :AsyncShell {cmd} *:AsyncShell* 38 | Execute shell {cmd} in the background. When it completes, open 39 | the result in a split window. 40 | Completes shell commands. 41 | 42 | :AsyncGrep [arguments] *:AsyncGrep* 43 | Run a grep command in the background using 'grepprg'. On 44 | completion, the results are loaded in the quickfix. The window 45 | is titled with the query. 46 | Completes filenames. 47 | 48 | :AsyncMake [arguments] *:AsyncMake* 49 | Run a build in the background using 'makeprg'. On completion, 50 | the build output is loaded in the quickfix. The window is 51 | titled with the build target. 52 | 53 | :AsyncCscopeFindX {querytype} {name} *:AsyncCscopeFindX* 54 | Run a cscope search in the background using 'cscopeprg'. On 55 | completion, the results are loaded in the quickfix. The window 56 | is titled with the query. 57 | Usage is the same as the |cscope-find| command: Expects the 58 | character describing the query type and the query name. For 59 | example, to search for the functions called by main(): 60 | > 61 | :AsyncCscopeFindX d main 62 | < 63 | Completes tags. 64 | 65 | :AsyncCscopeFindSymbol {name} *:AsyncCscopeFindSymbol* 66 | Same as AsyncCscopeFindSymbol but automatically prepends 's'. 67 | Just expects a symbol name. 68 | 69 | :AsyncCscopeFindCalls {name} *:AsyncCscopeFindCalls* 70 | Same as AsyncCscopeFindSymbol but automatically prepends 'c'. 71 | Just expects a function name. 72 | 73 | *asynccommand-functions* 74 | Functions~ 75 | 76 | asynccommand#run({command} [, {function}[, {dict}]]) *asynccommand#run* 77 | Execute a shell {command} in the background. 78 | After the command is executed {function} can will be called. 79 | {function} can be either: 80 | |Funcref| 81 | A Function ref 82 | |Dictionary| 83 | A dictionary with a |Dictionary-function| named key 84 | named 'get'. 85 | 86 | The {dict} argument can be supplied to be used to call with 87 | the {function}. This the same as using the third argument to 88 | |call()|. 89 | 90 | The {function} will be of the following form: 91 | some_function(temp_name) 92 | 93 | temp_name will be the name of temporary file used to capture 94 | the output of the {command}. 95 | 96 | Examples: > 97 | 98 | command! GenerateCscopeFiles call GenerateCscopeFiles() 99 | function! GenerateCscopeFiles() 100 | let cmd = 'find . -name "*.java"' 101 | let env = asynchandler#rename('cscope.files') 102 | call asynccommand#run(cmd, env) 103 | endfunction 104 | 105 | command! -nargs=+ Ack call AckFunc() 106 | function! AckFunc(query) 107 | let cmd = 'ack -H --nocolor --nogroup --column ' 108 | let cmd .= a:query 109 | let efm = "%f:%l:%c:%m" 110 | let title = "[Found: %s] Ack" 111 | let env = asynchandler#quickfix(efm, title) 112 | call asynccommand#run(cmd, env) 113 | endfunction 114 | 115 | command! -nargs=1 AntBuild call AntBuildFunc() 116 | function! AntBuildFunc(target) 117 | let cmd = 'ant ' 118 | let cmd .= a:target 119 | let env = {} 120 | function env.get(temp_file) dict 121 | let h = '' 122 | if self.return_code == 0 123 | " use tiny split window height on success 124 | let h = 1 125 | endif 126 | " open the file in a split 127 | exec h . "split " . a:temp_file 128 | " remove boring build output 129 | %s/^\[xslt\].*$/ 130 | " go back to the previous window 131 | wincmd p 132 | endfunction 133 | 134 | " tab_restore prevents interruption when the task completes. 135 | " All provided asynchandlers already use tab_restore. 136 | call asynccommand#run(cmd, asynccommand#tab_restore(env)) 137 | endfunction 138 | < 139 | 140 | *asynccommand-callbacks* *asynccommand-handlers* 141 | Common Callbacks~ 142 | 143 | asynchandler#rename({path}) *asynchandler#rename* 144 | Rename the temp_file to {path} after the background command 145 | has been executed. 146 | 147 | asynchandler#quickfix({errorformat}, {title}) *asynchandler#quickfix* 148 | Open the results in |quickfix-window| using {errorformat} as 149 | the 'errorformat'. A {title} can be provided to give the 150 | quickfix-window, similar to |w:quickfix_title|. Put %s into 151 | the {title} to have it replaced with the number of results. 152 | 153 | asynchandler#quickfix_add({errorformat}, {title}) *asynchandler#quickfix_add* 154 | Same as |asynchandler#quickfix| but results will be added. 155 | 156 | asynchandler#loclist({errorformat}, {title}) *asynchandler#loclist* 157 | Same as |asynchandler#quickfix| but use the |location-list|. 158 | 159 | asynchandler#loclist_add({errorformat}, {title}) *asynchandler#loclist_add* 160 | Same as |asynchandler#quickfix_add| but use the 161 | |location-list|. 162 | 163 | asynchandler#split(is_const_preview) *asynchandler#split* 164 | Open the results of the asynchronous shell command in a new 165 | split. Pass is_const_preview=1 to setup buffer as immutable 166 | preview window. It will have q mapped to close. 167 | 168 | ============================================================================== 169 | 170 | *asynccommand-configuration* 171 | Configuration~ 172 | 173 | g:asynccommand_prg *g:asynccommand_prg* 174 | Override the default path to vim to be used by AsyncCommand to 175 | capture data from backgrounded commands. AsyncCommand is 176 | compatible with |macvim| and will use mvim if it is available. 177 | See |mvim| for setup. 178 | 179 | statusline *asynccommand-statusline* 180 | Add %{asynccommand#statusline()} to your 'statusline' to show 181 | the pending command count. 182 | 183 | g:asynccommand_statusline *g:asynccommand_statusline* 184 | Default: 'Pending:%d' 185 | A |printf| format string that defines how to display the 186 | number of commands running in the background for 187 | |asynccommand-statusline|. Must include a "%d" where you want 188 | the count of pending commands. 189 | 190 | g:asynccommand_statusline_autohide *g:asynccommand_statusline_autohide* 191 | Default: 0 192 | When enabled, uses no text for |asynccommand-statusline| when 193 | there are no pending commands. May look strange if your 194 | 'statusline' adds separators between segments (e.g., the 195 | |Powerline| plugin). 196 | 197 | shellredir *asynccommand-shellredir* 198 | You may find it useful to change vim's |shellredir| setting to 199 | use tee (which writes to a file, but also to stdout). On 200 | MS-Windows, this will allow you to see the output of the 201 | command in the opened command window. 202 | > 203 | " redirect to both standard output and files 204 | set shellredir=2>&1\|tee\ %s 205 | < 206 | 207 | 208 | ============================================================================== 209 | 210 | vim:tw=78:ts=8:ft=help:norl: 211 | -------------------------------------------------------------------------------- /plugin/asynccommand.vim: -------------------------------------------------------------------------------- 1 | " AsyncCommand 2 | " Execute commands and have them send their result to vim when they 3 | " complete. 4 | " Author: pydave 5 | " Influences: http://vim.wikia.com/wiki/VimTip1549 (Getting results back into Vim) 6 | " Contributors: Peter Rincker 7 | " 8 | " To define new commands: 9 | " Define a launch function that passes the external command and result handler 10 | " (like asynchandler#quickfix) to asynccommand#run. 11 | " You can use the handlers in autoload/asynchandler.vim or define your own. 12 | " 13 | " Note that cscope functions require these variables: 14 | " let g:cscope_database = **full path to cscope database file** 15 | " let g:cscope_relative_path = **folder containing cscope database file** 16 | " These variables are set by tagfilehelpers.vim 17 | 18 | if exists('g:loaded_asynccommand') 19 | finish 20 | endif 21 | let g:loaded_asynccommand = 0 22 | 23 | if &cp 24 | echoerr "AsyncCommand cannot run in vi-compatible mode (see :help 'cp')." 25 | finish 26 | elseif !has('clientserver') 27 | echoerr "AsyncCommand requires vim compiled with +clientserver (see :help +clientserver)" 28 | finish 29 | endif 30 | let g:loaded_asynccommand = 1 31 | 32 | let s:save_cpo = &cpo 33 | set cpo&vim 34 | 35 | function! AsyncCommandDone(file, return_code) 36 | call asynccommand#done(a:file, a:return_code) 37 | return "" 38 | endfunction 39 | 40 | command! AsyncPending call asynccommand#open_pending() 41 | 42 | command! -nargs=+ -complete=shellcmd AsyncCommand call asynccommand#run() 43 | command! -nargs=+ -complete=file AsyncGrep call s:AsyncGrep() 44 | " AsyncShell! for immutable preview window. 45 | command! -nargs=+ -complete=file -complete=shellcmd -bang AsyncShell call s:AsyncShell(0, ) 46 | command! -nargs=* AsyncMake call s:AsyncMake() 47 | 48 | command! -nargs=1 -complete=tag AsyncCscopeFindSymbol call s:AsyncCscopeFindX('s '. ) 49 | command! -nargs=1 -complete=tag AsyncCscopeFindCalls call s:AsyncCscopeFindX('c '. ) 50 | command! -nargs=1 -complete=tag AsyncCscopeFindX call s:AsyncCscopeFindX() 51 | 52 | 53 | if (! exists("no_plugin_maps") || ! no_plugin_maps) && 54 | \ (! exists("no_asynccommand_maps") || ! no_asynccommand_maps) 55 | nnoremap :AsyncCscopeFindSymbol =expand('') 56 | endif 57 | 58 | """""""""""""""""""""" 59 | " Actual implementations 60 | 61 | " Fill in $* pattern for prg commands 62 | " grepprg and makeprg both allow an optional placeholder '$*' to specify where 63 | " arguments are included. If omitted, append a space and the arguments to the 64 | " end of the prg command. 65 | function! s:InsertArgumentsIntoPrgCmd(prg_command, arguments) 66 | let placeholder_re = '\V$*' 67 | let cmd = a:prg_command 68 | if match(cmd, placeholder_re) < 0 69 | let cmd .= ' $*' 70 | endif 71 | 72 | " Ensure user's escape sequences are passed to prg_command. Don't use 73 | " shellescape: only want user's escapes passed along to work like :grep, 74 | " etc... 75 | let args = escape(a:arguments, '\') 76 | 77 | return substitute(cmd, placeholder_re, args, 'g') 78 | endf 79 | 80 | 81 | " Grep 82 | " - open result in quickfix 83 | function! s:AsyncGrep(query) 84 | let grep_cmd = s:InsertArgumentsIntoPrgCmd(&grepprg, a:query) 85 | call asynccommand#run(grep_cmd, asynchandler#quickfix(&grepformat, '[Found: %d] grep ' . a:query)) 86 | endfunction 87 | 88 | " Shell commands 89 | " - open result in a split 90 | " - is_const_preview=false to allow editing and skip mappings 91 | function! s:AsyncShell(is_const_preview, command) 92 | call asynccommand#run(a:command, asynchandler#split(a:is_const_preview)) 93 | endfunction 94 | 95 | " Make 96 | " - uses the current make program 97 | " - optional parameter for make target(s) 98 | function! s:AsyncMake(target) 99 | let make_cmd = s:InsertArgumentsIntoPrgCmd(&makeprg, a:target) 100 | let title = 'Make [%d]: ' 101 | if a:target == '' 102 | let title .= "(default)" 103 | else 104 | let title .= a:target 105 | endif 106 | call asynccommand#run(make_cmd, asynchandler#quickfix(&errorformat, title)) 107 | endfunction 108 | 109 | 110 | " Cscope find 111 | " - open result in quickfix 112 | " Map the commands from `:cscope help` to the numbers expected by `cscope -l` 113 | " s > 0 Find this C symbol 114 | " g > 1 Find this definition 115 | " d > 2 Find functions called by this function 116 | " c > 3 Find functions calling this function 117 | " t > 4 Find assignments to 118 | " e > 6 Find this egrep pattern 119 | " f > 7 Find this file 120 | " i > 8 Find files #including this file 121 | let s:type_char_to_num = { 122 | \ 's': 0, 123 | \ 'g': 1, 124 | \ 'd': 2, 125 | \ 'c': 3, 126 | \ 't': 4, 127 | \ 'e': 6, 128 | \ 'f': 7, 129 | \ 'i': 8, 130 | \ } 131 | let s:num_to_description = { 132 | \ 0: 'C symbol', 133 | \ 1: 'Definition', 134 | \ 2: 'Functions called by this function', 135 | \ 3: 'Functions calling this function', 136 | \ 4: 'Assignments to', 137 | \ 6: 'Egrep pattern', 138 | \ 7: 'File', 139 | \ 8: '#including this file', 140 | \ } 141 | " Wrap AsyncCscopeFind to make it easier to do cscope searches. The user 142 | " passes everything as one parameter and doesn't have to use numbers. 143 | function! s:AsyncCscopeFindX(input) 144 | " Split the type from the query 145 | let type = a:input[0] 146 | let query = a:input[2:] 147 | 148 | " Convert the type from a char to a number 149 | " (cscope -l requires a number) 150 | try 151 | let type_num = s:type_char_to_num[ type ] 152 | catch /Key not present in Dictionary/ 153 | echo "Error: " . type . " is an invalid find query. See :cscope help" 154 | return 155 | endtry 156 | 157 | let title = s:num_to_description[type_num] . ' ' . query 158 | 159 | call s:AsyncCscopeFind(type_num, query, title) 160 | endfunction 161 | 162 | function! s:AsyncCscopeFind(type_num, query, title) 163 | " -d Don't rebuild the database 164 | " -l Use cscope's line-oriented mode to send a single search command 165 | " -f file Use file as the database file name instead of the default 166 | " -P path Prepend path to relative file names in a pre-built database 167 | " The output is in the form: "filename location line-number context" 168 | " Use sed to change it so we can use efm: "filename:line-number location context" 169 | if !exists('g:cscope_database') || !exists('g:cscope_relative_path') 170 | echoerr "You must define both g:cscope_database and g:cscope_relative_path" 171 | echoerr "See LocateCscopeFile in tagfilehelpers.vim" 172 | return 173 | endif 174 | let cscope_cmd = &cscopeprg . " -dl -f " . g:cscope_database . " -P " . g:cscope_relative_path 175 | " sed command: (filename) (symbol context -- may contain spaces) (line number) 176 | let command = "echo " . a:type_num . a:query . " | " . cscope_cmd . " | sed --regexp-extended -e\"s/(\\S+) (\\S+) ([0-9]+)/\\1:\\3 \\2 \t/\"" 177 | 178 | call asynccommand#run(command, s:CscopeResults(a:title)) 179 | endfunction 180 | 181 | function! s:CscopeResults(title) 182 | return asynchandler#quickfix("%-G>>%m,%f:%l\ %m", "[Found: %d] Cscope: " . a:title) 183 | endfunction 184 | 185 | let &cpo = s:save_cpo 186 | 187 | " vi: et sw=4 ts=4 188 | --------------------------------------------------------------------------------