vim-plug/plug.vim

539 lines
14 KiB
VimL
Raw Normal View History

2013-09-10 07:58:41 -07:00
" vim-plug: Vim plugin manager
" ============================
"
" Download plug.vim and put it in ~/.vim/autoload
"
" mkdir -p ~/.vim/autoload
2013-09-10 20:06:57 -07:00
" curl -fLo ~/.vim/autoload/plug.vim \
2013-09-10 07:58:41 -07:00
" https://raw.github.com/junegunn/vim-plug/master/plug.vim
"
" Edit your .vimrc
"
2013-09-16 01:37:09 -07:00
" call plug#begin()
2013-09-10 07:58:41 -07:00
"
" Plug 'junegunn/seoul256'
" Plug 'junegunn/vim-easy-align'
" " Plug 'user/repo1', 'branch_or_tag'
" " Plug 'user/repo2', { 'rtp': 'vim/plugin/dir', 'branch': 'devel' }
2013-09-10 07:58:41 -07:00
" " ...
"
2013-09-16 01:37:09 -07:00
" call plug#end()
"
2013-09-10 07:58:41 -07:00
" Then :PlugInstall to install plugins. (default: ~/.vim/plugged)
2013-09-16 01:37:09 -07:00
" You can change the location of the plugins with plug#begin(path) call.
2013-09-10 07:58:41 -07:00
"
"
" Copyright (c) 2013 Junegunn Choi
"
" MIT License
"
" Permission is hereby granted, free of charge, to any person obtaining
" a copy of this software and associated documentation files (the
" "Software"), to deal in the Software without restriction, including
" without limitation the rights to use, copy, modify, merge, publish,
" distribute, sublicense, and/or sell copies of the Software, and to
" permit persons to whom the Software is furnished to do so, subject to
" the following conditions:
"
" The above copyright notice and this permission notice shall be
" included in all copies or substantial portions of the Software.
"
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if exists('g:loaded_plug')
finish
endif
let g:loaded_plug = 1
2013-09-10 08:08:36 -07:00
let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
let s:plug_file = 'Plugfile'
2013-09-10 07:58:41 -07:00
let s:plug_win = 0
let s:is_win = has('win32') || has('win64')
let s:me = expand('<sfile>:p')
2013-09-16 01:37:09 -07:00
function! plug#begin(...)
let home = a:0 > 0 ? fnamemodify(a:1, ':p') :
\ get(g:, 'plug_home', split(&rtp, ',')[0].'/plugged')
2013-09-10 07:58:41 -07:00
if !isdirectory(home)
try
call mkdir(home, 'p')
catch
echoerr 'Invalid plug directory: '. home
return 0
2013-09-10 07:58:41 -07:00
endtry
endif
if !executable('git')
echoerr "`git' executable not found. vim-plug requires git."
return 0
2013-09-10 07:58:41 -07:00
endif
let g:plug_home = home
let g:plugs = {}
2013-09-10 07:58:41 -07:00
command! -nargs=+ Plug call s:add(1, <args>)
2013-09-10 07:58:41 -07:00
command! -nargs=* PlugInstall call s:install(<f-args>)
command! -nargs=* PlugUpdate call s:update(<f-args>)
2013-09-17 06:57:13 -07:00
command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
command! -nargs=0 PlugUpgrade if s:upgrade() | execute "source ". s:me | endif
2013-09-23 01:33:36 -07:00
command! -nargs=0 PlugStatus call s:status()
return 1
2013-09-10 07:58:41 -07:00
endfunction
2013-09-16 01:37:09 -07:00
function! plug#end()
let keys = keys(g:plugs)
while !empty(keys)
let keys = keys(s:extend(keys))
endwhile
2013-09-16 01:37:09 -07:00
set nocompatible
filetype off
for plug in values(g:plugs)
2013-09-25 06:35:34 -07:00
let rtp = s:rtp(plug)
execute "set rtp^=".rtp
2013-09-25 06:35:34 -07:00
if isdirectory(rtp.'after')
execute "set rtp+=".rtp.'after'
2013-09-16 01:37:09 -07:00
endif
endfor
filetype plugin indent on
syntax on
endfunction
2013-09-25 06:35:34 -07:00
function! s:rtp(spec)
2013-09-25 23:08:52 -07:00
return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
2013-09-25 06:35:34 -07:00
endfunction
2013-09-10 07:58:41 -07:00
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)
else
echoerr "Invalid argument type (expected: string or dictionary)"
return
endif
2013-09-10 07:58:41 -07:00
else
echoerr "Invalid number of arguments (1..2)"
return
endif
2013-09-16 20:35:10 -07:00
if plugin =~ ':'
let uri = plugin
else
if plugin !~ '/'
let plugin = 'vim-scripts/'. plugin
endif
let uri = 'https://git:@github.com/' . plugin . '.git'
2013-09-10 07:58:41 -07:00
endif
2013-09-16 20:35:10 -07:00
let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
if !force && has_key(g:plugs, name) | return | endif
2013-09-25 23:08:52 -07:00
let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') )
let spec = extend(opts, { 'dir': dir, 'uri': uri })
2013-09-16 20:35:10 -07:00
let g:plugs[name] = spec
2013-09-10 07:58:41 -07:00
endfunction
function! s:install(...)
call s:update_impl(0, a:000)
endfunction
function! s:update(...)
call s:update_impl(1, a:000)
endfunction
function! s:apply()
for spec in values(g:plugs)
2013-09-10 07:58:41 -07:00
let docd = join([spec.dir, 'doc'], '/')
if isdirectory(docd)
execute "helptags ". join([spec.dir, 'doc'], '/')
endif
endfor
runtime! plugin/*.vim
runtime! after/*.vim
2013-09-10 20:29:36 -07:00
silent! source $MYVIMRC
2013-09-10 07:58:41 -07:00
endfunction
function! s:syntax()
syntax clear
syntax region plug1 start=/\%1l/ end=/\%2l/ contains=ALL
syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
2013-09-22 07:25:02 -07:00
syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1 contained
syn match plugBracket /[[\]]/ containedin=plug2 contained
2013-09-10 07:58:41 -07:00
syn match plugDash /^-/
syn match plugName /\(^- \)\@<=[^:]*/
syn match plugError /^- [^:]\+: (x).*/
2013-09-10 07:58:41 -07:00
hi def link plug1 Title
hi def link plug2 Repeat
hi def link plugBracket Structure
hi def link plugNumber Number
hi def link plugDash Special
hi def link plugName Label
hi def link plugError Error
2013-09-10 07:58:41 -07:00
endfunction
function! s:lpad(str, len)
return a:str . repeat(' ', a:len - len(a:str))
endfunction
function! s:system(cmd)
2013-09-16 20:35:10 -07:00
let lines = split(system(a:cmd), '\n')
return get(lines, -1, '')
2013-09-10 07:58:41 -07:00
endfunction
function! s:prepare()
execute s:plug_win . 'wincmd w'
if exists('b:plug')
%d
else
vertical topleft new
noremap <silent> <buffer> q :q<cr>
let b:plug = 1
let s:plug_win = winnr()
call s:assign_name()
endif
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
setf vim-plug
call s:syntax()
endfunction
function! s:assign_name()
" Assign buffer name
let prefix = '[Plugins]'
let name = prefix
let idx = 2
while bufexists(name)
let name = printf("%s (%s)", prefix, idx)
let idx = idx + 1
endwhile
silent! execute "f ".fnameescape(name)
endfunction
function! s:finish()
call append(line('$'), '')
call append(line('$'), 'Finishing ... ')
redraw
call s:apply()
call s:syntax()
call setline(line('$'), getline(line('$')) . 'Done!')
normal! G
endfunction
function! s:update_impl(pull, args)
let threads = len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)
2013-09-10 07:58:41 -07:00
call s:prepare()
2013-09-23 20:47:47 -07:00
call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
call append(1, '['. s:lpad('', len(g:plugs)) .']')
2013-09-10 07:58:41 -07:00
normal! 2G
redraw
if has('ruby') && threads > 1
2013-09-10 07:58:41 -07:00
call s:update_parallel(a:pull, threads)
else
call s:update_serial(a:pull)
endif
call s:finish()
endfunction
function! s:extend(names)
let prev = copy(g:plugs)
try
command! -nargs=+ Plug call s:add(0, <args>)
for name in a:names
2013-09-25 06:35:34 -07:00
let plugfile = s:rtp(g:plugs[name]) . s:plug_file
if filereadable(plugfile)
execute "source ". plugfile
endif
endfor
finally
command! -nargs=+ Plug call s:add(1, <args>)
endtry
return filter(copy(g:plugs), '!has_key(prev, v:key)')
endfunction
function! s:update_progress(pull, cnt, total)
call setline(1, (a:pull ? 'Updating' : 'Installing').
\ " plugins (".a:cnt."/".a:total.")")
call s:progress_bar(2, a:cnt, a:total)
normal! 2G
redraw
endfunction
2013-09-10 07:58:41 -07:00
function! s:update_serial(pull)
let st = reltime()
let base = g:plug_home
let todo = copy(g:plugs)
let total = len(todo)
let done = {}
while !empty(todo)
for [name, spec] in items(todo)
let done[name] = 1
if isdirectory(spec.dir)
execute 'cd '.spec.dir
if s:git_valid(spec, 0)
let result = a:pull ?
\ s:system(
\ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1',
\ spec.branch, spec.branch)) : 'Already installed'
let error = a:pull ? v:shell_error != 0 : 0
else
let result = "PlugClean required. Invalid remote."
let error = 1
endif
2013-09-16 20:35:10 -07:00
else
if !isdirectory(base)
call mkdir(base, 'p')
endif
execute 'cd '.base
2013-09-25 23:08:52 -07:00
let d = shellescape(substitute(spec.dir, '[\/]\+$', '', ''))
let result = s:system(
\ printf('git clone --recursive %s -b %s %s 2>&1',
\ shellescape(spec.uri), shellescape(spec.branch), d))
let error = v:shell_error != 0
2013-09-16 20:35:10 -07:00
endif
cd -
if error
let result = '(x) ' . result
2013-09-10 07:58:41 -07:00
endif
call append(3, '- ' . name . ': ' . result)
call s:update_progress(a:pull, len(done), total)
endfor
if !empty(s:extend(keys(todo)))
let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
let total += len(todo)
call s:update_progress(a:pull, len(done), total)
else
break
2013-09-10 07:58:41 -07:00
endif
endwhile
2013-09-10 07:58:41 -07:00
call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
endfunction
function! s:update_parallel(pull, threads)
ruby << EOF
2013-09-10 22:05:38 -07:00
require 'thread'
2013-09-10 07:58:41 -07:00
require 'fileutils'
st = Time.now
cd = VIM::evaluate('s:is_win').to_i == 1 ? 'cd /d' : 'cd'
pull = VIM::evaluate('a:pull').to_i == 1
base = VIM::evaluate('g:plug_home')
all = VIM::evaluate('copy(g:plugs)')
done = {}
2013-09-10 07:58:41 -07:00
skip = 'Already installed'
2013-09-10 20:06:57 -07:00
mtx = Mutex.new
take1 = proc { mtx.synchronize { all.shift } }
logh = proc {
cnt, tot = done.length, VIM::evaluate('len(g:plugs)')
$curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
$curbuf[2] = '[' + ('=' * cnt).ljust(tot) + ']'
VIM::command('normal! 2G')
VIM::command('redraw')
}
log = proc { |name, result, ok|
mtx.synchronize do
done[name] = true
2013-09-10 20:06:57 -07:00
result = '(x) ' + result unless ok
result = "- #{name}: #{result}"
2013-09-21 21:38:09 -07:00
$curbuf.append 3, result
logh.call
end
}
until all.empty?
names = all.keys
[names.length, VIM::evaluate('a:threads').to_i].min.times.map { |i|
Thread.new(i) do
while pair = take1.call
name = pair.first
dir, uri, branch = pair.last.values_at *%w[dir uri branch]
ok, result =
if File.directory? dir
current_uri = `#{cd} #{dir} && git config remote.origin.url`.chomp
if $? == 0 && current_uri == uri
if pull
output = `#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1`
[$? == 0, output]
else
[true, skip]
end
2013-09-16 20:35:10 -07:00
else
[false, "PlugClean required. Invalid remote."]
2013-09-16 20:35:10 -07:00
end
else
FileUtils.mkdir_p(base)
2013-09-25 23:08:52 -07:00
d = dir.sub(%r{[\\/]+$}, '')
r = `#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1`
[$? == 0, r]
2013-09-16 20:35:10 -07:00
end
result = result.lines.to_a.last.strip
log.call name, result, ok
end
2013-09-10 07:58:41 -07:00
end
}.each(&:join)
all.merge! VIM::evaluate("s:extend(#{names.inspect})")
logh.call
end
2013-09-10 07:58:41 -07:00
$curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
EOF
endfunction
2013-09-10 20:29:36 -07:00
function! s:path(path)
return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
\ '[/\\]*$', '', '')
endfunction
2013-09-25 23:08:52 -07:00
function! s:dirpath(path)
let path = s:path(a:path)
if s:is_win
return path !~ '\\$' ? path.'\' : path
else
return path !~ '/$' ? path.'/' : path
endif
endfunction
2013-09-10 07:58:41 -07:00
function! s:glob_dir(path)
2013-09-25 23:08:52 -07:00
return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
2013-09-10 07:58:41 -07:00
endfunction
2013-09-17 06:57:13 -07:00
function! s:progress_bar(line, cnt, total)
call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
endfunction
2013-09-16 20:35:10 -07:00
function! s:git_valid(spec, cd)
2013-09-17 06:57:13 -07:00
if isdirectory(a:spec.dir)
if a:cd | execute "cd " . a:spec.dir | endif
let ret = s:system("git config remote.origin.url") == a:spec.uri
if a:cd | cd - | endif
else
let ret = 0
endif
2013-09-16 20:35:10 -07:00
return ret
endfunction
2013-09-17 06:57:13 -07:00
function! s:clean(force)
2013-09-10 07:58:41 -07:00
call s:prepare()
2013-09-17 20:19:19 -07:00
call append(0, 'Searching for unused plugins in '.g:plug_home)
2013-09-17 06:57:13 -07:00
call append(1, '')
2013-09-10 07:58:41 -07:00
2013-09-16 20:35:10 -07:00
" List of valid directories
2013-09-17 06:57:13 -07:00
let dirs = []
let [cnt, total] = [0, len(g:plugs)]
for spec in values(g:plugs)
if s:git_valid(spec, 1)
call add(dirs, spec.dir)
endif
let cnt += 1
call s:progress_bar(2, cnt, total)
redraw
endfor
2013-09-16 20:35:10 -07:00
2013-09-10 07:58:41 -07:00
let allowed = {}
2013-09-25 23:08:52 -07:00
for dir in dirs
2013-09-10 07:58:41 -07:00
let allowed[dir] = 1
2013-09-25 23:08:52 -07:00
for child in s:glob_dir(dir)
let allowed[child] = 1
endfor
2013-09-10 07:58:41 -07:00
endfor
let todo = []
let found = sort(s:glob_dir(g:plug_home))
while !empty(found)
let f = remove(found, 0)
if !has_key(allowed, f) && isdirectory(f)
call add(todo, f)
call append(line('$'), '- ' . f)
let found = filter(found, 'stridx(v:val, f) != 0')
end
endwhile
normal! G
redraw
if empty(todo)
call append(line('$'), 'Already clean.')
else
call inputsave()
2013-09-17 20:23:43 -07:00
let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
2013-09-10 07:58:41 -07:00
call inputrestore()
2013-09-17 20:23:43 -07:00
if yes
2013-09-10 07:58:41 -07:00
for dir in todo
if isdirectory(dir)
call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
endif
endfor
call append(line('$'), 'Removed.')
else
call append(line('$'), 'Cancelled.')
endif
endif
normal! G
endfunction
function! s:upgrade()
if executable('curl')
let mee = shellescape(s:me)
let new = shellescape(s:me . '.new')
echo "Downloading ". s:plug_source
redraw
let mv = s:is_win ? 'move /Y' : 'mv -f'
call system(printf(
2013-09-10 20:06:57 -07:00
\ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
2013-09-10 07:58:41 -07:00
\ new, s:plug_source, mee, mee, new, mee))
if v:shell_error == 0
unlet g:loaded_plug
echo "Downloaded ". s:plug_source
return 1
2013-09-10 07:58:41 -07:00
else
echoerr "Error upgrading vim-plug"
return 0
2013-09-10 07:58:41 -07:00
endif
else
echoerr "`curl' not found"
return 0
2013-09-10 07:58:41 -07:00
endif
endfunction
2013-09-23 01:33:36 -07:00
function! s:status()
call s:prepare()
call append(0, 'Checking plugins')
let errs = 0
for [name, spec] in items(g:plugs)
let err = 'OK'
if isdirectory(spec.dir)
execute 'cd '.spec.dir
if s:git_valid(spec, 0)
let branch = s:system('git rev-parse --abbrev-ref HEAD')
if spec.branch != branch
let err = '(x) Invalid branch: '.branch.'. Try PlugUpdate.'
endif
else
let err = '(x) Invalid remote. Try PlugClean.'
endif
cd -
else
let err = '(x) Not found. Try PlugInstall.'
endif
let errs += err != 'OK'
call append(2, printf('- %s: %s', name, err))
call cursor(3, 1)
redraw
endfor
call setline(1, 'Finished. '.errs.' error(s).')
endfunction