vim-makejob

Minimal, asynchronous quickfix commands for Vim 8.0
git clone git://git.danielmoch.com/vim-makejob.git
Log | Files | Refs | README | LICENSE

commit c5f304a3ff71451a569aa42deb3afad9859d7643
parent 868b4197653419c90959ce4bbd6b98c22eccd8df
Author: Daniel Moch <daniel@danielmoch.com>
Date:   Thu, 24 Nov 2016 13:07:57 -0500

MakeJob v1.1

Add support for GrepJob and friends

Diffstat:
MREADME.md | 67++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mdoc/makejob.txt | 38++++++++++++++++++++++++++++++--------
Mplugin/makejob.vim | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
3 files changed, 158 insertions(+), 65 deletions(-)

diff --git a/README.md b/README.md @@ -1,24 +1,23 @@ # Vim MakeJob -There are plenty of [other build -solutions](http://github.com/scrooloose/syntastic) for -[Vim](http://github.com/vim/vim), many of them offering feature sets -that overlap with those the editor already offers. With minimalism as a -goal, _MakeJob_ implements asynchronous `:make` and `:lmake` for Vim in -just over 100 lines of Vimscript. +This is a plugin for folks who think that Vim's quickfix feature is +great, but who don't like how calls to `:make` and `:grep` freeze the +editor. _MakeJob_ implements asynchronous versions of the builtin +commands in just over 150 lines of Vimscript. ## Goals -1. Implement a minimal solution for asynchronous `:make` and `:lmake`. +1. Implement a minimal solution for asynchronous `:make` and `:grep`. No unnecessary features. -2. Let Vim be Vim. Use compiler plugins to configure `makeprg` and - `errorformat`. Use the Quickfix or Location List window to view - findings. -3. Complete feature parity with `:make` and `:lmake` per the steps - outlined in `:help make`. `autowrite`, `QuickFixCmdPre` and - `QuickFixCmdPost`, and `!` work as expected. +2. Let Vim be Vim. Use `makeprg` and `errorformat` to configure + `:MakeJob` and the analogous grep options for `:GrepJob`. Use the + Quickfix or Location List window to view findings. +3. Complete feature parity with `:make` and `:grep` per the steps + outlined in `:help quickfix`. `autowrite`, `QuickFixCmdPre` and + `QuickFixCmdPost`, and the bang operator work as expected. ## Requirements -Vim 8 minimum compiled with `+job` and `+channel`. +Vim 8 minimum compiled with `+job`, `+channel`, and of course +`+quickfix`. ## Installation ### Pathogen @@ -32,16 +31,31 @@ Most other plugin managers will resemble one of these two. ## Usage ### The Short Version -Vim has `:make` and `:lmake`. Replace those calls with `:MakeJob` and -`:LmakeJob`. A buffer will open showing the command output, which will +Vim has `:make` and `:grep`. Replace those calls with `:MakeJob` and +`:GrepJob`. A buffer will open showing the command output, which will be parsed into the Quickfix or LocationList window when the job completes. Bask in your newfound freedom to do as you please in Vim -while MakeJob runs. +while _MakeJob_ runs. -If `:MakeJob` reports findings, use `:copen` to view the QuickFix window -(in the case of MakeJob), and likewise `:lopen` to open the LocationList +If _MakeJob_ reports findings, use `:copen` to view the QuickFix window +(in the case of `:MakeJob`), and likewise `:lopen` to open the LocationList for `:LmakeJob`. +Speaking of `:LmakeJob`, all of the LocationList complements to the +Quickfix commands are there with _MakeJob_, bringing the full list of +commands to: + +- `:MakeJob` +- `:LmakeJob` +- `:GrepJob` +- `:LgrepJob` +- `:GrepaddJob` +- `:LgrepaddJob` + +All of which work like their builtin counterparts. Those last two are +admittedly a bit longer than we would probably like, but if you grep a +lot you'll probably want to set a mapping for it anyway (see below). + ### The Less Short Version Users of Syntastic may not be aware that Vim offers many of the same features out of the box. Here's a brief rundown. @@ -55,8 +69,8 @@ format of the errors to look for. This gets pretty hairy, and so we're all better off trying to avoid this in favor of the easy way: compiler plugins. Using a compiler plugin easy (ex: `:compiler javac`), they abstract away the work of remembering the `errorformat`, they're -extendable, and many are already included in Vim. _MakeJob_ uses -compilers. +extendable, and many are already included in Vim. _MakeJob_ uses the +same compiler plugins users of Vim will be familiar with. It's also possible to use the`after/ftplugin` folder to automatically configure compilers on a per-file-type basis. An example of that trick @@ -79,6 +93,17 @@ basis with something like the following: `autocmd! BufWritePost *.py :LmakeJob! %<CR>` +Grep is a powerful way to search through a directory structure for a +keyword. I use it all the time, which is why I've added the following +mapping to my `.vimrc`: + +`nnoremap <Leader>g :GrepJob!<Space>` + +## Gotchas +If `grepprg` is set to `'internal'`, then Vim uses its own builtin grep +command. This still works when you call `:GrepJob`, but not +asynchronously. + ## Vim Documentation Part of the goal of _MakeJob_ is to minimize the size of the plugin by using features Vim already offers whenever possible. To that end, if diff --git a/doc/makejob.txt b/doc/makejob.txt @@ -5,18 +5,21 @@ License: MIT (see LICENSE file for details) INTRODUCTION *makejob* *vim-makejob* -There are plenty of other build solutions for Vim and Vim, many of them -offering feature sets that overlap with those the editor already offers. -With minimalism as a goal, MakeJob implements asynchronous |:make| and -|:lmake| for Vim in just over 100 lines of Vimscript. +This is a plugin for folks who think that Vim's quickfix feature is +great, but who don't like how calls to |:make| and |:grep| freeze the +editor. _MakeJob_ implements asynchronous versions of the builtin +commands in just over 150 lines of Vimscript. -Here are your new make commands. +{only available in Vim version 8 or later} +{only when compiled with the |channel|, |job|, and |quickfix| features} + +Here are your new quickfix commands. *makejob-MakeJob* -MakeJob[!] [{bufname}] Start a makejob on the specified buffer, +MakeJob[!] [{filename}] Start a makejob on the specified buffer, populating findings in the quickfix list. |makeprg| and |errorformat| must be set before - MakeJob is called. {bufname}, if specified, is the + MakeJob is called. {filename}, if specified, is the name of the file to make, and is appended to the end of |makeprg|. Ex special characters are expanded (see |cmdline-special|). |autowrite|, @@ -27,10 +30,29 @@ MakeJob[!] [{bufname}] Start a makejob on the specified buffer, first error is jumped to. *makejob-LmakeJob* -LmakeJob[!] [{bufname}] Same as ":MakeJob", except the location list for +LmakeJob[!] [{filename}] Same as ":MakeJob", except the location list for the current window is used instead of the quickfix list. + *makejob-GrepJob* +GrepJob[!] {filename} Run the grep command specified in |grepprg| + and read into the quickfix list using + |grepformat|. {filename} must be specified, + but can contain wildcards. + + *makejob-LgrepJob* +LgrepJob[!] {filename} Same as ":GrepJob", except the location list for + the current window is used instead of the quickfix + list. + + *makejob-GrepaddJob* +GrepaddJob[!] {filename} Same as ":GrepJob", but add findings to the + quickfix list without clearing it first. + + *makejob-LgrepaddJob* +GrepaddJob[!] {filename} Same as ":LrepJob", but add findings to the + location list without clearing it first. + ABOUT *makejob-about* More details can be found in README.md or by navigating to: diff --git a/plugin/makejob.vim b/plugin/makejob.vim @@ -1,21 +1,16 @@ " " TITLE: VIM-MAKEJOB " AUTHOR: Daniel Moch <daniel@danielmoch.com> -" VERSION: 1.0.2-dev +" VERSION: 1.1 " if exists('g:loaded_makejob') || version < 800 || !has('job') || - \ !has('channel') + \ !has('channel') || !has('quickfix') finish endif let g:loaded_makejob = 1 let s:jobinfo = {} -function! s:Function(name) - return substitute(a:name, '^s:', matchstr(expand('<sfile>'), - \'<SNR>\d\+_\ze[fF]unction$'),'') -endfunction - function! s:JobHandler(channel) abort let job = remove(s:jobinfo, split(a:channel)[1]) let is_lmake = job['lmake'] @@ -24,15 +19,27 @@ function! s:JobHandler(channel) abort " For reasons I don't understand, copying and re-writing " errorformat fixes a lot of parsing errors - let tempefm = &errorformat - let &errorformat = tempefm + let tempefm = job['grep'] ? &grepformat : &errorformat + if job['grep'] + let &grepformat = tempefm + else + let &errorformat = tempefm + endif execute bufwinnr(job['srcbufnr']).'wincmd w' if is_lmake - lgetexpr output + if job['grepadd'] + laddexpr output + else + lgetexpr output + endif else - cgetexpr output + if job['grepadd'] + caddexpr output + else + cgetexpr output + end endif wincmd p @@ -48,14 +55,22 @@ function! s:JobHandler(channel) abort let idx += 1 endwhile - if is_lmake - silent doautocmd QuickFixCmdPost lmake + if job['grep'] + if is_lmake + silent doautocmd QuickFixCmdPost lgrep + else + silent doautocmd QuickFixCmdPost grep + endif else - silent doautocmd QuickFixCmdPost make - endif + if is_lmake + silent doautocmd QuickFixCmdPost lmake + else + silent doautocmd QuickFixCmdPost make + endif + end if job['cfirst'] - cfirst + silent! cfirst end echomsg job['prog']." ended with ".makeoutput." findings" @@ -70,9 +85,10 @@ function! s:CreateMakeJobWindow(prog) return bufnum endfunction -function! s:MakeJob(lmake, bang, ...) - let make = &makeprg +function! s:MakeJob(grep, lmake, grepadd, bang, ...) + let make = a:grep ? &grepprg : &makeprg let prog = split(make)[0] + let internal_grep = make ==# 'internal' ? 1 : 0 execute 'let openbufnr = bufnr("^'.prog.'$")' if openbufnr != -1 echohl WarningMsg @@ -81,35 +97,65 @@ function! s:MakeJob(lmake, bang, ...) return endif if a:0 - let make = make.' '.expand(a:1) + if a:grep + if internal_grep + let make = 'vimgrep '.a:1 + elseif make =~ '\$\*' + let make = [&shell, &shellcmdflag, substitute(make, '\$\*', a:1, 'g')] + else + let make = [&shell, &shellcmdflag, make.' '.a:1] + endif + else + let make = make.' '.expand(a:1) + end endif - let opts = { 'close_cb' : s:Function('s:JobHandler'), + + let opts = { 'close_cb' : function('s:JobHandler'), \ 'out_io': 'buffer', \ 'out_name': prog, \ 'out_modifiable': 0, \ 'err_io': 'buffer', \ 'err_name': prog, - \ 'err_modifiable': 0} - - if a:lmake - silent doautocmd QuickFixCmdPre lmake + \ 'err_modifiable': 0, + \ 'in_io': 'null'} + + if a:grep + if a:lmake + silent doautocmd QuickFixCmdPre lgrep + else + silent doautocmd QuickFixCmdPre grep + endif else - silent doautocmd QuickFixCmdPre make + if a:lmake + silent doautocmd QuickFixCmdPre lmake + else + silent doautocmd QuickFixCmdPre make + endif endif - if &autowrite + if &autowrite && !a:grep silent write endif - let outbufnr = s:CreateMakeJobWindow(prog) - - let job = job_start(make, opts) - let s:jobinfo[split(job_getchannel(job))[1]] = - \ { 'prog': prog,'lmake': a:lmake, - \ 'outbufnr': outbufnr, 'srcbufnr': winbufnr(0), - \ 'cfirst': !a:bang } - echomsg s:jobinfo[split(job_getchannel(job))[1]]['prog'].' started' + if internal_grep + execute make + return + else + let outbufnr = s:CreateMakeJobWindow(prog) + + let job = job_start(make, opts) + let s:jobinfo[split(job_getchannel(job))[1]] = + \ { 'prog': prog,'lmake': a:lmake, + \ 'outbufnr': outbufnr, 'srcbufnr': winbufnr(0), + \ 'cfirst': !a:bang, 'grep': a:grep, + \ 'grepadd': a:grepadd } + echomsg s:jobinfo[split(job_getchannel(job))[1]]['prog'].' started' + end endfunction -command! -bang -nargs=? -complete=file MakeJob call s:MakeJob(0,<bang>0,<f-args>) -command! -bang -nargs=? -complete=file LmakeJob call s:MakeJob(1,<bang>0,<f-args>) +command! -bang -nargs=* -complete=file MakeJob call s:MakeJob(0,0,0,<bang>0,<q-args>) +command! -bang -nargs=* -complete=file LmakeJob call s:MakeJob(0,1,0,<bang>0,<q-args>) +command! -bang -nargs=+ -complete=file GrepJob call s:MakeJob(1,0,0,<bang>0,<q-args>) +command! -bang -nargs=+ -complete=file LgrepJob call s:MakeJob(1,1,0,<bang>0,<q-args>) +command! -bang -nargs=+ -complete=file GrepaddJob call s:MakeJob(1,0,1,<bang>0,<q-args>) +command! -bang -nargs=+ -complete=file LgrepaddJob call s:MakeJob(1,1,1,<bang>0,<q-args>)