diff options
Diffstat (limited to 'runtime')
43 files changed, 1197 insertions, 2172 deletions
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim deleted file mode 100644 index 70f525156c..0000000000 --- a/runtime/autoload/health/provider.vim +++ /dev/null @@ -1,755 +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 - -" 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 v:lua.vim.version.lt(nvim_version, pypi_version) - 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, effectively 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 v:lua.vim.version.lt(current_gem, latest_gem) - 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 || v:lua.vim.version.lt(node_v[1:], '6.0.0') - call health#report_warn('Nvim node.js host does not support Node '.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 latest_npm !=# 'unable to parse' && v:lua.vim.version.lt(current_npm, latest_npm) - 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 v:lua.vim.version.lt(current_cpan, latest_cpan) - 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/tar.vim b/runtime/autoload/tar.vim index e495e8262a..d7d1d8399e 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -735,82 +735,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/bugreport.vim b/runtime/bugreport.vim deleted file mode 100644 index 27761ca011..0000000000 --- a/runtime/bugreport.vim +++ /dev/null @@ -1,87 +0,0 @@ -:" Use this script to create the file "bugreport.txt", which contains -:" information about the environment of a possible bug in Vim. -:" -:" Maintainer: Bram Moolenaar <Bram@vim.org> -:" Last change: 2019 Jan 27 -:" -:" To use inside Vim: -:" :so $VIMRUNTIME/bugreport.vim -:" Or, from the command line: -:" vim -s $VIMRUNTIME/bugreport.vim -:" -:" The "if 1" lines are to avoid error messages when expression evaluation is -:" not compiled in. -:" -:if 1 -: let more_save = &more -:endif -:set nomore -:if has("unix") -: !echo "uname -a" >bugreport.txt -: !uname -a >>bugreport.txt -:endif -:redir >>bugreport.txt -:version -:if 1 -: func <SID>CheckDir(n) -: if isdirectory(a:n) -: echo 'directory "' . a:n . '" exists' -: else -: echo 'directory "' . a:n . '" does NOT exist' -: endif -: endfun -: func <SID>CheckFile(n) -: if filereadable(a:n) -: echo '"' . a:n . '" is readable' -: else -: echo '"' . a:n . '" is NOT readable' -: endif -: endfun -: echo "--- Directories and Files ---" -: echo '$VIM = "' . $VIM . '"' -: call <SID>CheckDir($VIM) -: echo '$VIMRUNTIME = "' . $VIMRUNTIME . '"' -: call <SID>CheckDir($VIMRUNTIME) -: call <SID>CheckFile(&helpfile) -: call <SID>CheckFile(fnamemodify(&helpfile, ":h") . "/tags") -: call <SID>CheckFile($VIMRUNTIME . "/menu.vim") -: call <SID>CheckFile($VIMRUNTIME . "/filetype.vim") -: call <SID>CheckFile($VIMRUNTIME . "/syntax/synload.vim") -: delfun <SID>CheckDir -: delfun <SID>CheckFile -: echo "--- Scripts sourced ---" -: scriptnames -:endif -:set all -:if has("autocmd") -: au -:endif -:if 1 -: echo "--- Normal/Visual mode mappings ---" -:endif -:map -:if 1 -: echo "--- Insert/Command-line mode mappings ---" -:endif -:map! -:if 1 -: echo "--- Abbreviations ---" -:endif -:ab -:if 1 -: echo "--- Highlighting ---" -:endif -:highlight -:if 1 -: echo "--- Variables ---" -:endif -:if 1 -: let -:endif -:redir END -:set more& -:if 1 -: let &more = more_save -: unlet more_save -:endif -:e bugreport.txt diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 09d260e0cd..d63563cc05 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3068,8 +3068,8 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* In general, values below 100 are recommended, unless there is a good reason to overshadow builtin elements. - • style: Configure the appearance of the window. Currently - only takes one non-empty value: + • style: (optional) Configure the appearance of the window. + Currently only supports one value: • "minimal" Nvim will display the window with many UI options disabled. This is useful when displaying a temporary float where the text should not be edited. diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 0e04e9035b..3c940ccfa2 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -3003,7 +3003,6 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* arglist file names in argument list augroup autocmd groups buffer buffer names - behave |:behave| suboptions breakpoint |:breakadd| and |:breakdel| suboptions cmdline |cmdline-completion| result color color schemes diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 3735073867..171d285950 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -120,6 +120,8 @@ LSP FUNCTIONS {buffer = bufnr} instead. - *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with {async = true} instead. +- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with + {async = false} instead. - *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| or |vim.lsp.buf.format()| instead. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index fe15ba6115..351690f4df 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -93,7 +93,27 @@ non-zero number it means TRUE: > :" executed To test for a non-empty string, use empty(): > :if !empty("foo") -< + +< *falsy* *truthy* +An expression can be used as a condition, ignoring the type and only using +whether the value is "sort of true" or "sort of false". Falsy is: + the number zero + empty string, blob, list or dictionary +Other values are truthy. Examples: + 0 falsy + 1 truthy + -1 truthy + 0.0 falsy + 0.1 truthy + '' falsy + 'x' truthy + [] falsy + [0] truthy + {} falsy + #{x: 1} truthy + 0z falsy + 0z00 truthy + *non-zero-arg* Function arguments often behave slightly different from |TRUE|: If the argument is present and it evaluates to a non-zero Number, |v:true| or a @@ -841,9 +861,12 @@ All expressions within one level are parsed from left to right. ------------------------------------------------------------------------------ -expr1 *expr1* *ternary* *E109* +expr1 *expr1* *ternary* *falsy-operator* *??* *E109* + +The ternary operator: expr2 ? expr1 : expr1 +The falsy operator: expr2 ?? expr1 -expr2 ? expr1 : expr1 +Ternary operator ~ The expression before the '?' is evaluated to a number. If it evaluates to |TRUE|, the result is the value of the expression between the '?' and ':', @@ -866,6 +889,23 @@ To keep this readable, using |line-continuation| is suggested: > You should always put a space before the ':', otherwise it can be mistaken for use in a variable such as "a:1". +Falsy operator ~ + +This is also known as the "null coalescing operator", but that's too +complicated, thus we just call it the falsy operator. + +The expression before the '??' is evaluated. If it evaluates to +|truthy|, this is used as the result. Otherwise the expression after the '??' +is evaluated and used as the result. This is most useful to have a default +value for an expression that may result in zero or empty: > + echo theList ?? 'list is empty' + echo GetName() ?? 'unknown' + +These are similar, but not equal: > + expr2 ?? expr1 + expr2 ? expr2 : expr1 +In the second line "expr2" is evaluated twice. + ------------------------------------------------------------------------------ expr2 and expr3 *expr2* *expr3* @@ -2499,7 +2539,7 @@ This does NOT work: > |List| item. *:let=<<* *:let-heredoc* - *E990* *E991* *E172* *E221* + *E990* *E991* *E172* *E221* *E1145* :let {var-name} =<< [trim] {endmarker} text... text... diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 174683a8c3..fbd3fccec0 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1159,7 +1159,6 @@ tag command action ~ |:badd| :bad[d] add buffer to the buffer list |:balt| :balt like ":badd" but also set the alternate file |:bdelete| :bd[elete] remove a buffer from the buffer list -|:behave| :be[have] set mouse and selection behavior |:belowright| :bel[owright] make split window appear right or below |:bfirst| :bf[irst] go to first buffer in the buffer list |:blast| :bl[ast] go to last buffer in the buffer list diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 6084a625ba..6fdf3775f6 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1516,9 +1516,10 @@ region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()* Parameters: ~ • {bufnr} (integer) number of buffer - • {pos1} integer[] (line, column) tuple marking beginning of - region - • {pos2} integer[] (line, column) tuple marking end of region + • {pos1} integer[]|string start of region as a (line, column) + tuple or string accepted by |getpos()| + • {pos2} integer[]|string end of region as a (line, column) tuple + or string accepted by |getpos()| • {regtype} (string) type of selection, see |setreg()| • {inclusive} (boolean) indicating whether column of pos2 is inclusive @@ -1697,6 +1698,19 @@ is_callable({f}) *vim.is_callable()* Return: ~ (boolean) `true` if `f` is callable, else `false` +list_contains({t}, {value}) *vim.list_contains()* + Checks if a list-like table (integer keys without gaps) contains `value`. + + Parameters: ~ + • {t} (table) Table to check (must be list-like, not validated) + • {value} any Value to compare + + Return: ~ + (boolean) `true` if `t` contains `value` + + See also: ~ + • |vim.tbl_contains()| for checking values in general tables + list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* Extends a list-like table with the values of another list-like table. @@ -1796,16 +1810,31 @@ tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* Return: ~ (table) o -tbl_contains({t}, {value}) *vim.tbl_contains()* - Checks if a list-like (vector) table contains `value`. +tbl_contains({t}, {value}, {opts}) *vim.tbl_contains()* + Checks if a table contains a given value, specified either directly or via + a predicate that is checked for each value. + + Example: >lua + + vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v) + return vim.deep_equal(v, { 'b', 'c' }) + end, { predicate = true }) + -- true +< Parameters: ~ • {t} (table) Table to check - • {value} any Value to compare + • {value} any Value to compare or predicate function reference + • {opts} (table|nil) Keyword arguments |kwargs|: + • predicate: (boolean) `value` is a function reference to be + checked (default false) Return: ~ (boolean) `true` if `t` contains `value` + See also: ~ + • |vim.list_contains()| for checking values in list-like tables + tbl_count({t}) *vim.tbl_count()* Counts the number of non-nil values in table `t`. @@ -1899,6 +1928,20 @@ tbl_get({o}, {...}) *vim.tbl_get()* Return: ~ any Nested value indexed by key (if it exists), else nil +tbl_isarray({t}) *vim.tbl_isarray()* + Tests if a Lua table can be treated as an array (a table indexed by + integers). + + Empty table `{}` is assumed to be an array, unless it was created by + |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result, + for example from |rpcrequest()| or |vim.fn|. + + Parameters: ~ + • {t} (table) + + Return: ~ + (boolean) `true` if array-like table, else `false`. + tbl_isempty({t}) *vim.tbl_isempty()* Checks if a table is empty. @@ -1912,17 +1955,18 @@ tbl_isempty({t}) *vim.tbl_isempty()* • https://github.com/premake/premake-core/blob/master/src/base/table.lua tbl_islist({t}) *vim.tbl_islist()* - Tests if a Lua table can be treated as an array. + Tests if a Lua table can be treated as a list (a table indexed by + consecutive integers starting from 1). - Empty table `{}` is assumed to be an array, unless it was created by + Empty table `{}` is assumed to be an list, unless it was created by |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result, for example from |rpcrequest()| or |vim.fn|. Parameters: ~ - • {t} (table) Table + • {t} (table) Return: ~ - (boolean) `true` if array-like table, else `false` + (boolean) `true` if list-like table, else `false`. tbl_keys({t}) *vim.tbl_keys()* Return a list of all keys used in a table. However, the order of the diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 6e2a1b1d3f..6697b3018a 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -15,9 +15,21 @@ BREAKING CHANGES *news-breaking* The following changes may require adaptations in user config or plugins. +• |vim.tbl_islist()| now checks whether a table is actually list-like (i.e., + has integer keys without gaps and starting from 1). For the previous + behavior (only check for integer keys, allow gaps or not starting with 1), + use |vim.tbl_isarray()|. + • "#" followed by a digit no longer stands for a function key at the start of the lhs of a mapping. +• `:behave` was removed. if you used `:behave mswin`, the following is equivalent: >vim + + set selection=exclusive + set selectmode=mouse,key + set mousemodel=popup + set keymodel=startsel,stopsel + ============================================================================== ADDED FEATURES *news-added* @@ -30,14 +42,19 @@ CHANGED FEATURES *news-changed* The following changes to existing APIs or features add new behavior. -• ... +• |vim.tbl_contains()| now works for general tables and allows specifying a + predicate function that is checked for each value. (Use |vim.list_contains()| + for checking list-like tables (integer keys without gaps) for literal values.) + +• |vim.region()| can use a string accepted by |getpos()| as position. ============================================================================== REMOVED FEATURES *news-removed* The following deprecated functions or APIs were removed. -• ... +• Vimball support is removed. + - :Vimuntar command removed. ============================================================================== DEPRECATIONS *news-deprecations* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d22a78700f..b4cad51990 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3589,7 +3589,6 @@ A jump table for the options with a short description can be found at |Q_op|. stopsel Using a not-shifted special key stops selection. Special keys in this context are the cursor keys, <End>, <Home>, <PageUp> and <PageDown>. - The 'keymodel' option is set by the |:behave| command. *'keywordprg'* *'kp'* 'keywordprg' 'kp' string (default ":Man", Windows: ":help") @@ -4168,21 +4167,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'mousehide' hide mouse pointer while typing text 'selectmode' whether to start Select mode or Visual mode - The :behave command provides some "profiles" for mouse behavior. - *:behave* *:be* - :be[have] {model} Set behavior for mouse and selection. Valid - arguments are: - mswin MS-Windows behavior - xterm Xterm behavior - - Using ":behave" changes these options: - option mswin xterm ~ - 'selectmode' "mouse,key" "" - 'mousemodel' "popup" "extend" - 'keymodel' "startsel,stopsel" "" - 'selection' "exclusive" "inclusive" - - *'mousefocus'* *'mousef'* *'nomousefocus'* *'nomousef'* 'mousefocus' 'mousef' boolean (default off) global @@ -4250,8 +4234,6 @@ A jump table for the options with a short description can be found at |Q_op|. "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click) "g<RightMouse>" is "<C-RightMouse> ("CTRL-T") - The 'mousemodel' option is set by the |:behave| command. - *'mousemoveevent'* *'mousemev'* *'nomousemoveevent'* *'nomousemev'* 'mousemoveevent' 'mousemev' boolean (default off) global @@ -5013,8 +4995,6 @@ A jump table for the options with a short description can be found at |Q_op|. backwards, you cannot include the last character of a line, when starting in Normal mode and 'virtualedit' empty. - The 'selection' option is set by the |:behave| command. - *'selectmode'* *'slm'* 'selectmode' 'slm' string (default "") global @@ -5025,7 +5005,6 @@ A jump table for the options with a short description can be found at |Q_op|. key when using shifted special keys cmd when using "v", "V" or CTRL-V See |Select-mode|. - The 'selectmode' option is set by the |:behave| command. *'sessionoptions'* *'ssop'* 'sessionoptions' 'ssop' string (default: "blank,buffers,curdir,folds, diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt index 2ae93b098a..a0e06a7d37 100644 --- a/runtime/doc/pi_health.txt +++ b/runtime/doc/pi_health.txt @@ -21,7 +21,7 @@ Plugin authors are encouraged to write new healthchecks. |health-dev| ============================================================================== Commands *health-commands* - *:che* *:checkhealth* *:CheckHealth* + *:che* *:checkhealth* :che[ckhealth] Run all healthchecks. *E5009* Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index 2230b82dec..e664b98086 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -33,23 +33,6 @@ Copyright 2005-2017: *tar-copyright* also write to the file. Currently, one may not make a new file in tar archives via the plugin. - *:Vimuntar* - VIMUNTAR~ - - :Vimuntar [vimhome] - - This command copies, if necessary, the tarball to the .vim or vimfiles - directory using the first writable directory in the |'runtimepath'| - when no [vimhome] is specified. Otherwise, the [vimhome] argument - allows the user to specify that directory, instead. - - The copy is done using the command in *g:tar_copycmd* , which is > - cp for cygwin, unix, macunix - copy for windows (32, 95, 64, 16) -< The extraction is done with the command specified with - *g:tar_extractcmd* , which by default is > - "tar -xf" -< *:TarDiff* DIFFERENCING SUPPORT~ diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 00b4f9eed4..8f7e393c02 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -190,26 +190,21 @@ The ":map" command (with no arguments) lists your current mappings. At least the ones for Normal mode. More about mappings in section |40.1|. ============================================================================== -*05.4* Adding a package *add-package* *vimball-install* +*05.4* Adding a package *add-package* -A package is a set of files that you can add to Vim. There are two kinds of -packages: optional and automatically loaded on startup. - -The Vim distribution comes with a few packages that you can optionally use. -For example, the vimball plugin. This plugin supports creating and using -vimballs (self-installing Vim plugin archives). - -To start using the vimball plugin, add one line to your vimrc file: > - packadd vimball +You may use |:packadd| to enable packages on demand. This is useful for plugins +you want to enable only sometimes. To enable `example_package`, use the +following command: > + packadd example_package -That's all! You can also type the command to try it out. Now you can find -help about this plugin: > - :help vimball +That's all! Now you can find help about this plugin: > + :help example_package This works, because when `:packadd` loaded the plugin it also added the -package directory in 'runtimepath', so that the help file can be found. The -tags for vimball's help are already created. If you need to generate the help -tags for a package, see the `:helptags` command. +package directory in 'runtimepath', so that the help file can be found. + +A package is a set of files that you can add to Vim. There are two kinds of +packages: optional and automatically loaded on startup. You can find packages on the Internet in various places. It usually comes as an archive or as a repository. For an archive you can follow these steps: diff --git a/runtime/doc/usr_09.txt b/runtime/doc/usr_09.txt index 8084d13b5d..ea16010dc2 100644 --- a/runtime/doc/usr_09.txt +++ b/runtime/doc/usr_09.txt @@ -124,41 +124,13 @@ This adds the 'l' flag to 'guioptions'. Standards are wonderful. In Microsoft Windows, you can use the mouse to select text in a standard manner. The X Window system also has a standard system for using the mouse. Unfortunately, these two standards are not the -same. - Fortunately, you can customize Vim. You can make the behavior of the mouse -work like an X Window system mouse or a Microsoft Windows mouse. The following -command makes the mouse behave like an X Window mouse: > +same. Fortunately, you can customize Vim. - :behave xterm - -The following command makes the mouse work like a Microsoft Windows mouse: > - - :behave mswin - -The default behavior of the mouse on Unix systems is xterm. The default -behavior on Windows systems is selected during the installation process. For -details about what the two behaviors are, see |:behave|. Here follows a -summary. - - -XTERM MOUSE BEHAVIOR - -Left mouse click position the cursor -Left mouse drag select text in Visual mode -Middle mouse click paste text from the clipboard -Right mouse click extend the selected text until the mouse - pointer - - -MSWIN MOUSE BEHAVIOR - -Left mouse click position the cursor -Left mouse drag select text in Select mode (see |09.4|) -Left mouse click, with Shift extend the selected text until the mouse - pointer -Middle mouse click paste text from the clipboard -Right mouse click display a pop-up menu +The following commands makes the mouse work more like a Microsoft Windows mouse: > + set selection=exclusive + set selectmode=mouse,key + set keymodel=startsel,stopsel The mouse can be further tuned. Check out these options if you want to change the way how the mouse works: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 7228473676..58f6d6f6f9 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -556,6 +556,7 @@ Aliases: vimdiff (alias for "nvim -d" |diff-mode|) Commands: + :behave :fixdel :hardcopy :helpfind @@ -573,6 +574,7 @@ Commands: :cscope :lcscope :scscope + :Vimuntar Compile-time features: Emacs tags support @@ -764,5 +766,8 @@ Hardcopy: `:hardcopy` was removed. Instead, use `:TOhtml` and print the resulting HTML using a web browser or some other HTML viewer. +Bundled plugins: + vimball *vimball* + ============================================================================== vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/ftplugin/cs.lua b/runtime/ftplugin/cs.lua new file mode 100644 index 0000000000..b4e68148f5 --- /dev/null +++ b/runtime/ftplugin/cs.lua @@ -0,0 +1 @@ +vim.bo.commentstring = '/*%s*/' diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua index 5b09126788..5188c13284 100644 --- a/runtime/lua/editorconfig.lua +++ b/runtime/lua/editorconfig.lua @@ -26,7 +26,7 @@ end function M.properties.charset(bufnr, val) assert( - vim.tbl_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val), + vim.list_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val), 'charset must be one of "utf-8", "utf-8-bom", "latin1", "utf-16be", or "utf-16le"' ) if val == 'utf-8' or val == 'utf-8-bom' then diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index cca9434e9c..1158d80941 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -64,6 +64,7 @@ local function system(cmd_, silent, env) local cmd_str = table.concat(cmd, ' ') man_error(string.format('command error: %s', cmd_str)) end + return '' end vim.wait(30000, function() diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/nvim/health.lua index b6d84404ec..6f544e9407 100644 --- a/runtime/lua/nvim/health.lua +++ b/runtime/lua/nvim/health.lua @@ -325,7 +325,7 @@ local function check_tmux() -- check for RGB capabilities local info = vim.fn.system({ 'tmux', 'display-message', '-p', '#{client_termfeatures}' }) info = vim.split(vim.trim(info), ',', { trimempty = true }) - if not vim.tbl_contains(info, 'RGB') then + if not vim.list_contains(info, 'RGB') then local has_rgb = false if #info == 0 then -- client_termfeatures may not be supported; fallback to checking show-messages diff --git a/runtime/lua/provider/health.lua b/runtime/lua/provider/health.lua new file mode 100644 index 0000000000..a5fe14732c --- /dev/null +++ b/runtime/lua/provider/health.lua @@ -0,0 +1,916 @@ +local M = {} + +local start = vim.health.report_start +local ok = vim.health.report_ok +local info = vim.health.report_info +local warn = vim.health.report_warn +local error = vim.health.report_error +local iswin = vim.loop.os_uname().sysname == 'Windows_NT' + +local shell_error_code = 0 +local function shell_error() + return shell_error_code ~= 0 +end + +-- Returns true if `cmd` exits with success, else false. +local function cmd_ok(cmd) + vim.fn.system(cmd) + return vim.v.shell_error == 0 +end + +local function executable(exe) + return vim.fn.executable(exe) == 1 +end + +local function is_blank(s) + return s:find('^%s*$') ~= nil +end + +local function isdir(path) + if not path then + return false + end + local stat = vim.loop.fs_stat(path) + if not stat then + return false + end + return stat.type == 'directory' +end + +local function isfile(path) + if not path then + return false + end + local stat = vim.loop.fs_stat(path) + if not stat then + return false + end + return stat.type == 'file' +end + +-- Handler for s:system() function. +local function system_handler(self, _, data, event) + if event == 'stderr' then + if self.add_stderr_to_output then + self.output = self.output .. vim.fn.join(data, '') + else + self.stderr = self.stderr .. vim.fn.join(data, '') + end + elseif event == 'stdout' then + self.output = self.output .. vim.fn.join(data, '') + elseif event == 'exit' then + shell_error_code = data + end +end + +-- Attempts to construct a shell command from an args list. +-- Only for display, to help users debug a failed command. +local function shellify(cmd) + if type(cmd) ~= 'table' then + return cmd + end + return vim.fn.join( + vim.fn.map(vim.fn.copy(cmd), [[v:val =~# ''\m[^\-.a-zA-Z_/]'' ? shellescape(v:val) : v:val]]), + ' ' + ) +end + +-- Run a system command and timeout after 30 seconds. +local function system(cmd, ...) + local args = { ... } + local args_count = vim.tbl_count(args) + + local stdin = (args_count > 0 and args[1] or '') + local stderr = (args_count > 1 and args[2] or false) + local ignore_error = (args_count > 2 and args[3] or false) + + local opts = { + add_stderr_to_output = stderr, + output = '', + stderr = '', + on_stdout = system_handler, + on_stderr = system_handler, + on_exit = system_handler, + } + local jobid = vim.fn.jobstart(cmd, opts) + + if jobid < 1 then + local message = 'Command error (job=' + .. jobid + .. '): `' + .. shellify(cmd) + .. '` (in ' + .. vim.fn.string(vim.fn.getcwd()) + .. ')' + + error(message) + shell_error_code = 1 + return opts.output + end + + if not is_blank(stdin) then + vim.cmd([[call jobsend(jobid, stdin)]]) + end + + local res = vim.fn.jobwait({ jobid }, 30000) + if res[1] == -1 then + error('Command timed out: ' .. shellify(cmd)) + vim.cmd([[call jobstop(jobid)]]) + elseif shell_error() and not ignore_error then + local emsg = 'Command error (job=' + .. jobid + .. ', exit code ' + .. shell_error_code + .. '): `' + .. shellify(cmd) + .. '` (in ' + .. vim.fn.string(vim.fn.getcwd()) + .. ')' + if not is_blank(opts.output) then + emsg = emsg .. '\noutput: ' .. opts.output + end + if not is_blank(opts.stderr) then + emsg = emsg .. '\nstderr: ' .. opts.stderr + end + error(emsg) + end + + -- return opts.output + local _ = ... + return vim.trim(vim.fn.system(cmd)) +end + +local function clipboard() + start('Clipboard (optional)') + + if + os.getenv('TMUX') + and executable('tmux') + and executable('pbpaste') + and not cmd_ok('pbpaste') + then + local tmux_version = string.match(vim.fn.system('tmux -V'), '%d+%.%d+') + local advice = { + 'Install tmux 2.6+. https://superuser.com/q/231130', + 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233', + } + error('pbcopy does not work with tmux version: ' .. tmux_version, advice) + end + + local clipboard_tool = vim.fn['provider#clipboard#Executable']() + if vim.g.clipboard and is_blank(clipboard_tool) then + local error_message = vim.fn['provider#clipboard#Error']() + error( + error_message, + "Use the example in :help g:clipboard as a template, or don't set g:clipboard at all." + ) + elseif is_blank(clipboard_tool) then + warn( + 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.', + ':help clipboard' + ) + else + ok('Clipboard tool found: ' .. clipboard_tool) + end +end + +local function disabled_via_loaded_var(provider) + local loaded_var = 'loaded_' .. provider .. '_provider' + local v = vim.g[loaded_var] + if v == 0 then + info('Disabled (' .. loaded_var .. '=' .. v .. ').') + return true + end + return false +end + +-- 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, effectively ignoring pyenv. +local function check_for_pyenv() + local pyenv_path = vim.fn.resolve(vim.fn.exepath('pyenv')) + + if is_blank(pyenv_path) then + return { '', '' } + end + + info('pyenv: Path: ' .. pyenv_path) + + local pyenv_root = os.getenv('PYENV_ROOT') and vim.fn.resolve('$PYENV_ROOT') or '' + + if is_blank(pyenv_root) then + pyenv_root = vim.trim(system({ pyenv_path, 'root' })) + info('pyenv: $PYENV_ROOT is not set. Infer from `pyenv root`.') + end + + if not isdir(pyenv_root) then + local message = 'pyenv: Root does not exist: ' + .. pyenv_root + .. '. Ignoring pyenv for all following checks.' + warn(message) + return { '', '' } + end + + info('pyenv: Root: ' .. pyenv_root) + + return { pyenv_path, pyenv_root } +end + +-- Check the Python interpreter's usability. +local function check_bin(bin) + if not isfile(bin) and (not iswin or not isfile(bin .. '.exe')) then + error('"' .. bin .. '" was not found.') + return false + elseif not executable(bin) then + error('"' .. bin .. '" is not executable.') + return false + end + return true +end + +-- Fetch the contents of a URL. +local function download(url) + local has_curl = executable('curl') + if has_curl and vim.fn.system({ 'curl', '-V' }):find('Protocols:.*https') then + local rv = system({ 'curl', '-sL', url }, '', 1, 1) + if shell_error() then + return 'curl error with ' .. url .. ': ' .. shell_error_code + else + return rv + end + elseif executable('python') then + local script = "try:\n\ + from urllib.request import urlopen\n\ + except ImportError:\n\ + from urllib2 import urlopen\n\ + response = urlopen('" .. url .. "')\n\ + print(response.read().decode('utf8'))\n" + local rv = system({ 'python', '-c', script }) + if is_blank(rv) and shell_error() then + return 'python urllib.request error: ' .. shell_error_code + else + return rv + end + end + + local message = 'missing `curl` ' + + if has_curl then + message = message .. '(with HTTPS support) ' + end + message = message .. 'and `python`, cannot make web request' + + return message +end + +-- Get the latest Nvim Python client (pynvim) version from PyPI. +local function latest_pypi_version() + local pypi_version = 'unable to get pypi response' + local pypi_response = download('https://pypi.python.org/pypi/pynvim/json') + if not is_blank(pypi_response) then + local pcall_ok, output = pcall(vim.fn.json_decode, pypi_response) + local pypi_data + if pcall_ok then + pypi_data = output + else + return 'error: ' .. pypi_response + end + + local pypi_element = pypi_data['info'] or {} + pypi_version = pypi_element['version'] or 'unable to parse' + end + return pypi_version +end + +local function is_bad_response(s) + local lower = s:lower() + return vim.startswith(lower, 'unable') + or vim.startswith(lower, 'error') + or vim.startswith(lower, 'outdated') +end + +-- 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} +-- } +local function version_info(python) + local pypi_version = latest_pypi_version() + + local python_version = vim.trim(system({ + python, + '-c', + 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))', + })) + + if is_blank(python_version) then + python_version = 'unable to parse ' .. python .. ' response' + end + + local nvim_path = vim.trim(system({ + python, + '-c', + 'import sys; sys.path = [p for p in sys.path if p != ""]; import neovim; print(neovim.__file__)', + })) + if shell_error() or is_blank(nvim_path) then + return { python_version, 'unable to load neovim Python module', pypi_version, nvim_path } + end + + -- Assuming that multiple versions of a package are installed, sort them + -- numerically in descending order. + local function compare(metapath1, metapath2) + local a = vim.fn.matchstr(vim.fn.fnamemodify(metapath1, ':p:h:t'), [[[0-9.]\+]]) + local b = vim.fn.matchstr(vim.fn.fnamemodify(metapath2, ':p:h:t'), [[[0-9.]\+]]) + if a == b then + return 0 + elseif a > b then + return 1 + else + return -1 + end + end + + -- Try to get neovim.VERSION (added in 0.1.11dev). + local nvim_version = system({ + python, + '-c', + 'from neovim import VERSION as v; print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))', + }, '', 1, 1) + if is_blank(nvim_version) then + nvim_version = 'unable to find pynvim module version' + local base = vim.fs.basename(nvim_path, ':h') + local metas = vim.fn.glob(base .. '-*/METADATA', 1, 1) + vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', 1, 1)) + vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', 1, 1)) + metas = table.sort(metas, compare) + + if metas and next(metas) ~= nil then + for _, meta_line in ipairs(vim.fn.readfile(metas[1])) do + if vim.startswith(meta_line, 'Version:') then + nvim_version = vim.fn.matchstr(meta_line, [[^Version: \zs\S\+]]) + break + end + end + end + end + + local nvim_path_base = vim.fn.fnamemodify(nvim_path, [[:~:h]]) + local version_status = 'unknown; ' .. nvim_path_base + if is_bad_response(nvim_version) and is_bad_response(pypi_version) then + if vim.version.lt(nvim_version, pypi_version) then + version_status = 'outdated; from ' .. nvim_path_base + else + version_status = 'up to date' + end + end + + return { python_version, nvim_version, pypi_version, version_status } +end + +-- Resolves Python executable path by invoking and checking `sys.executable`. +local function python_exepath(invocation) + return vim.fs.normalize( + system(vim.fn.fnameescape(invocation) .. ' -c "import sys; sys.stdout.write(sys.executable)"') + ) +end + +local function python() + start('Python 3 provider (optional)') + + local pyname = 'python3' + local python_exe = '' + local virtual_env = os.getenv('VIRTUAL_ENV') + local venv = virtual_env and vim.fn.resolve(virtual_env) or '' + local host_prog_var = pyname .. '_host_prog' + local python_multiple = {} + + if disabled_via_loaded_var(pyname) then + return + end + + local pyenv_table = check_for_pyenv() + local pyenv = pyenv_table[1] + local pyenv_root = pyenv_table[2] + + if vim.g[host_prog_var] then + local message = 'Using: g:' .. host_prog_var .. ' = "' .. vim.g[host_prog_var] .. '"' + info(message) + end + + local python_table = vim.fn['provider#pythonx#Detect'](3) + pyname = python_table[1] + local pythonx_warnings = python_table[2] + + if is_blank(pyname) then + warn( + 'No Python executable found that can `import neovim`. ' + .. 'Using the first available executable for diagnostics.' + ) + elseif vim.g[host_prog_var] then + python_exe = pyname + end + + -- No Python executable could `import neovim`, or host_prog_var was used. + if not is_blank(pythonx_warnings) then + 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 not is_blank(pyname) and is_blank(python_exe) then + if not vim.g[host_prog_var] then + local message = '`g:' + .. host_prog_var + .. '` is not set. Searching for ' + .. pyname + .. ' in the environment.' + info(message) + end + + if not is_blank(pyenv) then + python_exe = vim.trim(system({ pyenv, 'which', pyname }, '', 1)) + if is_blank(python_exe) then + warn('pyenv could not find ' .. pyname .. '.') + end + end + + if is_blank(python_exe) then + python_exe = vim.fn.exepath(pyname) + + if os.getenv('PATH') then + local path_sep = iswin and ';' or ':' + local paths = vim.split(os.getenv('PATH') or '', path_sep) + + for _, path in ipairs(paths) do + local path_bin = vim.fs.normalize(path .. '/' .. pyname) + if + path_bin ~= vim.fs.normalize(python_exe) + and vim.list_contains(python_multiple, path_bin) + and executable(path_bin) + then + python_multiple[#python_multiple + 1] = path_bin + end + end + + if vim.tbl_count(python_multiple) > 0 then + -- This is worth noting since the user may install something + -- that changes $PATH, like homebrew. + local message = 'Multiple ' + .. pyname + .. ' executables found. ' + .. 'Set `g:' + .. host_prog_var + .. '` to avoid surprises.' + info(message) + end + + if python_exe:find('shims') then + local message = '`' .. python_exe .. '` appears to be a pyenv shim.' + local advice = '`pyenv` is not in $PATH, your pyenv installation is broken. Set `g:' + .. host_prog_var + .. '` to avoid surprises.' + + warn(message, advice) + end + end + end + end + + if not is_blank(python_exe) and not vim.g[host_prog_var] then + if + is_blank(venv) + and not is_blank(pyenv) + and not is_blank(pyenv_root) + and vim.startswith(vim.fn.resolve(python_exe), pyenv_root .. '/') + then + local advice = 'Create a virtualenv specifically for Nvim using pyenv, and set `g:' + .. host_prog_var + .. '`. This will avoid the need to install the pynvim module in each version/virtualenv.' + warn('pyenv is not set up optimally.', advice) + elseif not is_blank(venv) then + local venv_root + if not is_blank(pyenv_root) then + venv_root = pyenv_root + else + venv_root = vim.fs.dirname(venv) + end + + if vim.startswith(vim.fn.resolve(python_exe), venv_root .. '/') then + local advice = 'Create a virtualenv specifically for Nvim and use `g:' + .. host_prog_var + .. '`. This will avoid the need to install the pynvim module in each virtualenv.' + warn('Your virtualenv is not set up optimally.', advice) + end + end + end + + if is_blank(python_exe) and not is_blank(pyname) then + -- An error message should have already printed. + error('`' .. pyname .. '` was not found.') + elseif not is_blank(python_exe) and not check_bin(python_exe) then + python_exe = '' + end + + -- Diagnostic output + info('Executable: ' .. (is_blank(python_exe) and 'Not found' or python_exe)) + if vim.tbl_count(python_multiple) > 0 then + for _, path_bin in ipairs(python_multiple) do + info('Other python executable: ' .. path_bin) + end + end + + if is_blank(python_exe) then + -- 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 + local pynvim_table = vim.fn['provider#pythonx#DetectByModule']('pynvim', 3) + local pynvim_exe = pynvim_table[1] + if not is_blank(pynvim_exe) then + local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": ' + .. pynvim_exe + local advice = { + 'Use that Python version to reinstall "pynvim" and optionally "neovim".', + pynvim_exe .. ' -m pip uninstall pynvim neovim', + pynvim_exe .. ' -m pip install pynvim', + pynvim_exe .. ' -m pip install neovim # only if needed by third-party software', + } + error(message, advice) + end + else + local version_info_table = version_info(python_exe) + local majorpyversion = version_info_table[1] + local current = version_info_table[2] + local latest = version_info_table[3] + local status = version_info_table[4] + + if vim.fn.str2nr(majorpyversion) ~= 3 then + warn('Unexpected Python version. This could lead to confusing error messages.') + end + + info('Python version: ' .. majorpyversion) + + if is_bad_response(status) then + info('pynvim version: ' .. current .. ' (' .. status .. ')') + else + info('pynvim version: ' .. current) + end + + if is_bad_response(current) then + error( + 'pynvim is not installed.\nError: ' .. current, + 'Run in shell: ' .. python_exe .. ' -m pip install pynvim' + ) + end + + if is_bad_response(latest) then + warn('Could not contact PyPI to get latest version.') + error('HTTP request failed: ' .. latest) + elseif is_bad_response(status) then + warn('Latest pynvim is NOT installed: ' .. latest) + elseif not is_bad_response(current) then + ok('Latest pynvim is installed.') + end + end + + start('Python virtualenv') + if not virtual_env then + ok('no $VIRTUAL_ENV') + return + end + local errors = {} + -- Keep hints as dict keys in order to discard duplicates. + local 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. + local bin_dir = iswin and 'Scripts' or 'bin' + local venv_bins = vim.tbl_filter(function(v) + -- XXX: Remove irrelevant executables found in bin/. + return not v:match('python%-config') + end, vim.fn.glob(string.format('%s/%s/python*', virtual_env, bin_dir), true, true)) + if vim.tbl_count(venv_bins) > 0 then + for _, venv_bin in pairs(venv_bins) do + venv_bin = vim.fs.normalize(venv_bin) + local py_bin_basename = vim.fs.basename(venv_bin) + local nvim_py_bin = python_exepath(vim.fn.exepath(py_bin_basename)) + local subshell_py_bin = python_exepath(py_bin_basename) + if venv_bin ~= nvim_py_bin then + errors[#errors + 1] = '$PATH yields this ' + .. py_bin_basename + .. ' executable: ' + .. nvim_py_bin + local 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.' + hints[hint] = true + end + if venv_bin ~= subshell_py_bin then + errors[#errors + 1] = '$PATH in subshells yields this ' + .. py_bin_basename + .. ' executable: ' + .. subshell_py_bin + local 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' + hints[hint] = true + end + end + else + errors[#errors + 1] = 'no Python executables found in the virtualenv ' + .. bin_dir + .. ' directory.' + end + + local msg = '$VIRTUAL_ENV is set to: ' .. virtual_env + if vim.tbl_count(errors) > 0 then + if vim.tbl_count(venv_bins) > 0 then + msg = msg + .. '\nAnd its ' + .. bin_dir + .. ' directory contains: ' + .. vim.fn.join(vim.fn.map(venv_bins, [[fnamemodify(v:val, ':t')]]), ', ') + end + local conj = '\nBut ' + for _, err in ipairs(errors) do + msg = msg .. conj .. err + conj = '\nAnd ' + end + msg = msg .. '\nSo invoking Python may lead to unexpected results.' + warn(msg, vim.fn.keys(hints)) + else + info(msg) + info( + 'Python version: ' + .. system('python -c "import platform, sys; sys.stdout.write(platform.python_version())"') + ) + ok('$VIRTUAL_ENV provides :!python.') + end +end + +local function ruby() + start('Ruby provider (optional)') + + if disabled_via_loaded_var('ruby') then + return + end + + if not executable('ruby') or not executable('gem') then + warn( + '`ruby` and `gem` must be in $PATH.', + 'Install Ruby and verify that `ruby` and `gem` commands work.' + ) + return + end + info('Ruby: ' .. system({ 'ruby', '-v' })) + + local ruby_detect_table = vim.fn['provider#ruby#Detect']() + local host = ruby_detect_table[1] + if is_blank(host) then + 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 + end + info('Host: ' .. host) + + local latest_gem_cmd = (iswin and 'cmd /c gem list -ra "^^neovim$"' or 'gem list -ra ^neovim$') + local latest_gem = system(vim.fn.split(latest_gem_cmd)) + if shell_error() or is_blank(latest_gem) then + error( + 'Failed to run: ' .. latest_gem_cmd, + { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } + ) + return + end + local gem_split = vim.split(latest_gem, [[neovim (\|, \|)$]]) + latest_gem = gem_split[1] or 'not found' + + local current_gem_cmd = { host, '--version' } + local current_gem = system(current_gem_cmd) + if shell_error() then + error( + 'Failed to run: ' .. table.concat(current_gem_cmd, ' '), + { 'Report this issue with the output of: ', table.concat(current_gem_cmd, ' ') } + ) + return + end + + if vim.version.lt(current_gem, latest_gem) then + local message = 'Gem "neovim" is out-of-date. Installed: ' + .. current_gem + .. ', latest: ' + .. latest_gem + warn(message, 'Run in shell: gem update neovim') + else + ok('Latest "neovim" gem is installed: ' .. current_gem) + end +end + +local function node() + start('Node.js provider (optional)') + + if disabled_via_loaded_var('node') then + return + end + + if + not executable('node') + or (not executable('npm') and not executable('yarn') and not executable('pnpm')) + then + 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 + end + + -- local node_v = vim.fn.split(system({'node', '-v'}), "\n")[1] or '' + local node_v = system({ 'node', '-v' }) + info('Node.js: ' .. node_v) + if shell_error() or vim.version.lt(node_v, '6.0.0') then + warn('Nvim node.js host does not support Node ' .. node_v) + -- Skip further checks, they are nonsense if nodejs is too old. + return + end + if vim.fn['provider#node#can_inspect']() == 0 then + warn( + 'node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.' + ) + end + + local node_detect_table = vim.fn['provider#node#Detect']() + local host = node_detect_table[1] + if is_blank(host) then + 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 + end + info('Nvim node.js host: ' .. host) + + local manager = 'npm' + if executable('yarn') then + manager = 'yarn' + elseif executable('pnpm') then + manager = 'pnpm' + end + + local latest_npm_cmd = ( + iswin and 'cmd /c ' .. manager .. ' info neovim --json' or manager .. ' info neovim --json' + ) + local latest_npm = system(vim.fn.split(latest_npm_cmd)) + if shell_error() or is_blank(latest_npm) then + error( + 'Failed to run: ' .. latest_npm_cmd, + { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } + ) + return + end + + local pcall_ok, output = pcall(vim.fn.json_decode, latest_npm) + local pkg_data + if pcall_ok then + pkg_data = output + else + return 'error: ' .. latest_npm + end + local latest_npm_subtable = pkg_data['dist-tags'] or {} + latest_npm = latest_npm_subtable['latest'] or 'unable to parse' + + local current_npm_cmd = { 'node', host, '--version' } + local current_npm = system(current_npm_cmd) + if shell_error() then + error( + 'Failed to run: ' .. table.concat(current_npm_cmd, ' '), + { 'Report this issue with the output of: ', table.concat(current_npm_cmd, ' ') } + ) + return + end + + if latest_npm ~= 'unable to parse' and vim.version.lt(current_npm, latest_npm) then + local message = 'Package "neovim" is out-of-date. Installed: ' + .. current_npm + .. ' latest: ' + .. latest_npm + warn(message({ + '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 + ok('Latest "neovim" npm/yarn/pnpm package is installed: ' .. current_npm) + end +end + +local function perl() + start('Perl provider (optional)') + + if disabled_via_loaded_var('perl') then + return + end + + local perl_detect_table = vim.fn['provider#perl#Detect']() + local perl_exec = perl_detect_table[1] + local perl_warnings = perl_detect_table[2] + + if is_blank(perl_exec) then + if not is_blank(perl_warnings) then + 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 + warn('No usable perl executable found') + end + return + end + + 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 + system({ perl_exec, '-W', '-MApp::cpanminus', '-e', '' }) + if shell_error() then + return { perl_exec, '"App::cpanminus" module is not installed' } + end + + local 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', + } + + local latest_cpan = system(latest_cpan_cmd) + if shell_error() or is_blank(latest_cpan) then + error( + 'Failed to run: ' .. table.concat(latest_cpan_cmd, ' '), + { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } + ) + return + elseif latest_cpan[1] == '!' then + local cpanm_errs = vim.split(latest_cpan, '!') + if cpanm_errs[1]:find("Can't write to ") then + local advice = {} + for i = 2, #cpanm_errs do + advice[#advice + 1] = cpanm_errs[i] + end + + warn(cpanm_errs[1], advice) + -- Last line is the package info + latest_cpan = cpanm_errs[#cpanm_errs] + else + error('Unknown warning from command: ' .. latest_cpan_cmd, cpanm_errs) + return + end + end + latest_cpan = vim.fn.matchstr(latest_cpan, [[\(\.\?\d\)\+]]) + if is_blank(latest_cpan) then + error('Cannot parse version number from cpanm output: ' .. latest_cpan) + return + end + + local current_cpan_cmd = { perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION' } + local current_cpan = system(current_cpan_cmd) + if shell_error then + error( + 'Failed to run: ' .. table.concat(current_cpan_cmd, ' '), + { 'Report this issue with the output of: ', table.concat(current_cpan_cmd, ' ') } + ) + return + end + + if vim.version.lt(current_cpan, latest_cpan) then + local message = 'Module "Neovim::Ext" is out-of-date. Installed: ' + .. current_cpan + .. ', latest: ' + .. latest_cpan + warn(message, 'Run in shell: cpanm -n Neovim::Ext') + else + ok('Latest "Neovim::Ext" cpan module is installed: ' .. current_cpan) + end +end + +function M.check() + clipboard() + python() + ruby() + node() + perl() +end + +return M diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index fa0980563a..c922ec93db 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -402,8 +402,8 @@ end --- Input and output positions are (0,0)-indexed and indicate byte positions. --- ---@param bufnr integer number of buffer ----@param pos1 integer[] (line, column) tuple marking beginning of region ----@param pos2 integer[] (line, column) tuple marking end of region +---@param pos1 integer[]|string start of region as a (line, column) tuple or string accepted by |getpos()| +---@param pos2 integer[]|string end of region as a (line, column) tuple or string accepted by |getpos()| ---@param regtype string type of selection, see |setreg()| ---@param inclusive boolean indicating whether column of pos2 is inclusive ---@return table region Table of the form `{linenr = {startcol,endcol}}`. @@ -414,6 +414,24 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) vim.fn.bufload(bufnr) end + if type(pos1) == 'string' then + local pos = vim.fn.getpos(pos1) + pos1 = { pos[2] - 1, pos[3] - 1 + pos[4] } + end + if type(pos2) == 'string' then + local pos = vim.fn.getpos(pos2) + pos2 = { pos[2] - 1, pos[3] - 1 + pos[4] } + end + + if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then + pos1, pos2 = pos2, pos1 + end + + -- getpos() may return {0,0,0,0} + if pos1[1] < 0 or pos1[2] < 0 then + return {} + end + -- check that region falls within current buffer local buf_line_count = vim.api.nvim_buf_line_count(bufnr) pos1[1] = math.min(pos1[1], buf_line_count - 1) diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 714038f8e4..d1b50304c7 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -744,7 +744,7 @@ function M.get_namespaces() end ---@class Diagnostic ----@field buffer integer +---@field bufnr integer ---@field lnum integer 0-indexed ---@field end_lnum nil|integer 0-indexed ---@field col integer 0-indexed diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index d71a806ea8..a6cfcb730f 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -15,8 +15,8 @@ M.priorities = { ---@param bufnr integer Buffer number to apply highlighting to ---@param ns integer Namespace to add highlight to ---@param higroup string Highlight group to use for highlighting ----@param start { [1]: integer, [2]: integer } Start position {line, col} ----@param finish { [1]: integer, [2]: integer } Finish position {line, col} +---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()| +---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()| ---@param opts table|nil Optional parameters -- - regtype type of range (see |setreg()|, default charwise) -- - inclusive boolean indicating whether the range is end-inclusive (default false) @@ -27,11 +27,6 @@ function M.range(bufnr, ns, higroup, start, finish, opts) local inclusive = opts.inclusive or false local priority = opts.priority or M.priorities.user - -- sanity check - if start[2] < 0 or finish[1] < start[1] then - return - end - local region = vim.region(bufnr, start, finish, regtype, inclusive) for linenr, cols in pairs(region) do local end_row @@ -104,18 +99,12 @@ function M.on_yank(opts) yank_timer:close() end - local pos1 = vim.fn.getpos("'[") - local pos2 = vim.fn.getpos("']") - - pos1 = { pos1[2] - 1, pos1[3] - 1 + pos1[4] } - pos2 = { pos2[2] - 1, pos2[3] - 1 + pos2[4] } - M.range( bufnr, yank_ns, higroup, - pos1, - pos2, + "'[", + "']", { regtype = event.regtype, inclusive = event.inclusive, priority = M.priorities.user } ) diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 38b1e9fc0f..66627fe4e7 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -5,7 +5,7 @@ local loaders = package.loaders local M = {} ----@alias CacheHash {mtime: {sec:number, nsec:number}, size:number, type: string} +---@alias CacheHash {mtime: {nsec: integer, sec: integer}, size: integer, type?: uv.aliases.fs_stat_types} ---@alias CacheEntry {hash:CacheHash, chunk:string} ---@class ModuleFindOpts @@ -28,12 +28,11 @@ M.enabled = false ---@field _rtp string[] ---@field _rtp_pure string[] ---@field _rtp_key string +---@field _hashes? table<string, CacheHash> local Loader = { VERSION = 3, ---@type table<string, table<string,ModuleInfo>> _indexed = {}, - ---@type table<string, CacheHash> - _hashes = {}, ---@type table<string, string[]> _topmods = {}, _loadfile = loadfile, @@ -44,9 +43,13 @@ local Loader = { } --- @param path string ---- @return uv.fs_stat.result +--- @return CacheHash --- @private function Loader.get_hash(path) + if not Loader._hashes then + return uv.fs_stat(path) --[[@as CacheHash]] + end + if not Loader._hashes[path] then -- Note we must never save a stat for a non-existent path. -- For non-existent paths fs_stat() will return nil. @@ -163,13 +166,16 @@ end ---@return string|function ---@private function Loader.loader(modname) + Loader._hashes = {} local ret = M.find(modname)[1] if ret then -- Make sure to call the global loadfile so we respect any augmentations done elsewhere. -- E.g. profiling local chunk, err = loadfile(ret.modpath) + Loader._hashes = nil return chunk or error(err) end + Loader._hashes = nil return '\ncache_loader: module ' .. modname .. ' not found' end @@ -373,7 +379,9 @@ function M.reset(path) end -- Path could be a directory so just clear all the hashes. - Loader._hashes = {} + if Loader._hashes then + Loader._hashes = {} + end end --- Enables the experimental Lua module loader: @@ -441,7 +449,7 @@ function Loader.lsmod(path) if topname then Loader._indexed[path][topname] = { modpath = modpath, modname = topname } Loader._topmods[topname] = Loader._topmods[topname] or {} - if not vim.tbl_contains(Loader._topmods[topname], path) then + if not vim.list_contains(Loader._topmods[topname], path) then table.insert(Loader._topmods[topname], path) end end @@ -515,7 +523,7 @@ function M._inspect(opts) { ms(Loader._stats[stat].time / Loader._stats[stat].total) .. '\n', 'Bold' }, }) for k, v in pairs(Loader._stats[stat]) do - if not vim.tbl_contains({ 'time', 'total' }, k) then + if not vim.list_contains({ 'time', 'total' }, k) then chunks[#chunks + 1] = { '* ' .. k .. ':' .. string.rep(' ', 9 - #k) } chunks[#chunks + 1] = { tostring(v) .. '\n', 'Number' } end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 2d39f2d45d..5c78bd7580 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -171,7 +171,7 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids) if restrict_client_ids and #restrict_client_ids > 0 then local filtered_client_ids = {} for client_id in pairs(client_ids) do - if vim.tbl_contains(restrict_client_ids, client_id) then + if vim.list_contains(restrict_client_ids, client_id) then filtered_client_ids[client_id] = true end end @@ -2186,7 +2186,7 @@ function lsp.formatexpr(opts) opts = opts or {} local timeout_ms = opts.timeout_ms or 500 - if vim.tbl_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then + if vim.list_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then -- `formatexpr` is also called when exceeding `textwidth` in insert mode -- fall back to internal formatting return 1 @@ -2384,4 +2384,3 @@ lsp.commands = setmetatable({}, { }) return lsp --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 797d8960d5..e7ada5415f 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -17,14 +17,14 @@ P.take_until = function(targets, specials) table.insert(raw, '\\') new_pos = new_pos + 1 c = string.sub(input, new_pos, new_pos) - if not vim.tbl_contains(targets, c) and not vim.tbl_contains(specials, c) then + if not vim.list_contains(targets, c) and not vim.list_contains(specials, c) then table.insert(esc, '\\') end table.insert(raw, c) table.insert(esc, c) new_pos = new_pos + 1 else - if vim.tbl_contains(targets, c) then + if vim.list_contains(targets, c) then break end table.insert(raw, c) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8bf3764f5e..3d9011656f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -806,4 +806,3 @@ function M.execute_command(command_params) end return M --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 81cac6a511..005a0047fa 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -42,7 +42,7 @@ local function execute_lens(lens, bufnr, client_id) -- Need to use the client that returned the lens → must not use buf_request local command_provider = client.server_capabilities.executeCommandProvider local commands = type(command_provider) == 'table' and command_provider.commands or {} - if not vim.tbl_contains(commands, command.command) then + if not vim.list_contains(commands, command.command) then vim.notify( string.format( 'Language server does not support command `%s`. This command may require a client extension.', diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index d01f8e6159..71bef43bc1 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -644,4 +644,3 @@ for k, fn in pairs(M) do end return M --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 51dcb7d21d..3d5bc06c3f 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -174,4 +174,3 @@ function log.should_log(level) end return log --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index f4489ad17d..2cb8fc7955 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -894,4 +894,3 @@ function protocol.resolve_capabilities(server_capabilities) end return protocol --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 30b61d01d6..af3190c9bd 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -753,4 +753,3 @@ return { client_errors = client_errors, create_read_loop = create_read_loop, } --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ebde7af16c..31af2afb0b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1476,7 +1476,7 @@ end local function close_preview_window(winnr, bufnrs) vim.schedule(function() -- exit if we are in one of ignored buffers - if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then + if bufnrs and vim.list_contains(bufnrs, api.nvim_get_current_buf()) then return end @@ -2154,4 +2154,3 @@ M._get_line_byte_from_position = get_line_byte_from_position M.buf_versions = {} return M --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index eb734fb512..f700f4a6b3 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -252,12 +252,53 @@ function vim.tbl_filter(func, t) return rettab end ---- Checks if a list-like (vector) table contains `value`. +--- Checks if a table contains a given value, specified either directly or via +--- a predicate that is checked for each value. +--- +--- Example: +--- <pre>lua +--- vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v) +--- return vim.deep_equal(v, { 'b', 'c' }) +--- end, { predicate = true }) +--- -- true +--- </pre> +--- +---@see |vim.list_contains()| for checking values in list-like tables --- ---@param t table Table to check +---@param value any Value to compare or predicate function reference +---@param opts (table|nil) Keyword arguments |kwargs|: +--- - predicate: (boolean) `value` is a function reference to be checked (default false) +---@return boolean `true` if `t` contains `value` +function vim.tbl_contains(t, value, opts) + vim.validate({ t = { t, 't' }, opts = { opts, 't', true } }) + + local pred + if opts and opts.predicate then + vim.validate({ value = { value, 'c' } }) + pred = value + else + pred = function(v) + return v == value + end + end + + for _, v in pairs(t) do + if pred(v) then + return true + end + end + return false +end + +--- Checks if a list-like table (integer keys without gaps) contains `value`. +--- +---@see |vim.tbl_contains()| for checking values in general tables +--- +---@param t table Table to check (must be list-like, not validated) ---@param value any Value to compare ---@return boolean `true` if `t` contains `value` -function vim.tbl_contains(t, value) +function vim.list_contains(t, value) vim.validate({ t = { t, 't' } }) for _, v in ipairs(t) do @@ -279,10 +320,10 @@ function vim.tbl_isempty(t) return next(t) == nil end ---- We only merge empty tables or tables that are not a list +--- We only merge empty tables or tables that are not an array (indexed by integers) ---@private local function can_merge(v) - return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_islist(v)) + return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_isarray(v)) end local function tbl_extend(behavior, deep_extend, ...) @@ -513,15 +554,15 @@ function vim.spairs(t) end end ---- Tests if a Lua table can be treated as an array. +--- Tests if a Lua table can be treated as an array (a table indexed by integers). --- --- Empty table `{}` is assumed to be an array, unless it was created by --- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result, --- for example from |rpcrequest()| or |vim.fn|. --- ----@param t table Table ----@return boolean `true` if array-like table, else `false` -function vim.tbl_islist(t) +---@param t table +---@return boolean `true` if array-like table, else `false`. +function vim.tbl_isarray(t) if type(t) ~= 'table' then return false end @@ -529,7 +570,8 @@ function vim.tbl_islist(t) local count = 0 for k, _ in pairs(t) do - if type(k) == 'number' then + --- Check if the number k is an integer + if type(k) == 'number' and k == math.floor(k) then count = count + 1 else return false @@ -548,6 +590,38 @@ function vim.tbl_islist(t) end end +--- Tests if a Lua table can be treated as a list (a table indexed by consecutive integers starting from 1). +--- +--- Empty table `{}` is assumed to be an list, unless it was created by +--- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result, +--- for example from |rpcrequest()| or |vim.fn|. +--- +---@param t table +---@return boolean `true` if list-like table, else `false`. +function vim.tbl_islist(t) + if type(t) ~= 'table' then + return false + end + + local num_elem = vim.tbl_count(t) + + if num_elem == 0 then + -- TODO(bfredl): in the future, we will always be inside nvim + -- then this check can be deleted. + if vim._empty_dict_mt == nil then + return nil + end + return getmetatable(t) ~= vim._empty_dict_mt + else + for i = 1, num_elem do + if t[i] == nil then + return false + end + end + return true + end +end + --- Counts the number of non-nil values in table `t`. --- --- <pre>lua @@ -811,4 +885,3 @@ function vim.defaulttable(create) end return vim --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 5b87e6ac31..8a747ba14c 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -458,7 +458,7 @@ local directive_handlers = { metadata[id] = {} end - local pattern, replacement = pred[3], pred[3] + local pattern, replacement = pred[3], pred[4] assert(type(pattern) == 'string') assert(type(replacement) == 'string') diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index 38759fcdc0..08ed829114 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -134,4 +134,3 @@ return { uri_to_fname = uri_to_fname, uri_to_bufnr = uri_to_bufnr, } --- vim:sw=2 ts=2 et diff --git a/runtime/mswin.vim b/runtime/mswin.vim index 2b04c1aea3..482b720c53 100644 --- a/runtime/mswin.vim +++ b/runtime/mswin.vim @@ -15,7 +15,10 @@ endif set cpo&vim " set 'selection', 'selectmode', 'mousemodel' and 'keymodel' for MS-Windows -behave mswin +set selection=exclusive +set selectmode=mouse,key +set mousemodel=popup +set keymodel=startsel,stopsel " backspace and cursor keys wrap to previous/next line set backspace=indent,eol,start whichwrap+=<,>,[,] diff --git a/runtime/pack/dist/opt/vimball/autoload/vimball.vim b/runtime/pack/dist/opt/vimball/autoload/vimball.vim deleted file mode 100644 index 9c7dcbda0f..0000000000 --- a/runtime/pack/dist/opt/vimball/autoload/vimball.vim +++ /dev/null @@ -1,775 +0,0 @@ -" vimball.vim : construct a file containing both paths and files -" Author: Charles E. Campbell -" Date: Apr 11, 2016 -" Version: 37 -" GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim -" Copyright: (c) 2004-2011 by Charles E. Campbell -" The VIM LICENSE applies to Vimball.vim, and Vimball.txt -" (see |copyright|) except use "Vimball" instead of "Vim". -" No warranty, express or implied. -" *** *** Use At-Your-Own-Risk! *** *** - -" --------------------------------------------------------------------- -" Load Once: {{{1 -if &cp || exists("g:loaded_vimball") - finish -endif -let g:loaded_vimball = "v37" -if v:version < 702 - echohl WarningMsg - echo "***warning*** this version of vimball needs vim 7.2" - echohl Normal - finish -endif -let s:keepcpo= &cpo -set cpo&vim -"DechoTabOn - -" ===================================================================== -" Constants: {{{1 -if !exists("s:USAGE") - let s:USAGE = 0 - let s:WARNING = 1 - let s:ERROR = 2 - - " determine if cygwin is in use or not - if !exists("g:netrw_cygwin") - if has("win32") || has("win95") || has("win64") || has("win16") - if &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$' - let g:netrw_cygwin= 1 - else - let g:netrw_cygwin= 0 - endif - else - let g:netrw_cygwin= 0 - endif - endif - - " set up g:vimball_mkdir if the mkdir() call isn't defined - if !exists("*mkdir") - if exists("g:netrw_local_mkdir") - let g:vimball_mkdir= g:netrw_local_mkdir - elseif executable("mkdir") - let g:vimball_mkdir= "mkdir" - elseif executable("makedir") - let g:vimball_mkdir= "makedir" - endif - if !exists(g:vimball_mkdir) - call vimball#ShowMesg(s:WARNING,"(vimball) g:vimball_mkdir undefined") - endif - endif -endif - -" ===================================================================== -" Functions: {{{1 - -" --------------------------------------------------------------------- -" vimball#MkVimball: creates a vimball given a list of paths to files {{{2 -" Input: -" line1,line2: a range of lines containing paths to files to be included in the vimball -" writelevel : if true, force a write to filename.vmb, even if it exists -" (usually accomplished with :MkVimball! ... -" filename : base name of file to be created (ie. filename.vmb) -" Output: a filename.vmb using vimball format: -" path -" filesize -" [file] -" path -" filesize -" [file] -fun! vimball#MkVimball(line1,line2,writelevel,...) range -" call Dfunc("MkVimball(line1=".a:line1." line2=".a:line2." writelevel=".a:writelevel." vimballname<".a:1.">) a:0=".a:0) - if a:1 =~ '\.vim$' || a:1 =~ '\.txt$' - let vbname= substitute(a:1,'\.\a\{3}$','.vmb','') - else - let vbname= a:1 - endif - if vbname !~ '\.vmb$' - let vbname= vbname.'.vmb' - endif -" call Decho("vbname<".vbname.">") - if !a:writelevel && a:1 =~ '[\/]' - call vimball#ShowMesg(s:ERROR,"(MkVimball) vimball name<".a:1."> should not include slashes; use ! to insist") -" call Dret("MkVimball : vimball name<".a:1."> should not include slashes") - return - endif - if !a:writelevel && filereadable(vbname) - call vimball#ShowMesg(s:ERROR,"(MkVimball) file<".vbname."> exists; use ! to insist") -" call Dret("MkVimball : file<".vbname."> already exists; use ! to insist") - return - endif - - " user option bypass - call vimball#SaveSettings() - - if a:0 >= 2 - " allow user to specify where to get the files - let home= expand(a:2) - else - " use first existing directory from rtp - let home= vimball#VimballHome() - endif - - " save current directory - let curdir = getcwd() - call s:ChgDir(home) - - " record current tab, initialize while loop index - let curtabnr = tabpagenr() - let linenr = a:line1 -" call Decho("curtabnr=".curtabnr) - - while linenr <= a:line2 - let svfile = getline(linenr) -" call Decho("svfile<".svfile.">") - - if !filereadable(svfile) - call vimball#ShowMesg(s:ERROR,"unable to read file<".svfile.">") - call s:ChgDir(curdir) - call vimball#RestoreSettings() -" call Dret("MkVimball") - return - endif - - " create/switch to mkvimball tab - if !exists("vbtabnr") - tabnew - sil! file Vimball - let vbtabnr= tabpagenr() - else - exe "tabn ".vbtabnr - endif - - let lastline= line("$") + 1 - if lastline == 2 && getline("$") == "" - call setline(1,'" Vimball Archiver by Charles E. Campbell') - call setline(2,'UseVimball') - call setline(3,'finish') - let lastline= line("$") + 1 - endif - call setline(lastline ,substitute(svfile,'$',' [[[1','')) - call setline(lastline+1,0) - - " write the file from the tab -" call Decho("exe $r ".fnameescape(svfile)) - exe "$r ".fnameescape(svfile) - - call setline(lastline+1,line("$") - lastline - 1) -" call Decho("lastline=".lastline." line$=".line("$")) - - " restore to normal tab - exe "tabn ".curtabnr - let linenr= linenr + 1 - endwhile - - " write the vimball - exe "tabn ".vbtabnr - call s:ChgDir(curdir) - setlocal ff=unix - if a:writelevel -" call Decho("exe w! ".fnameescape(vbname)) - exe "w! ".fnameescape(vbname) - else -" call Decho("exe w ".fnameescape(vbname)) - exe "w ".fnameescape(vbname) - endif -" call Decho("Vimball<".vbname."> created") - echo "Vimball<".vbname."> created" - - " remove the evidence - setlocal nomod bh=wipe - exe "tabn ".curtabnr - exe "tabc! ".vbtabnr - - " restore options - call vimball#RestoreSettings() - -" call Dret("MkVimball") -endfun - -" --------------------------------------------------------------------- -" vimball#Vimball: extract and distribute contents from a vimball {{{2 -" (invoked the the UseVimball command embedded in -" vimballs' prologue) -fun! vimball#Vimball(really,...) -" call Dfunc("vimball#Vimball(really=".a:really.") a:0=".a:0) - - if v:version < 701 || (v:version == 701 && !exists('*fnameescape')) - echoerr "your vim is missing the fnameescape() function (pls upgrade to vim 7.2 or later)" -" call Dret("vimball#Vimball : needs 7.1 with patch 299 or later") - return - endif - - if getline(1) !~ '^" Vimball Archiver' - echoerr "(Vimball) The current file does not appear to be a Vimball!" -" call Dret("vimball#Vimball") - return - endif - - " set up standard settings - call vimball#SaveSettings() - let curtabnr = tabpagenr() - let vimballfile = expand("%:tr") - - " set up vimball tab -" call Decho("setting up vimball tab") - tabnew - sil! file Vimball - let vbtabnr= tabpagenr() - let didhelp= "" - - " go to vim plugin home - if a:0 > 0 - " let user specify the directory where the vimball is to be unpacked. - " If, however, the user did not specify a full path, set the home to be below the current directory - let home= expand(a:1) - if has("win32") || has("win95") || has("win64") || has("win16") - if home !~ '^\a:[/\\]' - let home= getcwd().'/'.a:1 - endif - elseif home !~ '^/' - let home= getcwd().'/'.a:1 - endif - else - let home= vimball#VimballHome() - endif -" call Decho("home<".home.">") - - " save current directory and remove older same-named vimball, if any - let curdir = getcwd() -" call Decho("home<".home.">") -" call Decho("curdir<".curdir.">") - - call s:ChgDir(home) - let s:ok_unablefind= 1 - call vimball#RmVimball(vimballfile) - unlet s:ok_unablefind - - let linenr = 4 - let filecnt = 0 - - " give title to listing of (extracted) files from Vimball Archive - if a:really - echohl Title | echomsg "Vimball Archive" | echohl None - else - echohl Title | echomsg "Vimball Archive Listing" | echohl None - echohl Statement | echomsg "files would be placed under: ".home | echohl None - endif - - " apportion vimball contents to various files -" call Decho("exe tabn ".curtabnr) - exe "tabn ".curtabnr -" call Decho("linenr=".linenr." line$=".line("$")) - while 1 < linenr && linenr < line("$") - let fname = substitute(getline(linenr),'\t\[\[\[1$','','') - let fname = substitute(fname,'\\','/','g') - let fsize = substitute(getline(linenr+1),'^\(\d\+\).\{-}$','\1','')+0 - let fenc = substitute(getline(linenr+1),'^\d\+\s*\(\S\{-}\)$','\1','') - let filecnt = filecnt + 1 -" call Decho("fname<".fname."> fsize=".fsize." filecnt=".filecnt. " fenc=".fenc) - - if a:really - echomsg "extracted <".fname.">: ".fsize." lines" - else - echomsg "would extract <".fname.">: ".fsize." lines" - endif -" call Decho("using L#".linenr.": will extract file<".fname.">") -" call Decho("using L#".(linenr+1).": fsize=".fsize) - - " Allow AsNeeded/ directory to take place of plugin/ directory - " when AsNeeded/filename is filereadable or was present in VimballRecord - if fname =~ '\<plugin/' - let anfname= substitute(fname,'\<plugin/','AsNeeded/','') - if filereadable(anfname) || (exists("s:VBRstring") && s:VBRstring =~# anfname) -" call Decho("using anfname<".anfname."> instead of <".fname.">") - let fname= anfname - endif - endif - - " make directories if they don't exist yet - if a:really -" call Decho("making directories if they don't exist yet (fname<".fname.">)") - let fnamebuf= substitute(fname,'\\','/','g') - let dirpath = substitute(home,'\\','/','g') -" call Decho("init: fnamebuf<".fnamebuf.">") -" call Decho("init: dirpath <".dirpath.">") - while fnamebuf =~ '/' - let dirname = dirpath."/".substitute(fnamebuf,'/.*$','','') - let dirpath = dirname - let fnamebuf = substitute(fnamebuf,'^.\{-}/\(.*\)$','\1','') -" call Decho("dirname<".dirname.">") -" call Decho("dirpath<".dirpath.">") - if !isdirectory(dirname) -" call Decho("making <".dirname.">") - if exists("g:vimball_mkdir") - call system(g:vimball_mkdir." ".shellescape(dirname)) - else - call mkdir(dirname) - endif - call s:RecordInVar(home,"rmdir('".dirname."')") - endif - endwhile - endif - call s:ChgDir(home) - - " grab specified qty of lines and place into "a" buffer - " (skip over path/filename and qty-lines) - let linenr = linenr + 2 - let lastline = linenr + fsize - 1 -" call Decho("exe ".linenr.",".lastline."yank a") - " no point in handling a zero-length file - if lastline >= linenr - exe "silent ".linenr.",".lastline."yank a" - - " copy "a" buffer into tab -" call Decho('copy "a buffer into tab#'.vbtabnr) - exe "tabn ".vbtabnr - setlocal ma - sil! %d - silent put a - 1 - sil! d - - " write tab to file - if a:really - let fnamepath= home."/".fname -" call Decho("exe w! ".fnameescape(fnamepath)) - if fenc != "" - exe "silent w! ++enc=".fnameescape(fenc)." ".fnameescape(fnamepath) - else - exe "silent w! ".fnameescape(fnamepath) - endif - echo "wrote ".fnameescape(fnamepath) - call s:RecordInVar(home,"call delete('".fnamepath."')") - endif - - " return to tab with vimball -" call Decho("exe tabn ".curtabnr) - exe "tabn ".curtabnr - - " set up help if it's a doc/*.txt file -" call Decho("didhelp<".didhelp."> fname<".fname.">") - if a:really && didhelp == "" && fname =~ 'doc/[^/]\+\.\(txt\|..x\)$' - let didhelp= substitute(fname,'^\(.*\<doc\)[/\\][^.]*\.\(txt\|..x\)$','\1','') -" call Decho("didhelp<".didhelp.">") - endif - endif - - " update for next file -" call Decho("update linenr= [linenr=".linenr."] + [fsize=".fsize."] = ".(linenr+fsize)) - let linenr= linenr + fsize - endwhile - - " set up help -" call Decho("about to set up help: didhelp<".didhelp.">") - if didhelp != "" - let htpath= home."/".didhelp -" call Decho("exe helptags ".htpath) - exe "helptags ".fnameescape(htpath) - echo "did helptags" - endif - - " make sure a "Press ENTER..." prompt appears to keep the messages showing! - while filecnt <= &ch - echomsg " " - let filecnt= filecnt + 1 - endwhile - - " record actions in <.VimballRecord> - call s:RecordInFile(home) - - " restore events, delete tab and buffer - exe "sil! tabn ".vbtabnr - setlocal nomod bh=wipe - exe "sil! tabn ".curtabnr - exe "sil! tabc! ".vbtabnr - call vimball#RestoreSettings() - call s:ChgDir(curdir) - -" call Dret("vimball#Vimball") -endfun - -" --------------------------------------------------------------------- -" vimball#RmVimball: remove any files, remove any directories made by any {{{2 -" previous vimball extraction based on a file of the current -" name. -" Usage: RmVimball (assume current file is a vimball; remove) -" RmVimball vimballname -fun! vimball#RmVimball(...) -" call Dfunc("vimball#RmVimball() a:0=".a:0) - if exists("g:vimball_norecord") -" call Dret("vimball#RmVimball : (g:vimball_norecord)") - return - endif - - if a:0 == 0 - let curfile= expand("%:tr") -" call Decho("case a:0=0: curfile<".curfile."> (used expand(%:tr))") - else - if a:1 =~ '[\/]' - call vimball#ShowMesg(s:USAGE,"RmVimball vimballname [path]") -" call Dret("vimball#RmVimball : suspect a:1<".a:1.">") - return - endif - let curfile= a:1 -" call Decho("case a:0=".a:0.": curfile<".curfile.">") - endif - if curfile =~ '\.vmb$' - let curfile= substitute(curfile,'\.vmb','','') - elseif curfile =~ '\.vba$' - let curfile= substitute(curfile,'\.vba','','') - endif - if a:0 >= 2 - let home= expand(a:2) - else - let home= vimball#VimballHome() - endif - let curdir = getcwd() -" call Decho("home <".home.">") -" call Decho("curfile<".curfile.">") -" call Decho("curdir <".curdir.">") - - call s:ChgDir(home) - if filereadable(".VimballRecord") -" call Decho(".VimballRecord is readable") -" call Decho("curfile<".curfile.">") - keepalt keepjumps 1split - sil! keepalt keepjumps e .VimballRecord - let keepsrch= @/ -" call Decho('search for ^\M'.curfile.'.\m: ') -" call Decho('search for ^\M'.curfile.'.\m{vba|vmb}: ') -" call Decho('search for ^\M'.curfile.'\m[-0-9.]*\.{vba|vmb}: ') - if search('^\M'.curfile."\m: ".'cw') - let foundit= 1 - elseif search('^\M'.curfile.".\mvmb: ",'cw') - let foundit= 2 - elseif search('^\M'.curfile.'\m[-0-9.]*\.vmb: ','cw') - let foundit= 2 - elseif search('^\M'.curfile.".\mvba: ",'cw') - let foundit= 1 - elseif search('^\M'.curfile.'\m[-0-9.]*\.vba: ','cw') - let foundit= 1 - else - let foundit = 0 - endif - if foundit - if foundit == 1 - let exestring = substitute(getline("."),'^\M'.curfile.'\m\S\{-}\.vba: ','','') - else - let exestring = substitute(getline("."),'^\M'.curfile.'\m\S\{-}\.vmb: ','','') - endif - let s:VBRstring= substitute(exestring,'call delete(','','g') - let s:VBRstring= substitute(s:VBRstring,"[')]",'','g') -" call Decho("exe ".exestring) - sil! keepalt keepjumps exe exestring - sil! keepalt keepjumps d - let exestring= strlen(substitute(exestring,'call delete(.\{-})|\=',"D","g")) -" call Decho("exestring<".exestring.">") - echomsg "removed ".exestring." files" - else - let s:VBRstring= '' - let curfile = substitute(curfile,'\.vmb','','') -" call Decho("unable to find <".curfile."> in .VimballRecord") - if !exists("s:ok_unablefind") - call vimball#ShowMesg(s:WARNING,"(RmVimball) unable to find <".curfile."> in .VimballRecord") - endif - endif - sil! keepalt keepjumps g/^\s*$/d - sil! keepalt keepjumps wq! - let @/= keepsrch - endif - call s:ChgDir(curdir) - -" call Dret("vimball#RmVimball") -endfun - -" --------------------------------------------------------------------- -" vimball#Decompress: attempts to automatically decompress vimballs {{{2 -fun! vimball#Decompress(fname,...) -" call Dfunc("Decompress(fname<".a:fname.">) a:0=".a:0) - - " decompression: - if expand("%") =~ '.*\.gz' && executable("gunzip") - " handle *.gz with gunzip - silent exe "!gunzip ".shellescape(a:fname) - if v:shell_error != 0 - call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) gunzip may have failed with <".a:fname.">") - endif - let fname= substitute(a:fname,'\.gz$','','') - exe "e ".escape(fname,' \') - if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif - - elseif expand("%") =~ '.*\.gz' && executable("gzip") - " handle *.gz with gzip -d - silent exe "!gzip -d ".shellescape(a:fname) - if v:shell_error != 0 - call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "gzip -d" may have failed with <'.a:fname.">") - endif - let fname= substitute(a:fname,'\.gz$','','') - exe "e ".escape(fname,' \') - if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif - - elseif expand("%") =~ '.*\.bz2' && executable("bunzip2") - " handle *.bz2 with bunzip2 - silent exe "!bunzip2 ".shellescape(a:fname) - if v:shell_error != 0 - call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) bunzip2 may have failed with <".a:fname.">") - endif - let fname= substitute(a:fname,'\.bz2$','','') - exe "e ".escape(fname,' \') - if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif - - elseif expand("%") =~ '.*\.bz2' && executable("bzip2") - " handle *.bz2 with bzip2 -d - silent exe "!bzip2 -d ".shellescape(a:fname) - if v:shell_error != 0 - call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "bzip2 -d" may have failed with <'.a:fname.">") - endif - let fname= substitute(a:fname,'\.bz2$','','') - exe "e ".escape(fname,' \') - if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif - - elseif expand("%") =~ '.*\.zip' && executable("unzip") - " handle *.zip with unzip - silent exe "!unzip ".shellescape(a:fname) - if v:shell_error != 0 - call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) unzip may have failed with <".a:fname.">") - endif - let fname= substitute(a:fname,'\.zip$','','') - exe "e ".escape(fname,' \') - if a:0 == 0| call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") | endif - endif - - if a:0 == 0| setlocal noma bt=nofile fmr=[[[,]]] fdm=marker | endif - -" call Dret("Decompress") -endfun - -" --------------------------------------------------------------------- -" vimball#ShowMesg: {{{2 -fun! vimball#ShowMesg(level,msg) -" call Dfunc("vimball#ShowMesg(level=".a:level." msg<".a:msg.">)") - - let rulerkeep = &ruler - let showcmdkeep = &showcmd - set noruler noshowcmd - redraw! - - if &fo =~# '[ta]' - echomsg "***vimball*** ".a:msg - else - if a:level == s:WARNING || a:level == s:USAGE - echohl WarningMsg - elseif a:level == s:ERROR - echohl Error - endif - echomsg "***vimball*** ".a:msg - echohl None - endif - - if a:level != s:USAGE - call inputsave()|let ok= input("Press <cr> to continue")|call inputrestore() - endif - - let &ruler = rulerkeep - let &showcmd = showcmdkeep - -" call Dret("vimball#ShowMesg") -endfun -" ===================================================================== -" s:ChgDir: change directory (in spite of Windoze) {{{2 -fun! s:ChgDir(newdir) -" call Dfunc("ChgDir(newdir<".a:newdir.">)") - if (has("win32") || has("win95") || has("win64") || has("win16")) - try - exe 'silent cd '.fnameescape(substitute(a:newdir,'/','\\','g')) - catch /^Vim\%((\a\+)\)\=:E/ - call mkdir(fnameescape(substitute(a:newdir,'/','\\','g'))) - exe 'silent cd '.fnameescape(substitute(a:newdir,'/','\\','g')) - endtry - else - try - exe 'silent cd '.fnameescape(a:newdir) - catch /^Vim\%((\a\+)\)\=:E/ - call mkdir(fnameescape(a:newdir)) - exe 'silent cd '.fnameescape(a:newdir) - endtry - endif -" call Dret("ChgDir : curdir<".getcwd().">") -endfun - -" --------------------------------------------------------------------- -" s:RecordInVar: record a un-vimball command in the .VimballRecord file {{{2 -fun! s:RecordInVar(home,cmd) -" call Dfunc("RecordInVar(home<".a:home."> cmd<".a:cmd.">)") - if a:cmd =~ '^rmdir' -" if !exists("s:recorddir") -" let s:recorddir= substitute(a:cmd,'^rmdir',"call s:Rmdir",'') -" else -" let s:recorddir= s:recorddir."|".substitute(a:cmd,'^rmdir',"call s:Rmdir",'') -" endif - elseif !exists("s:recordfile") - let s:recordfile= a:cmd - else - let s:recordfile= s:recordfile."|".a:cmd - endif -" call Dret("RecordInVar : s:recordfile<".(exists("s:recordfile")? s:recordfile : "")."> s:recorddir<".(exists("s:recorddir")? s:recorddir : "").">") -endfun - -" --------------------------------------------------------------------- -" s:RecordInFile: {{{2 -fun! s:RecordInFile(home) -" call Dfunc("s:RecordInFile()") - if exists("g:vimball_norecord") -" call Dret("s:RecordInFile : g:vimball_norecord") - return - endif - - if exists("s:recordfile") || exists("s:recorddir") - let curdir= getcwd() - call s:ChgDir(a:home) - keepalt keepjumps 1split - - let cmd= expand("%:tr").": " -" call Decho("cmd<".cmd.">") - - sil! keepalt keepjumps e .VimballRecord - setlocal ma - $ - if exists("s:recordfile") && exists("s:recorddir") - let cmd= cmd.s:recordfile."|".s:recorddir - elseif exists("s:recorddir") - let cmd= cmd.s:recorddir - elseif exists("s:recordfile") - let cmd= cmd.s:recordfile - else -" call Dret("s:RecordInFile : neither recordfile nor recorddir exist") - return - endif -" call Decho("cmd<".cmd.">") - - " put command into buffer, write .VimballRecord `file - keepalt keepjumps put=cmd - sil! keepalt keepjumps g/^\s*$/d - sil! keepalt keepjumps wq! - call s:ChgDir(curdir) - - if exists("s:recorddir") -" call Decho("unlet s:recorddir<".s:recorddir.">") - unlet s:recorddir - endif - if exists("s:recordfile") -" call Decho("unlet s:recordfile<".s:recordfile.">") - unlet s:recordfile - endif - else -" call Decho("s:record[file|dir] doesn't exist") - endif - -" call Dret("s:RecordInFile") -endfun - -" --------------------------------------------------------------------- -" vimball#VimballHome: determine/get home directory path (usually from rtp) {{{2 -fun! vimball#VimballHome() -" call Dfunc("vimball#VimballHome()") - if exists("g:vimball_home") - let home= g:vimball_home - else - " go to vim plugin home - for home in split(&rtp,',') + [''] - if isdirectory(home) && filewritable(home) | break | endif - let basehome= substitute(home,'[/\\]\.vim$','','') - if isdirectory(basehome) && filewritable(basehome) - let home= basehome."/.vim" - break - endif - endfor - if home == "" - " just pick the first directory - let home= substitute(&rtp,',.*$','','') - endif - if (has("win32") || has("win95") || has("win64") || has("win16")) - let home= substitute(home,'/','\\','g') - endif - endif - " insure that the home directory exists -" call Decho("picked home<".home.">") - if !isdirectory(home) - if exists("g:vimball_mkdir") -" call Decho("home<".home."> isn't a directory -- making it now with g:vimball_mkdir<".g:vimball_mkdir.">") -" call Decho("system(".g:vimball_mkdir." ".shellescape(home).")") - call system(g:vimball_mkdir." ".shellescape(home)) - else -" call Decho("home<".home."> isn't a directory -- making it now with mkdir()") - call mkdir(home) - endif - endif -" call Dret("vimball#VimballHome <".home.">") - return home -endfun - -" --------------------------------------------------------------------- -" vimball#SaveSettings: {{{2 -fun! vimball#SaveSettings() -" call Dfunc("SaveSettings()") - let s:makeep = getpos("'a") - let s:regakeep= @a - if exists("+acd") - let s:acdkeep = &acd - endif - let s:eikeep = &ei - let s:fenkeep = &l:fen - let s:hidkeep = &hidden - let s:ickeep = &ic - let s:lzkeep = &lz - let s:pmkeep = &pm - let s:repkeep = &report - let s:vekeep = &ve - let s:ffkeep = &l:ff - let s:swfkeep = &l:swf - if exists("+acd") - setlocal ei=all ve=all noacd nofen noic report=999 nohid bt= ma lz pm= ff=unix noswf - else - setlocal ei=all ve=all nofen noic report=999 nohid bt= ma lz pm= ff=unix noswf - endif - " vimballs should be in unix format - setlocal ff=unix -" call Dret("SaveSettings") -endfun - -" --------------------------------------------------------------------- -" vimball#RestoreSettings: {{{2 -fun! vimball#RestoreSettings() -" call Dfunc("RestoreSettings()") - let @a = s:regakeep - if exists("+acd") - let &acd = s:acdkeep - endif - let &l:fen = s:fenkeep - let &hidden = s:hidkeep - let &ic = s:ickeep - let &lz = s:lzkeep - let &pm = s:pmkeep - let &report = s:repkeep - let &ve = s:vekeep - let &ei = s:eikeep - let &l:ff = s:ffkeep - if s:makeep[0] != 0 - " restore mark a -" call Decho("restore mark-a: makeep=".string(makeep)) - call setpos("'a",s:makeep) - endif - if exists("+acd") - unlet s:acdkeep - endif - unlet s:regakeep s:eikeep s:fenkeep s:hidkeep s:ickeep s:repkeep s:vekeep s:makeep s:lzkeep s:pmkeep s:ffkeep -" call Dret("RestoreSettings") -endfun - -let &cpo = s:keepcpo -unlet s:keepcpo - -" --------------------------------------------------------------------- -" Modelines: {{{1 -" vim: fdm=marker diff --git a/runtime/pack/dist/opt/vimball/doc/vimball.txt b/runtime/pack/dist/opt/vimball/doc/vimball.txt deleted file mode 100644 index 602fe85954..0000000000 --- a/runtime/pack/dist/opt/vimball/doc/vimball.txt +++ /dev/null @@ -1,273 +0,0 @@ -*vimball.txt* For Vim version 7.4. Last change: 2012 Jan 17 - - ---------------- - Vimball Archiver - ---------------- - -Author: Charles E. Campbell, Jr. <NdrOchip@ScampbellPfamily.AbizM> - (remove NOSPAM from Campbell's email first) -Copyright: (c) 2004-2012 by Charles E. Campbell, Jr. *Vimball-copyright* - The VIM LICENSE (see |copyright|) applies to the files in this - package, including vimballPlugin.vim, vimball.vim, and pi_vimball.txt. - except use "vimball" instead of "VIM". Like anything else that's free, - vimball.vim and its associated files are provided *as is* and comes with - no warranty of any kind, either expressed or implied. No guarantees - of merchantability. No guarantees of suitability for any purpose. By - using this plugin, you agree that in no event will the copyright - holder be liable for any damages resulting from the use of this - software. Use at your own risk! - -============================================================================== -1. Contents *vba* *vimball* *vimball-contents* - - 1. Contents......................................: |vimball-contents| - 2. Vimball Introduction..........................: |vimball-intro| - 3. Vimball Manual................................: |vimball-manual| - MkVimball.....................................: |:MkVimball| - UseVimball....................................: |:UseVimball| - RmVimball.....................................: |:RmVimball| - 4. Vimball History...............................: |vimball-history| - - -============================================================================== -2. Vimball Introduction *vimball-intro* - - Vimball is intended to make life simpler for users of plugins. All - a user needs to do with a vimball is: > - vim someplugin.vba - :so % - :q -< and the plugin and all its components will be installed into their - appropriate directories. Note that one doesn't need to be in any - particular directory when one does this. Plus, any help for the - plugin will also be automatically installed. - - If a user has decided to use the AsNeeded plugin, vimball is smart - enough to put scripts nominally intended for .vim/plugin/ into - .vim/AsNeeded/ instead. - - Removing a plugin that was installed with vimball is really easy: > - vim - :RmVimball someplugin -< This operation is not at all easy for zips and tarballs, for example. - - Vimball examines the user's |'runtimepath'| to determine where to put - the scripts. The first directory mentioned on the runtimepath is - usually used if possible. Use > - :echo &rtp -< to see that directory. - - -============================================================================== -3. Vimball Manual *vimball-manual* - -MAKING A VIMBALL *:MkVimball* - :[range]MkVimball[!] filename [path] - - The range is composed of lines holding paths to files to be included - in your new vimball, omitting the portion of the paths that is - normally specified by the runtimepath (|'rtp'|). As an example: > - plugin/something.vim - doc/something.txt -< using > - :[range]MkVimball filename -< - on this range of lines will create a file called "filename.vba" which - can be used by Vimball.vim to re-create these files. If the - "filename.vba" file already exists, then MkVimball will issue a - warning and not create the file. Note that these paths are relative - to your .vim (vimfiles) directory, and the files should be in that - directory. The vimball plugin normally uses the first |'runtimepath'| - directory that exists as a prefix; don't use absolute paths, unless - the user has specified such a path. - - If you use the exclamation point (!), then MkVimball will create the - "filename.vba" file, overwriting it if it already exists. This - behavior resembles that for |:w|. - - If you wish to force slashes into the filename, that can also be done - by using the exclamation mark (ie. :MkVimball! path/filename). - - The tip at https://vim.wikia.com/wiki/Using_VimBall_with_%27Make%27 - has a good idea on how to automate the production of vimballs using - make. - - -MAKING DIRECTORIES VIA VIMBALLS *g:vimball_mkdir* - - First, the |mkdir()| command is tried (not all systems support it). - - If it doesn't exist, then if g:vimball_mkdir doesn't exist, it is set - as follows: > - |g:netrw_local_mkdir|, if it exists - "mkdir" , if it is executable - "makedir" , if it is executable - Otherwise , it is undefined. -< One may explicitly specify the directory making command using - g:vimball_mkdir. This command is used to make directories that - are needed as indicated by the vimball. - - -CONTROLLING THE VIMBALL EXTRACTION DIRECTORY *g:vimball_home* - - You may override the use of the |'runtimepath'| by specifying a - variable, g:vimball_home. - - *vimball-extract* - vim filename.vba - - Simply editing a Vimball will cause Vimball.vim to tell the user to - source the file to extract its contents. - - Extraction will only proceed if the first line of a putative vimball - file holds the "Vimball Archiver by Charles E. Campbell, Jr., Ph.D." - line. - -LISTING FILES IN A VIMBALL *:VimballList* - - :VimballList - - This command will tell Vimball to list the files in the archive, along - with their lengths in lines. - -MANUALLY INVOKING VIMBALL EXTRACTION *:UseVimball* - - :UseVimball [path] - - This command is contained within the vimball itself; it invokes the - vimball#Vimball() routine which is responsible for unpacking the - vimball. One may choose to execute it by hand instead of sourcing - the vimball; one may also choose to specify a path for the - installation, thereby overriding the automatic choice of the first - existing directory on the |'runtimepath'|. - -REMOVING A VIMBALL *:RmVimball* - - :RmVimball vimballfile [path] - - This command removes all files generated by the specified vimball - (but not any directories it may have made). One may choose a path - for de-installation, too (see |'runtimepath'|); otherwise, the - default is the first existing directory on the |'runtimepath'|. - To implement this, a file (.VimballRecord) is made in that directory - containing a record of what files need to be removed for all vimballs - used thus far. - -PREVENTING LOADING - - If for some reason you don't want to be able to extract plugins - using vimballs: you may prevent the loading of vimball.vim by - putting the following two variables in your <.vimrc>: > - - let g:loaded_vimballPlugin= 1 - let g:loaded_vimball = 1 -< -WINDOWS *vimball-windows* - - Many vimball files are compressed with gzip. Windows, unfortunately, - does not come provided with a tool to decompress gzip'ped files. - Fortunately, there are a number of tools available for Windows users - to un-gzip files: -> - Item Tool/Suite Free Website - ---- ---------- ---- ------- - 7zip tool y https://www.7-zip.org/ - Winzip tool n https://www.winzip.com/downwz.htm - unxutils suite y https://unxutils.sourceforge.net/ - cygwin suite y https://www.cygwin.com/ - GnuWin32 suite y https://gnuwin32.sourceforge.net/ - MinGW suite y https://www.mingw.org/ -< - -============================================================================== -4. Vimball History *vimball-history* {{{1 - - 34 : Sep 22, 2011 * "UseVimball path" now supports a non-full path by - prepending the current directory to it. - 33 : Apr 02, 2011 * Gave priority to *.vmb over *.vba - * Changed silent! to sil! (shorter) - * Safed |'swf'| setting (during vimball extraction, - its now turned off) - 32 : May 19, 2010 * (Christian Brabandt) :so someplugin.vba and - :so someplugin.vba.gz (and the other supported - compression types) now works - * (Jan Steffens) added support for xz compression - * fenc extraction was erroneously picking up the - end of the line number when no file encoding - was present. Fixed. - * By request, beginning the switchover from the vba - extension to vmb. Currently both are supported; - MkVimball, however, now will create *.vmb files. - Feb 11, 2011 * motoyakurotsu reported an error with vimball's - handling of zero-length files - 30 : Dec 08, 2008 * fnameescape() inserted to protect error - messaging using corrupted filenames from - causing problems - * RmVimball supports filenames that would - otherwise be considered to have "magic" - characters (ie. Abc[1].vba) - Feb 18, 2009 * s:Escape(), g:vimball_shq, and g:netrw_shq - removed (shellescape() used directly) - Oct 05, 2009 * (Nikolai Weibull) suggested that MkVimball - be allowed to use slashes in the filename. - 26 : May 27, 2008 * g:vimball_mkdir usage installed. Makes the - $HOME/.vim (or $HOME\vimfiles) directory if - necessary. - May 30, 2008 * (tnx to Bill McCarthy) found and fixed a bug: - vimball wasn't updating plugins to AsNeeded/ - when it should - 25 : Mar 24, 2008 * changed vimball#Vimball() to recognize doc/*.??x - files as help files, too. - Apr 18, 2008 * RmVimball command is now protected by saving and - restoring settings -- in particular, acd was - causing problems as reported by Zhang Shuhan - 24 : Nov 15, 2007 * g:vimball_path_escape used by s:Path() to - prevent certain characters from causing trouble - (defunct: |fnameescape()| and |shellescape()| - now used instead) - 22 : Mar 21, 2007 * uses setlocal instead of set during BufEnter - 21 : Nov 27, 2006 * (tnx to Bill McCarthy) vimball had a header - handling problem and it now changes \s to /s - 20 : Nov 20, 2006 * substitute() calls have all had the 'e' flag - removed. - 18 : Aug 01, 2006 * vimballs now use folding to easily display their - contents. - * if a user has AsNeeded/somefile, then vimball - will extract plugin/somefile to the AsNeeded/ - directory - 17 : Jun 28, 2006 * changes all \s to /s internally for Windows - 16 : Jun 15, 2006 * A. Mechelynck's idea to allow users to specify - installation root paths implemented for - UseVimball, MkVimball, and RmVimball. - * RmVimball implemented - 15 : Jun 13, 2006 * bugfix - 14 : May 26, 2006 * bugfixes - 13 : May 01, 2006 * exists("&acd") used to determine if the acd - option exists - 12 : May 01, 2006 * bugfix - the |'acd'| option is not always defined - 11 : Apr 27, 2006 * VimballList would create missing subdirectories that - the vimball specified were needed. Fixed. - 10 : Apr 27, 2006 * moved all setting saving/restoration to a pair of - functions. Included some more settings in them - which frequently cause trouble. - 9 : Apr 26, 2006 * various changes to support Windows' predilection - for backslashes and spaces in file and directory - names. - 7 : Apr 25, 2006 * bypasses foldenable - * uses more exe and less norm! (:yank :put etc) - * does better at insuring a "Press ENTER" prompt - appears to keep its messages visible - 4 : Mar 31, 2006 * BufReadPost seems to fire twice; BufReadEnter - only fires once, so the "Source this file..." - message is now issued only once. - 3 : Mar 20, 2006 * removed query, now requires sourcing to be - extracted (:so %). Message to that effect - included. - * :VimballList now shows files that would be - extracted. - 2 : Mar 20, 2006 * query, :UseVimball included - 1 : Mar 20, 2006 * initial release - - -============================================================================== -vim:tw=78:ts=8:ft=help:fdm=marker diff --git a/runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim b/runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim deleted file mode 100644 index d7473a0296..0000000000 --- a/runtime/pack/dist/opt/vimball/plugin/vimballPlugin.vim +++ /dev/null @@ -1,43 +0,0 @@ -" vimballPlugin : construct a file containing both paths and files -" Author: Charles E. Campbell -" Copyright: (c) 2004-2014 by Charles E. Campbell -" The VIM LICENSE applies to Vimball.vim, and Vimball.txt -" (see |copyright|) except use "Vimball" instead of "Vim". -" No warranty, express or implied. -" *** *** Use At-Your-Own-Risk! *** *** -" -" (Rom 2:1 WEB) Therefore you are without excuse, O man, whoever you are who -" judge. For in that which you judge another, you condemn yourself. For -" you who judge practice the same things. -" GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim - -" --------------------------------------------------------------------- -" Load Once: {{{1 -if &cp || exists("g:loaded_vimballPlugin") - finish -endif -let g:loaded_vimballPlugin = "v37" -let s:keepcpo = &cpo -set cpo&vim - -" ------------------------------------------------------------------------------ -" Public Interface: {{{1 -com! -range -complete=file -nargs=+ -bang MkVimball call vimball#MkVimball(<line1>,<line2>,<bang>0,<f-args>) -com! -nargs=? -complete=dir UseVimball call vimball#Vimball(1,<f-args>) -com! -nargs=0 VimballList call vimball#Vimball(0) -com! -nargs=* -complete=dir RmVimball call vimball#SaveSettings()|call vimball#RmVimball(<f-args>)|call vimball#RestoreSettings() -augroup Vimball - au! - au BufEnter *.vba,*.vba.gz,*.vba.bz2,*.vba.zip,*.vba.xz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif - au SourceCmd *.vba.gz,*.vba.bz2,*.vba.zip,*.vba.xz let s:origfile=expand("%")|if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|endif|call vimball#Decompress(expand("<amatch>"))|so %|if s:origfile!=expand("<afile>")|close|endif - au SourceCmd *.vba if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|call vimball#Vimball(1)|close|else|call vimball#Vimball(1)|endif - au BufEnter *.vmb,*.vmb.gz,*.vmb.bz2,*.vmb.zip,*.vmb.xz setlocal bt=nofile fmr=[[[,]]] fdm=marker|if &ff != 'unix'|setlocal ma ff=unix noma|endif|if line('$') > 1|call vimball#ShowMesg(0,"Source this file to extract it! (:so %)")|endif - au SourceCmd *.vmb.gz,*.vmb.bz2,*.vmb.zip,*.vmb.xz let s:origfile=expand("%")|if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|endif|call vimball#Decompress(expand("<amatch>"))|so %|if s:origfile!=expand("<afile>")|close|endif - au SourceCmd *.vmb if expand("%")!=expand("<afile>") | exe "1sp" fnameescape(expand("<afile>"))|call vimball#Vimball(1)|close|else|call vimball#Vimball(1)|endif -augroup END - -" ===================================================================== -" Restoration And Modelines: {{{1 -" vim: fdm=marker -let &cpo= s:keepcpo -unlet s:keepcpo diff --git a/runtime/plugin/health.vim b/runtime/plugin/health.vim deleted file mode 100644 index 66ae8fb239..0000000000 --- a/runtime/plugin/health.vim +++ /dev/null @@ -1 +0,0 @@ -autocmd CmdUndefined CheckHealth checkhealth diff --git a/runtime/plugin/tarPlugin.vim b/runtime/plugin/tarPlugin.vim index d55492a93e..384a3ed823 100644 --- a/runtime/plugin/tarPlugin.vim +++ b/runtime/plugin/tarPlugin.vim @@ -47,7 +47,6 @@ augroup tar au BufReadCmd *.tar.zst call tar#Browse(expand("<amatch>")) au BufReadCmd *.tzs call tar#Browse(expand("<amatch>")) augroup END -com! -nargs=? -complete=file Vimuntar call tar#Vimuntar(<q-args>) " --------------------------------------------------------------------- " Restoration And Modelines: {{{1 |