42 Commits
0.5.7 ... 0.6.2

Author SHA1 Message Date
Junegunn Choi
3606c294de Merge pull request #135 from threeifbywhiskey/confirmation-convention
Downcase opt-in confirmation options
2014-12-08 23:58:05 +09:00
Dorien Snake
06f903d8af Downcase opt-in confirmation options
There's a fairly widespread convention which recommends indicating the default
confirmation option in uppercase. Granted, the number of applicable instances
in vim-plug is currently only two, but perhaps this'll save users with such an
inclination having to hit Enter just to sate their curiosities.
2014-12-08 08:49:45 -05:00
Junegunn Choi
1f704deb76 Merge branch 'master' of github.com:junegunn/vim-plug 2014-12-08 17:37:46 +09:00
Junegunn Choi
3ebf6361bf Update README 2014-12-08 17:37:26 +09:00
Junegunn Choi
9b99c1f885 Merge pull request #134 from wilywampa/master
Check for full match of command name
2014-12-08 10:02:09 +09:00
Jacob Niehus
53cc8a53d9 Check for full match of command name 2014-12-07 13:19:09 -07:00
Junegunn Choi
2ee153d610 Define s:triggers by default to avoid error after upgrade (#132) 2014-12-05 12:05:57 +09:00
Junegunn Choi
7019171737 Fix #131 - Syntax error 2014-12-04 15:13:37 +09:00
Junegunn Choi
044c3a67c4 Fix #130 - Proper cleanup of on-demand loading triggers 2014-12-04 13:06:13 +09:00
Junegunn Choi
48514768c2 Fix missing progress update (#127)
In the recent versions of NeoVim, jobstart() does not return
monotonically increasing numbers, this caused vim-plug to miss updating
the progress of a task when the job ID for the task is already
reassigned to a new task.
2014-12-02 02:48:25 +09:00
Junegunn Choi
68ad02c5c3 Display "Press 'D'" message only when existing plugins are updated
Fix #123
2014-11-24 03:32:01 +09:00
Junegunn Choi
12bc2ea1da Fix some test code 2014-11-21 01:06:17 +09:00
Junegunn Choi
ade7fb95b1 Fix failing test case 2014-11-08 11:33:11 +09:00
Junegunn Choi
9c55223869 Merge pull request #118 from splinterofchaos/job_data-list
Update: job_data[2] as a list.
2014-11-08 10:28:53 +09:00
Scott Prager
495becf442 Update: job_data[2] is a list. 2014-10-29 01:03:58 -04:00
Junegunn Choi
425ef39db2 Fix #114 - &rtp should not contain empty path 2014-10-22 13:36:57 +09:00
Junegunn Choi
bd2cb9d2de Install frozen plugin if it's not found (#113) 2014-10-22 13:05:10 +09:00
Junegunn Choi
0263370bd1 Fix #112: Do not suppress messages from ftplugin 2014-10-19 14:45:19 +09:00
Junegunn Choi
4b3fbd1592 Workaround for PlugUpgrade error on Neovim (#111) 2014-10-18 11:26:05 +09:00
Junegunn Choi
396a534a0a Update README
g:plug_timeout and g:plug_retries are only for Ruby installer
2014-10-10 23:18:40 +09:00
Junegunn Choi
a45c3834f8 Merge pull request #103 from junegunn/neuevim
Parallel installer for Neovim
2014-10-10 23:11:40 +09:00
Junegunn Choi
a5c3952380 Sort auto-completion candidates 2014-10-10 17:36:54 +09:00
Junegunn Choi
60bda7322f Improve ]]/[[ movement 2014-10-10 17:35:07 +09:00
Junegunn Choi
05008e7a82 Use s:extract_name instead of matchstr 2014-10-10 17:34:39 +09:00
Junegunn Choi
2889cb4739 Minor refactoring 2014-10-10 15:50:43 +09:00
Junegunn Choi
61b21068ee Update test case (related: #8c915a5) 2014-10-10 10:54:00 +09:00
Junegunn Choi
15da7eb78a Code cleanup 2014-10-10 10:39:49 +09:00
Junegunn Choi
385a1eb350 Detect abnormal process exit using 'Error' line 2014-10-10 10:28:15 +09:00
Junegunn Choi
8c915a5271 Reuse plug window even if it's in another tab 2014-10-10 10:23:00 +09:00
Junegunn Choi
ee9f0e55b8 Use s:lines instead of split 2014-10-10 10:10:52 +09:00
Junegunn Choi
018adb2aef Disable NVim parallel installer on Windows 2014-10-10 10:06:29 +09:00
Junegunn Choi
da47e6ee56 Avoid unnecessary tab/window switch 2014-10-10 04:39:38 +09:00
Junegunn Choi
662274e617 Stabilize Neovim installer
- Abort running jobs when plug windows is reset
- Multi-line error report
- Retain window view
2014-10-10 01:10:34 +09:00
Junegunn Choi
4eeff535fa Parallel installer for Neovim 2014-10-09 19:55:36 +09:00
Junegunn Choi
c3669836d3 Fix Travis CI build 2014-10-02 01:32:23 +09:00
Junegunn Choi
eb38fe3d32 Fix Travis CI build 2014-10-02 01:21:52 +09:00
Junegunn Choi
e7704e6cb3 PlugSnapshot to use unexpanded plug home
/cc @andreicristianpetcu

plug#begin expands its path argument and converts it to the absolute
path by default. However, it makes sense to use the unexpanded form in
case of PlugSnapshot as described in
https://github.com/junegunn/vim-plug/issues/97#issuecomment-57421483

For example, for the following cases,

- call plug#begin('~/.vim/plugged')
- call plug#begin('$HOME/.vim/plugged')

PlugSnapshot will use the exact arguments, `~/.vim/plugged` or
`$HOME/.vim/plugged`, instead of the absolute paths such as
`/home/jg/.vim/plugged`.
2014-10-01 23:14:27 +09:00
Junegunn Choi
e1e04cabd5 Minor refactoring
/cc @vheon
2014-10-01 11:37:58 +09:00
Junegunn Choi
afc20ecff3 Implement PlugSnapshot (#97)
Known issue: After running the script, PlugDiff will show "future"
commits.
2014-10-01 03:10:24 +09:00
Junegunn Choi
38510a8788 Remove unnecessary submodule command after recursive clone 2014-09-29 02:12:33 +09:00
Junegunn Choi
a34b06dd54 Fix Travis-CI build 2014-09-27 15:30:58 +09:00
Junegunn Choi
5d910fc9ea Allow unmanaged plugins inside g:plug_home (#95) 2014-09-27 14:41:57 +09:00
6 changed files with 623 additions and 210 deletions

View File

@@ -1,9 +1,9 @@
![vim-plug](https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.png) <img src="https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.png" height="56" alt="vim-plug">
[![travis-ci](https://travis-ci.org/junegunn/vim-plug.svg?branch=master)](https://travis-ci.org/junegunn/vim-plug) [![travis-ci](https://travis-ci.org/junegunn/vim-plug.svg?branch=master)](https://travis-ci.org/junegunn/vim-plug)
A minimalist Vim plugin manager. A minimalist Vim plugin manager.
![](https://raw.githubusercontent.com/junegunn/i/master/vim-plug/installer.gif) <img src="https://raw.githubusercontent.com/junegunn/i/master/vim-plug/installer.gif" height="450">
### Pros. ### Pros.
@@ -11,7 +11,8 @@ A minimalist Vim plugin manager.
- Easier to use: Concise, intuitive syntax - Easier to use: Concise, intuitive syntax
- [Super-fast](https://raw.githubusercontent.com/junegunn/i/master/vim-plug/40-in-4.gif) - [Super-fast](https://raw.githubusercontent.com/junegunn/i/master/vim-plug/40-in-4.gif)
parallel installation/update (requires parallel installation/update (requires
[+ruby](https://github.com/junegunn/vim-plug/wiki/ruby)) [+ruby](https://github.com/junegunn/vim-plug/wiki/ruby) or
[Neovim](http://neovim.org/))
- On-demand loading to achieve - On-demand loading to achieve
[fast startup time](http://junegunn.kr/images/vim-startup-time.png) [fast startup time](http://junegunn.kr/images/vim-startup-time.png)
- Post-update hooks - Post-update hooks
@@ -69,28 +70,29 @@ Reload .vimrc and `:PlugInstall` to install plugins.
| `PlugUpgrade` | Upgrade vim-plug itself | | `PlugUpgrade` | Upgrade vim-plug itself |
| `PlugStatus` | Check the status of plugins | | `PlugStatus` | Check the status of plugins |
| `PlugDiff` | See the updated changes from the previous PlugUpdate | | `PlugDiff` | See the updated changes from the previous PlugUpdate |
| `PlugSnapshot [output path]` | Generate script for restoring the current snapshot of the plugins |
### `Plug` options ### `Plug` options
| Option | Description | | Option | Description |
| -------------- | -------------------------------------------------------------------- | | -------------- | ------------------------------------------------ |
| `branch`/`tag` | Branch or tag of the repository to use | | `branch`/`tag` | Branch or tag of the repository to use |
| `rtp` | Subdirectory that contains Vim plugin | | `rtp` | Subdirectory that contains Vim plugin |
| `dir` | Custom directory for the plugin | | `dir` | Custom directory for the plugin |
| `do` | Post-update hook (string or funcref) | | `do` | Post-update hook (string or funcref) |
| `on` | On-demand loading: Commands or `<Plug>`-mappings | | `on` | On-demand loading: Commands or `<Plug>`-mappings |
| `for` | On-demand loading: File types | | `for` | On-demand loading: File types |
| `frozen` | Do not install/update plugin unless explicitly given as the argument | | `frozen` | Do not update unless explicitly specified |
### Global options ### Global options
| Flag | Default | Description | | Flag | Default | Description |
| ------------------- | --------------------------------- | ------------------------------------ | | ------------------- | --------------------------------- | ----------------------------------------------------------- |
| `g:plug_threads` | 16 | Default number of threads to use | | `g:plug_threads` | 16 | Default number of threads to use |
| `g:plug_timeout` | 60 | Time limit of each task in seconds | | `g:plug_timeout` | 60 | Time limit of each task in seconds (*for Ruby installer*) |
| `g:plug_retries` | 2 | Number of retries in case of timeout | | `g:plug_retries` | 2 | Number of retries in case of timeout (*for Ruby installer*) |
| `g:plug_window` | `vertical topleft new` | Command to open plug window | | `g:plug_window` | `vertical topleft new` | Command to open plug window |
| `g:plug_url_format` | `https://git::@github.com/%s.git` | `printf` format to build repo URL | | `g:plug_url_format` | `https://git::@github.com/%s.git` | `printf` format to build repo URL |
### Keybindings ### Keybindings

558
plug.vim
View File

@@ -68,10 +68,12 @@ let g:loaded_plug = 1
let s:cpo_save = &cpo let s:cpo_save = &cpo
set cpo&vim set cpo&vim
let s:plug_source = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' let s:plug_src = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
let s:plug_tab = get(s:, 'plug_tab', -1)
let s:plug_buf = get(s:, 'plug_buf', -1) let s:plug_buf = get(s:, 'plug_buf', -1)
let s:mac_gui = has('gui_macvim') && has('gui_running') let s:mac_gui = has('gui_macvim') && has('gui_running')
let s:is_win = has('win32') || has('win64') let s:is_win = has('win32') || has('win64')
let s:nvim = exists('##JobActivity') && !s:is_win
let s:me = resolve(expand('<sfile>:p')) let s:me = resolve(expand('<sfile>:p'))
let s:base_spec = { 'branch': 'master', 'frozen': 0 } let s:base_spec = { 'branch': 'master', 'frozen': 0 }
let s:TYPE = { let s:TYPE = {
@@ -81,9 +83,11 @@ let s:TYPE = {
\ 'funcref': type(function('call')) \ 'funcref': type(function('call'))
\ } \ }
let s:loaded = get(s:, 'loaded', {}) let s:loaded = get(s:, 'loaded', {})
let s:triggers = get(s:, 'triggers', {})
function! plug#begin(...) function! plug#begin(...)
if a:0 > 0 if a:0 > 0
let s:plug_home_org = a:1
let home = s:path(fnamemodify(expand(a:1), ':p')) let home = s:path(fnamemodify(expand(a:1), ':p'))
elseif exists('g:plug_home') elseif exists('g:plug_home')
let home = s:path(g:plug_home) let home = s:path(g:plug_home)
@@ -95,8 +99,8 @@ function! plug#begin(...)
let g:plug_home = home let g:plug_home = home
let g:plugs = {} let g:plugs = {}
" we want to keep track of the order plugins where registered.
let g:plugs_order = [] let g:plugs_order = []
let s:triggers = {}
call s:define_commands() call s:define_commands()
return 1 return 1
@@ -113,15 +117,20 @@ function! s:define_commands()
command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:me | endif command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:me | endif
command! -nargs=0 -bar PlugStatus call s:status() command! -nargs=0 -bar PlugStatus call s:status()
command! -nargs=0 -bar PlugDiff call s:diff() command! -nargs=0 -bar PlugDiff call s:diff()
command! -nargs=? -bar PlugSnapshot call s:snapshot(<f-args>)
endfunction endfunction
function! s:to_a(v) function! s:to_a(v)
return type(a:v) == s:TYPE.list ? a:v : [a:v] return type(a:v) == s:TYPE.list ? a:v : [a:v]
endfunction endfunction
function! s:to_s(v)
return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
endfunction
function! s:source(from, ...) function! s:source(from, ...)
for pattern in a:000 for pattern in a:000
for vim in split(globpath(a:from, pattern), '\n') for vim in s:lines(globpath(a:from, pattern))
execute 'source' vim execute 'source' vim
endfor endfor
endfor endfor
@@ -149,6 +158,7 @@ function! plug#end()
endif endif
if has_key(plug, 'on') if has_key(plug, 'on')
let s:triggers[name] = { 'map': [], 'cmd': [] }
for cmd in s:to_a(plug.on) for cmd in s:to_a(plug.on)
if cmd =~ '^<Plug>.\+' if cmd =~ '^<Plug>.\+'
if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i')) if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
@@ -159,10 +169,14 @@ function! plug#end()
\ mode, cmd, map_prefix, string(cmd), string(name), key_prefix) \ mode, cmd, map_prefix, string(cmd), string(name), key_prefix)
endfor endfor
endif endif
elseif !exists(':'.cmd) call add(s:triggers[name].map, cmd)
execute printf( elseif cmd =~ '^[A-Z]'
\ 'command! -nargs=* -range -bang %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)', if exists(':'.cmd) != 2
\ cmd, string(cmd), string(name)) execute printf(
\ 'command! -nargs=* -range -bang %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
\ cmd, string(cmd), string(name))
endif
call add(s:triggers[name].cmd, cmd)
endif endif
endfor endfor
endif endif
@@ -286,9 +300,10 @@ function! s:reorg_rtp()
let s:middle = get(s:, 'middle', &rtp) let s:middle = get(s:, 'middle', &rtp)
let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') 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)') let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), 'isdirectory(v:val)')
let &rtp = join(map(rtps, 's:escrtp(v:val)'), ',') let rtp = join(map(rtps, 's:escrtp(v:val)'), ',')
\ . substitute(','.s:middle.',', '^,,$', ',', '') \ . ','.s:middle.','
\ . join(map(afters, 's:escrtp(v:val)'), ',') \ . join(map(afters, 's:escrtp(v:val)'), ',')
let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
let s:prtp = &rtp let s:prtp = &rtp
if !empty(s:first_rtp) if !empty(s:first_rtp)
@@ -312,12 +327,27 @@ function! plug#load(...)
for name in a:000 for name in a:000
call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
endfor endfor
silent! doautocmd BufRead doautocmd BufRead
return 1 return 1
endfunction endfunction
function! s:remove_triggers(name)
if !has_key(s:triggers, a:name)
return
endif
for cmd in s:triggers[a:name].cmd
execute 'delc' cmd
endfor
for map in s:triggers[a:name].map
execute 'unmap' map
execute 'iunmap' map
endfor
call remove(s:triggers, a:name)
endfunction
function! s:lod(names, types) function! s:lod(names, types)
for name in a:names for name in a:names
call s:remove_triggers(name)
let s:loaded[name] = 1 let s:loaded[name] = 1
endfor endfor
call s:reorg_rtp() call s:reorg_rtp()
@@ -333,19 +363,16 @@ endfunction
function! s:lod_ft(pat, names) function! s:lod_ft(pat, names)
call s:lod(a:names, ['plugin', 'after/plugin']) call s:lod(a:names, ['plugin', 'after/plugin'])
execute 'autocmd! PlugLOD FileType' a:pat execute 'autocmd! PlugLOD FileType' a:pat
silent! doautocmd filetypeplugin FileType doautocmd filetypeplugin FileType
silent! doautocmd filetypeindent FileType doautocmd filetypeindent FileType
endfunction endfunction
function! s:lod_cmd(cmd, bang, l1, l2, args, name) function! s:lod_cmd(cmd, bang, l1, l2, args, name)
execute 'delc' a:cmd
call s:lod([a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) call s:lod([a:name], ['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) execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
endfunction endfunction
function! s:lod_map(map, name, prefix) function! s:lod_map(map, name, prefix)
execute 'unmap' a:map
execute 'iunmap' a:map
call s:lod([a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) call s:lod([a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
let extra = '' let extra = ''
while 1 while 1
@@ -481,25 +508,59 @@ function! s:lpad(str, len)
return a:str . repeat(' ', a:len - len(a:str)) return a:str . repeat(' ', a:len - len(a:str))
endfunction endfunction
function! s:lines(msg)
return split(a:msg, "[\r\n]")
endfunction
function! s:lastline(msg) function! s:lastline(msg)
let lines = split(a:msg, '\n') return get(s:lines(a:msg), -1, '')
return get(lines, -1, '')
endfunction endfunction
function! s:new_window() function! s:new_window()
execute get(g:, 'plug_window', 'vertical topleft new') execute get(g:, 'plug_window', 'vertical topleft new')
endfunction endfunction
function! s:prepare() function! s:plug_window_exists()
if bufexists(s:plug_buf) 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) let winnr = bufwinnr(s:plug_buf)
if winnr < 0 execute winnr.'wincmd w'
call s:new_window() call add(s:pos, winsaveview())
execute 'buffer' s:plug_buf else
else let s:pos = [winsaveview()]
execute winnr . 'wincmd w' endif
endif
setlocal modifiable 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:prepare()
call s:job_abort()
if s:switch_in()
silent %d _ silent %d _
else else
call s:new_window() call s:new_window()
@@ -512,6 +573,7 @@ function! s:prepare()
nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr> nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr> nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
let b:plug_preview = -1 let b:plug_preview = -1
let s:plug_tab = tabpagenr()
let s:plug_buf = winbufnr(0) let s:plug_buf = winbufnr(0)
call s:assign_name() call s:assign_name()
endif endif
@@ -540,11 +602,11 @@ function! s:do(pull, force, todo)
if !isdirectory(spec.dir) if !isdirectory(spec.dir)
continue continue
endif endif
execute 'cd' s:esc(spec.dir) let installed = has_key(s:update.new, name)
let installed = has_key(s:prev_update.new, name)
let updated = installed ? 0 : let updated = installed ? 0 :
\ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"'))) \ (a:pull && !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', spec.dir)))
if a:force || installed || updated if a:force || installed || updated
execute 'cd' s:esc(spec.dir)
call append(3, '- Post-update hook for '. name .' ... ') call append(3, '- Post-update hook for '. name .' ... ')
let type = type(spec.do) let type = type(spec.do)
if type == s:TYPE.string if type == s:TYPE.string
@@ -569,25 +631,28 @@ function! s:do(pull, force, todo)
let result = 'Error: Invalid type!' let result = 'Error: Invalid type!'
endif endif
call setline(4, getline(4) . result) call setline(4, getline(4) . result)
cd -
endif endif
cd -
endfor endfor
endfunction endfunction
function! s:finish(pull) 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 ... ') call append(3, '- Finishing ... ')
redraw redraw
call plug#helptags() call plug#helptags()
call plug#end() call plug#end()
call setline(4, getline(4) . 'Done!') call setline(4, getline(4) . 'Done!')
normal! gg
call s:syntax()
redraw redraw
let msgs = [] let msgs = []
if !empty(s:prev_update.errors) if !empty(s:update.errors)
call add(msgs, "Press 'R' to retry.") call add(msgs, "Press 'R' to retry.")
endif endif
if a:pull && !empty(filter(getline(5, '$'), if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
\ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0")) \ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0"))
call add(msgs, "Press 'D' to see the updated changes.") call add(msgs, "Press 'D' to see the updated changes.")
endif endif
@@ -595,11 +660,11 @@ function! s:finish(pull)
endfunction endfunction
function! s:retry() function! s:retry()
if empty(s:prev_update.errors) if empty(s:update.errors)
return return
endif endif
call s:update_impl(s:prev_update.pull, s:prev_update.force, call s:update_impl(s:update.pull, s:update.force,
\ extend(copy(s:prev_update.errors), [s:prev_update.threads])) \ extend(copy(s:update.errors), [s:update.threads]))
endfunction endfunction
function! s:is_managed(name) function! s:is_managed(name)
@@ -607,17 +672,16 @@ function! s:is_managed(name)
endfunction endfunction
function! s:names(...) function! s:names(...)
return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)') return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)'))
endfunction endfunction
function! s:update_impl(pull, force, args) abort function! s:update_impl(pull, force, args) abort
let st = reltime()
let args = copy(a:args) let args = copy(a:args)
let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
\ remove(args, -1) : get(g:, 'plug_threads', 16) \ remove(args, -1) : get(g:, 'plug_threads', 16)
let managed = filter(copy(g:plugs), 's:is_managed(v:key)') let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
let todo = empty(args) ? filter(managed, '!v:val.frozen') : let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
\ filter(managed, 'index(args, v:key) >= 0') \ filter(managed, 'index(args, v:key) >= 0')
if empty(todo) if empty(todo)
@@ -631,103 +695,251 @@ function! s:update_impl(pull, force, args) abort
try try
call mkdir(g:plug_home, 'p') call mkdir(g:plug_home, 'p')
catch catch
return s:err(printf('Invalid plug directory: %s.' return s:err(printf('Invalid plug directory: %s. '.
\ 'Try to call plug#begin with a valid directory', g:plug_home)) \ 'Try to call plug#begin with a valid directory', g:plug_home))
endtry endtry
endif endif
call s:prepare() let s:update = {
call append(0, a:pull ? 'Updating plugins' : 'Installing plugins') \ 'start': reltime(),
call append(1, '['. s:lpad('', len(todo)) .']') \ 'all': todo,
normal! 2G \ 'todo': copy(todo),
redraw \ 'errors': [],
\ 'pull': a:pull,
\ 'force': a:force,
\ 'new': {},
\ 'threads': (has('ruby') || s:nvim) ? min([len(todo), threads]) : 1,
\ 'bar': '',
\ 'fin': 0
\ }
let s:prev_update = { 'errors': [], 'pull': a:pull, 'force': a:force, 'new': {}, 'threads': threads } call s:prepare()
if has('ruby') && threads > 1 call append(0, ['', ''])
normal! 2G
if has('ruby') && s:update.threads > 1
try try
let imd = &imd let imd = &imd
if s:mac_gui if s:mac_gui
set noimd set noimd
endif endif
call s:update_parallel(a:pull, todo, threads) call s:update_ruby()
catch catch
let lines = getline(4, '$') let lines = getline(4, '$')
let printed = {} let printed = {}
silent 4,$d _ silent 4,$d _
for line in lines for line in lines
let name = matchstr(line, '^. \zs[^:]\+\ze:') let name = s:extract_name(line, '.', '')
if empty(name) || !has_key(printed, name) if empty(name) || !has_key(printed, name)
call append('$', line) call append('$', line)
if !empty(name) if !empty(name)
let printed[name] = 1 let printed[name] = 1
if line[0] == 'x' && index(s:prev_update.errors, name) < 0 if line[0] == 'x' && index(s:update.errors, name) < 0
call add(s:prev_update.errors, name) call add(s:update.errors, name)
end end
endif endif
endif endif
endfor endfor
finally finally
let &imd = imd let &imd = imd
call s:update_finish()
endtry endtry
else else
call s:update_serial(a:pull, todo) call s:update_vim()
endif endif
call s:do(a:pull, a:force, filter(copy(todo), 'has_key(v:val, "do")'))
call s:finish(a:pull)
call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(st)))[0] . ' sec.')
endfunction endfunction
function! s:update_progress(pull, cnt, bar, total) function! s:update_finish()
call setline(1, (a:pull ? 'Updating' : 'Installing'). if s:switch_in()
\ ' plugins ('.a:cnt.'/'.a:total.')') call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'has_key(v:val, "do")'))
call s:progress_bar(2, a:bar, a:total) call s:finish(s:update.pull)
normal! 2G call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
redraw call s:switch_out('normal! gg')
endif
endfunction endfunction
function! s:update_serial(pull, todo) function! s:job_abort()
let base = g:plug_home if !s:nvim || !exists('s:jobs')
let todo = copy(a:todo) return
let total = len(todo) endif
let done = {} augroup PlugJobControl
let bar = '' autocmd!
augroup END
for [name, spec] in items(todo) for [name, j] in items(s:jobs)
let done[name] = 1 silent! call jobstop(j.jobid)
if isdirectory(spec.dir) if j.new
execute 'cd' s:esc(spec.dir) call system('rm -rf ' . s:shellesc(g:plugs[name].dir))
let [valid, msg] = s:git_valid(spec, 0, 0)
if valid
let result = a:pull ?
\ s:system(
\ printf('git checkout -q %s 2>&1 && git pull --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1',
\ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
let error = a:pull ? v:shell_error != 0 : 0
else
let result = msg
let error = 1
endif
cd -
else
let result = s:system(
\ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
\ s:shellesc(spec.uri),
\ s:shellesc(spec.branch),
\ s:shellesc(s:trim(spec.dir)),
\ s:shellesc(spec.dir)))
let error = v:shell_error != 0
if !error | let s:prev_update.new[name] = 1 | endif
endif endif
let bar .= error ? 'x' : '='
if error
call add(s:prev_update.errors, name)
endif
call append(3, s:format_message(!error, name, result))
call s:update_progress(a:pull, len(done), bar, total)
endfor endfor
let s:jobs = {}
endfunction endfunction
function! s:update_parallel(pull, todo, threads) function! s:job_handler(name) abort
if !s:plug_window_exists() " plug window closed
return s:job_abort()
endif
if !has_key(s:jobs, a:name)
return
endif
let job = s:jobs[a:name]
if v:job_data[1] == 'exit'
let job.running = 0
if s:lastline(job.result) ==# 'Error'
let job.error = 1
let job.result = substitute(job.result, "Error[\r\n]$", '', '')
endif
call s:reap(a:name)
call s:tick()
else
let job.result .= s:to_s(v:job_data[2])
" To reduce the number of buffer updates
let job.tick = get(job, 'tick', -1) + 1
if job.tick % len(s:jobs) == 0
call s:log(job.new ? '+' : '*', a:name, job.result)
endif
endif
endfunction
function! s:spawn(name, cmd, opts)
let job = { 'running': 1, 'new': get(a:opts, 'new', 0),
\ 'error': 0, 'result': '' }
let s:jobs[a:name] = job
if s:nvim
let x = jobstart(a:name, 'sh', ['-c',
\ (has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd)
\ . ' || echo Error'])
if x > 0
let job.jobid = x
augroup PlugJobControl
execute 'autocmd JobActivity' a:name printf('call s:job_handler(%s)', string(a:name))
augroup END
else
let job.running = 0
let job.error = 1
let job.result = x < 0 ? 'sh is not executable' :
\ 'Invalid arguments (or job table is full)'
endif
else
let params = has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd]
let job.result = call('s:system', params)
let job.error = v:shell_error != 0
let job.running = 0
endif
endfunction
function! s:reap(name)
if s:nvim
silent! execute 'autocmd! PlugJobControl JobActivity' a:name
endif
let job = s:jobs[a:name]
if job.error
call add(s:update.errors, a:name)
elseif get(job, 'new', 0)
let s:update.new[a:name] = 1
endif
let s:update.bar .= job.error ? 'x' : '='
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()
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 pull = s:update.pull
let new = !isdirectory(spec.dir)
call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
redraw
if !new
let [valid, msg] = s:git_valid(spec, 0)
if valid
if pull
call s:spawn(name,
\ printf('git checkout -q %s 2>&1 && git pull --progress --no-rebase origin %s 2>&1 && git submodule update --init --recursive 2>&1',
\ s:shellesc(spec.branch), s:shellesc(spec.branch)), { 'dir': spec.dir })
else
let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 }
endif
else
let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 }
endif
else
call s:spawn(name,
\ printf('git clone --progress --recursive %s -b %s %s 2>&1',
\ s:shellesc(spec.uri),
\ s:shellesc(spec.branch),
\ s:shellesc(s:trim(spec.dir))), { 'new': 1 })
endif
if !s:jobs[name].running
call s:reap(name)
endif
if len(s:jobs) >= s:update.threads
break
endif
endwhile
endfunction
function! s:update_ruby()
ruby << EOF ruby << EOF
module PlugStream module PlugStream
SEP = ["\r", "\n", nil] SEP = ["\r", "\n", nil]
@@ -769,15 +981,15 @@ function! s:update_parallel(pull, todo, threads)
require 'timeout' require 'timeout'
running = true running = true
iswin = VIM::evaluate('s:is_win').to_i == 1 iswin = VIM::evaluate('s:is_win').to_i == 1
pull = VIM::evaluate('a:pull').to_i == 1 pull = VIM::evaluate('s:update.pull').to_i == 1
base = VIM::evaluate('g:plug_home') base = VIM::evaluate('g:plug_home')
all = VIM::evaluate('a:todo') all = VIM::evaluate('s:update.todo')
limit = VIM::evaluate('get(g:, "plug_timeout", 60)') limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
nthr = VIM::evaluate('a:threads').to_i nthr = VIM::evaluate('s:update.threads').to_i
maxy = VIM::evaluate('winheight(".")').to_i maxy = VIM::evaluate('winheight(".")').to_i
cd = iswin ? 'cd /d' : 'cd' cd = iswin ? 'cd /d' : 'cd'
tot = VIM::evaluate('len(a:todo)') || 0 tot = VIM::evaluate('len(s:update.todo)') || 0
bar = '' bar = ''
skip = 'Already installed' skip = 'Already installed'
mtx = Mutex.new mtx = Mutex.new
@@ -797,7 +1009,7 @@ function! s:update_parallel(pull, todo, threads)
b = case type b = case type
when :install then '+' when :update then '*' when :install then '+' when :update then '*'
when true, nil then '-' else when true, nil then '-' else
VIM::command("call add(s:prev_update.errors, '#{name}')") VIM::command("call add(s:update.errors, '#{name}')")
'x' 'x'
end end
result = result =
@@ -886,7 +1098,7 @@ function! s:update_parallel(pull, todo, threads)
} if VIM::evaluate('s:mac_gui') == 1 } if VIM::evaluate('s:mac_gui') == 1
progress = iswin ? '' : '--progress' progress = iswin ? '' : '--progress'
[all.length, nthr].min.times do nthr.times do
mtx.synchronize do mtx.synchronize do
threads << Thread.new { threads << Thread.new {
while pair = take1.call while pair = take1.call
@@ -921,11 +1133,11 @@ function! s:update_parallel(pull, todo, threads)
else else
d = esc dir.sub(%r{[\\/]+$}, '') d = esc dir.sub(%r{[\\/]+$}, '')
log.call name, 'Installing ...', :install log.call name, 'Installing ...', :install
bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install, proc { bt.call "git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1", name, :install, proc {
FileUtils.rm_rf dir FileUtils.rm_rf dir
} }
end end
mtx.synchronize { VIM::command("let s:prev_update.new['#{name}'] = 1") } if !exists && ok mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
log.call name, result, ok log.call name, result, ok
end end
} if running } if running
@@ -943,7 +1155,7 @@ function! s:shellesc(arg)
endfunction endfunction
function! s:glob_dir(path) function! s:glob_dir(path)
return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)') return map(filter(s:lines(globpath(a:path, '**')), 'isdirectory(v:val)'), 's:dirpath(v:val)')
endfunction endfunction
function! s:progress_bar(line, bar, total) function! s:progress_bar(line, bar, total)
@@ -956,30 +1168,34 @@ function! s:compare_git_uri(a, b)
return a ==# b return a ==# b
endfunction endfunction
function! s:format_message(ok, name, message) function! s:format_message(bullet, name, message)
if a:ok if a:bullet != 'x'
return [printf('- %s: %s', a:name, s:lastline(a:message))] return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))]
else else
let lines = map(split(a:message, '\n'), '" ".v:val') let lines = map(s:lines(a:message), '" ".v:val')
return extend([printf('x %s:', a:name)], lines) return extend([printf('x %s:', a:name)], lines)
endif endif
endfunction endfunction
function! s:system(cmd) function! s:with_cd(cmd, dir)
return system(s:is_win ? '('.a:cmd.')' : a:cmd) return 'cd '.s:esc(a:dir).' && '.a:cmd
endfunction endfunction
function! s:system_chomp(str) function! s:system(cmd, ...)
let ret = s:system(a:str) let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd
return system(s:is_win ? '('.cmd.')' : cmd)
endfunction
function! s:system_chomp(...)
let ret = call('s:system', a:000)
return v:shell_error ? '' : substitute(ret, '\n$', '', '') return v:shell_error ? '' : substitute(ret, '\n$', '', '')
endfunction endfunction
function! s:git_valid(spec, check_branch, cd) function! s:git_valid(spec, check_branch)
let ret = 1 let ret = 1
let msg = 'OK' let msg = 'OK'
if isdirectory(a:spec.dir) if isdirectory(a:spec.dir)
if a:cd | execute 'cd' s:esc(a:spec.dir) | endif let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url', a:spec.dir))
let result = split(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url'), '\n')
let remote = result[-1] let remote = result[-1]
if v:shell_error if v:shell_error
let msg = join([remote, 'PlugClean required.'], "\n") let msg = join([remote, 'PlugClean required.'], "\n")
@@ -992,7 +1208,7 @@ function! s:git_valid(spec, check_branch, cd)
elseif a:check_branch elseif a:check_branch
let branch = result[0] let branch = result[0]
if a:spec.branch !=# branch if a:spec.branch !=# branch
let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1') let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
if a:spec.branch !=# tag if a:spec.branch !=# tag
let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.', let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
\ (empty(tag) ? branch : tag), a:spec.branch) \ (empty(tag) ? branch : tag), a:spec.branch)
@@ -1000,7 +1216,6 @@ function! s:git_valid(spec, check_branch, cd)
endif endif
endif endif
endif endif
if a:cd | cd - | endif
else else
let msg = 'Not found' let msg = 'Not found'
let ret = 0 let ret = 0
@@ -1015,10 +1230,9 @@ function! s:clean(force)
" List of valid directories " List of valid directories
let dirs = [] let dirs = []
let managed = filter(copy(g:plugs), 's:is_managed(v:key)') let [cnt, total] = [0, len(g:plugs)]
let [cnt, total] = [0, len(managed)] for [name, spec] in items(g:plugs)
for spec in values(managed) if !s:is_managed(name) || s:git_valid(spec, 0)[0]
if s:git_valid(spec, 0, 1)[0]
call add(dirs, spec.dir) call add(dirs, spec.dir)
endif endif
let cnt += 1 let cnt += 1
@@ -1052,7 +1266,7 @@ function! s:clean(force)
call append(line('$'), 'Already clean.') call append(line('$'), 'Already clean.')
else else
call inputsave() call inputsave()
let yes = a:force || (input('Proceed? (Y/N) ') =~? '^y') let yes = a:force || (input('Proceed? (y/N) ') =~? '^y')
call inputrestore() call inputrestore()
if yes if yes
for dir in todo for dir in todo
@@ -1070,21 +1284,16 @@ endfunction
function! s:upgrade() function! s:upgrade()
let new = s:me . '.new' let new = s:me . '.new'
echo 'Downloading '. s:plug_source echo 'Downloading '. s:plug_src
redraw redraw
try try
if executable('curl') if executable('curl')
let output = system(printf('curl -fLo %s %s', s:shellesc(new), s:plug_source)) let output = system(printf('curl -fLo %s %s', s:shellesc(new), s:plug_src))
if v:shell_error if v:shell_error
throw get(split(output, '\n'), -1, v:shell_error) throw get(s:lines(output), -1, v:shell_error)
endif endif
elseif has('ruby') elseif has('ruby')
ruby << EOF call s:upgrade_using_ruby(new)
require 'open-uri'
File.open(VIM::evaluate('new'), 'w') do |f|
f << open(VIM::evaluate('s:plug_source')).read
end
EOF
else else
return s:err('curl executable or ruby support not found') return s:err('curl executable or ruby support not found')
endif endif
@@ -1105,6 +1314,15 @@ EOF
endif endif
endfunction endfunction
function! s:upgrade_using_ruby(new)
ruby << EOF
require 'open-uri'
File.open(VIM::evaluate('a:new'), 'w') do |f|
f << open(VIM::evaluate('s:plug_src')).read
end
EOF
endfunction
function! s:upgrade_specs() function! s:upgrade_specs()
for spec in values(g:plugs) for spec in values(g:plugs)
let spec.frozen = get(spec, 'frozen', 0) let spec.frozen = get(spec, 'frozen', 0)
@@ -1122,7 +1340,7 @@ function! s:status()
for [name, spec] in items(g:plugs) for [name, spec] in items(g:plugs)
if has_key(spec, 'uri') if has_key(spec, 'uri')
if isdirectory(spec.dir) if isdirectory(spec.dir)
let [valid, msg] = s:git_valid(spec, 1, 1) let [valid, msg] = s:git_valid(spec, 1)
else else
let [valid, msg] = [0, 'Not found. Try PlugInstall.'] let [valid, msg] = [0, 'Not found. Try PlugInstall.']
endif endif
@@ -1141,7 +1359,7 @@ function! s:status()
let msg .= ' (not loaded)' let msg .= ' (not loaded)'
endif endif
call s:progress_bar(2, repeat('=', cnt), total) call s:progress_bar(2, repeat('=', cnt), total)
call append(3, s:format_message(valid, name, msg)) call append(3, s:format_message(valid ? '-' : 'x', name, msg))
normal! 2G normal! 2G
redraw redraw
endfor endfor
@@ -1194,7 +1412,7 @@ function! s:find_name(lnum)
if empty(line) if empty(line)
return '' return ''
endif endif
let name = matchstr(line, '\(^- \)\@<=[^:]\+') let name = s:extract_name(line, '-', '')
if !empty(name) if !empty(name)
return name return name
endif endif
@@ -1220,15 +1438,13 @@ function! s:preview_commit()
execute 'pedit' sha execute 'pedit' sha
wincmd P wincmd P
setlocal filetype=git buftype=nofile nobuflisted setlocal filetype=git buftype=nofile nobuflisted
execute 'cd' s:esc(g:plugs[name].dir) execute 'silent read !cd' s:esc(g:plugs[name].dir) '&& git show' sha
execute 'silent read !git show' sha
cd -
normal! gg"_dd normal! gg"_dd
wincmd p wincmd p
endfunction endfunction
function! s:section(flags) function! s:section(flags)
call search('\(^- \)\@<=.', a:flags) call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
endfunction endfunction
function! s:diff() function! s:diff()
@@ -1243,17 +1459,15 @@ function! s:diff()
continue continue
endif endif
execute 'cd' s:esc(v.dir) let diff = s:system_chomp('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"', v.dir)
let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD...HEAD@{1}"') if !empty(diff)
if !v:shell_error && !empty(diff)
call append(1, '') call append(1, '')
call append(2, '- '.k.':') call append(2, '- '.k.':')
call append(3, map(split(diff, '\n'), '" ". v:val')) call append(3, map(s:lines(diff), '" ". v:val'))
let cnt += 1 let cnt += 1
normal! gg normal! gg
redraw redraw
endif endif
cd -
endfor endfor
call setline(1, cnt == 0 ? 'No updates.' : 'Last update:') call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
@@ -1269,19 +1483,55 @@ endfunction
function! s:revert() function! s:revert()
let name = s:find_name(line('.')) let name = s:find_name(line('.'))
if empty(name) || !has_key(g:plugs, name) || if empty(name) || !has_key(g:plugs, name) ||
\ input(printf('Revert the update of %s? (Y/N) ', name)) !~? '^y' \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
return return
endif endif
execute 'cd' s:esc(g:plugs[name].dir) call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch), g:plugs[name].dir)
call system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch))
cd -
setlocal modifiable setlocal modifiable
normal! "_dap normal! "_dap
setlocal nomodifiable setlocal nomodifiable
echo 'Reverted.' echo 'Reverted.'
endfunction endfunction
function! s:snapshot(...) abort
let home = get(s:, 'plug_home_org', g:plug_home)
let [type, var, header] = s:is_win ?
\ ['dosbatch', '%PLUG_HOME%',
\ ['@echo off', ':: Generated by vim-plug', ':: '.strftime("%c"), '',
\ ':: Make sure to PlugUpdate first', '', 'set PLUG_HOME='.s:esc(home)]] :
\ ['sh', '$PLUG_HOME',
\ ['#!/bin/bash', '# Generated by vim-plug', '# '.strftime("%c"), '',
\ 'vim +PlugUpdate +qa', '', 'PLUG_HOME='.s:esc(home)]]
call s:prepare()
execute 'setf' type
call append(0, header)
call append('$', '')
1
redraw
let dirs = sort(map(values(filter(copy(g:plugs),
\'has_key(v:val, "uri") && isdirectory(v:val.dir)')), 'v:val.dir'))
let anchor = line('$') - 1
for dir in reverse(dirs)
let sha = s:system_chomp('git rev-parse --short HEAD', dir)
if !empty(sha)
call append(anchor, printf('cd %s && git reset --hard %s',
\ substitute(dir, '^'.g:plug_home, var, ''), sha))
redraw
endif
endfor
if a:0 > 0
let fn = s:esc(expand(a:1))
call writefile(getline(1, '$'), fn)
if !s:is_win | call system('chmod +x ' . fn) | endif
echo 'Saved to '.a:1
silent execute 'e' fn
endif
endfunction
function! s:split_rtp() function! s:split_rtp()
return split(&rtp, '\\\@<!,') return split(&rtp, '\\\@<!,')
endfunction endfunction

5
test/fixtures/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
fzf*
xxx
yyy
z1
z2

View File

@@ -0,0 +1 @@
echomsg 'ftplugin'

View File

@@ -5,18 +5,20 @@ cd ..
PLUG_SRC=$(pwd)/plug.vim PLUG_SRC=$(pwd)/plug.vim
cd - > /dev/null cd - > /dev/null
export PLUG_FIXTURES=$(pwd)/fixtures
if [ ! -d vader.vim ]; then if [ ! -d vader.vim ]; then
git clone https://github.com/junegunn/vader.vim.git git clone https://github.com/junegunn/vader.vim.git
fi fi
rm -rf fzf rm -rf $PLUG_FIXTURES/fzf
if [ ! -d fzf-staged ]; then if [ ! -d fzf-staged ]; then
git clone https://github.com/junegunn/fzf.git fzf-staged git clone https://github.com/junegunn/fzf.git $PLUG_FIXTURES/fzf-staged
fi fi
make_dirs() { make_dirs() {
mkdir -p "$1" mkdir -p "$PLUG_FIXTURES/$1"
cd "$1" cd "$PLUG_FIXTURES/$1"
mkdir -p autoload colors ftdetect ftplugin indent plugin syntax mkdir -p autoload colors ftdetect ftplugin indent plugin syntax
for d in *; do for d in *; do
[ -d $d ] || continue [ -d $d ] || continue
@@ -32,18 +34,22 @@ EOF
cd - > /dev/null cd - > /dev/null
} }
make_dirs xxx/ xxx init() {
make_dirs xxx/after xxx rm -rf $PLUG_FIXTURES/{xxx,yyy,z1,z2}
mkdir -p xxx/doc
cat > xxx/doc/xxx.txt << DOC make_dirs xxx/ xxx
make_dirs xxx/after xxx
mkdir -p $PLUG_FIXTURES/xxx/doc
cat > $PLUG_FIXTURES/xxx/doc/xxx.txt << DOC
hello *xxx* hello *xxx*
DOC DOC
make_dirs yyy/ yyy make_dirs yyy/ yyy
make_dirs yyy/after yyy make_dirs yyy/after yyy
make_dirs z1/ z1 make_dirs z1/ z1
make_dirs z2/ z2 make_dirs z2/ z2
}
cat > /tmp/mini-vimrc << VIMRC cat > /tmp/mini-vimrc << VIMRC
set rtp+=vader.vim set rtp+=vader.vim
@@ -51,8 +57,11 @@ set shell=/bin/bash
source $PLUG_SRC source $PLUG_SRC
VIMRC VIMRC
[ -z "$TMPDIR" ] && export TMPDIR=/tmp/
init
if [ "$1" = '!' ]; then 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 &&
init &&
/usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'let g:plug_threads = 1 | 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 else
/usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'Vader workflow.vader' /usr/local/bin/vim -Nu /tmp/mini-vimrc -c 'Vader workflow.vader'

View File

@@ -304,7 +304,8 @@ Execute (Rollback recent updates, PlugUpdate, then PlugDiff):
" ]] motion " ]] motion
execute 'normal $]]' execute 'normal $]]'
AssertEqual lnum + 4, line('.') Assert index([lnum + 4, lnum + 5], line('.')) >= 0
" +5 for merge commit
AssertEqual 3, col('.') AssertEqual 3, col('.')
" [[ motion " [[ motion
@@ -337,7 +338,8 @@ Execute (Rollback recent updates, PlugUpdate, then PlugDiff):
AssertEqual 1, &previewwindow AssertEqual 1, &previewwindow
pclose pclose
Execute (Plug window in a new tab): Execute (Reuse Plug window in another tab):
let tabnr = tabpagenr()
PlugDiff PlugDiff
tab new new-tab tab new new-tab
set buftype=nofile set buftype=nofile
@@ -345,8 +347,8 @@ Execute (Plug window in a new tab):
normal D normal D
AssertExpect '^- ', 1 AssertExpect '^- ', 1
normal q normal q
AssertEqual 'new-tab', expand('%') AssertEqual tabnr, tabpagenr()
q normal! gt
q q
********************************************************************** **********************************************************************
@@ -420,7 +422,7 @@ Execute (On-demand loading based on filetypes):
********************************************************************** **********************************************************************
Execute (Add unmanaged plugin): Execute (Add unmanaged plugin):
let fzf = fnamemodify(g:vader_file, ':h') . '/fzf' let fzf = expand('$PLUG_FIXTURES/fzf')
Log fzf Log fzf
call plug#begin() call plug#begin()
@@ -450,6 +452,7 @@ Execute (Plug block for following tests):
call plug#end() call plug#end()
" Remove plugins from previous tests " Remove plugins from previous tests
PlugClean! PlugClean!
q
Execute (PlugInstall will only install vim-easy-align): Execute (PlugInstall will only install vim-easy-align):
PlugInstall PlugInstall
@@ -478,7 +481,7 @@ Execute (PlugStatus should point out that the plugin is missing):
Execute (Deploy unmanaged plugin): Execute (Deploy unmanaged plugin):
Assert !exists(':FZF'), ':FZF command should not exist' Assert !exists(':FZF'), ':FZF command should not exist'
call rename('fzf-staged', 'fzf') call rename(expand('$PLUG_FIXTURES/fzf-staged'), fzf)
Execute (PlugUpdate still should not care): Execute (PlugUpdate still should not care):
PlugUpdate PlugUpdate
@@ -516,12 +519,37 @@ Execute (Common parent):
********************************************************************** **********************************************************************
~ Frozen plugins ~ Frozen plugins
********************************************************************** **********************************************************************
- We've decided to install plugins that are frozen: see #113
Execute (Frozen plugin are not ~~installed nor~~ updated):
" Remove plugins
call plug#begin()
call plug#end()
PlugClean!
q
Execute (Frozen plugin are not installed nor updated): " vim-easy-align is not found, so it will be installed even though it's frozen
call plug#begin() call plug#begin()
Plug 'junegunn/vim-easy-align', { 'frozen': 1 } Plug 'junegunn/vim-easy-align', { 'frozen': 1 }
call plug#end() call plug#end()
PlugInstall
AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "vim-easy-align"'))
q
" Remove plugins again
call plug#begin()
call plug#end()
PlugClean!
q
" PlugUpdate will do the same
call plug#begin()
Plug 'junegunn/vim-easy-align', { 'frozen': 1 }
call plug#end()
PlugInstall
AssertEqual 1, len(filter(getline(1, '$'), 'v:val =~ "vim-easy-align"'))
q
" Since vim-easy-align already exists, PlugInstall or PlugUpdate will skip it
redir => out redir => out
silent PlugInstall silent PlugInstall
redir END redir END
@@ -593,6 +621,7 @@ Execute (Cleanup):
call plug#begin() call plug#begin()
call plug#end() call plug#end()
PlugClean! PlugClean!
q
Execute (On install): Execute (On install):
call plug#begin() call plug#begin()
@@ -731,12 +760,12 @@ Execute (Using custom dir):
********************************************************************** **********************************************************************
Before (Clear global vars): Before (Clear global vars):
let g:xxx = [] let g:xxx = []
set rtp-=$PWD/xxx/ set rtp-=$PLUG_FIXTURES/xxx/
set rtp-=$PWD/xxx/after set rtp-=$PLUG_FIXTURES/xxx/after
Execute (Immediate loading): Execute (Immediate loading):
call plug#begin() call plug#begin()
Plug '$PWD/xxx' Plug '$PLUG_FIXTURES/xxx'
call plug#end() call plug#end()
" FIXME: " FIXME:
@@ -750,7 +779,7 @@ Execute (Immediate loading):
Execute (Command-based on-demand loading): Execute (Command-based on-demand loading):
call plug#begin() call plug#begin()
Plug '$PWD/xxx', { 'on': 'XXX' } Plug '$PLUG_FIXTURES/xxx', { 'on': 'XXX' }
call plug#end() call plug#end()
AssertEqual [], g:xxx AssertEqual [], g:xxx
@@ -763,7 +792,7 @@ Execute (Command-based on-demand loading):
Execute (Filetype-based on-demand loading): Execute (Filetype-based on-demand loading):
call plug#begin() call plug#begin()
Plug '$PWD/xxx', { 'for': 'xxx' } Plug '$PLUG_FIXTURES/xxx', { 'for': 'xxx' }
call plug#end() call plug#end()
AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect'], g:xxx AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect'], g:xxx
@@ -778,10 +807,10 @@ Before:
********************************************************************** **********************************************************************
Execute (plug#helptags): Execute (plug#helptags):
silent! call delete(expand('$PWD/xxx/doc/tags')) silent! call delete(expand('$PLUG_FIXTURES/xxx/doc/tags'))
Assert !filereadable(expand('$PWD/xxx/doc/tags')) Assert !filereadable(expand('$PLUG_FIXTURES/xxx/doc/tags'))
AssertEqual 1, plug#helptags() AssertEqual 1, plug#helptags()
Assert filereadable(expand('$PWD/xxx/doc/tags')) Assert filereadable(expand('$PLUG_FIXTURES/xxx/doc/tags'))
********************************************************************** **********************************************************************
~ Manual loading ~ Manual loading
@@ -822,7 +851,7 @@ Execute (PlugStatus should not contain (not loaded)):
Execute (Load plugin from PlugStatus screen with L key in normal mode): Execute (Load plugin from PlugStatus screen with L key in normal mode):
call plug#begin() call plug#begin()
Plug '$PWD/yyy', { 'on': [] } Plug '$PLUG_FIXTURES/yyy', { 'on': [] }
call plug#end() call plug#end()
PlugStatus PlugStatus
@@ -836,8 +865,8 @@ Execute (Load plugin from PlugStatus screen with L key in normal mode):
Execute (Load plugin from PlugStatus screen with L key in visual mode): Execute (Load plugin from PlugStatus screen with L key in visual mode):
call plug#begin() call plug#begin()
Plug '$PWD/z1', { 'on': [] } Plug '$PLUG_FIXTURES/z1', { 'on': [] }
Plug '$PWD/z2', { 'for': [] } Plug '$PLUG_FIXTURES/z2', { 'for': [] }
call plug#end() call plug#end()
PlugStatus PlugStatus
@@ -963,8 +992,8 @@ Execute (Plug directory with comma):
Execute (Strict load order): Execute (Strict load order):
let g:total_order = [] let g:total_order = []
call plug#begin() call plug#begin()
Plug '$PWD/xxx' Plug '$PLUG_FIXTURES/xxx'
Plug '$PWD/yyy', { 'for': ['xxx'] } Plug '$PLUG_FIXTURES/yyy', { 'for': ['xxx'] }
call plug#end() call plug#end()
call EnsureLoaded() call EnsureLoaded()
setf xxx setf xxx
@@ -976,8 +1005,8 @@ Execute (Strict load order):
let g:total_order = [] let g:total_order = []
call plug#begin() call plug#begin()
Plug '$PWD/xxx', { 'for': ['xxx'] } Plug '$PLUG_FIXTURES/xxx', { 'for': ['xxx'] }
Plug '$PWD/yyy' Plug '$PLUG_FIXTURES/yyy'
call plug#end() call plug#end()
call EnsureLoaded() call EnsureLoaded()
set rtp^=manually-prepended set rtp^=manually-prepended
@@ -993,8 +1022,8 @@ Execute (Strict load order):
let g:total_order = [] let g:total_order = []
call plug#begin() call plug#begin()
Plug '$PWD/xxx', { 'for': ['xxx'] } Plug '$PLUG_FIXTURES/xxx', { 'for': ['xxx'] }
Plug '$PWD/yyy', { 'for': ['xxx'] } Plug '$PLUG_FIXTURES/yyy', { 'for': ['xxx'] }
call plug#end() call plug#end()
call EnsureLoaded() call EnsureLoaded()
setf xxx setf xxx
@@ -1004,9 +1033,126 @@ Execute (Strict load order):
Assert index(g:total_order, 'xxx/after/plugin') < index(g:total_order, 'yyy/after/plugin') Assert index(g:total_order, 'xxx/after/plugin') < index(g:total_order, 'yyy/after/plugin')
AssertEqual len + 2, len(split(&rtp, ',')) AssertEqual len + 2, len(split(&rtp, ','))
**********************************************************************
Execute (PlugClean should not try to remove unmanaged plugins inside g:plug_home):
call plug#begin('$PLUG_FIXTURES')
Plug '$PLUG_FIXTURES/ftplugin-msg', { 'for': [] }
Plug '$PLUG_FIXTURES/fzf'
Plug '$PLUG_FIXTURES/xxx'
Plug '$PLUG_FIXTURES/yyy'
call plug#end()
" Remove z1, z2
PlugClean!
AssertExpect '^- ', 2
AssertExpect 'Already clean', 0
PlugClean!
AssertExpect '^- ', 0
AssertExpect 'Already clean', 1
q
**********************************************************************
Execute (#112 On-demand loading should not suppress messages from ftplugin):
call plug#begin('$PLUG_FIXTURES')
Plug '$PLUG_FIXTURES/ftplugin-msg', { 'for': 'c' }
call plug#end()
redir => out
tabnew a.c
redir END
Assert stridx(out, 'ftplugin') >= 0
* The same applies to plug#load())
redir => out
call plug#load('ftplugin-msg')
redir END
Assert stridx(out, 'ftplugin') >= 0
q
**********************************************************************
Execute (PlugSnapshot):
call plug#begin('$TMPDIR/plugged')
Plug 'junegunn/vim-emoji'
Plug 'junegunn/seoul256.vim'
call plug#end()
PlugClean!
PlugInstall
PlugSnapshot
AssertEqual '#!/bin/bash', getline(1)
AssertEqual '# Generated by vim-plug', getline(2)
AssertEqual 'vim +PlugUpdate +qa', getline(5)
AssertEqual 'PLUG_HOME=$TMPDIR/plugged', getline(7)
AssertEqual 0, stridx(getline(9), 'cd $PLUG_HOME/seoul256.vim/ && git reset --hard')
AssertEqual 0, stridx(getline(10), 'cd $PLUG_HOME/vim-emoji/ && git reset --hard')
AssertEqual 'sh', &filetype
execute 'PlugSnapshot' g:plug_home.'/snapshot.sh'
AssertEqual 'sh', &filetype
AssertEqual 'snapshot.sh', fnamemodify(expand('%'), ':t')
q
**********************************************************************
Execute (#114 Should not contain empty path in &rtp):
call plug#begin('$TMPDIR/plugged')
call plug#end()
Log &rtp
Assert &rtp !~ ',,', 'Commas'
Assert &rtp !~ '^,', 'Comma prefix'
Assert &rtp !~ ',$', 'Comma suffix'
**********************************************************************
Execute (#130 Proper cleanup of on-demand loading triggers):
augroup PlugLOD
autocmd!
augroup END
" Cleared on command
call plug#begin('$TMPDIR/plugged')
Plug 'junegunn/vim-emoji', { 'on': ['EmojiCommand', 'EmojiCommand2', '<Plug>(EmojiMapping)'] }
call plug#end()
PlugInstall | q
Assert exists(':EmojiCommand'), 'EmojiCommand not defined'
Assert exists(':EmojiCommand2'), 'EmojiCommand2 not defined'
Assert !empty(mapcheck('<Plug>(EmojiMapping)')), '<Plug>(EmojiMapping) not defined'
silent! EmojiCommand
Assert !exists(':EmojiCommand'), 'EmojiCommand defined'
Assert !exists(':EmojiCommand2'), 'EmojiCommand2 defined'
Assert empty(mapcheck('<Plug>(EmojiMapping)')), '<Plug>(EmojiMapping) defined'
" Cleared on FileType
call plug#begin('$TMPDIR/plugged')
Plug 'junegunn/vim-emoji', { 'on': ['EmojiCommandExtra', '<Plug>(EmojiMappingExtra)'], 'for': ['emoji'] }
call plug#end()
Assert exists(':EmojiCommandExtra'), 'EmojiCommandExtra not defined'
Assert !empty(mapcheck('<Plug>(EmojiMappingExtra)')), '<Plug>(EmojiMappingExtra) not defined'
setf emoji
Assert !exists(':EmojiCommandExtra'), 'EmojiCommandExtra defined'
Assert empty(mapcheck('<Plug>(EmojiMappingExtra)')), '<Plug>(EmojiMappingExtra) defined'
**********************************************************************
Execute (#131 Syntax error):
call plug#begin('/no-permission')
Plug 'junegunn/vim-emoji'
call plug#end()
redir => out
silent PlugInstall
redir END
Assert out =~ 'Invalid plug directory: /no-permission'
Execute (Cleanup): Execute (Cleanup):
silent! call system('rm -rf '.temp_plugged) silent! call system('rm -rf '.temp_plugged)
silent! call rename('fzf', 'fzf-staged') silent! call system('rm -rf '.temp_plugged)
silent! call rename(fzf, expand('$PLUG_FIXTURES/fzf-staged'))
silent! unlet g:plugs silent! unlet g:plugs
silent! unlet g:plug_home silent! unlet g:plug_home
silent! unlet g:plug_url_format silent! unlet g:plug_url_format