Quellcode durchsuchen

Prototype implementation of dependency resolution (#2)

Junegunn Choi vor 12 Jahren
Ursprung
Commit
a9d5912b4d
2 geänderte Dateien mit 131 neuen und 50 gelöschten Zeilen
  1. 29 0
      README.md
  2. 102 50
      plug.vim

+ 29 - 0
README.md

@@ -14,6 +14,7 @@ Somewhere between [Pathogen](https://github.com/tpope/vim-pathogen) and
 - Parallel installation/update (requires
   [+ruby](http://junegunn.kr/2013/09/installing-vim-with-ruby-support/))
 - Smallest possible feature set
+- Dependency resolution using `Plugfile` (experimental)
 
 ### Cons.
 
@@ -64,6 +65,34 @@ plugins with `plug#begin(path)` call.
 
 (Default number of threads = `g:plug_threads` or 16)
 
+### Dependency resolution
+
+If a Vim plugin specifies its dependent plugins in `Plugfile` in its root
+directory, vim-plug will automatically source it recursively during the
+installation.
+
+A `Plugfile` should contain a set of `Plug` commands for the dependent plugins.
+
+I've created two dummy repositories with Plugfiles as an example to this scheme.
+
+- [junegunn/dummy1](https://github.com/junegunn/dummy1)
+  - `Plug 'junegunn/vim-scroll-position'`
+  - `Plug 'junegunn/dummy2'`
+- [junegunn/dummy2](https://github.com/junegunn/dummy2)
+  - `Plug 'junegunn/Zenburn'`
+  - `Plug 'junegunn/jellybeans.vim'`
+  - `Plug 'junegunn/dummy1'`
+    - (Circular dependencies are ignored)
+
+If you put `Plug 'junegunn/dummy1'` in your configuration file, and run
+`:PlugInstall`,
+
+1. vim-plug first installs dummy1
+2. And sees if the repository has Plugfile
+3. Plugfile is loaded and vim-plug discovers dependent plugins
+4. Dependent plugins are then installed as well, and their Plugfiles are
+   examined and their dependencies are resolved recursively.
+
 ### Articles
 
 - [Writing my own Vim plugin manager](http://junegunn.kr/2013/09/writing-my-own-vim-plugin-manager)

+ 102 - 50
plug.vim

@@ -51,6 +51,7 @@ endif
 let g:loaded_plug = 1
 
 let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
+let s:plug_file = 'Plugfile'
 let s:plug_win = 0
 let s:is_win = has('win32') || has('win64')
 let s:me = expand('<sfile>:p')
@@ -74,7 +75,7 @@ function! plug#begin(...)
   let g:plug_home = home
   let g:plugs = {}
 
-  command! -nargs=+ Plug        call s:add(<args>)
+  command! -nargs=+ Plug        call s:add(1, <args>)
   command! -nargs=* PlugInstall call s:install(<f-args>)
   command! -nargs=* PlugUpdate  call s:update(<f-args>)
   command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
@@ -83,6 +84,11 @@ function! plug#begin(...)
 endfunction
 
 function! plug#end()
+  let keys = keys(g:plugs)
+  while !empty(keys)
+    let keys = keys(s:extend(keys))
+  endwhile
+
   set nocompatible
   filetype off
   for plug in values(g:plugs)
@@ -97,9 +103,10 @@ function! plug#end()
 endfunction
 
 function! s:add(...)
-  if a:0 == 1
-    let [plugin, branch] = [a:1, 'master']
-  elseif a:0 == 2
+  let force = a:1
+  if a:0 == 2
+    let [plugin, branch] = [a:2, 'master']
+  elseif a:0 == 3
     let [plugin, branch] = a:000
   else
     echoerr "Invalid number of arguments (1..2)"
@@ -116,6 +123,8 @@ function! s:add(...)
   endif
 
   let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
+  if !force && has_key(g:plugs, name) | return | endif
+
   let dir  = fnamemodify(join([g:plug_home, name], '/'), ':p')
 
   let spec = { 'dir': dir, 'uri': uri, 'branch': branch }
@@ -229,47 +238,79 @@ function! s:update_impl(pull, args)
   call s:finish()
 endfunction
 
-function! s:update_serial(pull)
-  let st = reltime()
-  let base = g:plug_home
-  let cnt = 0
-  let total = len(g:plugs)
+function! s:extend(names)
+  let prev = copy(g:plugs)
+  try
+    command! -nargs=+ Plug call s:add(0, <args>)
+    for name in a:names
+      let spec = g:plugs[name]
+      let plugfile = spec.dir . '/'. s:plug_file
+      if filereadable(plugfile)
+        execute "source ". plugfile
+      endif
+    endfor
+  finally
+    command! -nargs=+ Plug call s:add(1, <args>)
+  endtry
+  return filter(copy(g:plugs), '!has_key(prev, v:key)')
+endfunction
 
-  for [name, spec] in items(g:plugs)
-    let cnt += 1
-    let d = shellescape(spec.dir)
-    if isdirectory(spec.dir)
-      execute 'cd '.spec.dir
-      if s:git_valid(spec, 0)
-        let result = a:pull ?
-          \ s:system(
-          \ printf('git checkout -q %s && git pull origin %s 2>&1',
-          \   spec.branch, spec.branch)) : 'Already installed'
-        let error = a:pull ? v:shell_error != 0 : 0
+function! s:update_progress(cnt, total)
+  call setline(1, "Updating plugins (".a:cnt."/".a:total.")")
+  call s:progress_bar(2, a:cnt, a:total)
+  normal! 2G
+  redraw
+endfunction
+
+function! s:update_serial(pull)
+  let st    = reltime()
+  let base  = g:plug_home
+  let todo  = copy(g:plugs)
+  let total = len(todo)
+  let done  = {}
+
+  while !empty(todo)
+    for [name, spec] in items(todo)
+      let done[name] = 1
+      let d = shellescape(spec.dir)
+      if isdirectory(spec.dir)
+        execute 'cd '.spec.dir
+        if s:git_valid(spec, 0)
+          let result = a:pull ?
+            \ s:system(
+            \ printf('git checkout -q %s && git pull origin %s 2>&1',
+            \   spec.branch, spec.branch)) : 'Already installed'
+          let error = a:pull ? v:shell_error != 0 : 0
+        else
+          let result = "PlugClean required. Invalid remote."
+          let error = 1
+        endif
       else
-        let result = "PlugClean required. Invalid remote."
-        let error = 1
+        if !isdirectory(base)
+          call mkdir(base, 'p')
+        endif
+        execute 'cd '.base
+        let result = s:system(
+              \ printf('git clone --recursive %s -b %s %s 2>&1',
+              \ shellescape(spec.uri), shellescape(spec.branch), d))
+        let error = v:shell_error != 0
       endif
-    else
-      if !isdirectory(base)
-        call mkdir(base, 'p')
+      cd -
+      if error
+        let result = '(x) ' . result
       endif
-      execute 'cd '.base
-      let result = s:system(
-            \ printf('git clone --recursive %s -b %s %s 2>&1',
-            \ shellescape(spec.uri), shellescape(spec.branch), d))
-      let error = v:shell_error != 0
-    endif
-    cd -
-    if error
-      let result = '(x) ' . result
+      call append(3, '- ' . name . ': ' . result)
+      call s:update_progress(len(done), total)
+    endfor
+
+    if !empty(s:extend(keys(todo)))
+      let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
+      let total += len(todo)
+      call s:update_progress(len(done), total)
+    else
+      break
     endif
-    call setline(1, "Updating plugins (".cnt."/".total.")")
-    call s:progress_bar(2, cnt, total)
-    call append(3, '- ' . name . ': ' . result)
-    normal! 2G
-    redraw
-  endfor
+  endwhile
 
   call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
 endfunction
@@ -282,22 +323,32 @@ function! s:update_parallel(pull, threads)
   cd    = VIM::evaluate('s:is_win').to_i == 1 ? 'cd /d' : 'cd'
   pull  = VIM::evaluate('a:pull').to_i == 1
   base  = VIM::evaluate('g:plug_home')
-  all   = VIM::evaluate('g:plugs')
-  total = all.length
-  cnt   = 0
+  all   = VIM::evaluate('copy(g:plugs)')
+  done  = {}
   skip  = 'Already installed'
   mtx   = Mutex.new
   take1 = proc { mtx.synchronize { all.shift } }
-  log   = proc { |name, result, ok|
-    mtx.synchronize {
+  logh  = proc {
+    cnt, tot = done.length, VIM::evaluate('len(g:plugs)')
+    $curbuf[1] = "Updating plugins (#{cnt}/#{tot})"
+    $curbuf[2] = '[' + ('=' * cnt).ljust(tot) + ']'
+    VIM::command('normal! 2G')
+    VIM::command('redraw')
+  }
+  log = proc { |name, result, ok|
+    mtx.synchronize do
+      done[name] = true
       result = '(x) ' + result unless ok
       result = "- #{name}: #{result}"
-      $curbuf[1] = "Updating plugins (#{cnt += 1}/#{total})"
-      $curbuf[2] = '[' + ('=' * cnt).ljust(total) + ']'
       $curbuf.append 3, result
-      VIM::command('normal! 2G')
-      VIM::command('redraw')
-    }
+      logh.call
+    end
+  }
+  refill = proc { |name|
+    mtx.synchronize do
+      all.merge! VIM::evaluate("s:extend(['#{name}'])")
+      logh.call
+    end
   }
   VIM::evaluate('a:threads').to_i.times.map { |i|
     Thread.new(i) do |ii|
@@ -323,6 +374,7 @@ function! s:update_parallel(pull, threads)
           end
         result = result.lines.to_a.last.strip
         log.call name, result, ok
+        refill.call name
       end
     end
   }.each(&:join)