From 668bc0fd2a2a6ec2076a3b49a5031177a92df31b Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 10 Apr 2020 15:40:28 -0400 Subject: [PATCH 01/13] Support list type command for s:system to reduce batchfiles on Windows (#956) * s:system supports list type for command Objective is to reduce batchfiles on Windows. List type gives more flexibility on s:system() on how to pass the shell command to the builtin system(). If system() supports list type for command and there is no working directory, run it directly on system(). Targets Neovim only. Else, convert the list to an escaped command so that the user's shell can execute it. Neovim's system() does not support working directory system() so consider refactoring s:system to use a synchronous job. * Do not escape simple shell arguments Regexp taken from vim-fugitive s:shellesc(). * Set shellredir on Windows Prep to use list type for command passed to s:system() within s:spawn() * Internalize shellredir for s:spawn s:spawn needs to redirect stderr to stdout for jobs callbacks but s:system (for old Vim versions) sets shellredir if needed. * Leverage job api for cwd and stderr Vim/Neovim support stderr redirection and support error callbacks. Vim 8 and Neovim can set a job's working directory via 'cwd' key but it cannot be used as is on Vim because CI fails for the Vim release in Ubuntu Bionic and the latest Vim release. --- plug.vim | 97 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/plug.vim b/plug.vim index ac14332..1471735 100644 --- a/plug.vim +++ b/plug.vim @@ -372,7 +372,7 @@ endfunction function! s:git_version_requirement(...) 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 return s:version_requirement(s:git_version, a:000) endfunction @@ -864,8 +864,15 @@ endfunction function! s:chsh(swap) 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 &shell =~# 'powershell\.exe' || &shell =~# 'pwsh$' + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' + elseif &shell =~# 'sh' || &shell =~# 'cmd\.exe' + set shellredir=>%s\ 2>&1 + endif endif return prev endfunction @@ -898,7 +905,7 @@ function! s:regress_bar() endfunction 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 function! s:do(pull, force, todo) @@ -961,7 +968,7 @@ endfunction function! s:checkout(spec) let sha = a:spec.commit - let output = s:system('git rev-parse HEAD', a:spec.dir) + 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 '.plug#shellescape(sha).' --', a:spec.dir) @@ -1264,7 +1271,7 @@ function! s:job_cb(fn, job, ch, data) endfunction 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_exit_cb', self, 0, a:data) endfunction @@ -1273,12 +1280,15 @@ function! s:spawn(name, cmd, opts) let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], \ '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, 0) : a:cmd - let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.a:cmd.'"'] : ['sh', '-c', a:cmd] call extend(job, { \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), \ 'on_exit': function('s:nvim_cb'), \ }) let jid = s:plug_call('jobstart', argv, job) @@ -1291,9 +1301,13 @@ function! s:spawn(name, cmd, opts) \ 'Invalid arguments (or job table is full)'] endif elseif s:vim8 + 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] let jid = job_start(s:is_win ? join(argv, ' ') : argv, { \ '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]), + \ 'err_mode': 'raw', \ 'out_mode': 'raw' \}) if job_status(jid) == 'run' @@ -1304,7 +1318,7 @@ function! s:spawn(name, cmd, opts) let job.lines = ['Failed to start job'] endif 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.running = 0 endif @@ -1402,7 +1416,7 @@ while 1 " Without TCO, Vim stack is bound to explode if empty(error) 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 }) + call s:spawn(name, printf('git fetch %s %s', fetch_opt, prog), { 'dir': spec.dir }) else let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } endif @@ -1411,7 +1425,7 @@ while 1 " Without TCO, Vim stack is bound to explode endif else call s:spawn(name, - \ printf('git clone %s %s %s %s 2>&1', + \ printf('git clone %s %s %s %s', \ has_tag ? '' : s:clone_opt, \ prog, \ plug#shellescape(spec.uri, {'script': 0}), @@ -2055,7 +2069,23 @@ 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, ...) + 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 shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') let script = get(opts, 'script', 1) @@ -2105,8 +2135,24 @@ 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 + 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 &shell =~# 'powershell\.exe' + 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) endif return system(cmd) @@ -2159,9 +2205,10 @@ function! s:git_validate(spec, check_branch) \ branch, a:spec.branch) endif 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', a:spec.branch) + \ ], a:spec.dir)), '\t') if !v:shell_error && ahead if behind " Only mention PlugClean if diverged, otherwise it's likely to be @@ -2185,7 +2232,9 @@ endfunction function! s:rm_rf(dir) if isdirectory(a:dir) - call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . plug#shellescape(a:dir)) + call s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) endif endfunction @@ -2294,7 +2343,7 @@ function! s:upgrade() let new = tmp . '/plug.vim' 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 return s:err('Error upgrading vim-plug: '. out) endif @@ -2489,11 +2538,13 @@ 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 ' - \ . (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)')) + 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') - let cmd .= ' -- '.plug#shellescape(v.rtp) + call extend(cmd, ['--', v.rtp]) endif let diff = s:system_chomp(cmd, v.dir) if !empty(diff) @@ -2561,7 +2612,7 @@ function! s:snapshot(force, ...) abort let names = sort(keys(filter(copy(g:plugs), \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) for name in reverse(names) - let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) + let sha = s:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir) if !empty(sha) call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) redraw From e718868e85e2a32410144dfcdc3ba1303719450d Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 11 Apr 2020 10:49:47 -0400 Subject: [PATCH 02/13] Use list type for command in s:spawn() (#959) This allows Neovim to bypass the shell and run git directly. Vim still needs the shell because of how commands are collapsed on Windows and because setting the job's working directory via "cwd" does not work. Refactored s:clone_opt to a list to make this possible. --- plug.vim | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/plug.vim b/plug.vim index 1471735..bc02f03 100644 --- a/plug.vim +++ b/plug.vim @@ -1086,11 +1086,16 @@ function! s:update_impl(pull, force, args) abort normal! 2G silent! redraw - let s:clone_opt = get(g:, 'plug_shallow', 1) ? - \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' + let s:clone_opt = [] + 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') - 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 let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' @@ -1285,7 +1290,7 @@ function! s:spawn(name, cmd, opts) if has_key(a:opts, 'dir') let job.cwd = a:opts.dir endif - let argv = s:is_win ? ['cmd', '/s', '/c', '"'.a:cmd.'"'] : ['sh', '-c', a:cmd] + let argv = a:cmd call extend(job, { \ 'on_stdout': function('s:nvim_cb'), \ 'on_stderr': function('s:nvim_cb'), @@ -1301,7 +1306,10 @@ function! s:spawn(name, cmd, opts) \ 'Invalid arguments (or job table is full)'] endif elseif s:vim8 - let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir, 0) : a:cmd + 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, { \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), @@ -1415,8 +1423,14 @@ while 1 " Without TCO, Vim stack is bound to explode let [error, _] = s:git_validate(spec, 0) if empty(error) if pull - let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' - call s:spawn(name, printf('git fetch %s %s', fetch_opt, prog), { 'dir': spec.dir }) + let cmd = ['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 let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } endif @@ -1424,12 +1438,14 @@ while 1 " Without TCO, Vim stack is bound to explode let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } endif else - call s:spawn(name, - \ printf('git clone %s %s %s %s', - \ 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 if !s:jobs[name].running @@ -1466,7 +1482,7 @@ G_NVIM = vim.eval("has('nvim')") == '1' G_PULL = vim.eval('s:update.pull') == '1' G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 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_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) G_STOP = thr.Event() @@ -2003,7 +2019,7 @@ function! s:update_ruby() end } 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)') nthr.times do mtx.synchronize do From 54d837fa543678314f33c325d96f4e18b1c0892e Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 3 May 2020 16:57:21 +0900 Subject: [PATCH 03/13] Load plugin before running funcref hook Fix #964 Fix https://github.com/junegunn/fzf.vim/issues/1008 --- plug.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/plug.vim b/plug.vim index bc02f03..25be27f 100644 --- a/plug.vim +++ b/plug.vim @@ -942,6 +942,7 @@ function! s:do(pull, force, todo) endif elseif type == s:TYPE.funcref try + call s:load_plugin(spec) let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') call spec.do({ 'name': name, 'status': status, 'force': a:force }) catch From 0862a76fdd89d0b3ffb951be367111510e1f2410 Mon Sep 17 00:00:00 2001 From: Harshad Srinivasan Date: Tue, 5 May 2020 18:28:53 -0700 Subject: [PATCH 04/13] Updated readme (#967) Co-authored-by: Harshad Srinivasan --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bad081d..1aa63bb 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ $uri = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' (New-Object Net.WebClient).DownloadFile( $uri, $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( - "~\AppData\Local\nvim\autoload\plug.vim" + "~\AppData\Local\nvim-data\site\autoload\plug.vim" ) ) ``` From 8846bc6af14d105d3c1121bb5c42fb2efc4dfe9e Mon Sep 17 00:00:00 2001 From: Stefano Date: Wed, 6 May 2020 01:54:46 +0000 Subject: [PATCH 05/13] Add Flatpak installation instructions to README.md (#846) Tested with https://flathub.org/apps/details/io.neovim.nvim --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aa63bb..5732b94 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,20 @@ $uri = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' #### Neovim -###### Unix +###### Unix, Linux ```sh curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim ``` +###### Linux (Flatpak) + +```sh +curl -fLo ~/.var/app/io.neovim.nvim/data/nvim/site/autoload/plug.vim \ + https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +``` + ###### Windows (PowerShell) ```powershell From 71c41fccf5ca42081d4d49aa1ea2f71c694bc4cf Mon Sep 17 00:00:00 2001 From: Gianluca Recchia Date: Wed, 6 May 2020 12:57:50 +0200 Subject: [PATCH 06/13] Comply with the XDG protocol in the README (#966) --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5732b94..4cc7af7 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,8 @@ $uri = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' ###### Unix, Linux ```sh -curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \ - https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \ + https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' ``` ###### Linux (Flatpak) @@ -315,4 +315,3 @@ The commands with `!` suffix ensure that all steps are run unconditionally. ### License MIT - From 6583b990321f03500505dc43a3f62cbbc7369862 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 3 Jun 2020 07:34:44 -0400 Subject: [PATCH 07/13] :Plug throws error for invalid option (#961) ":Plug" performs a quick type check for most options so that the user can check which plugin has invalid configuration on startup. This does not prevent errors, resulting from modiying "g:plugs" after running "plug#end()". Plugin repo is added to the error message for convenience. Most users should expect no noticeable difference in startup time. Close: #930 Related: #936 --- plug.vim | 24 +++++++++++++++++++++++- test/workflow.vader | 27 ++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/plug.vim b/plug.vim index 25be27f..8332537 100644 --- a/plug.vim +++ b/plug.vim @@ -632,17 +632,39 @@ function! plug#(repo, ...) let g:plugs[name] = spec let s:loaded[name] = get(s:loaded, name, 0) catch - return s:err(v:exception) + return s:err(repo . ' ' . v:exception) endtry endfunction function! s:parse_options(arg) let opts = copy(s:base_spec) let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif let opts.tag = a:arg elseif type == s:TYPE.dict call extend(opts, a:arg) + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(opts, opt) + \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(opts, opt) + \ && type(opts[opt]) != s:TYPE.list + \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(opts, 'do') + \ && type(opts.do) != s:TYPE.funcref + \ && (type(opts.do) != s:TYPE.string || empty(opts.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif if has_key(opts, 'dir') let opts.dir = s:dirpath(s:plug_expand(opts.dir)) endif diff --git a/test/workflow.vader b/test/workflow.vader index 7d70946..355928a 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -49,11 +49,19 @@ Execute (Test Plug command): AssertEqual 'no-t_co', g:plugs['seoul256.vim'].branch ^ Git repo with tag (DEPRECATED. USE TAG OPTION) + redir => out + Plug 'foo/bar.vim', '' + redir END + Assert out =~ 'Invalid argument for "tag" option of :Plug (expected: string)' Plug 'junegunn/goyo.vim', '1.5.2' AssertEqual 'file:///tmp/vim-plug-test/junegunn/goyo.vim', g:plugs['goyo.vim'].uri AssertEqual join([g:temp_plugged, 'goyo.vim/'], '/'), g:plugs['goyo.vim'].dir AssertEqual '1.5.2', g:plugs['goyo.vim'].tag + redir => out + Plug 'foo/bar.vim', {'tag': ''} + redir END + Assert out =~ 'Invalid argument for "tag" option of :Plug (expected: string)' Plug 'junegunn/goyo.vim', { 'tag': '1.5.3' } " Using tag option AssertEqual '1.5.3', g:plugs['goyo.vim'].tag @@ -77,6 +85,22 @@ Execute (Test Plug command): Execute (Plug command with dictionary option): Log string(g:plugs) + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + redir => out + Plug 'foo/bar.vim', {opt: ''} + redir END + Assert out =~ 'Invalid argument for "'.opt.'" option of :Plug (expected: string)' + endfor + for opt in ['on', 'for'] + redir => out + Plug 'foo/bar.vim', {opt: ''} + redir END + Assert out =~ 'Invalid argument for "'.opt.'" option of :Plug (expected: string or list)' + endfor + redir => out + Plug 'foo/bar.vim', {'do': ''} + redir END + Assert out =~ 'Invalid argument for "do" option of :Plug (expected: string or funcref)' Plug 'junegunn/seoul256.vim', { 'branch': 'no-t_co', 'rtp': '././' } AssertEqual join([g:temp_plugged, 'seoul256.vim/'], '/'), g:plugs['seoul256.vim'].dir AssertEqual '././', g:plugs['seoul256.vim'].rtp @@ -1037,9 +1061,10 @@ Execute (Post-update hook output; success and failure): Execute (Post-update hook output; invalid type or funcref): call plug#begin() - Plug 'junegunn/vim-easy-align', { 'do': 1 } + Plug 'junegunn/vim-easy-align', { 'do': ':echo 1' } Plug 'junegunn/vim-pseudocl', { 'do': function('call') } call plug#end() + let g:plugs['vim-easy-align'].do = 1 silent PlugInstall! 1 AssertEqual 'x Post-update hook for vim-pseudocl ... Vim(call):E119: Not enough arguments for function: call', getline(5) From 01aab60adef47c6606aa68c8783f5b994fda9fc4 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 8 Jun 2020 23:48:55 +0900 Subject: [PATCH 08/13] Fix PowerShell instruction for Neovim Close #976 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cc7af7..07374f6 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ curl -fLo ~/.var/app/io.neovim.nvim/data/nvim/site/autoload/plug.vim \ ###### Windows (PowerShell) ```powershell -md ~\AppData\Local\nvim\autoload +md ~\AppData\Local\nvim-data\site\autoload $uri = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' (New-Object Net.WebClient).DownloadFile( $uri, From 3aa3b5a4e85620dd58302926b571860c92fdbb2f Mon Sep 17 00:00:00 2001 From: Wolf Honore Date: Thu, 25 Jun 2020 07:56:47 -0400 Subject: [PATCH 09/13] Report when PlugClean fails to remove a directory (#985) --- plug.vim | 22 +++++++++++++++++----- test/run | 5 +++++ test/workflow.vader | 23 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/plug.vim b/plug.vim index 8332537..69033ec 100644 --- a/plug.vim +++ b/plug.vim @@ -2271,7 +2271,7 @@ endfunction function! s:rm_rf(dir) if isdirectory(a:dir) - call s:system(s:is_win + return s:system(s:is_win \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) \ : ['rm', '-rf', a:dir]) endif @@ -2355,6 +2355,7 @@ endfunction function! s:delete(range, force) let [l1, l2] = a:range let force = a:force + let err_count = 0 while l1 <= l2 let line = getline(l1) if line =~ '^- ' && isdirectory(line[2:]) @@ -2363,11 +2364,22 @@ function! s:delete(range, force) let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) let force = force || answer > 1 if answer - call s:rm_rf(line[2:]) + let err = s:rm_rf(line[2:]) 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 endif endif diff --git a/test/run b/test/run index bcccba3..2562b87 100755 --- a/test/run +++ b/test/run @@ -91,6 +91,11 @@ DOC echo "echomsg 'ftplugin-c'" > "$PLUG_FIXTURES/ftplugin-msg/ftplugin/c.vim" echo "echomsg 'ftplugin-java'" > "$PLUG_FIXTURES/ftplugin-msg/ftplugin/java.vim" + chmod +w "$PLUG_FIXTURES/cant-delete/autoload" || rm -rf "$PLUG_FIXTURES/cant-delete" + mkdir -p "$PLUG_FIXTURES/cant-delete/autoload" + touch "$PLUG_FIXTURES/cant-delete/autoload/cant-delete.vim" + chmod -w "$PLUG_FIXTURES/cant-delete/autoload" + rm -rf $TEMP/new-branch cd $TEMP git init new-branch diff --git a/test/workflow.vader b/test/workflow.vader index 355928a..65de605 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -1468,6 +1468,7 @@ Execute (PlugClean should not try to remove unmanaged plugins inside g:plug_home Plug '$PLUG_FIXTURES/fzf' Plug '$PLUG_FIXTURES/xxx' Plug '$PLUG_FIXTURES/yyy' + Plug '$PLUG_FIXTURES/cant-delete' call plug#end() " Remove z1, z2 @@ -1721,3 +1722,25 @@ Execute (#766 - Allow cloning into an empty directory): AssertExpect! '[=]', 1 q unlet d + +Execute (#982 - PlugClean should report when directories cannot be removed): + call plug#begin('$PLUG_FIXTURES') + Plug '$PLUG_FIXTURES/ftplugin-msg', { 'for': [] } + Plug '$PLUG_FIXTURES/fzf' + Plug '$PLUG_FIXTURES/xxx' + Plug '$PLUG_FIXTURES/yyy' + call plug#end() + + " Fail to remove cant-delete + PlugClean! + AssertEqual 'Removed 0 directories. Failed to remove 1 directories.', getline(4) + AssertExpect '^x ', 1 + q + + " Delete tmp but fail to remove cant-delete + call mkdir(expand('$PLUG_FIXTURES/tmp')) + PlugClean! + AssertEqual 'Removed 1 directories. Failed to remove 1 directories.', getline(4) + AssertExpect '^x ', 1 + AssertExpect '^\~ ', 1 + q From c31903639623c5be2431ac86b31ce77e54eb345f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 6 Jul 2020 01:07:42 -0400 Subject: [PATCH 10/13] Fix #961 tests for Vim 7.4 (#990) Use build stages to group related jobs (Vim, Neovim, Vim 7.4). Use "silent" to avoid hit-enter prompt when redirecting output. Always run async and sync tests to debug runtime errors. Vim 7.4.0052 (Ubuntu Trusty) does not allow dynamic keys in inline dictionary. https://docs.travis-ci.com/user/build-stages/ --- .travis.yml | 11 ++++++++++- test/run | 8 +++++--- test/workflow.vader | 14 +++++++++----- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f12e7f..2d2276d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,31 +3,40 @@ env: global: - DEPS=$HOME/deps - PATH=$DEPS/bin:$PATH -matrix: +jobs: include: - env: ENV=vim80-bionic dist: bionic + stage: vim8 - env: ENV=vim-nightly dist: trusty + stage: vim8 - env: ENV=neovim-stable dist: xenial addons: {apt: {packages: [neovim], sources: [{sourceline: 'ppa:neovim-ppa/stable'}]}} + stage: neovim - env: ENV=neovim-nightly dist: xenial addons: {apt: {packages: [neovim], sources: [{sourceline: 'ppa:neovim-ppa/unstable'}]}} + stage: neovim - env: ENV=vim74-trusty-python dist: trusty + stage: vim74 - env: ENV=vim74-xenial-python3 dist: xenial + stage: vim74 - env: ENV=vim74-trusty-ruby dist: trusty addons: {apt: {packages: [vim-nox]}} + stage: vim74 - env: ENV=vim74-xenial-ruby dist: xenial addons: {apt: {packages: [vim-nox]}} + stage: vim74 - env: ENV=osx-highsierra os: osx osx_image: xcode9.4 + stage: vim8 install: | git config --global user.email "you@example.com" git config --global user.name "Your Name" diff --git a/test/run b/test/run index 2562b87..23544c2 100755 --- a/test/run +++ b/test/run @@ -127,9 +127,11 @@ git --version vim=$(select_vim) echo "Selected Vim: $vim" if [ "${1:-}" = '!' ]; then - $vim -Nu $TEMP/mini-vimrc -c 'Vader! test.vader' > /dev/null && - prepare && - $vim -Nu $TEMP/mini-vimrc -c 'let g:plug_threads = 1 | Vader! test.vader' > /dev/null + FAIL=0 + $vim -Nu $TEMP/mini-vimrc -c 'Vader! test.vader' > /dev/null || FAIL=1 + prepare + $vim -Nu $TEMP/mini-vimrc -c 'let g:plug_threads = 1 | Vader! test.vader' > /dev/null || FAIL=1 + test $FAIL -eq 0 else $vim -Nu $TEMP/mini-vimrc -c 'Vader test.vader' fi diff --git a/test/workflow.vader b/test/workflow.vader index 65de605..8ea5114 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -50,7 +50,7 @@ Execute (Test Plug command): ^ Git repo with tag (DEPRECATED. USE TAG OPTION) redir => out - Plug 'foo/bar.vim', '' + silent Plug 'foo/bar.vim', '' redir END Assert out =~ 'Invalid argument for "tag" option of :Plug (expected: string)' Plug 'junegunn/goyo.vim', '1.5.2' @@ -59,7 +59,7 @@ Execute (Test Plug command): AssertEqual '1.5.2', g:plugs['goyo.vim'].tag redir => out - Plug 'foo/bar.vim', {'tag': ''} + silent Plug 'foo/bar.vim', {'tag': ''} redir END Assert out =~ 'Invalid argument for "tag" option of :Plug (expected: string)' Plug 'junegunn/goyo.vim', { 'tag': '1.5.3' } " Using tag option @@ -86,19 +86,23 @@ Execute (Test Plug command): Execute (Plug command with dictionary option): Log string(g:plugs) for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + let opts = {} + let opts[opt] = '' redir => out - Plug 'foo/bar.vim', {opt: ''} + silent Plug 'foo/bar.vim', opts redir END Assert out =~ 'Invalid argument for "'.opt.'" option of :Plug (expected: string)' endfor for opt in ['on', 'for'] + let opts = {} + let opts[opt] = '' redir => out - Plug 'foo/bar.vim', {opt: ''} + silent Plug 'foo/bar.vim', opts redir END Assert out =~ 'Invalid argument for "'.opt.'" option of :Plug (expected: string or list)' endfor redir => out - Plug 'foo/bar.vim', {'do': ''} + silent Plug 'foo/bar.vim', {'do': ''} redir END Assert out =~ 'Invalid argument for "do" option of :Plug (expected: string or funcref)' Plug 'junegunn/seoul256.vim', { 'branch': 'no-t_co', 'rtp': '././' } From b2133cf2ec935c55de0c3a306a6b7dc3546226da Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 20 Jul 2020 07:59:29 -0400 Subject: [PATCH 11/13] Support Windows shell without extension (#997) Close https://github.com/junegunn/vim-plug/issues/995 Vim supports omitting file extensions for its option. I omitted the file extension in Neovim's documentation for powershell. --- plug.vim | 14 +++++++------- test/functional.vader | 4 ++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plug.vim b/plug.vim index 69033ec..7914bfe 100644 --- a/plug.vim +++ b/plug.vim @@ -179,7 +179,7 @@ function! s:define_commands() endif if has('win32') \ && &shellslash - \ && (&shell =~# 'cmd\.exe' || &shell =~# 'powershell\.exe') + \ && (&shell =~# 'cmd\(\.exe\)\?$' || &shell =~# 'powershell\(\.exe\)\?$') return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') endif if !has('nvim') @@ -419,7 +419,7 @@ if s:is_win 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' + if &shell =~# 'powershell\(\.exe\)\?$' let cmd = '& ' . cmd endif return [batchfile, cmd] @@ -890,9 +890,9 @@ function! s:chsh(swap) set shell=sh endif if a:swap - if &shell =~# 'powershell\.exe' || &shell =~# 'pwsh$' + if &shell =~# 'powershell\(\.exe\)\?$' || &shell =~# 'pwsh$' let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' - elseif &shell =~# 'sh' || &shell =~# 'cmd\.exe' + elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' set shellredir=>%s\ 2>&1 endif endif @@ -2128,9 +2128,9 @@ 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' + if shell =~# 'cmd\(\.exe\)\?$' return s:shellesc_cmd(a:arg, script) - elseif shell =~# 'powershell\.exe' || shell =~# 'pwsh$' + elseif shell =~# 'powershell\(\.exe\)\?$' || shell =~# 'pwsh$' return s:shellesc_ps1(a:arg) endif return s:shellesc_sh(a:arg) @@ -2182,7 +2182,7 @@ function! s:system(cmd, ...) return system(a:cmd) endif let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) - if &shell =~# 'powershell\.exe' + if &shell =~# 'powershell\(\.exe\)\?$' let cmd = '& ' . cmd endif else diff --git a/test/functional.vader b/test/functional.vader index e928d7e..3742788 100644 --- a/test/functional.vader +++ b/test/functional.vader @@ -13,6 +13,7 @@ Execute (plug#shellescape() ignores invalid optional argument): Execute (plug#shellescape() depends on the shell): AssertEqual "'foo'\\'''", plug#shellescape("foo'", {'shell': 'sh'}) AssertEqual '^"foo''^"', plug#shellescape("foo'", {'shell': 'cmd.exe'}) + AssertEqual "'foo'''", plug#shellescape("foo'", {'shell': 'powershell'}) AssertEqual "'foo'''", plug#shellescape("foo'", {'shell': 'powershell.exe'}) AssertEqual "'foo'''", plug#shellescape("foo'", {'shell': 'pwsh'}) @@ -32,6 +33,9 @@ Execute (plug#shellescape() supports non-trivial cmd.exe escaping): \ }), Execute (plug#shellescape() supports non-trivial powershell.exe escaping): + AssertEqual '''\"Foo\\''''\\Bar\"''', plug#shellescape('"Foo\''\Bar"', { + \ 'shell': 'powershell', + \ }), AssertEqual '''\"Foo\\''''\\Bar\"''', plug#shellescape('"Foo\''\Bar"', { \ 'shell': 'powershell.exe', \ }), From 457bebcd30cbfca8b34b0d308f882b7b605714fc Mon Sep 17 00:00:00 2001 From: Raphael Martin Schindler Date: Sun, 2 Aug 2020 08:52:56 -0700 Subject: [PATCH 12/13] Fix typos (#1001) Add missing 'the' in some phrases and sentences. Use "Easy", not "Easier", because there are no comparisons in the same sentence or phrase. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 07374f6..0bb592b 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ A minimalist Vim plugin manager. ### Pros. -- Easier to setup: Single file. No boilerplate code required. -- Easier to use: Concise, intuitive syntax +- Easy to set up: Single file. No boilerplate code required. +- Easy to use: Concise, intuitive syntax - [Super-fast][40/4] parallel installation/update (with any of `+job`, `+python`, `+python3`, `+ruby`, or [Neovim][nv]) - Creates shallow clones to minimize disk space usage and download time @@ -230,14 +230,14 @@ Plug 'junegunn/goyo.vim', { 'for': 'markdown' } autocmd! User goyo.vim echom 'Goyo is now loaded!' ``` -`for` option is generally not needed as most plugins for specific file types -usually don't have too much code in `plugin` directory. You might want to +The `for` option is generally not needed as most plugins for specific file types +usually don't have too much code in the `plugin` directory. You might want to examine the output of `vim --startuptime` before applying the option. ### Post-update hooks There are some plugins that require extra steps after installation or update. -In that case, use `do` option to describe the task to be performed. +In that case, use the `do` option to describe the task to be performed. ```vim Plug 'Shougo/vimproc.vim', { 'do': 'make' } @@ -272,7 +272,7 @@ and only run when the repository has changed, but you can force it to run unconditionally with the bang-versions of the commands: `PlugInstall!` and `PlugUpdate!`. -Make sure to escape BARs and double-quotes when you write `do` option inline +Make sure to escape BARs and double-quotes when you write the `do` option inline as they are mistakenly recognized as command separator or the start of the trailing comment. @@ -298,7 +298,7 @@ The installer takes the following steps when installing/updating a plugin: 1. Update submodules 2. Execute post-update hooks -The commands with `!` suffix ensure that all steps are run unconditionally. +The commands with the `!` suffix ensure that all steps are run unconditionally. ### Articles From 13ea184015c30be5160ae285aedc0eaec0c72e6c Mon Sep 17 00:00:00 2001 From: timm bangma Date: Sat, 8 Aug 2020 18:25:59 -0400 Subject: [PATCH 13/13] Update README.md (Powershell Install One-Liner) (#1003) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A tidy one liner for the powershell install command. Much like the unix one 😄 --- README.md | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0bb592b..955bfc8 100644 --- a/README.md +++ b/README.md @@ -44,14 +44,8 @@ file as suggested [here][auto]. ###### Windows (PowerShell) ```powershell -md ~\vimfiles\autoload -$uri = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' -(New-Object Net.WebClient).DownloadFile( - $uri, - $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( - "~\vimfiles\autoload\plug.vim" - ) -) +iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |` + ni $HOME/vimfiles/autoload/plug.vim -Force ``` #### Neovim @@ -73,14 +67,8 @@ curl -fLo ~/.var/app/io.neovim.nvim/data/nvim/site/autoload/plug.vim \ ###### Windows (PowerShell) ```powershell -md ~\AppData\Local\nvim-data\site\autoload -$uri = 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' -(New-Object Net.WebClient).DownloadFile( - $uri, - $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( - "~\AppData\Local\nvim-data\site\autoload\plug.vim" - ) -) +iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |` + ni "$env:LOCALAPPDATA/nvim-data/site/autoload/plug.vim" -Force ``` ### Getting Help