diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
commit | 1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch) | |
tree | cd08258054db80bb9a11b1061bb091c70b76926a /runtime/autoload | |
parent | eaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-aucmd_textputpost.tar.gz rneovim-aucmd_textputpost.tar.bz2 rneovim-aucmd_textputpost.zip |
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'runtime/autoload')
25 files changed, 1584 insertions, 1806 deletions
diff --git a/runtime/autoload/cargo.vim b/runtime/autoload/cargo.vim new file mode 100644 index 0000000000..6696b3105f --- /dev/null +++ b/runtime/autoload/cargo.vim @@ -0,0 +1,149 @@ +" Last Modified: 2023-09-11 + +function! cargo#Load() + " Utility call to get this script loaded, for debugging +endfunction + +function! cargo#cmd(args) abort + " Trim trailing spaces. This is necessary since :terminal command parses + " trailing spaces as an empty argument. + let args = substitute(a:args, '\s\+$', '', '') + if exists('g:cargo_shell_command_runner') + let cmd = g:cargo_shell_command_runner + elseif has('terminal') + let cmd = 'terminal' + elseif has('nvim') + let cmd = 'noautocmd new | terminal' + else + let cmd = '!' + endif + execute cmd 'cargo' args +endfunction + +function! s:nearest_cargo(...) abort + " If the second argument is not specified, the first argument determines + " whether we will start from the current directory or the directory of the + " current buffer, otherwise, we start with the provided path on the + " second argument. + + let l:is_getcwd = get(a:, 1, 0) + if l:is_getcwd + let l:starting_path = get(a:, 2, getcwd()) + else + let l:starting_path = get(a:, 2, expand('%:p:h')) + endif + + return findfile('Cargo.toml', l:starting_path . ';') +endfunction + +function! cargo#nearestCargo(is_getcwd) abort + return s:nearest_cargo(a:is_getcwd) +endfunction + +function! cargo#nearestWorkspaceCargo(is_getcwd) abort + let l:nearest = s:nearest_cargo(a:is_getcwd) + while l:nearest !=# '' + for l:line in readfile(l:nearest, '', 0x100) + if l:line =~# '\V[workspace]' + return l:nearest + endif + endfor + let l:next = fnamemodify(l:nearest, ':p:h:h') + let l:nearest = s:nearest_cargo(0, l:next) + endwhile + return '' +endfunction + +function! cargo#nearestRootCargo(is_getcwd) abort + " Try to find a workspace Cargo.toml, and if not found, take the nearest + " regular Cargo.toml + let l:workspace_cargo = cargo#nearestWorkspaceCargo(a:is_getcwd) + if l:workspace_cargo !=# '' + return l:workspace_cargo + endif + return s:nearest_cargo(a:is_getcwd) +endfunction + + +function! cargo#build(args) + call cargo#cmd("build " . a:args) +endfunction + +function! cargo#check(args) + call cargo#cmd("check " . a:args) +endfunction + +function! cargo#clean(args) + call cargo#cmd("clean " . a:args) +endfunction + +function! cargo#doc(args) + call cargo#cmd("doc " . a:args) +endfunction + +function! cargo#new(args) + call cargo#cmd("new " . a:args) + cd `=a:args` +endfunction + +function! cargo#init(args) + call cargo#cmd("init " . a:args) +endfunction + +function! cargo#run(args) + call cargo#cmd("run " . a:args) +endfunction + +function! cargo#test(args) + call cargo#cmd("test " . a:args) +endfunction + +function! cargo#bench(args) + call cargo#cmd("bench " . a:args) +endfunction + +function! cargo#update(args) + call cargo#cmd("update " . a:args) +endfunction + +function! cargo#search(args) + call cargo#cmd("search " . a:args) +endfunction + +function! cargo#publish(args) + call cargo#cmd("publish " . a:args) +endfunction + +function! cargo#install(args) + call cargo#cmd("install " . a:args) +endfunction + +function! cargo#runtarget(args) + let l:filename = expand('%:p') + let l:read_manifest = system('cargo read-manifest') + let l:metadata = json_decode(l:read_manifest) + let l:targets = get(l:metadata, 'targets', []) + let l:did_run = 0 + for l:target in l:targets + let l:src_path = get(l:target, 'src_path', '') + let l:kinds = get(l:target, 'kind', []) + let l:name = get(l:target, 'name', '') + if l:src_path == l:filename + if index(l:kinds, 'example') != -1 + let l:did_run = 1 + call cargo#run("--example " . shellescape(l:name) . " " . a:args) + return + elseif index(l:kinds, 'bin') != -1 + let l:did_run = 1 + call cargo#run("--bin " . shellescape(l:name) . " " . a:args) + return + endif + endif + endfor + if l:did_run != 1 + call cargo#run(a:args) + return + endif +endfunction + +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/autoload/cargo/quickfix.vim b/runtime/autoload/cargo/quickfix.vim new file mode 100644 index 0000000000..f2a006f6c5 --- /dev/null +++ b/runtime/autoload/cargo/quickfix.vim @@ -0,0 +1,29 @@ +" Last Modified: 2023-09-11 + +function! cargo#quickfix#CmdPre() abort + if &filetype ==# 'rust' && get(b:, 'current_compiler', '') ==# 'cargo' && + \ &makeprg =~ '\V\^cargo\ \.\*' + " Preserve the current directory, and 'lcd' to the nearest Cargo file. + let b:rust_compiler_cargo_qf_has_lcd = haslocaldir() + let b:rust_compiler_cargo_qf_prev_cd = getcwd() + let b:rust_compiler_cargo_qf_prev_cd_saved = 1 + let l:nearest = fnamemodify(cargo#nearestRootCargo(0), ':h') + execute 'lchdir! '.l:nearest + else + let b:rust_compiler_cargo_qf_prev_cd_saved = 0 + endif +endfunction + +function! cargo#quickfix#CmdPost() abort + if exists("b:rust_compiler_cargo_qf_prev_cd_saved") && b:rust_compiler_cargo_qf_prev_cd_saved + " Restore the current directory. + if b:rust_compiler_cargo_qf_has_lcd + execute 'lchdir! '.b:rust_compiler_cargo_qf_prev_cd + else + execute 'chdir! '.b:rust_compiler_cargo_qf_prev_cd + endif + let b:rust_compiler_cargo_qf_prev_cd_saved = 0 + endif +endfunction + +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/autoload/ccomplete.lua b/runtime/autoload/ccomplete.lua index f4a3eabd9a..ce85e84f7a 100644 --- a/runtime/autoload/ccomplete.lua +++ b/runtime/autoload/ccomplete.lua @@ -24,10 +24,11 @@ local SearchMembers = nil -- vim9script -- # Vim completion script --- # Language: C --- # Maintainer: Bram Moolenaar <Bram@vim.org> +-- # Language: C +-- # Maintainer: The Vim Project <https://github.com/vim/vim> +-- # Last Change: 2023 Aug 10 -- # Rewritten in Vim9 script by github user lacygoill --- # Last Change: 2022 Jan 31 +-- # Former Maintainer: Bram Moolenaar <Bram@vim.org> prepended = '' grepCache = vim.empty_dict() diff --git a/runtime/autoload/dist/vim.vim b/runtime/autoload/dist/vim.vim new file mode 100644 index 0000000000..021244c93b --- /dev/null +++ b/runtime/autoload/dist/vim.vim @@ -0,0 +1,32 @@ +" Vim runtime support library, +" runs the vim9 script version or legacy script version +" on demand (mostly for Neovim compatability) +" +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Nov 04 + + +" enable the zip and gzip plugin by default, if not set +if !exists('g:zip_exec') + let g:zip_exec = 1 +endif + +if !exists('g:gzip_exec') + let g:gzip_exec = 1 +endif + +if !has('vim9script') + function dist#vim#IsSafeExecutable(filetype, executable) + let cwd = getcwd() + return get(g:, a:filetype .. '_exec', get(g:, 'plugin_exec', 0)) && + \ (fnamemodify(exepath(a:executable), ':p:h') !=# cwd + \ || (split($PATH, has('win32') ? ';' : ':')->index(cwd) != -1 && + \ cwd != '.')) + endfunction + + finish +endif + +def dist#vim#IsSafeExecutable(filetype: string, executable: string): bool + return dist#vim9#IsSafeExecutable(filetype, executable) +enddef diff --git a/runtime/autoload/gzip.vim b/runtime/autoload/gzip.vim index e4adec0947..26b1cda034 100644 --- a/runtime/autoload/gzip.vim +++ b/runtime/autoload/gzip.vim @@ -1,6 +1,7 @@ " Vim autoload file for editing compressed files. -" Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2016 Sep 28 +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> " These functions are used by the gzip plugin. @@ -9,12 +10,17 @@ fun s:check(cmd) let name = substitute(a:cmd, '\(\S*\).*', '\1', '') if !exists("s:have_" . name) + " safety check, don't execute anything from the current directory + let f = dist#vim#IsSafeExecutable('gzip', name) + if !f + echoerr "Warning: NOT executing " .. name .. " from current directory!" + endif let e = executable(name) if e < 0 let r = system(name . " --version") let e = (r !~ "not found" && r != "") endif - exe "let s:have_" . name . "=" . e + exe "let s:have_" . name . "=" . (e && f) endif exe "return s:have_" . name endfun diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 5fd4627b11..a7dbedab08 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -1,207 +1,40 @@ -" Runs the specified healthchecks. -" Runs all discovered healthchecks if a:plugin_names is empty. -function! health#check(plugin_names) abort - let healthchecks = empty(a:plugin_names) - \ ? s:discover_healthchecks() - \ : s:get_healthcheck(a:plugin_names) - - " Create buffer and open in a tab, unless this is the default buffer when Nvim starts. - let emptybuf = (bufnr('$') == 1 && empty(getline(1)) && 1 == line('$')) - execute (emptybuf ? 'buffer' : 'tab sbuffer') nvim_create_buf(v:true, v:true) - if bufexists('health://') - bwipe health:// - endif - file health:// - setfiletype checkhealth - - if empty(healthchecks) - call setline(1, 'ERROR: No healthchecks found.') - else - redraw|echo 'Running healthchecks...' - for name in sort(keys(healthchecks)) - let [func, type] = healthchecks[name] - let s:output = [] - try - if func == '' - throw 'healthcheck_not_found' - endif - eval type == 'v' ? call(func, []) : luaeval(func) - " in the event the healthcheck doesn't return anything - " (the plugin author should avoid this possibility) - if len(s:output) == 0 - throw 'healthcheck_no_return_value' - endif - catch - let s:output = [] " Clear the output - if v:exception =~# 'healthcheck_not_found' - call health#report_error('No healthcheck found for "'.name.'" plugin.') - elseif v:exception =~# 'healthcheck_no_return_value' - call health#report_error('The healthcheck report for "'.name.'" plugin is empty.') - else - call health#report_error(printf( - \ "Failed to run healthcheck for \"%s\" plugin. Exception:\n%s\n%s", - \ name, v:throwpoint, v:exception)) - endif - endtry - let header = [repeat('=', 78), name .. ': ' .. func, ''] - " remove empty line after header from report_start - let s:output = s:output[0] == '' ? s:output[1:] : s:output - let s:output = header + s:output + [''] - call append('$', s:output) - redraw - endfor +function! s:deprecate(type) abort + let deprecate = v:lua.vim.deprecate('health#report_' . a:type, 'vim.health.' . a:type, '0.11') + redraw | echo 'Running healthchecks...' + if deprecate isnot v:null + call v:lua.vim.health.warn(deprecate) endif - - " Clear the 'Running healthchecks...' message. - redraw|echo '' -endfunction - -function! s:collect_output(output) - let s:output += split(a:output, "\n", 1) endfunction -" Starts a new report. function! health#report_start(name) abort - call s:collect_output(printf("\n%s ~", a:name)) + call v:lua.vim.health.start(a:name) + call s:deprecate('start') endfunction -" Indents lines *except* line 1 of a string if it contains newlines. -function! s:indent_after_line1(s, columns) abort - let lines = split(a:s, "\n", 0) - if len(lines) < 2 " We do not indent line 1, so nothing to do. - return a:s - endif - for i in range(1, len(lines)-1) " Indent lines after the first. - let lines[i] = substitute(lines[i], '^\s*', repeat(' ', a:columns), 'g') - endfor - return join(lines, "\n") +function! health#report_info(msg) abort + call v:lua.vim.health.info(a:msg) + call s:deprecate('info') endfunction -" Changes ':h clipboard' to ':help |clipboard|'. -function! s:help_to_link(s) abort - return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n ]+)', ':help |\1|', 'g') +function! health#report_ok(msg) abort + call v:lua.vim.health.ok(a:msg) + call s:deprecate('ok') endfunction -" Format a message for a specific report item. -" a:1: Optional advice (string or list) -function! s:format_report_message(status, msg, ...) abort " {{{ - let output = '- ' .. a:status .. (empty(a:status) ? '' : ' ') .. s:indent_after_line1(a:msg, 2) - - " Optional parameters +function! health#report_warn(msg, ...) abort if a:0 > 0 - let advice = type(a:1) == type('') ? [a:1] : a:1 - if type(advice) != type([]) - throw 'a:1: expected String or List' - endif - - " Report each suggestion - if !empty(advice) - let output .= "\n - ADVICE:" - for suggestion in advice - let output .= "\n - " . s:indent_after_line1(suggestion, 6) - endfor - endif - endif - - return s:help_to_link(output) -endfunction " }}} - -" Reports a message as a listitem in the current section. -function! health#report_info(msg) abort " {{{ - call s:collect_output(s:format_report_message('', a:msg)) -endfunction " }}} - -" Reports a successful healthcheck. -function! health#report_ok(msg) abort " {{{ - call s:collect_output(s:format_report_message('OK', a:msg)) -endfunction " }}} - -" Reports a health warning. -" a:1: Optional advice (string or list) -function! health#report_warn(msg, ...) abort " {{{ - if a:0 > 0 - call s:collect_output(s:format_report_message('WARNING', a:msg, a:1)) + call v:lua.vim.health.warn(a:msg, a:1) else - call s:collect_output(s:format_report_message('WARNING', a:msg)) + call v:lua.vim.health.warn(a:msg) endif -endfunction " }}} + call s:deprecate('warn') +endfunction -" Reports a failed healthcheck. -" a:1: Optional advice (string or list) -function! health#report_error(msg, ...) abort " {{{ +function! health#report_error(msg, ...) abort if a:0 > 0 - call s:collect_output(s:format_report_message('ERROR', a:msg, a:1)) + call v:lua.vim.health.error(a:msg, a:1) else - call s:collect_output(s:format_report_message('ERROR', a:msg)) + call v:lua.vim.health.error(a:msg) endif -endfunction " }}} - -" From a path return a list [{name}, {func}, {type}] representing a healthcheck -function! s:filepath_to_healthcheck(path) abort - if a:path =~# 'vim$' - let name = matchstr(a:path, '\zs[^\/]*\ze\.vim$') - let func = 'health#'.name.'#check' - let type = 'v' - else - let base_path = substitute(a:path, - \ '.*lua[\/]\(.\{-}\)[\/]health\([\/]init\)\?\.lua$', - \ '\1', '') - let name = substitute(base_path, '[\/]', '.', 'g') - let func = 'require("'.name.'.health").check()' - let type = 'l' - endif - return [name, func, type] -endfunction - -function! s:discover_healthchecks() abort - return s:get_healthcheck('*') -endfunction - -" Returns Dictionary {name: [func, type], ..} representing healthchecks -function! s:get_healthcheck(plugin_names) abort - let health_list = s:get_healthcheck_list(a:plugin_names) - let healthchecks = {} - for c in health_list - let normalized_name = substitute(c[0], '-', '_', 'g') - let existent = get(healthchecks, normalized_name, []) - " Prefer Lua over vim entries - if existent != [] && existent[2] == 'l' - continue - else - let healthchecks[normalized_name] = c - endif - endfor - let output = {} - for v in values(healthchecks) - let output[v[0]] = v[1:] - endfor - try - " vim.health is not a healthcheck, skip it - call remove(output, 'vim') - catch - endtry - return output -endfunction - -" Returns list of lists [ [{name}, {func}, {type}] ] representing healthchecks -function! s:get_healthcheck_list(plugin_names) abort - let healthchecks = [] - let plugin_names = type('') == type(a:plugin_names) - \ ? split(a:plugin_names, ' ', v:false) - \ : a:plugin_names - for p in plugin_names - " support vim/lsp/health{/init/}.lua as :checkhealth vim.lsp - let p = substitute(p, '\.', '/', 'g') - let p = substitute(p, '*$', '**', 'g') " find all submodule e.g vim* - let paths = nvim_get_runtime_file('autoload/health/'.p.'.vim', v:true) - \ + nvim_get_runtime_file('lua/**/'.p.'/health/init.lua', v:true) - \ + nvim_get_runtime_file('lua/**/'.p.'/health.lua', v:true) - if len(paths) == 0 - let healthchecks += [[p, '', '']] " healthcheck not found - else - let healthchecks += map(uniq(sort(paths)), - \'<SID>filepath_to_healthcheck(v:val)') - end - endfor - return healthchecks + call s:deprecate('error') endfunction diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim deleted file mode 100644 index d104bcfd67..0000000000 --- a/runtime/autoload/health/provider.vim +++ /dev/null @@ -1,771 +0,0 @@ -let s:shell_error = 0 - -function! s:is_bad_response(s) abort - return a:s =~? '\v(^unable)|(^error)|(^outdated)' -endfunction - -function! s:trim(s) abort - return substitute(a:s, '^\_s*\|\_s*$', '', 'g') -endfunction - -" Convert '\' to '/'. Collapse '//' and '/./'. -function! s:normalize_path(s) abort - return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g') -endfunction - -" Returns TRUE if `cmd` exits with success, else FALSE. -function! s:cmd_ok(cmd) abort - call system(a:cmd) - return v:shell_error == 0 -endfunction - -" Simple version comparison. -function! s:version_cmp(a, b) abort - let a = split(a:a, '\.', 0) - let b = split(a:b, '\.', 0) - - for i in range(len(a)) - if str2nr(a[i]) > str2nr(b[i]) - return 1 - elseif str2nr(a[i]) < str2nr(b[i]) - return -1 - endif - endfor - - return 0 -endfunction - -" Handler for s:system() function. -function! s:system_handler(jobid, data, event) dict abort - if a:event ==# 'stderr' - if self.add_stderr_to_output - let self.output .= join(a:data, '') - else - let self.stderr .= join(a:data, '') - endif - elseif a:event ==# 'stdout' - let self.output .= join(a:data, '') - elseif a:event ==# 'exit' - let s:shell_error = a:data - endif -endfunction - -" Attempts to construct a shell command from an args list. -" Only for display, to help users debug a failed command. -function! s:shellify(cmd) abort - if type(a:cmd) != type([]) - return a:cmd - endif - return join(map(copy(a:cmd), - \'v:val =~# ''\m[^\-.a-zA-Z_/]'' ? shellescape(v:val) : v:val'), ' ') -endfunction - -" Run a system command and timeout after 30 seconds. -function! s:system(cmd, ...) abort - let stdin = a:0 ? a:1 : '' - let ignore_error = a:0 > 2 ? a:3 : 0 - let opts = { - \ 'add_stderr_to_output': a:0 > 1 ? a:2 : 0, - \ 'output': '', - \ 'stderr': '', - \ 'on_stdout': function('s:system_handler'), - \ 'on_stderr': function('s:system_handler'), - \ 'on_exit': function('s:system_handler'), - \ } - let jobid = jobstart(a:cmd, opts) - - if jobid < 1 - call health#report_error(printf('Command error (job=%d): `%s` (in %s)', - \ jobid, s:shellify(a:cmd), string(getcwd()))) - let s:shell_error = 1 - return opts.output - endif - - if !empty(stdin) - call jobsend(jobid, stdin) - endif - - let res = jobwait([jobid], 30000) - if res[0] == -1 - call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd))) - call jobstop(jobid) - elseif s:shell_error != 0 && !ignore_error - let emsg = printf("Command error (job=%d, exit code %d): `%s` (in %s)", - \ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd())) - if !empty(opts.output) - let emsg .= "\noutput: " . opts.output - end - if !empty(opts.stderr) - let emsg .= "\nstderr: " . opts.stderr - end - call health#report_error(emsg) - endif - - return opts.output -endfunction - -function! s:systemlist(cmd, ...) abort - let stdout = split(s:system(a:cmd, a:0 ? a:1 : ''), "\n") - if a:0 > 1 && !empty(a:2) - return filter(stdout, '!empty(v:val)') - endif - return stdout -endfunction - -" Fetch the contents of a URL. -function! s:download(url) abort - let has_curl = executable('curl') - if has_curl && system(['curl', '-V']) =~# 'Protocols:.*https' - let rv = s:system(['curl', '-sL', a:url], '', 1, 1) - return s:shell_error ? 'curl error with '.a:url.': '.s:shell_error : rv - elseif executable('python') - let script = " - \try:\n - \ from urllib.request import urlopen\n - \except ImportError:\n - \ from urllib2 import urlopen\n - \\n - \response = urlopen('".a:url."')\n - \print(response.read().decode('utf8'))\n - \" - let rv = s:system(['python', '-c', script]) - return empty(rv) && s:shell_error - \ ? 'python urllib.request error: '.s:shell_error - \ : rv - endif - return 'missing `curl` ' - \ .(has_curl ? '(with HTTPS support) ' : '') - \ .'and `python`, cannot make web request' -endfunction - -" Check for clipboard tools. -function! s:check_clipboard() abort - call health#report_start('Clipboard (optional)') - - if !empty($TMUX) && executable('tmux') && executable('pbpaste') && !s:cmd_ok('pbpaste') - let tmux_version = matchstr(system('tmux -V'), '\d\+\.\d\+') - call health#report_error('pbcopy does not work with tmux version: '.tmux_version, - \ ['Install tmux 2.6+. https://superuser.com/q/231130', - \ 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233']) - endif - - let clipboard_tool = provider#clipboard#Executable() - if exists('g:clipboard') && empty(clipboard_tool) - call health#report_error( - \ provider#clipboard#Error(), - \ ["Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."]) - elseif empty(clipboard_tool) - call health#report_warn( - \ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.', - \ [':help clipboard']) - else - call health#report_ok('Clipboard tool found: '. clipboard_tool) - endif -endfunction - -" Get the latest Nvim Python client (pynvim) version from PyPI. -function! s:latest_pypi_version() abort - let pypi_version = 'unable to get pypi response' - let pypi_response = s:download('https://pypi.python.org/pypi/pynvim/json') - if !empty(pypi_response) - try - let pypi_data = json_decode(pypi_response) - catch /E474/ - return 'error: '.pypi_response - endtry - let pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse') - endif - return pypi_version -endfunction - -" Get version information using the specified interpreter. The interpreter is -" used directly in case breaking changes were introduced since the last time -" Nvim's Python client was updated. -" -" Returns: [ -" {python executable version}, -" {current nvim version}, -" {current pypi nvim status}, -" {installed version status} -" ] -function! s:version_info(python) abort - let pypi_version = s:latest_pypi_version() - let python_version = s:trim(s:system([ - \ a:python, - \ '-c', - \ 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))', - \ ])) - - if empty(python_version) - let python_version = 'unable to parse '.a:python.' response' - endif - - let nvim_path = s:trim(s:system([ - \ a:python, '-c', - \ 'import sys; ' . - \ 'sys.path = [p for p in sys.path if p != ""]; ' . - \ 'import neovim; print(neovim.__file__)'])) - if s:shell_error || empty(nvim_path) - return [python_version, 'unable to load neovim Python module', pypi_version, - \ nvim_path] - endif - - " Assuming that multiple versions of a package are installed, sort them - " numerically in descending order. - function! s:compare(metapath1, metapath2) abort - let a = matchstr(fnamemodify(a:metapath1, ':p:h:t'), '[0-9.]\+') - let b = matchstr(fnamemodify(a:metapath2, ':p:h:t'), '[0-9.]\+') - return a == b ? 0 : a > b ? 1 : -1 - endfunction - - " Try to get neovim.VERSION (added in 0.1.11dev). - let nvim_version = s:system([a:python, '-c', - \ 'from neovim import VERSION as v; '. - \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'], - \ '', 1, 1) - if empty(nvim_version) - let nvim_version = 'unable to find pynvim module version' - let base = fnamemodify(nvim_path, ':h') - let metas = glob(base.'-*/METADATA', 1, 1) - \ + glob(base.'-*/PKG-INFO', 1, 1) - \ + glob(base.'.egg-info/PKG-INFO', 1, 1) - let metas = sort(metas, 's:compare') - - if !empty(metas) - for meta_line in readfile(metas[0]) - if meta_line =~# '^Version:' - let nvim_version = matchstr(meta_line, '^Version: \zs\S\+') - break - endif - endfor - endif - endif - - let nvim_path_base = fnamemodify(nvim_path, ':~:h') - let version_status = 'unknown; '.nvim_path_base - if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version) - if s:version_cmp(nvim_version, pypi_version) == -1 - let version_status = 'outdated; from '.nvim_path_base - else - let version_status = 'up to date' - endif - endif - - return [python_version, nvim_version, pypi_version, version_status] -endfunction - -" Check the Python interpreter's usability. -function! s:check_bin(bin) abort - if !filereadable(a:bin) && (!has('win32') || !filereadable(a:bin.'.exe')) - call health#report_error(printf('"%s" was not found.', a:bin)) - return 0 - elseif executable(a:bin) != 1 - call health#report_error(printf('"%s" is not executable.', a:bin)) - return 0 - endif - return 1 -endfunction - -" Check "loaded" var for given a:provider. -" Returns 1 if the caller should return (skip checks). -function! s:disabled_via_loaded_var(provider) abort - let loaded_var = 'g:loaded_'.a:provider.'_provider' - if exists(loaded_var) && !exists('*provider#'.a:provider.'#Call') - let v = eval(loaded_var) - if 0 is v - call health#report_info('Disabled ('.loaded_var.'='.v.').') - return 1 - else - call health#report_info('Disabled ('.loaded_var.'='.v.'). This might be due to some previous error.') - endif - endif - return 0 -endfunction - -function! s:check_python() abort - call health#report_start('Python 3 provider (optional)') - - let pyname = 'python3' - let python_exe = '' - let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' - let host_prog_var = pyname.'_host_prog' - let python_multiple = [] - - if s:disabled_via_loaded_var(pyname) - return - endif - - let [pyenv, pyenv_root] = s:check_for_pyenv() - - if exists('g:'.host_prog_var) - call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) - endif - - let [pyname, pythonx_warnings] = provider#pythonx#Detect(3) - - if empty(pyname) - call health#report_warn('No Python executable found that can `import neovim`. ' - \ . 'Using the first available executable for diagnostics.') - elseif exists('g:'.host_prog_var) - let python_exe = pyname - endif - - " No Python executable could `import neovim`, or host_prog_var was used. - if !empty(pythonx_warnings) - call health#report_warn(pythonx_warnings, ['See :help provider-python for more information.', - \ 'You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim']) - - elseif !empty(pyname) && empty(python_exe) - if !exists('g:'.host_prog_var) - call health#report_info(printf('`g:%s` is not set. Searching for ' - \ . '%s in the environment.', host_prog_var, pyname)) - endif - - if !empty(pyenv) - let python_exe = s:trim(s:system([pyenv, 'which', pyname], '', 1)) - - if empty(python_exe) - call health#report_warn(printf('pyenv could not find %s.', pyname)) - endif - endif - - if empty(python_exe) - let python_exe = exepath(pyname) - - if exists('$PATH') - for path in split($PATH, has('win32') ? ';' : ':') - let path_bin = s:normalize_path(path.'/'.pyname) - if path_bin != s:normalize_path(python_exe) - \ && index(python_multiple, path_bin) == -1 - \ && executable(path_bin) - call add(python_multiple, path_bin) - endif - endfor - - if len(python_multiple) - " This is worth noting since the user may install something - " that changes $PATH, like homebrew. - call health#report_info(printf('Multiple %s executables found. ' - \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var)) - endif - - if python_exe =~# '\<shims\>' - call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_exe), [ - \ '`pyenv` is not in $PATH, your pyenv installation is broken. ' - \ .'Set `g:'.host_prog_var.'` to avoid surprises.', - \ ]) - endif - endif - endif - endif - - if !empty(python_exe) && !exists('g:'.host_prog_var) - if empty(venv) && !empty(pyenv) - \ && !empty(pyenv_root) && resolve(python_exe) !~# '^'.pyenv_root.'/' - call health#report_warn('pyenv is not set up optimally.', [ - \ printf('Create a virtualenv specifically ' - \ . 'for Nvim using pyenv, and set `g:%s`. This will avoid ' - \ . 'the need to install the pynvim module in each ' - \ . 'version/virtualenv.', host_prog_var) - \ ]) - elseif !empty(venv) - if !empty(pyenv_root) - let venv_root = pyenv_root - else - let venv_root = fnamemodify(venv, ':h') - endif - - if resolve(python_exe) !~# '^'.venv_root.'/' - call health#report_warn('Your virtualenv is not set up optimally.', [ - \ printf('Create a virtualenv specifically ' - \ . 'for Nvim and use `g:%s`. This will avoid ' - \ . 'the need to install the pynvim module in each ' - \ . 'virtualenv.', host_prog_var) - \ ]) - endif - endif - endif - - if empty(python_exe) && !empty(pyname) - " An error message should have already printed. - call health#report_error(printf('`%s` was not found.', pyname)) - elseif !empty(python_exe) && !s:check_bin(python_exe) - let python_exe = '' - endif - - " Diagnostic output - call health#report_info('Executable: ' . (empty(python_exe) ? 'Not found' : python_exe)) - if len(python_multiple) - for path_bin in python_multiple - call health#report_info('Other python executable: ' . path_bin) - endfor - endif - - if empty(python_exe) - " No Python executable can import 'neovim'. Check if any Python executable - " can import 'pynvim'. If so, that Python failed to import 'neovim' as - " well, which is most probably due to a failed pip upgrade: - " https://github.com/neovim/neovim/wiki/Following-HEAD#20181118 - let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', 3) - if !empty(pynvim_exe) - call health#report_error( - \ 'Detected pip upgrade failure: Python executable can import "pynvim" but ' - \ . 'not "neovim": '. pynvim_exe, - \ "Use that Python version to reinstall \"pynvim\" and optionally \"neovim\".\n" - \ . pynvim_exe ." -m pip uninstall pynvim neovim\n" - \ . pynvim_exe ." -m pip install pynvim\n" - \ . pynvim_exe ." -m pip install neovim # only if needed by third-party software") - endif - else - let [majorpyversion, current, latest, status] = s:version_info(python_exe) - - if 3 != str2nr(majorpyversion) - call health#report_warn('Unexpected Python version.' . - \ ' This could lead to confusing error messages.') - endif - - call health#report_info('Python version: ' . majorpyversion) - - if s:is_bad_response(status) - call health#report_info(printf('pynvim version: %s (%s)', current, status)) - else - call health#report_info(printf('pynvim version: %s', current)) - endif - - if s:is_bad_response(current) - call health#report_error( - \ "pynvim is not installed.\nError: ".current, - \ ['Run in shell: '. python_exe .' -m pip install pynvim']) - endif - - if s:is_bad_response(latest) - call health#report_warn('Could not contact PyPI to get latest version.') - call health#report_error('HTTP request failed: '.latest) - elseif s:is_bad_response(status) - call health#report_warn(printf('Latest pynvim is NOT installed: %s', latest)) - elseif !s:is_bad_response(current) - call health#report_ok(printf('Latest pynvim is installed.')) - endif - endif -endfunction - -" Check if pyenv is available and a valid pyenv root can be found, then return -" their respective paths. If either of those is invalid, return two empty -" strings, effectivly ignoring pyenv. -function! s:check_for_pyenv() abort - let pyenv_path = resolve(exepath('pyenv')) - - if empty(pyenv_path) - return ['', ''] - endif - - call health#report_info('pyenv: Path: '. pyenv_path) - - let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : '' - - if empty(pyenv_root) - let pyenv_root = s:trim(s:system([pyenv_path, 'root'])) - call health#report_info('pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.') - endif - - if !isdirectory(pyenv_root) - call health#report_warn( - \ printf('pyenv: Root does not exist: %s. ' - \ . 'Ignoring pyenv for all following checks.', pyenv_root)) - return ['', ''] - endif - - call health#report_info('pyenv: Root: '.pyenv_root) - - return [pyenv_path, pyenv_root] -endfunction - -" Resolves Python executable path by invoking and checking `sys.executable`. -function! s:python_exepath(invocation) abort - return s:normalize_path(system(fnameescape(a:invocation) - \ . ' -c "import sys; sys.stdout.write(sys.executable)"')) -endfunction - -" Checks that $VIRTUAL_ENV Python executables are found at front of $PATH in -" Nvim and subshells. -function! s:check_virtualenv() abort - call health#report_start('Python virtualenv') - if !exists('$VIRTUAL_ENV') - call health#report_ok('no $VIRTUAL_ENV') - return - endif - let errors = [] - " Keep hints as dict keys in order to discard duplicates. - let hints = {} - " The virtualenv should contain some Python executables, and those - " executables should be first both on Nvim's $PATH and the $PATH of - " subshells launched from Nvim. - let bin_dir = has('win32') ? '/Scripts' : '/bin' - let venv_bins = glob($VIRTUAL_ENV . bin_dir . '/python*', v:true, v:true) - " XXX: Remove irrelevant executables found in bin/. - let venv_bins = filter(venv_bins, 'v:val !~# "python-config"') - if len(venv_bins) - for venv_bin in venv_bins - let venv_bin = s:normalize_path(venv_bin) - let py_bin_basename = fnamemodify(venv_bin, ':t') - let nvim_py_bin = s:python_exepath(exepath(py_bin_basename)) - let subshell_py_bin = s:python_exepath(py_bin_basename) - if venv_bin !=# nvim_py_bin - call add(errors, '$PATH yields this '.py_bin_basename.' executable: '.nvim_py_bin) - let hint = '$PATH ambiguities arise if the virtualenv is not ' - \.'properly activated prior to launching Nvim. Close Nvim, activate the virtualenv, ' - \.'check that invoking Python from the command line launches the correct one, ' - \.'then relaunch Nvim.' - let hints[hint] = v:true - endif - if venv_bin !=# subshell_py_bin - call add(errors, '$PATH in subshells yields this ' - \.py_bin_basename . ' executable: '.subshell_py_bin) - let hint = '$PATH ambiguities in subshells typically are ' - \.'caused by your shell config overriding the $PATH previously set by the ' - \.'virtualenv. Either prevent them from doing so, or use this workaround: ' - \.'https://vi.stackexchange.com/a/34996' - let hints[hint] = v:true - endif - endfor - else - call add(errors, 'no Python executables found in the virtualenv '.bin_dir.' directory.') - endif - - let msg = '$VIRTUAL_ENV is set to: '.$VIRTUAL_ENV - if len(errors) - if len(venv_bins) - let msg .= "\nAnd its ".bin_dir.' directory contains: ' - \.join(map(venv_bins, "fnamemodify(v:val, ':t')"), ', ') - endif - let conj = "\nBut " - for error in errors - let msg .= conj.error - let conj = "\nAnd " - endfor - let msg .= "\nSo invoking Python may lead to unexpected results." - call health#report_warn(msg, keys(hints)) - else - call health#report_info(msg) - call health#report_info('Python version: ' - \.system('python -c "import platform, sys; sys.stdout.write(platform.python_version())"')) - call health#report_ok('$VIRTUAL_ENV provides :!python.') - endif -endfunction - -function! s:check_ruby() abort - call health#report_start('Ruby provider (optional)') - - if s:disabled_via_loaded_var('ruby') - return - endif - - if !executable('ruby') || !executable('gem') - call health#report_warn( - \ '`ruby` and `gem` must be in $PATH.', - \ ['Install Ruby and verify that `ruby` and `gem` commands work.']) - return - endif - call health#report_info('Ruby: '. s:system(['ruby', '-v'])) - - let [host, err] = provider#ruby#Detect() - if empty(host) - call health#report_warn('`neovim-ruby-host` not found.', - \ ['Run `gem install neovim` to ensure the neovim RubyGem is installed.', - \ 'Run `gem environment` to ensure the gem bin directory is in $PATH.', - \ 'If you are using rvm/rbenv/chruby, try "rehashing".', - \ 'See :help g:ruby_host_prog for non-standard gem installations.', - \ 'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim']) - return - endif - call health#report_info('Host: '. host) - - let latest_gem_cmd = has('win32') ? 'cmd /c gem list -ra "^^neovim$"' : 'gem list -ra ^neovim$' - let latest_gem = s:system(split(latest_gem_cmd)) - if s:shell_error || empty(latest_gem) - call health#report_error('Failed to run: '. latest_gem_cmd, - \ ["Make sure you're connected to the internet.", - \ 'Are you behind a firewall or proxy?']) - return - endif - let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 0, 'not found') - - let current_gem_cmd = [host, '--version'] - let current_gem = s:system(current_gem_cmd) - if s:shell_error - call health#report_error('Failed to run: '. join(current_gem_cmd), - \ ['Report this issue with the output of: ', join(current_gem_cmd)]) - return - endif - - if s:version_cmp(current_gem, latest_gem) == -1 - call health#report_warn( - \ printf('Gem "neovim" is out-of-date. Installed: %s, latest: %s', - \ current_gem, latest_gem), - \ ['Run in shell: gem update neovim']) - else - call health#report_ok('Latest "neovim" gem is installed: '. current_gem) - endif -endfunction - -function! s:check_node() abort - call health#report_start('Node.js provider (optional)') - - if s:disabled_via_loaded_var('node') - return - endif - - if !executable('node') || (!executable('npm') && !executable('yarn') && !executable('pnpm')) - call health#report_warn( - \ '`node` and `npm` (or `yarn`, `pnpm`) must be in $PATH.', - \ ['Install Node.js and verify that `node` and `npm` (or `yarn`, `pnpm`) commands work.']) - return - endif - let node_v = get(split(s:system(['node', '-v']), "\n"), 0, '') - call health#report_info('Node.js: '. node_v) - if s:shell_error || s:version_cmp(node_v[1:], '6.0.0') < 0 - call health#report_warn('Nvim node.js host does not support '.node_v) - " Skip further checks, they are nonsense if nodejs is too old. - return - endif - if !provider#node#can_inspect() - call health#report_warn('node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.') - endif - - let [host, err] = provider#node#Detect() - if empty(host) - call health#report_warn('Missing "neovim" npm (or yarn, pnpm) package.', - \ ['Run in shell: npm install -g neovim', - \ 'Run in shell (if you use yarn): yarn global add neovim', - \ 'Run in shell (if you use pnpm): pnpm install -g neovim', - \ 'You may disable this provider (and warning) by adding `let g:loaded_node_provider = 0` to your init.vim']) - return - endif - call health#report_info('Nvim node.js host: '. host) - - let manager = 'npm' - if executable('yarn') - let manager = 'yarn' - elseif executable('pnpm') - let manager = 'pnpm' - endif - - let latest_npm_cmd = has('win32') ? - \ 'cmd /c '. manager .' info neovim --json' : - \ manager .' info neovim --json' - let latest_npm = s:system(split(latest_npm_cmd)) - if s:shell_error || empty(latest_npm) - call health#report_error('Failed to run: '. latest_npm_cmd, - \ ["Make sure you're connected to the internet.", - \ 'Are you behind a firewall or proxy?']) - return - endif - try - let pkg_data = json_decode(latest_npm) - catch /E474/ - return 'error: '.latest_npm - endtry - let latest_npm = get(get(pkg_data, 'dist-tags', {}), 'latest', 'unable to parse') - - let current_npm_cmd = ['node', host, '--version'] - let current_npm = s:system(current_npm_cmd) - if s:shell_error - call health#report_error('Failed to run: '. join(current_npm_cmd), - \ ['Report this issue with the output of: ', join(current_npm_cmd)]) - return - endif - - if s:version_cmp(current_npm, latest_npm) == -1 - call health#report_warn( - \ printf('Package "neovim" is out-of-date. Installed: %s, latest: %s', - \ current_npm, latest_npm), - \ ['Run in shell: npm install -g neovim', - \ 'Run in shell (if you use yarn): yarn global add neovim', - \ 'Run in shell (if you use pnpm): pnpm install -g neovim']) - else - call health#report_ok('Latest "neovim" npm/yarn/pnpm package is installed: '. current_npm) - endif -endfunction - -function! s:check_perl() abort - call health#report_start('Perl provider (optional)') - - if s:disabled_via_loaded_var('perl') - return - endif - - let [perl_exec, perl_warnings] = provider#perl#Detect() - if empty(perl_exec) - if !empty(perl_warnings) - call health#report_warn(perl_warnings, ['See :help provider-perl for more information.', - \ 'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim']) - else - call health#report_warn('No usable perl executable found') - endif - return - endif - - call health#report_info('perl executable: '. perl_exec) - - " we cannot use cpanm that is on the path, as it may not be for the perl - " set with g:perl_host_prog - call s:system([perl_exec, '-W', '-MApp::cpanminus', '-e', '']) - if s:shell_error - return [perl_exec, '"App::cpanminus" module is not installed'] - endif - - let latest_cpan_cmd = [perl_exec, - \ '-MApp::cpanminus::fatscript', '-e', - \ 'my $app = App::cpanminus::script->new; - \ $app->parse_options ("--info", "-q", "Neovim::Ext"); - \ exit $app->doit'] - - let latest_cpan = s:system(latest_cpan_cmd) - if s:shell_error || empty(latest_cpan) - call health#report_error('Failed to run: '. join(latest_cpan_cmd, " "), - \ ["Make sure you're connected to the internet.", - \ 'Are you behind a firewall or proxy?']) - return - elseif latest_cpan[0] ==# '!' - let cpanm_errs = split(latest_cpan, '!') - if cpanm_errs[0] =~# "Can't write to " - call health#report_warn(cpanm_errs[0], cpanm_errs[1:-2]) - " Last line is the package info - let latest_cpan = cpanm_errs[-1] - else - call health#report_error('Unknown warning from command: ' . latest_cpan_cmd, cpanm_errs) - return - endif - endif - let latest_cpan = matchstr(latest_cpan, '\(\.\?\d\)\+') - if empty(latest_cpan) - call health#report_error('Cannot parse version number from cpanm output: ' . latest_cpan) - return - endif - - let current_cpan_cmd = [perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION'] - let current_cpan = s:system(current_cpan_cmd) - if s:shell_error - call health#report_error('Failed to run: '. join(current_cpan_cmd), - \ ['Report this issue with the output of: ', join(current_cpan_cmd)]) - return - endif - - if s:version_cmp(current_cpan, latest_cpan) == -1 - call health#report_warn( - \ printf('Module "Neovim::Ext" is out-of-date. Installed: %s, latest: %s', - \ current_cpan, latest_cpan), - \ ['Run in shell: cpanm -n Neovim::Ext']) - else - call health#report_ok('Latest "Neovim::Ext" cpan module is installed: '. current_cpan) - endif -endfunction - -function! health#provider#check() abort - call s:check_clipboard() - call s:check_python() - call s:check_virtualenv() - call s:check_ruby() - call s:check_node() - call s:check_perl() -endfunction diff --git a/runtime/autoload/javascriptcomplete.vim b/runtime/autoload/javascriptcomplete.vim index 29b6b16254..3ec3b50490 100644 --- a/runtime/autoload/javascriptcomplete.vim +++ b/runtime/autoload/javascriptcomplete.vim @@ -156,8 +156,8 @@ function! javascriptcomplete#CompleteJS(findstart, base) \ 'text', 'vLink'] let bodys = bodyprop " Document - document. - let docuprop = ['anchors', 'body', 'characterSet', 'doctype', - \ 'documentElement', 'documentURI', 'embeds', 'fonts', 'forms', + let docuprop = ['anchors', 'applets', 'body', 'characterSet', 'childNodes', + \ 'doctype', 'documentElement', 'documentURI', 'embeds', 'fonts', 'forms', \ 'head', 'hidden', 'images', 'implementation', 'lastStyleSheetSet', \ 'links', 'plugins', 'preferredStyleSheetSet', 'scripts', \ 'scrollingElement', 'selectedStyleSheetSet', 'styleSheetSets', @@ -171,7 +171,7 @@ function! javascriptcomplete#CompleteJS(findstart, base) \ 'createEvent', 'createExpression', 'createNSResolver', \ 'createNodeIterator', 'createProcessingInstruction', 'createRange', \ 'createTextNode', 'createTouchList', 'createTreeWalker', - \ 'enableStyleSheetsForSet', 'evaluate', 'focus', 'getElementById', + \ 'enableStyleSheetsForSet', 'evaluate', 'focus', \ 'getElementById', 'getElementsByClassName', 'getElementsByName', \ 'getElementsByTagName', 'getElementsByTagNameNS', \ 'hasStorageAccess', 'importNode', 'onClick', 'onDblClick', diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim index 7f98a5b230..18dcd1e6a6 100644 --- a/runtime/autoload/msgpack.vim +++ b/runtime/autoload/msgpack.vim @@ -101,8 +101,8 @@ function s:msgpack_init_python() abort " @return Formatted timestamp. " " @warning Without +python or +python3 this function does not work correctly. - " The VimL code contains “reference” implementation which does not - " really work because of precision loss. + " The Vimscript code contains “reference” implementation which does + " not really work because of precision loss. function s:msgpack_dict_strftime(format, timestamp) return msgpack#strftime(a:format, +msgpack#int_dict_to_str(a:timestamp)) endfunction @@ -541,8 +541,8 @@ let s:MSGPACK_SPECIAL_OBJECTS = { \} "" -" Convert msgpack object dumped by msgpack#string() to a VimL object suitable -" for msgpackdump(). +" Convert msgpack object dumped by msgpack#string() to a Vimscript object +" suitable for msgpackdump(). " " @param[in] s String to evaluate. " @param[in] special_objs Additional special objects, in the same format as diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index 2fcf0b32c7..b8092ebeeb 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -1,7 +1,10 @@ " netrw.vim: Handles file transfer and remote directory listing across " AUTOLOAD SECTION -" Date: Aug 16, 2021 -" Version: 171 +" Date: May 03, 2023 +" Version: 173a +" Last Change: +" 2023 Nov 21 by Vim Project: ignore wildignore when expanding $COMSPEC (v173a) +" 2023 Nov 22 by Vim Project: fix handling of very long filename on longlist style (v173a) " Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 @@ -43,7 +46,7 @@ if exists("s:needspatches") endfor endif -let g:loaded_netrw = "v171" +let g:loaded_netrw = "v173" if !exists("s:NOTE") let s:NOTE = 0 let s:WARNING = 1 @@ -208,14 +211,11 @@ let g:netrw_localcopycmdopt = "" let g:netrw_localcopydircmdopt = "" let g:netrw_localmkdiropt = "" let g:netrw_localmovecmdopt = "" -let g:netrw_localrmdiropt = "" " --------------------------------------------------------------------- " Default values for netrw's global protocol variables {{{2 -if (v:version > 802 || (v:version == 802 && has("patch486"))) && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") && has("mouse") - call s:NetrwInit("g:netrw_use_errorwindow",2) -else - call s:NetrwInit("g:netrw_use_errorwindow",1) +if !exists("g:netrw_use_errorwindow") + let g:netrw_use_errorwindow = 0 endif if !exists("g:netrw_dav_cmd") @@ -401,7 +401,7 @@ if !exists("g:netrw_localcopycmd") if g:netrw_cygwin let g:netrw_localcopycmd= "cp" else - let g:netrw_localcopycmd = expand("$COMSPEC") + let g:netrw_localcopycmd = expand("$COMSPEC", v:true) let g:netrw_localcopycmdopt= " /c copy" endif elseif has("unix") || has("macunix") @@ -416,7 +416,7 @@ if !exists("g:netrw_localcopydircmd") let g:netrw_localcopydircmd = "cp" let g:netrw_localcopydircmdopt= " -R" else - let g:netrw_localcopydircmd = expand("$COMSPEC") + let g:netrw_localcopydircmd = expand("$COMSPEC", v:true) let g:netrw_localcopydircmdopt= " /c xcopy /e /c /h /i /k" endif elseif has("unix") @@ -437,7 +437,7 @@ if has("win32") || has("win95") || has("win64") || has("win16") if g:netrw_cygwin call s:NetrwInit("g:netrw_localmkdir","mkdir") else - let g:netrw_localmkdir = expand("$COMSPEC") + let g:netrw_localmkdir = expand("$COMSPEC", v:true) let g:netrw_localmkdiropt= " /c mkdir" endif else @@ -453,7 +453,7 @@ if !exists("g:netrw_localmovecmd") if g:netrw_cygwin let g:netrw_localmovecmd= "mv" else - let g:netrw_localmovecmd = expand("$COMSPEC") + let g:netrw_localmovecmd = expand("$COMSPEC", v:true) let g:netrw_localmovecmdopt= " /c move" endif elseif has("unix") || has("macunix") @@ -1137,7 +1137,6 @@ fun! netrw#Explore(indx,dosplit,style,...) 2match none if exists("s:explore_match") | unlet s:explore_match | endif if exists("s:explore_prvdir") | unlet s:explore_prvdir | endif - echo " " " call Decho("cleared explore match list",'~'.expand("<slnum>")) endif @@ -1151,6 +1150,11 @@ endfun " --------------------------------------------------------------------- " netrw#Lexplore: toggle Explorer window, keeping it on the left of the current tab {{{2 +" Uses g:netrw_chgwin : specifies the window where Lexplore files are to be opened +" t:netrw_lexposn : winsaveview() output (used on Lexplore window) +" t:netrw_lexbufnr: the buffer number of the Lexplore buffer (internal to this function) +" s:lexplore_win : window number of Lexplore window (serves to indicate which window is a Lexplore window) +" w:lexplore_buf : buffer number of Lexplore window (serves to indicate which window is a Lexplore window) fun! netrw#Lexplore(count,rightside,...) " call Dfunc("netrw#Lexplore(count=".a:count." rightside=".a:rightside.",...) a:0=".a:0." ft=".&ft) let curwin= winnr() @@ -1167,6 +1171,8 @@ fun! netrw#Lexplore(count,rightside,...) " call Decho("exe Explore ".fnameescape(a:1),'~'.expand("<slnum>")) exe "Explore ".fnameescape(a1) exe curwin."wincmd w" + let s:lexplore_win= curwin + let w:lexplore_buf= bufnr("%") if exists("t:netrw_lexposn") " call Decho("forgetting t:netrw_lexposn",'~'.expand("<slnum>")) unlet t:netrw_lexposn @@ -1241,7 +1247,7 @@ fun! netrw#Lexplore(count,rightside,...) let t:netrw_lexbufnr = bufnr("%") " done to prevent build-up of hidden buffers due to quitting and re-invocation of :Lexplore. " Since the intended use of :Lexplore is to have an always-present explorer window, the extra - " effort to mis-use :Lex is warranted. + " effort to prevent mis-use of :Lex is warranted. set bh=wipe " call Decho("let t:netrw_lexbufnr=".t:netrw_lexbufnr) " call Decho("t:netrw_lexposn".(exists("t:netrw_lexposn")? string(t:netrw_lexposn) : " n/a")) @@ -1645,7 +1651,7 @@ fun! s:NetrwOptionsSave(vt) endif let {a:vt}netrw_fokeep = &l:fo " formatoptions let {a:vt}netrw_gdkeep = &l:gd " gdefault - let {a:vt}netrw_gokeep = &l:go " guioptions + let {a:vt}netrw_gokeep = &go " guioptions let {a:vt}netrw_hidkeep = &l:hidden let {a:vt}netrw_imkeep = &l:im let {a:vt}netrw_iskkeep = &l:isk @@ -1711,8 +1717,9 @@ fun! s:NetrwOptionsSafe(islocal) if &cpo =~ 'a' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'a','','g')) | endif if &cpo =~ 'A' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'A','','g')) | endif setl fo=nroql2 - " call s:NetrwSetSafeSetting("&go","begmr") - if &go =~ '\ca' | call s:NetrwSetSafeSetting("&go",substitute(&go,'\ca','','g')) | endif + if &go =~ 'a' | set go-=a | endif + if &go =~ 'A' | set go-=A | endif + if &go =~ 'P' | set go-=P | endif call s:NetrwSetSafeSetting("&l:hid",0) call s:NetrwSetSafeSetting("&l:im",0) setl isk+=@ isk+=* isk+=/ @@ -1751,11 +1758,13 @@ fun! s:NetrwOptionsRestore(vt) " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) if !exists("{a:vt}netrw_optionsave") " call Decho("case ".a:vt."netrw_optionsave : doesn't exist",'~'.expand("<slnum>")) - if !isdirectory(expand('%')) + if filereadable(expand("%")) " call Decho("..doing filetype detect anyway") - filetype detect + filetype detect +" call Decho("..settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) + else + setl ft=netrw endif -" call Decho("..settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) " call Decho("..ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwOptionsRestore : ".a:vt."netrw_optionsave doesn't exist") return @@ -1795,7 +1804,7 @@ fun! s:NetrwOptionsRestore(vt) " call Decho("(s:NetrwOptionsRestore) #4 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_fokeep" ,"&l:fo") call s:NetrwRestoreSetting(a:vt."netrw_gdkeep" ,"&l:gd") - call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&l:go") + call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&go") call s:NetrwRestoreSetting(a:vt."netrw_hidkeep" ,"&l:hidden") " call Decho("(s:NetrwOptionsRestore) #5 lines=".&lines) call s:NetrwRestoreSetting(a:vt."netrw_imkeep" ,"&l:im") @@ -1861,11 +1870,9 @@ fun! s:NetrwOptionsRestore(vt) " were having their filetype detect-generated settings overwritten by " NetrwOptionRestore. if &ft != "netrw" - if !isdirectory(expand('%')) -" call Decho("before: filetype detect (ft=".&ft.")",'~'.expand("<slnum>")) - filetype detect -" call Decho("after : filetype detect (ft=".&ft.")",'~'.expand("<slnum>")) - endif +" call Decho("before: filetype detect (ft=".&ft.")",'~'.expand("<slnum>")) + filetype detect +" call Decho("after : filetype detect (ft=".&ft.")",'~'.expand("<slnum>")) endif " call Decho("(s:NetrwOptionsRestore) lines=".&lines) " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) @@ -1909,7 +1916,7 @@ endfun " Doing this means that netrw will not come up as having changed a " setting last when it really didn't actually change it. " -" Used by s:NetrwOptionsRestore() to restore each netrw-senstive setting +" Used by s:NetrwOptionsRestore() to restore each netrw-sensitive setting " keepvars are set up by s:NetrwOptionsSave fun! s:NetrwRestoreSetting(keepvar,setting) """ call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)") @@ -2940,13 +2947,19 @@ fun! s:NetrwGetFile(readcmd, tfile, method) " rename buffer back to remote filename call s:NetrwBufRename(rfile) + " Jan 19, 2022: COMBAK -- bram problem with https://github.com/vim/vim/pull/9554.diff filetype " Detect filetype of local version of remote file. " Note that isk must not include a "/" for scripts.vim " to process this detection correctly. -" call Decho("detect filetype of local version of remote file",'~'.expand("<slnum>")) - let iskkeep= &l:isk +" call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>")) +" call Decho("..did_filetype()=".did_filetype()) +" setl ft= +" call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">") + let iskkeep= &isk setl isk-=/ - let &l:isk= iskkeep + filetype detect +" call Decho("..local filetype<".&ft."> for buf#".bufnr()."<".bufname().">") + let &isk= iskkeep " call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)") let line1 = 1 let line2 = line("$") @@ -3458,6 +3471,11 @@ fun! s:NetrwBookHistHandler(chg,curdir) echo "bookmarked the current directory" endif + try + call s:NetrwBookHistSave() + catch + endtry + elseif a:chg == 1 " change to the bookmarked directory " call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>")) @@ -3602,6 +3620,11 @@ fun! s:NetrwBookHistHandler(chg,curdir) " call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) endif " call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) + + try + call s:NetrwBookHistSave() + catch + endtry endif call s:NetrwBookmarkMenu() call s:NetrwTgtMenu() @@ -4227,7 +4250,7 @@ fun! s:NetrwGetBuffer(islocal,dirname) endif " call Decho(" bufnum#".bufnum,'~'.expand("<slnum>")) - " hijack the current buffer + " highjack the current buffer " IF the buffer already has the desired name " AND it is empty let curbuf = bufname("%") @@ -4235,7 +4258,7 @@ fun! s:NetrwGetBuffer(islocal,dirname) let curbuf = getcwd() endif " call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)") -" call Decho("deciding if netrw may hijack the current buffer#".bufnr("%")."<".curbuf.">",'~'.expand("<slnum>")) +" call Decho("deciding if netrw may highjack the current buffer#".bufnr("%")."<".curbuf.">",'~'.expand("<slnum>")) " call Decho("..dirname<".dirname."> IF dirname == bufname",'~'.expand("<slnum>")) " call Decho("..curbuf<".curbuf.">",'~'.expand("<slnum>")) " call Decho("..line($)=".line("$")." AND this is 1",'~'.expand("<slnum>")) @@ -4244,7 +4267,7 @@ fun! s:NetrwGetBuffer(islocal,dirname) " call Dret("s:NetrwGetBuffer 0<cleared buffer> : highjacking buffer#".bufnr("%")) return 0 else " DEBUG -" call Decho("..did NOT hijack buffer",'~'.expand("<slnum>")) +" call Decho("..did NOT highjack buffer",'~'.expand("<slnum>")) endif " Aug 14, 2021: was thinking about looking for a [No Name] buffer here and using it, but that might cause problems @@ -4280,19 +4303,25 @@ fun! s:NetrwGetBuffer(islocal,dirname) else " Re-use the buffer " call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>")) + " ignore all events let eikeep= &ei setl ei=all - if getline(2) =~# '^" Netrw Directory Listing' -" call Decho(" getline(2)<".getline(2).'> matches "Netrw Directory Listing" : using keepalt b '.bufnum,'~'.expand("<slnum>")) - exe "sil! NetrwKeepj noswapfile keepalt b ".bufnum + + if &ft == "netrw" +" call Decho("buffer type is netrw; not using keepalt with b ".bufnum) + exe "sil! NetrwKeepj noswapfile b ".bufnum +" call Dredir("ls!","one") else -" call Decho(" getline(2)<".getline(2).'> does not match "Netrw Directory Listing" : using b '.bufnum,'~'.expand("<slnum>")) - exe "sil! NetrwKeepj noswapfile keepalt b ".bufnum +" call Decho("buffer type is not netrw; using keepalt with b ".bufnum) + call s:NetrwEditBuf(bufnum) +" call Dredir("ls!","two") endif " call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) if bufname("%") == '.' call s:NetrwBufRename(getcwd()) endif + + " restore ei let &ei= eikeep if line("$") <= 1 && getline(1) == "" @@ -4943,7 +4972,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " the point where netrw actually edits the (local) file " if its local only: LocalBrowseCheck() doesn't edit a file, but NetrwBrowse() will - " no keepalt to support :e # to return to a directory listing + " use keepalt to support :e # to return to a directory listing if !&mod " if e the new file would fail due to &mod, then don't change any of the flags let dolockout= 1 @@ -4954,12 +4983,8 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " others like c-^ to return to the netrw buffer " Apr 30, 2020: used to have e! here. That can cause loss of a modified file, " so emit error E37 instead. - if exists("g:netrw_altfile") && g:netrw_altfile - exe "NetrwKeepj keepalt e ".fnameescape(dirname) - else - exe "NetrwKeepj e ".fnameescape(dirname) - endif -" call Decho("edit-a-file: after e! ".dirname.": hidden=".&hidden." bufhidden<".&bufhidden."> mod=".&mod,'~'.expand("<slnum>")) + call s:NetrwEditFile("e","",dirname) +" call Decho("edit-a-file: after e ".dirname.": hidden=".&hidden." bufhidden<".&bufhidden."> mod=".&mod,'~'.expand("<slnum>")) " COMBAK -- cuc cul related call s:NetrwCursor(1) if &hidden || &bufhidden == "hide" @@ -5300,8 +5325,8 @@ fun! netrw#BrowseX(fname,remote) " g:Netrw_corehandler is a List of function references (see :help Funcref) " call Decho("g:Netrw_corehandler is a List",'~'.expand("<slnum>")) for Fncref in g:Netrw_corehandler - if type(FncRef) == 2 - call FncRef(a:fname) + if type(Fncref) == 2 + call Fncref(a:fname) endif endfor endif @@ -5374,6 +5399,8 @@ fun! netrw#BrowseX(fname,remote) else let redir= &srr . "/dev/null" endif + else + let redir= "" endif " call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("<slnum>")) @@ -5490,7 +5517,7 @@ fun! netrw#BrowseX(fname,remote) " cleanup: remove temporary file, " delete current buffer if success with handler, " return to prior buffer (directory listing) - " Feb 12, 2008: had to de-activiate removal of + " Feb 12, 2008: had to de-activate removal of " temporary file because it wasn't getting seen. " if remote == 1 && fname != a:fname "" call Decho("deleting temporary file<".fname.">",'~'.expand("<slnum>")) @@ -5663,12 +5690,39 @@ fun! s:NetrwClearExplore() if exists("w:netrw_explore_list") |unlet w:netrw_explore_list |endif if exists("w:netrw_explore_bufnr") |unlet w:netrw_explore_bufnr |endif " redraw! - echo " " - echo " " " call Dret("s:NetrwClearExplore") endfun " --------------------------------------------------------------------- +" s:NetrwEditBuf: decides whether or not to use keepalt to edit a buffer {{{2 +fun! s:NetrwEditBuf(bufnum) +" call Dfunc("s:NetrwEditBuf(fname<".a:bufnum.">)") + if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" +" call Decho("exe sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum)) + exe "sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum) + else +" call Decho("exe sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum)) + exe "sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum) + endif +" call Dret("s:NetrwEditBuf") +endfun + +" --------------------------------------------------------------------- +" s:NetrwEditFile: decides whether or not to use keepalt to edit a file {{{2 +" NetrwKeepj [keepalt] <OPT> <CMD> <FILENAME> +fun! s:NetrwEditFile(cmd,opt,fname) +" call Dfunc("s:NetrwEditFile(cmd<".a:cmd.">,opt<".a:opt.">,fname<".a:fname.">) ft<".&ft.">") + if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" +" call Decho("exe NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname)) + exe "NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname) + else +" call Decho("exe NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname)) + exe "NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname) + endif +" call Dret("s:NetrwEditFile") +endfun + +" --------------------------------------------------------------------- " s:NetrwExploreListUniq: {{{2 fun! s:NetrwExploreListUniq(explist) " call Dfunc("s:NetrwExploreListUniq(explist<".string(a:explist).">)") @@ -7278,8 +7332,7 @@ fun! s:NetrwMarkFileDiff(islocal) exe "NetrwKeepj e ".fnameescape(fname) diffthis elseif cnt == 2 || cnt == 3 - vsplit - wincmd l + below vsplit " call Decho("diffthis: ".fname,'~'.expand("<slnum>")) exe "NetrwKeepj e ".fnameescape(fname) diffthis @@ -7756,8 +7809,16 @@ fun! s:NetrwMarkFileMove(islocal) " call Decho("movecmd<".movecmd."> (#3 linux or cygwin)",'~'.expand("<slnum>")) endif for fname in s:netrwmarkfilelist_{bufnr("%")} + if g:netrw_keepdir + " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1 + let fname= b:netrw_curdir."/".fname + endif if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) let fname= substitute(fname,'/','\\','g') + if g:netrw_keepdir + " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1 + let fname= b:netrw_curdir."\\".fname + endif endif " call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>")) let ret= system(movecmd.g:netrw_localmovecmdopt." ".s:ShellEscape(fname)." ".tgt) @@ -8475,21 +8536,28 @@ endfun " choice = 2 : didn't save modified file, opened window " choice = 3 : cancel open fun! s:NetrwPrevWinOpen(islocal) -" call Dfunc("s:NetrwPrevWinOpen(islocal=".a:islocal.")") +" call Dfunc("s:NetrwPrevWinOpen(islocal=".a:islocal.") win#".winnr()) let ykeep= @@ " grab a copy of the b:netrw_curdir to pass it along to newly split windows let curdir = b:netrw_curdir +" call Decho("COMBAK#1: mod=".&mod." win#".winnr()) " get last window number and the word currently under the cursor let origwin = winnr() let lastwinnr = winnr("$") - let curword = s:NetrwGetWord() - let choice = 0 - let s:prevwinopen= 1 " lets s:NetrwTreeDir() know that NetrwPrevWinOpen called it +" call Decho("origwin#".origwin." lastwinnr#".lastwinnr) +" call Decho("COMBAK#2: mod=".&mod." win#".winnr()) + let curword = s:NetrwGetWord() + let choice = 0 + let s:prevwinopen= 1 " lets s:NetrwTreeDir() know that NetrwPrevWinOpen called it (s:NetrwTreeDir() will unlet s:prevwinopen) +" call Decho("COMBAK#3: mod=".&mod." win#".winnr()) let s:treedir = s:NetrwTreeDir(a:islocal) +" call Decho("COMBAK#4: mod=".&mod." win#".winnr()) let curdir = s:treedir +" call Decho("COMBAK#5: mod=".&mod." win#".winnr()) " call Decho("winnr($)#".lastwinnr." curword<".curword.">",'~'.expand("<slnum>")) +" call Decho("COMBAK#6: mod=".&mod." win#".winnr()) let didsplit = 0 if lastwinnr == 1 @@ -8512,11 +8580,26 @@ fun! s:NetrwPrevWinOpen(islocal) " call Decho("did split",'~'.expand("<slnum>")) else +" call Decho("COMBAK#7: mod=".&mod." win#".winnr()) NetrwKeepj call s:SaveBufVars() +" call Decho("COMBAK#8: mod=".&mod." win#".winnr()) let eikeep= &ei +" call Decho("COMBAK#9: mod=".&mod." win#".winnr()) setl ei=all +" call Decho("COMBAK#10: mod=".&mod." win#".winnr()) wincmd p +" call Decho("COMBAK#11: mod=".&mod) " call Decho("wincmd p (now in win#".winnr().") curdir<".curdir.">",'~'.expand("<slnum>")) +" call Decho("COMBAK#12: mod=".&mod) + + if exists("s:lexplore_win") && s:lexplore_win == winnr() + " whoops -- user trying to open file in the Lexplore window. + " Use Lexplore's opening-file window instead. +" call Decho("whoops -- user trying to open file in Lexplore Window. Use win#".g:netrw_chgwin." instead") +" exe g:netrw_chgwin."wincmd w" + wincmd p + call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord())) + endif " prevwinnr: the window number of the "prev" window " prevbufnr: the buffer number of the buffer in the "prev" window @@ -8526,8 +8609,10 @@ fun! s:NetrwPrevWinOpen(islocal) let prevbufname = bufname("%") let prevmod = &mod let bnrcnt = 0 +" call Decho("COMBAK#13: mod=".&mod." win#".winnr()) NetrwKeepj call s:RestoreBufVars() " call Decho("after wincmd p: win#".winnr()." win($)#".winnr("$")." origwin#".origwin." &mod=".&mod." bufname(%)<".bufname("%")."> prevbufnr=".prevbufnr,'~'.expand("<slnum>")) +" call Decho("COMBAK#14: mod=".&mod." win#".winnr()) " if the previous window's buffer has been changed (ie. its modified flag is set), " and it doesn't appear in any other extant window, then ask the @@ -8537,6 +8622,7 @@ fun! s:NetrwPrevWinOpen(islocal) windo if winbufnr(0) == prevbufnr | let bnrcnt=bnrcnt+1 | endif " call Decho("prevbufnr=".prevbufnr." bnrcnt=".bnrcnt." buftype=".&bt." winnr()=".winnr()." prevwinnr#".prevwinnr,'~'.expand("<slnum>")) exe prevwinnr."wincmd w" +" call Decho("COMBAK#15: mod=".&mod." win#".winnr()) if bnrcnt == 1 && &hidden == 0 " only one copy of the modified buffer in a window, and @@ -8544,6 +8630,7 @@ fun! s:NetrwPrevWinOpen(islocal) let choice = confirm("Save modified buffer<".prevbufname."> first?","&Yes\n&No\n&Cancel") " call Decho("prevbufname<".prevbufname."> choice=".choice." current-winnr#".winnr(),'~'.expand("<slnum>")) let &ei= eikeep +" call Decho("COMBAK#16: mod=".&mod." win#".winnr()) if choice == 1 " Yes -- write file & then browse @@ -8576,6 +8663,7 @@ fun! s:NetrwPrevWinOpen(islocal) endif let &ei= eikeep endif +" call Decho("COMBAK#17: mod=".&mod." win#".winnr()) " restore b:netrw_curdir (window split/enew may have lost it) let b:netrw_curdir= curdir @@ -9263,19 +9351,23 @@ fun! s:NetrwTreeDir(islocal) if exists("s:prevwinopen") unlet s:prevwinopen endif +" call Decho("COMBAK#18 : mod=".&mod." win#".winnr()) if !exists("b:netrw_curdir") || b:netrw_curdir == "" let b:netrw_curdir= getcwd() endif let treedir = b:netrw_curdir " call Decho("set initial treedir<".treedir.">",'~'.expand("<slnum>")) +" call Decho("COMBAK#19 : mod=".&mod." win#".winnr()) let s:treecurpos= winsaveview() " call Decho("saving posn to s:treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>")) +" call Decho("COMBAK#20 : mod=".&mod." win#".winnr()) if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST " call Decho("w:netrw_liststyle is TREELIST:",'~'.expand("<slnum>")) " call Decho("line#".line(".")." getline(.)<".getline('.')."> treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>")) +" call Decho("COMBAK#21 : mod=".&mod." win#".winnr()) " extract tree directory if on a line specifying a subdirectory (ie. ends with "/") let curline= substitute(getline('.'),"\t -->.*$",'','') @@ -9291,6 +9383,7 @@ fun! s:NetrwTreeDir(islocal) " call Decho("do not extract tree subdirectory from current line and set treedir to empty",'~'.expand("<slnum>")) let treedir= "" endif +" call Decho("COMBAK#22 : mod=".&mod." win#".winnr()) " detect user attempting to close treeroot " call Decho("check if user is attempting to close treeroot",'~'.expand("<slnum>")) @@ -9306,10 +9399,12 @@ fun! s:NetrwTreeDir(islocal) " else " Decho " call Decho(".user not attempting to close treeroot",'~'.expand("<slnum>")) endif +" call Decho("COMBAK#23 : mod=".&mod." win#".winnr()) " call Decho("islocal=".a:islocal." curline<".curline.">",'~'.expand("<slnum>")) let potentialdir= s:NetrwFile(substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)@$','\1','')) " call Decho("potentialdir<".potentialdir."> isdir=".isdirectory(potentialdir),'~'.expand("<slnum>")) +" call Decho("COMBAK#24 : mod=".&mod." win#".winnr()) " COMBAK: a symbolic link may point anywhere -- so it will be used to start a new treetop " if a:islocal && curline =~ '@$' && isdirectory(s:NetrwFile(potentialdir)) @@ -9323,10 +9418,12 @@ fun! s:NetrwTreeDir(islocal) let treedir = s:NetrwTreePath(w:netrw_treetop) " endif endif +" call Decho("COMBAK#25 : mod=".&mod." win#".winnr()) " sanity maintenance: keep those //s away... let treedir= substitute(treedir,'//$','/','') " call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) +" call Decho("COMBAK#26 : mod=".&mod." win#".winnr()) " call Dret("s:NetrwTreeDir <".treedir."> : (side effect) s:treecurpos<".(exists("s:treecurpos")? string(s:treecurpos) : 'n/a').">") return treedir @@ -10687,7 +10784,8 @@ fun! netrw#LocalBrowseCheck(dirname) " call Dfunc("netrw#LocalBrowseCheck(dirname<".a:dirname.">)") " call Decho("isdir<".a:dirname."> =".isdirectory(s:NetrwFile(a:dirname)).((exists("s:treeforceredraw")? " treeforceredraw" : "")).'~'.expand("<slnum>")) " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo,'~'.expand("<slnum>")) -" call Dredir("ls!","netrw#LocalBrowseCheck") + " getting E930: Cannot use :redir inside execute +"" call Dredir("ls!","netrw#LocalBrowseCheck") " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Decho("current buffer#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>")) @@ -10980,13 +11078,16 @@ fun! s:LocalListing() " call Decho("pfile <".pfile.">",'~'.expand("<slnum>")) if w:netrw_liststyle == s:LONGLIST + let longfile= printf("%-".g:netrw_maxfilenamelen."S",pfile) let sz = getfsize(filename) - let fsz = strpart(" ",1,15-strlen(sz)).sz + let szlen = 15 - (strdisplaywidth(longfile) - g:netrw_maxfilenamelen) + let szlen = (szlen > 0) ? szlen : 0 + if g:netrw_sizestyle =~# "[hH]" let sz= s:NetrwHumanReadable(sz) endif - let longfile= printf("%-".(g:netrw_maxfilenamelen+1)."s",pfile) - let pfile = longfile.sz." ".strftime(g:netrw_timefmt,getftime(filename)) + let fsz = printf("%".szlen."S",sz) + let pfile = longfile." ".fsz." ".strftime(g:netrw_timefmt,getftime(filename)) " call Decho("longlist support: sz=".sz." fsz=".fsz,'~'.expand("<slnum>")) endif @@ -10996,7 +11097,7 @@ fun! s:LocalListing() " call Decho("implementing g:netrw_sort_by=".g:netrw_sort_by." (time)") " call Decho("getftime(".filename.")=".getftime(filename),'~'.expand("<slnum>")) let t = getftime(filename) - let ft = strpart("000000000000000000",1,18-strlen(t)).t + let ft = printf("%018d",t) " call Decho("exe NetrwKeepj put ='".ft.'/'.pfile."'",'~'.expand("<slnum>")) let ftpfile= ft.'/'.pfile sil! NetrwKeepj put=ftpfile @@ -11006,10 +11107,7 @@ fun! s:LocalListing() " call Decho("implementing g:netrw_sort_by=".g:netrw_sort_by." (size)") " call Decho("getfsize(".filename.")=".getfsize(filename),'~'.expand("<slnum>")) let sz = getfsize(filename) - if g:netrw_sizestyle =~# "[hH]" - let sz= s:NetrwHumanReadable(sz) - endif - let fsz = strpart("000000000000000000",1,18-strlen(sz)).sz + let fsz = printf("%018d",sz) " call Decho("exe NetrwKeepj put ='".fsz.'/'.filename."'",'~'.expand("<slnum>")) let fszpfile= fsz.'/'.pfile sil! NetrwKeepj put =fszpfile @@ -11285,34 +11383,8 @@ fun! s:NetrwLocalRmFile(path,fname,all) let rmfile= substitute(rmfile,'[\/]$','','e') if all || ok =~# 'y\%[es]' || ok == "" - if v:version < 704 || (v:version == 704 && !has("patch1107")) -" " call Decho("1st attempt: system(netrw#WinPath(".g:netrw_localrmdir.') '.s:ShellEscape(rmfile).')','~'.expand("<slnum>")) - call system(netrw#WinPath(g:netrw_localrmdir).' '.s:ShellEscape(rmfile)) -" " call Decho("v:shell_error=".v:shell_error,'~'.expand("<slnum>")) - - if v:shell_error != 0 -" " call Decho("2nd attempt to remove directory<".rmfile.">",'~'.expand("<slnum>")) - let errcode= s:NetrwDelete(rmfile) -" " call Decho("errcode=".errcode,'~'.expand("<slnum>")) - - if errcode != 0 - if has("unix") -" " call Decho("3rd attempt to remove directory<".rmfile.">",'~'.expand("<slnum>")) - call system("rm ".s:ShellEscape(rmfile)) - if v:shell_error != 0 && !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",34) - let ok="no" - endif - elseif !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",35) - let ok="no" - endif - endif - endif - else - if delete(rmfile,"d") - call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103) - endif + if delete(rmfile,"d") + call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103) endif endif endif @@ -11868,9 +11940,9 @@ fun! s:NetrwBufRemover(bufid) " call Decho("buf#".a:bufid." has name <".bufname(a:bufid).">","~".expand("<slnum>")) " call Decho("buf#".a:bufid." has winid#".bufwinid(a:bufid),"~".expand("<slnum>")) - if a:bufid > 1 && !buflisted(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1 + if a:bufid > 1 && !buflisted(a:bufid) && bufloaded(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1 " call Decho("(s:NetrwBufRemover) removing buffer#".a:bufid,"~".expand("<slnum>")) - exe "bd! ".a:bufid + exe "sil! bd! ".a:bufid endif " call Dret("s:NetrwBufRemover") @@ -11907,13 +11979,13 @@ fun! s:NetrwEnew(...) if exists("b:netrw_prvdir") |let netrw_prvdir = b:netrw_prvdir |endif NetrwKeepj call s:NetrwOptionsRestore("w:") -" call Decho("generate a buffer with NetrwKeepj keepalt enew!",'~'.expand("<slnum>")) +" call Decho("generate a buffer with NetrwKeepj enew!",'~'.expand("<slnum>")) " when tree listing uses file TreeListing... a new buffer is made. " Want the old buffer to be unlisted. " COMBAK: this causes a problem, see P43 " setl nobl let netrw_keepdiff= &l:diff - noswapfile NetrwKeepj keepalt enew! + call s:NetrwEditFile("enew!","","") let &l:diff= netrw_keepdiff " call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionsSave("w:") @@ -11962,7 +12034,7 @@ endfun " s:NetrwExe: executes a string using "!" {{{2 fun! s:NetrwExe(cmd) " call Dfunc("s:NetrwExe(a:cmd<".a:cmd.">)") - if has("win32") && &shell !~? 'cmd' && !g:netrw_cygwin + if has("win32") && &shell !~? 'cmd\|pwsh\|powershell' && !g:netrw_cygwin " call Decho("using win32:",expand("<slnum>")) let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] set shell& shellcmdflag& shellxquote& shellxescape& @@ -12631,3 +12703,54 @@ unlet s:keepcpo " Modelines: {{{1 " =============== " vim:ts=8 fdm=marker +" doing autoload/netrw.vim version v172g ~57 +" varname<g:netrw_dirhistcnt> value=0 ~1 +" varname<s:THINLIST> value=0 ~1 +" varname<s:LONGLIST> value=1 ~1 +" varname<s:WIDELIST> value=2 ~1 +" varname<s:TREELIST> value=3 ~1 +" varname<s:MAXLIST> value=4 ~1 +" varname<g:netrw_use_errorwindow> value=2 ~1 +" varname<g:netrw_http_xcmd> value=-q -O ~1 +" varname<g:netrw_http_put_cmd> value=curl -T ~1 +" varname<g:netrw_keepj> value=keepj ~1 +" varname<g:netrw_rcp_cmd> value=rcp ~1 +" varname<g:netrw_rsync_cmd> value=rsync ~1 +" varname<g:netrw_rsync_sep> value=/ ~1 +" varname<g:netrw_scp_cmd> value=scp -q ~1 +" varname<g:netrw_sftp_cmd> value=sftp ~1 +" varname<g:netrw_ssh_cmd> value=ssh ~1 +" varname<g:netrw_alto> value=0 ~1 +" varname<g:netrw_altv> value=1 ~1 +" varname<g:netrw_banner> value=1 ~1 +" varname<g:netrw_browse_split> value=0 ~1 +" varname<g:netrw_bufsettings> value=noma nomod nonu nobl nowrap ro nornu ~1 +" varname<g:netrw_chgwin> value=-1 ~1 +" varname<g:netrw_clipboard> value=1 ~1 +" varname<g:netrw_compress> value=gzip ~1 +" varname<g:netrw_ctags> value=ctags ~1 +" varname<g:netrw_cursor> value=2 ~1 +" (netrw) COMBAK: cuc=0 cul=0 initialization of s:netrw_cu[cl] +" varname<g:netrw_cygdrive> value=/cygdrive ~1 +" varname<s:didstarstar> value=0 ~1 +" varname<g:netrw_dirhistcnt> value=0 ~1 +" varname<g:netrw_decompress> value={ ".gz" : "gunzip", ".bz2" : "bunzip2", ".zip" : "unzip", ".tar" : "tar -xf", ".xz" : "unxz" } ~1 +" varname<g:netrw_dirhistmax> value=10 ~1 +" varname<g:netrw_errorlvl> value=0 ~1 +" varname<g:netrw_fastbrowse> value=1 ~1 +" varname<g:netrw_ftp_browse_reject> value=^total\s\+\d\+$\|^Trying\s\+\d\+.*$\|^KERBEROS_V\d rejected\|^Security extensions not\|No such file\|: connect to address [0-9a-fA-F:]*: No route to host$ ~1 +" varname<g:netrw_ftpmode> value=binary ~1 +" varname<g:netrw_hide> value=1 ~1 +" varname<g:netrw_keepdir> value=1 ~1 +" varname<g:netrw_list_hide> value= ~1 +" varname<g:netrw_localmkdir> value=mkdir ~1 +" varname<g:netrw_remote_mkdir> value=mkdir ~1 +" varname<g:netrw_liststyle> value=0 ~1 +" varname<g:netrw_markfileesc> value=*./[\~ ~1 +" varname<g:netrw_maxfilenamelen> value=32 ~1 +" varname<g:netrw_menu> value=1 ~1 +" varname<g:netrw_mkdir_cmd> value=ssh USEPORT HOSTNAME mkdir ~1 +" varname<g:netrw_mousemaps> value=1 ~1 +" varname<g:netrw_retmap> value=0 ~1 +" varname<g:netrw_chgperm> value=chmod PERM FILENAME ~1 +" varname<g:netrw_preview> value=0 ~1 diff --git a/runtime/autoload/netrwSettings.vim b/runtime/autoload/netrwSettings.vim index 61c0ef739e..d65f83144e 100644 --- a/runtime/autoload/netrwSettings.vim +++ b/runtime/autoload/netrwSettings.vim @@ -1,7 +1,7 @@ " netrwSettings.vim: makes netrw settings simpler -" Date: Aug 12, 2021 +" Date: Nov 15, 2021 " Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> -" Version: 17 ASTRO-ONLY +" Version: 18 " Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -19,7 +19,7 @@ if exists("g:loaded_netrwSettings") || &cp finish endif -let g:loaded_netrwSettings = "v17" +let g:loaded_netrwSettings = "v18" if v:version < 700 echohl WarningMsg echo "***warning*** this version of netrwSettings needs vim 7.0" @@ -159,8 +159,6 @@ fun! netrwSettings#NetrwSettings() put = 'let g:netrw_localmkdiropt = '.g:netrw_localmkdiropt put = 'let g:netrw_localmovecmd = '.g:netrw_localmovecmd put = 'let g:netrw_localmovecmdopt = '.g:netrw_localmovecmdopt - put = 'let g:netrw_localrmdir = '.g:netrw_localrmdir - put = 'let g:netrw_localrmdiropt = '.g:netrw_localrmdiropt put = 'let g:netrw_maxfilenamelen = '.g:netrw_maxfilenamelen put = 'let g:netrw_menu = '.g:netrw_menu put = 'let g:netrw_mousemaps = '.g:netrw_mousemaps diff --git a/runtime/autoload/paste.vim b/runtime/autoload/paste.vim index 2d787e7a1d..1ba336c2b3 100644 --- a/runtime/autoload/paste.vim +++ b/runtime/autoload/paste.vim @@ -1,6 +1,7 @@ " Vim support file to help with paste mappings and menus -" Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2019 Jan 27 +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> " Define the string to use for items that are present both in Edit, Popup and " Toolbar menu. Also used in mswin.vim and macmap.vim. diff --git a/runtime/autoload/phpcomplete.vim b/runtime/autoload/phpcomplete.vim index f9aa15c827..2556ac857e 100644 --- a/runtime/autoload/phpcomplete.vim +++ b/runtime/autoload/phpcomplete.vim @@ -2924,7 +2924,7 @@ endfor " builtin class information let g:php_builtin_object_functions = {} -" When completing for 'everyting imaginable' (no class context, not a +" When completing for 'everything imaginable' (no class context, not a " variable) we need a list of built-in classes in a format of {'classname':''} " for performance reasons we precompile this too let g:php_builtin_classnames = {} diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 98c80f1843..05f6bdb871 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -25,7 +25,8 @@ function! s:selection.on_exit(jobid, data, event) abort if self.owner == a:jobid let self.owner = 0 endif - if a:data != 0 + " Don't print if exit code is >= 128 ( exit is 128+SIGNUM if by signal (e.g. 143 on SIGTERM)) + if a:data > 0 && a:data < 128 echohl WarningMsg echomsg 'clipboard: error invoking '.get(self.argv, 0, '?').': '.join(self.stderr) echohl None @@ -92,9 +93,9 @@ function! provider#clipboard#Executable() abort let s:cache_enabled = 0 return 'pbcopy' elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste') - let s:copy['+'] = ['wl-copy', '--foreground', '--type', 'text/plain'] + let s:copy['+'] = ['wl-copy', '--type', 'text/plain'] let s:paste['+'] = ['wl-paste', '--no-newline'] - let s:copy['*'] = ['wl-copy', '--foreground', '--primary', '--type', 'text/plain'] + let s:copy['*'] = ['wl-copy', '--primary', '--type', 'text/plain'] let s:paste['*'] = ['wl-paste', '--no-newline', '--primary'] return 'wl-copy' elseif !empty($WAYLAND_DISPLAY) && executable('waycopy') && executable('waypaste') @@ -145,8 +146,8 @@ function! provider#clipboard#Executable() abort let s:paste['*'] = s:paste['+'] return 'termux-clipboard' elseif !empty($TMUX) && executable('tmux') - let ver = matchlist(systemlist(['tmux', '-V'])[0], '\vtmux %(next-)?(\d+)\.(\d+)') - if len(ver) >= 3 && (ver[1] > 3 || (ver[1] == 3 && ver[2] >= 2)) + let tmux_v = v:lua.vim.version.parse(system(['tmux', '-V'])) + if !empty(tmux_v) && !v:lua.vim.version.lt(tmux_v, [3,2,0]) let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-'] else let s:copy['+'] = ['tmux', 'load-buffer', '-'] diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index 87af0094fe..3e7b8b4ef9 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -3,7 +3,7 @@ if exists('g:loaded_node_provider') endif let g:loaded_node_provider = 1 -function! s:is_minimum_version(version, min_major, min_minor) abort +function! s:is_minimum_version(version, min_version) abort if empty(a:version) let nodejs_version = get(split(system(['node', '-v']), "\n"), 0, '') if v:shell_error || nodejs_version[0] !=# 'v' @@ -15,11 +15,7 @@ function! s:is_minimum_version(version, min_major, min_minor) abort " Remove surrounding junk. Example: 'v4.12.0' => '4.12.0' let nodejs_version = matchstr(nodejs_version, '\(\d\.\?\)\+') " [major, minor, patch] - let v_list = split(nodejs_version, '\.') - return len(v_list) == 3 - \ && ((str2nr(v_list[0]) > str2nr(a:min_major)) - \ || (str2nr(v_list[0]) == str2nr(a:min_major) - \ && str2nr(v_list[1]) >= str2nr(a:min_minor))) + return !v:lua.vim.version.lt(nodejs_version, a:min_version) endfunction let s:NodeHandler = { @@ -43,20 +39,20 @@ function! provider#node#can_inspect() abort if v:shell_error || ver[0] !=# 'v' return 0 endif - return (ver[1] ==# '6' && s:is_minimum_version(ver, 6, 12)) - \ || s:is_minimum_version(ver, 7, 6) + return (ver[1] ==# '6' && s:is_minimum_version(ver, '6.12.0')) + \ || s:is_minimum_version(ver, '7.6.0') endfunction function! provider#node#Detect() abort - let minver = [6, 0] + let minver = '6.0.0' if exists('g:node_host_prog') return [expand(g:node_host_prog, v:true), ''] endif if !executable('node') return ['', 'node not found (or not executable)'] endif - if !s:is_minimum_version(v:null, minver[0], minver[1]) - return ['', printf('node version %s.%s not found', minver[0], minver[1])] + if !s:is_minimum_version(v:null, minver) + return ['', printf('node version %s not found', minver)] endif let npm_opts = {} diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 6211b457d6..48b96c699a 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -26,7 +26,7 @@ endfunction function! s:get_python_candidates(major_version) abort return { - \ 3: ['python3', 'python3.10', 'python3.9', 'python3.8', 'python3.7', 'python'] + \ 3: ['python3', 'python3.12', 'python3.11', 'python3.10', 'python3.9', 'python3.8', 'python3.7', 'python'] \ }[a:major_version] endfunction @@ -61,12 +61,11 @@ endfunction " Returns array: [prog_exitcode, prog_version] function! s:import_module(prog, module) abort - let prog_version = system([a:prog, '-c' , printf( - \ 'import sys; ' . + let prog_version = system([a:prog, '-W', 'ignore', '-c', printf( + \ 'import sys, importlib.util; ' . \ 'sys.path = [p for p in sys.path if p != ""]; ' . \ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' . - \ 'import pkgutil; ' . - \ 'exit(2*int(pkgutil.get_loader("%s") is None))', + \ 'sys.exit(2 * int(importlib.util.find_spec("%s") is None))', \ a:module)]) return [v:shell_error, prog_version] endfunction diff --git a/runtime/autoload/python.vim b/runtime/autoload/python.vim index 1eaad09ef5..d5f4862363 100644 --- a/runtime/autoload/python.vim +++ b/runtime/autoload/python.vim @@ -22,8 +22,7 @@ let s:maxoff = 50 " maximum number of lines to look backwards for () function s:SearchBracket(fromlnum, flags) return searchpairpos('[[({]', '', '[])}]', a:flags, \ {-> synstack('.', col('.')) - \ ->map({_, id -> id->synIDattr('name')}) - \ ->match('\%(Comment\|Todo\|String\)$') >= 0}, + \ ->indexof({_, id -> synIDattr(id, 'name') =~ '\%(Comment\|Todo\|String\)$'}) >= 0}, \ [0, a:fromlnum - s:maxoff]->max(), g:python_indent.searchpair_timeout) endfunction @@ -157,15 +156,13 @@ function python#GetIndent(lnum, ...) " the start of the comment. synID() is slow, a linear search would take " too long on a long line. if synstack(plnum, pline_len) - \ ->map({_, id -> id->synIDattr('name')}) - \ ->match('\%(Comment\|Todo\)$') >= 0 + \ ->indexof({_, id -> synIDattr(id, 'name') =~ '\%(Comment\|Todo\)$'}) >= 0 let min = 1 let max = pline_len while min < max let col = (min + max) / 2 if synstack(plnum, col) - \ ->map({_, id -> id->synIDattr('name')}) - \ ->match('\%(Comment\|Todo\)$') >= 0 + \ ->indexof({_, id -> synIDattr(id, 'name') =~ '\%(Comment\|Todo\)$'}) >= 0 let max = col else let min = col + 1 diff --git a/runtime/autoload/rust.vim b/runtime/autoload/rust.vim index 34a3b41773..5ccbf4b382 100644 --- a/runtime/autoload/rust.vim +++ b/runtime/autoload/rust.vim @@ -1,207 +1,258 @@ -" Author: Kevin Ballard " Description: Helper functions for Rust commands/mappings -" Last Modified: May 27, 2014 +" Last Modified: 2023-09-11 " For bugs, patches and license go to https://github.com/rust-lang/rust.vim +function! rust#Load() + " Utility call to get this script loaded, for debugging +endfunction + +function! rust#GetConfigVar(name, default) + " Local buffer variable with same name takes predeence over global + if has_key(b:, a:name) + return get(b:, a:name) + endif + if has_key(g:, a:name) + return get(g:, a:name) + endif + return a:default +endfunction + +" Include expression {{{1 + +function! rust#IncludeExpr(fname) abort + " Remove leading 'crate::' to deal with 2018 edition style 'use' + " statements + let l:fname = substitute(a:fname, '^crate::', '', '') + + " Remove trailing colons arising from lines like + " + " use foo::{Bar, Baz}; + let l:fname = substitute(l:fname, ':\+$', '', '') + + " Replace '::' with '/' + let l:fname = substitute(l:fname, '::', '/', 'g') + + " When we have + " + " use foo::bar::baz; + " + " we can't tell whether baz is a module or a function; and we can't tell + " which modules correspond to files. + " + " So we work our way up, trying + " + " foo/bar/baz.rs + " foo/bar.rs + " foo.rs + while l:fname !=# '.' + let l:path = findfile(l:fname) + if !empty(l:path) + return l:fname + endif + let l:fname = fnamemodify(l:fname, ':h') + endwhile + return l:fname +endfunction + " Jump {{{1 function! rust#Jump(mode, function) range - let cnt = v:count1 - normal! m' - if a:mode ==# 'v' - norm! gv - endif - let foldenable = &foldenable - set nofoldenable - while cnt > 0 - execute "call <SID>Jump_" . a:function . "()" - let cnt = cnt - 1 - endwhile - let &foldenable = foldenable + let cnt = v:count1 + normal! m' + if a:mode ==# 'v' + norm! gv + endif + let foldenable = &foldenable + set nofoldenable + while cnt > 0 + execute "call <SID>Jump_" . a:function . "()" + let cnt = cnt - 1 + endwhile + let &foldenable = foldenable endfunction function! s:Jump_Back() - call search('{', 'b') - keepjumps normal! w99[{ + call search('{', 'b') + keepjumps normal! w99[{ endfunction function! s:Jump_Forward() - normal! j0 - call search('{', 'b') - keepjumps normal! w99[{% - call search('{') + normal! j0 + call search('{', 'b') + keepjumps normal! w99[{% + call search('{') endfunction " Run {{{1 function! rust#Run(bang, args) - let args = s:ShellTokenize(a:args) - if a:bang - let idx = index(l:args, '--') - if idx != -1 - let rustc_args = idx == 0 ? [] : l:args[:idx-1] - let args = l:args[idx+1:] - else - let rustc_args = l:args - let args = [] - endif - else - let rustc_args = [] - endif - - let b:rust_last_rustc_args = l:rustc_args - let b:rust_last_args = l:args - - call s:WithPath(function("s:Run"), rustc_args, args) + let args = s:ShellTokenize(a:args) + if a:bang + let idx = index(l:args, '--') + if idx != -1 + let rustc_args = idx == 0 ? [] : l:args[:idx-1] + let args = l:args[idx+1:] + else + let rustc_args = l:args + let args = [] + endif + else + let rustc_args = [] + endif + + let b:rust_last_rustc_args = l:rustc_args + let b:rust_last_args = l:args + + call s:WithPath(function("s:Run"), rustc_args, args) endfunction function! s:Run(dict, rustc_args, args) - let exepath = a:dict.tmpdir.'/'.fnamemodify(a:dict.path, ':t:r') - if has('win32') - let exepath .= '.exe' - endif - - let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) - let rustc_args = [relpath, '-o', exepath] + a:rustc_args - - let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" - - let pwd = a:dict.istemp ? a:dict.tmpdir : '' - let output = s:system(pwd, shellescape(rustc) . " " . join(map(rustc_args, 'shellescape(v:val)'))) - if output != '' - echohl WarningMsg - echo output - echohl None - endif - if !v:shell_error - exe '!' . shellescape(exepath) . " " . join(map(a:args, 'shellescape(v:val)')) - endif + let exepath = a:dict.tmpdir.'/'.fnamemodify(a:dict.path, ':t:r') + if has('win32') + let exepath .= '.exe' + endif + + let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) + let rustc_args = [relpath, '-o', exepath] + a:rustc_args + + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + let pwd = a:dict.istemp ? a:dict.tmpdir : '' + let output = s:system(pwd, shellescape(rustc) . " " . join(map(rustc_args, 'shellescape(v:val)'))) + if output !=# '' + echohl WarningMsg + echo output + echohl None + endif + if !v:shell_error + exe '!' . shellescape(exepath) . " " . join(map(a:args, 'shellescape(v:val)')) + endif endfunction " Expand {{{1 function! rust#Expand(bang, args) - let args = s:ShellTokenize(a:args) - if a:bang && !empty(l:args) - let pretty = remove(l:args, 0) - else - let pretty = "expanded" - endif - call s:WithPath(function("s:Expand"), pretty, args) + let args = s:ShellTokenize(a:args) + if a:bang && !empty(l:args) + let pretty = remove(l:args, 0) + else + let pretty = "expanded" + endif + call s:WithPath(function("s:Expand"), pretty, args) endfunction function! s:Expand(dict, pretty, args) - try - let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" - - if a:pretty =~? '^\%(everybody_loops$\|flowgraph=\)' - let flag = '--xpretty' - else - let flag = '--pretty' - endif - let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) - let args = [relpath, '-Z', 'unstable-options', l:flag, a:pretty] + a:args - let pwd = a:dict.istemp ? a:dict.tmpdir : '' - let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) - if v:shell_error - echohl WarningMsg - echo output - echohl None - else - new - silent put =output - 1 - d - setl filetype=rust - setl buftype=nofile - setl bufhidden=hide - setl noswapfile - " give the buffer a nice name - let suffix = 1 - let basename = fnamemodify(a:dict.path, ':t:r') - while 1 - let bufname = basename - if suffix > 1 | let bufname .= ' ('.suffix.')' | endif - let bufname .= '.pretty.rs' - if bufexists(bufname) - let suffix += 1 - continue - endif - exe 'silent noautocmd keepalt file' fnameescape(bufname) - break - endwhile - endif - endtry + try + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + if a:pretty =~? '^\%(everybody_loops$\|flowgraph=\)' + let flag = '--xpretty' + else + let flag = '--pretty' + endif + let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) + let args = [relpath, '-Z', 'unstable-options', l:flag, a:pretty] + a:args + let pwd = a:dict.istemp ? a:dict.tmpdir : '' + let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) + if v:shell_error + echohl WarningMsg + echo output + echohl None + else + new + silent put =output + 1 + d + setl filetype=rust + setl buftype=nofile + setl bufhidden=hide + setl noswapfile + " give the buffer a nice name + let suffix = 1 + let basename = fnamemodify(a:dict.path, ':t:r') + while 1 + let bufname = basename + if suffix > 1 | let bufname .= ' ('.suffix.')' | endif + let bufname .= '.pretty.rs' + if bufexists(bufname) + let suffix += 1 + continue + endif + exe 'silent noautocmd keepalt file' fnameescape(bufname) + break + endwhile + endif + endtry endfunction function! rust#CompleteExpand(lead, line, pos) - if a:line[: a:pos-1] =~ '^RustExpand!\s*\S*$' - " first argument and it has a ! - let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph=", "everybody_loops"] - if !empty(a:lead) - call filter(list, "v:val[:len(a:lead)-1] == a:lead") - endif - return list - endif - - return glob(escape(a:lead, "*?[") . '*', 0, 1) + if a:line[: a:pos-1] =~# '^RustExpand!\s*\S*$' + " first argument and it has a ! + let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph=", "everybody_loops"] + if !empty(a:lead) + call filter(list, "v:val[:len(a:lead)-1] == a:lead") + endif + return list + endif + + return glob(escape(a:lead, "*?[") . '*', 0, 1) endfunction " Emit {{{1 function! rust#Emit(type, args) - let args = s:ShellTokenize(a:args) - call s:WithPath(function("s:Emit"), a:type, args) + let args = s:ShellTokenize(a:args) + call s:WithPath(function("s:Emit"), a:type, args) endfunction function! s:Emit(dict, type, args) - try - let output_path = a:dict.tmpdir.'/output' - - let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" - - let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) - let args = [relpath, '--emit', a:type, '-o', output_path] + a:args - let pwd = a:dict.istemp ? a:dict.tmpdir : '' - let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) - if output != '' - echohl WarningMsg - echo output - echohl None - endif - if !v:shell_error - new - exe 'silent keepalt read' fnameescape(output_path) - 1 - d - if a:type == "llvm-ir" - setl filetype=llvm - let extension = 'll' - elseif a:type == "asm" - setl filetype=asm - let extension = 's' - endif - setl buftype=nofile - setl bufhidden=hide - setl noswapfile - if exists('l:extension') - " give the buffer a nice name - let suffix = 1 - let basename = fnamemodify(a:dict.path, ':t:r') - while 1 - let bufname = basename - if suffix > 1 | let bufname .= ' ('.suffix.')' | endif - let bufname .= '.'.extension - if bufexists(bufname) - let suffix += 1 - continue - endif - exe 'silent noautocmd keepalt file' fnameescape(bufname) - break - endwhile - endif - endif - endtry + try + let output_path = a:dict.tmpdir.'/output' + + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) + let args = [relpath, '--emit', a:type, '-o', output_path] + a:args + let pwd = a:dict.istemp ? a:dict.tmpdir : '' + let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) + if output !=# '' + echohl WarningMsg + echo output + echohl None + endif + if !v:shell_error + new + exe 'silent keepalt read' fnameescape(output_path) + 1 + d + if a:type ==# "llvm-ir" + setl filetype=llvm + let extension = 'll' + elseif a:type ==# "asm" + setl filetype=asm + let extension = 's' + endif + setl buftype=nofile + setl bufhidden=hide + setl noswapfile + if exists('l:extension') + " give the buffer a nice name + let suffix = 1 + let basename = fnamemodify(a:dict.path, ':t:r') + while 1 + let bufname = basename + if suffix > 1 | let bufname .= ' ('.suffix.')' | endif + let bufname .= '.'.extension + if bufexists(bufname) + let suffix += 1 + continue + endif + exe 'silent noautocmd keepalt file' fnameescape(bufname) + break + endwhile + endif + endif + endtry endfunction " Utility functions {{{1 @@ -219,145 +270,154 @@ endfunction " existing path of the current buffer. If the path is inside of {dict.tmpdir} " then it is guaranteed to have a '.rs' extension. function! s:WithPath(func, ...) - let buf = bufnr('') - let saved = {} - let dict = {} - try - let saved.write = &write - set write - let dict.path = expand('%') - let pathisempty = empty(dict.path) - - " Always create a tmpdir in case the wrapped command wants it - let dict.tmpdir = tempname() - call mkdir(dict.tmpdir) - - if pathisempty || !saved.write - let dict.istemp = 1 - " if we're doing this because of nowrite, preserve the filename - if !pathisempty - let filename = expand('%:t:r').".rs" - else - let filename = 'unnamed.rs' - endif - let dict.tmpdir_relpath = filename - let dict.path = dict.tmpdir.'/'.filename - - let saved.mod = &mod - set nomod - - silent exe 'keepalt write! ' . fnameescape(dict.path) - if pathisempty - silent keepalt 0file - endif - else - let dict.istemp = 0 - update - endif - - call call(a:func, [dict] + a:000) - finally - if bufexists(buf) - for [opt, value] in items(saved) - silent call setbufvar(buf, '&'.opt, value) - unlet value " avoid variable type mismatches - endfor - endif - if has_key(dict, 'tmpdir') | silent call s:RmDir(dict.tmpdir) | endif - endtry + let buf = bufnr('') + let saved = {} + let dict = {} + try + let saved.write = &write + set write + let dict.path = expand('%') + let pathisempty = empty(dict.path) + + " Always create a tmpdir in case the wrapped command wants it + let dict.tmpdir = tempname() + call mkdir(dict.tmpdir) + + if pathisempty || !saved.write + let dict.istemp = 1 + " if we're doing this because of nowrite, preserve the filename + if !pathisempty + let filename = expand('%:t:r').".rs" + else + let filename = 'unnamed.rs' + endif + let dict.tmpdir_relpath = filename + let dict.path = dict.tmpdir.'/'.filename + + let saved.mod = &modified + set nomodified + + silent exe 'keepalt write! ' . fnameescape(dict.path) + if pathisempty + silent keepalt 0file + endif + else + let dict.istemp = 0 + update + endif + + call call(a:func, [dict] + a:000) + finally + if bufexists(buf) + for [opt, value] in items(saved) + silent call setbufvar(buf, '&'.opt, value) + unlet value " avoid variable type mismatches + endfor + endif + if has_key(dict, 'tmpdir') | silent call s:RmDir(dict.tmpdir) | endif + endtry endfunction function! rust#AppendCmdLine(text) - call setcmdpos(getcmdpos()) - let cmd = getcmdline() . a:text - return cmd + call setcmdpos(getcmdpos()) + let cmd = getcmdline() . a:text + return cmd endfunction " Tokenize the string according to sh parsing rules function! s:ShellTokenize(text) - " states: - " 0: start of word - " 1: unquoted - " 2: unquoted backslash - " 3: double-quote - " 4: double-quoted backslash - " 5: single-quote - let l:state = 0 - let l:current = '' - let l:args = [] - for c in split(a:text, '\zs') - if l:state == 0 || l:state == 1 " unquoted - if l:c ==# ' ' - if l:state == 0 | continue | endif - call add(l:args, l:current) - let l:current = '' - let l:state = 0 - elseif l:c ==# '\' - let l:state = 2 - elseif l:c ==# '"' - let l:state = 3 - elseif l:c ==# "'" - let l:state = 5 - else - let l:current .= l:c - let l:state = 1 - endif - elseif l:state == 2 " unquoted backslash - if l:c !=# "\n" " can it even be \n? - let l:current .= l:c - endif - let l:state = 1 - elseif l:state == 3 " double-quote - if l:c ==# '\' - let l:state = 4 - elseif l:c ==# '"' - let l:state = 1 - else - let l:current .= l:c - endif - elseif l:state == 4 " double-quoted backslash - if stridx('$`"\', l:c) >= 0 - let l:current .= l:c - elseif l:c ==# "\n" " is this even possible? - " skip it - else - let l:current .= '\'.l:c - endif - let l:state = 3 - elseif l:state == 5 " single-quoted - if l:c == "'" - let l:state = 1 - else - let l:current .= l:c - endif - endif - endfor - if l:state != 0 - call add(l:args, l:current) - endif - return l:args + " states: + " 0: start of word + " 1: unquoted + " 2: unquoted backslash + " 3: double-quote + " 4: double-quoted backslash + " 5: single-quote + let l:state = 0 + let l:current = '' + let l:args = [] + for c in split(a:text, '\zs') + if l:state == 0 || l:state == 1 " unquoted + if l:c ==# ' ' + if l:state == 0 | continue | endif + call add(l:args, l:current) + let l:current = '' + let l:state = 0 + elseif l:c ==# '\' + let l:state = 2 + elseif l:c ==# '"' + let l:state = 3 + elseif l:c ==# "'" + let l:state = 5 + else + let l:current .= l:c + let l:state = 1 + endif + elseif l:state == 2 " unquoted backslash + if l:c !=# "\n" " can it even be \n? + let l:current .= l:c + endif + let l:state = 1 + elseif l:state == 3 " double-quote + if l:c ==# '\' + let l:state = 4 + elseif l:c ==# '"' + let l:state = 1 + else + let l:current .= l:c + endif + elseif l:state == 4 " double-quoted backslash + if stridx('$`"\', l:c) >= 0 + let l:current .= l:c + elseif l:c ==# "\n" " is this even possible? + " skip it + else + let l:current .= '\'.l:c + endif + let l:state = 3 + elseif l:state == 5 " single-quoted + if l:c ==# "'" + let l:state = 1 + else + let l:current .= l:c + endif + endif + endfor + if l:state != 0 + call add(l:args, l:current) + endif + return l:args endfunction function! s:RmDir(path) - " sanity check; make sure it's not empty, /, or $HOME - if empty(a:path) - echoerr 'Attempted to delete empty path' - return 0 - elseif a:path == '/' || a:path == $HOME - echoerr 'Attempted to delete protected path: ' . a:path - return 0 - endif - return system("rm -rf " . shellescape(a:path)) + " sanity check; make sure it's not empty, /, or $HOME + if empty(a:path) + echoerr 'Attempted to delete empty path' + return 0 + elseif a:path ==# '/' || a:path ==# $HOME + let l:path = expand(a:path) + if l:path ==# '/' || l:path ==# $HOME + echoerr 'Attempted to delete protected path: ' . a:path + return 0 + endif + endif + + if !isdirectory(a:path) + return 0 + endif + + " delete() returns 0 when removing file successfully + return delete(a:path, 'rf') == 0 endfunction " Executes {cmd} with the cwd set to {pwd}, without changing Vim's cwd. " If {pwd} is the empty string then it doesn't change the cwd. function! s:system(pwd, cmd) - let cmd = a:cmd - if !empty(a:pwd) - let cmd = 'cd ' . shellescape(a:pwd) . ' && ' . cmd - endif - return system(cmd) + let cmd = a:cmd + if !empty(a:pwd) + let cmd = 'cd ' . shellescape(a:pwd) . ' && ' . cmd + endif + return system(cmd) endfunction " Playpen Support {{{1 @@ -366,10 +426,10 @@ endfunction " http://github.com/mattn/gist-vim function! s:has_webapi() if !exists("*webapi#http#post") - try - call webapi#http#post() - catch - endtry + try + call webapi#http#post() + catch + endtry endif return exists("*webapi#http#post") endfunction @@ -381,35 +441,130 @@ function! rust#Play(count, line1, line2, ...) abort let l:rust_shortener_url = get(g:, 'rust_shortener_url', 'https://is.gd/') if !s:has_webapi() - echohl ErrorMsg | echomsg ':RustPlay depends on webapi.vim (https://github.com/mattn/webapi-vim)' | echohl None - return + echohl ErrorMsg | echomsg ':RustPlay depends on webapi.vim (https://github.com/mattn/webapi-vim)' | echohl None + return endif let bufname = bufname('%') if a:count < 1 - let content = join(getline(a:line1, a:line2), "\n") + let content = join(getline(a:line1, a:line2), "\n") else - let save_regcont = @" - let save_regtype = getregtype('"') - silent! normal! gvy - let content = @" - call setreg('"', save_regcont, save_regtype) + let save_regcont = @" + let save_regtype = getregtype('"') + silent! normal! gvy + let content = @" + call setreg('"', save_regcont, save_regtype) endif - let body = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) + let url = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) - if strlen(body) > 5000 - echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(body).')' | echohl None - return + if strlen(url) > 5000 + echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(url).')' | echohl None + return endif - let payload = "format=simple&url=".webapi#http#encodeURI(body) + let payload = "format=simple&url=".webapi#http#encodeURI(url) let res = webapi#http#post(l:rust_shortener_url.'create.php', payload, {}) - let url = res.content + if res.status[0] ==# '2' + let url = res.content + endif + + let footer = '' + if exists('g:rust_clip_command') + call system(g:rust_clip_command, url) + if !v:shell_error + let footer = ' (copied to clipboard)' + endif + endif + redraw | echomsg 'Done: '.url.footer +endfunction + +" Run a test under the cursor or all tests {{{1 + +" Finds a test function name under the cursor. Returns empty string when a +" test function is not found. +function! s:SearchTestFunctionNameUnderCursor() abort + let cursor_line = line('.') - redraw | echomsg 'Done: '.url + " Find #[test] attribute + if search('\m\C#\[test\]', 'bcW') is 0 + return '' + endif + + " Move to an opening brace of the test function + let test_func_line = search('\m\C^\s*fn\s\+\h\w*\s*(.\+{$', 'eW') + if test_func_line is 0 + return '' + endif + + " Search the end of test function (closing brace) to ensure that the + " cursor position is within function definition + if maparg('<Plug>(MatchitNormalForward)') ==# '' + keepjumps normal! % + else + " Prefer matchit.vim official plugin to native % since the plugin + " provides better behavior than original % (#391) + " To load the plugin, run: + " :packadd matchit + execute 'keepjumps' 'normal' "\<Plug>(MatchitNormalForward)" + endif + if line('.') < cursor_line + return '' + endif + + return matchstr(getline(test_func_line), '\m\C^\s*fn\s\+\zs\h\w*') +endfunction + +function! rust#Test(mods, winsize, all, options) abort + let manifest = findfile('Cargo.toml', expand('%:p:h') . ';') + if manifest ==# '' + return rust#Run(1, '--test ' . a:options) + endif + + " <count> defaults to 0, but we prefer an empty string + let winsize = a:winsize ? a:winsize : '' + + if has('terminal') + if has('patch-8.0.910') + let cmd = printf('%s noautocmd %snew | terminal ++curwin ', a:mods, winsize) + else + let cmd = printf('%s terminal ', a:mods) + endif + elseif has('nvim') + let cmd = printf('%s noautocmd %snew | terminal ', a:mods, winsize) + else + let cmd = '!' + let manifest = shellescape(manifest) + endif + + if a:all + if a:options ==# '' + execute cmd . 'cargo test --manifest-path' manifest + else + execute cmd . 'cargo test --manifest-path' manifest a:options + endif + return + endif + + let saved = getpos('.') + try + let func_name = s:SearchTestFunctionNameUnderCursor() + finally + call setpos('.', saved) + endtry + if func_name ==# '' + echohl ErrorMsg + echomsg 'No test function was found under the cursor. Please add ! to command if you want to run all tests' + echohl None + return + endif + if a:options ==# '' + execute cmd . 'cargo test --manifest-path' manifest func_name + else + execute cmd . 'cargo test --manifest-path' manifest func_name a:options + endif endfunction " }}}1 -" vim: set noet sw=8 ts=8: +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/autoload/rust/debugging.vim b/runtime/autoload/rust/debugging.vim new file mode 100644 index 0000000000..0e84183172 --- /dev/null +++ b/runtime/autoload/rust/debugging.vim @@ -0,0 +1,105 @@ +" Last Modified: 2023-09-11 + +" For debugging, inspired by https://github.com/w0rp/rust/blob/master/autoload/rust/debugging.vim + +let s:global_variable_list = [ + \ '_rustfmt_autosave_because_of_config', + \ 'ftplugin_rust_source_path', + \ 'loaded_syntastic_rust_cargo_checker', + \ 'loaded_syntastic_rust_filetype', + \ 'loaded_syntastic_rust_rustc_checker', + \ 'rust_bang_comment_leader', + \ 'rust_cargo_avoid_whole_workspace', + \ 'rust_clip_command', + \ 'rust_conceal', + \ 'rust_conceal_mod_path', + \ 'rust_conceal_pub', + \ 'rust_fold', + \ 'rust_last_args', + \ 'rust_last_rustc_args', + \ 'rust_original_delimitMate_excluded_regions', + \ 'rust_playpen_url', + \ 'rust_prev_delimitMate_quotes', + \ 'rust_recent_nearest_cargo_tol', + \ 'rust_recent_root_cargo_toml', + \ 'rust_recommended_style', + \ 'rust_set_conceallevel', + \ 'rust_set_conceallevel=1', + \ 'rust_set_foldmethod', + \ 'rust_set_foldmethod=1', + \ 'rust_shortener_url', + \ 'rustc_makeprg_no_percent', + \ 'rustc_path', + \ 'rustfmt_autosave', + \ 'rustfmt_autosave_if_config_present', + \ 'rustfmt_command', + \ 'rustfmt_emit_files', + \ 'rustfmt_fail_silently', + \ 'rustfmt_options', + \ 'syntastic_extra_filetypes', + \ 'syntastic_rust_cargo_fname', + \] + +function! s:Echo(message) abort + execute 'echo a:message' +endfunction + +function! s:EchoGlobalVariables() abort + for l:key in s:global_variable_list + if l:key !~# '^_' + call s:Echo('let g:' . l:key . ' = ' . string(get(g:, l:key, v:null))) + endif + + if has_key(b:, l:key) + call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key])) + endif + endfor +endfunction + +function! rust#debugging#Info() abort + call cargo#Load() + call rust#Load() + call rustfmt#Load() + call s:Echo('rust.vim Global Variables:') + call s:Echo('') + call s:EchoGlobalVariables() + + silent let l:output = system(g:rustfmt_command . ' --version') + echo l:output + + let l:rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + silent let l:output = system(l:rustc . ' --version') + echo l:output + + silent let l:output = system('cargo --version') + echo l:output + + version + + if exists(":SyntasticInfo") + echo "----" + echo "Info from Syntastic:" + execute "SyntasticInfo" + endif +endfunction + +function! rust#debugging#InfoToClipboard() abort + redir @" + silent call rust#debugging#Info() + redir END + + call s:Echo('RustInfo copied to your clipboard') +endfunction + +function! rust#debugging#InfoToFile(filename) abort + let l:expanded_filename = expand(a:filename) + + redir => l:output + silent call rust#debugging#Info() + redir END + + call writefile(split(l:output, "\n"), l:expanded_filename) + call s:Echo('RustInfo written to ' . l:expanded_filename) +endfunction + +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/autoload/rustfmt.vim b/runtime/autoload/rustfmt.vim index a689b5e00d..8fd3858178 100644 --- a/runtime/autoload/rustfmt.vim +++ b/runtime/autoload/rustfmt.vim @@ -1,107 +1,261 @@ " Author: Stephen Sugden <stephen@stephensugden.com> +" Last Modified: 2023-09-11 " " Adapted from https://github.com/fatih/vim-go " For bugs, patches and license go to https://github.com/rust-lang/rust.vim if !exists("g:rustfmt_autosave") - let g:rustfmt_autosave = 0 + let g:rustfmt_autosave = 0 endif if !exists("g:rustfmt_command") - let g:rustfmt_command = "rustfmt" + let g:rustfmt_command = "rustfmt" endif if !exists("g:rustfmt_options") - let g:rustfmt_options = "" + let g:rustfmt_options = "" endif if !exists("g:rustfmt_fail_silently") - let g:rustfmt_fail_silently = 0 + let g:rustfmt_fail_silently = 0 +endif + +function! rustfmt#DetectVersion() + " Save rustfmt '--help' for feature inspection + silent let s:rustfmt_help = system(g:rustfmt_command . " --help") + let s:rustfmt_unstable_features = s:rustfmt_help =~# "--unstable-features" + + " Build a comparable rustfmt version variable out of its `--version` output: + silent let l:rustfmt_version_full = system(g:rustfmt_command . " --version") + let l:rustfmt_version_list = matchlist(l:rustfmt_version_full, + \ '\vrustfmt ([0-9]+[.][0-9]+[.][0-9]+)') + if len(l:rustfmt_version_list) < 3 + let s:rustfmt_version = "0" + else + let s:rustfmt_version = l:rustfmt_version_list[1] + endif + return s:rustfmt_version +endfunction + +call rustfmt#DetectVersion() + +if !exists("g:rustfmt_emit_files") + let g:rustfmt_emit_files = s:rustfmt_version >= "0.8.2" +endif + +if !exists("g:rustfmt_file_lines") + let g:rustfmt_file_lines = s:rustfmt_help =~# "--file-lines JSON" endif let s:got_fmt_error = 0 +function! rustfmt#Load() + " Utility call to get this script loaded, for debugging +endfunction + +function! s:RustfmtWriteMode() + if g:rustfmt_emit_files + return "--emit=files" + else + return "--write-mode=overwrite" + endif +endfunction + +function! s:RustfmtConfigOptions() + let l:rustfmt_toml = findfile('rustfmt.toml', expand('%:p:h') . ';') + if l:rustfmt_toml !=# '' + return '--config-path '.shellescape(fnamemodify(l:rustfmt_toml, ":p")) + endif + + let l:_rustfmt_toml = findfile('.rustfmt.toml', expand('%:p:h') . ';') + if l:_rustfmt_toml !=# '' + return '--config-path '.shellescape(fnamemodify(l:_rustfmt_toml, ":p")) + endif + + " Default to edition 2018 in case no rustfmt.toml was found. + return '--edition 2018' +endfunction + function! s:RustfmtCommandRange(filename, line1, line2) - let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]} - return printf("%s %s --write-mode=overwrite --file-lines '[%s]'", g:rustfmt_command, g:rustfmt_options, json_encode(l:arg)) + if g:rustfmt_file_lines == 0 + echo "--file-lines is not supported in the installed `rustfmt` executable" + return + endif + + let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]} + let l:write_mode = s:RustfmtWriteMode() + let l:rustfmt_config = s:RustfmtConfigOptions() + + " FIXME: When --file-lines gets to be stable, add version range checking + " accordingly. + let l:unstable_features = s:rustfmt_unstable_features ? '--unstable-features' : '' + + let l:cmd = printf("%s %s %s %s %s --file-lines '[%s]' %s", g:rustfmt_command, + \ l:write_mode, g:rustfmt_options, + \ l:unstable_features, l:rustfmt_config, + \ json_encode(l:arg), shellescape(a:filename)) + return l:cmd endfunction -function! s:RustfmtCommand(filename) - return g:rustfmt_command . " --write-mode=overwrite " . g:rustfmt_options . " " . shellescape(a:filename) +function! s:RustfmtCommand() + let write_mode = g:rustfmt_emit_files ? '--emit=stdout' : '--write-mode=display' + let config = s:RustfmtConfigOptions() + return join([g:rustfmt_command, write_mode, config, g:rustfmt_options]) endfunction -function! s:RunRustfmt(command, curw, tmpname) - if exists("*systemlist") - let out = systemlist(a:command) - else - let out = split(system(a:command), '\r\?\n') - endif - - if v:shell_error == 0 || v:shell_error == 3 - " remove undo point caused via BufWritePre - try | silent undojoin | catch | endtry - - " Replace current file with temp file, then reload buffer - call rename(a:tmpname, expand('%')) - silent edit! - let &syntax = &syntax - - " only clear location list if it was previously filled to prevent - " clobbering other additions - if s:got_fmt_error - let s:got_fmt_error = 0 - call setloclist(0, []) - lwindow - endif - elseif g:rustfmt_fail_silently == 0 - " otherwise get the errors and put them in the location list - let errors = [] - - for line in out - " src/lib.rs:13:5: 13:10 error: expected `,`, or `}`, found `value` - let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\):\s*\(\d\+:\d\+\s*\)\?\s*error: \(.*\)') - if !empty(tokens) - call add(errors, {"filename": @%, - \"lnum": tokens[2], - \"col": tokens[3], - \"text": tokens[5]}) - endif - endfor - - if empty(errors) - % | " Couldn't detect rustfmt error format, output errors - endif - - if !empty(errors) - call setloclist(0, errors, 'r') - echohl Error | echomsg "rustfmt returned error" | echohl None - endif - - let s:got_fmt_error = 1 - lwindow - " We didn't use the temp file, so clean up - call delete(a:tmpname) - endif - - call winrestview(a:curw) +function! s:DeleteLines(start, end) abort + silent! execute a:start . ',' . a:end . 'delete _' endfunction -function! rustfmt#FormatRange(line1, line2) - let l:curw = winsaveview() - let l:tmpname = expand("%:p:h") . "/." . expand("%:p:t") . ".rustfmt" - call writefile(getline(1, '$'), l:tmpname) +function! s:RunRustfmt(command, tmpname, from_writepre) + let l:view = winsaveview() + + let l:stderr_tmpname = tempname() + call writefile([], l:stderr_tmpname) + + let l:command = a:command . ' 2> ' . l:stderr_tmpname - let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2) + if a:tmpname ==# '' + " Rustfmt in stdin/stdout mode - call s:RunRustfmt(command, l:curw, l:tmpname) + " chdir to the directory of the file + let l:has_lcd = haslocaldir() + let l:prev_cd = getcwd() + execute 'lchdir! '.expand('%:h') + + let l:buffer = getline(1, '$') + if exists("*systemlist") + silent let out = systemlist(l:command, l:buffer) + else + silent let out = split(system(l:command, + \ join(l:buffer, "\n")), '\r\?\n') + endif + else + if exists("*systemlist") + silent let out = systemlist(l:command) + else + silent let out = split(system(l:command), '\r\?\n') + endif + endif + + let l:stderr = readfile(l:stderr_tmpname) + + call delete(l:stderr_tmpname) + + let l:open_lwindow = 0 + if v:shell_error == 0 + if a:from_writepre + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry + endif + + if a:tmpname ==# '' + let l:content = l:out + else + " take the tmpfile's content, this is better than rename + " because it preserves file modes. + let l:content = readfile(a:tmpname) + endif + + call s:DeleteLines(len(l:content), line('$')) + call setline(1, l:content) + + " only clear location list if it was previously filled to prevent + " clobbering other additions + if s:got_fmt_error + let s:got_fmt_error = 0 + call setloclist(0, []) + let l:open_lwindow = 1 + endif + elseif g:rustfmt_fail_silently == 0 && !a:from_writepre + " otherwise get the errors and put them in the location list + let l:errors = [] + + let l:prev_line = "" + for l:line in l:stderr + " error: expected one of `;` or `as`, found `extern` + " --> src/main.rs:2:1 + let tokens = matchlist(l:line, '^\s\+-->\s\(.\{-}\):\(\d\+\):\(\d\+\)$') + if !empty(tokens) + call add(l:errors, {"filename": @%, + \"lnum": tokens[2], + \"col": tokens[3], + \"text": l:prev_line}) + endif + let l:prev_line = l:line + endfor + + if !empty(l:errors) + call setloclist(0, l:errors, 'r') + echohl Error | echomsg "rustfmt returned error" | echohl None + else + echo "rust.vim: was not able to parse rustfmt messages. Here is the raw output:" + echo "\n" + for l:line in l:stderr + echo l:line + endfor + endif + + let s:got_fmt_error = 1 + let l:open_lwindow = 1 + endif + + " Restore the current directory if needed + if a:tmpname ==# '' + if l:has_lcd + execute 'lchdir! '.l:prev_cd + else + execute 'chdir! '.l:prev_cd + endif + endif + + " Open lwindow after we have changed back to the previous directory + if l:open_lwindow == 1 + lwindow + endif + + call winrestview(l:view) +endfunction + +function! rustfmt#FormatRange(line1, line2) + let l:tmpname = tempname() + call writefile(getline(1, '$'), l:tmpname) + let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2) + call s:RunRustfmt(command, l:tmpname, v:false) + call delete(l:tmpname) endfunction function! rustfmt#Format() - let l:curw = winsaveview() - let l:tmpname = expand("%:p:h") . "/." . expand("%:p:t") . ".rustfmt" - call writefile(getline(1, '$'), l:tmpname) + call s:RunRustfmt(s:RustfmtCommand(), '', v:false) +endfunction - let command = s:RustfmtCommand(l:tmpname) +function! rustfmt#Cmd() + " Mainly for debugging + return s:RustfmtCommand() +endfunction + +function! rustfmt#PreWrite() + if !filereadable(expand("%@")) + return + endif + if rust#GetConfigVar('rustfmt_autosave_if_config_present', 0) + if findfile('rustfmt.toml', '.;') !=# '' || findfile('.rustfmt.toml', '.;') !=# '' + let b:rustfmt_autosave = 1 + let b:_rustfmt_autosave_because_of_config = 1 + endif + else + if has_key(b:, '_rustfmt_autosave_because_of_config') + unlet b:_rustfmt_autosave_because_of_config + unlet b:rustfmt_autosave + endif + endif + + if !rust#GetConfigVar("rustfmt_autosave", 0) + return + endif - call s:RunRustfmt(command, l:curw, l:tmpname) + call s:RunRustfmt(s:RustfmtCommand(), '', v:true) endfunction + + +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/autoload/shada.vim b/runtime/autoload/shada.vim index 87acc515ee..ae718ad2e7 100644 --- a/runtime/autoload/shada.vim +++ b/runtime/autoload/shada.vim @@ -487,7 +487,7 @@ let s:SHADA_ENTRY_OBJECT_SEQUENCE = ['type', 'timestamp', 'length', 'data'] "" " Convert list returned by msgpackparse() to a list of ShaDa objects " -" @param[in] mpack List of VimL objects returned by msgpackparse(). +" @param[in] mpack List of Vimscript objects returned by msgpackparse(). " " @return List of dictionaries with keys type, timestamp, length and data. Each " dictionary describes one ShaDa entry. diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index e495e8262a..e242fe98e3 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -1,7 +1,7 @@ " tar.vim: Handles browsing tarfiles " AUTOLOAD PORTION -" Date: Jan 07, 2020 -" Version: 32 +" Date: Nov 14, 2023 +" Version: 32b (with modifications from the Vim Project) " Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " License: Vim License (see vim's :help license) " @@ -22,7 +22,7 @@ if &cp || exists("g:loaded_tar") finish endif -let g:loaded_tar= "v32" +let g:loaded_tar= "v32a" if v:version < 702 echohl WarningMsg echo "***warning*** this version of tar needs vim 7.2" @@ -208,18 +208,24 @@ fun! tar#Browse(tarfile) " call Dret("tar#Browse : a:tarfile<".a:tarfile.">") return endif - if line("$") == curlast || ( line("$") == (curlast + 1) && getline("$") =~# '\c\%(warning\|error\|inappropriate\|unrecognized\)') - redraw! - echohl WarningMsg | echo "***warning*** (tar#Browse) ".a:tarfile." doesn't appear to be a tar file" | echohl None - keepj sil! %d - let eikeep= &ei - set ei=BufReadCmd,FileReadCmd - exe "r ".fnameescape(a:tarfile) - let &ei= eikeep - keepj sil! 1d -" call Dret("tar#Browse : a:tarfile<".a:tarfile.">") - return - endif + " + " The following should not be neccessary, since in case of errors the + " previous if statement should have caught the problem (because tar exited + " with a non-zero exit code). + " if line("$") == curlast || ( line("$") == (curlast + 1) && + " \ getline("$") =~# '\c\<\%(warning\|error\|inappropriate\|unrecognized\)\>' && + " \ getline("$") =~ '\s' ) + " redraw! + " echohl WarningMsg | echo "***warning*** (tar#Browse) ".a:tarfile." doesn't appear to be a tar file" | echohl None + " keepj sil! %d + " let eikeep= &ei + " set ei=BufReadCmd,FileReadCmd + " exe "r ".fnameescape(a:tarfile) + " let &ei= eikeep + " keepj sil! 1d + " call Dret("tar#Browse : a:tarfile<".a:tarfile.">") + " return + " endif " set up maps supported for tar setlocal noma nomod ro @@ -735,82 +741,6 @@ fun! s:Rmdir(fname) " call Dret("Rmdir") endfun -" --------------------------------------------------------------------- -" tar#Vimuntar: installs a tarball in the user's .vim / vimfiles directory {{{2 -fun! tar#Vimuntar(...) -" call Dfunc("tar#Vimuntar() a:0=".a:0." a:1<".(exists("a:1")? a:1 : "-n/a-").">") - let tarball = expand("%") -" call Decho("tarball<".tarball.">") - let tarbase = substitute(tarball,'\..*$','','') -" call Decho("tarbase<".tarbase.">") - let tarhome = expand("%:p") - if has("win32") || has("win95") || has("win64") || has("win16") - let tarhome= substitute(tarhome,'\\','/','g') - endif - let tarhome= substitute(tarhome,'/[^/]*$','','') -" call Decho("tarhome<".tarhome.">") - let tartail = expand("%:t") -" call Decho("tartail<".tartail.">") - let curdir = getcwd() -" call Decho("curdir <".curdir.">") - " set up vimhome - if a:0 > 0 && a:1 != "" - let vimhome= a:1 - else - let vimhome= vimball#VimballHome() - endif -" call Decho("vimhome<".vimhome.">") - -" call Decho("curdir<".curdir."> vimhome<".vimhome.">") - if simplify(curdir) != simplify(vimhome) - " copy (possibly compressed) tarball to .vim/vimfiles -" call Decho(netrw#WinPath(g:tar_copycmd)." ".shellescape(tartail)." ".shellescape(vimhome)) - call system(netrw#WinPath(g:tar_copycmd)." ".shellescape(tartail)." ".shellescape(vimhome)) -" call Decho("exe cd ".fnameescape(vimhome)) - exe "cd ".fnameescape(vimhome) - endif -" call Decho("getcwd<".getcwd().">") - - " if necessary, decompress the tarball; then, extract it - if tartail =~ '\.tgz' - if executable("gunzip") - silent exe "!gunzip ".shellescape(tartail) - elseif executable("gzip") - silent exe "!gzip -d ".shellescape(tartail) - else - echoerr "unable to decompress<".tartail."> on this system" - if simplify(curdir) != simplify(tarhome) - " remove decompressed tarball, restore directory -" call Decho("delete(".tartail.".tar)") - call delete(tartail.".tar") -" call Decho("exe cd ".fnameescape(curdir)) - exe "cd ".fnameescape(curdir) - endif -" call Dret("tar#Vimuntar") - return - endif - else - call vimball#Decompress(tartail,0) - endif - let extractcmd= netrw#WinPath(g:tar_extractcmd) -" call Decho("system(".extractcmd." ".shellescape(tarbase.".tar").")") - call system(extractcmd." ".shellescape(tarbase.".tar")) - - " set up help - if filereadable("doc/".tarbase.".txt") -" call Decho("exe helptags ".getcwd()."/doc") - exe "helptags ".getcwd()."/doc" - endif - - if simplify(tarhome) != simplify(vimhome) - " remove decompressed tarball, restore directory - call delete(vimhome."/".tarbase.".tar") - exe "cd ".fnameescape(curdir) - endif - -" call Dret("tar#Vimuntar") -endfun - " ===================================================================== " Modelines And Restoration: {{{1 let &cpo= s:keepcpo diff --git a/runtime/autoload/tohtml.vim b/runtime/autoload/tohtml.vim index 4ae17815ba..b1693efc5d 100644 --- a/runtime/autoload/tohtml.vim +++ b/runtime/autoload/tohtml.vim @@ -1,6 +1,6 @@ " Vim autoload file for the tohtml plugin. " Maintainer: Ben Fritz <fritzophrenic@gmail.com> -" Last Change: 2019 Aug 16 +" Last Change: 2023 Sep 03 " " Additional contributors: " @@ -307,7 +307,7 @@ func! tohtml#Convert2HTML(line1, line2) "{{{ let g:html_diff_win_num = 0 for window in win_list " switch to the next buffer to convert - exe ":" . bufwinnr(window) . "wincmd w" + exe ":" .. bufwinnr(window) .. "wincmd w" " figure out whether current charset and encoding will work, if not " default to UTF-8 @@ -351,68 +351,70 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{ let s:old_magic = &magic set magic - if s:settings.use_xhtml - if s:settings.encoding != "" - let xml_line = "<?xml version=\"1.0\" encoding=\"" . s:settings.encoding . "\"?>" - else - let xml_line = "<?xml version=\"1.0\"?>" + let html = [] + if !s:settings.no_doc + if s:settings.use_xhtml + if s:settings.encoding != "" + let xml_line = "<?xml version=\"1.0\" encoding=\"" .. s:settings.encoding .. "\"?>" + else + let xml_line = "<?xml version=\"1.0\"?>" + endif + let tag_close = ' />' endif - let tag_close = ' />' - endif - let style = [s:settings.use_xhtml ? "" : '-->'] - let body_line = '' + let style = [s:settings.use_xhtml ? "" : '-->'] + let body_line = '' - let html = [] - let s:html5 = 0 - if s:settings.use_xhtml - call add(html, xml_line) - endif - if s:settings.use_xhtml - call add(html, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">") - call add(html, '<html xmlns="http://www.w3.org/1999/xhtml">') - elseif s:settings.use_css && !s:settings.no_pre - call add(html, "<!DOCTYPE html>") - call add(html, '<html>') - let s:html5 = 1 - else - call add(html, '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"') - call add(html, ' "http://www.w3.org/TR/html4/loose.dtd">') - call add(html, '<html>') - endif - call add(html, '<head>') - - " include encoding as close to the top as possible, but only if not already - " contained in XML information - if s:settings.encoding != "" && !s:settings.use_xhtml - if s:html5 - call add(html, '<meta charset="' . s:settings.encoding . '"' . tag_close) + let s:html5 = 0 + if s:settings.use_xhtml + call add(html, xml_line) + endif + if s:settings.use_xhtml + call add(html, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">") + call add(html, '<html xmlns="http://www.w3.org/1999/xhtml">') + elseif s:settings.use_css && !s:settings.no_pre + call add(html, "<!DOCTYPE html>") + call add(html, '<html>') + let s:html5 = 1 else - call add(html, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:settings.encoding . '"' . tag_close) + call add(html, '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"') + call add(html, ' "http://www.w3.org/TR/html4/loose.dtd">') + call add(html, '<html>') + endif + call add(html, '<head>') + + " include encoding as close to the top as possible, but only if not already + " contained in XML information + if s:settings.encoding != "" && !s:settings.use_xhtml + if s:html5 + call add(html, '<meta charset="' .. s:settings.encoding .. '"' .. tag_close) + else + call add(html, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" .. s:settings.encoding .. '"' .. tag_close) + endif endif - endif - call add(html, '<title>diff</title>') - call add(html, '<meta name="Generator" content="Vim/'.v:version/100.'.'.v:version%100.'"'.tag_close) - call add(html, '<meta name="plugin-version" content="'.g:loaded_2html_plugin.'"'.tag_close) - call add(html, '<meta name="settings" content="'. - \ join(filter(keys(s:settings),'s:settings[v:val]'),','). - \ ',prevent_copy='.s:settings.prevent_copy. - \ ',use_input_for_pc='.s:settings.use_input_for_pc. - \ '"'.tag_close) - call add(html, '<meta name="colorscheme" content="'. - \ (exists('g:colors_name') - \ ? g:colors_name - \ : 'none'). '"'.tag_close) - - call add(html, '</head>') - let body_line_num = len(html) - call add(html, '<body'.(s:settings.line_ids ? ' onload="JumpToLine();"' : '').'>') - call add(html, "<table ".(s:settings.use_css? "" : "border='1' width='100%' ")."id='vimCodeElement".s:settings.id_suffix."'>") + call add(html, '<title>diff</title>') + call add(html, '<meta name="Generator" content="Vim/'..v:version/100..'.'..v:version%100..'"'..tag_close) + call add(html, '<meta name="plugin-version" content="'..g:loaded_2html_plugin..'"'..tag_close) + call add(html, '<meta name="settings" content="'. + \ join(filter(keys(s:settings),'s:settings[v:val]'),','). + \ ',prevent_copy='..s:settings.prevent_copy. + \ ',use_input_for_pc='..s:settings.use_input_for_pc. + \ '"'..tag_close) + call add(html, '<meta name="colorscheme" content="'. + \ (exists('g:colors_name') + \ ? g:colors_name + \ : 'none').. '"'..tag_close) + + call add(html, '</head>') + let body_line_num = len(html) + call add(html, '<body'..(s:settings.line_ids ? ' onload="JumpToLine();"' : '')..'>') + endif + call add(html, "<table "..(s:settings.use_css? "" : "border='1' width='100%' ").."id='vimCodeElement"..s:settings.id_suffix.."'>") call add(html, '<tr>') for buf in a:win_list - call add(html, '<th>'.bufname(buf).'</th>') + call add(html, '<th>'..bufname(buf)..'</th>') endfor call add(html, '</tr><tr>') @@ -421,7 +423,7 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{ for buf in a:buf_list let temp = [] - exe bufwinnr(buf) . 'wincmd w' + exe bufwinnr(buf) .. 'wincmd w' " If text is folded because of user foldmethod settings, etc. we don't want " to act on everything in a fold by mistake. @@ -430,47 +432,53 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{ " When not using CSS or when using xhtml, the <body> line can be important. " Assume it will be the same for all buffers and grab it from the first " buffer. Similarly, need to grab the body end line as well. - if body_line == '' - 1 - call search('<body') - let body_line = getline('.') - $ - call search('</body>', 'b') - let s:body_end_line = getline('.') - endif + if !s:settings.no_doc + if body_line == '' + 1 + call search('<body') + let body_line = getline('.') + $ + call search('</body>', 'b') + let s:body_end_line = getline('.') + endif - " Grab the style information. Some of this will be duplicated so only insert - " it if it's not already there. {{{ - 1 - let style_start = search('^<style\( type="text/css"\)\?>') - 1 - let style_end = search('^</style>') - if style_start > 0 && style_end > 0 - let buf_styles = getline(style_start + 1, style_end - 1) - for a_style in buf_styles - if index(style, a_style) == -1 - if diff_style_start == 0 - if a_style =~ '\<Diff\(Change\|Text\|Add\|Delete\)' - let diff_style_start = len(style)-1 + " Grab the style information. Some of this will be duplicated so only insert + " it if it's not already there. {{{ + 1 + let style_start = search('^<style\( type="text/css"\)\?>') + 1 + let style_end = search('^</style>') + if style_start > 0 && style_end > 0 + let buf_styles = getline(style_start + 1, style_end - 1) + for a_style in buf_styles + if index(style, a_style) == -1 + if diff_style_start == 0 + if a_style =~ '\<Diff\(Change\|Text\|Add\|Delete\)' + let diff_style_start = len(style)-1 + endif endif + call insert(style, a_style, insert_index) + let insert_index += 1 endif - call insert(style, a_style, insert_index) - let insert_index += 1 - endif - endfor - endif " }}} + endfor + endif " }}} - " everything new will get added before the diff styles so diff highlight - " properly overrides normal highlight - if diff_style_start != 0 - let insert_index = diff_style_start - endif + " everything new will get added before the diff styles so diff highlight + " properly overrides normal highlight + if diff_style_start != 0 + let insert_index = diff_style_start + endif - " Delete those parts that are not needed so we can include the rest into the - " resulting table. - 1,/^<body.*\%(\n<!--.*-->\_s\+.*id='oneCharWidth'.*\_s\+.*id='oneInputWidth'.*\_s\+.*id='oneEmWidth'\)\?\zs/d_ - $ - ?</body>?,$d_ + " Delete those parts that are not needed so we can include the rest into the + " resulting table. + 1,/^<body.*\%(\n<!--.*-->\_s\+.*id='oneCharWidth'.*\_s\+.*id='oneInputWidth'.*\_s\+.*id='oneEmWidth'\)\?\zs/d_ + $ + ?</body>?,$d_ + elseif !s:settings.no_modeline + " remove modeline from source files if it is included and we haven't deleted + " due to removing html footer already + $d + endif let temp = getline(1,'$') " clean out id on the main content container because we already set it on " the table @@ -478,7 +486,11 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{ " undo deletion of start and end part " so we can later save the file as valid html " TODO: restore using grabbed lines if undolevel is 1? - normal! 2u + if !s:settings.no_doc + normal! 2u + elseif !s:settings.no_modeline + normal! u + endif if s:settings.use_css call add(html, '<td><div>') elseif s:settings.use_xhtml @@ -495,29 +507,35 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{ quit! endfor - let html[body_line_num] = body_line + if !s:settings.no_doc + let html[body_line_num] = body_line + endif call add(html, '</tr>') call add(html, '</table>') - call add(html, s:body_end_line) - call add(html, '</html>') + if !s:settings.no_doc + call add(html, s:body_end_line) + call add(html, '</html>') + endif " The generated HTML is admittedly ugly and takes a LONG time to fold. " Make sure the user doesn't do syntax folding when loading a generated file, " using a modeline. - call add(html, '<!-- vim: set foldmethod=manual : -->') + if !s:settings.no_modeline + call add(html, '<!-- vim: set foldmethod=manual : -->') + endif let i = 1 - let name = "Diff" . (s:settings.use_xhtml ? ".xhtml" : ".html") + let name = "Diff" .. (s:settings.use_xhtml ? ".xhtml" : ".html") " Find an unused file name if current file name is already in use while filereadable(name) - let name = substitute(name, '\d*\.x\?html$', '', '') . i . '.' . fnamemodify(copy(name), ":t:e") + let name = substitute(name, '\d*\.x\?html$', '', '') .. i .. '.' .. fnamemodify(copy(name), ":t:e") let i += 1 endwhile let s:ei_sav = &eventignore set eventignore+=FileType - exe "topleft new " . name + exe "topleft new " .. name let &eventignore=s:ei_sav unlet s:ei_sav @@ -542,129 +560,131 @@ func! tohtml#Diff2HTML(win_list, buf_list) "{{{ call append(0, html) - if len(style) > 0 - 1 - let style_start = search('^</head>')-1 + if !s:settings.no_doc + if len(style) > 0 + 1 + let style_start = search('^</head>')-1 - " add required javascript in reverse order so we can just call append again - " and again without adjusting {{{ + " add required javascript in reverse order so we can just call append again + " and again without adjusting {{{ - let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids + let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids - " insert script closing tag if needed - if s:uses_script - call append(style_start, [ - \ '', - \ s:settings.use_xhtml ? '//]]>' : '-->', - \ "</script>" - \ ]) - endif + " insert script closing tag if needed + if s:uses_script + call append(style_start, [ + \ '', + \ s:settings.use_xhtml ? '//]]>' : '-->', + \ "</script>" + \ ]) + endif - " insert javascript to get IDs from line numbers, and to open a fold before - " jumping to any lines contained therein - if s:settings.line_ids - call append(style_start, [ - \ " /* Always jump to new location even if the line was hidden inside a fold, or", - \ " * we corrected the raw number to a line ID.", - \ " */", - \ " if (lineElem) {", - \ " lineElem.scrollIntoView(true);", - \ " }", - \ " return true;", - \ "}", - \ "if ('onhashchange' in window) {", - \ " window.onhashchange = JumpToLine;", - \ "}" - \ ]) + " insert javascript to get IDs from line numbers, and to open a fold before + " jumping to any lines contained therein + if s:settings.line_ids + call append(style_start, [ + \ " /* Always jump to new location even if the line was hidden inside a fold, or", + \ " * we corrected the raw number to a line ID.", + \ " */", + \ " if (lineElem) {", + \ " lineElem.scrollIntoView(true);", + \ " }", + \ " return true;", + \ "}", + \ "if ('onhashchange' in window) {", + \ " window.onhashchange = JumpToLine;", + \ "}" + \ ]) - if s:settings.dynamic_folds + if s:settings.dynamic_folds + call append(style_start, [ + \ "", + \ " /* navigate upwards in the DOM tree to open all folds containing the line */", + \ " var node = lineElem;", + \ " while (node && node.id != 'vimCodeElement"..s:settings.id_suffix.."')", + \ " {", + \ " if (node.className == 'closed-fold')", + \ " {", + \ " /* toggle open the fold ID (remove window ID) */", + \ " toggleFold(node.id.substr(4));", + \ " }", + \ " node = node.parentNode;", + \ " }", + \ ]) + endif + endif + + if s:settings.line_ids call append(style_start, [ \ "", - \ " /* navigate upwards in the DOM tree to open all folds containing the line */", - \ " var node = lineElem;", - \ " while (node && node.id != 'vimCodeElement".s:settings.id_suffix."')", - \ " {", - \ " if (node.className == 'closed-fold')", - \ " {", - \ " /* toggle open the fold ID (remove window ID) */", - \ " toggleFold(node.id.substr(4));", - \ " }", - \ " node = node.parentNode;", + \ "/* function to open any folds containing a jumped-to line before jumping to it */", + \ "function JumpToLine()", + \ "{", + \ " var lineNum;", + \ " lineNum = window.location.hash;", + \ " lineNum = lineNum.substr(1); /* strip off '#' */", + \ "", + \ " if (lineNum.indexOf('L') == -1) {", + \ " lineNum = 'L'+lineNum;", \ " }", + \ " if (lineNum.indexOf('W') == -1) {", + \ " lineNum = 'W1'+lineNum;", + \ " }", + \ " var lineElem = document.getElementById(lineNum);" \ ]) endif - endif - if s:settings.line_ids - call append(style_start, [ - \ "", - \ "/* function to open any folds containing a jumped-to line before jumping to it */", - \ "function JumpToLine()", - \ "{", - \ " var lineNum;", - \ " lineNum = window.location.hash;", - \ " lineNum = lineNum.substr(1); /* strip off '#' */", - \ "", - \ " if (lineNum.indexOf('L') == -1) {", - \ " lineNum = 'L'+lineNum;", - \ " }", - \ " if (lineNum.indexOf('W') == -1) {", - \ " lineNum = 'W1'+lineNum;", - \ " }", - \ " var lineElem = document.getElementById(lineNum);" - \ ]) - endif + " Insert javascript to toggle matching folds open and closed in all windows, + " if dynamic folding is active. + if s:settings.dynamic_folds + call append(style_start, [ + \ " function toggleFold(objID)", + \ " {", + \ " for (win_num = 1; win_num <= "..len(a:buf_list).."; win_num++)", + \ " {", + \ " var fold;", + \ ' fold = document.getElementById("win"+win_num+objID);', + \ " if(fold.className == 'closed-fold')", + \ " {", + \ " fold.className = 'open-fold';", + \ " }", + \ " else if (fold.className == 'open-fold')", + \ " {", + \ " fold.className = 'closed-fold';", + \ " }", + \ " }", + \ " }", + \ ]) + endif - " Insert javascript to toggle matching folds open and closed in all windows, - " if dynamic folding is active. - if s:settings.dynamic_folds - call append(style_start, [ - \ " function toggleFold(objID)", - \ " {", - \ " for (win_num = 1; win_num <= ".len(a:buf_list)."; win_num++)", - \ " {", - \ " var fold;", - \ ' fold = document.getElementById("win"+win_num+objID);', - \ " if(fold.className == 'closed-fold')", - \ " {", - \ " fold.className = 'open-fold';", - \ " }", - \ " else if (fold.className == 'open-fold')", - \ " {", - \ " fold.className = 'closed-fold';", - \ " }", - \ " }", - \ " }", - \ ]) - endif + if s:uses_script + " insert script tag if needed + call append(style_start, [ + \ "<script" .. (s:html5 ? "" : " type='text/javascript'") .. ">", + \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"]) + endif - if s:uses_script - " insert script tag if needed - call append(style_start, [ - \ "<script" . (s:html5 ? "" : " type='text/javascript'") . ">", - \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"]) + " Insert styles from all the generated html documents and additional styles + " for the table-based layout of the side-by-side diff. The diff should take + " up the full browser window (but not more), and be static in size, + " horizontally scrollable when the lines are too long. Otherwise, the diff + " is pretty useless for really long lines. {{{ + if s:settings.use_css + call append(style_start, + \ ['<style' .. (s:html5 ? '' : 'type="text/css"') .. '>']+ + \ style+ + \ [ s:settings.use_xhtml ? '' : '<!--', + \ 'table { table-layout: fixed; }', + \ 'html, body, table, tbody { width: 100%; margin: 0; padding: 0; }', + \ 'table, td, th { border: 1px solid; }', + \ 'td { vertical-align: top; }', + \ 'th, td { width: '..printf("%.1f",100.0/len(a:win_list))..'%; }', + \ 'td div { overflow: auto; }', + \ s:settings.use_xhtml ? '' : '-->', + \ '</style>' + \]) + endif "}}} endif - - " Insert styles from all the generated html documents and additional styles - " for the table-based layout of the side-by-side diff. The diff should take - " up the full browser window (but not more), and be static in size, - " horizontally scrollable when the lines are too long. Otherwise, the diff - " is pretty useless for really long lines. {{{ - if s:settings.use_css - call append(style_start, - \ ['<style' . (s:html5 ? '' : 'type="text/css"') . '>']+ - \ style+ - \ [ s:settings.use_xhtml ? '' : '<!--', - \ 'table { table-layout: fixed; }', - \ 'html, body, table, tbody { width: 100%; margin: 0; padding: 0; }', - \ 'table, td, th { border: 1px solid; }', - \ 'td { vertical-align: top; }', - \ 'th, td { width: '.printf("%.1f",100.0/len(a:win_list)).'%; }', - \ 'td div { overflow: auto; }', - \ s:settings.use_xhtml ? '' : '-->', - \ '</style>' - \]) - endif "}}} endif let &paste = s:old_paste @@ -674,7 +694,7 @@ endfunc "}}} " Gets a single user option and sets it in the passed-in Dict, or gives it the " default value if the option doesn't actually exist. func! tohtml#GetOption(settings, option, default) "{{{ - if exists('g:html_'.a:option) + if exists('g:html_'..a:option) let a:settings[a:option] = g:html_{a:option} else let a:settings[a:option] = a:default @@ -693,10 +713,11 @@ func! tohtml#GetUserSettings() "{{{ let user_settings = {} " Define the correct option if the old option name exists and we haven't - " already defined the correct one. Maybe I'll put out a warning message about - " this sometime and remove the old option entirely at some even later time, - " but for now just silently accept the old option. + " already defined the correct one. if exists('g:use_xhtml') && !exists("g:html_use_xhtml") + echohl WarningMsg + echomsg "Warning: g:use_xhtml is deprecated, use g:html_use_xhtml" + echohl None let g:html_use_xhtml = g:use_xhtml endif @@ -719,7 +740,7 @@ func! tohtml#GetUserSettings() "{{{ call tohtml#GetOption(user_settings, 'whole_filler', 0 ) call tohtml#GetOption(user_settings, 'use_xhtml', 0 ) call tohtml#GetOption(user_settings, 'line_ids', user_settings.number_lines ) - call tohtml#GetOption(user_settings, 'use_input_for_pc', 'fallback') + call tohtml#GetOption(user_settings, 'use_input_for_pc', 'none') " }}} " override those settings that need it {{{ @@ -834,16 +855,16 @@ func! tohtml#GetUserSettings() "{{{ if user_settings.use_css if exists("g:html_prevent_copy") if user_settings.dynamic_folds && !user_settings.no_foldcolumn && g:html_prevent_copy =~# 'f' - let user_settings.prevent_copy .= 'f' + let user_settings.prevent_copy ..= 'f' endif if user_settings.number_lines && g:html_prevent_copy =~# 'n' - let user_settings.prevent_copy .= 'n' + let user_settings.prevent_copy ..= 'n' endif if &diff && g:html_prevent_copy =~# 'd' - let user_settings.prevent_copy .= 'd' + let user_settings.prevent_copy ..= 'd' endif if !user_settings.ignore_folding && g:html_prevent_copy =~# 't' - let user_settings.prevent_copy .= 't' + let user_settings.prevent_copy ..= 't' endif else let user_settings.prevent_copy = "" @@ -855,10 +876,10 @@ func! tohtml#GetUserSettings() "{{{ " enforce valid values for use_input_for_pc if user_settings.use_input_for_pc !~# 'fallback\|none\|all' - let user_settings.use_input_for_pc = 'fallback' + let user_settings.use_input_for_pc = 'none' echohl WarningMsg - echomsg '2html: "' . g:html_use_input_for_pc . '" is not valid for g:html_use_input_for_pc' - echomsg '2html: defaulting to "' . user_settings.use_input_for_pc . '"' + echomsg '2html: "' .. g:html_use_input_for_pc .. '" is not valid for g:html_use_input_for_pc' + echomsg '2html: defaulting to "' .. user_settings.use_input_for_pc .. '"' echohl None sleep 3 endif diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim index 4da4213826..a546f7007e 100644 --- a/runtime/autoload/tutor.vim +++ b/runtime/autoload/tutor.vim @@ -29,7 +29,7 @@ function! tutor#MouseDoubleClick() if foldclosed(line('.')) > -1 normal! zo else - if match(getline('.'), '^#\{1,} ') > -1 + if match(getline('.'), '^#\{1,} ') > -1 && foldlevel(line('.')) > 0 silent normal! zc else call tutor#FollowLink(0) @@ -220,6 +220,7 @@ function! tutor#TutorCmd(tutor_name) call tutor#SetupVim() exe "edit ".l:to_open + call tutor#ApplyTransform() endfunction function! tutor#TutorCmdComplete(lead,line,pos) @@ -227,3 +228,12 @@ function! tutor#TutorCmdComplete(lead,line,pos) let l:names = uniq(sort(map(l:tutors, 'fnamemodify(v:val, ":t:r")'), 's:Sort')) return join(l:names, "\n") endfunction + +function! tutor#ApplyTransform() + if has('win32') + sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\2/g + else + sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\1/g + endif + normal! gg0 +endfunction diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim index bc9b62ddb0..e61293c357 100644 --- a/runtime/autoload/zip.vim +++ b/runtime/autoload/zip.vim @@ -1,7 +1,7 @@ " zip.vim: Handles browsing zipfiles " AUTOLOAD PORTION -" Date: Nov 08, 2021 -" Version: 32 +" Date: Mar 12, 2023 +" Version: 33 " Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " License: Vim License (see vim's :help license) " Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1 @@ -20,7 +20,7 @@ if &cp || exists("g:loaded_zip") finish endif -let g:loaded_zip= "v32" +let g:loaded_zip= "v33" if v:version < 702 echohl WarningMsg echo "***warning*** this version of zip needs vim 7.2 or later" @@ -57,6 +57,11 @@ if !exists("g:zip_extractcmd") let g:zip_extractcmd= g:zip_unzipcmd endif +if !dist#vim#IsSafeExecutable('zip', g:zip_unzipcmd) + echoerr "Warning: NOT executing " .. g:zip_unzipcmd .. " from current directory!" + finish +endif + " ---------------- " Functions: {{{1 " ---------------- @@ -160,10 +165,14 @@ endfun " --------------------------------------------------------------------- " ZipBrowseSelect: {{{2 fun! s:ZipBrowseSelect() -" call Dfunc("ZipBrowseSelect() zipfile<".b:zipfile."> curfile<".expand("%").">") + " call Dfunc("ZipBrowseSelect() zipfile<".((exists("b:zipfile"))? b:zipfile : "n/a")."> curfile<".expand("%").">") let repkeep= &report set report=10 let fname= getline(".") + if !exists("b:zipfile") +" call Dret("ZipBrowseSelect : b:zipfile doesn't exist!") + return + endif " sanity check if fname =~ '^"' |