dotvim/.vim/autoload/plug.vim

2172 lines
61 KiB
VimL
Raw Normal View History

2015-01-28 10:05:47 +00:00
" vim-plug: Vim plugin manager
" ============================
"
" Download plug.vim and put it in ~/.vim/autoload
"
2015-04-04 11:32:24 +00:00
" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
2015-01-28 10:05:47 +00:00
" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
"
" Edit your .vimrc
"
" call plug#begin('~/.vim/plugged')
"
" " Make sure you use single quotes
"
" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
2015-01-28 10:05:47 +00:00
" Plug 'junegunn/vim-easy-align'
"
" " Any valid git URL is allowed
" Plug 'https://github.com/junegunn/vim-github-dashboard.git'
"
" " Group dependencies, vim-snippets depends on ultisnips
" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
"
2015-01-28 10:05:47 +00:00
" " On-demand loading
" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
" Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
"
2015-11-19 08:58:34 +00:00
" " Using a non-master branch
" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
"
2015-01-28 10:05:47 +00:00
" " Plugin options
" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
2015-01-28 10:05:47 +00:00
"
" " Plugin outside ~/.vim/plugged with post-update hook
2015-11-19 08:58:34 +00:00
" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
2015-01-28 10:05:47 +00:00
"
" " Unmanaged plugin (manually installed and updated)
" Plug '~/my-prototype-plugin'
"
2015-07-31 08:42:27 +00:00
" " Add plugins to &runtimepath
2015-01-28 10:05:47 +00:00
" call plug#end()
"
" Then reload .vimrc and :PlugInstall to install plugins.
"
" Plug options:
"
"| Option | Description |
"| ----------------------- | ------------------------------------------------ |
"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use |
"| `rtp` | Subdirectory that contains Vim plugin |
"| `dir` | Custom directory for the plugin |
"| `as` | Use different name for the plugin |
"| `do` | Post-update hook (string or funcref) |
"| `on` | On-demand loading: Commands or `<Plug>`-mappings |
"| `for` | On-demand loading: File types |
"| `frozen` | Do not update unless explicitly specified |
"
" More information: https://github.com/junegunn/vim-plug
2015-01-28 10:05:47 +00:00
"
"
2015-04-04 11:32:24 +00:00
" Copyright (c) 2015 Junegunn Choi
2015-01-28 10:05:47 +00:00
"
" 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
let s:cpo_save = &cpo
set cpo&vim
2015-04-04 11:32:24 +00:00
let s:plug_src = 'https://github.com/junegunn/vim-plug.git'
2015-01-28 10:05:47 +00:00
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')
2015-04-18 00:59:39 +00:00
let s:nvim = has('nvim') && exists('*jobwait') && !s:is_win
2015-01-28 10:05:47 +00:00
let s:me = resolve(expand('<sfile>:p'))
let s:base_spec = { 'branch': 'master', 'frozen': 0 }
let s:TYPE = {
\ 'string': type(''),
\ 'list': type([]),
\ 'dict': type({}),
\ 'funcref': type(function('call'))
\ }
let s:loaded = get(s:, 'loaded', {})
let s:triggers = get(s:, 'triggers', {})
function! plug#begin(...)
if a:0 > 0
let s:plug_home_org = a:1
let home = s:path(fnamemodify(expand(a:1), ':p'))
elseif exists('g:plug_home')
let home = s:path(g:plug_home)
elseif !empty(&rtp)
let home = s:path(split(&rtp, ',')[0]) . '/plugged'
else
return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
endif
let g:plug_home = home
let g:plugs = {}
let g:plugs_order = []
let s:triggers = {}
call s:define_commands()
return 1
endfunction
function! s:define_commands()
command! -nargs=+ -bar Plug call s:Plug(<args>)
2015-01-28 10:05:47 +00:00
if !executable('git')
return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
2015-01-28 10:05:47 +00:00
endif
command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>])
command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>])
command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0)
2015-02-15 02:19:01 +00:00
command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif
2015-01-28 10:05:47 +00:00
command! -nargs=0 -bar PlugStatus call s:status()
command! -nargs=0 -bar PlugDiff call s:diff()
command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>)
2015-01-28 10:05:47 +00:00
endfunction
function! s:to_a(v)
return type(a:v) == s:TYPE.list ? a:v : [a:v]
endfunction
function! s:to_s(v)
return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
endfunction
2016-03-03 20:38:39 +00:00
function! s:glob(from, pattern)
return s:lines(globpath(a:from, a:pattern))
endfunction
2015-01-28 10:05:47 +00:00
function! s:source(from, ...)
2016-03-03 20:38:39 +00:00
let found = 0
2015-01-28 10:05:47 +00:00
for pattern in a:000
2016-03-03 20:38:39 +00:00
for vim in s:glob(a:from, pattern)
2015-02-15 02:19:01 +00:00
execute 'source' s:esc(vim)
2016-03-03 20:38:39 +00:00
let found = 1
2015-01-28 10:05:47 +00:00
endfor
endfor
2016-03-03 20:38:39 +00:00
return found
2015-01-28 10:05:47 +00:00
endfunction
function! s:assoc(dict, key, val)
let a:dict[a:key] = add(get(a:dict, a:key, []), a:val)
endfunction
function! s:ask(message)
call inputsave()
echohl WarningMsg
let proceed = input(a:message.' (y/N) ') =~? '^y'
echohl None
call inputrestore()
echo "\r"
return proceed
endfunction
2015-01-28 10:05:47 +00:00
function! plug#end()
if !exists('g:plugs')
return s:err('Call plug#begin() first')
endif
if exists('#PlugLOD')
augroup PlugLOD
autocmd!
augroup END
augroup! PlugLOD
endif
let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
filetype off
for name in g:plugs_order
let plug = g:plugs[name]
if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(plug, 'for')
let s:loaded[name] = 1
continue
endif
if has_key(plug, 'on')
let s:triggers[name] = { 'map': [], 'cmd': [] }
for cmd in s:to_a(plug.on)
2015-04-18 00:59:39 +00:00
if cmd =~? '^<Plug>.\+'
2015-01-28 10:05:47 +00:00
if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
call s:assoc(lod.map, cmd, name)
endif
call add(s:triggers[name].map, cmd)
2015-11-19 08:58:34 +00:00
elseif cmd =~# '^[A-Z]'
2015-01-28 10:05:47 +00:00
if exists(':'.cmd) != 2
call s:assoc(lod.cmd, cmd, name)
endif
call add(s:triggers[name].cmd, cmd)
2015-11-19 08:58:34 +00:00
else
call s:err('Invalid `on` option: '.cmd.
\ '. Should start with an uppercase letter or `<Plug>`.')
2015-01-28 10:05:47 +00:00
endif
endfor
endif
if has_key(plug, 'for')
let types = s:to_a(plug.for)
if !empty(types)
call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
endif
for type in types
call s:assoc(lod.ft, type, name)
endfor
endif
endfor
for [cmd, names] in items(lod.cmd)
execute printf(
\ 'command! -nargs=* -range -bang %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
\ cmd, string(cmd), string(names))
endfor
for [map, names] in items(lod.map)
for [mode, map_prefix, key_prefix] in
\ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
execute printf(
\ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, "%s")<CR>',
\ mode, map, map_prefix, string(map), string(names), key_prefix)
endfor
endfor
for [ft, names] in items(lod.ft)
augroup PlugLOD
execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
\ ft, string(ft), string(names))
augroup END
endfor
call s:reorg_rtp()
filetype plugin indent on
if has('vim_starting')
if has('syntax') && !exists('g:syntax_on')
syntax enable
end
2015-01-28 10:05:47 +00:00
else
call s:reload()
endif
endfunction
function! s:loaded_names()
return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)')
endfunction
function! s:reload()
for name in s:loaded_names()
call s:source(s:rtp(g:plugs[name]), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
endfor
endfunction
function! s:trim(str)
return substitute(a:str, '[\/]\+$', '', '')
endfunction
2015-02-26 02:48:04 +00:00
function! s:version_requirement(val, min)
for idx in range(0, len(a:min) - 1)
let v = get(a:val, idx, 0)
if v < a:min[idx] | return 0
elseif v > a:min[idx] | return 1
2015-02-15 02:19:01 +00:00
endif
endfor
return 1
endfunction
2015-02-26 02:48:04 +00:00
function! s:git_version_requirement(...)
let s:git_version = get(s:, 'git_version',
\ map(split(split(s:system('git --version'))[-1], '\.'), 'str2nr(v:val)'))
return s:version_requirement(s:git_version, a:000)
endfunction
2015-02-15 02:19:01 +00:00
function! s:progress_opt(base)
return a:base && !s:is_win &&
\ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
endfunction
2015-01-28 10:05:47 +00:00
if s:is_win
function! s:rtp(spec)
return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
endfunction
function! s:path(path)
return s:trim(substitute(a:path, '/', '\', 'g'))
endfunction
function! s:dirpath(path)
return s:path(a:path) . '\'
endfunction
function! s:is_local_plug(repo)
2015-04-18 00:59:39 +00:00
return a:repo =~? '^[a-z]:\|^[%~]'
2015-01-28 10:05:47 +00:00
endfunction
else
function! s:rtp(spec)
return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
endfunction
function! s:path(path)
return s:trim(a:path)
endfunction
function! s:dirpath(path)
return substitute(a:path, '[/\\]*$', '/', '')
endfunction
function! s:is_local_plug(repo)
return a:repo[0] =~ '[/$~]'
endfunction
endif
function! s:err(msg)
echohl ErrorMsg
2015-11-19 08:58:34 +00:00
echom '[vim-plug] '.a:msg
2015-01-28 10:05:47 +00:00
echohl None
return 0
endfunction
function! s:esc(path)
return escape(a:path, ' ')
endfunction
function! s:escrtp(path)
return escape(a:path, ' ,')
endfunction
function! s:remove_rtp()
for name in s:loaded_names()
let rtp = s:rtp(g:plugs[name])
execute 'set rtp-='.s:escrtp(rtp)
let after = globpath(rtp, 'after')
if isdirectory(after)
execute 'set rtp-='.s:escrtp(after)
endif
endfor
endfunction
function! s:reorg_rtp()
if !empty(s:first_rtp)
execute 'set rtp-='.s:first_rtp
execute 'set rtp-='.s:last_rtp
endif
" &rtp is modified from outside
if exists('s:prtp') && s:prtp !=# &rtp
call s:remove_rtp()
unlet! s:middle
endif
let s:middle = get(s:, 'middle', &rtp)
let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])')
let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), 'isdirectory(v:val)')
2015-02-15 02:19:01 +00:00
let rtp = join(map(rtps, 'escape(v:val, ",")'), ',')
2015-01-28 10:05:47 +00:00
\ . ','.s:middle.','
2015-02-15 02:19:01 +00:00
\ . join(map(afters, 'escape(v:val, ",")'), ',')
2015-01-28 10:05:47 +00:00
let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
let s:prtp = &rtp
if !empty(s:first_rtp)
execute 'set rtp^='.s:first_rtp
execute 'set rtp+='.s:last_rtp
endif
endfunction
function! plug#load(...)
if a:0 == 0
return s:err('Argument missing: plugin name(s) required')
endif
if !exists('g:plugs')
return s:err('plug#begin was not called')
endif
let unknowns = filter(copy(a:000), '!has_key(g:plugs, v:val)')
if !empty(unknowns)
let s = len(unknowns) > 1 ? 's' : ''
return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
end
for name in a:000
call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
endfor
if exists('#BufRead')
doautocmd BufRead
endif
2015-01-28 10:05:47 +00:00
return 1
endfunction
function! s:remove_triggers(name)
if !has_key(s:triggers, a:name)
return
endif
for cmd in s:triggers[a:name].cmd
execute 'silent! delc' cmd
endfor
for map in s:triggers[a:name].map
execute 'silent! unmap' map
execute 'silent! iunmap' map
endfor
call remove(s:triggers, a:name)
endfunction
function! s:lod(names, types, ...)
2015-01-28 10:05:47 +00:00
for name in a:names
call s:remove_triggers(name)
let s:loaded[name] = 1
endfor
call s:reorg_rtp()
for name in a:names
let rtp = s:rtp(g:plugs[name])
for dir in a:types
call s:source(rtp, dir.'/**/*.vim')
endfor
2016-03-03 20:38:39 +00:00
if a:0
if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
execute 'runtime' a:1
endif
call s:source(rtp, a:2)
endif
if exists('#User#'.name)
execute 'doautocmd User' name
endif
2015-01-28 10:05:47 +00:00
endfor
endfunction
function! s:lod_ft(pat, names)
let syn = 'syntax/'.a:pat.'.vim'
call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn)
2015-01-28 10:05:47 +00:00
execute 'autocmd! PlugLOD FileType' a:pat
if exists('#filetypeplugin#FileType')
doautocmd filetypeplugin FileType
endif
if exists('#filetypeindent#FileType')
doautocmd filetypeindent FileType
endif
2015-01-28 10:05:47 +00:00
endfunction
function! s:lod_cmd(cmd, bang, l1, l2, args, names)
call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
endfunction
function! s:lod_map(map, names, prefix)
call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
let extra = ''
while 1
let c = getchar(0)
if c == 0
break
endif
let extra .= nr2char(c)
endwhile
call feedkeys(a:prefix . substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
endfunction
function! s:Plug(repo, ...)
2015-01-28 10:05:47 +00:00
if a:0 > 1
return s:err('Invalid number of arguments (1..2)')
endif
try
let repo = s:trim(a:repo)
let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??'))
let spec = extend(s:infer_properties(name, repo), opts)
2015-02-26 02:48:04 +00:00
if !has_key(g:plugs, name)
call add(g:plugs_order, name)
endif
2015-01-28 10:05:47 +00:00
let g:plugs[name] = spec
let s:loaded[name] = get(s:loaded, name, 0)
2015-01-28 10:05:47 +00:00
catch
return s:err(v:exception)
endtry
endfunction
function! s:parse_options(arg)
let opts = copy(s:base_spec)
let type = type(a:arg)
if type == s:TYPE.string
2015-02-26 02:48:04 +00:00
let opts.tag = a:arg
2015-01-28 10:05:47 +00:00
elseif type == s:TYPE.dict
call extend(opts, a:arg)
if has_key(opts, 'dir')
let opts.dir = s:dirpath(expand(opts.dir))
endif
else
throw 'Invalid argument type (expected: string or dictionary)'
endif
return opts
endfunction
function! s:infer_properties(name, repo)
let repo = a:repo
if s:is_local_plug(repo)
return { 'dir': s:dirpath(expand(repo)) }
else
if repo =~ ':'
let uri = repo
else
if repo !~ '/'
let repo = 'vim-scripts/'. repo
endif
let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git')
let uri = printf(fmt, repo)
endif
let dir = s:dirpath( fnamemodify(join([g:plug_home, a:name], '/'), ':p') )
return { 'dir': dir, 'uri': uri }
endif
endfunction
function! s:install(force, names)
call s:update_impl(0, a:force, a:names)
endfunction
function! s:update(force, names)
call s:update_impl(1, a:force, a:names)
endfunction
function! plug#helptags()
if !exists('g:plugs')
return s:err('plug#begin was not called')
endif
for spec in values(g:plugs)
let docd = join([spec.dir, 'doc'], '/')
if isdirectory(docd)
silent! execute 'helptags' s:esc(docd)
endif
endfor
return 1
endfunction
function! s:syntax()
syntax clear
syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
syn match plugNumber /[0-9]\+[0-9.]*/ contained
syn match plugBracket /[[\]]/ contained
syn match plugX /x/ contained
syn match plugDash /^-/
syn match plugPlus /^+/
syn match plugStar /^*/
syn match plugMessage /\(^- \)\@<=.*/
syn match plugName /\(^- \)\@<=[^ ]*:/
syn match plugSha /\%(: \)\@<=[0-9a-z]\{4,}$/
syn match plugTag /(tag: [^)]\+)/
2015-01-28 10:05:47 +00:00
syn match plugInstall /\(^+ \)\@<=[^:]*/
syn match plugUpdate /\(^* \)\@<=[^:]*/
2016-03-03 20:38:39 +00:00
syn match plugCommit /^ \X*[0-9a-z]\{7} .*/ contains=plugRelDate,plugEdge,plugTag
syn match plugEdge /^ \X\+$/
syn match plugEdge /^ \X*/ contained nextgroup=plugSha
syn match plugSha /[0-9a-z]\{7}/ contained
2015-01-28 10:05:47 +00:00
syn match plugRelDate /([^)]*)$/ contained
syn match plugNotLoaded /(not loaded)$/
syn match plugError /^x.*/
syn match plugH2 /^.*:\n-\+$/
2015-01-28 10:05:47 +00:00
syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
hi def link plug1 Title
hi def link plug2 Repeat
hi def link plugH2 Type
2015-01-28 10:05:47 +00:00
hi def link plugX Exception
hi def link plugBracket Structure
hi def link plugNumber Number
hi def link plugDash Special
hi def link plugPlus Constant
hi def link plugStar Boolean
hi def link plugMessage Function
hi def link plugName Label
hi def link plugInstall Function
hi def link plugUpdate Type
hi def link plugError Error
hi def link plugRelDate Comment
2016-03-03 20:38:39 +00:00
hi def link plugEdge PreProc
2015-01-28 10:05:47 +00:00
hi def link plugSha Identifier
hi def link plugTag Constant
2015-01-28 10:05:47 +00:00
hi def link plugNotLoaded Comment
endfunction
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)
return get(s:lines(a:msg), -1, '')
endfunction
function! s:new_window()
execute get(g:, 'plug_window', 'vertical topleft new')
endfunction
function! s:plug_window_exists()
let buflist = tabpagebuflist(s:plug_tab)
return !empty(buflist) && index(buflist, s:plug_buf) >= 0
endfunction
function! s:switch_in()
if !s:plug_window_exists()
return 0
endif
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
setlocal modifiable
return 1
endfunction
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'
execute s:pos[1] 'wincmd w'
call winrestview(s:pos[2])
endif
endfunction
function! s:finish_bindings()
nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr>
nnoremap <silent> <buffer> D :PlugDiff<cr>
nnoremap <silent> <buffer> S :PlugStatus<cr>
nnoremap <silent> <buffer> U :call <SID>status_update()<cr>
xnoremap <silent> <buffer> U :call <SID>status_update()<cr>
nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
endfunction
function! s:prepare(...)
2015-01-28 10:05:47 +00:00
call s:job_abort()
if s:switch_in()
normal q
2015-01-28 10:05:47 +00:00
endif
call s:new_window()
nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
if a:0 == 0
call s:finish_bindings()
endif
let b:plug_preview = -1
let s:plug_tab = tabpagenr()
let s:plug_buf = winbufnr(0)
call s:assign_name()
2015-01-28 10:05:47 +00:00
silent! unmap <buffer> <cr>
silent! unmap <buffer> L
2015-02-26 02:48:04 +00:00
silent! unmap <buffer> o
2015-01-28 10:05:47 +00:00
silent! unmap <buffer> X
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable
setf vim-plug
if exists('g:syntax_on')
call s:syntax()
endif
2015-01-28 10:05:47 +00:00
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:do(pull, force, todo)
for [name, spec] in items(a:todo)
if !isdirectory(spec.dir)
continue
endif
let installed = has_key(s:update.new, name)
let updated = installed ? 0 :
2015-11-19 08:58:34 +00:00
\ (a:pull && index(s:update.errors, name) < 0 && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir)))
2015-01-28 10:05:47 +00:00
if a:force || installed || updated
execute 'cd' s:esc(spec.dir)
call append(3, '- Post-update hook for '. name .' ... ')
2015-11-19 08:58:34 +00:00
let error = ''
2015-01-28 10:05:47 +00:00
let type = type(spec.do)
if type == s:TYPE.string
try
" FIXME: Escaping is incomplete. We could use shellescape with eval,
" but it won't work on Windows.
let g:_plug_do = '!'.escape(spec.do, '#!%')
execute "normal! :execute g:_plug_do\<cr>\<cr>"
finally
2015-11-19 08:58:34 +00:00
if v:shell_error
let error = 'Exit status: ' . v:shell_error
endif
2015-01-28 10:05:47 +00:00
unlet g:_plug_do
endtry
elseif type == s:TYPE.funcref
try
let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
call spec.do({ 'name': name, 'status': status, 'force': a:force })
catch
2015-11-19 08:58:34 +00:00
let error = v:exception
2015-01-28 10:05:47 +00:00
endtry
else
2015-11-19 08:58:34 +00:00
let error = 'Invalid hook type'
2015-01-28 10:05:47 +00:00
endif
2015-11-19 08:58:34 +00:00
call setline(4, empty(error) ? (getline(4) . 'OK')
\ : ('x' . getline(4)[1:] . error))
2015-01-28 10:05:47 +00:00
cd -
endif
endfor
endfunction
2015-12-12 12:40:42 +00:00
function! s:hash_match(a, b)
return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
endfunction
function! s:checkout(plugs)
for [name, spec] in items(a:plugs)
let sha = spec.commit
call append(3, '- Checking out '.sha[:6].' of '.name.' ... ')
redraw
let error = []
let output = s:lines(s:system('git rev-parse HEAD', spec.dir))
if v:shell_error
let error = output
elseif !s:hash_match(sha, output[0])
let output = s:lines(s:system(
\ 'git fetch --depth 999999 && git checkout '.sha, spec.dir))
if v:shell_error
let error = output
endif
endif
if empty(error)
call setline(4, getline(4) . 'OK')
else
call setline(4, 'x'.getline(4)[1:] . 'Error')
for line in reverse(error)
call append(4, ' '.line)
endfor
endif
endfor
endfunction
2015-01-28 10:05:47 +00:00
function! s:finish(pull)
let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
if new_frozen
let s = new_frozen > 1 ? 's' : ''
call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s))
endif
call append(3, '- Finishing ... ')
redraw
call plug#helptags()
call plug#end()
call setline(4, getline(4) . 'Done!')
redraw
let msgs = []
if !empty(s:update.errors)
call add(msgs, "Press 'R' to retry.")
endif
if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
\ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0"))
call add(msgs, "Press 'D' to see the updated changes.")
endif
echo join(msgs, ' ')
call s:finish_bindings()
2015-01-28 10:05:47 +00:00
endfunction
function! s:retry()
if empty(s:update.errors)
return
endif
call s:update_impl(s:update.pull, s:update.force,
\ extend(copy(s:update.errors), [s:update.threads]))
endfunction
function! s:is_managed(name)
return has_key(g:plugs[a:name], 'uri')
endfunction
function! s:names(...)
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
let args = copy(a:args)
let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
2015-12-12 12:40:42 +00:00
\ remove(args, -1) : get(g:, 'plug_threads', 16)
2015-01-28 10:05:47 +00:00
let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
\ filter(managed, 'index(args, v:key) >= 0')
if empty(todo)
echohl WarningMsg
echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
echohl None
return
endif
2015-02-26 02:48:04 +00:00
if !s:is_win && s:git_version_requirement(2, 3)
let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : ''
let $GIT_TERMINAL_PROMPT = 0
for plug in values(todo)
let plug.uri = substitute(plug.uri,
\ '^https://git::@github\.com', 'https://github.com', '')
endfor
endif
2015-01-28 10:05:47 +00:00
if !isdirectory(g:plug_home)
try
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))
endtry
endif
2015-04-18 00:59:39 +00:00
if has('nvim') && !exists('*jobwait') && threads > 1
echohl WarningMsg
echomsg 'vim-plug: update Neovim for parallel installer'
echohl None
endif
2015-12-12 12:40:42 +00:00
let python = (has('python') || has('python3')) && (!s:nvim || has('vim_starting'))
let ruby = has('ruby') && !s:nvim && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running'))
2015-01-28 10:05:47 +00:00
let s:update = {
\ 'start': reltime(),
\ 'all': todo,
\ 'todo': copy(todo),
\ 'errors': [],
\ 'pull': a:pull,
\ 'force': a:force,
\ 'new': {},
\ 'threads': (python || ruby || s:nvim) ? min([len(todo), threads]) : 1,
2015-01-28 10:05:47 +00:00
\ 'bar': '',
\ 'fin': 0
\ }
call s:prepare(1)
2015-01-28 10:05:47 +00:00
call append(0, ['', ''])
normal! 2G
silent! redraw
let s:clone_opt = get(g:, 'plug_shallow', 1) ?
\ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : ''
2015-01-28 10:05:47 +00:00
2015-02-26 02:48:04 +00:00
" Python version requirement (>= 2.7)
if python && !has('python3') && !ruby && !s:nvim && s:update.threads > 1
2015-02-26 02:48:04 +00:00
redir => pyv
silent python import platform; print platform.python_version()
2015-02-26 02:48:04 +00:00
redir END
let python = s:version_requirement(
2015-03-17 11:17:20 +00:00
\ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
2015-02-26 02:48:04 +00:00
endif
if (python || ruby) && s:update.threads > 1
2015-01-28 10:05:47 +00:00
try
let imd = &imd
if s:mac_gui
set noimd
endif
if ruby
2015-02-26 02:48:04 +00:00
call s:update_ruby()
else
call s:update_python()
endif
2015-01-28 10:05:47 +00:00
catch
let lines = getline(4, '$')
let printed = {}
2015-02-15 02:19:01 +00:00
silent! 4,$d _
2015-01-28 10:05:47 +00:00
for line in lines
let name = s:extract_name(line, '.', '')
if empty(name) || !has_key(printed, name)
call append('$', line)
if !empty(name)
let printed[name] = 1
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_vim()
endif
endfunction
function! s:update_finish()
2015-02-26 02:48:04 +00:00
if exists('s:git_terminal_prompt')
let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
endif
2015-01-28 10:05:47 +00:00
if s:switch_in()
2015-12-12 12:40:42 +00:00
call s:checkout(filter(copy(s:update.all), 'has_key(v:val, "commit")'))
2015-01-28 10:05:47 +00:00
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('normal! gg')
endif
endfunction
function! s:job_abort()
if !s:nvim || !exists('s:jobs')
return
endif
2015-04-04 11:32:24 +00:00
2015-01-28 10:05:47 +00:00
for [name, j] in items(s:jobs)
silent! call jobstop(j.jobid)
if j.new
2015-02-15 02:19:01 +00:00
call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir))
2015-01-28 10:05:47 +00:00
endif
endfor
let s:jobs = {}
endfunction
2015-04-04 11:32:24 +00:00
" When a:event == 'stdout', data = list of strings
" When a:event == 'exit', data = returncode
function! s:job_handler(job_id, data, event) abort
2015-01-28 10:05:47 +00:00
if !s:plug_window_exists() " plug window closed
return s:job_abort()
endif
2015-04-04 11:32:24 +00:00
if a:event == 'stdout'
2015-12-12 12:40:42 +00:00
let complete = empty(a:data[-1])
let lines = map(filter(a:data, 'len(v:val) > 0'), 'split(v:val, "[\r\n]")[-1]')
call extend(self.lines, lines)
let self.result = join(self.lines, "\n")
if !complete
call remove(self.lines, -1)
endif
2015-01-28 10:05:47 +00:00
" To reduce the number of buffer updates
2015-04-04 11:32:24 +00:00
let self.tick = get(self, 'tick', -1) + 1
if self.tick % len(s:jobs) == 0
call s:log(self.new ? '+' : '*', self.name, self.result)
endif
elseif a:event == 'exit'
let self.running = 0
if a:data != 0
let self.error = 1
2015-01-28 10:05:47 +00:00
endif
2015-04-04 11:32:24 +00:00
call s:reap(self.name)
call s:tick()
2015-01-28 10:05:47 +00:00
endif
endfunction
function! s:spawn(name, cmd, opts)
2015-12-12 12:40:42 +00:00
let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [], 'result': '',
2015-04-04 11:32:24 +00:00
\ 'new': get(a:opts, 'new', 0),
\ 'on_stdout': function('s:job_handler'),
\ 'on_exit' : function('s:job_handler'),
\ }
2015-01-28 10:05:47 +00:00
let s:jobs[a:name] = job
if s:nvim
2015-04-04 11:32:24 +00:00
let argv = [ 'sh', '-c',
\ (has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd) ]
let jid = jobstart(argv, job)
if jid > 0
let job.jobid = jid
2015-01-28 10:05:47 +00:00
else
let job.running = 0
let job.error = 1
2015-04-04 11:32:24 +00:00
let job.result = jid < 0 ? 'sh is not executable' :
2015-01-28 10:05:47 +00:00
\ '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.result = call('s:system', params)
let job.error = v:shell_error != 0
let job.running = 0
endif
endfunction
function! s:reap(name)
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' : '='
call s:log(job.error ? 'x' : '-', a:name, job.result)
call s:bar()
call remove(s:jobs, a:name)
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(bullet, name, lines)
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 pos = 4
endif
call append(pos - 1, s:format_message(a:bullet, a:name, a:lines))
call s:switch_out()
endif
endfunction
function! s:update_vim()
let s:jobs = {}
call s:bar()
call s:tick()
endfunction
function! s:tick()
2015-02-15 02:19:01 +00:00
let pull = s:update.pull
let prog = s:progress_opt(s:nvim)
2015-01-28 10:05:47 +00:00
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
call s:update_finish()
endif
return
endif
let name = keys(s:update.todo)[0]
let spec = remove(s:update.todo, name)
let new = !isdirectory(spec.dir)
call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
redraw
let has_tag = has_key(spec, 'tag')
let checkout = s:shellesc(has_tag ? spec.tag : spec.branch)
let merge = s:shellesc(has_tag ? spec.tag : 'origin/'.spec.branch)
2015-02-26 02:48:04 +00:00
2015-01-28 10:05:47 +00:00
if !new
2015-12-12 12:40:42 +00:00
let error = s:git_validate(spec, 0)
if empty(error)
2015-01-28 10:05:47 +00:00
if pull
let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
2015-01-28 10:05:47 +00:00
call s:spawn(name,
\ printf('(git fetch %s %s 2>&1 && git checkout -q %s 2>&1 && git merge --ff-only %s 2>&1 && git submodule update --init --recursive 2>&1)',
\ fetch_opt, prog, checkout, merge), { 'dir': spec.dir })
2015-01-28 10:05:47 +00:00
else
let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 }
endif
else
2015-12-12 12:40:42 +00:00
let s:jobs[name] = { 'running': 0, 'result': error, 'error': 1 }
2015-01-28 10:05:47 +00:00
endif
else
call s:spawn(name,
\ printf('git clone %s %s --recursive %s -b %s %s 2>&1',
\ has_tag ? '' : s:clone_opt,
2015-02-15 02:19:01 +00:00
\ prog,
2015-01-28 10:05:47 +00:00
\ s:shellesc(spec.uri),
2015-02-26 02:48:04 +00:00
\ checkout,
2015-01-28 10:05:47 +00:00
\ s:shellesc(s:trim(spec.dir))), { 'new': 1 })
endif
if !s:jobs[name].running
call s:reap(name)
endif
if len(s:jobs) >= s:update.threads
break
endif
endwhile
endfunction
2015-02-26 02:48:04 +00:00
function! s:update_python()
2015-12-12 12:40:42 +00:00
let py_exe = has('python') ? 'python' : 'python3'
execute py_exe "<< EOF"
2015-02-26 02:48:04 +00:00
import datetime
import functools
import os
try:
import queue
except ImportError:
import Queue as queue
2015-02-26 02:48:04 +00:00
import random
import re
import shutil
import signal
import subprocess
import tempfile
import threading as thr
import time
import traceback
import vim
G_NVIM = vim.eval("has('nvim')") == '1'
2015-02-26 02:48:04 +00:00
G_PULL = vim.eval('s:update.pull') == '1'
G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
G_CLONE_OPT = vim.eval('s:clone_opt')
2015-02-26 02:48:04 +00:00
G_PROGRESS = vim.eval('s:progress_opt(1)')
G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
G_STOP = thr.Event()
2015-12-12 12:40:42 +00:00
G_IS_WIN = vim.eval('s:is_win') == '1'
2015-11-19 08:58:34 +00:00
class PlugError(Exception):
def __init__(self, msg):
2015-12-12 12:40:42 +00:00
self.msg = msg
2015-11-19 08:58:34 +00:00
class CmdTimedOut(PlugError):
2015-02-26 02:48:04 +00:00
pass
2015-11-19 08:58:34 +00:00
class CmdFailed(PlugError):
2015-02-26 02:48:04 +00:00
pass
2015-11-19 08:58:34 +00:00
class InvalidURI(PlugError):
2015-02-26 02:48:04 +00:00
pass
class Action(object):
INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
class Buffer(object):
2015-12-12 12:40:42 +00:00
def __init__(self, lock, num_plugs, is_pull):
2015-02-26 02:48:04 +00:00
self.bar = ''
self.event = 'Updating' if is_pull else 'Installing'
2015-02-26 02:48:04 +00:00
self.lock = lock
self.maxy = int(vim.eval('winheight(".")'))
self.num_plugs = num_plugs
2015-11-19 08:58:34 +00:00
def __where(self, name):
2015-02-26 02:48:04 +00:00
""" Find first line with name in current buffer. Return line num. """
found, lnum = False, 0
2015-03-17 11:17:20 +00:00
matcher = re.compile('^[-+x*] {0}:'.format(name))
2015-02-26 02:48:04 +00:00
for line in vim.current.buffer:
if matcher.search(line) is not None:
found = True
break
lnum += 1
if not found:
lnum = -1
return lnum
def header(self):
curbuf = vim.current.buffer
2015-03-17 11:17:20 +00:00
curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs)
2015-02-26 02:48:04 +00:00
num_spaces = self.num_plugs - len(self.bar)
2015-03-17 11:17:20 +00:00
curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ')
2015-02-26 02:48:04 +00:00
with self.lock:
vim.command('normal! 2G')
2015-12-12 12:40:42 +00:00
vim.command('redraw')
2015-02-26 02:48:04 +00:00
def write(self, action, name, lines):
2015-02-26 02:48:04 +00:00
first, rest = lines[0], lines[1:]
2015-03-17 11:17:20 +00:00
msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)]
2015-11-19 08:58:34 +00:00
msg.extend([' ' + line for line in rest])
2015-02-26 02:48:04 +00:00
try:
if action == Action.ERROR:
self.bar += 'x'
2015-03-17 11:17:20 +00:00
vim.command("call add(s:update.errors, '{0}')".format(name))
2015-02-26 02:48:04 +00:00
elif action == Action.DONE:
self.bar += '='
curbuf = vim.current.buffer
2015-11-19 08:58:34 +00:00
lnum = self.__where(name)
2015-02-26 02:48:04 +00:00
if lnum != -1: # Found matching line num
del curbuf[lnum]
2015-03-17 11:17:20 +00:00
if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]):
2015-02-26 02:48:04 +00:00
lnum = 3
else:
lnum = 3
curbuf.append(msg, lnum)
self.header()
except vim.error:
pass
2015-02-26 02:48:04 +00:00
class Command(object):
CD = 'cd /d' if G_IS_WIN else 'cd'
2015-11-19 08:58:34 +00:00
def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
2015-02-26 02:48:04 +00:00
self.cmd = cmd
2015-12-12 12:40:42 +00:00
if cmd_dir:
self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd)
2015-02-26 02:48:04 +00:00
self.timeout = timeout
self.callback = cb if cb else (lambda msg: None)
2015-11-19 08:58:34 +00:00
self.clean = clean if clean else (lambda: None)
self.proc = None
2015-02-26 02:48:04 +00:00
2015-11-19 08:58:34 +00:00
@property
def alive(self):
""" Returns true only if command still running. """
return self.proc and self.proc.poll() is None
def execute(self, ntries=3):
""" Execute the command with ntries if CmdTimedOut.
Returns the output of the command if no Exception.
"""
attempt, finished, limit = 0, False, self.timeout
2015-02-26 02:48:04 +00:00
while not finished:
try:
attempt += 1
2015-11-19 08:58:34 +00:00
result = self.try_command()
2015-02-26 02:48:04 +00:00
finished = True
2015-11-19 08:58:34 +00:00
return result
2015-02-26 02:48:04 +00:00
except CmdTimedOut:
2015-11-19 08:58:34 +00:00
if attempt != ntries:
self.notify_retry()
2015-02-26 02:48:04 +00:00
self.timeout += limit
else:
raise
2015-11-19 08:58:34 +00:00
def notify_retry(self):
""" Retry required for command, notify user. """
for count in range(3, 0, -1):
if G_STOP.is_set():
raise KeyboardInterrupt
msg = 'Timeout. Will retry in {0} second{1} ...'.format(
count, 's' if count != 1 else '')
self.callback([msg])
time.sleep(1)
self.callback(['Retrying ...'])
def try_command(self):
2015-02-26 02:48:04 +00:00
""" Execute a cmd & poll for callback. Returns list of output.
2015-11-19 08:58:34 +00:00
Raises CmdFailed -> return code for Popen isn't 0
Raises CmdTimedOut -> command exceeded timeout without new output
2015-02-26 02:48:04 +00:00
"""
first_line = True
2015-11-19 08:58:34 +00:00
2015-02-26 02:48:04 +00:00
try:
2015-11-19 08:58:34 +00:00
tfile = tempfile.NamedTemporaryFile(mode='w+b')
2015-12-12 12:40:42 +00:00
preexec_fn = not G_IS_WIN and os.setsid or None
self.proc = subprocess.Popen(self.cmd, stdout=tfile,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE, shell=True,
preexec_fn=preexec_fn)
2015-11-19 08:58:34 +00:00
thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
thrd.start()
thread_not_started = True
while thread_not_started:
try:
thrd.join(0.1)
thread_not_started = False
except RuntimeError:
pass
2015-02-26 02:48:04 +00:00
2015-11-19 08:58:34 +00:00
while self.alive:
2015-02-26 02:48:04 +00:00
if G_STOP.is_set():
raise KeyboardInterrupt
if first_line or random.random() < G_LOG_PROB:
first_line = False
2015-12-12 12:40:42 +00:00
line = '' if G_IS_WIN else nonblock_read(tfile.name)
2015-02-26 02:48:04 +00:00
if line:
self.callback([line])
time_diff = time.time() - os.path.getmtime(tfile.name)
if time_diff > self.timeout:
raise CmdTimedOut(['Timeout!'])
2015-11-19 08:58:34 +00:00
thrd.join(0.5)
2015-02-26 02:48:04 +00:00
tfile.seek(0)
2015-07-31 08:42:27 +00:00
result = [line.decode('utf-8', 'replace').rstrip() for line in tfile]
2015-02-26 02:48:04 +00:00
2015-11-19 08:58:34 +00:00
if self.proc.returncode != 0:
raise CmdFailed([''] + result)
return result
2015-02-26 02:48:04 +00:00
except:
2015-11-19 08:58:34 +00:00
self.terminate()
2015-02-26 02:48:04 +00:00
raise
2015-11-19 08:58:34 +00:00
def terminate(self):
""" Terminate process and cleanup. """
if self.alive:
2015-12-12 12:40:42 +00:00
if G_IS_WIN:
os.kill(self.proc.pid, signal.SIGINT)
else:
os.killpg(self.proc.pid, signal.SIGTERM)
2015-11-19 08:58:34 +00:00
self.clean()
2015-02-26 02:48:04 +00:00
class Plugin(object):
def __init__(self, name, args, buf_q, lock):
2015-02-26 02:48:04 +00:00
self.name = name
self.args = args
self.buf_q = buf_q
2015-02-26 02:48:04 +00:00
self.lock = lock
tag = args.get('tag', 0)
self.checkout = esc(tag if tag else args['branch'])
self.merge = esc(tag if tag else 'origin/' + args['branch'])
self.tag = tag
2015-02-26 02:48:04 +00:00
def manage(self):
try:
if os.path.exists(self.args['dir']):
self.update()
else:
self.install()
with self.lock:
thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
2015-11-19 08:58:34 +00:00
except PlugError as exc:
self.write(Action.ERROR, self.name, exc.msg)
2015-02-26 02:48:04 +00:00
except KeyboardInterrupt:
G_STOP.set()
self.write(Action.ERROR, self.name, ['Interrupted!'])
except:
# Any exception except those above print stack trace
2015-03-17 11:17:20 +00:00
msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip())
2015-02-26 02:48:04 +00:00
self.write(Action.ERROR, self.name, msg.split('\n'))
raise
def install(self):
target = self.args['dir']
if target[-1] == '\\':
target = target[0:-1]
2015-02-26 02:48:04 +00:00
def clean(target):
def _clean():
try:
shutil.rmtree(target)
except OSError:
pass
return _clean
self.write(Action.INSTALL, self.name, ['Installing ...'])
callback = functools.partial(self.write, Action.INSTALL, self.name)
cmd = 'git clone {0} {1} --recursive {2} -b {3} {4} 2>&1'.format(
2015-11-19 08:58:34 +00:00
'' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'],
self.checkout, esc(target))
com = Command(cmd, None, G_TIMEOUT, callback, clean(target))
result = com.execute(G_RETRIES)
2015-02-26 02:48:04 +00:00
self.write(Action.DONE, self.name, result[-1:])
2015-11-19 08:58:34 +00:00
def repo_uri(self):
cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url'
command = Command(cmd, self.args['dir'], G_TIMEOUT,)
result = command.execute(G_RETRIES)
return result[-1]
2015-02-26 02:48:04 +00:00
def update(self):
match = re.compile(r'git::?@')
actual_uri = re.sub(match, '', self.repo_uri())
expect_uri = re.sub(match, '', self.args['uri'])
if actual_uri != expect_uri:
msg = ['',
2015-03-17 11:17:20 +00:00
'Invalid URI: {0}'.format(actual_uri),
'Expected {0}'.format(expect_uri),
2015-02-26 02:48:04 +00:00
'PlugClean required.']
raise InvalidURI(msg)
if G_PULL:
self.write(Action.UPDATE, self.name, ['Updating ...'])
callback = functools.partial(self.write, Action.UPDATE, self.name)
fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else ''
cmds = ['git fetch {0} {1}'.format(fetch_opt, G_PROGRESS),
2015-03-17 11:17:20 +00:00
'git checkout -q {0}'.format(self.checkout),
'git merge --ff-only {0}'.format(self.merge),
2015-02-26 02:48:04 +00:00
'git submodule update --init --recursive']
cmd = ' 2>&1 && '.join(cmds)
2015-11-19 08:58:34 +00:00
com = Command(cmd, self.args['dir'], G_TIMEOUT, callback)
result = com.execute(G_RETRIES)
2015-02-26 02:48:04 +00:00
self.write(Action.DONE, self.name, result[-1:])
else:
self.write(Action.DONE, self.name, ['Already installed'])
def write(self, action, name, msg):
self.buf_q.put((action, name, msg))
2015-02-26 02:48:04 +00:00
class PlugThread(thr.Thread):
def __init__(self, tname, args):
super(PlugThread, self).__init__()
self.tname = tname
self.args = args
def run(self):
thr.current_thread().name = self.tname
buf_q, work_q, lock = self.args
2015-02-26 02:48:04 +00:00
try:
while not G_STOP.is_set():
name, args = work_q.get_nowait()
plug = Plugin(name, args, buf_q, lock)
2015-02-26 02:48:04 +00:00
plug.manage()
work_q.task_done()
except queue.Empty:
pass
2015-02-26 02:48:04 +00:00
class RefreshThread(thr.Thread):
def __init__(self, lock):
super(RefreshThread, self).__init__()
self.lock = lock
self.running = True
def run(self):
while self.running:
with self.lock:
thread_vim_command('noautocmd normal! a')
2015-11-19 08:58:34 +00:00
time.sleep(0.33)
2015-02-26 02:48:04 +00:00
def stop(self):
self.running = False
if G_NVIM:
def thread_vim_command(cmd):
vim.session.threadsafe_call(lambda: vim.command(cmd))
else:
def thread_vim_command(cmd):
vim.command(cmd)
2015-02-26 02:48:04 +00:00
def esc(name):
return '"' + name.replace('"', '\"') + '"'
def nonblock_read(fname):
""" Read a file with nonblock flag. Return the last line. """
fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
2015-07-31 08:42:27 +00:00
buf = os.read(fread, 100000).decode('utf-8', 'replace')
2015-02-26 02:48:04 +00:00
os.close(fread)
line = buf.rstrip('\r\n')
left = max(line.rfind('\r'), line.rfind('\n'))
if left != -1:
left += 1
line = line[left:]
return line
def main():
thr.current_thread().name = 'main'
nthreads = int(vim.eval('s:update.threads'))
plugs = vim.eval('s:update.todo')
mac_gui = vim.eval('s:mac_gui') == '1'
lock = thr.Lock()
2015-12-12 12:40:42 +00:00
buf = Buffer(lock, len(plugs), G_PULL)
buf_q, work_q = queue.Queue(), queue.Queue()
2015-02-26 02:48:04 +00:00
for work in plugs.items():
work_q.put(work)
2015-12-12 12:40:42 +00:00
start_cnt = thr.active_count()
2015-02-26 02:48:04 +00:00
for num in range(nthreads):
tname = 'PlugT-{0:02}'.format(num)
thread = PlugThread(tname, (buf_q, work_q, lock))
2015-02-26 02:48:04 +00:00
thread.start()
if mac_gui:
rthread = RefreshThread(lock)
rthread.start()
2015-12-12 12:40:42 +00:00
while not buf_q.empty() or thr.active_count() != start_cnt:
try:
action, name, msg = buf_q.get(True, 0.25)
buf.write(action, name, msg)
buf_q.task_done()
except queue.Empty:
pass
except KeyboardInterrupt:
G_STOP.set()
2015-02-26 02:48:04 +00:00
if mac_gui:
rthread.stop()
rthread.join()
main()
EOF
endfunction
2015-01-28 10:05:47 +00:00
function! s:update_ruby()
ruby << EOF
module PlugStream
SEP = ["\r", "\n", nil]
def get_line
buffer = ''
loop do
char = readchar rescue return
if SEP.include? char.chr
buffer << $/
break
else
buffer << char
end
end
buffer
end
end unless defined?(PlugStream)
def esc arg
%["#{arg.gsub('"', '\"')}"]
end
def killall pid
pids = [pid]
2015-12-12 12:40:42 +00:00
if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
else
unless `which pgrep 2> /dev/null`.empty?
children = pids
until children.empty?
children = children.map { |pid|
`pgrep -P #{pid}`.lines.map { |l| l.chomp }
}.flatten
pids += children
end
2015-01-28 10:05:47 +00:00
end
2015-12-12 12:40:42 +00:00
pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
2015-01-28 10:05:47 +00:00
end
end
require 'thread'
require 'fileutils'
require 'timeout'
running = true
iswin = VIM::evaluate('s:is_win').to_i == 1
pull = VIM::evaluate('s:update.pull').to_i == 1
base = VIM::evaluate('g:plug_home')
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('s:update.threads').to_i
maxy = VIM::evaluate('winheight(".")').to_i
cd = iswin ? 'cd /d' : 'cd'
tot = VIM::evaluate('len(s:update.todo)') || 0
bar = ''
skip = 'Already installed'
mtx = Mutex.new
take1 = proc { mtx.synchronize { running && all.shift } }
logh = proc {
cnt = bar.length
$curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
$curbuf[2] = '[' + bar.ljust(tot) + ']'
VIM::command('normal! 2G')
2015-12-12 12:40:42 +00:00
VIM::command('redraw')
2015-01-28 10:05:47 +00:00
}
where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
log = proc { |name, result, type|
mtx.synchronize do
ing = ![true, false].include?(type)
bar += type ? '=' : 'x' unless ing
b = case type
when :install then '+' when :update then '*'
when true, nil then '-' else
VIM::command("call add(s:update.errors, '#{name}')")
'x'
end
result =
if type || type.nil?
["#{b} #{name}: #{result.lines.to_a.last}"]
elsif result =~ /^Interrupted|^Timeout/
["#{b} #{name}: #{result}"]
else
["#{b} #{name}"] + result.lines.map { |l| " " << l }
end
if lnum = where.call(name)
$curbuf.delete lnum
lnum = 4 if ing && lnum > maxy
end
result.each_with_index do |line, offset|
$curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
end
logh.call
end
}
bt = proc { |cmd, name, type, cleanup|
tried = timeout = 0
begin
tried += 1
timeout += limit
fd = nil
data = ''
if iswin
Timeout::timeout(timeout) do
tmp = VIM::evaluate('tempname()')
2015-02-26 02:48:04 +00:00
system("(#{cmd}) > #{tmp}")
2015-01-28 10:05:47 +00:00
data = File.read(tmp).chomp
File.unlink tmp rescue nil
end
else
fd = IO.popen(cmd).extend(PlugStream)
first_line = true
log_prob = 1.0 / nthr
while line = Timeout::timeout(timeout) { fd.get_line }
data << line
log.call name, line.chomp, type if name && (first_line || rand < log_prob)
first_line = false
end
fd.close
end
[$? == 0, data.chomp]
rescue Timeout::Error, Interrupt => e
if fd && !fd.closed?
killall fd.pid
fd.close
end
cleanup.call if cleanup
if e.is_a?(Timeout::Error) && tried < tries
3.downto(1) do |countdown|
s = countdown > 1 ? 's' : ''
log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
sleep 1
end
log.call name, 'Retrying ...', type
retry
end
[false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
end
}
main = Thread.current
threads = []
watcher = Thread.new {
while VIM::evaluate('getchar(1)')
sleep 0.1
end
mtx.synchronize do
running = false
threads.each { |t| t.raise Interrupt }
end
threads.each { |t| t.join rescue nil }
main.kill
}
refresh = Thread.new {
while true
mtx.synchronize do
break unless running
VIM::command('noautocmd normal! a')
end
sleep 0.2
end
} if VIM::evaluate('s:mac_gui') == 1
clone_opt = VIM::evaluate('s:clone_opt')
2015-02-15 02:19:01 +00:00
progress = VIM::evaluate('s:progress_opt(1)')
2015-01-28 10:05:47 +00:00
nthr.times do
mtx.synchronize do
threads << Thread.new {
while pair = take1.call
name = pair.first
2015-02-26 02:48:04 +00:00
dir, uri, branch, tag = pair.last.values_at *%w[dir uri branch tag]
checkout = esc(tag ? tag : branch)
merge = esc(tag ? tag : "origin/#{branch}")
2015-01-28 10:05:47 +00:00
subm = "git submodule update --init --recursive 2>&1"
exists = File.directory? dir
ok, result =
if exists
2015-12-12 12:40:42 +00:00
chdir = "#{cd} #{iswin ? dir : esc(dir)}"
ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil
2015-01-28 10:05:47 +00:00
current_uri = data.lines.to_a.last
if !ret
if data =~ /^Interrupted|^Timeout/
[false, data]
else
[false, [data.chomp, "PlugClean required."].join($/)]
end
elsif current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '')
[false, ["Invalid URI: #{current_uri}",
"Expected: #{uri}",
"PlugClean required."].join($/)]
else
if pull
log.call name, 'Updating ...', :update
fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
2015-12-12 12:40:42 +00:00
bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1 && git checkout -q #{checkout} 2>&1 && git merge --ff-only #{merge} 2>&1 && #{subm}", name, :update, nil
2015-01-28 10:05:47 +00:00
else
[true, skip]
end
end
else
d = esc dir.sub(%r{[\\/]+$}, '')
log.call name, 'Installing ...', :install
bt.call "git clone #{clone_opt unless tag} #{progress} --recursive #{uri} -b #{checkout} #{d} 2>&1", name, :install, proc {
2015-01-28 10:05:47 +00:00
FileUtils.rm_rf dir
}
end
mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
log.call name, result, ok
end
} if running
end
end
threads.each { |t| t.join rescue nil }
logh.call
refresh.kill if refresh
watcher.kill
EOF
endfunction
function! s:shellesc(arg)
2015-02-26 02:48:04 +00:00
return '"'.escape(a:arg, '"').'"'
2015-01-28 10:05:47 +00:00
endfunction
function! s:glob_dir(path)
2016-03-03 20:38:39 +00:00
return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
2015-01-28 10:05:47 +00:00
endfunction
function! s:progress_bar(line, bar, total)
call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
endfunction
function! s:compare_git_uri(a, b)
let a = substitute(a:a, 'git:\{1,2}@', '', '')
let b = substitute(a:b, 'git:\{1,2}@', '', '')
return a ==# b
endfunction
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(s:lines(a:message), '" ".v:val')
return extend([printf('x %s:', a:name)], lines)
endif
endfunction
function! s:with_cd(cmd, dir)
2015-02-26 02:48:04 +00:00
return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd)
2015-01-28 10:05:47 +00:00
endfunction
function! s:system(cmd, ...)
2015-02-15 02:19:01 +00:00
try
let [sh, shrd] = [&shell, &shellredir]
2015-02-15 02:19:01 +00:00
if !s:is_win
set shell=sh shellredir=>%s\ 2>&1
2015-02-15 02:19:01 +00:00
endif
let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd
return system(s:is_win ? '('.cmd.')' : cmd)
finally
let [&shell, &shellredir] = [sh, shrd]
2015-02-15 02:19:01 +00:00
endtry
2015-01-28 10:05:47 +00:00
endfunction
function! s:system_chomp(...)
let ret = call('s:system', a:000)
return v:shell_error ? '' : substitute(ret, '\n$', '', '')
endfunction
2015-12-12 12:40:42 +00:00
function! s:git_validate(spec, check_branch)
let err = ''
2015-01-28 10:05:47 +00:00
if isdirectory(a:spec.dir)
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
2015-12-12 12:40:42 +00:00
let err = join([remote, 'PlugClean required.'], "\n")
2015-01-28 10:05:47 +00:00
elseif !s:compare_git_uri(remote, a:spec.uri)
2015-12-12 12:40:42 +00:00
let err = join(['Invalid URI: '.remote,
2015-01-28 10:05:47 +00:00
\ 'Expected: '.a:spec.uri,
\ 'PlugClean required.'], "\n")
2015-12-12 12:40:42 +00:00
elseif a:check_branch && has_key(a:spec, 'commit')
let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
let sha = result[-1]
if v:shell_error
let err = join(add(result, 'PlugClean required.'), "\n")
elseif !s:hash_match(sha, a:spec.commit)
let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
\ a:spec.commit[:6], sha[:6]),
\ 'PlugUpdate required.'], "\n")
endif
2015-01-28 10:05:47 +00:00
elseif a:check_branch
let branch = result[0]
2015-02-26 02:48:04 +00:00
" Check tag
if has_key(a:spec, 'tag')
2015-01-28 10:05:47 +00:00
let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
2015-02-26 02:48:04 +00:00
if a:spec.tag !=# tag
2015-12-12 12:40:42 +00:00
let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
2015-02-26 02:48:04 +00:00
\ (empty(tag) ? 'N/A' : tag), a:spec.tag)
2015-01-28 10:05:47 +00:00
endif
2015-02-26 02:48:04 +00:00
" Check branch
elseif a:spec.branch !=# branch
2015-12-12 12:40:42 +00:00
let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
2015-02-26 02:48:04 +00:00
\ branch, a:spec.branch)
2015-01-28 10:05:47 +00:00
endif
endif
else
2015-12-12 12:40:42 +00:00
let err = 'Not found'
2015-01-28 10:05:47 +00:00
endif
2015-12-12 12:40:42 +00:00
return err
2015-01-28 10:05:47 +00:00
endfunction
2015-04-04 11:32:24 +00:00
function! s:rm_rf(dir)
if isdirectory(a:dir)
call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir))
endif
endfunction
2015-01-28 10:05:47 +00:00
function! s:clean(force)
call s:prepare()
call append(0, 'Searching for unused plugins in '.g:plug_home)
call append(1, '')
" List of valid directories
let dirs = []
let [cnt, total] = [0, len(g:plugs)]
for [name, spec] in items(g:plugs)
2015-12-12 12:40:42 +00:00
if !s:is_managed(name) || empty(s:git_validate(spec, 0))
2015-01-28 10:05:47 +00:00
call add(dirs, spec.dir)
endif
let cnt += 1
call s:progress_bar(2, repeat('=', cnt), total)
normal! 2G
redraw
endfor
let allowed = {}
for dir in dirs
2015-02-15 02:19:01 +00:00
let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1
2015-01-28 10:05:47 +00:00
let allowed[dir] = 1
for child in s:glob_dir(dir)
let allowed[child] = 1
endfor
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
if a:force || s:ask('Proceed?')
2015-01-28 10:05:47 +00:00
for dir in todo
2015-04-04 11:32:24 +00:00
call s:rm_rf(dir)
2015-01-28 10:05:47 +00:00
endfor
call append(line('$'), 'Removed.')
else
call append(line('$'), 'Cancelled.')
endif
endif
normal! G
endfunction
function! s:upgrade()
2015-04-04 11:32:24 +00:00
echo 'Downloading the latest version of vim-plug'
2015-01-28 10:05:47 +00:00
redraw
2015-04-04 11:32:24 +00:00
let tmp = tempname()
let new = tmp . '/plug.vim'
2015-01-28 10:05:47 +00:00
try
2015-04-04 11:32:24 +00:00
let out = s:system(printf('git clone --depth 1 %s %s', s:plug_src, tmp))
if v:shell_error
return s:err('Error upgrading vim-plug: '. out)
endif
if readfile(s:me) ==# readfile(new)
echo 'vim-plug is already up-to-date'
return 0
2015-01-28 10:05:47 +00:00
else
2015-04-04 11:32:24 +00:00
call rename(s:me, s:me . '.old')
call rename(new, s:me)
unlet g:loaded_plug
echo 'vim-plug has been upgraded'
return 1
2015-01-28 10:05:47 +00:00
endif
2015-04-04 11:32:24 +00:00
finally
silent! call s:rm_rf(tmp)
2015-01-28 10:05:47 +00:00
endtry
2015-02-26 02:48:04 +00:00
endfunction
2015-01-28 10:05:47 +00:00
function! s:upgrade_specs()
for spec in values(g:plugs)
let spec.frozen = get(spec, 'frozen', 0)
endfor
endfunction
function! s:status()
call s:prepare()
call append(0, 'Checking plugins')
call append(1, '')
let ecnt = 0
let unloaded = 0
let [cnt, total] = [0, len(g:plugs)]
for [name, spec] in items(g:plugs)
if has_key(spec, 'uri')
if isdirectory(spec.dir)
2015-12-12 12:40:42 +00:00
let err = s:git_validate(spec, 1)
let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
2015-01-28 10:05:47 +00:00
else
let [valid, msg] = [0, 'Not found. Try PlugInstall.']
endif
else
if isdirectory(spec.dir)
let [valid, msg] = [1, 'OK']
else
let [valid, msg] = [0, 'Not found.']
endif
endif
let cnt += 1
let ecnt += !valid
" `s:loaded` entry can be missing if PlugUpgraded
if valid && get(s:loaded, name, -1) == 0
let unloaded = 1
let msg .= ' (not loaded)'
endif
call s:progress_bar(2, repeat('=', cnt), total)
call append(3, s:format_message(valid ? '-' : 'x', name, msg))
normal! 2G
redraw
endfor
call setline(1, 'Finished. '.ecnt.' error(s).')
normal! gg
setlocal nomodifiable
if unloaded
echo "Press 'L' on each line to load plugin, or 'U' to update"
nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr>
end
endfunction
function! s:extract_name(str, prefix, suffix)
return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
endfunction
function! s:status_load(lnum)
let line = getline(a:lnum)
let name = s:extract_name(line, '-', '(not loaded)')
if !empty(name)
call plug#load(name)
setlocal modifiable
call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
setlocal nomodifiable
endif
endfunction
function! s:status_update() range
let lines = getline(a:firstline, a:lastline)
let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
if !empty(names)
echo
execute 'PlugUpdate' join(names)
endif
endfunction
function! s:is_preview_window_open()
silent! wincmd P
if &previewwindow
wincmd p
return 1
endif
return 0
endfunction
function! s:find_name(lnum)
for lnum in reverse(range(1, a:lnum))
let line = getline(lnum)
if empty(line)
return ''
endif
let name = s:extract_name(line, '-', '')
if !empty(name)
return name
endif
endfor
return ''
endfunction
function! s:preview_commit()
if b:plug_preview < 0
let b:plug_preview = !s:is_preview_window_open()
endif
2016-03-03 20:38:39 +00:00
let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-z]\{7}')
2015-01-28 10:05:47 +00:00
if empty(sha)
return
endif
let name = s:find_name(line('.'))
if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
return
endif
execute 'pedit' sha
wincmd P
2015-07-09 04:07:39 +00:00
setlocal filetype=git buftype=nofile nobuflisted modifiable
execute 'silent read !cd' s:shellesc(g:plugs[name].dir) '&& git show --pretty=medium' sha
2015-01-28 10:05:47 +00:00
normal! gg"_dd
2015-07-09 04:07:39 +00:00
setlocal nomodifiable
nnoremap <silent> <buffer> q :q<cr>
2015-01-28 10:05:47 +00:00
wincmd p
endfunction
function! s:section(flags)
call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
endfunction
function! s:format_git_log(line)
2016-03-03 20:38:39 +00:00
let indent = ' '
let tokens = split(a:line, nr2char(1))
if len(tokens) != 5
return indent.substitute(a:line, '\s*$', '', '')
endif
let [graph, sha, refs, subject, date] = tokens
let tag = matchstr(refs, 'tag: [^,)]\+')
let tag = empty(tag) ? ' ' : ' ('.tag.') '
2016-03-03 20:38:39 +00:00
return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date)
endfunction
function! s:append_ul(lnum, text)
call append(a:lnum, ['', a:text, repeat('-', len(a:text))])
endfunction
2015-01-28 10:05:47 +00:00
function! s:diff()
call s:prepare()
call append(0, ['Collecting changes ...', ''])
let cnts = [0, 0]
let bar = ''
let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)')
call s:progress_bar(2, bar, len(total))
for origin in [1, 0]
let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))'))))
if empty(plugs)
2015-01-28 10:05:47 +00:00
continue
endif
call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
for [k, v] in plugs
let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
2016-03-03 20:38:39 +00:00
let diff = s:system_chomp('git log --graph --color=never --pretty=format:"%x01%h%x01%d%x01%s%x01%cr" '.s:shellesc(range), v.dir)
if !empty(diff)
let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
let cnts[origin] += 1
endif
let bar .= '='
call s:progress_bar(2, bar, len(total))
normal! 2G
2015-01-28 10:05:47 +00:00
redraw
endfor
if !cnts[origin]
call append(5, ['', 'N/A'])
2015-01-28 10:05:47 +00:00
endif
endfor
call setline(1, printf('%d plugin(s) updated.', cnts[0])
\ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : ''))
2015-01-28 10:05:47 +00:00
if cnts[0] || cnts[1]
nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
nnoremap <silent> <buffer> o :silent! call <SID>preview_commit()<cr>
endif
if cnts[0]
nnoremap <silent> <buffer> X :call <SID>revert()<cr>
2015-01-28 10:05:47 +00:00
echo "Press 'X' on each block to revert the update"
endif
normal! gg
setlocal nomodifiable
2015-01-28 10:05:47 +00:00
endfunction
function! s:revert()
if search('^Pending updates', 'bnW')
return
endif
2015-01-28 10:05:47 +00:00
let name = s:find_name(line('.'))
if empty(name) || !has_key(g:plugs, name) ||
\ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
return
endif
call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch), g:plugs[name].dir)
setlocal modifiable
normal! "_dap
setlocal nomodifiable
echo 'Reverted.'
endfunction
function! s:snapshot(force, ...) abort
2015-01-28 10:05:47 +00:00
call s:prepare()
setf vim
call append(0, ['" Generated by vim-plug',
\ '" '.strftime("%c"),
\ '" :source this file in vim to restore the snapshot',
\ '" or execute: vim -S snapshot.vim',
\ '', '', 'PlugUpdate!'])
2015-01-28 10:05:47 +00:00
1
let anchor = line('$') - 3
let names = sort(keys(filter(copy(g:plugs),
\'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
for name in reverse(names)
let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir)
2015-01-28 10:05:47 +00:00
if !empty(sha)
call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
2015-01-28 10:05:47 +00:00
redraw
endif
endfor
if a:0 > 0
2015-02-15 02:19:01 +00:00
let fn = expand(a:1)
if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
return
endif
2015-01-28 10:05:47 +00:00
call writefile(getline(1, '$'), fn)
echo 'Saved as '.a:1
silent execute 'e' s:esc(fn)
setf vim
2015-01-28 10:05:47 +00:00
endif
endfunction
function! s:split_rtp()
return split(&rtp, '\\\@<!,')
endfunction
let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, ''))
let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, ''))
if exists('g:plugs')
let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs))
call s:upgrade_specs()
call s:define_commands()
endif
let &cpo = s:cpo_save
unlet s:cpo_save