@ -99,7 +99,13 @@ let s:mac_gui = has('gui_macvim') && has('gui_running')
let s :is_win = has ( 'win32' )
let s :nvim = has ( 'nvim-0.2' ) | | ( has ( 'nvim' ) && exists ( '*jobwait' ) && ! s :is_win )
let s :vim8 = has ( 'patch-8.0.0039' ) && exists ( '*job_start' )
if s :is_win && &shellslash
set noshellslash
let s :me = resolve ( expand ( '<sfile>:p' ) )
set shellslash
else
let s :me = resolve ( expand ( '<sfile>:p' ) )
endif
let s :base_spec = { 'branch' : 'master' , 'frozen' : 0 }
let s :TYPE = {
\ 'string' : type ( '' ) ,
@ -110,10 +116,42 @@ let s:TYPE = {
let s :loaded = get ( s :, 'loaded' , {})
let s :triggers = get ( s :, 'triggers' , {})
if s :is_win
function ! s :plug_call ( fn , ...)
let shellslash = &shellslash
try
set noshellslash
return call ( a :fn , a :000 )
finally
let &shellslash = shellslash
endtry
endfunction
else
function ! s :plug_call ( fn , ...)
return call ( a :fn , a :000 )
endfunction
endif
function ! s :plug_getcwd ( )
return s :plug_call ( 'getcwd' )
endfunction
function ! s :plug_fnamemodify ( fname , mods )
return s :plug_call ( 'fnamemodify' , a :fname , a :mods )
endfunction
function ! s :plug_expand ( fmt )
return s :plug_call ( 'expand' , a :fmt , 1 )
endfunction
function ! s :plug_tempname ( )
return s :plug_call ( 'tempname' )
endfunction
function ! plug #begin ( ...)
if a :0 > 0
let s :plug_home_org = a :1
let home = s :path ( fnamemodify ( expand ( a :1 ) , ':p' ) )
let home = s :path ( s:plug_ fnamemodify( s:plug_ expand( a :1 ) , ':p' ) )
elseif exists ( 'g:plug_home' )
let home = s :path ( g :plug_home )
elseif ! empty ( &rtp )
@ -121,7 +159,7 @@ function! plug#begin(...)
else
return s :err ( 'Unable to determine plug home. Try calling plug#begin() with a path argument.' )
endif
if fnamemodify( home , ':t' ) = = # 'plugin' && fnamemodify( home , ':h' ) = = # s :first_rtp
if s:plug_ fnamemodify( home , ':t' ) = = # 'plugin' && s:plug_ fnamemodify( home , ':h' ) = = # s :first_rtp
return s :err ( 'Invalid plug home. ' .home .' is a standard Vim runtime path and is not allowed.' )
endif
@ -139,6 +177,16 @@ function! s:define_commands()
if ! executable ( 'git' )
return s :err ( '`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.' )
endif
if has ( 'win32' )
\ && &shellslash
\ && ( &shell = ~ # 'cmd\.exe' | | &shell = ~ # 'powershell\.exe' )
return s :err ( 'vim-plug does not support shell, ' . &shell . ', when shellslash is set.' )
endif
if ! has ( 'nvim' )
\ && ( has ( 'win32' ) | | has ( 'win32unix' ) )
\ && ! has ( 'multi_byte' )
return s :err ( 'Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.' )
endif
command ! - nargs = * - bar - bang - complete = customlist , s :names PlugInstall call s :install ( < bang > 0 , [< f - args > ])
command ! - nargs = * - bar - bang - complete = customlist , s :names PlugUpdate call s :update ( < bang > 0 , [< f - args > ])
command ! - nargs = 0 - bar - bang PlugClean call s :clean ( < bang > 0 )
@ -203,7 +251,7 @@ endfunction
function ! plug #end ( )
if ! exists ( 'g:plugs' )
return s :err ( ' Call plug#begin() first')
return s :err ( ' plug#end() called without calling plug#begin() first')
endif
if exists ( '#PlugLOD' )
@ -334,11 +382,11 @@ function! s:progress_opt(base)
\ s :git_version_requirement ( 1 , 7 , 1 ) ? '--progress' : ''
endfunction
if s :is_win
function ! s :rtp ( spec )
return s :path ( a :spec .dir . get ( a :spec , 'rtp' , '' ) )
endfunction
if s :is_win
function ! s :path ( path )
return s :trim ( substitute ( a :path , '/' , '\' , 'g' ) )
endfunction
@ -350,11 +398,33 @@ if s:is_win
function ! s :is_local_plug ( repo )
return a :repo = ~ ? '^[a-z]:\|^[%~]'
endfunction
else
function ! s :rtp ( spec )
return s :dirpath ( a :spec .dir . get ( a :spec , 'rtp' , '' ) )
" Copied from fzf
function ! s :wrap_cmds ( cmds )
let cmds = [
\ '@echo off' ,
\ 'setlocal enabledelayedexpansion' ]
\ + ( type ( a :cmds ) = = type ( []) ? a :cmds : [a :cmds ])
\ + ['endlocal' ]
if has ( 'iconv' )
if ! exists ( 's:codepage' )
let s :codepage = libcallnr ( 'kernel32.dll' , 'GetACP' , 0 )
endif
return map ( cmds , printf ( 'iconv(v:val."\r", "%s", "cp%d")' , &encoding , s :codepage ) )
endif
return map ( cmds , 'v:val."\r"' )
endfunction
function ! s :batchfile ( cmd )
let batchfile = s :plug_tempname ( ) .'.bat'
call writefile ( s :wrap_cmds ( a :cmd ) , batchfile )
let cmd = plug #shellescape ( batchfile , {'shell' : &shell , 'script' : 0 })
if &shell = ~ # 'powershell\.exe'
let cmd = '& ' . cmd
endif
return [batchfile , cmd ]
endfunction
else
function ! s :path ( path )
return s :trim ( a :path )
endfunction
@ -434,8 +504,8 @@ endfunction
function ! s :dobufread ( names )
for name in a :names
let path = s :rtp ( g :plugs [name ]) .'/**'
for dir in ['ftdetect' , 'ftplugin' ]
let path = s :rtp ( g :plugs [name ])
for dir in ['ftdetect' , 'ftplugin' , 'after/ftdetect' , 'after/ftplugin' ]
if len ( finddir ( dir , path ) )
if exists ( '#BufRead' )
doautocmd BufRead
@ -554,7 +624,7 @@ function! plug#(repo, ...)
try
let repo = s :trim ( a :repo )
let opts = a :0 = = 1 ? s :parse_options ( a :1 ) : s :base_spec
let name = get ( opts , 'as' , fnamemodify( repo , ':t:s?\.git$??' ) )
let name = get ( opts , 'as' , s:plug_ fnamemodify( repo , ':t:s?\.git$??' ) )
let spec = extend ( s :infer_properties ( name , repo ) , opts )
if ! has_key ( g :plugs , name )
call add ( g :plugs_order , name )
@ -574,7 +644,7 @@ function! s:parse_options(arg)
elseif type = = s :TYPE .dict
call extend ( opts , a :arg )
if has_key ( opts , 'dir' )
let opts .dir = s :dirpath ( expand( opts .dir ) )
let opts .dir = s :dirpath ( s:plug_ expand( opts .dir ) )
endif
else
throw 'Invalid argument type (expected: string or dictionary)'
@ -585,7 +655,7 @@ endfunction
function ! s :infer_properties ( name , repo )
let repo = a :repo
if s :is_local_plug ( repo )
return { 'dir' : s :dirpath ( expand( repo ) ) }
return { 'dir' : s :dirpath ( s:plug_ expand( repo ) ) }
else
if repo = ~ ':'
let uri = repo
@ -738,7 +808,7 @@ function! s:finish_bindings()
endfunction
function ! s :prepare ( ...)
if empty ( getcwd( ) )
if empty ( s:plug_ getcwd( ) )
throw 'Invalid current working directory. Cannot proceed.'
endif
@ -794,31 +864,28 @@ endfunction
function ! s :chsh ( swap )
let prev = [&shell , &shellcmdflag , &shellredir ]
if s :is_win
set shell = cmd .exe shellcmdflag = /c shellredir = > %s \ 2 > &1
elseif a :swap
if ! s :is_win && a :swap
set shell = sh shellredir = > %s \ 2 > &1
endif
return prev
endfunction
function ! s :bang ( cmd , ...)
let batchfile = ''
try
let [sh , shellcmdflag , shrd ] = s :chsh ( a :0 )
" FIXME: Escaping is incomplete. We could use shellescape with eval,
" but it won't work on Windows.
let cmd = a :0 ? s :with_cd ( a :cmd , a :1 ) : a :cmd
if s :is_win
let batchfile = tempname ( ) .'.bat'
call writefile ( ["@echo off\r" , cmd . "\r" ], batchfile )
let cmd = batchfile
let [batchfile , cmd ] = s :batchfile ( cmd )
endif
let g :_plug_bang = ( s :is_win && has ( 'gui_running' ) ? 'silent ' : '' ) .'!' .escape ( cmd , '#!%' )
execute "normal! :execute g:_plug_bang\<cr>\<cr>"
finally
unlet g :_plug_bang
let [&shell , &shellcmdflag , &shellredir ] = [sh , shellcmdflag , shrd ]
if s :is_win
if s :is_win && filereadable ( batchfile )
call delete ( batchfile )
endif
endtry
@ -897,7 +964,7 @@ function! s:checkout(spec)
let output = s :system ( 'git rev-parse HEAD' , a :spec .dir )
if ! v :shell_error && ! s :hash_match ( sha , s :lines ( output ) [0 ])
let output = s :system (
\ 'git fetch --depth 999999 && git checkout ' .s:esc ( sha ) .' --' , a :spec .dir )
\ 'git fetch --depth 999999 && git checkout ' .plug#shellescape ( sha ) .' --' , a :spec .dir )
endif
return output
endfunction
@ -1015,7 +1082,7 @@ function! s:update_impl(pull, force, args) abort
let s :clone_opt = get ( g :, 'plug_shallow' , 1 ) ?
\ '--depth 1' . ( s :git_version_requirement ( 1 , 7 , 10 ) ? ' --no-single-branch' : '' ) : ''
if has ( 'win32unix' )
if has ( 'win32unix' ) | | has ( 'wsl' )
let s :clone_opt .= ' -c core.eol=lf -c core.autocrlf=input'
endif
@ -1094,7 +1161,7 @@ function! s:update_finish()
elseif has_key ( spec , 'tag' )
let tag = spec .tag
if tag = ~ '\*'
let tags = s :lines ( s :system ( 'git tag --list ' .s:shellesc ( tag ) .' --sort -version:refname 2>&1' , spec .dir ) )
let tags = s :lines ( s :system ( 'git tag --list ' .plug#shellescape ( tag ) .' --sort -version:refname 2>&1' , spec .dir ) )
if ! v :shell_error && ! empty ( tags )
let tag = tags [0 ]
call s :log4 ( name , printf ( 'Latest tag for %s -> %s' , spec .tag , tag ) )
@ -1102,12 +1169,12 @@ function! s:update_finish()
endif
endif
call s :log4 ( name , 'Checking out ' .tag )
let out = s :system ( 'git checkout -q ' .s:esc ( tag ) .' -- 2>&1' , spec .dir )
let out = s :system ( 'git checkout -q ' .plug#shellescape ( tag ) .' -- 2>&1' , spec .dir )
else
let branch = s:esc ( get( spec , 'branch' , 'master' ) )
call s :log4 ( name , 'Merging origin/' . branch)
let out = s :system ( 'git checkout -q ' . branch.' -- 2>&1'
\. ( has_key ( s :update .new , name ) ? '' : ( '&& git merge --ff-only origin/'.branch .' 2>&1' ) ) , spec .dir )
let branch = get( spec , 'branch' , 'master' )
call s :log4 ( name , 'Merging origin/' .s:esc ( branch) )
let out = s :system ( 'git checkout -q ' .plug#shellescape ( branch) .' -- 2>&1'
\. ( has_key ( s :update .new , name ) ? '' : ( '&& git merge --ff-only '.plug #shellescape ( ' origin/'.branch ) .' 2>&1' ) ) , spec .dir )
endif
if ! v :shell_error && filereadable ( spec .dir .'/.gitmodules' ) &&
\ ( s :update .force | | has_key ( s :update .new , name ) | | s :is_updated ( spec .dir ) )
@ -1151,7 +1218,7 @@ function! s:job_abort()
silent ! call job_stop ( j .jobid )
endif
if j .new
call s :system( 'rm -rf ' . s :shellesc ( g :plugs [name ].dir ) )
call s :rm_rf ( g :plugs [name ].dir )
endif
endfor
let s :jobs = {}
@ -1204,22 +1271,17 @@ endfunction
function ! s :spawn ( name , cmd , opts )
let job = { 'name' : a :name , 'running' : 1 , 'error' : 0 , 'lines' : ['' ],
\ 'batchfile' : ( s :is_win && ( s :nvim | | s :vim8 ) ) ? tempname ( ) .'.bat' : '' ,
\ 'new' : get ( a :opts , 'new' , 0 ) }
let s :jobs [a :name ] = job
let cmd = has_key ( a :opts , 'dir' ) ? s :with_cd ( a :cmd , a :opts .dir ) : a :cmd
if ! empty ( job .batchfile )
call writefile ( ["@echo off\r" , cmd . "\r" ], job .batchfile )
let cmd = job .batchfile
endif
let argv = add ( s :is_win ? ['cmd' , '/c' ] : ['sh' , '-c' ], cmd )
let cmd = has_key ( a :opts , 'dir' ) ? s :with_cd ( a :cmd , a :opts .dir , 0 ) : a :cmd
let argv = s :is_win ? ['cmd' , '/s' , '/c' , '"' .cmd .'"' ] : ['sh' , '-c' , cmd ]
if s :nvim
call extend ( job , {
\ 'on_stdout' : function ( 's:nvim_cb' ) ,
\ 'on_exit' : function ( 's:nvim_cb' ) ,
\ })
let jid = jobstart( argv , job )
let jid = s:plug_call ( 'jobstart' , argv , job )
if jid > 0
let job .jobid = jid
else
@ -1262,9 +1324,6 @@ function! s:reap(name)
call s :log ( bullet , a :name , empty ( result ) ? 'OK' : result )
call s :bar ( )
if has_key ( job , 'batchfile' ) && ! empty ( job .batchfile )
call delete ( job .batchfile )
endif
call remove ( s :jobs , a :name )
endfunction
@ -1279,9 +1338,10 @@ function! s:bar()
endfunction
function ! s :logpos ( name )
for i in range ( 4 , line ( '$' ) )
let max = line ( '$' )
for i in range ( 4 , max > 4 ? max : 4 )
if getline ( i ) = ~ # '^[-+x*] ' .a :name .':'
for j in range ( i + 1 , line( '$' ) )
for j in range ( i + 1 , max > 5 ? max : 5 )
if getline ( j ) ! ~ '^ '
return [i , j - 1 ]
endif
@ -1354,8 +1414,8 @@ while 1 " Without TCO, Vim stack is bound to explode
\ printf ( 'git clone %s %s %s %s 2>&1' ,
\ has_tag ? '' : s :clone_opt ,
\ prog ,
\ s:shellesc ( spec .uri ) ,
\ s:shellesc ( s :trim ( spec .dir ) ) ) , { 'new' : 1 })
\ plug#shellescape ( spec .uri , {'script' : 0 } ) ,
\ plug#shellescape ( s :trim ( spec .dir ) , {'script' : 0 }) ) , { 'new' : 1 })
endif
if ! s :jobs [name ].running
@ -1982,19 +2042,29 @@ function! s:update_ruby()
EOF
endfunction
function ! s :shellesc_cmd ( arg )
let escaped = substitute ( a :arg , '[&|<>()@^]' , '^&' , 'g' )
let escaped = substitute ( escaped , '%' , '%%' , 'g' )
let escaped = substitute ( escaped , '"' , '\\^&' , 'g' )
let escaped = substitute ( escaped , '\(\\\+\)\(\\^\)' , '\1\1\2' , 'g' )
return '^"' .substitute ( escaped , '\(\\\+\)$' , '\1\1' , '' ) .'^"'
function ! s :shellesc_cmd ( arg , script )
let escaped = substitute ( '"' .a :arg .'"' , '[&|<>()@^!"]' , '^&' , 'g' )
return substitute ( escaped , '%' , ( a :script ? '%' : '^' ) . '&' , 'g' )
endfunction
function ! s :shellesc_ps1 ( arg )
return "'" .substitute ( escape ( a :arg , '\"' ) , "'" , "''" , 'g' ) ."'"
endfunction
function ! s :shellesc_sh ( arg )
return "'" .substitute ( a :arg , "'" , "'\\\\''" , 'g' ) ."'"
endfunction
function ! s :shellesc ( arg )
if &shell = ~ # 'cmd.exe$'
return s :shellesc_cmd ( a :arg )
function ! plug #shellescape ( arg , ...)
let opts = a :0 > 0 && type ( a :1 ) = = s :TYPE .dict ? a :1 : {}
let shell = get ( opts , 'shell' , s :is_win ? 'cmd.exe' : 'sh' )
let script = get ( opts , 'script' , 1 )
if shell = ~ # 'cmd\.exe'
return s :shellesc_cmd ( a :arg , script )
elseif shell = ~ # 'powershell\.exe' | | shell = ~ # 'pwsh$'
return s :shellesc_ps1 ( a :arg )
endif
return shellescape ( a :arg )
return s :shellesc_sh ( a :arg )
endfunction
function ! s :glob_dir ( path )
@ -2026,23 +2096,23 @@ function! s:format_message(bullet, name, message)
endif
endfunction
function ! s :with_cd ( cmd , dir )
return printf ( 'cd%s %s && %s' , s :is_win ? ' /d' : '' , s :shellesc ( a :dir ) , a :cmd )
function ! s :with_cd ( cmd , dir , ...)
let script = a :0 > 0 ? a :1 : 1
return printf ( 'cd%s %s && %s' , s :is_win ? ' /d' : '' , plug #shellescape ( a :dir , {'script' : script }) , a :cmd )
endfunction
function ! s :system ( cmd , ...)
let batchfile = ''
try
let [sh , shellcmdflag , shrd ] = s :chsh ( 1 )
let cmd = a :0 > 0 ? s :with_cd ( a :cmd , a :1 ) : a :cmd
if s :is_win
let batchfile = tempname ( ) .'.bat'
call writefile ( ["@echo off\r" , cmd . "\r" ], batchfile )
let cmd = batchfile
let [batchfile , cmd ] = s :batchfile ( cmd )
endif
return system ( s:is_win ? '(' .cmd .')' : cmd)
return system ( cmd )
finally
let [&shell , &shellcmdflag , &shellredir ] = [sh , shellcmdflag , shrd ]
if s :is_win
if s :is_win && filereadable ( batchfile )
call delete ( batchfile )
endif
endtry
@ -2115,7 +2185,7 @@ endfunction
function ! s :rm_rf ( dir )
if isdirectory ( a :dir )
call s :system ( ( s :is_win ? 'rmdir /S /Q ' : 'rm -rf ' ) . s:shellesc ( a :dir ) )
call s :system ( ( s :is_win ? 'rmdir /S /Q ' : 'rm -rf ' ) . plug#shellescape ( a :dir ) )
endif
endfunction
@ -2147,7 +2217,7 @@ function! s:clean(force)
let allowed = {}
for dir in dirs
let allowed [s :dirpath ( fnamemodify( dir , ':h:h' ) ) ] = 1
let allowed [s :dirpath ( s:plug_ fnamemodify( dir , ':h:h' ) ) ] = 1
let allowed [dir ] = 1
for child in s :glob_dir ( dir )
let allowed [child ] = 1
@ -2220,11 +2290,11 @@ endfunction
function ! s :upgrade ( )
echo 'Downloading the latest version of vim-plug'
redraw
let tmp = tempname( )
let tmp = s:plug_ tempname( )
let new = tmp . '/plug.vim'
try
let out = s :system ( printf ( 'git clone --depth 1 %s %s' , s:shellesc ( s :plug_src ) , s :shellesc ( tmp ) ) )
let out = s :system ( printf ( 'git clone --depth 1 %s %s' , plug#shellescape ( s :plug_src ) , plug #shellescape ( tmp ) ) )
if v :shell_error
return s :err ( 'Error upgrading vim-plug: ' . out )
endif
@ -2365,18 +2435,17 @@ function! s:preview_commit()
wincmd P
endif
setlocal previewwindow filetype = git buftype = nofile nobuflisted modifiable
let batchfile = ''
try
let [sh , shellcmdflag , shrd ] = s :chsh ( 1 )
let cmd = 'cd ' .s:shellesc ( g :plugs [name ].dir ) .' && git show --no-color --pretty=medium ' .sha
let cmd = 'cd ' .plug#shellescape ( g :plugs [name ].dir ) .' && git show --no-color --pretty=medium ' .sha
if s :is_win
let batchfile = tempname ( ) .'.bat'
call writefile ( ["@echo off\r" , cmd . "\r" ], batchfile )
let cmd = batchfile
let [batchfile , cmd ] = s :batchfile ( cmd )
endif
execute 'silent %!' cmd
finally
let [&shell , &shellcmdflag , &shellredir ] = [sh , shellcmdflag , shrd ]
if s :is_win
if s :is_win && filereadable ( batchfile )
call delete ( batchfile )
endif
endtry
@ -2420,9 +2489,11 @@ function! s:diff()
call s :append_ul ( 2 , origin ? 'Pending updates:' : 'Last update:' )
for [k , v ] in plugs
let range = origin ? '..origin/' .v .branch : 'HEAD@{1}..'
let cmd = 'git log --graph --color=never ' .join ( map ( ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr' , range ], 's:shellesc(v:val)' ) )
let cmd = 'git log --graph --color=never '
\ . ( s :git_version_requirement ( 2 , 10 , 0 ) ? '--no-show-signature ' : '' )
\ . join ( map ( ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr' , range ], 'plug#shellescape(v:val)' ) )
if has_key ( v , 'rtp' )
let cmd .= ' -- ' .s :shellesc ( v .rtp )
let cmd .= ' -- ' .plug#shellescape ( v .rtp )
endif
let diff = s :system_chomp ( cmd , v .dir )
if ! empty ( diff )
@ -2470,7 +2541,7 @@ function! s:revert()
return
endif
call s :system ( 'git reset --hard HEAD@{1} && git checkout ' .s:esc ( g :plugs [name ].branch ) .' --' , g :plugs [name ].dir )
call s :system ( 'git reset --hard HEAD@{1} && git checkout ' .plug#shellescape ( g :plugs [name ].branch ) .' --' , g :plugs [name ].dir )
setlocal modifiable
normal ! "_dap
setlocal nomodifiable
@ -2498,7 +2569,7 @@ function! s:snapshot(force, ...) abort
endfor
if a :0 > 0
let fn = expand( a :1 )
let fn = s:plug_ expand( a :1 )
if filereadable ( fn ) && ! ( a :force | | s :ask ( a :1 .' already exists. Overwrite?' ) )
return
endif