From 4eeff535fadba836bd2c0ed4eb0adcf8bfc43d6b Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Thu, 9 Oct 2014 19:55:36 +0900 Subject: [PATCH 01/13] Parallel installer for Neovim --- README.md | 3 +- plug.vim | 323 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 255 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 757d469..2a98127 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ A minimalist Vim plugin manager. - Easier to use: Concise, intuitive syntax - [Super-fast](https://raw.githubusercontent.com/junegunn/i/master/vim-plug/40-in-4.gif) parallel installation/update (requires - [+ruby](https://github.com/junegunn/vim-plug/wiki/ruby)) + [+ruby](https://github.com/junegunn/vim-plug/wiki/ruby) or + [Neovim](http://neovim.org/)) - On-demand loading to achieve [fast startup time](http://junegunn.kr/images/vim-startup-time.png) - Post-update hooks diff --git a/plug.vim b/plug.vim index 7d01b65..99a4f72 100644 --- a/plug.vim +++ b/plug.vim @@ -69,6 +69,7 @@ let s:cpo_save = &cpo set cpo&vim let s:plug_source = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' +let s:plug_tab = get(s:, 'plug_tab', -1) let s:plug_buf = get(s:, 'plug_buf', -1) let s:mac_gui = has('gui_macvim') && has('gui_running') let s:is_win = has('win32') || has('win64') @@ -484,7 +485,7 @@ function! s:lpad(str, len) endfunction function! s:lastline(msg) - let lines = split(a:msg, '\n') + let lines = split(a:msg, "[\r\n]") return get(lines, -1, '') endfunction @@ -492,6 +493,31 @@ function! s:new_window() execute get(g:, 'plug_window', 'vertical topleft new') endfunction +function! s:plug_window_exists() + return index(tabpagebuflist(s:plug_tab), s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr 'wincmd w' + setlocal modifiable + + return 1 +endfunction + +function! s:switch_out() + setlocal nomodifiable + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) +endfunction + function! s:prepare() if bufexists(s:plug_buf) let winnr = bufwinnr(s:plug_buf) @@ -499,7 +525,7 @@ function! s:prepare() call s:new_window() execute 'buffer' s:plug_buf else - execute winnr . 'wincmd w' + execute winnr 'wincmd w' endif setlocal modifiable silent %d _ @@ -514,6 +540,7 @@ function! s:prepare() nnoremap ]] :silent! call section('') nnoremap [[ :silent! call section('b') let b:plug_preview = -1 + let s:plug_tab = tabpagenr() let s:plug_buf = winbufnr(0) call s:assign_name() endif @@ -542,7 +569,7 @@ function! s:do(pull, force, todo) if !isdirectory(spec.dir) continue endif - let installed = has_key(s:prev_update.new, name) + let installed = has_key(s:update.new, name) let updated = installed ? 0 : \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir))) if a:force || installed || updated @@ -586,7 +613,7 @@ function! s:finish(pull) call s:syntax() redraw let msgs = [] - if !empty(s:prev_update.errors) + if !empty(s:update.errors) call add(msgs, "Press 'R' to retry.") endif if a:pull && !empty(filter(getline(5, '$'), @@ -597,11 +624,11 @@ function! s:finish(pull) endfunction function! s:retry() - if empty(s:prev_update.errors) + if empty(s:update.errors) return endif - call s:update_impl(s:prev_update.pull, s:prev_update.force, - \ extend(copy(s:prev_update.errors), [s:prev_update.threads])) + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) endfunction function! s:is_managed(name) @@ -613,7 +640,6 @@ function! s:names(...) endfunction function! s:update_impl(pull, force, args) abort - let st = reltime() let args = copy(a:args) let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? \ remove(args, -1) : get(g:, 'plug_threads', 16) @@ -621,6 +647,7 @@ function! s:update_impl(pull, force, args) abort let managed = filter(copy(g:plugs), 's:is_managed(v:key)') let todo = empty(args) ? filter(managed, '!v:val.frozen') : \ filter(managed, 'index(args, v:key) >= 0') + let threads = min([len(todo), threads]) if empty(todo) echohl WarningMsg @@ -638,20 +665,30 @@ function! s:update_impl(pull, force, args) abort endtry endif - call s:prepare() - call append(0, a:pull ? 'Updating plugins' : 'Installing plugins') - call append(1, '['. s:lpad('', len(todo)) .']') - normal! 2G - redraw + let s:update = { + \ 'start': reltime(), + \ 'neovim': exists('##JobActivity'), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (has('ruby') || exists('##JobActivity')) ? threads : 1, + \ 'bar': '', + \ 'fin': 0 + \ } - let s:prev_update = { 'errors': [], 'pull': a:pull, 'force': a:force, 'new': {}, 'threads': threads } - if has('ruby') && threads > 1 + call s:prepare() + call append(0, ['', '']) + + if has('ruby') && s:update.threads > 1 try let imd = &imd if s:mac_gui set noimd endif - call s:update_parallel(a:pull, todo, threads) + call s:update_ruby() catch let lines = getline(4, '$') let printed = {} @@ -662,71 +699,213 @@ function! s:update_impl(pull, force, args) abort call append('$', line) if !empty(name) let printed[name] = 1 - if line[0] == 'x' && index(s:prev_update.errors, name) < 0 - call add(s:prev_update.errors, name) + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) end endif endif endfor finally let &imd = imd + call s:update_finish() endtry else - call s:update_serial(a:pull, todo) + call s:update_vim() endif - call s:do(a:pull, a:force, filter(copy(todo), 'has_key(v:val, "do")')) - call s:finish(a:pull) - call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(st)))[0] . ' sec.') endfunction -function! s:update_progress(pull, cnt, bar, total) - call setline(1, (a:pull ? 'Updating' : 'Installing'). - \ ' plugins ('.a:cnt.'/'.a:total.')') - call s:progress_bar(2, a:bar, a:total) - normal! 2G - redraw +function! s:update_finish() + if s:switch_in() + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'has_key(v:val, "do")')) + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out() + endif endfunction -function! s:update_serial(pull, todo) - let base = g:plug_home - let todo = copy(a:todo) - let total = len(todo) - let done = {} - let bar = '' +function! s:job_handler() + let name = s:jobs_idx[v:job_data[0]] + let job = s:jobs[name] - for [name, spec] in items(todo) - let done[name] = 1 - if isdirectory(spec.dir) - let [valid, msg] = s:git_valid(spec, 0) - if valid - let result = a:pull ? - \ s:system( - \ printf('git checkout -q %s 2>&1 && git pull --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1', - \ s:shellesc(spec.branch), s:shellesc(spec.branch)), spec.dir) : 'Already installed' - let error = a:pull ? v:shell_error != 0 : 0 - else - let result = msg - let error = 1 + " plug window closed + if !s:plug_window_exists() + augroup PlugJobControl + autocmd! + augroup END + for [name, j] in items(s:jobs) + call jobstop(j.jobid) + if j.new + call system('rm -rf ' . s:shellesc(g:plugs[name].dir)) + endif + endfor + return + endif + + let s:tick += 1 + if v:job_data[1] == 'exit' + let job.running = 0 + call s:reap(name) + call s:tick() + else + let job.result .= v:job_data[2] + " To reduce the number of buffer updates + let job.tick = get(job, 'tick', -1) + 1 + if job.tick % len(s:jobs) == 0 + call s:log(name, s:lastline(job.result), s:update.pull ? 'u' : 'i') + endif + endif +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'running': 1, 'new': get(a:opts, 'new', 0), + \ 'error': 0, 'result': '' } + let s:jobs[a:name] = job + + if s:update.neovim + let x = jobstart(a:name, 'sh', ['-c', + \ has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd]) + if x > 0 + let s:jobs_idx[x] = a:name + let job.jobid = x + augroup PlugJobControl + execute 'autocmd JobActivity' a:name 'call s:job_handler()' + augroup END + else + let job.running = 0 + let job.error = 1 + let job.result = x < 0 ? 'sh is not executable' : + \ 'Invalid arguments (or job table is full)' + endif + else + let params = has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd] + let job.running = 0 + let job.result = call('s:system', params) + let job.error = v:shell_error != 0 + endif +endfunction + +function! s:reap(name) + if s:update.neovim + silent! execute 'autocmd! PlugJobControl JobActivity' a:name + endif + + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + " TODO: Error formatting + call s:log(a:name, s:lastline(job.result), job.error ? 'x' : '-') + call s:bar() + + call remove(s:jobs, a:name) + return 1 +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + for i in range(1, line('$')) + if getline(i) =~# '^[-+x*] '.a:name.':' + return i + endif + endfor + return 0 +endfunction + +function! s:log(name, line, type) + if s:switch_in() + let pos = s:logpos(a:name) + if pos > 0 + execute pos 'd _' + if pos > winheight('.') + let pos = 4 endif else - let result = s:system( - \ printf('git clone --recursive %s -b %s %s 2>&1', - \ s:shellesc(spec.uri), - \ s:shellesc(spec.branch), - \ s:shellesc(s:trim(spec.dir)))) - let error = v:shell_error != 0 - if !error | let s:prev_update.new[name] = 1 | endif + let pos = 4 endif - let bar .= error ? 'x' : '=' - if error - call add(s:prev_update.errors, name) - endif - call append(3, s:format_message(!error, name, result)) - call s:update_progress(a:pull, len(done), bar, total) - endfor + + let bullet = a:type == 'i' ? '+' : + \ (a:type == 'u' ? '*' : + \ (a:type == 'x' ? 'x' : '-')) + + call append(pos - 1, printf('%s %s: %s', bullet, a:name, a:line)) + call s:switch_out() + endif endfunction -function! s:update_parallel(pull, todo, threads) +function! s:update_vim() + let s:jobs = {} + let s:jobs_idx = {} + let s:tick = 0 + + call s:bar() + normal! 2G + call s:tick() +endfunction + +function! s:tick() +while 1 + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + let s:update.fin = 1 + call s:update_finish() + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let pull = s:update.pull + call s:log(name, pull ? 'Updating ...' : 'Installing ...', pull ? 'u' : 'i') + redraw + + if isdirectory(spec.dir) + let [valid, msg] = s:git_valid(spec, 0) + if valid + if pull + call s:spawn(name, + \ printf('git checkout -q %s 2>&1 && git pull --progress --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1', + \ s:shellesc(spec.branch), s:shellesc(spec.branch)), { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } + endif + else + call s:spawn(name, + \ printf('git clone --progress --recursive %s -b %s %s 2>&1', + \ s:shellesc(spec.uri), + \ s:shellesc(spec.branch), + \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + " Without TCO, Vim stack is bound to explode + " return s:tick() + continue + elseif len(s:jobs) < s:update.threads + " return s:tick() + continue + endif + break +endwhile +endfunction + +function! s:update_ruby() ruby << EOF module PlugStream SEP = ["\r", "\n", nil] @@ -768,15 +947,15 @@ function! s:update_parallel(pull, todo, threads) require 'timeout' running = true iswin = VIM::evaluate('s:is_win').to_i == 1 - pull = VIM::evaluate('a:pull').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 base = VIM::evaluate('g:plug_home') - all = VIM::evaluate('a:todo') + all = VIM::evaluate('s:update.todo') limit = VIM::evaluate('get(g:, "plug_timeout", 60)') tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 - nthr = VIM::evaluate('a:threads').to_i + nthr = VIM::evaluate('s:update.threads').to_i maxy = VIM::evaluate('winheight(".")').to_i cd = iswin ? 'cd /d' : 'cd' - tot = VIM::evaluate('len(a:todo)') || 0 + tot = VIM::evaluate('len(s:update.todo)') || 0 bar = '' skip = 'Already installed' mtx = Mutex.new @@ -796,7 +975,7 @@ function! s:update_parallel(pull, todo, threads) b = case type when :install then '+' when :update then '*' when true, nil then '-' else - VIM::command("call add(s:prev_update.errors, '#{name}')") + VIM::command("call add(s:update.errors, '#{name}')") 'x' end result = @@ -885,7 +1064,7 @@ function! s:update_parallel(pull, todo, threads) } if VIM::evaluate('s:mac_gui') == 1 progress = iswin ? '' : '--progress' - [all.length, nthr].min.times do + nthr.times do mtx.synchronize do threads << Thread.new { while pair = take1.call @@ -924,7 +1103,7 @@ function! s:update_parallel(pull, todo, threads) FileUtils.rm_rf dir } end - mtx.synchronize { VIM::command("let s:prev_update.new['#{name}'] = 1") } if !exists && ok + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok log.call name, result, ok end } if running @@ -964,8 +1143,12 @@ function! s:format_message(ok, name, message) endif endfunction +function! s:with_cd(cmd, dir) + return 'cd '.s:esc(a:dir).' && '.a:cmd +endfunction + function! s:system(cmd, ...) - let cmd = a:0 > 0 ? 'cd '.s:esc(a:1).' && '.a:cmd : a:cmd + let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd return system(s:is_win ? '('.cmd.')' : cmd) endfunction From 662274e61721f0b3bafb5892e36cfd57b19f8daa Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 01:01:49 +0900 Subject: [PATCH 02/13] Stabilize Neovim installer - Abort running jobs when plug windows is reset - Multi-line error report - Retain window view --- plug.vim | 99 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/plug.vim b/plug.vim index 99a4f72..d41c6c5 100644 --- a/plug.vim +++ b/plug.vim @@ -73,6 +73,7 @@ let s:plug_tab = get(s:, 'plug_tab', -1) let s:plug_buf = get(s:, 'plug_buf', -1) let s:mac_gui = has('gui_macvim') && has('gui_running') let s:is_win = has('win32') || has('win64') +let s:nvim = exists('##JobActivity') let s:me = resolve(expand(':p')) let s:base_spec = { 'branch': 'master', 'frozen': 0 } let s:TYPE = { @@ -484,9 +485,12 @@ function! s:lpad(str, len) return a:str . repeat(' ', a:len - len(a:str)) endfunction +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + function! s:lastline(msg) - let lines = split(a:msg, "[\r\n]") - return get(lines, -1, '') + return get(s:lines(a:msg), -1, '') endfunction function! s:new_window() @@ -506,19 +510,23 @@ function! s:switch_in() execute 'normal!' s:plug_tab.'gt' let winnr = bufwinnr(s:plug_buf) execute winnr 'wincmd w' - setlocal modifiable + call add(s:pos, winsaveview()) + setlocal modifiable return 1 endfunction function! s:switch_out() + call winrestview(s:pos[3]) setlocal nomodifiable + execute 'normal!' s:pos[0].'gt' execute s:pos[1] 'wincmd w' call winrestview(s:pos[2]) endfunction function! s:prepare() + call s:job_abort() if bufexists(s:plug_buf) let winnr = bufwinnr(s:plug_buf) if winnr < 0 @@ -647,7 +655,6 @@ function! s:update_impl(pull, force, args) abort let managed = filter(copy(g:plugs), 's:is_managed(v:key)') let todo = empty(args) ? filter(managed, '!v:val.frozen') : \ filter(managed, 'index(args, v:key) >= 0') - let threads = min([len(todo), threads]) if empty(todo) echohl WarningMsg @@ -667,14 +674,13 @@ function! s:update_impl(pull, force, args) abort let s:update = { \ 'start': reltime(), - \ 'neovim': exists('##JobActivity'), \ 'all': todo, \ 'todo': copy(todo), \ 'errors': [], \ 'pull': a:pull, \ 'force': a:force, \ 'new': {}, - \ 'threads': (has('ruby') || exists('##JobActivity')) ? threads : 1, + \ 'threads': (has('ruby') || s:nvim) ? min([len(todo), threads]) : 1, \ 'bar': '', \ 'fin': 0 \ } @@ -723,25 +729,34 @@ function! s:update_finish() endif endfunction -function! s:job_handler() - let name = s:jobs_idx[v:job_data[0]] - let job = s:jobs[name] - - " plug window closed - if !s:plug_window_exists() - augroup PlugJobControl - autocmd! - augroup END - for [name, j] in items(s:jobs) - call jobstop(j.jobid) - if j.new - call system('rm -rf ' . s:shellesc(g:plugs[name].dir)) - endif - endfor +function! s:job_abort() + if !s:nvim || !exists('s:jobs') return endif + augroup PlugJobControl + autocmd! + augroup END + for [name, j] in items(s:jobs) + silent! call jobstop(j.jobid) + if j.new + call system('rm -rf ' . s:shellesc(g:plugs[name].dir)) + endif + endfor + let s:jobs = {} + let s:jobs_idx = {} +endfunction + +function! s:job_handler() abort + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + + let name = get(s:jobs_idx, v:job_data[0], '') + if empty(name) " stale task + return + endif + let job = s:jobs[name] - let s:tick += 1 if v:job_data[1] == 'exit' let job.running = 0 call s:reap(name) @@ -751,7 +766,7 @@ function! s:job_handler() " To reduce the number of buffer updates let job.tick = get(job, 'tick', -1) + 1 if job.tick % len(s:jobs) == 0 - call s:log(name, s:lastline(job.result), s:update.pull ? 'u' : 'i') + call s:log(job.new ? '+' : '*', name, job.result) endif endif endfunction @@ -761,7 +776,7 @@ function! s:spawn(name, cmd, opts) \ 'error': 0, 'result': '' } let s:jobs[a:name] = job - if s:update.neovim + if s:nvim let x = jobstart(a:name, 'sh', ['-c', \ has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd]) if x > 0 @@ -785,7 +800,7 @@ function! s:spawn(name, cmd, opts) endfunction function! s:reap(name) - if s:update.neovim + if s:nvim silent! execute 'autocmd! PlugJobControl JobActivity' a:name endif @@ -797,8 +812,7 @@ function! s:reap(name) endif let s:update.bar .= job.error ? 'x' : '=' - " TODO: Error formatting - call s:log(a:name, s:lastline(job.result), job.error ? 'x' : '-') + call s:log(job.error ? 'x' : '-', a:name, job.result) call s:bar() call remove(s:jobs, a:name) @@ -824,7 +838,7 @@ function! s:logpos(name) return 0 endfunction -function! s:log(name, line, type) +function! s:log(bullet, name, lines) if s:switch_in() let pos = s:logpos(a:name) if pos > 0 @@ -835,12 +849,7 @@ function! s:log(name, line, type) else let pos = 4 endif - - let bullet = a:type == 'i' ? '+' : - \ (a:type == 'u' ? '*' : - \ (a:type == 'x' ? 'x' : '-')) - - call append(pos - 1, printf('%s %s: %s', bullet, a:name, a:line)) + call append(pos - 1, s:format_message(a:bullet, a:name, a:lines)) call s:switch_out() endif endfunction @@ -848,7 +857,6 @@ endfunction function! s:update_vim() let s:jobs = {} let s:jobs_idx = {} - let s:tick = 0 call s:bar() normal! 2G @@ -856,7 +864,7 @@ function! s:update_vim() endfunction function! s:tick() -while 1 +while 1 " Without TCO, Vim stack is bound to explode if empty(s:update.todo) if empty(s:jobs) && !s:update.fin let s:update.fin = 1 @@ -868,10 +876,12 @@ while 1 let name = keys(s:update.todo)[0] let spec = remove(s:update.todo, name) let pull = s:update.pull - call s:log(name, pull ? 'Updating ...' : 'Installing ...', pull ? 'u' : 'i') + let new = !isdirectory(spec.dir) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') redraw - if isdirectory(spec.dir) + if !new let [valid, msg] = s:git_valid(spec, 0) if valid if pull @@ -894,11 +904,8 @@ while 1 if !s:jobs[name].running call s:reap(name) - " Without TCO, Vim stack is bound to explode - " return s:tick() continue elseif len(s:jobs) < s:update.threads - " return s:tick() continue endif break @@ -1134,11 +1141,11 @@ function! s:compare_git_uri(a, b) return a ==# b endfunction -function! s:format_message(ok, name, message) - if a:ok - return [printf('- %s: %s', a:name, s:lastline(a:message))] +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] else - let lines = map(split(a:message, '\n'), '" ".v:val') + let lines = map(s:lines(a:message), '" ".v:val') return extend([printf('x %s:', a:name)], lines) endif endfunction @@ -1321,7 +1328,7 @@ function! s:status() let msg .= ' (not loaded)' endif call s:progress_bar(2, repeat('=', cnt), total) - call append(3, s:format_message(valid, name, msg)) + call append(3, s:format_message(valid ? '-' : 'x', name, msg)) normal! 2G redraw endfor From da47e6ee561a282e9844918e9e2bcf4cc6e2be41 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 04:39:38 +0900 Subject: [PATCH 03/13] Avoid unnecessary tab/window switch --- plug.vim | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/plug.vim b/plug.vim index d41c6c5..30221db 100644 --- a/plug.vim +++ b/plug.vim @@ -506,23 +506,29 @@ function! s:switch_in() return 0 endif - let s:pos = [tabpagenr(), winnr(), winsaveview()] - execute 'normal!' s:plug_tab.'gt' - let winnr = bufwinnr(s:plug_buf) - execute winnr 'wincmd w' + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif - call add(s:pos, winsaveview()) setlocal modifiable return 1 endfunction function! s:switch_out() - call winrestview(s:pos[3]) + call winrestview(s:pos[-1]) setlocal nomodifiable - execute 'normal!' s:pos[0].'gt' - execute s:pos[1] 'wincmd w' - call winrestview(s:pos[2]) + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif endfunction function! s:prepare() From 018adb2aefe98b25c7ccbd8802e3ca485cc4929b Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 10:06:29 +0900 Subject: [PATCH 04/13] Disable NVim parallel installer on Windows --- plug.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plug.vim b/plug.vim index 30221db..ea3d593 100644 --- a/plug.vim +++ b/plug.vim @@ -73,7 +73,7 @@ let s:plug_tab = get(s:, 'plug_tab', -1) let s:plug_buf = get(s:, 'plug_buf', -1) let s:mac_gui = has('gui_macvim') && has('gui_running') let s:is_win = has('win32') || has('win64') -let s:nvim = exists('##JobActivity') +let s:nvim = exists('##JobActivity') && !s:is_win let s:me = resolve(expand(':p')) let s:base_spec = { 'branch': 'master', 'frozen': 0 } let s:TYPE = { From ee9f0e55b8804642be20fd71aafa1ed5012d3918 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 10:10:52 +0900 Subject: [PATCH 05/13] Use s:lines instead of split --- plug.vim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plug.vim b/plug.vim index ea3d593..90c7e6f 100644 --- a/plug.vim +++ b/plug.vim @@ -125,7 +125,7 @@ endfunction function! s:source(from, ...) for pattern in a:000 - for vim in split(globpath(a:from, pattern), '\n') + for vim in s:lines(globpath(a:from, pattern)) execute 'source' vim endfor endfor @@ -1134,7 +1134,7 @@ function! s:shellesc(arg) endfunction function! s:glob_dir(path) - return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)') + return map(filter(s:lines(globpath(a:path, '**')), 'isdirectory(v:val)'), 's:dirpath(v:val)') endfunction function! s:progress_bar(line, bar, total) @@ -1174,7 +1174,7 @@ function! s:git_valid(spec, check_branch) let ret = 1 let msg = 'OK' if isdirectory(a:spec.dir) - let result = split(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url', a:spec.dir), '\n') + let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url', a:spec.dir)) let remote = result[-1] if v:shell_error let msg = join([remote, 'PlugClean required.'], "\n") @@ -1269,7 +1269,7 @@ function! s:upgrade() if executable('curl') let output = system(printf('curl -fLo %s %s', s:shellesc(new), s:plug_source)) if v:shell_error - throw get(split(output, '\n'), -1, v:shell_error) + throw get(s:lines(output), -1, v:shell_error) endif elseif has('ruby') ruby << EOF @@ -1438,7 +1438,7 @@ function! s:diff() if !empty(diff) call append(1, '') call append(2, '- '.k.':') - call append(3, map(split(diff, '\n'), '" ". v:val')) + call append(3, map(s:lines(diff), '" ". v:val')) let cnt += 1 normal! gg redraw From 8c915a527155b2e57037c3d9bb1c547293207b13 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 10:23:00 +0900 Subject: [PATCH 06/13] Reuse plug window even if it's in another tab --- plug.vim | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/plug.vim b/plug.vim index 90c7e6f..7f56521 100644 --- a/plug.vim +++ b/plug.vim @@ -498,7 +498,8 @@ function! s:new_window() endfunction function! s:plug_window_exists() - return index(tabpagebuflist(s:plug_tab), s:plug_buf) >= 0 + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 endfunction function! s:switch_in() @@ -533,15 +534,7 @@ endfunction function! s:prepare() call s:job_abort() - if bufexists(s:plug_buf) - let winnr = bufwinnr(s:plug_buf) - if winnr < 0 - call s:new_window() - execute 'buffer' s:plug_buf - else - execute winnr 'wincmd w' - endif - setlocal modifiable + if s:switch_in() silent %d _ else call s:new_window() From 385a1eb35074bb231826c4085387bbafeff451ca Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 10:28:15 +0900 Subject: [PATCH 07/13] Detect abnormal process exit using 'Error' line --- plug.vim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plug.vim b/plug.vim index 7f56521..de98a32 100644 --- a/plug.vim +++ b/plug.vim @@ -758,6 +758,10 @@ function! s:job_handler() abort if v:job_data[1] == 'exit' let job.running = 0 + if s:lastline(job.result) ==# 'Error' + let job.error = 1 + let job.result = substitute(job.result, "Error[\r\n]$", '', '') + endif call s:reap(name) call s:tick() else @@ -777,7 +781,8 @@ function! s:spawn(name, cmd, opts) if s:nvim let x = jobstart(a:name, 'sh', ['-c', - \ has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd]) + \ (has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd) + \ . ' || echo Error']) if x > 0 let s:jobs_idx[x] = a:name let job.jobid = x From 15da7eb78a9e733ed90e323bba53b7df1d756f8f Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 10:39:49 +0900 Subject: [PATCH 08/13] Code cleanup --- plug.vim | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plug.vim b/plug.vim index de98a32..95fab6c 100644 --- a/plug.vim +++ b/plug.vim @@ -667,7 +667,7 @@ function! s:update_impl(pull, force, args) abort call mkdir(g:plug_home, 'p') catch return s:err(printf('Invalid plug directory: %s.' - \ 'Try to call plug#begin with a valid directory', g:plug_home)) + \ 'Try to call plug#begin with a valid directory', g:plug_home)) endtry endif @@ -686,6 +686,7 @@ function! s:update_impl(pull, force, args) abort call s:prepare() call append(0, ['', '']) + normal! 2G if has('ruby') && s:update.threads > 1 try @@ -820,7 +821,6 @@ function! s:reap(name) call s:bar() call remove(s:jobs, a:name) - return 1 endfunction function! s:bar() @@ -863,7 +863,6 @@ function! s:update_vim() let s:jobs_idx = {} call s:bar() - normal! 2G call s:tick() endfunction @@ -908,11 +907,10 @@ while 1 " Without TCO, Vim stack is bound to explode if !s:jobs[name].running call s:reap(name) - continue - elseif len(s:jobs) < s:update.threads - continue endif - break + if len(s:jobs) >= s:update.threads + break + endif endwhile endfunction From 61b21068ee2d26f02799ef809d273811181d812f Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 10:54:00 +0900 Subject: [PATCH 09/13] Update test case (related: #8c915a5) --- test/workflow.vader | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/workflow.vader b/test/workflow.vader index fdbee76..3c075b3 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -337,7 +337,8 @@ Execute (Rollback recent updates, PlugUpdate, then PlugDiff): AssertEqual 1, &previewwindow pclose -Execute (Plug window in a new tab): +Execute (Reuse Plug window in another tab): + let tabnr = tabpagenr() PlugDiff tab new new-tab set buftype=nofile @@ -345,8 +346,8 @@ Execute (Plug window in a new tab): normal D AssertExpect '^- ', 1 normal q - AssertEqual 'new-tab', expand('%') - q + AssertEqual tabnr, tabpagenr() + normal! gt q ********************************************************************** From 2889cb4739cef6e9b0dc240b0e470fcf37e0472b Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 15:48:17 +0900 Subject: [PATCH 10/13] Minor refactoring --- plug.vim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plug.vim b/plug.vim index 95fab6c..766827f 100644 --- a/plug.vim +++ b/plug.vim @@ -68,7 +68,7 @@ let g:loaded_plug = 1 let s:cpo_save = &cpo set cpo&vim -let s:plug_source = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' +let s:plug_src = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' let s:plug_tab = get(s:, 'plug_tab', -1) let s:plug_buf = get(s:, 'plug_buf', -1) let s:mac_gui = has('gui_macvim') && has('gui_running') @@ -98,7 +98,6 @@ function! plug#begin(...) let g:plug_home = home let g:plugs = {} - " we want to keep track of the order plugins where registered. let g:plugs_order = [] call s:define_commands() @@ -521,9 +520,12 @@ function! s:switch_in() return 1 endfunction -function! s:switch_out() +function! s:switch_out(...) call winrestview(s:pos[-1]) setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif if len(s:pos) > 1 execute 'normal!' s:pos[0].'gt' @@ -616,8 +618,6 @@ function! s:finish(pull) call plug#helptags() call plug#end() call setline(4, getline(4) . 'Done!') - normal! gg - call s:syntax() redraw let msgs = [] if !empty(s:update.errors) @@ -700,7 +700,7 @@ function! s:update_impl(pull, force, args) abort let printed = {} silent 4,$d _ for line in lines - let name = matchstr(line, '^. \zs[^:]\+\ze:') + let name = s:extract_name(line, '.', '') if empty(name) || !has_key(printed, name) call append('$', line) if !empty(name) @@ -725,7 +725,7 @@ function! s:update_finish() call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'has_key(v:val, "do")')) call s:finish(s:update.pull) call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') - call s:switch_out() + call s:switch_out('normal! gg') endif endfunction @@ -798,9 +798,9 @@ function! s:spawn(name, cmd, opts) endif else let params = has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd] - let job.running = 0 let job.result = call('s:system', params) let job.error = v:shell_error != 0 + let job.running = 0 endif endfunction @@ -1259,11 +1259,11 @@ endfunction function! s:upgrade() let new = s:me . '.new' - echo 'Downloading '. s:plug_source + echo 'Downloading '. s:plug_src redraw try if executable('curl') - let output = system(printf('curl -fLo %s %s', s:shellesc(new), s:plug_source)) + let output = system(printf('curl -fLo %s %s', s:shellesc(new), s:plug_src)) if v:shell_error throw get(s:lines(output), -1, v:shell_error) endif @@ -1271,7 +1271,7 @@ function! s:upgrade() ruby << EOF require 'open-uri' File.open(VIM::evaluate('new'), 'w') do |f| - f << open(VIM::evaluate('s:plug_source')).read + f << open(VIM::evaluate('s:plug_src')).read end EOF else From 05008e7a82b9d83669481e9b56468cd8660b45d6 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 17:34:39 +0900 Subject: [PATCH 11/13] Use s:extract_name instead of matchstr --- plug.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plug.vim b/plug.vim index 766827f..36f030a 100644 --- a/plug.vim +++ b/plug.vim @@ -1383,7 +1383,7 @@ function! s:find_name(lnum) if empty(line) return '' endif - let name = matchstr(line, '\(^- \)\@<=[^:]\+') + let name = s:extract_name(line, '-', '') if !empty(name) return name endif From 60bda7322f012a4ac5c1478b0eb3a15698b1037d Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 17:35:07 +0900 Subject: [PATCH 12/13] Improve ]]/[[ movement --- plug.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plug.vim b/plug.vim index 36f030a..bd515d5 100644 --- a/plug.vim +++ b/plug.vim @@ -1415,7 +1415,7 @@ function! s:preview_commit() endfunction function! s:section(flags) - call search('\(^- \)\@<=.', a:flags) + call search('\(^[x-] \)\@<=[^:]\+:', a:flags) endfunction function! s:diff() From a5c3952380415a0fcdaa6f41ebcc4d735f88eeee Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 10 Oct 2014 17:36:54 +0900 Subject: [PATCH 13/13] Sort auto-completion candidates --- plug.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plug.vim b/plug.vim index bd515d5..36b7f71 100644 --- a/plug.vim +++ b/plug.vim @@ -643,7 +643,7 @@ function! s:is_managed(name) endfunction function! s:names(...) - return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)') + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) endfunction function! s:update_impl(pull, force, args) abort