@ -1,17 +1,35 @@
" ---------------------------------------------------------------------------
" Try to clone all new bundles given (or all bundles in g:bundles by default)
" to g:bundle_dir. If a:bang is 1 it will also update all plugins (git pull).
"
" bang -- 1 or 0
" ... -- any number of bundle specifications (separate arguments)
" ---------------------------------------------------------------------------
func ! vundle #installer #new ( bang , ...) abort
let bundles = ( a :1 = = '' ) ?
\ g :bundles :
\ map ( copy ( a :000 ) , 'vundle#config#bundle(v:val, {})' )
let names = vundle #scripts #bundle_names ( map ( copy ( bundles ) , 'v:val.name_spec' ) )
call vundle #scripts #view ( 'Installer' , ['" Installing bundles to ' .expand ( g :bundle_dir , 1 ) ], names + ['Helptags' ])
call vundle #scripts #view ( 'Installer' , ['" Installing plugin s to ' .expand ( g :bundle_dir , 1 ) ], names + ['Helptags' ])
" This calls 'add' as a normal mode command. This is a buffer local mapping
" defined in vundle#scripts#view(). The mapping will call a buffer local
" command InstallPlugin which in turn will call vundle#installer#run() with
" vundle#installer#install().
call s :process ( a :bang , ( a :bang ? 'add!' : 'add' ) )
call vundle #config #require ( bundles )
endf
" ---------------------------------------------------------------------------
" Iterate over all lines in a Vundle window and execute the given command for
" every line. Used by the installation and cleaning functions.
"
" bang -- not used (FIXME)
" cmd -- the (normal mode) command to execute for every line as a string
" ---------------------------------------------------------------------------
func ! s :process ( bang , cmd )
let msg = ''
@ -30,7 +48,7 @@ func! s:process(bang, cmd)
endif
if 'updated' = = g :vundle_last_status && empty ( msg )
let msg = 'Bundle s updated; press u to view changelog'
let msg = 'Plugin s updated; press u to view changelog'
endif
" goto next one
@ -43,6 +61,16 @@ func! s:process(bang, cmd)
echo 'Done! ' .msg
endf
" ---------------------------------------------------------------------------
" Call another function in the different Vundle windows.
"
" func_name -- the function to call
" name -- the bundle name to call func_name for (string)
" ... -- the argument to be used when calling func_name (only the first
" optional argument will be used)
" return -- the status returned by the call to func_name
" ---------------------------------------------------------------------------
func ! vundle #installer #run ( func_name , name , ...) abort
let n = a :name
@ -67,6 +95,8 @@ func! vundle#installer#run(func_name, name, ...) abort
echo n .' deleted'
elseif 'helptags' = = status
echo n .' regenerated'
elseif 'pinned' = = status
echo n .' pinned'
elseif 'error' = = status
echohl Error
echo 'Error processing ' .n
@ -81,7 +111,14 @@ func! vundle#installer#run(func_name, name, ...) abort
return status
endf
func ! s :sign ( status )
" ---------------------------------------------------------------------------
" Put a sign on the current line, indicating the status of the installation
" step.
"
" status -- string describing the status
" ---------------------------------------------------------------------------
func ! s :sign ( status )
if ( ! has ( 'signs' ) )
return
endif
@ -89,6 +126,14 @@ func! s:sign(status)
exe ":sign place " .line ( '.' ) ." line=" .line ( '.' ) ." name=Vu_" . a :status ." buffer=" . bufnr ( "%" )
endf
" ---------------------------------------------------------------------------
" Install a plugin, then add it to the runtimepath and source it.
"
" bang -- 1 or 0, passed directly to vundle#installer#install()
" name -- the name of a bundle (string)
" return -- the return value from vundle#installer#install()
" ---------------------------------------------------------------------------
func ! vundle #installer #install_and_require ( bang , name ) abort
let result = vundle #installer #install ( a :bang , a :name )
let b = vundle #config #bundle ( a :name , {})
@ -97,14 +142,35 @@ func! vundle#installer#install_and_require(bang, name) abort
return result
endf
" ---------------------------------------------------------------------------
" Install or update a bundle given by its name.
"
" bang -- 1 or 0, passed directly to s:sync()
" name -- the name of a bundle (string)
" return -- the return value from s:sync()
" ---------------------------------------------------------------------------
func ! vundle #installer #install ( bang , name ) abort
if ! isdirectory ( g :bundle_dir ) | call mkdir ( g :bundle_dir , 'p' ) | endif
let b = vundle #config #init_bundle ( a :name , {})
let n = substitute ( a :name , "['" .'"]\+' , '' , 'g' )
let matched = filter ( copy ( g :bundles ) , 'v:val.name_spec == n' )
if len ( matched ) > 0
let b = matched [0 ]
else
let b = vundle #config #init_bundle ( a :name , {})
endif
return s :sync ( a :bang , b )
endf
" ---------------------------------------------------------------------------
" Call :helptags for all bundles in g:bundles.
"
" return -- 'error' if an error occurred, else return 'helptags'
" ---------------------------------------------------------------------------
func ! vundle #installer #docs ( ) abort
let error_count = vundle #installer #helptags ( g :bundles )
if error_count > 0
@ -113,6 +179,14 @@ func! vundle#installer#docs() abort
return 'helptags'
endf
" ---------------------------------------------------------------------------
" Call :helptags for a list of bundles.
"
" bundles -- a list of bundle dictionaries for which :helptags should be
" called.
" return -- the number of directories where :helptags failed
" ---------------------------------------------------------------------------
func ! vundle #installer #helptags ( bundles ) abort
let bundle_dirs = map ( copy ( a :bundles ) , 'v:val.rtpath' )
let help_dirs = filter ( bundle_dirs , 's:has_doc(v:val)' )
@ -123,21 +197,35 @@ func! vundle#installer#helptags(bundles) abort
let statuses = map ( copy ( help_dirs ) , 's:helptags(v:val)' )
let errors = filter ( statuses , 'v:val == 0' )
call s :log ( 'Helptags: ' .len ( help_dirs ) .' bundle s processed' )
call s :log ( 'Helptags: ' .len ( help_dirs ) .' plugin s processed' )
return len ( errors )
endf
" ---------------------------------------------------------------------------
" List all installed plugins.
" Corresponding documentation: vundle-plugins-list
"
" bang -- not used
" ---------------------------------------------------------------------------
func ! vundle #installer #list ( bang ) abort
let bundles = vundle #scripts #bundle_names ( map ( copy ( g :bundles ) , 'v:val.name_spec' ) )
call vundle #scripts #view ( 'list' , ['" My Bundles' ], bundles )
call vundle #scripts #view ( 'list' , ['" My Plugin s' ], bundles )
redraw
echo len ( g :bundles ) .' bundles configured'
echo len ( g :bundles ) .' plugin s configured'
endf
" ---------------------------------------------------------------------------
" List and remove all directories in the bundle directory which are not
" activated (added to the bundle list).
"
" bang -- 0 if the user should be asked to confirm every deletion, 1 if they
" should be removed unconditionally
" ---------------------------------------------------------------------------
func ! vundle #installer #clean ( bang ) abort
let bundle_dirs = map ( copy ( g :bundles ) , 'v:val.path()' )
let bundle_dirs = map ( copy ( g :bundles ) , 'v:val.path()' )
let all_dirs = ( v :version > 702 | | ( v :version = = 702 && has ( "patch51" ) ) )
\ ? split ( globpath ( g :bundle_dir , '*' , 1 ) , "\n" )
\ : split ( globpath ( g :bundle_dir , '*' ) , "\n" )
@ -147,7 +235,7 @@ func! vundle#installer#clean(bang) abort
let headers = ['" All clean!' ]
let names = []
else
let headers = ['" Removing bundle s:' ]
let headers = ['" Removing Plugin s:' ]
let names = vundle #scripts #bundle_names ( map ( copy ( x_dirs ) , 'fnamemodify(v:val, ":t")' ) )
end
@ -167,41 +255,68 @@ func! vundle#installer#clean(bang) abort
endf
func ! vundle #installer #should_use_submodules ( ) abort
call s :system ( 'cd ' .shellescape ( g :bundle_dir ) .'; git clean -Xnd `pwd` 2>/dev/null | grep "Would remove \./" > /dev/null' )
let cmd = 'cd ' .vundle #installer #shellesc ( g :bundle_dir ) .' && git clean -Xnd `pwd` | grep "Would remove \./"'
let cmd = vundle #installer #shellesc_cd ( cmd )
call s :system ( cmd )
return v :shell_error ! = 0
endf
func ! vundle #installer #top_level_path ( ) abort
let cmd = 'cd ' .vundle #installer #shellesc ( g :bundle_dir ) .' && git rev-parse --show-toplevel'
let cmd = vundle #installer #shellesc_cd ( cmd )
let out = s :strip ( s :system ( cmd ) )
return out
endf
func ! vundle #installer #relative_path ( ) abort
let cmd = 'cd ' .vundle #installer #shellesc ( g :bundle_dir ) .' && git rev-parse --show-prefix'
let cmd = vundle #installer #shellesc_cd ( cmd )
let prefix = s :strip ( s :system ( cmd ) )
return prefix .substitute ( bundle .path ( ) , g :bundle_dir .'/' , '' , '' )
endf
" ---------------------------------------------------------------------------
" Delete to directory for a plugin.
"
" bang -- not used
" dir_name -- the bundle directory to be deleted (as a string)
" return -- 'error' if an error occurred, 'deleted' if the plugin folder was
" successfully deleted
" ---------------------------------------------------------------------------
func ! vundle #installer #delete ( bang , dir_name ) abort
let bundle = vundle #config #init_bundle ( a :dir_name , {})
if vundle #installer #should_use_submodules ( )
let top_level = substitute ( s :system ( 'cd ' .shellescape ( g :bundle_dir ) .'; git rev-parse --show-toplevel' ) , '\n' , '' , 'g' )
let prefix = substitute ( s :system ( 'cd ' .shellescape ( g :bundle_dir ) .'; git rev-parse --show-prefix' ) , '\n' , '' , 'g' )
let relative_path = prefix .substitute ( bundle .path ( ) , g :bundle_dir .'/' , '' , '' )
let cmd = 'cd ' .shellescape ( top_level )
let cmd .= '; git config -f .gitmodules --remove-section submodule.' .shellescape ( relative_path )
let cmd .= '; git config -f .git/config --remove-section submodule.' .shellescape ( relative_path )
let cmd .= '; git rm --cached ' .shellescape ( relative_path )
let cmd .= '; rm -rf ' .shellescape ( relative_path )
let cmd .= '; rm -rf ' .shellescape ( '.git/modules/' .relative_path )
let cmd .= ';'
let top_level_path = vundle #installer #top_level_path ( )
let relative_path = vundle #installer #relative_path ( )
let cmd_parts = [
\ 'cd ' .vundle #installer #shellesc ( top_level_path ) ,
\ 'git config -f .gitmodules --remove-section submodule.' .vundle #installer #shellesc ( relative_path ) ,
\ 'git config -f .git/config --remove-section submodule.' .vundle #installer #shellesc ( relative_path ) ,
\ 'git rm --cached ' .vundle #installer #shellesc ( relative_path ) ,
\ 'rm -rf ' .vundle #installer #shellesc ( relative_path ) ,
\ 'rm -rf ' .vundle #installer #shellesc ( '.git/modules/' .relative_path ) ,
\ ]
let cmd = join ( cmd_parts , ' && ' )
let cmd = vundle #installer #shellesc_cd ( cmd )
let cmd .= ' &&'
else
let cmd = ''
endif
let cmd .= ( has ( 'win32' ) | | has ( 'win64' ) ) ?
let cmd .= ( ( has ( 'win32' ) | | has ( 'win64' ) ) && empty ( matchstr ( &shell , 'sh' ) ) ) ?
\ 'rmdir /S /Q' :
\ 'rm -rf'
let cmd .= ' ' .shellescape ( bundle .path ( ) )
let bundle = vundle #config #init_bundle ( a :dir_name , {})
let cmd .= ' ' .vundle #installer #shellesc ( bundle .path ( ) )
let out = s :system ( cmd )
call s :log ( '' )
call s :log ( 'Bundle ' .a :dir_name )
call s :log ( '$ ' .cmd )
call s :log ( '> ' .out )
call s :log ( 'Plugin ' .a :dir_name )
call s :log ( cmd , '$ ' )
call s :log ( out , '> ' )
if 0 ! = v :shell_error
return 'error'
@ -210,6 +325,13 @@ func! vundle#installer#delete(bang, dir_name) abort
endif
endf
" ---------------------------------------------------------------------------
" Check if a bundled plugin has any documentation.
"
" rtp -- a path (string) where the plugin is installed
" return -- 1 if some documentation was found, 0 otherwise
" ---------------------------------------------------------------------------
func ! s :has_doc ( rtp ) abort
return isdirectory ( a :rtp .'/doc' )
\ && ( ! filereadable ( a :rtp .'/doc/tags' ) | | filewritable ( a :rtp .'/doc/tags' ) )
@ -218,8 +340,16 @@ func! s:has_doc(rtp) abort
\ : ! ( empty ( glob ( a :rtp .'/doc/*.txt' ) ) && empty ( glob ( a :rtp .'/doc/*.??x' ) ) )
endf
" ---------------------------------------------------------------------------
" Update the helptags for a plugin.
"
" rtp -- the path to the plugin's root directory (string)
" return -- 1 if :helptags succeeded, 0 otherwise
" ---------------------------------------------------------------------------
func ! s :helptags ( rtp ) abort
let doc_path = a :rtp .'/doc/'
" it is important to keep trailing slash here
let doc_path = resolve ( a :rtp . '/doc/' )
call s :log ( ':helptags ' .doc_path )
try
execute 'helptags ' . doc_path
@ -230,40 +360,135 @@ func! s:helptags(rtp) abort
return 1
endf
func ! s :sync ( bang , bundle ) abort
let git_dir = expand ( a :bundle .path ( ) .'/.git' , 1 )
let is_submodule = isdirectory ( git_dir ) | | filereadable ( git_dir )
if vundle #installer #should_use_submodules ( )
let top_level = substitute ( s :system ( 'cd ' .shellescape ( g :bundle_dir ) .' && git rev-parse --show-toplevel' ) , '\n' , '' , 'g' )
let prefix = substitute ( s :system ( 'cd ' .shellescape ( g :bundle_dir ) .' && git rev-parse --show-prefix' ) , '\n' , '' , 'g' )
let relative_path = prefix .substitute ( a :bundle .path ( ) , g :bundle_dir .'/' , '' , '' )
endif
" ---------------------------------------------------------------------------
" Get the URL for the remote called 'origin' on the repository that
" corresponds to a given bundle.
"
" bundle -- a bundle object to check the repository for
" return -- the URL for the origin remote (string)
" ---------------------------------------------------------------------------
func ! s :get_current_origin_url ( bundle ) abort
let cmd = 'cd ' .vundle #installer #shellesc ( a :bundle .path ( ) ) .' && git config --get remote.origin.url'
let cmd = vundle #installer #shellesc_cd ( cmd )
let out = s :strip ( s :system ( cmd ) )
return out
endf
" ---------------------------------------------------------------------------
" Get a short sha of the HEAD of the repository for a given bundle
"
" bundle -- a bundle object
" return -- A 15 character log sha for the current HEAD
" ---------------------------------------------------------------------------
func ! s :get_current_sha ( bundle )
let cmd = 'cd ' .vundle #installer #shellesc ( a :bundle .path ( ) ) .' && git rev-parse HEAD'
let cmd = vundle #installer #shellesc_cd ( cmd )
let out = s :system ( cmd ) [0 :15 ]
return out
endf
" ---------------------------------------------------------------------------
" Create the appropriate sync command to run according to the current state of
" the local repository (clone, pull, reset, etc).
"
" In the case of a pull (update), also return the current sha, so that we can
" later check that there has been an upgrade.
"
" bang -- 0 if only new plugins should be installed, 1 if existing plugins
" should be updated
" bundle -- a bundle object to create the sync command for
" return -- A list containing the command to run and the sha for the current
" HEAD
" ---------------------------------------------------------------------------
func ! s :make_sync_command ( bang , bundle ) abort
let git_dir = expand ( a :bundle .path ( ) .'/.git/' , 1 )
if isdirectory ( git_dir ) | | filereadable ( expand ( a :bundle .path ( ) .'/.git' , 1 ) )
let current_origin_url = s :get_current_origin_url ( a :bundle )
if current_origin_url ! = a :bundle .uri
call s :log ( 'Plugin URI change detected for Plugin ' . a :bundle .name )
call s :log ( '> Plugin ' . a :bundle .name . ' old URI: ' . current_origin_url )
call s :log ( '> Plugin ' . a :bundle .name . ' new URI: ' . a :bundle .uri )
" Directory names match but the origin remotes are not the same
let cmd_parts = [
\ 'cd ' .vundle #installer #shellesc ( a :bundle .path ( ) ) ,
\ 'git remote set-url origin ' . vundle #installer #shellesc ( a :bundle .uri ) ,
\ 'git fetch' ,
\ 'git reset --hard origin/HEAD' ,
\ 'git submodule update --init --merge --recursive' ,
\ ]
let cmd = join ( cmd_parts , ' && ' )
let cmd = vundle #installer #shellesc_cd ( cmd )
let initial_sha = ''
return [cmd , initial_sha ]
endif
if is_submodule
if ! ( a :bang ) | return 'todate' | endif
if ! ( a :bang )
" The repo exists, and no !, so leave as it is.
return ['' , '' ]
endif
let cmd = 'cd ' .shellescape ( a :bundle .path ( ) ) .' && git pull && git submodule update --init --merge --recursive'
let cmd = g :shellesc_cd ( cmd )
let cmd_parts = [
\ 'cd ' .vundle #installer #shellesc ( a :bundle .path ( ) ) ,
\ 'git pull' ,
\ 'git submodule update --init --merge --recursive' ,
\ ]
let cmd = join ( cmd_parts , ' && ' )
let cmd = vundle #installer #shellesc_cd ( cmd )
let get_current_sha = 'cd ' .shellescape ( a :bundle .path ( ) ) .' && git rev-parse HEAD'
let get_current_sha = g :shellesc_cd ( get_current_sha )
let initial_sha = s :system ( get_current_sha ) [0 :15 ]
let initial_sha = s :get_current_sha ( a :bundle )
else
if vundle #installer #should_use_submodules ( )
let cmd = 'cd ' .shellescape ( top_level ) .' && git submodule add ' .a :bundle .uri .' ' .shellescape ( relative_path ) .' && git submodule init'
let top_level_path = vundle #installer #top_level_path ( )
let relative_path = vundle #installer #relative_path ( )
let cmd_parts = [
\ 'cd ' .vundle #installer #shellesc ( top_level_path ) ,
\ 'git submodule add ' .vundle #installer #shellesc ( a :bundle .uri ) .' ' .vundle #installer #shellesc ( relative_path ) ,
\ 'git submodule init' ,
\ ]
let cmd = join ( cmd_parts , ' && ' )
let cmd = vundle #installer #shellesc_cd ( cmd )
else
let cmd = 'git clone --recursive ' .shellescape ( a :bundle .uri ) .' ' .shellescape ( a :bundle .path ( ) )
let cmd = 'git clone --recursive ' .vundle #installer #shellesc ( a :bundle .uri ) .' ' .vundle #installer #shellesc ( a :bundle .path ( ) )
endif
let initial_sha = ''
endif
return [cmd , initial_sha ]
endf
" ---------------------------------------------------------------------------
" Install or update a given bundle object with git.
"
" bang -- 0 if only new plugins should be installed, 1 if existing plugins
" should be updated
" bundle -- a bundle object (dictionary)
" return -- a string indicating the status of the bundle installation:
" - todate : Nothing was updated or the repository was up to date
" - new : The plugin was newly installed
" - updated : Some changes where pulled via git
" - error : An error occurred in the shell command
" - pinned : The bundle is marked as pinned
" ---------------------------------------------------------------------------
func ! s :sync ( bang , bundle ) abort
" Do not sync if this bundle is pinned
if a :bundle .is_pinned ( )
return 'pinned'
endif
let [ cmd , initial_sha ] = s :make_sync_command ( a :bang , a :bundle )
if empty ( cmd )
return 'todate'
endif
let out = s :system ( cmd )
call s :log ( '' )
call s :log ( 'Bundle ' .a :bundle .name_spec )
call s :log ( '$ ' .cmd )
call s :log ( '> ' .out )
call s :log ( 'Plugin ' .a :bundle .name_spec )
call s :log ( cmd , '$ ' )
call s :log ( out , '> ' )
if 0 ! = v :shell_error
return 'error'
@ -273,7 +498,7 @@ func! s:sync(bang, bundle) abort
return 'new'
endif
let updated_sha = s :system ( get_current_sha ) [0 :15 ]
let updated_sha = s :get_current_sha ( a :bundle )
if initial_sha = = updated_sha
return 'todate'
@ -283,31 +508,78 @@ func! s:sync(bang, bundle) abort
return 'updated'
endf
func ! g :shellesc ( cmd ) abort
if ( has ( 'win32' ) | | has ( 'win64' ) )
if &shellxquote ! = '(' " workaround for patch #445
return '"' .a :cmd .'"' " enclose in quotes so && joined cmds work
endif
" ---------------------------------------------------------------------------
" Escape special characters in a string to be able to use it as a shell
" command with system().
"
" cmd -- the string holding the shell command
" return -- a string with the relevant characters escaped
" ---------------------------------------------------------------------------
func ! vundle #installer #shellesc ( cmd ) abort
if ( ( has ( 'win32' ) | | has ( 'win64' ) ) && empty ( matchstr ( &shell , 'sh' ) ) )
return '"' . substitute ( a :cmd , '"' , '\\"' , 'g' ) . '"'
endif
return a :cmd
return shellescape ( a :cmd )
endf
func ! g :shellesc_cd ( cmd ) abort
if ( has ( 'win32' ) | | has ( 'win64' ) )
" ---------------------------------------------------------------------------
" Fix a cd shell command to be used on Windows.
"
" cmd -- the command to be fixed (string)
" return -- the fixed command (string)
" ---------------------------------------------------------------------------
func ! vundle #installer #shellesc_cd ( cmd ) abort
if ( ( has ( 'win32' ) | | has ( 'win64' ) ) && empty ( matchstr ( &shell , 'sh' ) ) )
let cmd = substitute ( a :cmd , '^cd ' , 'cd /d ' , '' ) " add /d switch to change drives
let cmd = g :shellesc ( cmd )
return cmd
else
return a :cmd
endif
endf
" ---------------------------------------------------------------------------
" Make a system call. This can be used to change the way system calls
" are made during developing, without searching the whole code base for
" actual system() calls.
"
" cmd -- the command passed to system() (string)
" return -- the return value from system()
" ---------------------------------------------------------------------------
func ! s :system ( cmd ) abort
return system ( a :cmd )
endf
func ! s :log ( str ) abort
let fmt = '%y%m%d %H:%M:%S'
call add ( g :vundle_log , '[' .strftime ( fmt ) .'] ' .a :str )
" ---------------------------------------------------------------------------
" Add a log message to Vundle's internal logging variable.
"
" str -- the log message (string)
" prefix -- optional prefix for multi-line entries (string)
" return -- a:str
" ---------------------------------------------------------------------------
func ! s :log ( str , ...) abort
let prefix = a :0 > 0 ? a :1 : ''
let fmt = '%Y-%m-%d %H:%M:%S'
let lines = split ( a :str , '\n' , 1 )
let time = strftime ( fmt )
for line in lines
call add ( g :vundle_log , '[' . time .'] ' . prefix . line )
endfor
return a :str
endf
" ---------------------------------------------------------------------------
" Remove leading and trailing whitespace from a string
"
" str -- The string to rid of trailing and leading spaces
" return -- A string stripped of side spaces
" ---------------------------------------------------------------------------
func ! s :strip ( str )
return substitute ( a :str , '\%^\_s*\(.\{-}\)\_s*\%$' , '\1' , '' )
endf
" vim: set expandtab sts=2 ts=2 sw=2 tw=78 norl: