瀏覽代碼

upgraded and some changes

William Reed 2 年之前
父節點
當前提交
0377487e32
共有 2 個文件被更改,包括 383 次插入107 次删除
  1. 377 106
      autoload/plug.vim
  2. 6 1
      vimrc

+ 377 - 106
autoload/plug.vim

@@ -22,10 +22,10 @@
 "   Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
 "   Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
 "
 "
 "   " On-demand loading
 "   " On-demand loading
-"   Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
+"   Plug 'preservim/nerdtree', { 'on': 'NERDTreeToggle' }
 "   Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
 "   Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
 "
 "
-"   " Using a non-master branch
+"   " Using a non-default branch
 "   Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
 "   Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
 "
 "
 "   " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
 "   " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
@@ -99,8 +99,14 @@ let s:mac_gui = has('gui_macvim') && has('gui_running')
 let s:is_win = has('win32')
 let s:is_win = has('win32')
 let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
 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')
 let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
-let s:me = resolve(expand('<sfile>:p'))
-let s:base_spec = { 'branch': 'master', 'frozen': 0 }
+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': '', 'frozen': 0 }
 let s:TYPE = {
 let s:TYPE = {
 \   'string':  type(''),
 \   'string':  type(''),
 \   'list':    type([]),
 \   'list':    type([]),
@@ -110,18 +116,140 @@ let s:TYPE = {
 let s:loaded = get(s:, 'loaded', {})
 let s:loaded = get(s:, 'loaded', {})
 let s:triggers = get(s:, 'triggers', {})
 let s:triggers = get(s:, 'triggers', {})
 
 
+function! s:is_powershell(shell)
+  return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$'
+endfunction
+
+function! s:isabsolute(dir) abort
+  return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)')
+endfunction
+
+function! s:git_dir(dir) abort
+  let gitdir = s:trim(a:dir) . '/.git'
+  if isdirectory(gitdir)
+    return gitdir
+  endif
+  if !filereadable(gitdir)
+    return ''
+  endif
+  let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*')
+  if len(gitdir) && !s:isabsolute(gitdir)
+    let gitdir = a:dir . '/' . gitdir
+  endif
+  return isdirectory(gitdir) ? gitdir : ''
+endfunction
+
+function! s:git_origin_url(dir) abort
+  let gitdir = s:git_dir(a:dir)
+  let config = gitdir . '/config'
+  if empty(gitdir) || !filereadable(config)
+    return ''
+  endif
+  return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze')
+endfunction
+
+function! s:git_revision(dir) abort
+  let gitdir = s:git_dir(a:dir)
+  let head = gitdir . '/HEAD'
+  if empty(gitdir) || !filereadable(head)
+    return ''
+  endif
+
+  let line = get(readfile(head), 0, '')
+  let ref = matchstr(line, '^ref: \zs.*')
+  if empty(ref)
+    return line
+  endif
+
+  if filereadable(gitdir . '/' . ref)
+    return get(readfile(gitdir . '/' . ref), 0, '')
+  endif
+
+  if filereadable(gitdir . '/packed-refs')
+    for line in readfile(gitdir . '/packed-refs')
+      if line =~# ' ' . ref
+        return matchstr(line, '^[0-9a-f]*')
+      endif
+    endfor
+  endif
+
+  return ''
+endfunction
+
+function! s:git_local_branch(dir) abort
+  let gitdir = s:git_dir(a:dir)
+  let head = gitdir . '/HEAD'
+  if empty(gitdir) || !filereadable(head)
+    return ''
+  endif
+  let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*')
+  return len(branch) ? branch : 'HEAD'
+endfunction
+
+function! s:git_origin_branch(spec)
+  if len(a:spec.branch)
+    return a:spec.branch
+  endif
+
+  " The file may not be present if this is a local repository
+  let gitdir = s:git_dir(a:spec.dir)
+  let origin_head = gitdir.'/refs/remotes/origin/HEAD'
+  if len(gitdir) && filereadable(origin_head)
+    return matchstr(get(readfile(origin_head), 0, ''),
+                  \ '^ref: refs/remotes/origin/\zs.*')
+  endif
+
+  " The command may not return the name of a branch in detached HEAD state
+  let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir))
+  return v:shell_error ? '' : result[-1]
+endfunction
+
+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(...)
 function! plug#begin(...)
   if a:0 > 0
   if a:0 > 0
     let s:plug_home_org = a:1
     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')
   elseif exists('g:plug_home')
     let home = s:path(g:plug_home)
     let home = s:path(g:plug_home)
+  elseif has('nvim')
+    let home = stdpath('data') . '/plugged'
   elseif !empty(&rtp)
   elseif !empty(&rtp)
     let home = s:path(split(&rtp, ',')[0]) . '/plugged'
     let home = s:path(split(&rtp, ',')[0]) . '/plugged'
   else
   else
     return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
     return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
   endif
   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.')
     return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.')
   endif
   endif
 
 
@@ -139,6 +267,16 @@ function! s:define_commands()
   if !executable('git')
   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(...)`.')
     return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
   endif
   endif
+  if has('win32')
+  \ && &shellslash
+  \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell))
+    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 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=* -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)
   command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0)
@@ -203,7 +341,7 @@ endfunction
 
 
 function! plug#end()
 function! plug#end()
   if !exists('g:plugs')
   if !exists('g:plugs')
-    return s:err('Call plug#begin() first')
+    return s:err('plug#end() called without calling plug#begin() first')
   endif
   endif
 
 
   if exists('#PlugLOD')
   if exists('#PlugLOD')
@@ -214,7 +352,7 @@ function! plug#end()
   endif
   endif
   let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
   let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
 
 
-  if exists('g:did_load_filetypes')
+  if get(g:, 'did_load_filetypes', 0)
     filetype off
     filetype off
   endif
   endif
   for name in g:plugs_order
   for name in g:plugs_order
@@ -269,7 +407,7 @@ function! plug#end()
 
 
   for [map, names] in items(lod.map)
   for [map, names] in items(lod.map)
     for [mode, map_prefix, key_prefix] in
     for [mode, map_prefix, key_prefix] in
-          \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
+          \ [['i', '<C-\><C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
       execute printf(
       execute printf(
       \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
       \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
       \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
       \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
@@ -324,7 +462,7 @@ endfunction
 
 
 function! s:git_version_requirement(...)
 function! s:git_version_requirement(...)
   if !exists('s:git_version')
   if !exists('s:git_version')
-    let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)')
+    let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)')
   endif
   endif
   return s:version_requirement(s:git_version, a:000)
   return s:version_requirement(s:git_version, a:000)
 endfunction
 endfunction
@@ -353,24 +491,25 @@ if s:is_win
 
 
   " Copied from fzf
   " Copied from fzf
   function! s:wrap_cmds(cmds)
   function! s:wrap_cmds(cmds)
-    let use_chcp = executable('sed')
-    return map([
+    let cmds = [
       \ '@echo off',
       \ '@echo off',
       \ 'setlocal enabledelayedexpansion']
       \ 'setlocal enabledelayedexpansion']
-    \ + (use_chcp ? [
-      \ 'for /f "usebackq" %%a in (`chcp ^| sed "s/[^0-9]//gp"`) do set origchcp=%%a',
-      \ 'chcp 65001 > nul'] : [])
     \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
     \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])
-    \ + (use_chcp ? ['chcp !origchcp! > nul'] : [])
-    \ + ['endlocal'],
-    \ 'v:val."\r"')
+    \ + ['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
   endfunction
 
 
   function! s:batchfile(cmd)
   function! s:batchfile(cmd)
-    let batchfile = tempname().'.bat'
+    let batchfile = s:plug_tempname().'.bat'
     call writefile(s:wrap_cmds(a:cmd), batchfile)
     call writefile(s:wrap_cmds(a:cmd), batchfile)
-    let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 1})
-    if &shell =~# 'powershell\.exe$'
+    let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
+    if s:is_powershell(&shell)
       let cmd = '& ' . cmd
       let cmd = '& ' . cmd
     endif
     endif
     return [batchfile, cmd]
     return [batchfile, cmd]
@@ -575,7 +714,7 @@ function! plug#(repo, ...)
   try
   try
     let repo = s:trim(a:repo)
     let repo = s:trim(a:repo)
     let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
     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)
     let spec = extend(s:infer_properties(name, repo), opts)
     if !has_key(g:plugs, name)
     if !has_key(g:plugs, name)
       call add(g:plugs_order, name)
       call add(g:plugs_order, name)
@@ -583,19 +722,41 @@ function! plug#(repo, ...)
     let g:plugs[name] = spec
     let g:plugs[name] = spec
     let s:loaded[name] = get(s:loaded, name, 0)
     let s:loaded[name] = get(s:loaded, name, 0)
   catch
   catch
-    return s:err(v:exception)
+    return s:err(repo . ' ' . v:exception)
   endtry
   endtry
 endfunction
 endfunction
 
 
 function! s:parse_options(arg)
 function! s:parse_options(arg)
   let opts = copy(s:base_spec)
   let opts = copy(s:base_spec)
   let type = type(a:arg)
   let type = type(a:arg)
+  let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)'
   if type == s:TYPE.string
   if type == s:TYPE.string
+    if empty(a:arg)
+      throw printf(opt_errfmt, 'tag', 'string')
+    endif
     let opts.tag = a:arg
     let opts.tag = a:arg
   elseif type == s:TYPE.dict
   elseif type == s:TYPE.dict
+    for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as']
+      if has_key(a:arg, opt)
+      \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
+        throw printf(opt_errfmt, opt, 'string')
+      endif
+    endfor
+    for opt in ['on', 'for']
+      if has_key(a:arg, opt)
+      \ && type(a:arg[opt]) != s:TYPE.list
+      \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
+        throw printf(opt_errfmt, opt, 'string or list')
+      endif
+    endfor
+    if has_key(a:arg, 'do')
+      \ && type(a:arg.do) != s:TYPE.funcref
+      \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do))
+        throw printf(opt_errfmt, 'do', 'string or funcref')
+    endif
     call extend(opts, a:arg)
     call extend(opts, a:arg)
     if has_key(opts, 'dir')
     if has_key(opts, 'dir')
-      let opts.dir = s:dirpath(expand(opts.dir))
+      let opts.dir = s:dirpath(s:plug_expand(opts.dir))
     endif
     endif
   else
   else
     throw 'Invalid argument type (expected: string or dictionary)'
     throw 'Invalid argument type (expected: string or dictionary)'
@@ -606,7 +767,7 @@ endfunction
 function! s:infer_properties(name, repo)
 function! s:infer_properties(name, repo)
   let repo = a:repo
   let repo = a:repo
   if s:is_local_plug(repo)
   if s:is_local_plug(repo)
-    return { 'dir': s:dirpath(expand(repo)) }
+    return { 'dir': s:dirpath(s:plug_expand(repo)) }
   else
   else
     if repo =~ ':'
     if repo =~ ':'
       let uri = repo
       let uri = repo
@@ -649,7 +810,7 @@ function! s:syntax()
   syn match plugNumber /[0-9]\+[0-9.]*/ contained
   syn match plugNumber /[0-9]\+[0-9.]*/ contained
   syn match plugBracket /[[\]]/ contained
   syn match plugBracket /[[\]]/ contained
   syn match plugX /x/ contained
   syn match plugX /x/ contained
-  syn match plugDash /^-/
+  syn match plugDash /^-\{1}\ /
   syn match plugPlus /^+/
   syn match plugPlus /^+/
   syn match plugStar /^*/
   syn match plugStar /^*/
   syn match plugMessage /\(^- \)\@<=.*/
   syn match plugMessage /\(^- \)\@<=.*/
@@ -667,6 +828,7 @@ function! s:syntax()
   syn match plugError /^x.*/
   syn match plugError /^x.*/
   syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
   syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
   syn match plugH2 /^.*:\n-\+$/
   syn match plugH2 /^.*:\n-\+$/
+  syn match plugH2 /^-\{2,}/
   syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
   syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
   hi def link plug1       Title
   hi def link plug1       Title
   hi def link plug2       Repeat
   hi def link plug2       Repeat
@@ -759,7 +921,7 @@ function! s:finish_bindings()
 endfunction
 endfunction
 
 
 function! s:prepare(...)
 function! s:prepare(...)
-  if empty(getcwd())
+  if empty(s:plug_getcwd())
     throw 'Invalid current working directory. Cannot proceed.'
     throw 'Invalid current working directory. Cannot proceed.'
   endif
   endif
 
 
@@ -779,7 +941,7 @@ function! s:prepare(...)
     call s:new_window()
     call s:new_window()
   endif
   endif
 
 
-  nnoremap <silent> <buffer> q  :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
+  nnoremap <silent> <buffer> q :call <SID>close_pane()<cr>
   if a:0 == 0
   if a:0 == 0
     call s:finish_bindings()
     call s:finish_bindings()
   endif
   endif
@@ -801,6 +963,15 @@ function! s:prepare(...)
   endif
   endif
 endfunction
 endfunction
 
 
+function! s:close_pane()
+  if b:plug_preview == 1
+    pc
+    let b:plug_preview = -1
+  else
+    bd
+  endif
+endfunction
+
 function! s:assign_name()
 function! s:assign_name()
   " Assign buffer name
   " Assign buffer name
   let prefix = '[Plugins]'
   let prefix = '[Plugins]'
@@ -815,8 +986,15 @@ endfunction
 
 
 function! s:chsh(swap)
 function! s:chsh(swap)
   let prev = [&shell, &shellcmdflag, &shellredir]
   let prev = [&shell, &shellcmdflag, &shellredir]
-  if !s:is_win && a:swap
-    set shell=sh shellredir=>%s\ 2>&1
+  if !s:is_win
+    set shell=sh
+  endif
+  if a:swap
+    if s:is_powershell(&shell)
+      let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
+    elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$'
+      set shellredir=>%s\ 2>&1
+    endif
   endif
   endif
   return prev
   return prev
 endfunction
 endfunction
@@ -849,7 +1027,7 @@ function! s:regress_bar()
 endfunction
 endfunction
 
 
 function! s:is_updated(dir)
 function! s:is_updated(dir)
-  return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir))
+  return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir))
 endfunction
 endfunction
 
 
 function! s:do(pull, force, todo)
 function! s:do(pull, force, todo)
@@ -886,6 +1064,7 @@ function! s:do(pull, force, todo)
         endif
         endif
       elseif type == s:TYPE.funcref
       elseif type == s:TYPE.funcref
         try
         try
+          call s:load_plugin(spec)
           let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
           let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
           call spec.do({ 'name': name, 'status': status, 'force': a:force })
           call spec.do({ 'name': name, 'status': status, 'force': a:force })
         catch
         catch
@@ -912,10 +1091,11 @@ endfunction
 
 
 function! s:checkout(spec)
 function! s:checkout(spec)
   let sha = a:spec.commit
   let sha = a:spec.commit
-  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:git_revision(a:spec.dir)
+  if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
+    let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
     let output = s:system(
     let output = s:system(
-          \ 'git fetch --depth 999999 && git checkout '.s:esc(sha).' --', a:spec.dir)
+          \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
   endif
   endif
   return output
   return output
 endfunction
 endfunction
@@ -1030,11 +1210,17 @@ function! s:update_impl(pull, force, args) abort
   normal! 2G
   normal! 2G
   silent! redraw
   silent! redraw
 
 
-  let s:clone_opt = get(g:, 'plug_shallow', 1) ?
-        \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : ''
+  " Set remote name, overriding a possible user git config's clone.defaultRemoteName
+  let s:clone_opt = ['--origin', 'origin']
+  if get(g:, 'plug_shallow', 1)
+    call extend(s:clone_opt, ['--depth', '1'])
+    if s:git_version_requirement(1, 7, 10)
+      call add(s:clone_opt, '--no-single-branch')
+    endif
+  endif
 
 
   if has('win32unix') || has('wsl')
   if has('win32unix') || has('wsl')
-    let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input'
+    call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input'])
   endif
   endif
 
 
   let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : ''
   let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : ''
@@ -1120,12 +1306,12 @@ function! s:update_finish()
           endif
           endif
         endif
         endif
         call s:log4(name, 'Checking out '.tag)
         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
       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 = s:git_origin_branch(spec)
+        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
       endif
       if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
       if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
             \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
             \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
@@ -1169,7 +1355,7 @@ function! s:job_abort()
       silent! call job_stop(j.jobid)
       silent! call job_stop(j.jobid)
     endif
     endif
     if j.new
     if j.new
-      call s:system('rm -rf ' . plug#shellescape(g:plugs[name].dir))
+      call s:rm_rf(g:plugs[name].dir)
     endif
     endif
   endfor
   endfor
   let s:jobs = {}
   let s:jobs = {}
@@ -1215,7 +1401,7 @@ function! s:job_cb(fn, job, ch, data)
 endfunction
 endfunction
 
 
 function! s:nvim_cb(job_id, data, event) dict abort
 function! s:nvim_cb(job_id, data, event) dict abort
-  return a:event == 'stdout' ?
+  return (a:event == 'stdout' || a:event == 'stderr') ?
     \ s:job_cb('s:job_out_cb',  self, 0, join(a:data, "\n")) :
     \ s:job_cb('s:job_out_cb',  self, 0, join(a:data, "\n")) :
     \ s:job_cb('s:job_exit_cb', self, 0, a:data)
     \ s:job_cb('s:job_exit_cb', self, 0, a:data)
 endfunction
 endfunction
@@ -1224,15 +1410,18 @@ function! s:spawn(name, cmd, opts)
   let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
   let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
             \ 'new': get(a:opts, 'new', 0) }
             \ 'new': get(a:opts, 'new', 0) }
   let s:jobs[a:name] = job
   let s:jobs[a:name] = job
-  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
   if s:nvim
+    if has_key(a:opts, 'dir')
+      let job.cwd = a:opts.dir
+    endif
+    let argv = a:cmd
     call extend(job, {
     call extend(job, {
     \ 'on_stdout': function('s:nvim_cb'),
     \ 'on_stdout': function('s:nvim_cb'),
+    \ 'on_stderr': function('s:nvim_cb'),
     \ 'on_exit':   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
     if jid > 0
       let job.jobid = jid
       let job.jobid = jid
     else
     else
@@ -1242,9 +1431,16 @@ function! s:spawn(name, cmd, opts)
             \ 'Invalid arguments (or job table is full)']
             \ 'Invalid arguments (or job table is full)']
     endif
     endif
   elseif s:vim8
   elseif s:vim8
+    let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})'))
+    if has_key(a:opts, 'dir')
+      let cmd = s:with_cd(cmd, a:opts.dir, 0)
+    endif
+    let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd]
     let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
     let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
     \ 'out_cb':   function('s:job_cb', ['s:job_out_cb',  job]),
     \ 'out_cb':   function('s:job_cb', ['s:job_out_cb',  job]),
+    \ 'err_cb':   function('s:job_cb', ['s:job_out_cb',  job]),
     \ 'exit_cb':  function('s:job_cb', ['s:job_exit_cb', job]),
     \ 'exit_cb':  function('s:job_cb', ['s:job_exit_cb', job]),
+    \ 'err_mode': 'raw',
     \ 'out_mode': 'raw'
     \ 'out_mode': 'raw'
     \})
     \})
     if job_status(jid) == 'run'
     if job_status(jid) == 'run'
@@ -1255,7 +1451,7 @@ function! s:spawn(name, cmd, opts)
       let job.lines   = ['Failed to start job']
       let job.lines   = ['Failed to start job']
     endif
     endif
   else
   else
-    let job.lines = s:lines(call('s:system', [cmd]))
+    let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd]))
     let job.error = v:shell_error != 0
     let job.error = v:shell_error != 0
     let job.running = 0
     let job.running = 0
   endif
   endif
@@ -1289,9 +1485,10 @@ function! s:bar()
 endfunction
 endfunction
 
 
 function! s:logpos(name)
 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.':'
     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) !~ '^ '
         if getline(j) !~ '^ '
           return [i, j - 1]
           return [i, j - 1]
         endif
         endif
@@ -1351,8 +1548,14 @@ while 1 " Without TCO, Vim stack is bound to explode
     let [error, _] = s:git_validate(spec, 0)
     let [error, _] = s:git_validate(spec, 0)
     if empty(error)
     if empty(error)
       if pull
       if pull
-        let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
-        call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir })
+        let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch']
+        if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
+          call extend(cmd, ['--depth', '99999999'])
+        endif
+        if !empty(prog)
+          call add(cmd, prog)
+        endif
+        call s:spawn(name, cmd, { 'dir': spec.dir })
       else
       else
         let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
         let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
       endif
       endif
@@ -1360,12 +1563,14 @@ while 1 " Without TCO, Vim stack is bound to explode
       let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
       let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
     endif
     endif
   else
   else
-    call s:spawn(name,
-          \ printf('git clone %s %s %s %s 2>&1',
-          \ has_tag ? '' : s:clone_opt,
-          \ prog,
-          \ plug#shellescape(spec.uri, {'script': 0}),
-          \ plug#shellescape(s:trim(spec.dir), {'script': 0})), { 'new': 1 })
+    let cmd = ['git', 'clone']
+    if !has_tag
+      call extend(cmd, s:clone_opt)
+    endif
+    if !empty(prog)
+      call add(cmd, prog)
+    endif
+    call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 })
   endif
   endif
 
 
   if !s:jobs[name].running
   if !s:jobs[name].running
@@ -1402,7 +1607,7 @@ G_NVIM = vim.eval("has('nvim')") == '1'
 G_PULL = vim.eval('s:update.pull') == '1'
 G_PULL = vim.eval('s:update.pull') == '1'
 G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
 G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
 G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
 G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
-G_CLONE_OPT = vim.eval('s:clone_opt')
+G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt'))
 G_PROGRESS = vim.eval('s:progress_opt(1)')
 G_PROGRESS = vim.eval('s:progress_opt(1)')
 G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
 G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
 G_STOP = thr.Event()
 G_STOP = thr.Event()
@@ -1939,7 +2144,7 @@ function! s:update_ruby()
     end
     end
   } if VIM::evaluate('s:mac_gui') == 1
   } if VIM::evaluate('s:mac_gui') == 1
 
 
-  clone_opt = VIM::evaluate('s:clone_opt')
+  clone_opt = VIM::evaluate('s:clone_opt').join(' ')
   progress = VIM::evaluate('s:progress_opt(1)')
   progress = VIM::evaluate('s:progress_opt(1)')
   nthr.times do
   nthr.times do
     mtx.synchronize do
     mtx.synchronize do
@@ -2001,16 +2206,36 @@ function! s:shellesc_ps1(arg)
   return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
   return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'"
 endfunction
 endfunction
 
 
+function! s:shellesc_sh(arg)
+  return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'"
+endfunction
+
+" Escape the shell argument based on the shell.
+" Vim and Neovim's shellescape() are insufficient.
+" 1. shellslash determines whether to use single/double quotes.
+"    Double-quote escaping is fragile for cmd.exe.
+" 2. It does not work for powershell.
+" 3. It does not work for *sh shells if the command is executed
+"    via cmd.exe (ie. cmd.exe /c sh -c command command_args)
+" 4. It does not support batchfile syntax.
+"
+" Accepts an optional dictionary with the following keys:
+" - shell: same as Vim/Neovim 'shell' option.
+"          If unset, fallback to 'cmd.exe' on Windows or 'sh'.
+" - script: If truthy and shell is cmd.exe, escape for batchfile syntax.
 function! plug#shellescape(arg, ...)
 function! plug#shellescape(arg, ...)
+  if a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
+    return a:arg
+  endif
   let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
   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 shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
   let script = get(opts, 'script', 1)
   let script = get(opts, 'script', 1)
-  if shell =~# 'cmd\.exe$'
+  if shell =~# 'cmd\(\.exe\)\?$'
     return s:shellesc_cmd(a:arg, script)
     return s:shellesc_cmd(a:arg, script)
-  elseif shell =~# 'powershell\.exe$' || shell =~# 'pwsh$'
+  elseif s:is_powershell(shell)
     return s:shellesc_ps1(a:arg)
     return s:shellesc_ps1(a:arg)
   endif
   endif
-  return shellescape(a:arg)
+  return s:shellesc_sh(a:arg)
 endfunction
 endfunction
 
 
 function! s:glob_dir(path)
 function! s:glob_dir(path)
@@ -2051,8 +2276,24 @@ function! s:system(cmd, ...)
   let batchfile = ''
   let batchfile = ''
   try
   try
     let [sh, shellcmdflag, shrd] = s:chsh(1)
     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
+    if type(a:cmd) == s:TYPE.list
+      " Neovim's system() supports list argument to bypass the shell
+      " but it cannot set the working directory for the command.
+      " Assume that the command does not rely on the shell.
+      if has('nvim') && a:0 == 0
+        return system(a:cmd)
+      endif
+      let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
+      if s:is_powershell(&shell)
+        let cmd = '& ' . cmd
+      endif
+    else
+      let cmd = a:cmd
+    endif
+    if a:0 > 0
+      let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list)
+    endif
+    if s:is_win && type(a:cmd) != s:TYPE.list
       let [batchfile, cmd] = s:batchfile(cmd)
       let [batchfile, cmd] = s:batchfile(cmd)
     endif
     endif
     return system(cmd)
     return system(cmd)
@@ -2072,18 +2313,17 @@ endfunction
 function! s:git_validate(spec, check_branch)
 function! s:git_validate(spec, check_branch)
   let err = ''
   let err = ''
   if isdirectory(a:spec.dir)
   if isdirectory(a:spec.dir)
-    let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir))
+    let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)]
     let remote = result[-1]
     let remote = result[-1]
-    if v:shell_error
+    if empty(remote)
       let err = join([remote, 'PlugClean required.'], "\n")
       let err = join([remote, 'PlugClean required.'], "\n")
     elseif !s:compare_git_uri(remote, a:spec.uri)
     elseif !s:compare_git_uri(remote, a:spec.uri)
       let err = join(['Invalid URI: '.remote,
       let err = join(['Invalid URI: '.remote,
                     \ 'Expected:    '.a:spec.uri,
                     \ 'Expected:    '.a:spec.uri,
                     \ 'PlugClean required.'], "\n")
                     \ 'PlugClean required.'], "\n")
     elseif a:check_branch && has_key(a:spec, 'commit')
     elseif a:check_branch && has_key(a:spec, 'commit')
-      let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
-      let sha = result[-1]
-      if v:shell_error
+      let sha = s:git_revision(a:spec.dir)
+      if empty(sha)
         let err = join(add(result, 'PlugClean required.'), "\n")
         let err = join(add(result, 'PlugClean required.'), "\n")
       elseif !s:hash_match(sha, a:spec.commit)
       elseif !s:hash_match(sha, a:spec.commit)
         let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
         let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
@@ -2091,8 +2331,9 @@ function! s:git_validate(spec, check_branch)
                       \ 'PlugUpdate required.'], "\n")
                       \ 'PlugUpdate required.'], "\n")
       endif
       endif
     elseif a:check_branch
     elseif a:check_branch
-      let branch = result[0]
+      let current_branch = result[0]
       " Check tag
       " Check tag
+      let origin_branch = s:git_origin_branch(a:spec)
       if has_key(a:spec, 'tag')
       if has_key(a:spec, 'tag')
         let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
         let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
         if a:spec.tag !=# tag && a:spec.tag !~ '\*'
         if a:spec.tag !=# tag && a:spec.tag !~ '\*'
@@ -2100,25 +2341,26 @@ function! s:git_validate(spec, check_branch)
                 \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
                 \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
         endif
         endif
       " Check branch
       " Check branch
-      elseif a:spec.branch !=# branch
+      elseif origin_branch !=# current_branch
         let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
         let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
-              \ branch, a:spec.branch)
+              \ current_branch, origin_branch)
       endif
       endif
       if empty(err)
       if empty(err)
-        let [ahead, behind] = split(s:lastline(s:system(printf(
-              \ 'git rev-list --count --left-right HEAD...origin/%s',
-              \ a:spec.branch), a:spec.dir)), '\t')
+        let [ahead, behind] = split(s:lastline(s:system([
+        \ 'git', 'rev-list', '--count', '--left-right',
+        \ printf('HEAD...origin/%s', origin_branch)
+        \ ], a:spec.dir)), '\t')
         if !v:shell_error && ahead
         if !v:shell_error && ahead
           if behind
           if behind
             " Only mention PlugClean if diverged, otherwise it's likely to be
             " Only mention PlugClean if diverged, otherwise it's likely to be
             " pushable (and probably not that messed up).
             " pushable (and probably not that messed up).
             let err = printf(
             let err = printf(
                   \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
                   \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
-                  \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
+                  \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind)
           else
           else
             let err = printf("Ahead of origin/%s by %d commit(s).\n"
             let err = printf("Ahead of origin/%s by %d commit(s).\n"
                   \ .'Cannot update until local changes are pushed.',
                   \ .'Cannot update until local changes are pushed.',
-                  \ a:spec.branch, ahead)
+                  \ origin_branch, ahead)
           endif
           endif
         endif
         endif
       endif
       endif
@@ -2131,7 +2373,9 @@ endfunction
 
 
 function! s:rm_rf(dir)
 function! s:rm_rf(dir)
   if isdirectory(a:dir)
   if isdirectory(a:dir)
-    call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . plug#shellescape(a:dir))
+    return s:system(s:is_win
+    \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
+    \ : ['rm', '-rf', a:dir])
   endif
   endif
 endfunction
 endfunction
 
 
@@ -2163,7 +2407,7 @@ function! s:clean(force)
 
 
   let allowed = {}
   let allowed = {}
   for dir in dirs
   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
     let allowed[dir] = 1
     for child in s:glob_dir(dir)
     for child in s:glob_dir(dir)
       let allowed[child] = 1
       let allowed[child] = 1
@@ -2213,6 +2457,7 @@ endfunction
 function! s:delete(range, force)
 function! s:delete(range, force)
   let [l1, l2] = a:range
   let [l1, l2] = a:range
   let force = a:force
   let force = a:force
+  let err_count = 0
   while l1 <= l2
   while l1 <= l2
     let line = getline(l1)
     let line = getline(l1)
     if line =~ '^- ' && isdirectory(line[2:])
     if line =~ '^- ' && isdirectory(line[2:])
@@ -2221,11 +2466,22 @@ function! s:delete(range, force)
       let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
       let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
       let force = force || answer > 1
       let force = force || answer > 1
       if answer
       if answer
-        call s:rm_rf(line[2:])
+        let err = s:rm_rf(line[2:])
         setlocal modifiable
         setlocal modifiable
-        call setline(l1, '~'.line[1:])
-        let s:clean_count += 1
-        call setline(4, printf('Removed %d directories.', s:clean_count))
+        if empty(err)
+          call setline(l1, '~'.line[1:])
+          let s:clean_count += 1
+        else
+          delete _
+          call append(l1 - 1, s:format_message('x', line[1:], err))
+          let l2 += len(s:lines(err))
+          let err_count += 1
+        endif
+        let msg = printf('Removed %d directories.', s:clean_count)
+        if err_count > 0
+          let msg .= printf(' Failed to remove %d directories.', err_count)
+        endif
+        call setline(4, msg)
         setlocal nomodifiable
         setlocal nomodifiable
       endif
       endif
     endif
     endif
@@ -2236,11 +2492,11 @@ endfunction
 function! s:upgrade()
 function! s:upgrade()
   echo 'Downloading the latest version of vim-plug'
   echo 'Downloading the latest version of vim-plug'
   redraw
   redraw
-  let tmp = tempname()
+  let tmp = s:plug_tempname()
   let new = tmp . '/plug.vim'
   let new = tmp . '/plug.vim'
 
 
   try
   try
-    let out = s:system(printf('git clone --depth 1 %s %s', plug#shellescape(s:plug_src), plug#shellescape(tmp)))
+    let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp])
     if v:shell_error
     if v:shell_error
       return s:err('Error upgrading vim-plug: '. out)
       return s:err('Error upgrading vim-plug: '. out)
     endif
     endif
@@ -2365,26 +2621,34 @@ function! s:preview_commit()
 
 
   let sha = matchstr(getline('.'), '^  \X*\zs[0-9a-f]\{7,9}')
   let sha = matchstr(getline('.'), '^  \X*\zs[0-9a-f]\{7,9}')
   if empty(sha)
   if empty(sha)
-    return
+    let name = matchstr(getline('.'), '^- \zs[^:]*\ze:$')
+    if empty(name)
+      return
+    endif
+    let title = 'HEAD@{1}..'
+    let command = 'git diff --no-color HEAD@{1}'
+  else
+    let title = sha
+    let command = 'git show --no-color --pretty=medium '.sha
+    let name = s:find_name(line('.'))
   endif
   endif
 
 
-  let name = s:find_name(line('.'))
   if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
   if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
     return
     return
   endif
   endif
 
 
   if exists('g:plug_pwindow') && !s:is_preview_window_open()
   if exists('g:plug_pwindow') && !s:is_preview_window_open()
     execute g:plug_pwindow
     execute g:plug_pwindow
-    execute 'e' sha
+    execute 'e' title
   else
   else
-    execute 'pedit' sha
+    execute 'pedit' title
     wincmd P
     wincmd P
   endif
   endif
-  setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable
+  setlocal previewwindow filetype=git buftype=nofile bufhidden=wipe nobuflisted modifiable
   let batchfile = ''
   let batchfile = ''
   try
   try
     let [sh, shellcmdflag, shrd] = s:chsh(1)
     let [sh, shellcmdflag, shrd] = s:chsh(1)
-    let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+    let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && '.command
     if s:is_win
     if s:is_win
       let [batchfile, cmd] = s:batchfile(cmd)
       let [batchfile, cmd] = s:batchfile(cmd)
     endif
     endif
@@ -2434,16 +2698,23 @@ function! s:diff()
     endif
     endif
     call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
     call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
     for [k, v] in plugs
     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], 'plug#shellescape(v:val)'))
-      if has_key(v, 'rtp')
-        let cmd .= ' -- '.plug#shellescape(v.rtp)
-      endif
-      let diff = s:system_chomp(cmd, v.dir)
-      if !empty(diff)
-        let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
-        call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
-        let cnts[origin] += 1
+      let branch = s:git_origin_branch(v)
+      if len(branch)
+        let range = origin ? '..origin/'.branch : 'HEAD@{1}..'
+        let cmd = ['git', 'log', '--graph', '--color=never']
+        if s:git_version_requirement(2, 10, 0)
+          call add(cmd, '--no-show-signature')
+        endif
+        call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
+        if has_key(v, 'rtp')
+          call extend(cmd, ['--', v.rtp])
+        endif
+        let diff = s:system_chomp(cmd, v.dir)
+        if !empty(diff)
+          let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
+          call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
+          let cnts[origin] += 1
+        endif
       endif
       endif
       let bar .= '='
       let bar .= '='
       call s:progress_bar(2, bar, len(total))
       call s:progress_bar(2, bar, len(total))
@@ -2485,7 +2756,7 @@ function! s:revert()
     return
     return
   endif
   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
   setlocal modifiable
   normal! "_dap
   normal! "_dap
   setlocal nomodifiable
   setlocal nomodifiable
@@ -2503,9 +2774,9 @@ function! s:snapshot(force, ...) abort
   1
   1
   let anchor = line('$') - 3
   let anchor = line('$') - 3
   let names = sort(keys(filter(copy(g:plugs),
   let names = sort(keys(filter(copy(g:plugs),
-        \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
+        \'has_key(v:val, "uri") && isdirectory(v:val.dir)')))
   for name in reverse(names)
   for name in reverse(names)
-    let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir)
+    let sha = has_key(g:plugs[name], 'commit') ? g:plugs[name].commit : s:git_revision(g:plugs[name].dir)
     if !empty(sha)
     if !empty(sha)
       call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
       call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
       redraw
       redraw
@@ -2513,7 +2784,7 @@ function! s:snapshot(force, ...) abort
   endfor
   endfor
 
 
   if a:0 > 0
   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?'))
     if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
       return
       return
     endif
     endif

+ 6 - 1
vimrc

@@ -585,6 +585,8 @@ let g:jedi#goto_command = "<leader>]"
 " }}}
 " }}}
 " {{{ ##### vim-go #####
 " {{{ ##### vim-go #####
 let g:go_def_mapping_enabled = 0
 let g:go_def_mapping_enabled = 0
+let g:go_fmt_experimental = 1  " retains folds on save (For some reason) https://github.com/fatih/vim-go/issues/502
+
 augroup vimgosettings
 augroup vimgosettings
   " autocmd FileType go nmap <leader>] :GoDef<CR>
   " autocmd FileType go nmap <leader>] :GoDef<CR>
   au FileType go nmap <F4> <Plug>(go-test)
   au FileType go nmap <F4> <Plug>(go-test)
@@ -605,6 +607,8 @@ nmap <silent> gr <Plug>(coc-references)
 " Use K to show documentation in preview window
 " Use K to show documentation in preview window
 nnoremap <silent> K :call <SID>show_documentation()<CR>
 nnoremap <silent> K :call <SID>show_documentation()<CR>
 
 
+let g:coc_node_path = '/snap/bin/node'
+
 function! s:show_documentation()
 function! s:show_documentation()
   if (index(['vim','help'], &filetype) >= 0)
   if (index(['vim','help'], &filetype) >= 0)
     execute 'h '.expand('<cword>')
     execute 'h '.expand('<cword>')
@@ -621,6 +625,7 @@ nmap <leader>r <Plug>(coc-rename)
 " }}}
 " }}}
 " {{{ ##### instant-markdown-viewer #####
 " {{{ ##### instant-markdown-viewer #####
 " let g:instant_markdown_python = 1
 " let g:instant_markdown_python = 1
+let g:instant_markdown_autostart = 0
 " }}}
 " }}}
 
 
 " }}}
 " }}}
@@ -923,7 +928,7 @@ nnoremap <leader>O O<ESC>
 nnoremap <leader>c cc<ESC>
 nnoremap <leader>c cc<ESC>
 
 
 " Exit insert mode and put ';' at the end of the line
 " Exit insert mode and put ';' at the end of the line
-inoremap ;<Esc> <Esc>A;<Esc>
+" inoremap ;<Esc> <Esc>A;<Esc>
 
 
 " Open tag in new tab
 " Open tag in new tab
 nnoremap <M-]> <C-W><C-]><C-W>T
 nnoremap <M-]> <C-W><C-]><C-W>T