From 466d1839b37c174a66b329fadce2512b0a2ffa5e Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 16 Jul 2014 19:52:58 +0900 Subject: [PATCH] Add support for local plugins. Add `frozen` option (#32) --- README.md | 11 +++ plug.vim | 114 ++++++++++++++++++++----------- test/run | 8 ++- test/workflow.vader | 161 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 240 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 671236b..badb47d 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Plug 'tpope/vim-fireplace', { 'for': 'clojure' } " Plug 'user/repo1', 'branch_or_tag' " Plug 'user/repo2', { 'rtp': 'vim/plugin/dir', 'branch': 'devel' } " Plug 'git@github.com:junegunn/vim-github-dashboard.git' +" Plug '/my/local/vim/plugin' " ... call plug#end() @@ -70,6 +71,16 @@ before the call. | PlugStatus | Check the status of plugins | | PlugDiff | See the updated changes from the previous PlugUpdate | +### `Plug` options + +| Option | Description | +| -------------- | -------------------------------------------------------------------- | +| `branch`/`tag` | Branch or tag of the repository to use | +| `rtp` | Subdirectory that contains Vim plugin | +| `on` | On-demand loading: Commands or -mappings | +| `for` | On-demand loading: File types | +| `frozen` | Do not install/update plugin unless explicitly given as the argument | + ### Options for parallel installer | Flag | Default | Description | diff --git a/plug.vim b/plug.vim index cf04ab3..b81db43 100644 --- a/plug.vim +++ b/plug.vim @@ -257,17 +257,19 @@ function! s:lod_map(map, name, prefix) call feedkeys(a:prefix . substitute(a:map, '^', "\", '') . extra) endfunction -function! s:add(...) - let force = a:1 - let opts = { 'branch': 'master' } - if a:0 == 2 - let plugin = a:2 - elseif a:0 == 3 - let plugin = a:2 - if type(a:3) == 1 - let opts.branch = a:3 - elseif type(a:3) == 4 - call extend(opts, a:3) +function! s:add(force, ...) + let opts = { 'branch': 'master', 'frozen': 0 } + if a:0 == 1 + let plugin = a:1 + elseif a:0 == 2 + let plugin = a:1 + if type(a:2) == 1 + let opts.branch = a:2 + elseif type(a:2) == 4 + call extend(opts, a:2) + if has_key(opts, 'tag') + let opts.branch = remove(opts, 'tag') + endif else echoerr "Invalid argument type (expected: string or dictionary)" return @@ -277,21 +279,32 @@ function! s:add(...) return endif + let plugin = substitute(plugin, '[/\\]*$', '', '') let name = substitute(split(plugin, '/')[-1], '\.git$', '', '') - if !force && has_key(g:plugs, name) | return | endif - - if plugin =~ ':' - let uri = plugin - else - if plugin !~ '/' - let plugin = 'vim-scripts/'. plugin - endif - let uri = 'https://git:@github.com/' . plugin . '.git' + if !a:force && has_key(g:plugs, name) + let s:extended[name] = g:plugs[name] + return + endif + + if plugin[0] =~ '[/$~]' || plugin =~? '^[a-z]:' + let spec = extend(opts, { 'dir': s:dirpath(expand(plugin)) }) + else + if plugin =~ ':' + let uri = plugin + else + if plugin !~ '/' + let plugin = 'vim-scripts/'. plugin + endif + let uri = 'https://git:@github.com/' . plugin . '.git' + endif + let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') ) + let spec = extend(opts, { 'dir': dir, 'uri': uri }) endif - let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') ) - let spec = extend(opts, { 'dir': dir, 'uri': uri }) let g:plugs[name] = spec + if !a:force + let s:extended[name] = spec + endif let g:plugs_order += [name] endfunction @@ -413,8 +426,12 @@ function! s:finish(pull) endif endfunction +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + function! s:names(...) - return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0') + return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)') endfunction function! s:update_impl(pull, args) abort @@ -422,8 +439,9 @@ function! s:update_impl(pull, args) abort let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? \ remove(args, -1) : get(g:, 'plug_threads', 16) - let todo = empty(args) ? g:plugs : - \ filter(copy(g:plugs), 'index(args, v:key) >= 0') + 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') if empty(todo) echohl WarningMsg @@ -465,7 +483,7 @@ function! s:update_impl(pull, args) abort endfunction function! s:extend(names) - let prev = copy(g:plugs) + let s:extended = {} try command! -nargs=+ Plug call s:add(0, ) for name in a:names @@ -477,7 +495,7 @@ function! s:extend(names) finally command! -nargs=+ Plug call s:add(1, ) endtry - return filter(copy(g:plugs), '!has_key(prev, v:key)') + return s:extended endfunction function! s:update_progress(pull, cnt, bar, total) @@ -512,6 +530,7 @@ function! s:update_serial(pull, todo) let result = msg let error = 1 endif + cd - else if !isdirectory(base) call mkdir(base, 'p') @@ -524,14 +543,14 @@ function! s:update_serial(pull, todo) \ s:shellesc(spec.dir))) let error = v:shell_error != 0 endif - cd - let bar .= error ? 'x' : '=' call append(3, s:format_message(!error, name, result)) call s:update_progress(a:pull, len(done), bar, total) endfor - if !empty(s:extend(keys(todo))) - let todo = filter(copy(g:plugs), '!has_key(done, v:key)') + let extended = s:extend(keys(todo)) + if !empty(extended) + let todo = filter(extended, '!has_key(done, v:key)') let total += len(todo) call s:update_progress(a:pull, len(done), bar, total) else @@ -565,11 +584,12 @@ function! s:update_parallel(pull, todo, threads) %["#{arg.gsub('"', '\"')}"] end - st = Time.now + require 'set' require 'thread' require 'fileutils' require 'timeout' running = true + st = Time.now iswin = VIM::evaluate('s:is_win').to_i == 1 pull = VIM::evaluate('a:pull').to_i == 1 base = VIM::evaluate('g:plug_home') @@ -578,14 +598,13 @@ function! s:update_parallel(pull, todo, threads) nthr = VIM::evaluate('a:threads').to_i maxy = VIM::evaluate('winheight(".")').to_i cd = iswin ? 'cd /d' : 'cd' - tot = 0 + tot = VIM::evaluate('len(a:todo)') || 0 bar = '' skip = 'Already installed' mtx = Mutex.new take1 = proc { mtx.synchronize { running && all.shift } } logh = proc { - cnt = $curbuf[2][1...-1].strip.length - tot = VIM::evaluate('len(a:todo)') || tot + cnt = bar.length $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" $curbuf[2] = '[' + bar.ljust(tot) + ']' VIM::command('normal! 2G') @@ -672,9 +691,11 @@ function! s:update_parallel(pull, todo, threads) main.kill } + processed = Set.new progress = iswin ? '' : '--progress' until all.empty? names = all.keys + processed.merge names [names.length, nthr].min.times do mtx.synchronize do threads << Thread.new { @@ -719,7 +740,11 @@ function! s:update_parallel(pull, todo, threads) end threads.each { |t| t.join rescue nil } mtx.synchronize { threads.clear } - all.merge!(VIM::evaluate("s:extend(#{names.inspect})") || {}) + extended = Hash[(VIM::evaluate("s:extend(#{names.inspect})") || {}).reject { |k, _| + processed.include? k + }] + tot += extended.length + all.merge!(extended) logh.call end watcher.kill @@ -804,8 +829,9 @@ function! s:clean(force) " List of valid directories let dirs = [] - let [cnt, total] = [0, len(g:plugs)] - for spec in values(g:plugs) + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let [cnt, total] = [0, len(managed)] + for spec in values(managed) if s:git_valid(spec, 0, 1)[0] call add(dirs, spec.dir) endif @@ -906,10 +932,18 @@ function! s:status() let ecnt = 0 let [cnt, total] = [0, len(g:plugs)] for [name, spec] in items(g:plugs) - if isdirectory(spec.dir) - let [valid, msg] = s:git_valid(spec, 1, 1) + if has_key(spec, 'uri') + if isdirectory(spec.dir) + let [valid, msg] = s:git_valid(spec, 1, 1) + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif else - let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + if isdirectory(spec.dir) + let [valid, msg] = [1, 'OK'] + else + let [valid, msg] = [0, 'Not found.'] + endif endif let cnt += 1 let ecnt += !valid diff --git a/test/run b/test/run index a8098ee..8c302d6 100755 --- a/test/run +++ b/test/run @@ -9,13 +9,19 @@ if [ ! -d vader.vim ]; then git clone https://github.com/junegunn/vader.vim.git fi +rm -rf fzf +if [ ! -d fzf-staged ]; then + git clone https://github.com/junegunn/fzf.git fzf-staged +fi + cat > /tmp/mini-vimrc << VIMRC set rtp+=vader.vim source $PLUG_SRC VIMRC if [ "$1" = '!' ]; then - /usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'Vader! workflow.vader' > /dev/null + /usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'Vader! workflow.vader' > /dev/null && + /usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'let g:plug_threads = 1 | Vader! workflow.vader' > /dev/null else /usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'Vader workflow.vader' fi diff --git a/test/workflow.vader b/test/workflow.vader index 55c89cc..b897804 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -68,15 +68,21 @@ Execute (Subsequent plug#begin() calls will reuse g:plug_home): Execute (Test Plug command): " Git repo with branch - Plug 'junegunn/seoul256.vim', 'no-t_co' + Plug 'junegunn/seoul256.vim', 'yes-t_co' AssertEqual 'https://git:@github.com/junegunn/seoul256.vim.git', g:plugs['seoul256.vim'].uri AssertEqual join([temp_plugged, 'seoul256.vim/'], '/'), g:plugs['seoul256.vim'].dir + AssertEqual 'yes-t_co', g:plugs['seoul256.vim'].branch + + Plug 'junegunn/seoul256.vim', { 'branch': 'no-t_co' } " Using branch option AssertEqual 'no-t_co', g:plugs['seoul256.vim'].branch " Git repo with tag - Plug 'junegunn/goyo.vim', '1.5.3' + Plug 'junegunn/goyo.vim', '1.5.2' AssertEqual 'https://git:@github.com/junegunn/goyo.vim.git', g:plugs['goyo.vim'].uri AssertEqual join([temp_plugged, 'goyo.vim/'], '/'), g:plugs['goyo.vim'].dir + AssertEqual '1.5.2', g:plugs['goyo.vim'].branch + + Plug 'junegunn/goyo.vim', { 'tag': '1.5.3' } " Using tag option AssertEqual '1.5.3', g:plugs['goyo.vim'].branch " Git URI @@ -103,12 +109,12 @@ Execute (Plug command with dictionary option): Execute (PlugStatus before installation): PlugStatus - AssertEqual 4, len(filter(getline(1, line('$')), 'v:val =~ "Not found"')) + AssertEqual 4, len(filter(getline(1, '$'), 'v:val =~ "Not found"')) q Execute (PlugClean before installation): PlugClean - AssertEqual 1, len(filter(getline(1, line('$')), 'v:val =~ "Already clean"')) + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "Already clean"')) q Execute (plug#end() updates &rtp): @@ -128,12 +134,12 @@ Execute (Plugin available after installation): Execute (PlugClean after installation): PlugClean - AssertEqual 1, len(filter(getline(1, line('$')), 'v:val =~ "Already clean"')) + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "Already clean"')) q Execute (PlugStatus after installation): PlugStatus - AssertEqual 4, len(filter(getline(1, line('$')), 'v:val =~ "OK"')) + AssertEqual 4, len(filter(getline(1, '$'), 'v:val =~ "OK"')) q Execute (Change tag of goyo.vim): @@ -204,8 +210,8 @@ Expect: Execute (PlugClean! to remove seoul256.vim): PlugClean! " Three removed, emoji left - AssertEqual 3, len(filter(getline(1, line('$')), 'v:val =~ "^- "')) - AssertEqual 1, len(filter(getline(1, line('$')), 'v:val =~ "Removed"')) + AssertEqual 3, len(filter(getline(1, '$'), 'v:val =~ "^- "')) + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "Removed"')) Assert empty(globpath(&rtp, 'colors/seoul256.vim')) Assert !empty(globpath(&rtp, 'autoload/emoji.vim')) q @@ -231,15 +237,15 @@ Expect: Execute (PlugClean! to remove vim-emoji): PlugClean! - AssertEqual 1, len(filter(getline(1, line('$')), 'v:val =~ "^- "')) - AssertEqual 1, len(filter(getline(1, line('$')), 'v:val =~ "Removed"')) + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "^- "')) + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "Removed"')) Assert empty(globpath(&rtp, 'colors/seoul256.vim')) Assert empty(globpath(&rtp, 'autoload/emoji.vim')) q Execute (PlugUpdate to install both again): PlugUpdate - AssertEqual 2, len(filter(getline(1, line('$')), 'v:val =~ "^- [^:]*:"')) + AssertEqual 2, len(filter(getline(1, '$'), 'v:val =~ "^- [^:]*:"')) AssertEqual 3, g:vimrc_reloaded Assert !empty(globpath(&rtp, 'colors/seoul256.vim')), 'seoul256.vim should be found' Assert !empty(globpath(&rtp, 'autoload/emoji.vim')), 'vim-emoji should be found' @@ -247,7 +253,7 @@ Execute (PlugUpdate to install both again): Execute (PlugUpdate only to find out plugins are up-to-date, D key to check): PlugUpdate - AssertEqual 2, len(filter(getline(1, line('$')), 'v:val =~ "Already up-to-date"')) + AssertEqual 2, len(filter(getline(1, '$'), 'v:val =~ "Already up-to-date"')) AssertEqual 4, g:vimrc_reloaded normal D AssertEqual 'No updates.', getline(1) @@ -354,7 +360,9 @@ Execute (Check commands): Execute (Partial PlugInstall): PlugInstall vim-fnr vim-easy-align + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "vim-pseudocl"')) PlugInstall vim-fnr vim-easy-align 1 + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "vim-pseudocl"')) q Execute (Check dependent plugin): @@ -384,13 +392,140 @@ Execute (On-demand loading based on filetypes): set ft=redis Assert exists(':RedisExecute'), 'RedisExecute command is now found' +********************************************************************** +~ Local (unmanaged) plugins +********************************************************************** + +Execute (Add unmanaged plugin): + let fzf = fnamemodify(g:vader_file, ':h') . '/fzf' + Log fzf + + call plug#begin() + Plug fzf, { 'on': 'SomeCommand' } + call plug#end() + + " Check uri field + Assert !has_key(g:plugs.fzf, 'uri'), 'Should not have uri field' + + " Check dir field + AssertEqual fzf.'/', g:plugs.fzf.dir + + " Trailing slashes and backslashes should be stripped + for suffix in ['///', '/\/\/'] + call plug#begin() + Plug fzf.suffix, { 'on': 'SomeCommand' } + call plug#end() + + " Check dir field + AssertEqual fzf.'/', g:plugs.fzf.dir + endfor + +Execute (Plug block for following tests): + call plug#begin() + Plug 'junegunn/vim-easy-align' + Plug fzf, { 'on': 'SomeCommand' } + call plug#end() + " Remove plugins from previous tests + PlugClean! + +Execute (PlugInstall will only install vim-easy-align): + PlugInstall + Log getline(1, '$') + AssertEqual 0, len(filter(getline(1, '$'), 'v:val =~ "fzf"')) + q + +Execute (PlugUpdate will only update vim-easy-align): + PlugUpdate + Log getline(1, '$') + AssertEqual 0, len(filter(getline(1, '$'), 'v:val =~ "fzf"')) + q + +Execute (PlugClean should not care about unmanaged plugins): + PlugClean + Log getline(1, '$') + AssertEqual 0, len(filter(getline(1, '$'), 'v:val =~ "fzf"')) + q + +Execute (PlugStatus should point out that the plugin is missing): + PlugStatus + Log getline(1, '$') + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "x fzf"')) + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "Not found"')) + q + +Execute (Deploy unmanaged plugin): + Assert !exists(':FZF'), ':FZF command should not exist' + call rename('fzf-staged', 'fzf') + +Execute (PlugUpdate still should not care): + PlugUpdate + Log getline(1, '$') + AssertEqual 0, len(filter(getline(1, '$'), 'v:val =~ "fzf"')) + q + +Execute (PlugStatus with no error): + PlugStatus + Log getline(1, '$') + AssertEqual 0, len(filter(getline(1, '$'), 'v:val =~ "x fzf"')) + AssertEqual 0, len(filter(getline(1, '$'), 'v:val =~ "Not found"')) + q + +Execute (Check &rtp after SomeCommand): + Log &rtp + Assert &rtp !~ 'fzf' + silent! SomeCommand + Assert &rtp =~ 'fzf' + +Execute (Common parent): + call plug#begin() + Plug 'junegunn/vim-pseudocl' + Plug 'junegunn/vim-fnr' + Plug 'junegunn/vim-oblique' + call plug#end() + + PlugInstall + Log getline(1, '$') + AssertEqual 1, len(filter(getline(1, '$'), 'v:val == "[===]"')) + q + +********************************************************************** +~ Frozen plugins +********************************************************************** + +Execute (Frozen plugin are not installed nor updated): + call plug#begin() + Plug 'junegunn/vim-easy-align', { 'frozen': 1 } + call plug#end() + + redir => output + silent PlugInstall + redir END + Assert output =~ 'No plugin to install' + + redir => output + silent PlugUpdate + redir END + Assert output =~ 'No plugin to update' + +Execute (But you can still install it if the name is given as the argument): + PlugInstall vim-easy-align + Log getline(1, '$') + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "vim-easy-align"')) + q + + PlugUpdate vim-easy-align + Log getline(1, '$') + AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "vim-easy-align"')) + q + Execute (Cleanup): call system('rm -rf '.temp_plugged) + call rename('fzf', 'fzf-staged') unlet g:plugs unlet g:plug_home unlet g:vimrc_reloaded - unlet temp_plugged vader plug basertp save_rtp repo lnum + unlet temp_plugged vader plug basertp save_rtp repo lnum fzf delf PlugStatusSorted Restore