Răsfoiți Sursa

Improve &rtp management (#85)

- Respect the order of `Plug` commands even when some plugins are loaded
  on demand
- Correct the order of `after` directories added to `&rtp`
Junegunn Choi 11 ani în urmă
părinte
comite
cac2f9f439
3 a modificat fișierele cu 155 adăugiri și 55 ștergeri
  1. 73 42
      plug.vim
  2. 8 2
      test/run
  3. 74 11
      test/workflow.vader

+ 73 - 42
plug.vim

@@ -142,17 +142,10 @@ function! plug#end()
   let lod = {}
 
   filetype off
-  " we want to make sure the plugin directories are added to rtp in the same
-  " order that they are registered with the Plug command. since the s:add_rtp
-  " function uses ^= to add plugin directories to the front of the rtp, we
-  " need to loop through the plugins in reverse
-  for name in reverse(copy(g:plugs_order))
+  for name in g:plugs_order
     let plug = g:plugs[name]
-    if get(s:loaded, plug.dir, 0) || !has_key(plug, 'on') && !has_key(plug, 'for')
-      let rtp = s:add_rtp(plug)
-      if reload
-        call s:source(rtp, 'plugin/**/*.vim', 'after/plugin/**/*.vim')
-      endif
+    if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(plug, 'for')
+      let s:loaded[name] = 1
       continue
     endif
 
@@ -192,14 +185,28 @@ function! plug#end()
   for [key, names] in items(lod)
     augroup PlugLOD
       execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
-            \ key, string(key), string(reverse(names)))
+            \ key, string(key), string(names))
     augroup END
   endfor
+
+  if reload
+    call s:reload()
+  endif
   call s:reorg_rtp()
   filetype plugin indent on
   syntax enable
 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
@@ -246,27 +253,46 @@ function! s:err(msg)
 endfunction
 
 function! s:esc(path)
-  return substitute(a:path, ' ', '\\ ', 'g')
+  return escape(a:path, ' ')
 endfunction
 
-function! s:add_rtp(plug)
-  let rtp = s:rtp(a:plug)
-  execute 'set rtp^='.s:esc(rtp)
-  let after = globpath(rtp, 'after')
-  if isdirectory(after)
-    execute 'set rtp+='.s:esc(after)
-  endif
-  let s:loaded[a:plug.dir] = 1
-  return rtp
+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:first_rtp
-  endif
-  if s:last_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)')
+  let &rtp     = join(map(rtps, 's:escrtp(v:val)'), ',')
+                 \ . substitute(','.s:middle.',', ',,', ',', '')
+                 \ . join(map(afters, 's:escrtp(v:val)'), ',')
+  let s:prtp   = &rtp
+
+  if !empty(s:first_rtp)
+    execute 'set rtp^='.s:first_rtp
     execute 'set rtp+='.s:last_rtp
   endif
 endfunction
@@ -284,25 +310,28 @@ function! plug#load(...)
     return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
   end
   for name in a:000
-    call s:lod(g:plugs[name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+    call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
   endfor
-  call s:reorg_rtp()
   silent! doautocmd BufRead
   return 1
 endfunction
 
-function! s:lod(plug, types)
-  let rtp = s:add_rtp(a:plug)
-  for dir in a:types
-    call s:source(rtp, dir.'/**/*.vim')
+function! s:lod(names, types)
+  for name in a:names
+    let s:loaded[name] = 1
   endfor
-endfunction
+  call s:reorg_rtp()
 
-function! s:lod_ft(pat, names)
   for name in a:names
-    call s:lod(g:plugs[name], ['plugin', 'after/plugin'])
+    let rtp = s:rtp(g:plugs[name])
+    for dir in a:types
+      call s:source(rtp, dir.'/**/*.vim')
+    endfor
   endfor
-  call s:reorg_rtp()
+endfunction
+
+function! s:lod_ft(pat, names)
+  call s:lod(a:names, ['plugin', 'after/plugin'])
   execute 'autocmd! PlugLOD FileType' a:pat
   silent! doautocmd filetypeplugin FileType
   silent! doautocmd filetypeindent FileType
@@ -310,16 +339,14 @@ endfunction
 
 function! s:lod_cmd(cmd, bang, l1, l2, args, name)
   execute 'delc' a:cmd
-  call s:lod(g:plugs[a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
-  call s:reorg_rtp()
+  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)
 endfunction
 
 function! s:lod_map(map, name, prefix)
   execute 'unmap' a:map
   execute 'iunmap' a:map
-  call s:lod(g:plugs[a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
-  call s:reorg_rtp()
+  call s:lod([a:name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
   let extra = ''
   while 1
     let c = getchar(0)
@@ -343,7 +370,7 @@ function! s:add(repo, ...)
                     \ a:0 == 1 ? s:parse_options(a:1) : s:base_spec)
     let g:plugs[name] = spec
     let g:plugs_order += [name]
-    let s:loaded[spec.dir] = 0
+    let s:loaded[name] = 0
   catch
     return s:err(v:exception)
   endtry
@@ -1109,7 +1136,7 @@ function! s:status()
     let cnt += 1
     let ecnt += !valid
     " `s:loaded` entry can be missing if PlugUpgraded
-    if valid && get(s:loaded, spec.dir, -1) == 0
+    if valid && get(s:loaded, name, -1) == 0
       let unloaded = 1
       let msg .= ' (not loaded)'
     endif
@@ -1255,8 +1282,12 @@ function! s:revert()
   echo 'Reverted.'
 endfunction
 
-let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
-let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
+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))

+ 8 - 2
test/run

@@ -19,10 +19,14 @@ make_dirs() {
   cd "$1"
   mkdir -p autoload colors ftdetect ftplugin indent plugin syntax
   for d in *; do
+    [ -d $d ] || continue
     cat > $d/xxx.vim << EOF
     " echom expand('<sfile>')
+    let g:total_order = get(g:, 'total_order', [])
     let g:$2 = get(g:, '$2', [])
-    call add(g:$2, '${1:4}/$d')
+    let s:name = join(filter(['$2', '${1:4}', '$d'], '!empty(v:val)'), '/')
+    call add(g:$2, s:name)
+    call add(g:total_order, s:name)
 EOF
   done
   cd - > /dev/null
@@ -30,12 +34,14 @@ EOF
 
 make_dirs xxx/ xxx
 make_dirs xxx/after xxx
-mkdir xxx/doc
+mkdir -p xxx/doc
 cat > xxx/doc/xxx.txt << DOC
 hello *xxx*
 DOC
 
 make_dirs yyy/ yyy
+make_dirs yyy/after yyy
+
 make_dirs z1/ z1
 make_dirs z2/ z2
 

+ 74 - 11
test/workflow.vader

@@ -32,6 +32,12 @@ Execute (Initialize test environment):
   endfunction
   command! -nargs=+ -bang AssertExpect call AssertExpect('<bang>' == '!', <args>)
 
+  function! EnsureLoaded()
+    if has('vim_starting')
+      runtime! plugin/**/*.vim
+    endif
+  endfunction
+
 Execute (Print Ruby version):
   redir => out
   silent ruby puts RUBY_VERSION
@@ -388,10 +394,7 @@ Given (Unaligned code):
   aa=2
 
 Execute (Check installed plugins):
-  if has('vim_starting')
-    Log 'Vader is run from commandline'
-    runtime! plugin/**/*.vim
-  endif
+  call EnsureLoaded()
   Assert exists(':FNR'),           'FNR command should be found'
   Assert !exists(':RedisExecute'), 'RedisExecute command still should not be found'
 
@@ -740,9 +743,9 @@ Execute (Immediate loading):
   " Different result when Vader is run from commandline with `-c` option
   Log g:xxx
   if has('vim_starting')
-    AssertEqual ['/ftdetect', 'after/ftdetect'], g:xxx
+    AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect'], g:xxx
   else
-    AssertEqual ['/plugin', 'after/plugin', '/ftdetect', 'after/ftdetect'], g:xxx
+    AssertEqual ['xxx/plugin', 'xxx/after/plugin', 'xxx/ftdetect', 'xxx/after/ftdetect'], g:xxx
   endif
 
 Execute (Command-based on-demand loading):
@@ -753,20 +756,20 @@ Execute (Command-based on-demand loading):
   AssertEqual [], g:xxx
 
   silent! XXX
-  AssertEqual ['/ftdetect', 'after/ftdetect', '/plugin', 'after/plugin'], g:xxx
+  AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect', 'xxx/plugin', 'xxx/after/plugin'], g:xxx
 
   setf xxx
-  AssertEqual ['/ftdetect', 'after/ftdetect', '/plugin', 'after/plugin', '/ftplugin', 'after/ftplugin', '/indent', 'after/indent', '/syntax', 'after/syntax'], g:xxx
+  AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect', 'xxx/plugin', 'xxx/after/plugin', 'xxx/ftplugin', 'xxx/after/ftplugin', 'xxx/indent', 'xxx/after/indent', 'xxx/syntax', 'xxx/after/syntax'], g:xxx
 
 Execute (Filetype-based on-demand loading):
   call plug#begin()
   Plug '$PWD/xxx', { 'for': 'xxx' }
   call plug#end()
 
-  AssertEqual ['/ftdetect', 'after/ftdetect'], g:xxx
+  AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect'], g:xxx
 
   setf xxx
-  AssertEqual ['/ftdetect', 'after/ftdetect', '/plugin', 'after/plugin', '/ftplugin', 'after/ftplugin', '/indent', 'after/indent', '/syntax', 'after/syntax'], g:xxx
+  AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect', 'xxx/plugin', 'xxx/after/plugin', 'xxx/ftplugin', 'xxx/after/ftplugin', 'xxx/indent', 'xxx/after/indent', 'xxx/syntax', 'xxx/after/syntax'], g:xxx
 
 Before:
 
@@ -937,21 +940,81 @@ Execute (Update plugins with U key in visual mode):
   AssertExpect! '[==]', 1
   q
 
+**********************************************************************
 Execute (plug#begin should expand env vars):
   AssertNotEqual '$HOME/.emacs/plugged', expand('$HOME/.emacs/plugged')
   call plug#begin('$HOME/.emacs/plugged')
   AssertEqual expand('$HOME/.emacs/plugged'), g:plug_home
 
+**********************************************************************
+Execute (Plug directory with comma):
+  call plug#begin(temp_plugged . '/p,l,u,g,g,e,d')
+  Plug 'junegunn/vim-emoji'
+  call plug#end()
+  Log &rtp
+
+  PlugInstall
+  q
+  let found = filter(split(globpath(&rtp, 'README.md'), '\n'), 'v:val =~ ","')
+  Log found
+  AssertEqual 1, len(found)
+
+**********************************************************************
+Execute (Strict load order):
+  let g:total_order = []
+  call plug#begin()
+  Plug '$PWD/xxx'
+  Plug '$PWD/yyy', { 'for': ['xxx'] }
+  call plug#end()
+  call EnsureLoaded()
+  setf xxx
+  Log 'Case 1: ' . &rtp
+  AssertEqual ['yyy/ftdetect', 'yyy/after/ftdetect', 'xxx/ftdetect', 'xxx/after/ftdetect'], g:total_order[0:3]
+  Assert index(g:total_order, 'xxx/plugin') < index(g:total_order, 'yyy/plugin')
+  Assert index(g:total_order, 'xxx/after/plugin') < index(g:total_order, 'yyy/after/plugin')
+  let len = len(split(&rtp, ','))
+
+  let g:total_order = []
+  call plug#begin()
+  Plug '$PWD/xxx', { 'for': ['xxx'] }
+  Plug '$PWD/yyy'
+  call plug#end()
+  call EnsureLoaded()
+  set rtp^=manually-prepended
+  set rtp+=manually-appended
+  setf xxx
+  Log 'Case 2: ' . &rtp
+  AssertEqual 'manually-prepended', split(&rtp, ',')[3]
+  AssertEqual 'manually-appended',  split(&rtp, ',')[-4]
+  AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect', 'yyy/ftdetect', 'yyy/after/ftdetect'], g:total_order[0:3]
+  Assert index(g:total_order, 'yyy/plugin') < index(g:total_order, 'xxx/plugin')
+  Assert index(g:total_order, 'yyy/after/plugin') < index(g:total_order, 'xxx/after/plugin')
+  AssertEqual len + 2, len(split(&rtp, ','))
+
+  let g:total_order = []
+  call plug#begin()
+  Plug '$PWD/xxx', { 'for': ['xxx'] }
+  Plug '$PWD/yyy', { 'for': ['xxx'] }
+  call plug#end()
+  call EnsureLoaded()
+  setf xxx
+  Log 'Case 3: ' . &rtp
+  AssertEqual ['xxx/ftdetect', 'xxx/after/ftdetect', 'yyy/ftdetect', 'yyy/after/ftdetect'], g:total_order[0:3]
+  Assert index(g:total_order, 'xxx/plugin') < index(g:total_order, 'yyy/plugin')
+  Assert index(g:total_order, 'xxx/after/plugin') < index(g:total_order, 'yyy/after/plugin')
+  AssertEqual len + 2, len(split(&rtp, ','))
+
 Execute (Cleanup):
   silent! call system('rm -rf '.temp_plugged)
   silent! call rename('fzf', 'fzf-staged')
   silent! unlet g:plugs
   silent! unlet g:plug_home
   silent! unlet g:plug_url_format
-  silent! unlet temp_plugged vader plug basertp save_rtp repo lnum fzf out tabnr
+  silent! unlet temp_plugged vader plug basertp save_rtp repo lnum fzf out tabnr found len
   silent! delf PlugStatusSorted
   silent! delf AssertExpect
   silent! delf PlugUpdated
+  silent! delf EnsureLoaded
   silent! delc AssertExpect
   silent! unmap /
   silent! unmap ?