├── LICENSE ├── README.md ├── autoload └── dispatch │ └── neovim.vim └── plugin └── plugin.vim /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Richard Adenling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dispatch-neovim 2 | 3 | Add support for neovim's terminal emulator and job control to 4 | [dispatch.vim][dispatch]. 5 | 6 | ## Usage 7 | 8 | _Note: This plugin depends on [dispatch.vim][dispatch] and you need to have that 9 | plugin installed for this plugin to work_ 10 | 11 | When you have installed dispatch-neovim and start up neovim, it will insert 12 | itself as the first handler in dispatch's list of handlers. If it can't detect 13 | neovim it will not do anything. 14 | 15 | ### Notes 16 | 17 | Foreground builds run in a small terminal window at the bottom of the current 18 | tab. If the foreground job fails, the quickfix window will open showing the 19 | errors. 20 | 21 | Background builds will run as jobs in neovim, which means that they won't open a 22 | terminal window, nor will they open the quickfix window afterwards. Use `:Copen` 23 | to open the quickfix window for background builds. 24 | 25 | `:Start` and `:Start!` uses vim tabs and the built-in terminal emulator. 26 | 27 | ## Todo 28 | 29 | * Test on other platforms than linux 30 | 31 | [dispatch]: https://github.com/tpope/vim-dispatch 32 | -------------------------------------------------------------------------------- /autoload/dispatch/neovim.vim: -------------------------------------------------------------------------------- 1 | if exists('g:autoloaded_dispatch_neovim') 2 | finish 3 | endif 4 | 5 | let g:autoloaded_dispatch_neovim = 1 6 | 7 | function! s:UsesTerminal(request) 8 | return a:request.action ==# 'start' || 9 | \(a:request.action ==# 'make' && !a:request.background) 10 | endfunction 11 | 12 | function! s:NeedsOutput(request) 13 | return a:request.action ==# 'make' 14 | endfunction 15 | 16 | function! s:IsBackgroundJob(request) 17 | return a:request.action ==# 'make' && a:request.background 18 | endfunction 19 | 20 | function! s:CommandOptions(request) abort 21 | let opts = { 22 | \ 'name': a:request.title, 23 | \ 'background': a:request.background, 24 | \ 'request': a:request, 25 | \} 26 | let terminal_opts = { 'pty': 1, 'width': 80, 'height': 25 } 27 | 28 | if s:UsesTerminal(a:request) 29 | call extend(opts, terminal_opts) 30 | endif 31 | 32 | if s:NeedsOutput(a:request) 33 | if s:IsBackgroundJob(a:request) 34 | call extend(opts, { 35 | \ 'on_stdout': function('s:BufferOutput'), 36 | \ 'on_stderr': function('s:BufferOutput'), 37 | \ 'on_exit': function('s:JobExit'), 38 | \ 'tempfile': a:request.file, 39 | \ 'output': '' 40 | \}) 41 | else 42 | call extend(opts, { 43 | \ 'on_exit': function('s:JobExit'), 44 | \ 'tempfile': a:request.file, 45 | \}) 46 | endif 47 | endif 48 | return opts 49 | endfunction 50 | 51 | function! s:SaveCurrentBufferPid(request) 52 | let pid = get(b:, 'terminal_job_pid', 0) 53 | call writefile([pid], a:request.file . '.pid') 54 | let a:request.pid = pid " This is used by Start! (see g:DISPATCH_STARTS) 55 | endfunction 56 | 57 | function! dispatch#neovim#handle(request) abort 58 | let action = a:request.action 59 | let cmd = a:request.expanded 60 | let bg = a:request.background 61 | let opts = s:CommandOptions(a:request) 62 | if s:UsesTerminal(a:request) 63 | if s:NeedsOutput(a:request) 64 | execute 'botright split | enew | resize 10' 65 | let opts.buf_id = bufnr('%') 66 | call termopen(cmd, opts) 67 | call s:SaveCurrentBufferPid(a:request) 68 | execute 'wincmd p' 69 | else 70 | execute 'tabnew' 71 | call termopen(cmd, opts) 72 | call s:SaveCurrentBufferPid(a:request) 73 | if bg 74 | execute 'tabprev' 75 | else 76 | execute 'startinsert' 77 | endif 78 | endif 79 | else 80 | let l:job_id = jobstart(cmd, opts) 81 | 82 | " Create empty file in case there is no output 83 | call writefile([], a:request.file) 84 | 85 | " There is currently no way to get the pid in neovim when using 86 | " jobstart. See: https://github.com/neovim/neovim/issues/557 87 | " Use job id as pid for now. 88 | call writefile([l:job_id], a:request.file.'.pid') 89 | endif 90 | return 1 91 | endfunction 92 | 93 | function! s:FindBufferByPID(pid) abort 94 | let bufcount = bufnr('$') 95 | for b in range(1, bufcount) 96 | if buflisted(b) 97 | if a:pid == getbufvar(b, 'terminal_job_pid', -1) + 0 98 | return b 99 | endif 100 | endif 101 | endfor 102 | return 0 103 | endfunction 104 | 105 | function! dispatch#neovim#activate(pid) abort 106 | let l:buf = s:FindBufferByPID(a:pid) 107 | if buf > 0 108 | for t in range(1, tabpagenr('$')) 109 | if index(tabpagebuflist(t), l:buf) != -1 110 | " When we find the buffer, switch to the right tab and window 111 | execute 'normal! '.t.'gt' 112 | execute bufwinnr(l:buf).'wincmd w' 113 | return 1 114 | endif 115 | endfor 116 | else 117 | " Program was not found among the buffers so nothing to activate 118 | return 0 119 | endif 120 | endfunction 121 | 122 | " Remove newlines and merge lines without newlines 123 | function! s:FilterNewlines(lines, state) abort 124 | let l:lines = [] 125 | for line in a:lines 126 | let l:line_without_newline = substitute(line, '\n\|\r', '', 'g') 127 | let a:state.output .= l:line_without_newline 128 | if line =~ '\n\|\r' 129 | call add(l:lines, a:state.output) 130 | let a:state.output = '' 131 | endif 132 | endfor 133 | return l:lines 134 | endfunction 135 | 136 | function! s:RemoveANSI(lines) 137 | return map(a:lines, 'substitute(v:val, ''\e\[[0-9;]*[a-zA-Z]'', "", "g")') 138 | endfunction 139 | 140 | function! s:BufferOutput(job_id, data, event) dict abort 141 | let l:lines = a:data 142 | let l:lines = filter(l:lines, '!empty(v:val)') 143 | let l:lines = s:RemoveANSI(l:lines) 144 | let l:lines = s:FilterNewlines(l:lines, self) 145 | call writefile(l:lines, self.tempfile, "a") 146 | endfunction 147 | 148 | function! s:JobExit(job_id, data, event) dict abort 149 | if s:UsesTerminal(self.request) && s:NeedsOutput(self.request) 150 | call writefile(getbufline(self.buf_id, 1, '$'), self.tempfile) 151 | endif 152 | 153 | " Clean up terminal window if visible 154 | if !self.background 155 | let term_win = bufwinnr(self.buf_id) 156 | if term_win != -1 157 | let cur_win = winnr() 158 | execute term_win . ' wincmd w' 159 | call feedkeys("\\", 'n') 160 | execute cur_win . ' wincmd w' 161 | execute 'silent bd! ' . self.buf_id 162 | endif 163 | endif 164 | call writefile([a:data], self.tempfile . '.complete') 165 | call dispatch#complete(self.tempfile) 166 | endfunction 167 | -------------------------------------------------------------------------------- /plugin/plugin.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_dispatch_neovim') 2 | finish 3 | endif 4 | 5 | let g:loaded_dispatch_neovim = 1 6 | 7 | if has('nvim') 8 | augroup dispatch-neovim 9 | autocmd! 10 | autocmd VimEnter * 11 | \ if index(get(g:, 'dispatch_handlers', ['neovim']), 'neovim') < 0 | 12 | \ call insert(g:dispatch_handlers, 'neovim', 0) | 13 | \ endif 14 | augroup END 15 | endif 16 | --------------------------------------------------------------------------------