diff options
Diffstat (limited to 'runtime')
92 files changed, 12178 insertions, 2654 deletions
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index c25f5ee64f..f18801ea69 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -129,6 +129,25 @@ function! s:check_performance() abort endif endfunction +function! s:get_tmux_option(option) abort + let cmd = 'tmux show-option -qvg '.a:option " try global scope + let out = system(cmd) + let val = substitute(out, '\v(\s|\r|\n)', '', 'g') + if v:shell_error + call health#report_error('command failed: '.cmd."\n".out) + return 'error' + elseif empty(val) + let cmd = 'tmux show-option -qvgs '.a:option " try session scope + let out = system(cmd) + let val = substitute(out, '\v(\s|\r|\n)', '', 'g') + if v:shell_error + call health#report_error('command failed: '.cmd."\n".out) + return 'error' + endif + endif + return val +endfunction + function! s:check_tmux() abort if empty($TMUX) || !executable('tmux') return @@ -136,20 +155,31 @@ function! s:check_tmux() abort call health#report_start('tmux') " check escape-time - let suggestions = ["Set escape-time in ~/.tmux.conf:\nset-option -sg escape-time 10", + let suggestions = ["set escape-time in ~/.tmux.conf:\nset-option -sg escape-time 10", \ s:suggest_faq] - let cmd = 'tmux show-option -qvgs escape-time' - let out = system(cmd) - let tmux_esc_time = substitute(out, '\v(\s|\r|\n)', '', 'g') - if v:shell_error - call health#report_error('command failed: '.cmd."\n".out) - elseif empty(tmux_esc_time) - call health#report_error('escape-time is not set', suggestions) - elseif tmux_esc_time > 300 - call health#report_error( - \ 'escape-time ('.tmux_esc_time.') is higher than 300ms', suggestions) - else - call health#report_ok('escape-time: '.tmux_esc_time.'ms') + let tmux_esc_time = s:get_tmux_option('escape-time') + if tmux_esc_time !=# 'error' + if empty(tmux_esc_time) + call health#report_error('`escape-time` is not set', suggestions) + elseif tmux_esc_time > 300 + call health#report_error( + \ '`escape-time` ('.tmux_esc_time.') is higher than 300ms', suggestions) + else + call health#report_ok('escape-time: '.tmux_esc_time) + endif + endif + + " check focus-events + let suggestions = ["(tmux 1.9+ only) Set `focus-events` in ~/.tmux.conf:\nset-option -g focus-events on"] + let tmux_focus_events = s:get_tmux_option('focus-events') + call health#report_info('Checking stuff') + if tmux_focus_events !=# 'error' + if empty(tmux_focus_events) || tmux_focus_events !=# 'on' + call health#report_warn( + \ "`focus-events` is not enabled. |'autoread'| may not work.", suggestions) + else + call health#report_ok('focus-events: '.tmux_focus_events) + endif endif " check default-terminal and $TERM @@ -203,9 +233,9 @@ function! s:check_terminal() abort call health#report_error('command failed: '.cmd."\n".out) else call health#report_info('key_backspace (kbs) terminfo entry: ' - \ .(empty(kbs_entry) ? '? (not found)' : kbs_entry)) + \ .(empty(kbs_entry) ? '? (not found)' : kbs_entry)) call health#report_info('key_dc (kdch1) terminfo entry: ' - \ .(empty(kbs_entry) ? '? (not found)' : kdch1_entry)) + \ .(empty(kbs_entry) ? '? (not found)' : kdch1_entry)) endif for env_var in ['XTERM_VERSION', 'VTE_VERSION', 'TERM_PROGRAM', 'COLORTERM', 'SSH_TTY'] if exists('$'.env_var) diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 87d82150b6..0482cb7f3c 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -38,9 +38,10 @@ endfunction " Handler for s:system() function. function! s:system_handler(jobid, data, event) dict abort if a:event ==# 'stderr' - let self.stderr .= join(a:data, '') - if !self.ignore_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, '') @@ -64,7 +65,7 @@ function! s:system(cmd, ...) abort let stdin = a:0 ? a:1 : '' let ignore_error = a:0 > 2 ? a:3 : 0 let opts = { - \ 'ignore_stderr': a:0 > 1 ? a:2 : 0, + \ 'add_stderr_to_output': a:0 > 1 ? a:2 : 0, \ 'output': '', \ 'stderr': '', \ 'on_stdout': function('s:system_handler'), @@ -89,8 +90,15 @@ function! s:system(cmd, ...) abort call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd))) call jobstop(jobid) elseif s:shell_error != 0 && !ignore_error - call health#report_error(printf("Command error (job=%d, exit code %d): `%s` (in %s)\nOutput: %s\nStderr: %s", - \ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd()), opts.output, opts.stderr)) + 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 @@ -155,7 +163,7 @@ function! s:check_clipboard() abort endif endfunction -" Get the latest Neovim Python client (pynvim) version from PyPI. +" 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') @@ -172,7 +180,7 @@ endfunction " Get version information using the specified interpreter. The interpreter is " used directly in case breaking changes were introduced since the last time -" Neovim's Python client was updated. +" Nvim's Python client was updated. " " Returns: [ " {python executable version}, @@ -194,7 +202,8 @@ function! s:version_info(python) abort let nvim_path = s:trim(s:system([ \ a:python, '-c', - \ 'import sys; sys.path.remove(""); ' . + \ 'import sys; ' . + \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' . \ 'import neovim; print(neovim.__file__)'])) if s:shell_error || empty(nvim_path) return [python_version, 'unable to load neovim Python module', pypi_version, @@ -215,7 +224,7 @@ function! s:version_info(python) abort \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'], \ '', 1, 1) if empty(nvim_version) - let nvim_version = 'unable to find neovim Python module 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) @@ -257,6 +266,22 @@ function! s:check_bin(bin) abort 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(version) abort call health#report_start('Python ' . a:version . ' provider (optional)') @@ -264,11 +289,10 @@ function! s:check_python(version) abort let python_exe = '' let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' let host_prog_var = pyname.'_host_prog' - let loaded_var = 'g:loaded_'.pyname.'_provider' let python_multiple = [] - if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') - call health#report_info('Disabled ('.loaded_var.'='.eval(loaded_var).'). This might be due to some previous error.') + if s:disabled_via_loaded_var(pyname) + return endif let [pyenv, pyenv_root] = s:check_for_pyenv() @@ -286,7 +310,7 @@ function! s:check_python(version) abort let python_exe = pyname endif - " No Python executable could `import neovim`. + " No Python executable could `import neovim`, or host_prog_var was used. if !empty(pythonx_errors) call health#report_error('Python provider error:', pythonx_errors) @@ -339,7 +363,7 @@ function! s:check_python(version) abort \ && !empty(pyenv_root) && resolve(python_exe) !~# '^'.pyenv_root.'/' call health#report_warn('pyenv is not set up optimally.', [ \ printf('Create a virtualenv specifically ' - \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid ' + \ . '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) \ ]) @@ -353,7 +377,7 @@ function! s:check_python(version) abort if resolve(python_exe) !~# '^'.venv_root.'/' call health#report_warn('Your virtualenv is not set up optimally.', [ \ printf('Create a virtualenv specifically ' - \ . 'for Neovim and use `g:%s`. This will avoid ' + \ . 'for Nvim and use `g:%s`. This will avoid ' \ . 'the need to install the pynvim module in each ' \ . 'virtualenv.', host_prog_var) \ ]) @@ -368,18 +392,6 @@ function! s:check_python(version) abort let python_exe = '' endif - " Check if $VIRTUAL_ENV is valid. - if exists('$VIRTUAL_ENV') && !empty(python_exe) - if $VIRTUAL_ENV ==# matchstr(python_exe, '^\V'.$VIRTUAL_ENV) - call health#report_info('$VIRTUAL_ENV matches executable') - else - call health#report_warn( - \ '$VIRTUAL_ENV exists but appears to be inactive. ' - \ . 'This could lead to unexpected results.', - \ [ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654' ]) - endif - endif - " Diagnostic output call health#report_info('Executable: ' . (empty(python_exe) ? 'Not found' : python_exe)) if len(python_multiple) @@ -473,12 +485,83 @@ function! s:check_for_pyenv() abort 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(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/7654' + 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)') - let loaded_var = 'g:loaded_ruby_provider' - if exists(loaded_var) && !exists('*provider#ruby#Call') - call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + if s:disabled_via_loaded_var('ruby') return endif @@ -501,7 +584,7 @@ function! s:check_ruby() abort 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_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, @@ -509,7 +592,7 @@ function! s:check_ruby() abort \ 'Are you behind a firewall or proxy?']) return endif - let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 1, 'not found') + 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) @@ -532,9 +615,7 @@ endfunction function! s:check_node() abort call health#report_start('Node.js provider (optional)') - let loaded_var = 'g:loaded_node_provider' - if exists(loaded_var) && !exists('*provider#node#Call') - call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + if s:disabled_via_loaded_var('node') return endif @@ -546,8 +627,8 @@ function! s:check_node() abort endif let node_v = get(split(s:system('node -v'), "\n"), 0, '') call health#report_info('Node.js: '. node_v) - if !s:shell_error && s:version_cmp(node_v[1:], '6.0.0') < 0 - call health#report_warn('Neovim node.js host does not support '.node_v) + if s:shell_error || s:version_cmp(node_v[1:], '6.0.0') < 0 + call health#report_warn('Nvim node.js host does not support '.node_v) " Skip further checks, they are nonsense if nodejs is too old. return endif @@ -562,7 +643,7 @@ function! s:check_node() abort \ 'Run in shell (if you use yarn): yarn global add neovim']) return endif - call health#report_info('Neovim node.js host: '. host) + call health#report_info('Nvim node.js host: '. host) let manager = executable('npm') ? 'npm' : 'yarn' let latest_npm_cmd = has('win32') ? @@ -575,14 +656,12 @@ function! s:check_node() abort \ 'Are you behind a firewall or proxy?']) return endif - if !empty(latest_npm) - 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') - 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) @@ -603,10 +682,83 @@ function! s:check_node() abort endif endfunction +function! s:check_perl() abort + call health#report_start('Perl provider (optional)') + + if s:disabled_via_loaded_var('perl') + return + endif + + if !executable('perl') || !executable('cpanm') + call health#report_warn( + \ '`perl` and `cpanm` must be in $PATH.', + \ ['Install Perl and cpanminus and verify that `perl` and `cpanm` commands work.']) + return + endif + let perl_v = get(split(s:system(['perl', '-W', '-e', 'print $^V']), "\n"), 0, '') + call health#report_info('Perl: '. perl_v) + if s:shell_error + call health#report_warn('Nvim perl host does not support '.perl_v) + " Skip further checks, they are nonsense if perl is too old. + return + endif + + let host = provider#perl#Detect() + if empty(host) + call health#report_warn('Missing "Neovim::Ext" cpan module.', + \ ['Run in shell: cpanm Neovim::Ext']) + return + endif + call health#report_info('Nvim perl host: '. host) + + let latest_cpan_cmd = 'cpanm --info -q Neovim::Ext' + let latest_cpan = s:system(latest_cpan_cmd) + if s:shell_error || empty(latest_cpan) + call health#report_error('Failed to run: '. 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 = [host, '-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: '. string(current_cpan_cmd), + \ ['Report this issue with the output of: ', string(current_cpan_cmd)]) + return + endif + + if s:version_cmp(current_cpan, latest_cpan) == -1 + call health#report_warn( + \ printf('Module "Neovim::Ext" is out-of-date. Installed: %s, latest: %s', + \ current_cpan, latest_cpan), + \ ['Run in shell: cpanm 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(2) call s:check_python(3) + call s:check_virtualenv() call s:check_ruby() call s:check_node() + call s:check_perl() endfunction diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 153f1afed8..dab88fde23 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> if exists('s:loaded_man') finish @@ -7,22 +7,10 @@ let s:loaded_man = 1 let s:find_arg = '-w' let s:localfile_arg = v:true " Always use -l if possible. #6683 -let s:section_arg = '-s' +let s:section_arg = '-S' -function! s:init_section_flag() - call system(['env', 'MANPAGER=cat', 'man', s:section_arg, '1', 'man']) - if v:shell_error - let s:section_arg = '-S' - endif -endfunction - -function! s:init() abort - call s:init_section_flag() - " TODO(nhooyr): Does `man -l` on SunOS list searched directories? +function! man#init() abort try - if !has('win32') && $OSTYPE !~? 'cygwin\|linux' && system('uname -s') =~? 'SunOS' && system('uname -r') =~# '^5' - let s:find_arg = '-l' - endif " Check for -l support. call s:get_page(s:get_path('', 'man')) catch /E145:/ @@ -52,51 +40,40 @@ function! man#open_page(count, count1, mods, ...) abort let ref = a:2.'('.a:1.')' endif try - let [sect, name] = man#extract_sect_and_name_ref(ref) + let [sect, name] = s:extract_sect_and_name_ref(ref) if a:count ==# a:count1 " v:count defaults to 0 which is a valid section, and v:count1 defaults to " 1, also a valid section. If they are equal, count explicitly set. let sect = string(a:count) endif - let [sect, name, path] = s:verify_exists(sect, name) + let path = s:verify_exists(sect, name) + let [sect, name] = s:extract_sect_and_name_path(path) catch call s:error(v:exception) return endtry - call s:push_tag() - let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')') - + let [l:buf, l:save_tfu] = [bufnr(), &tagfunc] try - set eventignore+=BufReadCmd + set tagfunc=man#goto_tag + let l:target = l:name . '(' . l:sect . ')' if a:mods !~# 'tab' && s:find_man() - execute 'silent keepalt edit' fnameescape(bufname) + execute 'silent keepalt tag' l:target else - execute 'silent keepalt' a:mods 'split' fnameescape(bufname) + execute 'silent keepalt' a:mods 'stag' l:target endif finally - set eventignore-=BufReadCmd - endtry - - try - let page = s:get_page(path) - catch - if a:mods =~# 'tab' || !s:find_man() - " a new window was opened - close - endif - call s:error(v:exception) - return + call setbufvar(l:buf, '&tagfunc', l:save_tfu) endtry let b:man_sect = sect - call s:put_page(page) endfunction function! man#read_page(ref) abort try - let [sect, name] = man#extract_sect_and_name_ref(a:ref) - let [sect, name, path] = s:verify_exists(sect, name) + let [sect, name] = s:extract_sect_and_name_ref(a:ref) + let path = s:verify_exists(sect, name) + let [sect, name] = s:extract_sect_and_name_path(path) let page = s:get_page(path) catch call s:error(v:exception) @@ -152,7 +129,7 @@ function! s:get_page(path) abort " Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065). " Soft-wrap: ftplugin/man.vim sets wrap/breakindent/…. " Hard-wrap: driven by `man`. - let manwidth = !get(g:,'man_hardwrap') ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH) + let manwidth = !get(g:, 'man_hardwrap', 1) ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH) " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). " http://comments.gmane.org/gmane.editors.vim.devel/29085 " Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. @@ -163,6 +140,9 @@ endfunction function! s:put_page(page) abort setlocal modifiable setlocal noreadonly + setlocal noswapfile + " git-ls-files(1) is all one keyword/tag-target + setlocal iskeyword+=(,) silent keepjumps %delete _ silent put =a:page while getline(1) =~# '^\s*$' @@ -204,7 +184,7 @@ endfunction " attempt to extract the name and sect out of 'name(sect)' " otherwise just return the largest string of valid characters in ref -function! man#extract_sect_and_name_ref(ref) abort +function! s:extract_sect_and_name_ref(ref) abort if a:ref[0] ==# '-' " try ':Man -pandoc' with this disabled. throw 'manpage name cannot start with ''-''' endif @@ -214,7 +194,7 @@ function! man#extract_sect_and_name_ref(ref) abort if empty(name) throw 'manpage reference cannot contain only parentheses' endif - return [get(b:, 'man_default_sects', ''), name] + return ['', name] endif let left = split(ref, '(') " see ':Man 3X curses' on why tolower. @@ -237,42 +217,62 @@ function! s:get_path(sect, name) abort return substitute(get(split(s:system(['man', s:find_arg, s:section_arg, a:sect, a:name])), 0, ''), '\n\+$', '', '') endfunction +" s:verify_exists attempts to find the path to a manpage +" based on the passed section and name. +" +" 1. If the passed section is empty, b:man_default_sects is used. +" 2. If manpage could not be found with the given sect and name, +" then another attempt is made with b:man_default_sects. +" 3. If it still could not be found, then we try again without a section. +" 4. If still not found but $MANSECT is set, then we try again with $MANSECT +" unset. +" +" This function is careful to avoid duplicating a search if a previous +" step has already done it. i.e if we use b:man_default_sects in step 1, +" then we don't do it again in step 2. function! s:verify_exists(sect, name) abort + let sect = a:sect + if empty(sect) + let sect = get(b:, 'man_default_sects', '') + endif + try - let path = s:get_path(a:sect, a:name) + return s:get_path(sect, a:name) catch /^command error (/ + endtry + + if !empty(get(b:, 'man_default_sects', '')) && sect !=# b:man_default_sects try - let path = s:get_path(get(b:, 'man_default_sects', ''), a:name) + return s:get_path(b:man_default_sects, a:name) catch /^command error (/ - let path = s:get_path('', a:name) endtry - endtry - " Extract the section from the path, because sometimes the actual section is - " more specific than what we provided to `man` (try `:Man 3 App::CLI`). - " Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we - " still want the name of the buffer to be 'printf'. - return s:extract_sect_and_name_path(path) + [path] -endfunction - -let s:tag_stack = [] + endif -function! s:push_tag() abort - let s:tag_stack += [{ - \ 'buf': bufnr('%'), - \ 'lnum': line('.'), - \ 'col': col('.'), - \ }] -endfunction + if !empty(sect) + try + return s:get_path('', a:name) + catch /^command error (/ + endtry + endif -function! man#pop_tag() abort - if !empty(s:tag_stack) - let tag = remove(s:tag_stack, -1) - execute 'silent' tag['buf'].'buffer' - call cursor(tag['lnum'], tag['col']) + if !empty($MANSECT) + try + let MANSECT = $MANSECT + unset $MANSECT + return s:get_path('', a:name) + catch /^command error (/ + finally + let $MANSECT = MANSECT + endtry endif + + throw 'no manual entry for ' . a:name endfunction -" extracts the name and sect out of 'path/name.sect' +" Extracts the name/section from the 'path/name.sect', because sometimes the actual section is +" more specific than what we provided to `man` (try `:Man 3 App::CLI`). +" Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we +" still want the name of the buffer to be 'printf'. function! s:extract_sect_and_name_path(path) abort let tail = fnamemodify(a:path, ':t') if a:path =~# '\.\%([glx]z\|bz2\|lzma\|Z\)$' " valid extensions @@ -284,20 +284,16 @@ function! s:extract_sect_and_name_path(path) abort endfunction function! s:find_man() abort - if &filetype ==# 'man' - return 1 - elseif winnr('$') ==# 1 - return 0 - endif - let thiswin = winnr() - while 1 - wincmd w - if &filetype ==# 'man' + let l:win = 1 + while l:win <= winnr('$') + let l:buf = winbufnr(l:win) + if getbufvar(l:buf, '&filetype', '') ==# 'man' + execute l:win.'wincmd w' return 1 - elseif thiswin ==# winnr() - return 0 endif + let l:win += 1 endwhile + return 0 endfunction function! s:error(msg) abort @@ -307,7 +303,7 @@ function! s:error(msg) abort echohl None endfunction -" see man#extract_sect_and_name_ref on why tolower(sect) +" see s:extract_sect_and_name_ref on why tolower(sect) function! man#complete(arg_lead, cmd_line, cursor_pos) abort let args = split(a:cmd_line) let cmd_offset = index(args, 'Man') @@ -360,14 +356,35 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort return s:complete(sect, sect, name) endfunction -function! s:complete(sect, psect, name) abort +function! s:get_paths(sect, name, do_fallback) abort + " callers must try-catch this, as some `man` implementations don't support `s:find_arg` try let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',') + let paths = globpath(mandirs, 'man?/'.a:name.'*.'.a:sect.'*', 0, 1) + try + " Prioritize the result from verify_exists as it obeys b:man_default_sects. + let first = s:verify_exists(a:sect, a:name) + let paths = filter(paths, 'v:val !=# first') + let paths = [first] + paths + catch + endtry + return paths catch - call s:error(v:exception) - return + if !a:do_fallback + throw v:exception + endif + + " Fallback to a single path, with the page we're trying to find. + try + return [s:verify_exists(a:sect, a:name)] + catch + return [] + endtry endtry - let pages = globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) +endfunction + +function! s:complete(sect, psect, name) abort + let pages = s:get_paths(a:sect, a:name, v:false) " We remove duplicates in case the same manpage in different languages was found. return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i')) endfunction @@ -387,6 +404,10 @@ function! s:format_candidate(path, psect) abort endfunction function! man#init_pager() abort + " https://github.com/neovim/neovim/issues/6828 + let og_modifiable = &modifiable + setlocal modifiable + if getline(1) =~# '^\s*$' silent keepjumps 1delete _ else @@ -397,13 +418,40 @@ function! man#init_pager() abort " know the correct casing, cf. `man glDrawArraysInstanced`). let ref = substitute(matchstr(getline(1), '^[^)]\+)'), ' ', '_', 'g') try - let b:man_sect = man#extract_sect_and_name_ref(ref)[0] + let b:man_sect = s:extract_sect_and_name_ref(ref)[0] catch let b:man_sect = '' endtry if -1 == match(bufname('%'), 'man:\/\/') " Avoid duplicate buffers, E95. execute 'silent file man://'.tolower(fnameescape(ref)) endif + + let &l:modifiable = og_modifiable +endfunction + +function! man#goto_tag(pattern, flags, info) abort + let [l:sect, l:name] = s:extract_sect_and_name_ref(a:pattern) + + let l:paths = s:get_paths(l:sect, l:name, v:true) + let l:structured = [] + + for l:path in l:paths + let l:n = s:extract_sect_and_name_path(l:path)[1] + let l:structured += [{ 'name': l:n, 'path': l:path }] + endfor + + if &cscopetag + " return only a single entry so we work well with :cstag (#11675) + let l:structured = l:structured[:0] + endif + + return map(l:structured, { + \ _, entry -> { + \ 'name': entry.name, + \ 'filename': 'man://' . entry.path, + \ 'cmd': '1' + \ } + \ }) endfunction -call s:init() +call man#init() diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index a5b47e06d5..b69ad7187a 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -688,10 +688,6 @@ fun! netrw#Explore(indx,dosplit,style,...) endif " save registers - if has("clipboard") - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif sil! let keepregslash= @/ " if dosplit @@ -915,10 +911,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " call Decho("..case Nexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("<slnum>")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore") return @@ -940,10 +932,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " call Decho("case Pexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("<slnum>")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore") return @@ -995,10 +983,6 @@ fun! netrw#Explore(indx,dosplit,style,...) catch /^Vim\%((\a\+)\)\=:E480/ keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45) if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : no files matched pattern") return @@ -1031,10 +1015,6 @@ fun! netrw#Explore(indx,dosplit,style,...) if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/') keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : no files matched") return @@ -1079,10 +1059,6 @@ fun! netrw#Explore(indx,dosplit,style,...) if !exists("g:netrw_quiet") keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44) endif - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : missing +path_extra") return @@ -1152,10 +1128,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " there's no danger of a late FocusGained event on initialization. " Consequently, set s:netrw_events to 2. let s:netrw_events= 2 - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : @/<".@/.">") endfun @@ -1676,10 +1648,6 @@ fun! s:NetrwOptionsSave(vt) if g:netrw_keepdir let {a:vt}netrw_dirkeep = getcwd() endif - if has("clipboard") - sil! let {a:vt}netrw_starkeep = @* - sil! let {a:vt}netrw_pluskeep = @+ - endif sil! let {a:vt}netrw_slashkeep= @/ " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) @@ -1828,10 +1796,6 @@ fun! s:NetrwOptionsRestore(vt) unlet {a:vt}netrw_dirkeep endif endif - if has("clipboard") - call s:NetrwRestoreSetting(a:vt."netrw_starkeep","@*") - call s:NetrwRestoreSetting(a:vt."netrw_pluskeep","@+") - endif call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/") " call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("<slnum>")) @@ -5496,6 +5460,11 @@ fun! netrw#CheckIfRemote(...) else let curfile= expand("%") endif + + " Ignore terminal buffers + if &buftype ==# 'terminal' + return 0 + endif " call Decho("curfile<".curfile.">") if curfile =~ '^\a\{3,}://' " call Dret("netrw#CheckIfRemote 1") @@ -9559,10 +9528,6 @@ fun! s:NetrwWideListing() let newcolstart = w:netrw_bannercnt + fpc let newcolend = newcolstart + fpc - 1 " call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>")) - if has("clipboard") - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif while line("$") >= newcolstart if newcolend > line("$") | let newcolend= line("$") | endif let newcolqty= newcolend - newcolstart @@ -9575,10 +9540,6 @@ fun! s:NetrwWideListing() exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _' exe 'sil! NetrwKeepj '.w:netrw_bannercnt endwhile - if has("clipboard") - sil! let @*= keepregstar - sil! let @+= keepregplus - endif exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e' NetrwKeepj call histdel("/",-1) exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>" diff --git a/runtime/autoload/provider.vim b/runtime/autoload/provider.vim index dc24e801d0..803c1a0b1c 100644 --- a/runtime/autoload/provider.vim +++ b/runtime/autoload/provider.vim @@ -3,8 +3,11 @@ " Start the provider and perform a 'poll' request " " Returns a valid channel on success -function! provider#Poll(argv, orig_name, log_env) abort +function! provider#Poll(argv, orig_name, log_env, ...) abort let job = {'rpc': v:true, 'stderr_buffered': v:true} + if a:0 + let job = extend(job, a:1) + endif try let channel_id = jobstart(a:argv, job) if channel_id > 0 && rpcrequest(channel_id, 'poll') ==# 'ok' diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index e33dc31f6d..a96a0a61b7 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -113,8 +113,13 @@ function! provider#clipboard#Executable() abort let s:paste['*'] = s:paste['+'] return 'doitclient' elseif executable('win32yank.exe') - let s:copy['+'] = 'win32yank.exe -i --crlf' - let s:paste['+'] = 'win32yank.exe -o --lf' + if has('wsl') && getftype(exepath('win32yank.exe')) == 'link' + let win32yank = resolve(exepath('win32yank.exe')) + else + let win32yank = 'win32yank.exe' + endif + let s:copy['+'] = win32yank.' -i --crlf' + let s:paste['+'] = win32yank.' -o --lf' let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'win32yank' @@ -172,6 +177,10 @@ function! s:clipboard.set(lines, regtype, reg) abort if jobid > 0 call jobsend(jobid, a:lines) call jobclose(jobid, 'stdin') + " xclip does not close stdout when receiving input via stdin + if argv[0] ==# 'xclip' + call jobclose(jobid, 'stdout') + endif let selection.owner = jobid let ret = 1 else diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index b2a3b3ee08..c5d5e87729 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -51,6 +51,9 @@ function! provider#node#Detect() abort if exists('g:node_host_prog') return expand(g:node_host_prog) endif + if !executable('node') + return '' + endif if !s:is_minimum_version(v:null, 6, 0) return '' endif diff --git a/runtime/autoload/provider/perl.vim b/runtime/autoload/provider/perl.vim new file mode 100644 index 0000000000..36ca2bbf14 --- /dev/null +++ b/runtime/autoload/provider/perl.vim @@ -0,0 +1,69 @@ +if exists('s:loaded_perl_provider') + finish +endif + +let s:loaded_perl_provider = 1 + +function! provider#perl#Detect() abort + " use g:perl_host_prof if set or check if perl is on the path + let prog = exepath(get(g:, 'perl_host_prog', 'perl')) + if empty(prog) + return '' + endif + + " if perl is available, make sure the required module is available + call system([prog, '-W', '-MNeovim::Ext', '-e', '']) + return v:shell_error ? '' : prog +endfunction + +function! provider#perl#Prog() abort + return s:prog +endfunction + +function! provider#perl#Require(host) abort + if s:err != '' + echoerr s:err + return + endif + + let prog = provider#perl#Prog() + let args = [s:prog, '-e', 'use Neovim::Ext; start_host();'] + + " Collect registered perl plugins into args + let perl_plugins = remote#host#PluginsForHost(a:host.name) + for plugin in perl_plugins + call add(args, plugin.path) + endfor + + return provider#Poll(args, a:host.orig_name, '$NVIM_PERL_LOG_FILE') +endfunction + +function! provider#perl#Call(method, args) abort + if s:err != '' + echoerr s:err + return + endif + + if !exists('s:host') + try + let s:host = remote#host#Require('perl') + catch + let s:err = v:exception + echohl WarningMsg + echomsg v:exception + echohl None + return + endtry + endif + return call('rpcrequest', insert(insert(a:args, 'perl_'.a:method), s:host)) +endfunction + +let s:err = '' +let s:prog = provider#perl#Detect() +let g:loaded_perl_provider = empty(s:prog) ? 1 : 2 + +if g:loaded_perl_provider != 2 + let s:err = 'Cannot find perl or the required perl module' +endif + +call remote#host#RegisterPlugin('perl-provider', 'perl', []) diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 59b1c27b72..e89d519790 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -10,7 +10,8 @@ function! provider#pythonx#Require(host) abort " Python host arguments let prog = (ver == '2' ? provider#python#Prog() : provider#python3#Prog()) - let args = [prog, '-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()'] + let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()'] + " Collect registered Python plugins into args let python_plugins = remote#host#PluginsForHost(a:host.name) @@ -18,7 +19,7 @@ function! provider#pythonx#Require(host) abort call add(args, plugin.path) endfor - return provider#Poll(args, a:host.orig_name, '$NVIM_PYTHON_LOG_FILE') + return provider#Poll(args, a:host.orig_name, '$NVIM_PYTHON_LOG_FILE', {'overlapped': v:true}) endfunction function! s:get_python_executable_from_host_var(major_version) abort @@ -28,8 +29,8 @@ endfunction function! s:get_python_candidates(major_version) abort return { \ 2: ['python2', 'python2.7', 'python2.6', 'python'], - \ 3: ['python3', 'python3.7', 'python3.6', 'python3.5', 'python3.4', 'python3.3', - \ 'python'] + \ 3: ['python3', 'python3.9', 'python3.8', 'python3.7', 'python3.6', 'python3.5', + \ 'python3.4', 'python3.3', 'python'] \ }[a:major_version] endfunction @@ -43,7 +44,7 @@ function! provider#pythonx#DetectByModule(module, major_version) abort let python_exe = s:get_python_executable_from_host_var(a:major_version) if !empty(python_exe) - return [python_exe, ''] + return [exepath(expand(python_exe)), ''] endif let candidates = s:get_python_candidates(a:major_version) @@ -66,7 +67,7 @@ endfunction function! s:import_module(prog, module) abort let prog_version = system([a:prog, '-c' , printf( \ 'import sys; ' . - \ 'sys.path.remove(""); ' . + \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' . \ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' . \ 'import pkgutil; ' . \ 'exit(2*int(pkgutil.get_loader("%s") is None))', diff --git a/runtime/autoload/remote/define.vim b/runtime/autoload/remote/define.vim index 2688a62a82..2aec96e365 100644 --- a/runtime/autoload/remote/define.vim +++ b/runtime/autoload/remote/define.vim @@ -24,7 +24,7 @@ function! remote#define#CommandOnHost(host, method, sync, name, opts) endif if has_key(a:opts, 'nargs') - call add(forward_args, ' <args>') + call add(forward_args, ' " . <q-args> . "') endif exe s:GetCommandPrefix(a:name, a:opts) diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index 1cf328e08d..c34ff4bee7 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -203,3 +203,7 @@ call remote#host#Register('ruby', '*.rb', " nodejs call remote#host#Register('node', '*', \ function('provider#node#Require')) + +" perl +call remote#host#Register('perl', '*', + \ function('provider#perl#Require')) diff --git a/runtime/autoload/spellfile.vim b/runtime/autoload/spellfile.vim index c0ef51cdfe..d098902305 100644 --- a/runtime/autoload/spellfile.vim +++ b/runtime/autoload/spellfile.vim @@ -13,6 +13,13 @@ let s:spellfile_URL = '' " Start with nothing so that s:donedict is reset. " This function is used for the spellfile plugin. function! spellfile#LoadFile(lang) + " Check for sandbox/modeline. #11359 + try + :! + catch /\<E12\>/ + throw 'Cannot download spellfile in sandbox/modeline. Try ":set spell" from the cmdline.' + endtry + " If the netrw plugin isn't loaded we silently skip everything. if !exists(":Nread") if &verbose diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 98dd330b48..ea3a8242ae 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -19,6 +19,7 @@ API Usage *api-rpc* *RPC* *rpc* *msgpack-rpc* RPC is the typical way to control Nvim programmatically. Nvim implements the MessagePack-RPC protocol: + https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md https://github.com/msgpack/msgpack/blob/0b8f5ac/spec.md Many clients use the API: user interfaces (GUIs), remote plugins, scripts like @@ -182,21 +183,17 @@ External programs (clients) can use the metadata to discover the API, using any of these approaches: 1. Connect to a running Nvim instance and call |nvim_get_api_info()| via - msgpack-rpc. This is best for clients written in dynamic languages which + msgpack-RPC. This is best for clients written in dynamic languages which can define functions at runtime. - 2. Start Nvim with the |--api-info| option. Useful for clients written in - statically-compiled languages. - - 3. Use the |api_info()| Vimscript function. - -Example: To get a human-readable list of API functions: > - :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val.name') -< -Example: To get a formatted dump of the API using python (requires the -"pyyaml" and "msgpack-python" modules): > - nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' + 2. Start Nvim with |--api-info|. Useful for statically-compiled clients. + Example (requires Python "pyyaml" and "msgpack-python" modules): > + nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' < + 3. Use the |api_info()| Vimscript function. > + :lua print(vim.inspect(vim.fn.api_info())) +< Example using |filter()| to exclude non-deprecated API functions: > + :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val.name') ============================================================================== API contract *api-contract* @@ -439,141 +436,170 @@ Example: create a float with scratch buffer: > > ============================================================================== +Extended marks *api-extended-marks* + +Extended marks (extmarks) represent buffer annotations that track text changes +in the buffer. They could be used to represent cursors, folds, misspelled +words, and anything else that needs to track a logical location in the buffer +over time. + +Example: + +We will set an extmark at the first row and third column. |api-indexing| is +zero-indexed, so we use row=0 and column=2. Passing id=0 creates a new mark +and returns the id: > + + let g:mark_ns = nvim_create_namespace('myplugin') + let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2, {}) + +We can get a mark by its id: > + + echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id) + => [0, 2] + +We can get all marks in a buffer for our namespace (or by a range): > + + echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, {}) + => [[1, 0, 2]] + +Deleting all text surrounding an extmark does not remove the extmark. To +remove an extmark use |nvim_buf_del_extmark()|. + +Namespaces allow your plugin to manage only its own extmarks, ignoring those +created by another plugin. + +Extmark positions changed by an edit will be restored on undo/redo. Creating +and deleting extmarks is not a buffer change, thus new undo states are not +created for extmark changes. + +============================================================================== Global Functions *api-global* -nvim_command({command}) *nvim_command()* - Executes an ex-command. +nvim__get_lib_dir() *nvim__get_lib_dir()* + TODO: Documentation - On execution error: fails with VimL error, does not update - v:errmsg. +nvim__id({obj}) *nvim__id()* + Returns object given as argument. + + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {command} Ex-command string + {obj} Object to return. -nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()* - Gets a highlight definition by name. + Return: ~ + its argument. + +nvim__id_array({arr}) *nvim__id_array()* + Returns array given as argument. + + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {name} Highlight group name - {rgb} Export RGB colors + {arr} Array to return. Return: ~ - Highlight definition map + its argument. - See also: ~ - nvim_get_hl_by_id +nvim__id_dictionary({dct}) *nvim__id_dictionary()* + Returns dictionary given as argument. -nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()* - Gets a highlight definition by id. |hlID()| + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {hl_id} Highlight id as returned by |hlID()| - {rgb} Export RGB colors + {dct} Dictionary to return. Return: ~ - Highlight definition map - - See also: ~ - nvim_get_hl_by_name + its argument. -nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* - Sends input-keys to Nvim, subject to various quirks controlled - by `mode` flags. This is a blocking call, unlike - |nvim_input()|. +nvim__id_float({flt}) *nvim__id_float()* + Returns floating-point value given as argument. - On execution error: does not fail, but updates v:errmsg. + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {keys} to be typed - {mode} behavior flags, see |feedkeys()| - {escape_csi} If true, escape K_SPECIAL/CSI bytes in - `keys` + {flt} Value to return. - See also: ~ - feedkeys() - vim_strsave_escape_csi + Return: ~ + its argument. -nvim_input({keys}) *nvim_input()* - Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a - low-level input buffer and the call is non-blocking (input is - processed asynchronously by the eventloop). +nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* + TODO: Documentation - On execution error: does not fail, but updates v:errmsg. + *nvim__put_attr()* +nvim__put_attr({id}, {start_row}, {start_col}, {end_row}, {end_col}) + Set attrs in nvim__buf_set_lua_hl callbacks - Note: - |keycodes| like <CR> are translated, so "<" is special. To - input a literal "<", send <LT>. - Note: - For mouse events use |nvim_input_mouse()|. The pseudokey - form "<LeftMouse><col,row>" is deprecated since - |api-level| 6. + TODO(bfredl): This is rather pedestrian. The final interface + should probably be derived from a reformed bufhl/virttext + interface with full support for multi-line ranges etc - Attributes: ~ - {fast} +nvim__stats() *nvim__stats()* + Gets internal stats. + + Return: ~ + Map of various internal stats. + +nvim_call_atomic({calls}) *nvim_call_atomic()* + Calls many API methods atomically. + + This has two main usages: + 1. To perform several requests from an async context + atomically, i.e. without interleaving redraws, RPC requests + from other clients, or user interactions (however API + methods may trigger autocommands or event processing which + have such side-effects, e.g. |:sleep| may wake timers). + 2. To minimize RPC overhead (roundtrips) of a sequence of many + requests. Parameters: ~ - {keys} to be typed + {calls} an array of calls, where each call is described + by an array with two elements: the request name, + and an array of arguments. Return: ~ - Number of bytes actually written (can be fewer than - requested if the buffer becomes full). + Array of two elements. The first is an array of return + values. The second is NIL if all calls succeeded. If a + call resulted in an error, it is a three-element array + with the zero-based index of the call which resulted in an + error, the error type and the error message. If an error + occurred, the values from all preceding calls will still + be returned. - *nvim_input_mouse()* -nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col}) - Send mouse event from GUI. + *nvim_call_dict_function()* +nvim_call_dict_function({dict}, {fn}, {args}) + Calls a VimL |Dictionary-function| with the given arguments. - Non-blocking: does not wait on any result, but queues the - event to be processed soon by the event loop. + On execution error: fails with VimL error, does not update + v:errmsg. - Note: - Currently this doesn't support "scripting" multiple mouse - events by calling it multiple times in a loop: the - intermediate mouse positions will be ignored. It should be - used to implement real-time mouse input in a GUI. The - deprecated pseudokey form ("<LeftMouse><col,row>") of - |nvim_input()| has the same limitiation. + Parameters: ~ + {dict} Dictionary, or String evaluating to a VimL |self| + dict + {fn} Name of the function defined on the VimL dict + {args} Function arguments packed in an Array - Attributes: ~ - {fast} + Return: ~ + Result of the function call - Parameters: ~ - {button} Mouse button: one of "left", "right", - "middle", "wheel". - {action} For ordinary buttons, one of "press", "drag", - "release". For the wheel, one of "up", "down", - "left", "right". - {modifier} String of modifiers each represented by a - single char. The same specifiers are used as - for a key press, except that the "-" separator - is optional, so "C-A-", "c-a" and "CA" can all - be used to specify Ctrl+Alt+click. - {grid} Grid number if the client uses |ui-multigrid|, - else 0. - {row} Mouse row-position (zero-based, like redraw - events) - {col} Mouse column-position (zero-based, like redraw - events) +nvim_call_function({fn}, {args}) *nvim_call_function()* + Calls a VimL function with the given arguments. - *nvim_replace_termcodes()* -nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) - Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a - string with the internal representation. + On execution error: fails with VimL error, does not update + v:errmsg. Parameters: ~ - {str} String to be converted. - {from_part} Legacy Vim parameter. Usually true. - {do_lt} Also translate <lt>. Ignored if `special` is - false. - {special} Replace |keycodes|, e.g. <CR> becomes a "\n" - char. + {fn} Function to call + {args} Function arguments packed in an Array - See also: ~ - replace_termcodes - cpoptions + Return: ~ + Result of the function call -nvim_command_output({command}) *nvim_command_output()* - Executes an ex-command and returns its (non-error) output. - Shell |:!| output is not captured. +nvim_command({command}) *nvim_command()* + Executes an ex-command. On execution error: fails with VimL error, does not update v:errmsg. @@ -581,9 +607,79 @@ nvim_command_output({command}) *nvim_command_output()* Parameters: ~ {command} Ex-command string + See also: ~ + |nvim_exec()| + +nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* + Creates a new, empty, unnamed buffer. + + Parameters: ~ + {listed} Sets 'buflisted' + {scratch} Creates a "throwaway" |scratch-buffer| for + temporary work (always 'nomodified'). Also sets + 'nomodeline' on the buffer. + + Return: ~ + Buffer handle, or 0 on error + + See also: ~ + buf_open_scratch + +nvim_create_namespace({name}) *nvim_create_namespace()* + Creates a new namespace, or gets an existing one. + + Namespaces are used for buffer highlights and virtual text, + see |nvim_buf_add_highlight()| and + |nvim_buf_set_virtual_text()|. + + Namespaces can be named or anonymous. If `name` matches an + existing namespace, the associated id is returned. If `name` + is an empty string a new, anonymous namespace is created. + + Parameters: ~ + {name} Namespace name or empty string + + Return: ~ + Namespace id + +nvim_del_current_line() *nvim_del_current_line()* + Deletes the current line. + +nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()* + Unmaps a global |mapping| for the given mode. + + To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|. + + See also: ~ + |nvim_set_keymap()| + +nvim_del_var({name}) *nvim_del_var()* + Removes a global (g:) variable. + + Parameters: ~ + {name} Variable name + +nvim_err_write({str}) *nvim_err_write()* + Writes a message to the Vim error buffer. Does not append + "\n", the message is buffered (won't display) until a linefeed + is written. + + Parameters: ~ + {str} Message + +nvim_err_writeln({str}) *nvim_err_writeln()* + Writes a message to the Vim error buffer. Appends "\n", so the + buffer is flushed (and displayed). + + Parameters: ~ + {str} Message + + See also: ~ + nvim_err_write() + nvim_eval({expr}) *nvim_eval()* - Evaluates a VimL expression (:help expression). Dictionaries - and Lists are recursively expanded. + Evaluates a VimL |expression|. Dictionaries and Lists are + recursively expanded. On execution error: fails with VimL error, does not update v:errmsg. @@ -594,7 +690,30 @@ nvim_eval({expr}) *nvim_eval()* Return: ~ Evaluation result or expanded object -nvim_execute_lua({code}, {args}) *nvim_execute_lua()* +nvim_exec({src}, {output}) *nvim_exec()* + Executes Vimscript (multiline block of Ex-commands), like + anonymous |:source|. + + Unlike |nvim_command()| this function supports heredocs, + script-scope (s:), etc. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {src} Vimscript code + {output} Capture and return all (non-error, non-shell + |:!|) output + + Return: ~ + Output (non-error, non-shell |:!|) if `output` is true, + else empty string. + + See also: ~ + |execute()| + |nvim_command()| + +nvim_exec_lua({code}, {args}) *nvim_exec_lua()* Execute Lua code. Parameters (if any) are available as `...` inside the chunk. The chunk can return a value. @@ -608,55 +727,124 @@ nvim_execute_lua({code}, {args}) *nvim_execute_lua()* Return: ~ Return value of Lua code if present or NIL. -nvim_call_function({fn}, {args}) *nvim_call_function()* - Calls a VimL function with the given arguments. +nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* + Sends input-keys to Nvim, subject to various quirks controlled + by `mode` flags. This is a blocking call, unlike + |nvim_input()|. - On execution error: fails with VimL error, does not update - v:errmsg. + On execution error: does not fail, but updates v:errmsg. + + If you need to input sequences like <C-o> use nvim_replace_termcodes + to replace the termcodes and then pass the resulting string to + nvim_feedkeys. You'll also want to enable escape_csi. + + Example: > + :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true) + :call nvim_feedkeys(key, 'n', v:true) +< Parameters: ~ - {fn} Function to call - {args} Function arguments packed in an Array + {keys} to be typed + {mode} behavior flags, see |feedkeys()| + {escape_csi} If true, escape K_SPECIAL/CSI bytes in + `keys` + + See also: ~ + feedkeys() + vim_strsave_escape_csi + +nvim_get_api_info() *nvim_get_api_info()* + Returns a 2-tuple (Array), where item 0 is the current channel + id and item 1 is the |api-metadata| map (Dictionary). Return: ~ - Result of the function call + 2-tuple [{channel-id}, {api-metadata}] -nvim_call_dict_function({dict}, {fn}, {args}) *nvim_call_dict_function()* - Calls a VimL |Dictionary-function| with the given arguments. + Attributes: ~ + {fast} - On execution error: fails with VimL error, does not update - v:errmsg. +nvim_get_chan_info({chan}) *nvim_get_chan_info()* + Get information about a channel. + + Return: ~ + Dictionary describing a channel, with these keys: + • "stream" the stream underlying the channel + • "stdio" stdin and stdout of this Nvim instance + • "stderr" stderr of this Nvim instance + • "socket" TCP/IP socket or named pipe + • "job" job with communication over its stdio + + • "mode" how data received on the channel is interpreted + • "bytes" send and receive raw bytes + • "terminal" a |terminal| instance interprets ASCII + sequences + • "rpc" |RPC| communication on the channel is active + + • "pty" Name of pseudoterminal, if one is used (optional). + On a POSIX system, this will be a device path like + /dev/pts/1. Even if the name is unknown, the key will + still be present to indicate a pty is used. This is + currently the case when using winpty on windows. + • "buffer" buffer with connected |terminal| instance + (optional) + • "client" information about the client on the other end + of the RPC channel, if it has added it using + |nvim_set_client_info()|. (optional) + +nvim_get_color_by_name({name}) *nvim_get_color_by_name()* + Returns the 24-bit RGB value of a |nvim_get_color_map()| color + name or "#rrggbb" hexadecimal string. + + Example: > + :echo nvim_get_color_by_name("Pink") + :echo nvim_get_color_by_name("#cbcbcb") +< Parameters: ~ - {dict} Dictionary, or String evaluating to a VimL |self| - dict - {fn} Name of the function defined on the VimL dict - {args} Function arguments packed in an Array + {name} Color name or "#rrggbb" string Return: ~ - Result of the function call + 24-bit RGB value, or -1 for invalid argument. -nvim_strwidth({text}) *nvim_strwidth()* - Calculates the number of display cells occupied by `text` . - <Tab> counts as one cell. +nvim_get_color_map() *nvim_get_color_map()* + Returns a map of color names and RGB values. - Parameters: ~ - {text} Some text + Keys are color names (e.g. "Aqua") and values are 24-bit RGB + color values (e.g. 65535). Return: ~ - Number of cells + Map of color names and RGB values. -nvim_list_runtime_paths() *nvim_list_runtime_paths()* - Gets the paths contained in 'runtimepath'. +nvim_get_commands({opts}) *nvim_get_commands()* + Gets a map of global (non-buffer-local) Ex commands. + + Currently only |user-commands| are supported, not builtin Ex + commands. + + Parameters: ~ + {opts} Optional parameters. Currently only supports + {"builtin":false} Return: ~ - List of paths + Map of maps describing commands. -nvim_set_current_dir({dir}) *nvim_set_current_dir()* - Changes the global working directory. +nvim_get_context({opts}) *nvim_get_context()* + Gets a map of the current editor state. Parameters: ~ - {dir} Directory path + {opts} Optional parameters. + • types: List of |context-types| ("regs", "jumps", + "bufs", "gvars", …) to gather, or empty for + "all". + + Return: ~ + map of global |context|. + +nvim_get_current_buf() *nvim_get_current_buf()* + Gets the current buffer. + + Return: ~ + Buffer handle nvim_get_current_line() *nvim_get_current_line()* Gets the current line. @@ -664,52 +852,75 @@ nvim_get_current_line() *nvim_get_current_line()* Return: ~ Current line string -nvim_set_current_line({line}) *nvim_set_current_line()* - Sets the current line. +nvim_get_current_tabpage() *nvim_get_current_tabpage()* + Gets the current tabpage. - Parameters: ~ - {line} Line contents + Return: ~ + Tabpage handle -nvim_del_current_line() *nvim_del_current_line()* - Deletes the current line. +nvim_get_current_win() *nvim_get_current_win()* + Gets the current window. -nvim_get_var({name}) *nvim_get_var()* - Gets a global (g:) variable. + Return: ~ + Window handle + +nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()* + Gets a highlight definition by id. |hlID()| Parameters: ~ - {name} Variable name + {hl_id} Highlight id as returned by |hlID()| + {rgb} Export RGB colors Return: ~ - Variable value + Highlight definition map -nvim_set_var({name}, {value}) *nvim_set_var()* - Sets a global (g:) variable. + See also: ~ + nvim_get_hl_by_name + +nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()* + Gets a highlight definition by name. Parameters: ~ - {name} Variable name - {value} Variable value + {name} Highlight group name + {rgb} Export RGB colors -nvim_del_var({name}) *nvim_del_var()* - Removes a global (g:) variable. + Return: ~ + Highlight definition map - Parameters: ~ - {name} Variable name + See also: ~ + nvim_get_hl_by_id -nvim_get_vvar({name}) *nvim_get_vvar()* - Gets a v: variable. +nvim_get_hl_id_by_name({name}) *nvim_get_hl_id_by_name()* + Gets a highlight group by name + + similar to |hlID()|, but allocates a new ID if not present. + +nvim_get_keymap({mode}) *nvim_get_keymap()* + Gets a list of global (non-buffer-local) |mapping| + definitions. Parameters: ~ - {name} Variable name + {mode} Mode short-name ("n", "i", "v", ...) Return: ~ - Variable value + Array of maparg()-like dictionaries describing mappings. + The "buffer" key is always zero. -nvim_set_vvar({name}, {value}) *nvim_set_vvar()* - Sets a v: variable, if it is not readonly. +nvim_get_mode() *nvim_get_mode()* + Gets the current mode. |mode()| "blocking" is true if Nvim is + waiting for input. - Parameters: ~ - {name} Variable name - {value} Variable value + Return: ~ + Dictionary { "mode": String, "blocking": Boolean } + + Attributes: ~ + {fast} + +nvim_get_namespaces() *nvim_get_namespaces()* + Gets existing, non-anonymous namespaces. + + Return: ~ + dict that maps from names to namespace ids. nvim_get_option({name}) *nvim_get_option()* Gets an option value string. @@ -720,38 +931,113 @@ nvim_get_option({name}) *nvim_get_option()* Return: ~ Option value (global) -nvim_set_option({name}, {value}) *nvim_set_option()* - Sets an option value. +nvim_get_proc({pid}) *nvim_get_proc()* + Gets info describing process `pid` . + + Return: ~ + Map of process properties, or NIL if process not found. + +nvim_get_proc_children({pid}) *nvim_get_proc_children()* + Gets the immediate children of process `pid` . + + Return: ~ + Array of child process ids, empty if process not found. + +nvim_get_runtime_file({name}, {all}) *nvim_get_runtime_file()* + Find files in runtime directories + + 'name' can contain wildcards. For example + nvim_get_runtime_file("colors/*.vim", true) will return all + color scheme files. + + It is not an error to not find any files. An empty array is + returned then. Parameters: ~ - {name} Option name - {value} New option value + {name} pattern of files to search for + {all} whether to return all matches or only the first -nvim_out_write({str}) *nvim_out_write()* - Writes a message to the Vim output buffer. Does not append - "\n", the message is buffered (won't display) until a linefeed - is written. + Return: ~ + list of absolute paths to the found files + +nvim_get_var({name}) *nvim_get_var()* + Gets a global (g:) variable. Parameters: ~ - {str} Message + {name} Variable name -nvim_err_write({str}) *nvim_err_write()* - Writes a message to the Vim error buffer. Does not append - "\n", the message is buffered (won't display) until a linefeed - is written. + Return: ~ + Variable value + +nvim_get_vvar({name}) *nvim_get_vvar()* + Gets a v: variable. Parameters: ~ - {str} Message + {name} Variable name -nvim_err_writeln({str}) *nvim_err_writeln()* - Writes a message to the Vim error buffer. Appends "\n", so the - buffer is flushed (and displayed). + Return: ~ + Variable value + +nvim_input({keys}) *nvim_input()* + Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a + low-level input buffer and the call is non-blocking (input is + processed asynchronously by the eventloop). + + On execution error: does not fail, but updates v:errmsg. + + Note: + |keycodes| like <CR> are translated, so "<" is special. To + input a literal "<", send <LT>. + Note: + For mouse events use |nvim_input_mouse()|. The pseudokey + form "<LeftMouse><col,row>" is deprecated since + |api-level| 6. + + Attributes: ~ + {fast} Parameters: ~ - {str} Message + {keys} to be typed - See also: ~ - nvim_err_write() + Return: ~ + Number of bytes actually written (can be fewer than + requested if the buffer becomes full). + + *nvim_input_mouse()* +nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col}) + Send mouse event from GUI. + + Non-blocking: does not wait on any result, but queues the + event to be processed soon by the event loop. + + Note: + Currently this doesn't support "scripting" multiple mouse + events by calling it multiple times in a loop: the + intermediate mouse positions will be ignored. It should be + used to implement real-time mouse input in a GUI. The + deprecated pseudokey form ("<LeftMouse><col,row>") of + |nvim_input()| has the same limitiation. + + Attributes: ~ + {fast} + + Parameters: ~ + {button} Mouse button: one of "left", "right", + "middle", "wheel". + {action} For ordinary buttons, one of "press", "drag", + "release". For the wheel, one of "up", "down", + "left", "right". + {modifier} String of modifiers each represented by a + single char. The same specifiers are used as + for a key press, except that the "-" separator + is optional, so "C-A-", "c-a" and "CA" can all + be used to specify Ctrl+Alt+click. + {grid} Grid number if the client uses |ui-multigrid|, + else 0. + {row} Mouse row-position (zero-based, like redraw + events) + {col} Mouse column-position (zero-based, like redraw + events) nvim_list_bufs() *nvim_list_bufs()* Gets the current list of buffer handles @@ -762,49 +1048,48 @@ nvim_list_bufs() *nvim_list_bufs()* Return: ~ List of buffer handles -nvim_get_current_buf() *nvim_get_current_buf()* - Gets the current buffer. +nvim_list_chans() *nvim_list_chans()* + Get information about all open channels. Return: ~ - Buffer handle + Array of Dictionaries, each describing a channel with the + format specified at |nvim_get_chan_info()|. -nvim_set_current_buf({buffer}) *nvim_set_current_buf()* - Sets the current buffer. +nvim_list_runtime_paths() *nvim_list_runtime_paths()* + Gets the paths contained in 'runtimepath'. - Parameters: ~ - {buffer} Buffer handle + Return: ~ + List of paths -nvim_list_wins() *nvim_list_wins()* - Gets the current list of window handles. +nvim_list_tabpages() *nvim_list_tabpages()* + Gets the current list of tabpage handles. Return: ~ - List of window handles + List of tabpage handles -nvim_get_current_win() *nvim_get_current_win()* - Gets the current window. +nvim_list_uis() *nvim_list_uis()* + Gets a list of dictionaries representing attached UIs. Return: ~ - Window handle + Array of UI dictionaries, each with these keys: + • "height" Requested height of the UI + • "width" Requested width of the UI + • "rgb" true if the UI uses RGB colors (false implies + |cterm-colors|) + • "ext_..." Requested UI extensions, see |ui-option| + • "chan" Channel id of remote UI (not present for TUI) -nvim_set_current_win({window}) *nvim_set_current_win()* - Sets the current window. +nvim_list_wins() *nvim_list_wins()* + Gets the current list of window handles. - Parameters: ~ - {window} Window handle + Return: ~ + List of window handles -nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* - Creates a new, empty, unnamed buffer. +nvim_load_context({dict}) *nvim_load_context()* + Sets the current editor state from the given |context| map. Parameters: ~ - {listed} Sets 'buflisted' - {scratch} Creates a "throwaway" |scratch-buffer| for - temporary work (always 'nomodified') - - Return: ~ - Buffer handle, or 0 on error - - See also: ~ - buf_open_scratch + {dict} |Context| map. nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* Open a new window. @@ -849,15 +1134,15 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* {buffer} Buffer to display, or 0 for current buffer {enter} Enter the window (make it the current window) {config} Map defining the window configuration. Keys: - • `relative` : Sets the window layout to "floating", placed - at (row,col) coordinates relative to one of: + • `relative`: Sets the window layout to "floating", placed + at (row,col) coordinates relative to: • "editor" The global editor grid • "win" Window given by the `win` field, or - current window by default. + current window. • "cursor" Cursor position in current window. • `win` : |window-ID| for relative="win". - • `anchor` : Decides which corner of the float to place + • `anchor`: Decides which corner of the float to place at (row,col): • "NW" northwest (default) • "NE" northeast @@ -887,7 +1172,7 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* an external top-level window. Currently accepts no other positioning configuration together with this. - • `style` : Configure the appearance of the window. + • `style`: Configure the appearance of the window. Currently only takes one non-empty value: • "minimal" Nvim will display the window with many UI options disabled. This is useful @@ -896,54 +1181,119 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* 'number', 'relativenumber', 'cursorline', 'cursorcolumn', 'foldcolumn', 'spell' and 'list' options. 'signcolumn' is changed to - `auto` . The end-of-buffer region is hidden - by setting `eob` flag of 'fillchars' to a - space char, and clearing the |EndOfBuffer| - region in 'winhighlight'. + `auto` and 'colorcolumn' is cleared. The + end-of-buffer region is hidden by setting + `eob` flag of 'fillchars' to a space char, + and clearing the |EndOfBuffer| region in + 'winhighlight'. Return: ~ Window handle, or 0 on error -nvim_list_tabpages() *nvim_list_tabpages()* - Gets the current list of tabpage handles. - - Return: ~ - List of tabpage handles - -nvim_get_current_tabpage() *nvim_get_current_tabpage()* - Gets the current tabpage. - - Return: ~ - Tabpage handle - -nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()* - Sets the current tabpage. +nvim_out_write({str}) *nvim_out_write()* + Writes a message to the Vim output buffer. Does not append + "\n", the message is buffered (won't display) until a linefeed + is written. Parameters: ~ - {tabpage} Tabpage handle - -nvim_create_namespace({name}) *nvim_create_namespace()* - Creates a new namespace, or gets an existing one. + {str} Message - Namespaces are used for buffer highlights and virtual text, - see |nvim_buf_add_highlight()| and - |nvim_buf_set_virtual_text()|. + *nvim_parse_expression()* +nvim_parse_expression({expr}, {flags}, {highlight}) + Parse a VimL expression. - Namespaces can be named or anonymous. If `name` matches an - existing namespace, the associated id is returned. If `name` - is an empty string a new, anonymous namespace is created. + Attributes: ~ + {fast} Parameters: ~ - {name} Namespace name or empty string + {expr} Expression to parse. Always treated as a + single line. + {flags} Flags: + • "m" if multiple expressions in a row are + allowed (only the first one will be + parsed), + • "E" if EOC tokens are not allowed + (determines whether they will stop parsing + process or be recognized as an + operator/space, though also yielding an + error). + • "l" when needing to start parsing with + lvalues for ":let" or ":for". Common flag + sets: + • "m" to parse like for ":echo". + • "E" to parse like for "<C-r>=". + • empty string for ":call". + • "lm" to parse for ":let". + {highlight} If true, return value will also include + "highlight" key containing array of 4-tuples + (arrays) (Integer, Integer, Integer, String), + where first three numbers define the + highlighted region and represent line, + starting column and ending column (latter + exclusive: one should highlight region + [start_col, end_col)). Return: ~ - Namespace id -nvim_get_namespaces() *nvim_get_namespaces()* - Gets existing, non-anonymous namespaces. + • AST: top-level dictionary with these keys: + • "error": Dictionary with error, present only if parser + saw some error. Contains the following keys: + • "message": String, error message in printf format, + translated. Must contain exactly one "%.*s". + • "arg": String, error message argument. - Return: ~ - dict that maps from names to namespace ids. + • "len": Amount of bytes successfully parsed. With flags + equal to "" that should be equal to the length of expr + string. (“Sucessfully parsed” here means “participated + in AST creation”, not “till the first error”.) + • "ast": AST, either nil or a dictionary with these + keys: + • "type": node type, one of the value names from + ExprASTNodeType stringified without "kExprNode" + prefix. + • "start": a pair [line, column] describing where node + is "started" where "line" is always 0 (will not be 0 + if you will be using nvim_parse_viml() on e.g. + ":let", but that is not present yet). Both elements + are Integers. + • "len": “length” of the node. This and "start" are + there for debugging purposes primary (debugging + parser and providing debug information). + • "children": a list of nodes described in top/"ast". + There always is zero, one or two children, key will + not be present if node has no children. Maximum + number of children may be found in node_maxchildren + array. + + • Local values (present only for certain nodes): + • "scope": a single Integer, specifies scope for + "Option" and "PlainIdentifier" nodes. For "Option" it + is one of ExprOptScope values, for "PlainIdentifier" + it is one of ExprVarScope values. + • "ident": identifier (without scope, if any), present + for "Option", "PlainIdentifier", "PlainKey" and + "Environment" nodes. + • "name": Integer, register name (one character) or -1. + Only present for "Register" nodes. + • "cmp_type": String, comparison type, one of the value + names from ExprComparisonType, stringified without + "kExprCmp" prefix. Only present for "Comparison" + nodes. + • "ccs_strategy": String, case comparison strategy, one + of the value names from ExprCaseCompareStrategy, + stringified without "kCCStrategy" prefix. Only present + for "Comparison" nodes. + • "augmentation": String, augmentation type for + "Assignment" nodes. Is either an empty string, "Add", + "Subtract" or "Concat" for "=", "+=", "-=" or ".=" + respectively. + • "invert": Boolean, true if result of comparison needs + to be inverted. Only present for "Comparison" nodes. + • "ivalue": Integer, integer value for "Integer" nodes. + • "fvalue": Float, floating-point value for "Float" + nodes. + • "svalue": String, value for "SingleQuotedString" and + "DoubleQuotedString" nodes. nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* Pastes at cursor, in any mode. @@ -984,146 +1334,49 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()* {type} Edit behavior: any |getregtype()| result, or: • "b" |blockwise-visual| mode (may include width, e.g. "b3") - • "c" |characterwise| mode + • "c" |charwise| mode • "l" |linewise| mode • "" guess by contents, see |setreg()| {after} Insert after cursor (like |p|), or before (like |P|). {follow} Place cursor at end of inserted text. -nvim_subscribe({event}) *nvim_subscribe()* - Subscribes to event broadcasts. - - Parameters: ~ - {event} Event type string - -nvim_unsubscribe({event}) *nvim_unsubscribe()* - Unsubscribes to event broadcasts. - - Parameters: ~ - {event} Event type string - -nvim_get_color_by_name({name}) *nvim_get_color_by_name()* - Returns the 24-bit RGB value of a |nvim_get_color_map()| color - name or "#rrggbb" hexadecimal string. - - Example: > - :echo nvim_get_color_by_name("Pink") - :echo nvim_get_color_by_name("#cbcbcb") -< - - Parameters: ~ - {name} Color name or "#rrggbb" string - - Return: ~ - 24-bit RGB value, or -1 for invalid argument. - -nvim_get_color_map() *nvim_get_color_map()* - Returns a map of color names and RGB values. - - Keys are color names (e.g. "Aqua") and values are 24-bit RGB - color values (e.g. 65535). - - Return: ~ - Map of color names and RGB values. - -nvim_get_context({opts}) *nvim_get_context()* - Gets a map of the current editor state. - - Parameters: ~ - {opts} Optional parameters. - • types: List of |context-types| ("regs", "jumps", - "bufs", "gvars", …) to gather, or empty for - "all". - - Return: ~ - map of global |context|. - -nvim_load_context({dict}) *nvim_load_context()* - Sets the current editor state from the given |context| map. - - Parameters: ~ - {dict} |Context| map. - -nvim_get_mode() *nvim_get_mode()* - Gets the current mode. |mode()| "blocking" is true if Nvim is - waiting for input. - - Return: ~ - Dictionary { "mode": String, "blocking": Boolean } - - Attributes: ~ - {fast} - -nvim_get_keymap({mode}) *nvim_get_keymap()* - Gets a list of global (non-buffer-local) |mapping| - definitions. - - Parameters: ~ - {mode} Mode short-name ("n", "i", "v", ...) - - Return: ~ - Array of maparg()-like dictionaries describing mappings. - The "buffer" key is always zero. - -nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* - Sets a global |mapping| for the given mode. - - To set a buffer-local mapping, use |nvim_buf_set_keymap()|. - - Unlike |:map|, leading/trailing whitespace is accepted as part - of the {lhs} or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are - replaced as usual. - - Example: > - call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true}) -< - - is equivalent to: > - nmap <nowait> <Space><NL> <Nop> -< + *nvim_replace_termcodes()* +nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) + Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a + string with the internal representation. Parameters: ~ - {mode} Mode short-name (map command prefix: "n", "i", - "v", "x", …) or "!" for |:map!|, or empty string - for |:map|. - {lhs} Left-hand-side |{lhs}| of the mapping. - {rhs} Right-hand-side |{rhs}| of the mapping. - {opts} Optional parameters map. Accepts all - |:map-arguments| as keys excluding |<buffer>| but - including |noremap|. Values are Booleans. Unknown - key is an error. - -nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()* - Unmaps a global |mapping| for the given mode. - - To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|. + {str} String to be converted. + {from_part} Legacy Vim parameter. Usually true. + {do_lt} Also translate <lt>. Ignored if `special` is + false. + {special} Replace |keycodes|, e.g. <CR> becomes a "\n" + char. See also: ~ - |nvim_set_keymap()| + replace_termcodes + cpoptions -nvim_get_commands({opts}) *nvim_get_commands()* - Gets a map of global (non-buffer-local) Ex commands. + *nvim_select_popupmenu_item()* +nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) + Selects an item in the completion popupmenu. - Currently only |user-commands| are supported, not builtin Ex - commands. + If |ins-completion| is not active this API call is silently + ignored. Useful for an external UI using |ui-popupmenu| to + control the popupmenu with the mouse. Can also be used in a + mapping; use <cmd> |:map-cmd| to ensure the mapping doesn't + end completion mode. Parameters: ~ - {opts} Optional parameters. Currently only supports - {"builtin":false} - - Return: ~ - Map of maps describing commands. - -nvim_get_api_info() *nvim_get_api_info()* - Returns a 2-tuple (Array), where item 0 is the current channel - id and item 1 is the |api-metadata| map (Dictionary). - - Return: ~ - 2-tuple [{channel-id}, {api-metadata}] - - Attributes: ~ - {fast} + {item} Index (zero-based) of the item to select. Value + of -1 selects nothing and restores the original + text. + {insert} Whether the selection should be inserted in the + buffer. + {finish} Finish the completion and dismiss the popupmenu. + Implies `insert` . + {opts} Optional parameters. Reserved for future use. *nvim_set_client_info()* nvim_set_client_info({name}, {version}, {type}, {methods}, @@ -1190,269 +1443,114 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, small logo or icon. .png or .svg format is preferred. -nvim_get_chan_info({chan}) *nvim_get_chan_info()* - Get information about a channel. - - Return: ~ - Dictionary describing a channel, with these keys: - • "stream" the stream underlying the channel - • "stdio" stdin and stdout of this Nvim instance - • "stderr" stderr of this Nvim instance - • "socket" TCP/IP socket or named pipe - • "job" job with communication over its stdio - - • "mode" how data received on the channel is interpreted - • "bytes" send and receive raw bytes - • "terminal" a |terminal| instance interprets ASCII - sequences - • "rpc" |RPC| communication on the channel is active - - • "pty" Name of pseudoterminal, if one is used (optional). - On a POSIX system, this will be a device path like - /dev/pts/1. Even if the name is unknown, the key will - still be present to indicate a pty is used. This is - currently the case when using winpty on windows. - • "buffer" buffer with connected |terminal| instance - (optional) - • "client" information about the client on the other end - of the RPC channel, if it has added it using - |nvim_set_client_info()|. (optional) +nvim_set_current_buf({buffer}) *nvim_set_current_buf()* + Sets the current buffer. -nvim_list_chans() *nvim_list_chans()* - Get information about all open channels. + Parameters: ~ + {buffer} Buffer handle - Return: ~ - Array of Dictionaries, each describing a channel with the - format specified at |nvim_get_chan_info()|. +nvim_set_current_dir({dir}) *nvim_set_current_dir()* + Changes the global working directory. -nvim_call_atomic({calls}) *nvim_call_atomic()* - Calls many API methods atomically. + Parameters: ~ + {dir} Directory path - This has two main usages: - 1. To perform several requests from an async context - atomically, i.e. without interleaving redraws, RPC requests - from other clients, or user interactions (however API - methods may trigger autocommands or event processing which - have such side-effects, e.g. |:sleep| may wake timers). - 2. To minimize RPC overhead (roundtrips) of a sequence of many - requests. +nvim_set_current_line({line}) *nvim_set_current_line()* + Sets the current line. Parameters: ~ - {calls} an array of calls, where each call is described - by an array with two elements: the request name, - and an array of arguments. + {line} Line contents - Return: ~ - Array of two elements. The first is an array of return - values. The second is NIL if all calls succeeded. If a - call resulted in an error, it is a three-element array - with the zero-based index of the call which resulted in an - error, the error type and the error message. If an error - occurred, the values from all preceding calls will still - be returned. +nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()* + Sets the current tabpage. - *nvim_parse_expression()* -nvim_parse_expression({expr}, {flags}, {highlight}) - Parse a VimL expression. + Parameters: ~ + {tabpage} Tabpage handle - Attributes: ~ - {fast} +nvim_set_current_win({window}) *nvim_set_current_win()* + Sets the current window. Parameters: ~ - {expr} Expression to parse. Always treated as a - single line. - {flags} Flags: - • "m" if multiple expressions in a row are - allowed (only the first one will be - parsed), - • "E" if EOC tokens are not allowed - (determines whether they will stop parsing - process or be recognized as an - operator/space, though also yielding an - error). - • "l" when needing to start parsing with - lvalues for ":let" or ":for". Common flag - sets: - • "m" to parse like for ":echo". - • "E" to parse like for "<C-r>=". - • empty string for ":call". - • "lm" to parse for ":let". - {highlight} If true, return value will also include - "highlight" key containing array of 4-tuples - (arrays) (Integer, Integer, Integer, String), - where first three numbers define the - highlighted region and represent line, - starting column and ending column (latter - exclusive: one should highlight region - [start_col, end_col)). - - Return: ~ + {window} Window handle - • AST: top-level dictionary with these keys: - • "error": Dictionary with error, present only if parser - saw some error. Contains the following keys: - • "message": String, error message in printf format, - translated. Must contain exactly one "%.*s". - • "arg": String, error message argument. +nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* + Sets a global |mapping| for the given mode. - • "len": Amount of bytes successfully parsed. With flags - equal to "" that should be equal to the length of expr - string. (“Sucessfully parsed” here means “participated - in AST creation”, not “till the first error”.) - • "ast": AST, either nil or a dictionary with these - keys: - • "type": node type, one of the value names from - ExprASTNodeType stringified without "kExprNode" - prefix. - • "start": a pair [line, column] describing where node - is "started" where "line" is always 0 (will not be 0 - if you will be using nvim_parse_viml() on e.g. - ":let", but that is not present yet). Both elements - are Integers. - • "len": “length” of the node. This and "start" are - there for debugging purposes primary (debugging - parser and providing debug information). - • "children": a list of nodes described in top/"ast". - There always is zero, one or two children, key will - not be present if node has no children. Maximum - number of children may be found in node_maxchildren - array. + To set a buffer-local mapping, use |nvim_buf_set_keymap()|. - • Local values (present only for certain nodes): - • "scope": a single Integer, specifies scope for - "Option" and "PlainIdentifier" nodes. For "Option" it - is one of ExprOptScope values, for "PlainIdentifier" - it is one of ExprVarScope values. - • "ident": identifier (without scope, if any), present - for "Option", "PlainIdentifier", "PlainKey" and - "Environment" nodes. - • "name": Integer, register name (one character) or -1. - Only present for "Register" nodes. - • "cmp_type": String, comparison type, one of the value - names from ExprComparisonType, stringified without - "kExprCmp" prefix. Only present for "Comparison" - nodes. - • "ccs_strategy": String, case comparison strategy, one - of the value names from ExprCaseCompareStrategy, - stringified without "kCCStrategy" prefix. Only present - for "Comparison" nodes. - • "augmentation": String, augmentation type for - "Assignment" nodes. Is either an empty string, "Add", - "Subtract" or "Concat" for "=", "+=", "-=" or ".=" - respectively. - • "invert": Boolean, true if result of comparison needs - to be inverted. Only present for "Comparison" nodes. - • "ivalue": Integer, integer value for "Integer" nodes. - • "fvalue": Float, floating-point value for "Float" - nodes. - • "svalue": String, value for "SingleQuotedString" and - "DoubleQuotedString" nodes. + Unlike |:map|, leading/trailing whitespace is accepted as part + of the {lhs} or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are + replaced as usual. -nvim__id({obj}) *nvim__id()* - Returns object given as argument. + Example: > + call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true}) +< - This API function is used for testing. One should not rely on - its presence in plugins. + is equivalent to: > + nmap <nowait> <Space><NL> <Nop> +< Parameters: ~ - {obj} Object to return. - - Return: ~ - its argument. - -nvim__id_array({arr}) *nvim__id_array()* - Returns array given as argument. + {mode} Mode short-name (map command prefix: "n", "i", + "v", "x", …) or "!" for |:map!|, or empty string + for |:map|. + {lhs} Left-hand-side |{lhs}| of the mapping. + {rhs} Right-hand-side |{rhs}| of the mapping. + {opts} Optional parameters map. Accepts all + |:map-arguments| as keys excluding |<buffer>| but + including |noremap|. Values are Booleans. Unknown + key is an error. - This API function is used for testing. One should not rely on - its presence in plugins. +nvim_set_option({name}, {value}) *nvim_set_option()* + Sets an option value. Parameters: ~ - {arr} Array to return. - - Return: ~ - its argument. - -nvim__id_dictionary({dct}) *nvim__id_dictionary()* - Returns dictionary given as argument. + {name} Option name + {value} New option value - This API function is used for testing. One should not rely on - its presence in plugins. +nvim_set_var({name}, {value}) *nvim_set_var()* + Sets a global (g:) variable. Parameters: ~ - {dct} Dictionary to return. - - Return: ~ - its argument. - -nvim__id_float({flt}) *nvim__id_float()* - Returns floating-point value given as argument. + {name} Variable name + {value} Variable value - This API function is used for testing. One should not rely on - its presence in plugins. +nvim_set_vvar({name}, {value}) *nvim_set_vvar()* + Sets a v: variable, if it is not readonly. Parameters: ~ - {flt} Value to return. - - Return: ~ - its argument. - -nvim__stats() *nvim__stats()* - Gets internal stats. - - Return: ~ - Map of various internal stats. - -nvim_list_uis() *nvim_list_uis()* - Gets a list of dictionaries representing attached UIs. + {name} Variable name + {value} Variable value - Return: ~ - Array of UI dictionaries, each with these keys: - • "height" Requested height of the UI - • "width" Requested width of the UI - • "rgb" true if the UI uses RGB colors (false implies - |cterm-colors|) - • "ext_..." Requested UI extensions, see |ui-option| - • "chan" Channel id of remote UI (not present for TUI) +nvim_strwidth({text}) *nvim_strwidth()* + Calculates the number of display cells occupied by `text` . + <Tab> counts as one cell. -nvim_get_proc_children({pid}) *nvim_get_proc_children()* - Gets the immediate children of process `pid` . + Parameters: ~ + {text} Some text Return: ~ - Array of child process ids, empty if process not found. - -nvim_get_proc({pid}) *nvim_get_proc()* - Gets info describing process `pid` . + Number of cells - Return: ~ - Map of process properties, or NIL if process not found. +nvim_subscribe({event}) *nvim_subscribe()* + Subscribes to event broadcasts. - *nvim_select_popupmenu_item()* -nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) - Selects an item in the completion popupmenu. + Parameters: ~ + {event} Event type string - If |ins-completion| is not active this API call is silently - ignored. Useful for an external UI using |ui-popupmenu| to - control the popupmenu with the mouse. Can also be used in a - mapping; use <cmd> |:map-cmd| to ensure the mapping doesn't - end completion mode. +nvim_unsubscribe({event}) *nvim_unsubscribe()* + Unsubscribes to event broadcasts. Parameters: ~ - {item} Index (zero-based) of the item to select. Value - of -1 selects nothing and restores the original - text. - {insert} Whether the selection should be inserted in the - buffer. - {finish} Finish the completion and dismiss the popupmenu. - Implies `insert` . - {opts} Optional parameters. Reserved for future use. - -nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* - TODO: Documentation + {event} Event type string ============================================================================== Buffer Functions *api-buffer* + +For more information on buffers, see |buffers|. + Unloaded Buffers:~ Buffers may be unloaded by the |:bunload| command or the @@ -1466,139 +1564,266 @@ affected. You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check whether a buffer is loaded. -nvim_buf_line_count({buffer}) *nvim_buf_line_count()* - Gets the buffer line count + *nvim__buf_add_decoration()* +nvim__buf_add_decoration({buffer}, {ns_id}, {hl_group}, {start_row}, + {start_col}, {end_row}, {end_col}, + {virt_text}) + TODO: Documentation + + *nvim__buf_redraw_range()* +nvim__buf_redraw_range({buffer}, {first}, {last}) + TODO: Documentation + +nvim__buf_set_luahl({buffer}, {opts}) *nvim__buf_set_luahl()* + Unstabilized interface for defining syntax hl in lua. + + This is not yet safe for general use, lua callbacks will need + to be restricted, like textlock and probably other stuff. + + The API on_line/nvim__put_attr is quite raw and not intended + to be the final shape. Ideally this should operate on chunks + larger than a single line to reduce interpreter overhead, and + generate annotation objects (bufhl/virttext) on the fly but + using the same representation. + +nvim__buf_stats({buffer}) *nvim__buf_stats()* + TODO: Documentation + + *nvim_buf_add_highlight()* +nvim_buf_add_highlight({buffer}, {src_id}, {hl_group}, {line}, + {col_start}, {col_end}) + Adds a highlight to buffer. + + Useful for plugins that dynamically generate highlights to a + buffer (like a semantic highlighter or linter). The function + adds a single highlight to a buffer. Unlike |matchaddpos()| + highlights follow changes to line numbering (as lines are + inserted/removed above the highlighted line), like signs and + marks do. + + Namespaces are used for batch deletion/updating of a set of + highlights. To create a namespace, use |nvim_create_namespace| + which returns a namespace id. Pass it in to this function as + `ns_id` to add highlights to the namespace. All highlights in + the same namespace can then be cleared with single call to + |nvim_buf_clear_namespace|. If the highlight never will be + deleted by an API call, pass `ns_id = -1` . + + As a shorthand, `ns_id = 0` can be used to create a new + namespace for the highlight, the allocated id is then + returned. If `hl_group` is the empty string no highlight is + added, but a new `ns_id` is still returned. This is supported + for backwards compatibility, new code should use + |nvim_create_namespace| to create a new empty namespace. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer + {buffer} Buffer handle, or 0 for current buffer + {ns_id} namespace to use or -1 for ungrouped + highlight + {hl_group} Name of the highlight group to use + {line} Line to highlight (zero-indexed) + {col_start} Start of (byte-indexed) column range to + highlight + {col_end} End of (byte-indexed) column range to + highlight, or -1 to highlight to end of line Return: ~ - Line count, or 0 for unloaded buffer. |api-buffer| + The ns_id that was used nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* - Activates buffer-update events on a channel, or as lua + Activates buffer-update events on a channel, or as Lua callbacks. + Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its + contents): > + events = {} + vim.api.nvim_buf_attach(0, false, { + on_lines=function(...) table.insert(events, {...}) end}) +< + Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {send_buffer} Set to true if the initial notification - should contain the whole buffer. If so, the - first notification will be a - `nvim_buf_lines_event` . Otherwise, the - first notification will be a - `nvim_buf_changedtick_event` . Not used for - lua callbacks. + {send_buffer} True if the initial notification should + contain the whole buffer: first + notification will be `nvim_buf_lines_event` + . Else the first notification will be + `nvim_buf_changedtick_event` . Not for Lua + callbacks. {opts} Optional parameters. - • `on_lines` : lua callback received on - change. - • `on_changedtick` : lua callback received - on changedtick increment without text - change. - • `utf_sizes` : include UTF-32 and UTF-16 - size of the replaced region. See - |api-buffer-updates-lua| for more - information + • on_lines: Lua callback invoked on change. + Return`true`to detach. Args: + • the string "lines" + • buffer handle + • b:changedtick + • first line that changed (zero-indexed) + • last line that was changed + • last line in the updated range + • byte count of previous contents + • deleted_codepoints (if `utf_sizes` is + true) + • deleted_codeunits (if `utf_sizes` is + true) + + • on_changedtick: Lua callback invoked on + changedtick increment without text + change. Args: + • the string "changedtick" + • buffer handle + • b:changedtick + + • on_detach: Lua callback invoked on + detach. Args: + • the string "detach" + • buffer handle + + • utf_sizes: include UTF-32 and UTF-16 size + of the replaced region, as args to + `on_lines` . + + Return: ~ + False if attach failed (invalid parameter, or buffer isn't + loaded); otherwise True. TODO: LUA_API_NO_EVAL - Return: ~ - False when updates couldn't be enabled because the buffer - isn't loaded or `opts` contained an invalid key; otherwise - True. TODO: LUA_API_NO_EVAL + See also: ~ + |nvim_buf_detach()| + |api-buffer-updates-lua| -nvim_buf_detach({buffer}) *nvim_buf_detach()* - Deactivates buffer-update events on the channel. + *nvim_buf_clear_namespace()* +nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) + Clears namespaced objects (highlights, extmarks, virtual text) + from a region. + + Lines are 0-indexed. |api-indexing| To clear the namespace in + the entire buffer, specify line_start=0 and line_end=-1. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace to clear, or -1 to clear all + namespaces. + {line_start} Start of range of lines to clear + {line_end} End of range of lines to clear (exclusive) + or -1 to clear to end of buffer. - For Lua callbacks see |api-lua-detach|. +nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* + Removes an extmark. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ - False when updates couldn't be disabled because the buffer - isn't loaded; otherwise True. + true if the extmark was found, else false - *nvim_buf_get_lines()* -nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) - Gets a line-range from the buffer. +nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()* + Unmaps a buffer-local |mapping| for the given mode. - Indexing is zero-based, end-exclusive. Negative indices are - interpreted as length+1+index: -1 refers to the index past the - end. So to get the last element use start=-2 and end=-1. + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer - Out-of-bounds indices are clamped to the nearest valid value, - unless `strict_indexing` is set. + See also: ~ + |nvim_del_keymap()| + +nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()* + Removes a buffer-scoped (b:) variable Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {start} First line index - {end} Last line index (exclusive) - {strict_indexing} Whether out-of-bounds should be an - error. + {buffer} Buffer handle, or 0 for current buffer + {name} Variable name - Return: ~ - Array of lines, or empty array for unloaded buffer. +nvim_buf_detach({buffer}) *nvim_buf_detach()* + Deactivates buffer-update events on the channel. - *nvim_buf_set_lines()* -nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, - {replacement}) - Sets (replaces) a line-range in the buffer. + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer - Indexing is zero-based, end-exclusive. Negative indices are - interpreted as length+1+index: -1 refers to the index past the - end. So to change or delete the last element use start=-2 and - end=-1. + Return: ~ + False if detach failed (because the buffer isn't loaded); + otherwise True. - To insert lines at a given index, set `start` and `end` to the - same index. To delete a range of lines, set `replacement` to - an empty array. + See also: ~ + |nvim_buf_attach()| + |api-lua-detach| for detaching Lua callbacks - Out-of-bounds indices are clamped to the nearest valid value, - unless `strict_indexing` is set. +nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()* + Gets a changed tick of a buffer Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {start} First line index - {end} Last line index (exclusive) - {strict_indexing} Whether out-of-bounds should be an - error. - {replacement} Array of lines to use as replacement - -nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()* - Returns the byte offset of a line (0-indexed). |api-indexing| + {buffer} Buffer handle, or 0 for current buffer - Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is - one byte. 'fileformat' and 'fileencoding' are ignored. The - line index just after the last line gives the total byte-count - of the buffer. A final EOL byte is counted if it would be - written, see 'eol'. + Return: ~ + `b:changedtick` value. - Unlike |line2byte()|, throws error for out-of-bounds indexing. - Returns -1 for unloaded buffer. +nvim_buf_get_commands({buffer}, {opts}) *nvim_buf_get_commands()* + Gets a map of buffer-local |user-commands|. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {index} Line index + {opts} Optional parameters. Currently not used. Return: ~ - Integer byte offset, or -1 for unloaded buffer. + Map of maps describing commands. -nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()* - Gets a buffer-scoped (b:) variable. + *nvim_buf_get_extmark_by_id()* +nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}) + Returns position for a given extmark id Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Variable name + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ - Variable value + (row, col) tuple or empty list () if extmark id was absent -nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()* - Gets a changed tick of a buffer + *nvim_buf_get_extmarks()* +nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) + Gets extmarks in "traversal order" from a |charwise| region + defined by buffer positions (inclusive, 0-indexed + |api-indexing|). + + Region can be given as (row,col) tuples, or valid extmark ids + (whose positions define the bounds). 0 and -1 are understood + as (0,0) and (-1,-1) respectively, thus the following are + equivalent: +> + nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) +< + + If `end` is less than `start` , traversal works backwards. + (Useful with `limit` , to get the first marks prior to a given + position.) + + Example: +> + local a = vim.api + local pos = a.nvim_win_get_cursor(0) + local ns = a.nvim_create_namespace('my-plugin') + -- Create new extmark at line 1, column 1. + local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + -- Create new extmark at line 3, column 1. + local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) + -- Get extmarks only from line 3. + local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) + -- Get all marks in this buffer + namespace. + local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) + print(vim.inspect(ms)) +< Parameters: ~ {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {start} Start of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {end} End of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {opts} Optional parameters. Keys: + • limit: Maximum number of marks to return Return: ~ - `b:changedtick` value. + List of [extmark_id, row, col] tuples in "traversal + order". nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()* Gets a list of buffer-local |mapping| definitions. @@ -1611,49 +1836,67 @@ nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()* Array of maparg()-like dictionaries describing mappings. The "buffer" key holds the associated buffer handle. - *nvim_buf_set_keymap()* -nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {opts}) - Sets a buffer-local |mapping| for the given mode. + *nvim_buf_get_lines()* +nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) + Gets a line-range from the buffer. + + Indexing is zero-based, end-exclusive. Negative indices are + interpreted as length+1+index: -1 refers to the index past the + end. So to get the last element use start=-2 and end=-1. + + Out-of-bounds indices are clamped to the nearest valid value, + unless `strict_indexing` is set. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer + {buffer} Buffer handle, or 0 for current buffer + {start} First line index + {end} Last line index (exclusive) + {strict_indexing} Whether out-of-bounds should be an + error. - See also: ~ - |nvim_set_keymap()| + Return: ~ + Array of lines, or empty array for unloaded buffer. -nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()* - Unmaps a buffer-local |mapping| for the given mode. +nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()* + Return a tuple (row,col) representing the position of the + named mark. + + Marks are (1,0)-indexed. |api-indexing| Parameters: ~ {buffer} Buffer handle, or 0 for current buffer + {name} Mark name - See also: ~ - |nvim_del_keymap()| + Return: ~ + (row, col) tuple -nvim_buf_get_commands({buffer}, {opts}) *nvim_buf_get_commands()* - Gets a map of buffer-local |user-commands|. +nvim_buf_get_name({buffer}) *nvim_buf_get_name()* + Gets the full file name for the buffer Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {opts} Optional parameters. Currently not used. Return: ~ - Map of maps describing commands. + Buffer name -nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* - Sets a buffer-scoped (b:) variable +nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()* + Returns the byte offset of a line (0-indexed). |api-indexing| - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {name} Variable name - {value} Variable value + Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is + one byte. 'fileformat' and 'fileencoding' are ignored. The + line index just after the last line gives the total byte-count + of the buffer. A final EOL byte is counted if it would be + written, see 'eol'. -nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()* - Removes a buffer-scoped (b:) variable + Unlike |line2byte()|, throws error for out-of-bounds indexing. + Returns -1 for unloaded buffer. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Variable name + {index} Line index + + Return: ~ + Integer byte offset, or -1 for unloaded buffer. nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()* Gets a buffer option value @@ -1665,30 +1908,37 @@ nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()* Return: ~ Option value -nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* - Sets a buffer option value. Passing 'nil' as value deletes the - option (only works if there's a global fallback) +nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()* + Gets a buffer-scoped (b:) variable. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Option name - {value} Option value + {name} Variable name -nvim_buf_get_name({buffer}) *nvim_buf_get_name()* - Gets the full file name for the buffer + Return: ~ + Variable value - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer + *nvim_buf_get_virtual_text()* +nvim_buf_get_virtual_text({buffer}, {line}) + Get the virtual text (annotation) for a buffer line. - Return: ~ - Buffer name + The virtual text is returned as list of lists, whereas the + inner lists have either one or two elements. The first element + is the actual text, the optional second element is the + highlight group. -nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* - Sets the full file name for a buffer + The format is exactly the same as given to + nvim_buf_set_virtual_text(). + + If there is no virtual text associated with the given line, an + empty list is returned. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Buffer name + {line} Line to get the virtual text from (zero-indexed) + + Return: ~ + List of virtual text chunks nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()* Checks if a buffer is valid and loaded. See |api-buffer| for @@ -1713,78 +1963,98 @@ nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()* Return: ~ true if the buffer is valid, false otherwise. -nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()* - Return a tuple (row,col) representing the position of the - named mark. +nvim_buf_line_count({buffer}) *nvim_buf_line_count()* + Gets the buffer line count - Marks are (1,0)-indexed. |api-indexing| + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + + Return: ~ + Line count, or 0 for unloaded buffer. |api-buffer| + + *nvim_buf_set_extmark()* +nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) + Creates or updates an extmark. + + To create a new extmark, pass id=0. The extmark id will be + returned. It is also allowed to create a new mark by passing + in a previously unused id, but the caller must then keep track + of existing and unused ids itself. (Useful over RPC, to avoid + waiting for the return value.) Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Mark name + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id, or 0 to create new + {line} Line number where to place the mark + {col} Column where to place the mark + {opts} Optional parameters. Currently not used. Return: ~ - (row, col) tuple + Id of the created/updated extmark - *nvim_buf_add_highlight()* -nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, - {col_start}, {col_end}) - Adds a highlight to buffer. + *nvim_buf_set_keymap()* +nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {opts}) + Sets a buffer-local |mapping| for the given mode. - Useful for plugins that dynamically generate highlights to a - buffer (like a semantic highlighter or linter). The function - adds a single highlight to a buffer. Unlike |matchaddpos()| - highlights follow changes to line numbering (as lines are - inserted/removed above the highlighted line), like signs and - marks do. + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer - Namespaces are used for batch deletion/updating of a set of - highlights. To create a namespace, use |nvim_create_namespace| - which returns a namespace id. Pass it in to this function as - `ns_id` to add highlights to the namespace. All highlights in - the same namespace can then be cleared with single call to - |nvim_buf_clear_namespace|. If the highlight never will be - deleted by an API call, pass `ns_id = -1` . + See also: ~ + |nvim_set_keymap()| - As a shorthand, `ns_id = 0` can be used to create a new - namespace for the highlight, the allocated id is then - returned. If `hl_group` is the empty string no highlight is - added, but a new `ns_id` is still returned. This is supported - for backwards compatibility, new code should use - |nvim_create_namespace| to create a new empty namespace. + *nvim_buf_set_lines()* +nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, + {replacement}) + Sets (replaces) a line-range in the buffer. + + Indexing is zero-based, end-exclusive. Negative indices are + interpreted as length+1+index: -1 refers to the index past the + end. So to change or delete the last element use start=-2 and + end=-1. + + To insert lines at a given index, set `start` and `end` to the + same index. To delete a range of lines, set `replacement` to + an empty array. + + Out-of-bounds indices are clamped to the nearest valid value, + unless `strict_indexing` is set. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} namespace to use or -1 for ungrouped - highlight - {hl_group} Name of the highlight group to use - {line} Line to highlight (zero-indexed) - {col_start} Start of (byte-indexed) column range to - highlight - {col_end} End of (byte-indexed) column range to - highlight, or -1 to highlight to end of line + {buffer} Buffer handle, or 0 for current buffer + {start} First line index + {end} Last line index (exclusive) + {strict_indexing} Whether out-of-bounds should be an + error. + {replacement} Array of lines to use as replacement - Return: ~ - The ns_id that was used +nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* + Sets the full file name for a buffer - *nvim_buf_clear_namespace()* -nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) - Clears namespaced objects, highlights and virtual text, from a - line range + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Buffer name - Lines are 0-indexed. |api-indexing| To clear the namespace in - the entire buffer, specify line_start=0 and line_end=-1. +nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* + Sets a buffer option value. Passing 'nil' as value deletes the + option (only works if there's a global fallback) Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace to clear, or -1 to clear all - namespaces. - {line_start} Start of range of lines to clear - {line_end} End of range of lines to clear (exclusive) - or -1 to clear to end of buffer. + {buffer} Buffer handle, or 0 for current buffer + {name} Option name + {value} Option value + +nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* + Sets a buffer-scoped (b:) variable + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Variable name + {value} Variable value *nvim_buf_set_virtual_text()* -nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts}) +nvim_buf_set_virtual_text({buffer}, {src_id}, {line}, {chunks}, + {opts}) Set the virtual text (annotation) for a buffer line. By default (and currently the only option) the text will be @@ -1821,105 +2091,77 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts}) Return: ~ The ns_id that was used -nvim__buf_stats({buffer}) *nvim__buf_stats()* - TODO: Documentation - ============================================================================== Window Functions *api-window* -nvim_win_get_buf({window}) *nvim_win_get_buf()* - Gets the current buffer in a window +nvim_win_close({window}, {force}) *nvim_win_close()* + Closes the window (like |:close| with a |window-ID|). Parameters: ~ {window} Window handle, or 0 for current window + {force} Behave like `:close!` The last window of a + buffer with unwritten changes can be closed. The + buffer will become hidden, even if 'hidden' is + not set. - Return: ~ - Buffer handle - -nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()* - Sets the current buffer in a window, without side-effects +nvim_win_del_var({window}, {name}) *nvim_win_del_var()* + Removes a window-scoped (w:) variable Parameters: ~ {window} Window handle, or 0 for current window - {buffer} Buffer handle + {name} Variable name -nvim_win_get_cursor({window}) *nvim_win_get_cursor()* - Gets the (1,0)-indexed cursor position in the window. - |api-indexing| +nvim_win_get_buf({window}) *nvim_win_get_buf()* + Gets the current buffer in a window Parameters: ~ {window} Window handle, or 0 for current window Return: ~ - (row, col) tuple + Buffer handle -nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()* - Sets the (1,0)-indexed cursor position in the window. - |api-indexing| +nvim_win_get_config({window}) *nvim_win_get_config()* + Gets window configuration. - Parameters: ~ - {window} Window handle, or 0 for current window - {pos} (row, col) tuple representing the new position + The returned value may be given to |nvim_open_win()|. -nvim_win_get_height({window}) *nvim_win_get_height()* - Gets the window height + `relative` is empty for normal windows. Parameters: ~ {window} Window handle, or 0 for current window Return: ~ - Height as a count of rows - -nvim_win_set_height({window}, {height}) *nvim_win_set_height()* - Sets the window height. This will only succeed if the screen - is split horizontally. - - Parameters: ~ - {window} Window handle, or 0 for current window - {height} Height as a count of rows + Map defining the window configuration, see + |nvim_open_win()| -nvim_win_get_width({window}) *nvim_win_get_width()* - Gets the window width +nvim_win_get_cursor({window}) *nvim_win_get_cursor()* + Gets the (1,0)-indexed cursor position in the window. + |api-indexing| Parameters: ~ {window} Window handle, or 0 for current window Return: ~ - Width as a count of columns - -nvim_win_set_width({window}, {width}) *nvim_win_set_width()* - Sets the window width. This will only succeed if the screen is - split vertically. - - Parameters: ~ - {window} Window handle, or 0 for current window - {width} Width as a count of columns + (row, col) tuple -nvim_win_get_var({window}, {name}) *nvim_win_get_var()* - Gets a window-scoped (w:) variable +nvim_win_get_height({window}) *nvim_win_get_height()* + Gets the window height Parameters: ~ {window} Window handle, or 0 for current window - {name} Variable name Return: ~ - Variable value + Height as a count of rows -nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()* - Sets a window-scoped (w:) variable +nvim_win_get_number({window}) *nvim_win_get_number()* + Gets the window number Parameters: ~ {window} Window handle, or 0 for current window - {name} Variable name - {value} Variable value - -nvim_win_del_var({window}, {name}) *nvim_win_del_var()* - Removes a window-scoped (w:) variable - Parameters: ~ - {window} Window handle, or 0 for current window - {name} Variable name + Return: ~ + Window number nvim_win_get_option({window}, {name}) *nvim_win_get_option()* Gets a window option value @@ -1931,15 +2173,6 @@ nvim_win_get_option({window}, {name}) *nvim_win_get_option()* Return: ~ Option value -nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()* - Sets a window option value. Passing 'nil' as value deletes the - option(only works if there's a global fallback) - - Parameters: ~ - {window} Window handle, or 0 for current window - {name} Option name - {value} Option value - nvim_win_get_position({window}) *nvim_win_get_position()* Gets the window position in display cells. First position is zero. @@ -1959,14 +2192,24 @@ nvim_win_get_tabpage({window}) *nvim_win_get_tabpage()* Return: ~ Tabpage that contains the window -nvim_win_get_number({window}) *nvim_win_get_number()* - Gets the window number +nvim_win_get_var({window}, {name}) *nvim_win_get_var()* + Gets a window-scoped (w:) variable Parameters: ~ {window} Window handle, or 0 for current window + {name} Variable name Return: ~ - Window number + Variable value + +nvim_win_get_width({window}) *nvim_win_get_width()* + Gets the window width + + Parameters: ~ + {window} Window handle, or 0 for current window + + Return: ~ + Width as a count of columns nvim_win_is_valid({window}) *nvim_win_is_valid()* Checks if a window is valid @@ -1977,6 +2220,13 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()* Return: ~ true if the window is valid, false otherwise +nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()* + Sets the current buffer in a window, without side-effects + + Parameters: ~ + {window} Window handle, or 0 for current window + {buffer} Buffer handle + nvim_win_set_config({window}, {config}) *nvim_win_set_config()* Configures window layout. Currently only for floating and external windows (including changing a split window to those @@ -1994,67 +2244,76 @@ nvim_win_set_config({window}, {config}) *nvim_win_set_config()* See also: ~ |nvim_open_win()| -nvim_win_get_config({window}) *nvim_win_get_config()* - Gets window configuration. +nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()* + Sets the (1,0)-indexed cursor position in the window. + |api-indexing| - The returned value may be given to |nvim_open_win()|. + Parameters: ~ + {window} Window handle, or 0 for current window + {pos} (row, col) tuple representing the new position - `relative` is empty for normal windows. +nvim_win_set_height({window}, {height}) *nvim_win_set_height()* + Sets the window height. This will only succeed if the screen + is split horizontally. Parameters: ~ {window} Window handle, or 0 for current window + {height} Height as a count of rows - Return: ~ - Map defining the window configuration, see - |nvim_open_win()| +nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()* + Sets a window option value. Passing 'nil' as value deletes the + option(only works if there's a global fallback) -nvim_win_close({window}, {force}) *nvim_win_close()* - Closes the window (like |:close| with a |window-ID|). + Parameters: ~ + {window} Window handle, or 0 for current window + {name} Option name + {value} Option value + +nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()* + Sets a window-scoped (w:) variable Parameters: ~ {window} Window handle, or 0 for current window - {force} Behave like `:close!` The last window of a - buffer with unwritten changes can be closed. The - buffer will become hidden, even if 'hidden' is - not set. + {name} Variable name + {value} Variable value + +nvim_win_set_width({window}, {width}) *nvim_win_set_width()* + Sets the window width. This will only succeed if the screen is + split vertically. + + Parameters: ~ + {window} Window handle, or 0 for current window + {width} Width as a count of columns ============================================================================== Tabpage Functions *api-tabpage* -nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()* - Gets the windows in a tabpage +nvim_tabpage_del_var({tabpage}, {name}) *nvim_tabpage_del_var()* + Removes a tab-scoped (t:) variable Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage + {name} Variable name - Return: ~ - List of windows in `tabpage` - -nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()* - Gets a tab-scoped (t:) variable +nvim_tabpage_get_number({tabpage}) *nvim_tabpage_get_number()* + Gets the tabpage number Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage - {name} Variable name Return: ~ - Variable value + Tabpage number -nvim_tabpage_set_var({tabpage}, {name}, {value}) *nvim_tabpage_set_var()* - Sets a tab-scoped (t:) variable +nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()* + Gets a tab-scoped (t:) variable Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage {name} Variable name - {value} Variable value - -nvim_tabpage_del_var({tabpage}, {name}) *nvim_tabpage_del_var()* - Removes a tab-scoped (t:) variable - Parameters: ~ - {tabpage} Tabpage handle, or 0 for current tabpage - {name} Variable name + Return: ~ + Variable value nvim_tabpage_get_win({tabpage}) *nvim_tabpage_get_win()* Gets the current window in a tabpage @@ -2065,23 +2324,32 @@ nvim_tabpage_get_win({tabpage}) *nvim_tabpage_get_win()* Return: ~ Window handle -nvim_tabpage_get_number({tabpage}) *nvim_tabpage_get_number()* - Gets the tabpage number +nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()* + Checks if a tabpage is valid Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage Return: ~ - Tabpage number + true if the tabpage is valid, false otherwise -nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()* - Checks if a tabpage is valid +nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()* + Gets the windows in a tabpage Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage Return: ~ - true if the tabpage is valid, false otherwise + List of windows in `tabpage` + + *nvim_tabpage_set_var()* +nvim_tabpage_set_var({tabpage}, {name}, {value}) + Sets a tab-scoped (t:) variable + + Parameters: ~ + {tabpage} Tabpage handle, or 0 for current tabpage + {name} Variable name + {value} Variable value ============================================================================== @@ -2110,12 +2378,38 @@ nvim_ui_detach() *nvim_ui_detach()* Removes the client from the list of UIs. |nvim_list_uis()| -nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()* - TODO: Documentation + *nvim_ui_pum_set_bounds()* +nvim_ui_pum_set_bounds({width}, {height}, {row}, {col}) + Tells Nvim the geometry of the popumenu, to align floating + windows with an external popup menu. + + Note that this method is not to be confused with + |nvim_ui_pum_set_height()|, which sets the number of visible + items in the popup menu, while this function sets the bounding + box of the popup menu, including visual decorations such as + boarders and sliders. Floats need not use the same font size, + nor be anchored to exact grid corners, so one can set + floating-point numbers to the popup menu geometry. + + Parameters: ~ + {width} Popupmenu width. + {height} Popupmenu height. + {row} Popupmenu row. + {col} Popupmenu height. + +nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()* + Tells Nvim the number of elements displaying in the popumenu, + to decide <PageUp> and <PageDown> movement. + + Parameters: ~ + {height} Popupmenu height, must be greater than zero. nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()* TODO: Documentation +nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()* + TODO: Documentation + *nvim_ui_try_resize_grid()* nvim_ui_try_resize_grid({grid}, {width}, {height}) Tell Nvim to resize a grid. Triggers a grid_resize event with @@ -2129,11 +2423,4 @@ nvim_ui_try_resize_grid({grid}, {width}, {height}) {width} The new requested width. {height} The new requested height. -nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()* - Tells Nvim the number of elements displaying in the popumenu, - to decide <PageUp> and <PageDown> movement. - - Parameters: ~ - {height} Popupmenu height, must be greater than zero. - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 34ea083f96..f1753b75cc 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -251,7 +251,6 @@ Name triggered by ~ Buffers |BufAdd| just after adding a buffer to the buffer list -|BufCreate| just after adding a buffer to the buffer list |BufDelete| before deleting a buffer from the buffer list |BufWipeout| before completely deleting a buffer @@ -318,6 +317,7 @@ Name triggered by ~ |CursorMoved| the cursor was moved in Normal mode |CursorMovedI| the cursor was moved in Insert mode +|WinClosed| after closing a window |WinNew| after creating a new window |WinEnter| after entering another window |WinLeave| before leaving a window @@ -358,7 +358,10 @@ Name triggered by ~ |MenuPopup| just before showing the popup menu |CompleteChanged| after popup menu changed, not fired on popup menu hide -|CompleteDone| after Insert mode completion is done +|CompleteDonePre| after Insert mode completion is done, before clearing + info +|CompleteDone| after Insert mode completion is done, after clearing + info |User| to be used in combination with ":doautocmd" |Signal| after Nvim receives a signal @@ -367,32 +370,29 @@ Name triggered by ~ The alphabetical list of autocommand events: *autocmd-events-abc* - *BufCreate* *BufAdd* -BufAdd or BufCreate Just after creating a new buffer which is + *BufAdd* +BufAdd Just after creating a new buffer which is added to the buffer list, or adding a buffer - to the buffer list. - Also used just after a buffer in the buffer - list has been renamed. - The BufCreate event is for historic reasons. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being created "<afile>". + to the buffer list, a buffer in the buffer + list was renamed. + Before |BufEnter|. + NOTE: Current buffer "%" may be different from + the buffer being created "<afile>". *BufDelete* BufDelete Before deleting a buffer from the buffer list. The BufUnload may be called first (if the buffer was loaded). Also used just before a buffer in the buffer list is renamed. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being deleted "<afile>" and "<abuf>". - Don't change to another buffer, it will cause - problems. + NOTE: Current buffer "%" may be different from + the buffer being deleted "<afile>" and "<abuf>". + Do not change to another buffer. *BufEnter* BufEnter After entering a buffer. Useful for setting options for a file type. Also executed when starting to edit a buffer, after the - BufReadPost autocommands. + After |BufAdd|. + After |BufReadPost|. *BufFilePost* BufFilePost After changing the name of the current buffer with the ":file" or ":saveas" command. @@ -400,27 +400,26 @@ BufFilePost After changing the name of the current buffer BufFilePre Before changing the name of the current buffer with the ":file" or ":saveas" command. *BufHidden* -BufHidden Just before a buffer becomes hidden. That is, - when there are no longer windows that show - the buffer, but the buffer is not unloaded or - deleted. Not used for ":qa" or ":q" when - exiting Vim. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being unloaded "<afile>". +BufHidden Before a buffer becomes hidden: when there are + no longer windows that show the buffer, but + the buffer is not unloaded or deleted. + + Not used for ":qa" or ":q" when exiting Vim. + NOTE: current buffer "%" may be different from + the buffer being unloaded "<afile>". *BufLeave* BufLeave Before leaving to another buffer. Also when leaving or closing the current window and the new current window is not for the same buffer. + Not used for ":qa" or ":q" when exiting Vim. *BufNew* BufNew Just after creating a new buffer. Also used just after a buffer has been renamed. When the buffer is added to the buffer list BufAdd will be triggered too. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being created "<afile>". + NOTE: Current buffer "%" may be different from + the buffer being created "<afile>". *BufNewFile* BufNewFile When starting to edit a file that doesn't exist. Can be used to read in a skeleton @@ -428,16 +427,17 @@ BufNewFile When starting to edit a file that doesn't *BufRead* *BufReadPost* BufRead or BufReadPost When starting to edit a new buffer, after reading the file into the buffer, before - executing the modelines. See |BufWinEnter| - for when you need to do something after - processing the modelines. - This does NOT work for ":r file". Not used - when the file doesn't exist. Also used after - successfully recovering a file. - Also triggered for the filetypedetect group - when executing ":filetype detect" and when - writing an unnamed buffer in a way that the - buffer gets a name. + processing modelines. See |BufWinEnter| to do + something after processing modelines. + Also triggered: + - when writing an unnamed buffer such that the + buffer gets a name + - after successfully recovering a file + - for the "filetypedetect" group when + executing ":filetype detect" + Not triggered: + - for ":r file" + - if the file doesn't exist *BufReadCmd* BufReadCmd Before starting to edit a new buffer. Should read the file into the buffer. |Cmd-event| @@ -446,41 +446,37 @@ BufReadPre When starting to edit a new buffer, before reading the file into the buffer. Not used if the file doesn't exist. *BufUnload* -BufUnload Before unloading a buffer. This is when the - text in the buffer is going to be freed. This - may be after a BufWritePost and before a - BufDelete. Also used for all buffers that are - loaded when Vim is going to exit. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being unloaded "<afile>". - Don't change to another buffer or window, it - will cause problems! - When exiting and v:dying is 2 or more this - event is not triggered. +BufUnload Before unloading a buffer, when the text in + the buffer is going to be freed. + After BufWritePost. + Before BufDelete. + Triggers for all loaded buffers when Vim is + going to exit. + NOTE: Current buffer "%" may be different from + the buffer being unloaded "<afile>". + Do not switch buffers or windows! + Not triggered when exiting and v:dying is 2 or + more. *BufWinEnter* BufWinEnter After a buffer is displayed in a window. This - can be when the buffer is loaded (after - processing the modelines) or when a hidden - buffer is displayed in a window (and is no - longer hidden). - Does not happen for |:split| without - arguments, since you keep editing the same - buffer, or ":split" with a file that's already - open in a window, because it re-uses an - existing buffer. But it does happen for a - ":split" with the name of the current buffer, - since it reloads that buffer. + may be when the buffer is loaded (after + processing modelines) or when a hidden buffer + is displayed (and is no longer hidden). + + Not triggered for |:split| without arguments, + since the buffer does not change, or :split + with a file already open in a window. + Triggered for ":split" with the name of the + current buffer, since it reloads that buffer. *BufWinLeave* BufWinLeave Before a buffer is removed from a window. Not when it's still visible in another window. - Also triggered when exiting. It's triggered - before BufUnload or BufHidden. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being unloaded "<afile>". - When exiting and v:dying is 2 or more this - event is not triggered. + Also triggered when exiting. + Before BufUnload, BufHidden. + NOTE: Current buffer "%" may be different from + the buffer being unloaded "<afile>". + Not triggered when exiting and v:dying is 2 or + more. *BufWipeout* BufWipeout Before completely deleting a buffer. The BufUnload and BufDelete events may be called @@ -488,11 +484,9 @@ BufWipeout Before completely deleting a buffer. The buffer list). Also used just before a buffer is renamed (also when it's not in the buffer list). - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being deleted "<afile>". - Don't change to another buffer, it will cause - problems. + NOTE: Current buffer "%" may be different from + the buffer being deleted "<afile>". + Do not change to another buffer. *BufWrite* *BufWritePre* BufWrite or BufWritePre Before writing the whole buffer to a file. *BufWriteCmd* @@ -526,7 +520,7 @@ CmdUndefined When a user command is used but it isn't defined. Useful for defining a command only when it's used. The pattern is matched against the command name. Both <amatch> and - <afile> are set to the name of the command. + <afile> expand to the command name. NOTE: Autocompletion won't work until the command is defined. An alternative is to always define the user command and have it @@ -535,12 +529,12 @@ CmdUndefined When a user command is used but it isn't CmdlineChanged After a change was made to the text inside command line. Be careful not to mess up the command line, it may cause Vim to lock up. - <afile> is set to the |cmdline-char|. + <afile> expands to the |cmdline-char|. *CmdlineEnter* CmdlineEnter After entering the command-line (including non-interactive use of ":" in a mapping: use |<Cmd>| instead to avoid this). - <afile> is set to the |cmdline-char|. + <afile> expands to the |cmdline-char|. Sets these |v:event| keys: cmdlevel cmdtype @@ -548,28 +542,26 @@ CmdlineEnter After entering the command-line (including CmdlineLeave Before leaving the command-line (including non-interactive use of ":" in a mapping: use |<Cmd>| instead to avoid this). - <afile> is set to the |cmdline-char|. + <afile> expands to the |cmdline-char|. Sets these |v:event| keys: abort (mutable) cmdlevel cmdtype Note: `abort` can only be changed from false - to true. An autocmd cannot execute an already - aborted cmdline by changing it to false. + to true: cannot execute an already aborted + cmdline by changing it to false. *CmdwinEnter* CmdwinEnter After entering the command-line window. Useful for setting options specifically for - this special type of window. This is - triggered _instead_ of BufEnter and WinEnter. - <afile> is set to a single character, + this special type of window. + <afile> expands to a single character, indicating the type of command-line. |cmdwin-char| *CmdwinLeave* CmdwinLeave Before leaving the command-line window. Useful to clean up any global setting done - with CmdwinEnter. This is triggered _instead_ - of BufLeave and WinLeave. - <afile> is set to a single character, + with CmdwinEnter. + <afile> expands to a single character, indicating the type of command-line. |cmdwin-char| *ColorScheme* @@ -585,18 +577,11 @@ ColorSchemePre Before loading a color scheme. |:colorscheme| Useful to setup removing things added by a color scheme, before another one is loaded. - *CompleteDone* -CompleteDone After Insert mode completion is done. Either - when something was completed or abandoning - completion. |ins-completion| - The |v:completed_item| variable contains the - completed item. - CompleteChanged *CompleteChanged* After each time the Insert mode completion menu changed. Not fired on popup menu hide, - use |CompleteDone| for that. Never triggered - recursively. + use |CompleteDonePre| or |CompleteDone| for + that. Sets these |v:event| keys: completed_item See |complete-items|. @@ -607,7 +592,29 @@ CompleteChanged *CompleteChanged* size total nr of items scrollbar TRUE if visible - It is not allowed to change the text |textlock|. + Non-recursive (event cannot trigger itself). + Cannot change the text. |textlock| + + The size and position of the popup are also + available by calling |pum_getpos()|. + + *CompleteDonePre* +CompleteDonePre After Insert mode completion is done. Either + when something was completed or abandoning + completion. |ins-completion| + |complete_info()| can be used, the info is + cleared after triggering CompleteDonePre. + The |v:completed_item| variable contains + information about the completed item. + + *CompleteDone* +CompleteDone After Insert mode completion is done. Either + when something was completed or abandoning + completion. |ins-completion| + |complete_info()| cannot be used, the info is + cleared before triggering CompleteDone. Use + CompleteDonePre if you need it. + |v:completed_item| gives the completed item. *CursorHold* CursorHold When the user doesn't press a key for the time @@ -637,9 +644,9 @@ CursorHold When the user doesn't press a key for the time :let &ro = &ro *CursorHoldI* -CursorHoldI Just like CursorHold, but in Insert mode. - Not triggered when waiting for another key, - e.g. after CTRL-V, and not when in CTRL-X mode +CursorHoldI Like CursorHold, but in Insert mode. Not + triggered when waiting for another key, e.g. + after CTRL-V, and not in CTRL-X mode |insert_expand|. *CursorMoved* @@ -650,7 +657,7 @@ CursorMoved After the cursor was moved in Normal or Visual Not triggered when there is typeahead or when an operator is pending. For an example see |match-parens|. - Note: Cannot be skipped with `:noautocmd`. + Note: Cannot be skipped with |:noautocmd|. Careful: This is triggered very often, don't do anything that the user does not expect or that is slow. @@ -668,11 +675,11 @@ DirChanged After the |current-directory| was changed. Sets these |v:event| keys: cwd: current working directory scope: "global", "tab", "window" - Recursion is ignored. + Non-recursive (event cannot trigger itself). *FileAppendCmd* FileAppendCmd Before appending to a file. Should do the appending to the file. Use the '[ and '] - marks for the range of lines.|Cmd-event| + marks for the range of lines. |Cmd-event| *FileAppendPost* FileAppendPost After appending to a file. *FileAppendPre* @@ -680,19 +687,18 @@ FileAppendPre Before appending to a file. Use the '[ and '] marks for the range of lines. *FileChangedRO* FileChangedRO Before making the first change to a read-only - file. Can be used to check-out the file from + file. Can be used to checkout the file from a source control system. Not triggered when the change was caused by an autocommand. - This event is triggered when making the first - change in a buffer or the first change after - 'readonly' was set, just before the change is - applied to the text. + Triggered when making the first change in + a buffer or the first change after 'readonly' + was set, just before the change is applied to + the text. WARNING: If the autocommand moves the cursor the effect of the change is undefined. *E788* - It is not allowed to change to another buffer - here. You can reload the buffer but not edit - another one. + Cannot switch buffers. You can reload the + buffer but not edit another one. *E881* If the number of lines changes saving for undo may fail and the change will be aborted. @@ -704,34 +710,28 @@ ExitPre When using `:quit`, `:wq` in a way it makes cancelled if there is a modified buffer that isn't automatically saved, use |VimLeavePre| for really exiting. + See also |QuitPre|, |WinClosed|. *FileChangedShell* FileChangedShell When Vim notices that the modification time of a file has changed since editing started. Also when the file attributes of the file change or when the size of the file changes. |timestamp| - Mostly triggered after executing a shell - command, but also with a |:checktime| command - or when gvim regains input focus. - This autocommand is triggered for each changed - file. It is not used when 'autoread' is set - and the buffer was not changed. If a - FileChangedShell autocommand is present the - warning message and prompt is not given. - The |v:fcs_reason| variable is set to indicate - what happened and |v:fcs_choice| can be used - to tell Vim what to do next. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer that was changed, which is in "<afile>". - NOTE: The commands must not change the current - buffer, jump to another buffer or delete a - buffer. *E246* *E811* - NOTE: This event never nests, to avoid an - endless loop. This means that while executing - commands for the FileChangedShell event no - other FileChangedShell event will be - triggered. + Triggered for each changed file, after: + - executing a shell command + - |:checktime| + - |FocusGained| + Not used when 'autoread' is set and the buffer + was not changed. If a FileChangedShell + autocommand exists the warning message and + prompt is not given. + |v:fcs_reason| indicates what happened. Set + |v:fcs_choice| to control what happens next. + NOTE: Current buffer "%" may be different from + the buffer that was changed "<afile>". + *E246* *E811* + Cannot switch, jump to or delete buffers. + Non-recursive (event cannot trigger itself). *FileChangedShellPost* FileChangedShellPost After handling a file that was changed outside of Vim. Can be used to update the statusline. @@ -748,10 +748,10 @@ FileReadPre Before reading a file with a ":read" command. *FileType* FileType When the 'filetype' option has been set. The pattern is matched against the filetype. - <afile> can be used for the name of the file - where this option was set, and <amatch> for - the new value of 'filetype'. Navigating to - another window or buffer is not allowed. + <afile> is the name of the file where this + option was set. <amatch> is the new value of + 'filetype'. + Cannot switch windows or buffers. See |filetypes|. *FileWriteCmd* FileWriteCmd Before writing to a file, when not writing the @@ -844,11 +844,12 @@ TextYankPost Just after a |yank| or |deleting| command, but not regcontents regname regtype + visual The `inclusive` flag combined with the |'[| and |']| marks can be used to calculate the precise region of the operation. - Recursion is ignored. + Non-recursive (event cannot trigger itself). Cannot change the text. |textlock| *InsertEnter* InsertEnter Just before starting Insert mode. Also for @@ -915,8 +916,8 @@ OptionSet After setting an option (except during always use |:noautocmd| to prevent triggering OptionSet. - Recursion is ignored, thus |:set| in the - autocommand does not trigger OptionSet again. + Non-recursive: |:set| in the autocommand does + not trigger OptionSet again. *QuickFixCmdPre* QuickFixCmdPre Before a quickfix command is run (|:make|, @@ -948,7 +949,7 @@ QuitPre When using `:quit`, `:wq` or `:qall`, before or quits Vim. Can be used to close any non-essential window if the current window is the last ordinary window. - Also see |ExitPre|. + See also |ExitPre|, ||WinClosed|. *RemoteReply* RemoteReply When a reply from a Vim that functions as server was received |server2client()|. The @@ -1024,33 +1025,32 @@ SwapExists Detected an existing swap file when starting When set to an empty string the user will be asked, as if there was no SwapExists autocmd. *E812* - It is not allowed to change to another buffer, - change a buffer name or change directory - here. + Cannot change to another buffer, change + the buffer name or change directory. *Syntax* Syntax When the 'syntax' option has been set. The pattern is matched against the syntax name. - <afile> can be used for the name of the file - where this option was set, and <amatch> for - the new value of 'syntax'. + <afile> expands to the name of the file where + this option was set. <amatch> expands to the + new value of 'syntax'. See |:syn-on|. *TabEnter* TabEnter Just after entering a tab page. |tab-page| - After triggering the WinEnter and before - triggering the BufEnter event. + After WinEnter. + Before BufEnter. *TabLeave* TabLeave Just before leaving a tab page. |tab-page| - A WinLeave event will have been triggered - first. + After WinLeave. *TabNew* TabNew When creating a new tab page. |tab-page| - After WinEnter and before TabEnter. + After WinEnter. + Before TabEnter. *TabNewEntered* TabNewEntered After entering a new tab page. |tab-page| After BufEnter. *TabClosed* -TabClosed After closing a tab page. <afile> can be used - for the tab page number. +TabClosed After closing a tab page. <afile> expands to + the tab page number. *TermOpen* TermOpen When a |terminal| job is starting. Can be used to configure the terminal buffer. @@ -1066,10 +1066,9 @@ TermClose When a |terminal| job ends. TermResponse After the response to t_RV is received from the terminal. The value of |v:termresponse| can be used to do things depending on the - terminal version. Note that this event may be - triggered halfway through another event - (especially if file I/O, a shell command, or - anything else that takes time is involved). + terminal version. May be triggered halfway + through another event (file I/O, a shell + command, or anything else that takes time). *TextChanged* TextChanged After a change was made to the text in the current buffer in Normal mode. That is after @@ -1139,6 +1138,12 @@ VimResized After the Vim window was resized, thus 'lines' VimResume After Nvim resumes from |suspend| state. *VimSuspend* VimSuspend Before Nvim enters |suspend| state. + *WinClosed* +WinClosed After closing a window. <afile> expands to the + |window-ID|. + After WinLeave. + Non-recursive (event cannot trigger itself). + See also |ExitPre|, |QuitPre|. *WinEnter* WinEnter After entering another window. Not done for the first window, when Vim has just started. @@ -1156,11 +1161,11 @@ WinLeave Before leaving a window. If the window to be executes the BufLeave autocommands before the WinLeave autocommands (but not for ":new"). Not used for ":qa" or ":q" when exiting Vim. - + After WinClosed. *WinNew* WinNew When a new window was created. Not done for the first window, when Vim has just started. - Before a WinEnter event. + Before WinEnter. ============================================================================== 6. Patterns *autocmd-pattern* *{pat}* diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index bd3f22a371..dcebbc524c 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -90,7 +90,7 @@ start and end of the motion are not in the same line, and there are only blanks before the start and there are no non-blanks after the end of the motion, the delete becomes linewise. This means that the delete also removes the line of blanks that you might expect to remain. Use the |o_v| operator to -force the motion to be characterwise. +force the motion to be charwise. Trying to delete an empty region of text (e.g., "d0" in the first column) is an error when 'cpoptions' includes the 'E' flag. @@ -1074,7 +1074,7 @@ also use these commands to move text from one file to another, because Vim preserves all registers when changing buffers (the CTRL-^ command is a quick way to toggle between two files). - *linewise-register* *characterwise-register* + *linewise-register* *charwise-register* You can repeat the put commands with "." (except for :put) and undo them. If the command that was used to get the text into the register was |linewise|, Vim inserts the text below ("p") or above ("P") the line where the cursor is. @@ -1116,10 +1116,9 @@ this happen. However, if the width of the block is not a multiple of a <Tab> width and the text after the inserted block contains <Tab>s, that text may be misaligned. -Note that after a characterwise yank command, Vim leaves the cursor on the -first yanked character that is closest to the start of the buffer. This means -that "yl" doesn't move the cursor, but "yh" moves the cursor one character -left. +Note that after a charwise yank command, Vim leaves the cursor on the first +yanked character that is closest to the start of the buffer. This means that +"yl" doesn't move the cursor, but "yh" moves the cursor one character left. Rationale: In Vi the "y" command followed by a backwards motion would sometimes not move the cursor to the first yanked character, because redisplaying was skipped. In Vim it always moves to diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index ee1f76e4e4..b31177ce0e 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1122,11 +1122,9 @@ edited as described in |cmdwin-char|. AUTOCOMMANDS -Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. Since this -window is of a special type, the WinEnter, WinLeave, BufEnter and BufLeave -events are not triggered. You can use the Cmdwin events to do settings -specifically for the command-line window. Be careful not to cause side -effects! +Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. You can use +the Cmdwin events to do settings specifically for the command-line window. +Be careful not to cause side effects! Example: > :au CmdwinEnter : let b:cpt_save = &cpt | set cpt=. :au CmdwinLeave : let &cpt = b:cpt_save diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index b76a37810c..ce075e1bee 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -14,6 +14,8 @@ updated. API ~ *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. +*nvim_command_output()* Use |nvim_exec()| instead. +*nvim_execute_lua()* Use |nvim_exec_lua()| instead. Commands ~ *:rv* @@ -26,6 +28,7 @@ Environment Variables ~ $NVIM_LISTEN_ADDRESS is ignored. Events ~ +*BufCreate* Use |BufAdd| instead. *EncodingChanged* Never fired; 'encoding' is always "utf-8". *FileEncoding* Never fired; equivalent to |EncodingChanged|. *GUIEnter* Never fired; use |UIEnter| instead. diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 90c2e30771..09c5b7c4ad 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -143,6 +143,87 @@ DOCUMENTATION *dev-doc* /// @param dirname The path fragment before `pend` < +C docstrings ~ + +Nvim API documentation lives in the source code, as docstrings (Doxygen +comments) on the function definitions. The |api| :help is generated +from the docstrings defined in src/nvim/api/*.c. + +Docstring format: +- Lines start with `///` +- Special tokens start with `@` followed by the token name: + `@note`, `@param`, `@returns` +- Limited markdown is supported. + - List-items start with `-` (useful to nest or "indent") +- Use `<pre>` for code samples. + +Example: the help for |nvim_open_win()| is generated from a docstring defined +in src/nvim/api/vim.c like this: > + + /// Opens a new window. + /// ... + /// + /// Example (Lua): window-relative float + /// <pre> + /// vim.api.nvim_open_win(0, false, + /// {relative='win', row=3, col=3, width=12, height=3}) + /// </pre> + /// + /// @param buffer Buffer to display + /// @param enter Enter the window + /// @param config Map defining the window configuration. Keys: + /// - relative: Sets the window layout, relative to: + /// - "editor" The global editor grid. + /// - "win" Window given by the `win` field. + /// - "cursor" Cursor position in current window. + /// ... + /// @param[out] err Error details, if any + /// + /// @return Window handle, or 0 on error + + +Lua docstrings ~ + *dev-lua-doc* +Lua documentation lives in the source code, as docstrings on the function +definitions. The |lua-vim| :help is generated from the docstrings. + +Docstring format: +- Lines in the main description start with `---` +- Special tokens start with `--@` followed by the token name: + `--@see`, `--@param`, `--@returns` +- Limited markdown is supported. + - List-items start with `-` (useful to nest or "indent") +- Use `<pre>` for code samples. + +Example: the help for |vim.paste()| is generated from a docstring decorating +vim.paste in src/nvim/lua/vim.lua like this: > + + --- Paste handler, invoked by |nvim_paste()| when a conforming UI + --- (such as the |TUI|) pastes text into the editor. + --- + --- Example: To remove ANSI color codes when pasting: + --- <pre> + --- vim.paste = (function() + --- local overridden = vim.paste + --- ... + --- end)() + --- </pre> + --- + --@see |paste| + --- + --@param lines ... + --@param phase ... + --@returns false if client should cancel the paste. + + +LUA *dev-lua* + +- Keep the core Lua modules |lua-stdlib| simple. Avoid elaborate OOP or + pseudo-OOP designs. Plugin authors just want functions to call, they don't + want to learn a big, fancy inheritance hierarchy. So we should avoid complex + objects: tables are usually better. + + API *dev-api* Use this template to name new API functions: @@ -155,10 +236,11 @@ with a {thing} that groups functions under a common concept). Use existing common {action} names if possible: add Append to, or insert into, a collection - get Get a thing (or group of things by query) - set Set a thing (or group of things) del Delete a thing (or group of things) + exec Execute code + get Get a thing (or group of things by query) list Get all things + set Set a thing (or group of things) Use consistent names for {thing} in all API functions. E.g. a buffer is called "buf" everywhere, not "buffer" in some places and "buf" in others. @@ -268,8 +350,8 @@ External UIs are expected to implement these common features: chords (<C-,> <C-Enter> <C-S-x> <D-x>) and patterns ("shift shift") that do not potentially conflict with Nvim defaults, plugins, etc. - Consider the "option_set" |ui-global| event as a hint for other GUI - behaviors. UI-related options ('guifont', 'ambiwidth', …) are published in - this event. + behaviors. Various UI-related options ('guifont', 'ambiwidth', …) are + published in this event. See also "mouse_on", "mouse_off". vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt index b106e625f2..7f807b5eee 100644 --- a/runtime/doc/digraph.txt +++ b/runtime/doc/digraph.txt @@ -20,7 +20,9 @@ An alternative is using the 'keymap' option. 1. Defining digraphs *digraphs-define* *:dig* *:digraphs* -:dig[raphs] show currently defined digraphs. +:dig[raphs][!] Show currently defined digraphs. + With [!] headers are used to make it a bit easier to + find a specific character. *E104* *E39* :dig[raphs] {char1}{char2} {number} ... Add digraph {char1}{char2} to the list. {number} is diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 23a65f16e4..ac398ec494 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1265,7 +1265,7 @@ exist, the next-higher scope in the hierarchy applies. other tabs and windows is not changed. *:tcd-* -:tcd[!] - Change to the previous current directory (before the +:tc[d][!] - Change to the previous current directory (before the previous ":tcd {path}" command). *:tch* *:tchdir* @@ -1280,7 +1280,7 @@ exist, the next-higher scope in the hierarchy applies. :lch[dir][!] Same as |:lcd|. *:lcd-* -:lcd[!] - Change to the previous current directory (before the +:lc[d][!] - Change to the previous current directory (before the previous ":lcd {path}" command). *:pw* *:pwd* *E187* diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 512cfc4e58..efb6272e58 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1217,7 +1217,7 @@ lambda expression *expr-lambda* *lambda* {args -> expr1} lambda expression A lambda expression creates a new unnamed function which returns the result of -evaluating |expr1|. Lambda expressions differ from |user-functions| in +evaluating |expr1|. Lambda expressions differ from |user-function|s in the following ways: 1. The body of the lambda expression is an |expr1| and not a sequence of |Ex| @@ -1423,6 +1423,10 @@ PREDEFINED VIM VARIABLES *vim-variable* *v:var* *v:* *E963* Some variables can be set by the user, but the type cannot be changed. + *v:argv* *argv-variable* +v:argv The command line arguments Vim was invoked with. This is a + list of strings. The first item is the Vim command. + *v:beval_col* *beval_col-variable* v:beval_col The number of the column, over which the mouse pointer is. This is the byte index in the |v:beval_lnum| line. @@ -1547,10 +1551,12 @@ v:errmsg Last given error message. :if v:errmsg != "" : ... handle error < - *v:errors* *errors-variable* + *v:errors* *errors-variable* *assert-return* v:errors Errors found by assert functions, such as |assert_true()|. This is a list of strings. The assert functions append an item when an assert fails. + The return value indicates this: a one is returned if an item + was added to v:errors, otherwise zero is returned. To remove old results make it empty: > :let v:errors = [] < If v:errors is set to anything but a list it is made an empty @@ -1585,6 +1591,8 @@ v:event Dictionary of event data for the current |autocommand|. Valid operation. regtype Type of register as returned by |getregtype()|. + visual Selection is visual (as opposed to, + e.g., via motion). completed_item Current selected complete item on |CompleteChanged|, Is `{}` when no complete item selected. @@ -1735,6 +1743,10 @@ v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and expressions is being evaluated. Read-only when in the |sandbox|. + *v:lua* *lua-variable* +v:lua Prefix for calling Lua functions from expressions. + See |v:lua-call| for more information. + *v:mouse_win* *mouse_win-variable* v:mouse_win Window number for a mouse click obtained with |getchar()|. First window has number 1, like with |winnr()|. The value is @@ -1984,9 +1996,12 @@ v:windowid Application-specific window "handle" which may be set by any |window-ID|. ============================================================================== -4. Builtin Functions *functions* +4. Builtin Functions *vim-function* *functions* + +The Vimscript subsystem (referred to as "eval" internally) provides the +following builtin functions. Scripts can also define |user-function|s. -See |function-list| for a list grouped by what the function is used for. +See |function-list| to browse functions by topic. (Use CTRL-] on the function name to jump to the full explanation.) @@ -2004,25 +2019,27 @@ argidx() Number current index in the argument list arglistid([{winnr} [, {tabnr}]]) Number argument list id argv({nr} [, {winid}]) String {nr} entry of the argument list argv([-1, {winid}]) List the argument list -assert_beeps({cmd}) none assert {cmd} causes a beep +asin({expr}) Float arc sine of {expr} +assert_beeps({cmd}) Number assert {cmd} causes a beep assert_equal({exp}, {act} [, {msg}]) - none assert {exp} is equal to {act} + Number assert {exp} is equal to {act} +assert_equalfile({fname-one}, {fname-two} [, {msg}]) + Number assert file contents are equal assert_exception({error} [, {msg}]) - none assert {error} is in v:exception -assert_fails({cmd} [, {error}]) none assert {cmd} fails + Number assert {error} is in v:exception +assert_fails({cmd} [, {error}]) Number assert {cmd} fails assert_false({actual} [, {msg}]) - none assert {actual} is false + Number assert {actual} is false assert_inrange({lower}, {upper}, {actual} [, {msg}]) - none assert {actual} is inside the range + Number assert {actual} is inside the range assert_match({pat}, {text} [, {msg}]) - none assert {pat} matches {text} + Number assert {pat} matches {text} assert_notequal({exp}, {act} [, {msg}]) - none assert {exp} is not equal {act} + Number assert {exp} is not equal {act} assert_notmatch({pat}, {text} [, {msg}]) - none assert {pat} not matches {text} -assert_report({msg}) none report a test failure -assert_true({actual} [, {msg}]) none assert {actual} is true -asin({expr}) Float arc sine of {expr} + Number assert {pat} not matches {text} +assert_report({msg}) Number report a test failure +assert_true({actual} [, {msg}]) Number assert {actual} is true atan({expr}) Float arc tangent of {expr} atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2} browse({save}, {title}, {initdir}, {default}) @@ -2048,7 +2065,7 @@ chanclose({id}[, {stream}]) Number Closes a channel or one of its streams chansend({id}, {data}) Number Writes {data} to channel char2nr({expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} cindent({lnum}) Number C indent for line {lnum} -clearmatches() none clear all matches +clearmatches([{win}]) none clear all matches col({expr}) Number column nr of cursor or mark complete({startcol}, {matches}) none set Insert mode completion complete_add({expr}) Number add completion match @@ -2073,6 +2090,7 @@ ctxsize() Number return |context-stack| size cursor({lnum}, {col} [, {off}]) Number move cursor to {lnum}, {col}, {off} cursor({list}) Number move cursor to position in {list} +debugbreak({pid}) Number interrupt process being debugged deepcopy({expr} [, {noref}]) any make a full copy of {expr} delete({fname} [, {flags}]) Number delete the file or directory {fname} deletebufline({expr}, {first}[, {last}]) @@ -2098,6 +2116,7 @@ extend({expr1}, {expr2} [, {expr3}]) exp({expr}) Float exponential of {expr} expand({expr} [, {nosuf} [, {list}]]) any expand special keywords in {expr} +expandcmd({expr}) String expand {expr} like with `:edit` feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer filereadable({file}) Number |TRUE| if {file} is a readable file filewritable({file}) Number |TRUE| if {file} is a writable file @@ -2107,6 +2126,7 @@ finddir({name} [, {path} [, {count}]]) String find directory {name} in {path} findfile({name} [, {path} [, {count}]]) String find file {name} in {path} +flatten({list} [, {maxdepth}]) List flatten {list} up to {maxdepth} levels float2nr({expr}) Number convert Float {expr} to a Number floor({expr}) Float round {expr} down fmod({expr1}, {expr2}) Float remainder of {expr1} / {expr2} @@ -2154,7 +2174,7 @@ getjumplist([{winnr} [, {tabnr}]]) getline({lnum}) String line {lnum} of current buffer getline({lnum}, {end}) List lines {lnum} to {end} of current buffer getloclist({nr} [, {what}]) List list of location list items -getmatches() List list of current matches +getmatches([{win}]) List list of current matches getpid() Number process ID of Vim getpos({expr}) List position of cursor, mark, etc. getqflist([{what}]) List list of quickfix items @@ -2228,6 +2248,7 @@ libcallnr({lib}, {func}, {arg}) Number idem, but return a Number line({expr}) Number line nr of cursor, last line or mark line2byte({lnum}) Number byte count of line {lnum} lispindent({lnum}) Number Lisp indent for line {lnum} +list2str({list} [, {utf8}]) String turn numbers in {list} into a String localtime() Number current time log({expr}) Float natural logarithm (base e) of {expr} log10({expr}) Float logarithm of Float {expr} to base 10 @@ -2245,7 +2266,7 @@ matchadd({group}, {pattern}[, {priority}[, {id}]]) matchaddpos({group}, {list}[, {priority}[, {id}]]) Number highlight positions with {group} matcharg({nr}) List arguments of |:match| -matchdelete({id}) Number delete match identified by {id} +matchdelete({id} [, {win}]) Number delete match identified by {id} matchend({expr}, {pat}[, {start}[, {count}]]) Number position where {pat} ends in {expr} matchlist({expr}, {pat}[, {start}[, {count}]]) @@ -2269,6 +2290,11 @@ pathshorten({expr}) String shorten directory names in a path pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} printf({fmt}, {expr1}...) String format text +prompt_addtext({buf}, {expr}) none add text to a prompt buffer +prompt_setcallback({buf}, {expr}) none set prompt callback function +prompt_setinterrupt({buf}, {text}) none set prompt interrupt function +prompt_setprompt({buf}, {text}) none set prompt text +pum_getpos() Dict position and size of pum if visible pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression py3eval({expr}) any evaluate |python3| expression @@ -2278,7 +2304,7 @@ range({expr} [, {max} [, {stride}]]) readdir({dir} [, {expr}]) List file names in {dir} selected by {expr} readfile({fname} [, {binary} [, {max}]]) List get list of lines from file {fname} -reg_executing() Number get the executing register name +reg_executing() String get the executing register name reg_recording() String get the recording register name reltime([{start} [, {end}]]) List get time value reltimefloat({time}) Float turn the time value into a Float @@ -2333,7 +2359,7 @@ setfperm({fname}, {mode} Number set {fname} file permissions to {mode} setline({lnum}, {line}) Number set line {lnum} to {line} setloclist({nr}, {list}[, {action}[, {what}]]) Number modify location list using {list} -setmatches({list}) Number restore a list of matches +setmatches({list} [, {win}]) Number restore a list of matches setpos({expr}, {list}) Number set the {expr} position to {list} setqflist({list}[, {action}[, {what}]] Number modify quickfix list using {list} @@ -2377,6 +2403,8 @@ sqrt({expr}) Float square root of {expr} stdioopen({dict}) Number open stdio in a headless instance. stdpath({what}) String/List returns the standard path(s) for {what} str2float({expr}) Float convert String to Float +str2list({expr} [, {utf8}]) List convert each character of {expr} to + ASCII/UTF8 value str2nr({expr} [, {base}]) Number convert String to Number strchars({expr} [, {skipcc}]) Number character length of the String {expr} strcharpart({str}, {start} [, {len}]) @@ -2508,6 +2536,9 @@ and({expr}, {expr}) *and()* api_info() *api_info()* Returns Dictionary of |api-metadata|. + View it in a nice human-readable format: > + :lua print(vim.inspect(vim.fn.api_info())) + append({lnum}, {text}) *append()* When {text} is a |List|: Append each item of the |List| as a text line below line {lnum} in the current buffer. @@ -2576,16 +2607,18 @@ argv([{nr} [, {winid}]) the whole |arglist| is returned. The {winid} argument specifies the window ID, see |argc()|. + For the Vim command line arguments see |v:argv|. assert_beeps({cmd}) *assert_beeps()* Run {cmd} and add an error message to |v:errors| if it does NOT produce a beep or visual bell. - Also see |assert_fails()|. + Also see |assert_fails()| and |assert-return|. *assert_equal()* assert_equal({expected}, {actual}, [, {msg}]) When {expected} and {actual} are not equal an error message is - added to |v:errors|. + added to |v:errors| and 1 is returned. Otherwise zero is + returned |assert-return|. There is no automatic conversion, the String "4" is different from the Number 4. And the number 4 is different from the Float 4.0. The value of 'ignorecase' is not used here, case @@ -2597,9 +2630,17 @@ assert_equal({expected}, {actual}, [, {msg}]) < Will result in a string to be added to |v:errors|: test.vim line 12: Expected 'foo' but got 'bar' ~ + *assert_equalfile()* +assert_equalfile({fname-one}, {fname-two} [, {msg}]) + When the files {fname-one} and {fname-two} do not contain + exactly the same text an error message is added to |v:errors|. + Also see |assert-return|. + When {fname-one} or {fname-two} does not exist the error will + mention that. + assert_exception({error} [, {msg}]) *assert_exception()* When v:exception does not contain the string {error} an error - message is added to |v:errors|. + message is added to |v:errors|. Also see |assert-return|. This can be used to assert that a command throws an exception. Using the error number, followed by a colon, avoids problems with translations: > @@ -2612,7 +2653,7 @@ assert_exception({error} [, {msg}]) *assert_exception()* assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()* Run {cmd} and add an error message to |v:errors| if it does - NOT produce an error. + NOT produce an error. Also see |assert-return|. When {error} is given it must match in |v:errmsg|. Note that beeping is not considered an error, and some failing commands only beep. Use |assert_beeps()| for those. @@ -2620,6 +2661,7 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()* assert_false({actual} [, {msg}]) *assert_false()* When {actual} is not false an error message is added to |v:errors|, like with |assert_equal()|. + Also see |assert-return|. A value is false when it is zero or |v:false|. When "{actual}" is not a number or |v:false| the assert fails. When {msg} is omitted an error in the form @@ -2636,7 +2678,7 @@ assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()* *assert_match()* assert_match({pattern}, {actual} [, {msg}]) When {pattern} does not match {actual} an error message is - added to |v:errors|. + added to |v:errors|. Also see |assert-return|. {pattern} is used as with |=~|: The matching is always done like 'magic' was set and 'cpoptions' is empty, no matter what @@ -2657,18 +2699,22 @@ assert_match({pattern}, {actual} [, {msg}]) assert_notequal({expected}, {actual} [, {msg}]) The opposite of `assert_equal()`: add an error message to |v:errors| when {expected} and {actual} are equal. + Also see |assert-return|. *assert_notmatch()* assert_notmatch({pattern}, {actual} [, {msg}]) The opposite of `assert_match()`: add an error message to |v:errors| when {pattern} matches {actual}. + Also see |assert-return|. assert_report({msg}) *assert_report()* Report a test failure directly, using {msg}. + Always returns one. assert_true({actual} [, {msg}]) *assert_true()* When {actual} is not true an error message is added to |v:errors|, like with |assert_equal()|. + Also see |assert-return|. A value is |TRUE| when it is a non-zero number or |v:true|. When {actual} is not a number or |v:true| the assert fails. When {msg} is omitted an error in the form "Expected True but @@ -2967,9 +3013,11 @@ cindent({lnum}) *cindent()* When {lnum} is invalid -1 is returned. See |C-indenting|. -clearmatches() *clearmatches()* - Clears all matches previously defined for the current window - by |matchadd()| and the |:match| commands. +clearmatches([{win}]) *clearmatches()* + Clears all matches previously defined for the current window + by |matchadd()| and the |:match| commands. + If {win} is specified, use the window with this number or + window ID instead of the current window. *col()* col({expr}) The result is a Number, which is the byte index of the column @@ -3095,6 +3143,10 @@ complete_info([{what}]) the items listed in {what} are returned. Unsupported items in {what} are silently ignored. + To get the position and size of the popup menu, see + |pum_getpos()|. It's also available in |v:event| during the + |CompleteChanged| event. + Examples: > " Get all items call complete_info() @@ -3525,7 +3577,7 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is string) *funcname built-in function (see |functions|) or user defined function (see - |user-functions|). Also works for a + |user-function|). Also works for a variable that is a Funcref. varname internal variable (see |internal-variables|). Also works @@ -3604,6 +3656,11 @@ exp({expr}) *exp()* :echo exp(-1) < 0.367879 +debugbreak({pid}) *debugbreak()* + Specifically used to interrupt a program being debugged. It + will cause process {pid} to get a SIGTRAP. Behavior for other + processes is undefined. See |terminal-debugger|. + {Sends a SIGINT to a process {pid} other than MS-Windows} expand({expr} [, {nosuf} [, {list}]]) *expand()* Expand wildcards and the following special keywords in {expr}. @@ -3687,6 +3744,14 @@ expand({expr} [, {nosuf} [, {list}]]) *expand()* See |glob()| for finding existing files. See |system()| for getting the raw output of an external command. +expandcmd({expr}) *expandcmd()* + Expand special items in {expr} like what is done for an Ex + command such as `:edit`. This expands special keywords, like + with |expand()|, and environment variables, anywhere in + {expr}. Returns the expanded string. + Example: > + :echo expandcmd('make %<.o') +< extend({expr1}, {expr2} [, {expr3}]) *extend()* {expr1} and {expr2} must be both |Lists| or both |Dictionaries|. @@ -3741,6 +3806,8 @@ feedkeys({string} [, {mode}]) *feedkeys()* and "\..." notation |expr-quote|. For example, feedkeys("\<CR>") simulates pressing of the <Enter> key. But feedkeys('\<CR>') pushes 5 characters. + The |<Ignore>| keycode may be used to exit the + wait-for-character without doing anything. {mode} is a String, which can contain these character flags: 'm' Remap keys. This is default. If {mode} is absent, @@ -3852,6 +3919,25 @@ findfile({name} [, {path} [, {count}]]) *findfile()* < Searches from the directory of the current file upwards until it finds the file "tags.vim". +flatten({list} [, {maxdepth}]) *flatten()* + Flatten {list} up to {maxdepth} levels. Without {maxdepth} + the result is a |List| without nesting, as if {maxdepth} is + a very large number. + The {list} is changed in place, make a copy first if you do + not want that. + *E964* + {maxdepth} means how deep in nested lists changes are made. + {list} is not modified when {maxdepth} is 0. + {maxdepth} must be positive number. + + If there is an error the number zero is returned. + + Example: > + :echo flatten([1, [2, [3, 4]], 5]) +< [1, 2, 3, 4, 5] > + :echo flatten([1, [2, [3, 4]], 5], 1) +< [1, 2, [3, 4], 5] + float2nr({expr}) *float2nr()* Convert {expr} to a Number by omitting the part after the decimal point. @@ -4107,8 +4193,13 @@ getbufinfo([{dict}]) changed TRUE if the buffer is modified. changedtick number of changes made to the buffer. hidden TRUE if the buffer is hidden. + lastused timestamp in seconds, like + |localtime()|, when the buffer was + last used. listed TRUE if the buffer is listed. lnum current line number in buffer. + linecount number of lines in the buffer (only + valid when loaded) loaded TRUE if the buffer is loaded. name full path to the file in the buffer. signs list of signs placed in the buffer. @@ -4479,8 +4570,7 @@ getftype({fname}) *getftype()* systems that support it. On some systems only "dir" and "file" are returned. - *getjumplist()* -getjumplist([{winnr} [, {tabnr}]]) +getjumplist([{winnr} [, {tabnr}]]) *getjumplist()* Returns the |jumplist| for the specified window. Without arguments use the current window. @@ -4505,7 +4595,7 @@ getline({lnum} [, {end}]) from the current buffer. Example: > getline(1) < When {lnum} is a String that doesn't start with a - digit, line() is called to translate the String into a Number. + digit, |line()| is called to translate the String into a Number. To get the line under the cursor: > getline(".") < When {lnum} is smaller than 1 or bigger than the number of @@ -4536,8 +4626,12 @@ getloclist({nr},[, {what}]) *getloclist()* If the optional {what} dictionary argument is supplied, then returns the items listed in {what} as a dictionary. Refer to |getqflist()| for the supported items in {what}. + If {what} contains 'filewinid', then returns the id of the + window used to display files from the location list. This + field is applicable only when called from a location list + window. -getmatches() *getmatches()* +getmatches([{win}]) *getmatches()* Returns a |List| with all matches previously defined for the current window by |matchadd()| and the |:match| commands. |getmatches()| is useful in combination with |setmatches()|, @@ -4699,7 +4793,7 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()* getregtype([{regname}]) *getregtype()* The result is a String, which is type of register {regname}. The value will be one of: - "v" for |characterwise| text + "v" for |charwise| text "V" for |linewise| text "<CTRL-V>{width}" for |blockwise-visual| text "" for an empty or unknown register @@ -4941,9 +5035,11 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The < *feature-list* List of supported pseudo-feature names: acl |ACL| support + bsd BSD system (not macOS, use "mac" for that). iconv Can use |iconv()| for conversion. +shellslash Can use backslashes in filenames (Windows) clipboard |clipboard| provider is available. + mac MacOS system. nvim This is Nvim. python2 Legacy Vim |python2| interface. |has-python| python3 Legacy Vim |python3| interface. |has-python| @@ -4953,6 +5049,7 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The unix Unix system. *vim_starting* True during |startup|. win32 Windows system (32 or 64 bit). + win64 Windows system (64 bit). wsl WSL (Windows Subsystem for Linux) system *has-patch* @@ -5413,32 +5510,46 @@ jobstart({cmd}[, {opts}]) *jobstart()* *jobstart-options* {opts} is a dictionary with these keys: - |on_stdout|: stdout event handler (function name or |Funcref|) - stdout_buffered : read stdout in |channel-buffered| mode. - |on_stderr|: stderr event handler (function name or |Funcref|) - stderr_buffered : read stderr in |channel-buffered| mode. - |on_exit| : exit event handler (function name or |Funcref|) - cwd : Working directory of the job; defaults to - |current-directory|. - rpc : If set, |msgpack-rpc| will be used to communicate - with the job over stdin and stdout. "on_stdout" is - then ignored, but "on_stderr" can still be used. - pty : If set, the job will be connected to a new pseudo - terminal and the job streams are connected to the - master file descriptor. "on_stderr" is ignored, - "on_stdout" receives all output. - - width : (pty only) Width of the terminal screen - height : (pty only) Height of the terminal screen - TERM : (pty only) $TERM environment variable - detach : (non-pty only) Detach the job process: it will - not be killed when Nvim exits. If the process - exits before Nvim, "on_exit" will be invoked. + clear_env: (boolean) `env` defines the job environment + exactly, instead of merging current environment. + cwd: (string, default=|current-directory|) Working + directory of the job. + detach: (boolean) Detach the job process: it will not be + killed when Nvim exits. If the process exits + before Nvim, `on_exit` will be invoked. + env: (dict) Map of environment variable name:value + pairs extending (or replacing if |clear_env|) + the current environment. + height: (number) Height of the `pty` terminal. + |on_exit|: (function) Callback invoked when the job exits. + |on_stdout|: (function) Callback invoked when the job emits + stdout data. + |on_stderr|: (function) Callback invoked when the job emits + stderr data. + overlapped: (boolean) Set FILE_FLAG_OVERLAPPED for the + standard input/output passed to the child process. + Normally you do not need to set this. + (Only available on MS-Windows, On other + platforms, this option is silently ignored.) + pty: (boolean) Connect the job to a new pseudo + terminal, and its streams to the master file + descriptor. Then `on_stderr` is ignored, + `on_stdout` receives all output. + rpc: (boolean) Use |msgpack-rpc| to communicate with + the job over stdio. Then `on_stdout` is ignored, + but `on_stderr` can still be used. + stderr_buffered: (boolean) Collect data until EOF (stream closed) + before invoking `on_stderr`. |channel-buffered| + stdout_buffered: (boolean) Collect data until EOF (stream + closed) before invoking `on_stdout`. |channel-buffered| + TERM: (string) Sets the `pty` $TERM environment variable. + width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the caller may set other keys to pass application-specific data. + Returns: - - The channel ID on success + - |channel-id| on success - 0 on invalid arguments - -1 if {cmd}[0] is not executable. See also |job-control|, |channel|, |msgpack-rpc|. @@ -5450,6 +5561,9 @@ jobstop({id}) *jobstop()* (if any) will be invoked. See |job-control|. + Returns 1 for valid job id, 0 for invalid id, including jobs have + exited or stopped. + jobwait({jobs}[, {timeout}]) *jobwait()* Waits for jobs and their |on_exit| handlers to complete. @@ -5622,6 +5736,20 @@ lispindent({lnum}) *lispindent()* When {lnum} is invalid or Vim was not compiled the |+lispindent| feature, -1 is returned. +list2str({list} [, {utf8}]) *list2str()* + Convert each number in {list} to a character string can + concatenate them all. Examples: > + list2str([32]) returns " " + list2str([65, 66, 67]) returns "ABC" +< The same can be done (slowly) with: > + join(map(list, {nr, val -> nr2char(val)}), '') +< |str2list()| does the opposite. + + When {utf8} is omitted or zero, the current 'encoding' is used. + With {utf8} is 1, always return utf-8 characters. + With utf-8 composing characters work as expected: > + list2str([97, 769]) returns "á" +< localtime() *localtime()* Return the current time, measured as seconds since 1st Jan 1970. See also |strftime()| and |getftime()|. @@ -5732,6 +5860,7 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* "rhs" The {rhs} of the mapping as typed. "silent" 1 for a |:map-silent| mapping, else 0. "noremap" 1 if the {rhs} of the mapping is not remappable. + "script" 1 if mapping was defined with <script>. "expr" 1 for an expression mapping (|:map-<expr>|). "buffer" 1 for a buffer local mapping (|:map-local|). "mode" Modes for which the mapping is defined. In @@ -5849,7 +5978,7 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]]) Defines a pattern to be highlighted in the current window (a "match"). It will be highlighted with {group}. Returns an identification number (ID), which can be used to delete the - match using |matchdelete()|. + match using |matchdelete()|. The ID is bound to the window. Matching is case sensitive and magic, unless case sensitivity or magicness are explicitly overridden in {pattern}. The 'magic', 'smartcase' and 'ignorecase' options are not used. @@ -5949,11 +6078,13 @@ matcharg({nr}) *matcharg()* Highlighting matches using the |:match| commands are limited to three matches. |matchadd()| does not have this limitation. -matchdelete({id}) *matchdelete()* *E802* *E803* +matchdelete({id} [, {win}) *matchdelete()* *E802* *E803* Deletes a match with ID {id} previously defined by |matchadd()| or one of the |:match| commands. Returns 0 if successful, otherwise -1. See example for |matchadd()|. All matches can be deleted in one operation by |clearmatches()|. + If {win} is specified, use the window with this number or + window ID instead of the current window. matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()* Same as |match()|, but return the index of first character @@ -6103,7 +6234,7 @@ mode([expr]) Return a string that indicates the current mode. n Normal no Operator-pending - nov Operator-pending (forced characterwise |o_v|) + nov Operator-pending (forced charwise |o_v|) noV Operator-pending (forced linewise |o_V|) noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|) niI Normal using |i_CTRL-O| in |Insert-mode| @@ -6486,6 +6617,63 @@ printf({fmt}, {expr1} ...) *printf()* of "%" items. If there are not sufficient or too many arguments an error is given. Up to 18 arguments can be used. +prompt_setcallback({buf}, {expr}) *prompt_setcallback()* + Set prompt callback for buffer {buf} to {expr}. When {expr} + is an empty string the callback is removed. This has only + effect if {buf} has 'buftype' set to "prompt". + + The callback is invoked when pressing Enter. The current + buffer will always be the prompt buffer. A new line for a + prompt is added before invoking the callback, thus the prompt + for which the callback was invoked will be in the last but one + line. + If the callback wants to add text to the buffer, it must + insert it above the last line, since that is where the current + prompt is. This can also be done asynchronously. + The callback is invoked with one argument, which is the text + that was entered at the prompt. This can be an empty string + if the user only typed Enter. + Example: > + call prompt_setcallback(bufnr(''), function('s:TextEntered')) + func s:TextEntered(text) + if a:text == 'exit' || a:text == 'quit' + stopinsert + close + else + call append(line('$') - 1, 'Entered: "' . a:text . '"') + " Reset 'modified' to allow the buffer to be closed. + set nomodified + endif + endfunc + +prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()* + Set a callback for buffer {buf} to {expr}. When {expr} is an + empty string the callback is removed. This has only effect if + {buf} has 'buftype' set to "prompt". + + This callback will be invoked when pressing CTRL-C in Insert + mode. Without setting a callback Vim will exit Insert mode, + as in any buffer. + +prompt_setprompt({buf}, {text}) *prompt_setprompt()* + Set prompt for buffer {buf} to {text}. You most likely want + {text} to end in a space. + The result is only visible if {buf} has 'buftype' set to + "prompt". Example: > + call prompt_setprompt(bufnr(''), 'command: ') + +pum_getpos() *pum_getpos()* + If the popup menu (see |ins-completion-menu|) is not visible, + returns an empty |Dictionary|, otherwise, returns a + |Dictionary| with the following keys: + height nr of items visible + width screen cells + row top screen row (0 first row) + col leftmost screen column (0 first col) + size total nr of items + scrollbar |TRUE| if visible + + The values are the same as in |v:event| during |CompleteChanged|. pumvisible() *pumvisible()* Returns non-zero when the popup menu is visible, zero @@ -6538,6 +6726,33 @@ range({expr} [, {max} [, {stride}]]) *range()* range(0) " [] range(2, 0) " error! < + *readdir()* +readdir({directory} [, {expr}]) + Return a list with file and directory names in {directory}. + + When {expr} is omitted all entries are included. + When {expr} is given, it is evaluated to check what to do: + If {expr} results in -1 then no further entries will + be handled. + If {expr} results in 0 then this entry will not be + added to the list. + If {expr} results in 1 then this entry will be added + to the list. + Each time {expr} is evaluated |v:val| is set to the entry name. + When {expr} is a function the name is passed as the argument. + For example, to get a list of files ending in ".txt": > + readdir(dirname, {n -> n =~ '.txt$'}) +< To skip hidden and backup files: > + readdir(dirname, {n -> n !~ '^\.\|\~$'}) + +< If you want to get a directory tree: > + function! s:tree(dir) + return {a:dir : map(readdir(a:dir), + \ {_, x -> isdirectory(x) ? + \ {x : s:tree(a:dir . '/' . x)} : x})} + endfunction + echo s:tree(".") +< *readfile()* readfile({fname} [, {binary} [, {max}]]) Read file {fname} and return a |List|, each line of the file @@ -6569,17 +6784,6 @@ readfile({fname} [, {binary} [, {max}]]) the result is an empty list. Also see |writefile()|. - *readdir()* -readdir({directory} [, {expr}]) - Return a list with file and directory names in {directory}. - You can also use |glob()| if you don't need to do complicated - things, such as limiting the number of matches. - - When {expr} is omitted all entries are included. - When {expr} is given, it is evaluated to check what to do: - If {expr} results in -1 then no further entries will - be handled. - reg_executing() *reg_executing()* Returns the single letter name of the register being executed. Returns an empty string when no register is being executed. @@ -6640,7 +6844,7 @@ remote_expr({server}, {string} [, {idvar} [, {timeout}]]) between (not at the end), like with join(expr, "\n"). If {idvar} is present and not empty, it is taken as the name of a variable and a {serverid} for later use with - remote_read() is stored there. + |remote_read()| is stored there. If {timeout} is given the read times out after this many seconds. Otherwise a timeout of 600 seconds is used. See also |clientserver| |RemoteReply|. @@ -7253,11 +7457,13 @@ setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* only the items listed in {what} are set. Refer to |setqflist()| for the list of supported keys in {what}. -setmatches({list}) *setmatches()* +setmatches({list} [, {win}]) *setmatches()* Restores a list of matches saved by |getmatches() for the current window|. Returns 0 if successful, otherwise -1. All current matches are cleared before the list is restored. See example for |getmatches()|. + If {win} is specified, use the window with this number or + window ID instead of the current window. *setpos()* setpos({expr}, {list}) @@ -7413,7 +7619,7 @@ setreg({regname}, {value} [, {options}]) If {options} contains "a" or {regname} is upper case, then the value is appended. {options} can also contain a register type specification: - "c" or "v" |characterwise| mode + "c" or "v" |charwise| mode "l" or "V" |linewise| mode "b" or "<CTRL-V>" |blockwise-visual| mode If a number immediately follows "b" or "<CTRL-V>" then this is @@ -7482,11 +7688,21 @@ settagstack({nr}, {dict} [, {action}]) *settagstack()* {nr} can be the window number or the |window-ID|. For a list of supported items in {dict}, refer to - |gettagstack()| + |gettagstack()|. "curidx" takes effect before changing the tag + stack. *E962* - If {action} is not present or is set to 'r', then the tag - stack is replaced. If {action} is set to 'a', then new entries - from {dict} are pushed onto the tag stack. + How the tag stack is modified depends on the {action} + argument: + - If {action} is not present or is set to 'r', then the tag + stack is replaced. + - If {action} is set to 'a', then new entries from {dict} are + pushed (added) onto the tag stack. + - If {action} is set to 't', then all the entries from the + current entry in the tag stack or "curidx" in {dict} are + removed and then new entries are pushed to the stack. + + The current index is set to one after the length of the tag + stack after the modification. Returns zero for success, -1 for failure. @@ -7660,7 +7876,7 @@ sign_getplaced([{expr} [, {dict}]]) *sign_getplaced()* priority sign priority The returned signs in a buffer are ordered by their line - number. + number and priority. Returns an empty list on failure or if there are no placed signs. @@ -8065,6 +8281,18 @@ str2float({expr}) *str2float()* |substitute()|: > let f = str2float(substitute(text, ',', '', 'g')) +str2list({expr} [, {utf8}]) *str2list()* + Return a list containing the number values which represent + each character in String {expr}. Examples: > + str2list(" ") returns [32] + str2list("ABC") returns [65, 66, 67] +< |list2str()| does the opposite. + + When {utf8} is omitted or zero, the current 'encoding' is used. + With {utf8} set to 1, always treat the String as utf-8 + characters. With utf-8 composing characters are handled + properly: > + str2list("á") returns [97, 769] str2nr({expr} [, {base}]) *str2nr()* Convert string {expr} to a number. @@ -8535,8 +8763,12 @@ tabpagebuflist([{arg}]) *tabpagebuflist()* tabpagenr([{arg}]) *tabpagenr()* The result is a Number, which is the number of the current tab page. The first tab page has number 1. - When the optional argument is "$", the number of the last tab - page is returned (the tab page count). + The optional argument {arg} supports the following values: + $ the number of the last tab page (the tab page + count). + # the number of the last accessed tab page (where + |g<Tab>| goes to). If there is no previous + tab page, 0 is returned. The number can be used with the |:tab| command. @@ -9155,7 +9387,7 @@ wordcount() *wordcount()* (only in Visual mode) visual_chars Number of chars visually selected (only in Visual mode) - visual_words Number of chars visually selected + visual_words Number of words visually selected (only in Visual mode) @@ -9222,7 +9454,7 @@ Don't forget that "^" will only match at the first character of the String and "\n". ============================================================================== -5. Defining functions *user-functions* +5. Defining functions *user-function* New functions can be defined. These can be called just like builtin functions. The function executes a sequence of Ex commands. Normal mode @@ -9680,7 +9912,7 @@ This does NOT work: > register, "@/" for the search pattern. If the result of {expr1} ends in a <CR> or <NL>, the register will be linewise, otherwise it will be set to - characterwise. + charwise. This can be used to clear the last search pattern: > :let @/ = "" < This is different from searching for an empty string, @@ -9762,6 +9994,54 @@ This does NOT work: > Like above, but append/add/subtract the value for each |List| item. + *:let=<<* *:let-heredoc* + *E990* *E991* *E172* *E221* +:let {var-name} =<< [trim] {marker} +text... +text... +{marker} + Set internal variable {var-name} to a List containing + the lines of text bounded by the string {marker}. + {marker} cannot start with a lower case character. + The last line should end only with the {marker} string + without any other character. Watch out for white + space after {marker}! + + Without "trim" any white space characters in the lines + of text are preserved. If "trim" is specified before + {marker}, then indentation is stripped so you can do: > + let text =<< trim END + if ok + echo 'done' + endif + END +< Results in: ["if ok", " echo 'done'", "endif"] + The marker must line up with "let" and the indentation + of the first line is removed from all the text lines. + Specifically: all the leading indentation exactly + matching the leading indentation of the first + non-empty text line is stripped from the input lines. + All leading indentation exactly matching the leading + indentation before `let` is stripped from the line + containing {marker}. Note that the difference between + space and tab matters here. + + If {var-name} didn't exist yet, it is created. + Cannot be followed by another command, but can be + followed by a comment. + + Examples: > + let var1 =<< END + Sample text 1 + Sample text 2 + Sample text 3 + END + + let data =<< trim DATA + 1 2 3 4 + 5 6 7 8 + DATA +< *E121* :let {var-name} .. List the value of variable {var-name}. Multiple variable names may be given. Special names recognized @@ -10157,8 +10437,8 @@ This does NOT work: > The parsing works slightly different from |:echo|, more like |:execute|. All the expressions are first evaluated and concatenated before echoing anything. - The expressions must evaluate to a Number or String, a - Dictionary or List causes an error. + If expressions does not evaluate to a Number or + String, string() is used to turn it into a string. Uses the highlighting set by the |:echohl| command. Example: > :echomsg "It's a Zizzer Zazzer Zuzz, as you can plainly see." @@ -10169,7 +10449,7 @@ This does NOT work: > message in the |message-history|. When used in a script or function the line number will be added. Spaces are placed between the arguments as with the - :echo command. When used inside a try conditional, + |:echomsg| command. When used inside a try conditional, the message is raised as an error exception instead (see |try-echoerr|). Example: > diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index c579c390c6..1fce37c594 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -275,7 +275,7 @@ Note that the last one is the value of $VIMRUNTIME which has been expanded. Note that when using a plugin manager or |packages| many directories will be added to 'runtimepath'. These plugins each require their own directory, don't -put them directly in ~/.vim/plugin. +put them directly in ~/.config/nvim/plugin. What if it looks like your plugin is not being loaded? You can find out what happens when Vim starts up by using the |-V| argument: > @@ -549,7 +549,9 @@ Variables: *b:man_default_sects* Comma-separated, ordered list of preferred sections. For example in C one usually wants section 3 or 2: > :let b:man_default_sections = '3,2' -*g:man_hardwrap* Hard-wrap to $MANWIDTH. May improve layout. +*g:man_hardwrap* Hard-wrap to $MANWIDTH or window width if $MANWIDTH is + empty. Enabled by default. Set |FALSE| to enable soft + wrapping. To use Nvim as a manpager: > export MANPAGER='nvim +Man!' @@ -558,10 +560,13 @@ Note that when running `man` from the shell and with that `MANPAGER` in your environment, `man` will pre-format the manpage using `groff`. Thus, Neovim will inevitably display the manual page as it was passed to it from stdin. One of the caveats of this is that the width will _always_ be hard-wrapped and not -soft wrapped as with `:Man`. You can set in your environment: > +soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: > export MANWIDTH=999 -So `groff`'s pre-formatting output will be the same as with `:Man` i.e soft-wrapped. +So `groff`'s pre-formatting output will be the same as with `g:man_hardwrap=0` i.e soft-wrapped. + +To disable bold highlighting: > + :highlight link manBold Normal PDF *ft-pdf-plugin* diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index f2f6c70b0c..8e2cb2f728 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -527,8 +527,7 @@ FOLDCOLUMN *fold-foldcolumn* 'foldcolumn' is a number, which sets the width for a column on the side of the window to indicate folds. When it is zero, there is no foldcolumn. A normal -value is 4 or 5. The minimal useful value is 2, although 1 still provides -some information. The maximum is 12. +value is auto:9. The maximum is 9. An open fold is indicated with a column that has a '-' at the top and '|' characters below it. This column stops where the open fold stops. When folds diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 284cd26583..a384b5f876 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -129,6 +129,7 @@ Advanced editing ~ |autocmd.txt| automatically executing commands on an event |eval.txt| expression evaluation, conditional commands |fold.txt| hide (fold) ranges of lines +|lua.txt| Lua API Special issues ~ |print.txt| printing @@ -136,6 +137,7 @@ Special issues ~ Programming language support ~ |indent.txt| automatic indenting for C and other languages +|lsp.txt| Language Server Protocol (LSP) |syntax.txt| syntax highlighting |filetype.txt| settings done specifically for a type of file |quickfix.txt| commands for a quick edit-compile-fix cycle @@ -157,7 +159,6 @@ GUI ~ Interfaces ~ |if_cscop.txt| using Cscope with Vim -|if_lua.txt| Lua interface |if_pyth.txt| Python interface |if_ruby.txt| Ruby interface |sign.txt| debugging signs @@ -169,7 +170,7 @@ Versions ~ Standard plugins ~ |pi_gzip.txt| Reading and writing compressed files |pi_health.txt| Healthcheck framework -|pi_matchit.txt| Extended "%" matching +|pi_matchit.txt| Extended |%| matching |pi_msgpack.txt| msgpack utilities |pi_netrw.txt| Reading and writing files over a network |pi_paren.txt| Highlight matching parens diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index aa2d0a03c6..34bcf0f039 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -1,668 +1,8 @@ -*if_lua.txt* Nvim - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL - -Lua engine *lua* *Lua* - - Type |gO| to see the table of contents. - -============================================================================== -Introduction *lua-intro* - -The Lua 5.1 language is builtin and always available. Try this command to get -an idea of what lurks beneath: > - - :lua print(vim.inspect(package.loaded)) - -Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the -"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can -be used from Lua code. - -Module conflicts are resolved by "last wins". For example if both of these -are on 'runtimepath': - runtime/lua/foo.lua - ~/.config/nvim/lua/foo.lua -then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and -"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim -finds and loads Lua modules. The conventions are similar to VimL plugins, -with some extra features. See |lua-require-example| for a walkthrough. - -============================================================================== -Importing Lua modules *lua-require* - -Nvim automatically adjusts `package.path` and `package.cpath` according to -effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is -changed. `package.path` is adjusted by simply appending `/lua/?.lua` and -`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the -first character of `package.config`). - -Similarly to `package.path`, modified directories from 'runtimepath' are also -added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and -`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of -the existing `package.cpath` are used. Example: - -1. Given that - - 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`; - - initial (defined at compile-time or derived from - `$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains - `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`. -2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in - order: parts of the path starting from the first path component containing - question mark and preceding path separator. -3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same - as the suffix of the first path from `package.path` (i.e. `./?.so`). Which - leaves `/?.so` and `/a?d/j/g.elf`, in this order. -4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The - second one contains semicolon which is a paths separator so it is out, - leaving only `/foo/bar` and `/abc`, in order. -5. The cartesian product of paths from 4. and suffixes from 3. is taken, - giving four variants. In each variant `/lua` path segment is inserted - between path and suffix, leaving - - - `/foo/bar/lua/?.so` - - `/foo/bar/lua/a?d/j/g.elf` - - `/abc/lua/?.so` - - `/abc/lua/a?d/j/g.elf` - -6. New paths are prepended to the original `package.cpath`. - -The result will look like this: - - `/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath') - × `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`) - - = `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` - -Note: - -- To track 'runtimepath' updates, paths added at previous update are - remembered and removed at the next update, while all paths derived from the - new 'runtimepath' are prepended as described above. This allows removing - paths when path is removed from 'runtimepath', adding paths when they are - added and reordering `package.path`/`package.cpath` content if 'runtimepath' - was reordered. - -- Although adjustments happen automatically, Nvim does not track current - values of `package.path` or `package.cpath`. If you happen to delete some - paths from there you can set 'runtimepath' to trigger an update: > - let &runtimepath = &runtimepath - -- Skipping paths from 'runtimepath' which contain semicolons applies both to - `package.path` and `package.cpath`. Given that there are some badly written - plugins using shell which will not work with paths containing semicolons it - is better to not have them in 'runtimepath' at all. - ------------------------------------------------------------------------------- -LUA PLUGIN EXAMPLE *lua-require-example* - -The following example plugin adds a command `:MakeCharBlob` which transforms -current buffer into a long `unsigned char` array. Lua contains transformation -function in a module `lua/charblob.lua` which is imported in -`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed -to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in -this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`). - -autoload/charblob.vim: > - - function charblob#encode_buffer() - call setline(1, luaeval( - \ 'require("charblob").encode(unpack(_A))', - \ [getline(1, '$'), &textwidth, ' '])) - endfunction - -plugin/charblob.vim: > - - if exists('g:charblob_loaded') - finish - endif - let g:charblob_loaded = 1 - - command MakeCharBlob :call charblob#encode_buffer() - -lua/charblob.lua: > - - local function charblob_bytes_iter(lines) - local init_s = { - next_line_idx = 1, - next_byte_idx = 1, - lines = lines, - } - local function next(s, _) - if lines[s.next_line_idx] == nil then - return nil - end - if s.next_byte_idx > #(lines[s.next_line_idx]) then - s.next_line_idx = s.next_line_idx + 1 - s.next_byte_idx = 1 - return ('\n'):byte() - end - local ret = lines[s.next_line_idx]:byte(s.next_byte_idx) - if ret == ('\n'):byte() then - ret = 0 -- See :h NL-used-for-NUL. - end - s.next_byte_idx = s.next_byte_idx + 1 - return ret - end - return next, init_s, nil - end - - local function charblob_encode(lines, textwidth, indent) - local ret = { - 'const unsigned char blob[] = {', - indent, - } - for byte in charblob_bytes_iter(lines) do - -- .- space + number (width 3) + comma - if #(ret[#ret]) + 5 > textwidth then - ret[#ret + 1] = indent - else - ret[#ret] = ret[#ret] .. ' ' - end - ret[#ret] = ret[#ret] .. (('%3u,'):format(byte)) - end - ret[#ret + 1] = '};' - return ret - end - - return { - bytes_iter = charblob_bytes_iter, - encode = charblob_encode, - } - -============================================================================== -Commands *lua-commands* - - *:lua* -:[range]lua {chunk} - Execute Lua chunk {chunk}. - -Examples: -> - :lua vim.api.nvim_command('echo "Hello, Nvim!"') -< -To see the Lua version: > - :lua print(_VERSION) - -To see the LuaJIT version: > - :lua print(jit.version) -< - -:[range]lua << [endmarker] -{script} -{endmarker} - Execute Lua script {script}. Useful for including Lua - code in Vim scripts. - -The {endmarker} must NOT be preceded by any white space. - -If [endmarker] is omitted from after the "<<", a dot '.' must be used after -{script}, like for the |:append| and |:insert| commands. - -Example: -> - function! CurrentLineInfo() - lua << EOF - local linenr = vim.api.nvim_win_get_cursor(0)[1] - local curline = vim.api.nvim_buf_get_lines( - 0, linenr, linenr + 1, false)[1] - print(string.format("Current line [%d] has %d bytes", - linenr, #curline)) - EOF - endfunction - -Note that the `local` variables will disappear when block finishes. This is -not the case for globals. - - *:luado* -:[range]luado {body} Execute Lua function "function (line, linenr) {body} - end" for each line in the [range], with the function - argument being set to the text of each line in turn, - without a trailing <EOL>, and the current line number. - If the value returned by the function is a string it - becomes the text of the line in the current turn. The - default for [range] is the whole file: "1,$". - -Examples: -> - :luado return string.format("%s\t%d", line:reverse(), #line) - - :lua require"lpeg" - :lua -- balanced parenthesis grammar: - :lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" } - :luado if bp:match(line) then return "-->\t" .. line end -< - - *:luafile* -:[range]luafile {file} - Execute Lua script in {file}. - The whole argument is used as a single file name. - -Examples: -> - :luafile script.lua - :luafile % -< - -All these commands execute a Lua chunk from either the command line (:lua and -:luado) or a file (:luafile) with the given line [range]. Similarly to the Lua -interpreter, each chunk has its own scope and so only global variables are -shared between command calls. All Lua default libraries are available. In -addition, Lua "print" function has its output redirected to the Nvim message -area, with arguments separated by a white space instead of a tab. - -Lua uses the "vim" module (see |lua-vim|) to issue commands to Nvim. However, -procedures that alter buffer content, open new buffers, and change cursor -position are restricted when the command is executed in the |sandbox|. - - -============================================================================== -luaeval() *lua-eval* *luaeval()* - -The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is -"luaeval". "luaeval" takes an expression string and an optional argument used -for _A inside expression and returns the result of the expression. It is -semantically equivalent in Lua to: -> - local chunkheader = "local _A = select(1, ...) return " - function luaeval (expstr, arg) - local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) - return chunk(arg) -- return typval - end - -Lua nils, numbers, strings, tables and booleans are converted to their -respective VimL types. An error is thrown if conversion of any other Lua types -is attempted. - -The magic global "_A" contains the second argument to luaeval(). - -Example: > - :echo luaeval('_A[1] + _A[2]', [40, 2]) - 42 - :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123') - foo - -Lua tables are used as both dictionaries and lists, so it is impossible to -determine whether empty table is meant to be empty list or empty dictionary. -Additionally lua does not have integer numbers. To distinguish between these -cases there is the following agreement: - -0. Empty table is empty list. -1. Table with N incrementally growing integral numbers, starting from 1 and - ending with N is considered to be a list. -2. Table with string keys, none of which contains NUL byte, is considered to - be a dictionary. -3. Table with string keys, at least one of which contains NUL byte, is also - considered to be a dictionary, but this time it is converted to - a |msgpack-special-map|. - *lua-special-tbl* -4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point - value: - - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to - a floating-point 1.0. Note that by default integral lua numbers are - converted to |Number|s, non-integral are converted to |Float|s. This - variant allows integral |Float|s. - - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty - dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is - converted to a dictionary `{'a': 42}`: non-string keys are ignored. - Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. - are errors. - - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well - as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not - form a 1-step sequence from 1 to N are ignored, as well as all - non-integral keys. - -Examples: > - - :echo luaeval('math.pi') - :function Rand(x,y) " random uniform between x and y - : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) - : endfunction - :echo Rand(1,10) - -Note that currently second argument to `luaeval` undergoes VimL to lua -conversion, so changing containers in lua do not affect values in VimL. Return -value is also always converted. When converting, |msgpack-special-dict|s are -treated specially. - -============================================================================== -Lua standard modules *lua-stdlib* - -The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes -various functions and sub-modules. It is always loaded, thus require("vim") -is unnecessary. - -You can peek at the module properties: > - - :lua print(vim.inspect(vim)) - -Result is something like this: > - - { - _os_proc_children = <function 1>, - _os_proc_info = <function 2>, - ... - api = { - nvim__id = <function 5>, - nvim__id_array = <function 6>, - ... - }, - deepcopy = <function 106>, - gsplit = <function 107>, - ... - } - -To find documentation on e.g. the "deepcopy" function: > - - :help vim.deepcopy - -Note that underscore-prefixed functions (e.g. "_os_proc_children") are -internal/private and must not be used by plugins. - ------------------------------------------------------------------------------- -VIM.API *lua-api* - -`vim.api` exposes the full Nvim |API| as a table of Lua functions. - -Example: to use the "nvim_get_current_line()" API function, call -"vim.api.nvim_get_current_line()": > - - print(tostring(vim.api.nvim_get_current_line())) - ------------------------------------------------------------------------------- -VIM.LOOP *lua-loop* - -`vim.loop` exposes all features of the Nvim event-loop. This is a low-level -API that provides functionality for networking, filesystem, and process -management. Try this command to see available functions: > - - :lua print(vim.inspect(vim.loop)) - -Reference: http://docs.libuv.org -Examples: https://github.com/luvit/luv/tree/master/examples - - *E5560* *lua-loop-callbacks* -It is an error to directly invoke `vim.api` functions (except |api-fast|) in -`vim.loop` callbacks. For example, this is an error: > - - local timer = vim.loop.new_timer() - timer:start(1000, 0, function() - vim.api.nvim_command('echomsg "test"') - end) - -To avoid the error use |vim.schedule_wrap()| to defer the callback: > - - local timer = vim.loop.new_timer() - timer:start(1000, 0, vim.schedule_wrap(function() - vim.api.nvim_command('echomsg "test"') - end)) - -Example: repeating timer - 1. Save this code to a file. - 2. Execute it with ":luafile %". > - - -- Create a timer handle (implementation detail: uv_timer_t). - local timer = vim.loop.new_timer() - local i = 0 - -- Waits 1000ms, then repeats every 750ms until timer:close(). - timer:start(1000, 750, function() - print('timer invoked! i='..tostring(i)) - if i > 4 then - timer:close() -- Always close handles to avoid leaks. - end - i = i + 1 - end) - print('sleeping'); - - -Example: TCP echo-server *tcp-server* - 1. Save this code to a file. - 2. Execute it with ":luafile %". - 3. Note the port number. - 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): > - - local function create_server(host, port, on_connection) - local server = vim.loop.new_tcp() - server:bind(host, port) - server:listen(128, function(err) - assert(not err, err) -- Check for errors. - local sock = vim.loop.new_tcp() - server:accept(sock) -- Accept client connection. - on_connection(sock) -- Start reading messages. - end) - return server - end - local server = create_server('0.0.0.0', 0, function(sock) - sock:read_start(function(err, chunk) - assert(not err, err) -- Check for errors. - if chunk then - sock:write(chunk) -- Echo received messages to the channel. - else -- EOF (stream closed). - sock:close() -- Always close handles to avoid leaks. - end - end) - end) - print('TCP echo-server listening on port: '..server:getsockname().port) - ------------------------------------------------------------------------------- -VIM *lua-util* - -vim.in_fast_event() *vim.in_fast_event()* - Returns true if the code is executing as part of a "fast" event - handler, where most of the API is disabled. These are low-level events - (e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls - for input. When this is `false` most API functions are callable (but - may be subject to other restrictions such as |textlock|). - -vim.stricmp({a}, {b}) *vim.stricmp()* - Compares strings case-insensitively. Returns 0, 1 or -1 if strings - are equal, {a} is greater than {b} or {a} is lesser than {b}, - respectively. - -vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()* - Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not - supplied, the length of the string is used. All indicies are zero-based. - Returns two values: the UTF-32 and UTF-16 indicies respectively. - - Embedded NUL bytes are treated as terminating the string. Invalid - UTF-8 bytes, and embedded surrogates are counted as one code - point each. An {index} in the middle of a UTF-8 sequence is rounded - upwards to the end of that sequence. - -vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()* - Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not - supplied, it defaults to false (use UTF-32). Returns the byte index. - - Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index} - in the middle of a UTF-16 sequence is rounded upwards to the end of that - sequence. - -vim.schedule({callback}) *vim.schedule()* - Schedules {callback} to be invoked soon by the main event-loop. Useful - to avoid |textlock| or other temporary restrictions. - -vim.type_idx *vim.type_idx* - Type index for use in |lua-special-tbl|. Specifying one of the - values from |vim.types| allows typing the empty table (it is - unclear whether empty lua table represents empty list or empty array) - and forcing integral numbers to be |Float|. See |lua-special-tbl| for - more details. - -vim.val_idx *vim.val_idx* - Value index for tables representing |Float|s. A table representing - floating-point value 1.0 looks like this: > - { - [vim.type_idx] = vim.types.float, - [vim.val_idx] = 1.0, - } -< See also |vim.type_idx| and |lua-special-tbl|. - -vim.types *vim.types* - Table with possible values for |vim.type_idx|. Contains two sets - of key-value pairs: first maps possible values for |vim.type_idx| - to human-readable strings, second maps human-readable type names to - values for |vim.type_idx|. Currently contains pairs for `float`, - `array` and `dictionary` types. - - Note: one must expect that values corresponding to `vim.types.float`, - `vim.types.array` and `vim.types.dictionary` fall under only two - following assumptions: - 1. Value may serve both as a key and as a value in a table. Given the - properties of lua tables this basically means “value is not `nil`”. - 2. For each value in `vim.types` table `vim.types[vim.types[value]]` - is the same as `value`. - No other restrictions are put on types, and it is not guaranteed that - values corresponding to `vim.types.float`, `vim.types.array` and - `vim.types.dictionary` will not change or that `vim.types` table will - only contain values for these three types. +Moved to |lua.txt| ============================================================================== -Lua module: vim *lua-vim* - -inspect({object}, {options}) *vim.inspect()* - Return a human-readable representation of the given object. - - See also: ~ - https://github.com/kikito/inspect.lua - https://github.com/mpeterv/vinspect - -paste({lines}, {phase}) *vim.paste()* - Paste handler, invoked by |nvim_paste()| when a conforming UI - (such as the |TUI|) pastes text into the editor. - - Parameters: ~ - {lines} |readfile()|-style list of lines to paste. - |channel-lines| - {phase} -1: "non-streaming" paste: the call contains all - lines. If paste is "streamed", `phase` indicates the stream state: - • 1: starts the paste (exactly once) - • 2: continues the paste (zero or more times) - • 3: ends the paste (exactly once) - - Return: ~ - false if client should cancel the paste. - - See also: ~ - |paste| - -schedule_wrap({cb}) *vim.schedule_wrap()* - Defers callback `cb` until the Nvim API is safe to call. - - See also: ~ - |lua-loop-callbacks| - |vim.schedule()| - |vim.in_fast_event()| - - - - -deepcopy({orig}) *vim.deepcopy()* - Returns a deep copy of the given object. Non-table objects are - copied as in a typical Lua assignment, whereas table objects - are copied recursively. - - Parameters: ~ - {orig} Table to copy - - Return: ~ - New table of copied keys and (nested) values. - -gsplit({s}, {sep}, {plain}) *vim.gsplit()* - Splits a string at each instance of a separator. - - Parameters: ~ - {s} String to split - {sep} Separator string or pattern - {plain} If `true` use `sep` literally (passed to - String.find) - - Return: ~ - Iterator over the split components - - See also: ~ - |vim.split()| - https://www.lua.org/pil/20.2.html - http://lua-users.org/wiki/StringLibraryTutorial - -split({s}, {sep}, {plain}) *vim.split()* - Splits a string at each instance of a separator. - - Examples: > - split(":aa::b:", ":") --> {'','aa','','bb',''} - split("axaby", "ab?") --> {'','x','y'} - split(x*yz*o, "*", true) --> {'x','yz','o'} -< - - Parameters: ~ - {s} String to split - {sep} Separator string or pattern - {plain} If `true` use `sep` literally (passed to - String.find) - - Return: ~ - List-like table of the split components. - - See also: ~ - |vim.gsplit()| - -tbl_contains({t}, {value}) *vim.tbl_contains()* - Checks if a list-like (vector) table contains `value` . - - Parameters: ~ - {t} Table to check - {value} Value to compare - - Return: ~ - true if `t` contains `value` - -tbl_extend({behavior}, {...}) *vim.tbl_extend()* - Merges two or more map-like tables. - - Parameters: ~ - {behavior} Decides what to do if a key is found in more - than one map: - • "error": raise an error - • "keep": use value from the leftmost map - • "force": use value from the rightmost map - {...} Two or more map-like tables. - - See also: ~ - |extend()| - -tbl_flatten({t}) *vim.tbl_flatten()* - Creates a copy of a list-like table such that any nested - tables are "unrolled" and appended to the result. - - Parameters: ~ - {t} List-like table - - Return: ~ - Flattened copy of the given list-like table. - -trim({s}) *vim.trim()* - Trim whitespace (Lua pattern "%s") from both sides of a - string. - - Parameters: ~ - {s} String to trim - - Return: ~ - String with whitespace removed from its beginning and end - - See also: ~ - https://www.lua.org/pil/20.2.html - -pesc({s}) *vim.pesc()* - Escapes magic chars in a Lua pattern string. - - Parameters: ~ - {s} String to escape - - Return: ~ - %-escaped pattern string - - See also: ~ - https://github.com/rxi/lume - - vim:tw=78:ts=8:ft=help:norl: + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index be9e25113a..bdab10c0e4 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -10,7 +10,7 @@ short description. The lists are sorted on ASCII value. Tip: When looking for certain functionality, use a search command. E.g., to look for deleting something, use: "/delete". -For an overview of options see help.txt |option-list|. +For an overview of options see |option-list|. For an overview of built-in functions see |functions|. For a list of Vim variables see |vim-variable|. For a complete listing of all help items see |help-tags|. @@ -222,6 +222,8 @@ tag char note action in Normal mode ~ |CTRL-]| CTRL-] :ta to ident under cursor |CTRL-^| CTRL-^ edit Nth alternate file (equivalent to ":e #N") +|CTRL-<Tab>| CTRL-<Tab> same as `g<Tab>` : go to last accessed tab + page CTRL-_ not used |<Space>| <Space> 1 same as "l" @@ -404,7 +406,7 @@ tag char note action in Normal mode ~ |t| t{char} 1 cursor till before Nth occurrence of {char} to the right |u| u 2 undo changes -|v| v start characterwise Visual mode +|v| v start charwise Visual mode |w| w 1 cursor N words forward |x| ["x]x 2 delete N characters under and after the cursor [into register x] @@ -570,6 +572,8 @@ tag command action in Normal mode ~ following the file name. |CTRL-W_gt| CTRL-W g t same as `gt`: go to next tab page |CTRL-W_gT| CTRL-W g T same as `gT`: go to previous tab page +|CTRL-W_g<Tab>| CTRL-W g <Tab> same as `g<Tab>` : go to last accessed tab + page |CTRL-W_h| CTRL-W h go to Nth left window (stop at first window) |CTRL-W_i| CTRL-W i split window and jump to declaration of identifier under the cursor @@ -767,6 +771,7 @@ tag char note action in Normal mode ~ |gn| gn 1,2 find the next match with the last used search pattern and Visually select it |gm| gm 1 go to character at middle of the screenline +|gM| gM 1 go to character at middle of the text line |go| go 1 cursor to byte N in the buffer |gp| ["x]gp 2 put the text [from register x] after the cursor N times, leave the cursor after it @@ -787,6 +792,7 @@ tag char note action in Normal mode ~ |g<LeftMouse>| g<LeftMouse> same as <C-LeftMouse> g<MiddleMouse> same as <C-MiddleMouse> |g<RightMouse>| g<RightMouse> same as <C-RightMouse> +|g<Tab>| g<Tab> go to last accessed tab page |g<Up>| g<Up> 1 same as "gk" ============================================================================== @@ -865,7 +871,7 @@ These can be used after an operator, but before a {motion} has been entered. tag char action in Operator-pending mode ~ ----------------------------------------------------------------------- -|o_v| v force operator to work characterwise +|o_v| v force operator to work charwise |o_V| V force operator to work linewise |o_CTRL-V| CTRL-V force operator to work blockwise @@ -977,7 +983,7 @@ tag command note action in Visual mode ~ |v_r| r 2 replace highlighted area with a character |v_s| s 2 delete highlighted area and start insert |v_u| u 2 make highlighted area lowercase -|v_v| v make Visual mode characterwise or stop +|v_v| v make Visual mode charwise or stop Visual mode |v_x| x 2 delete the highlighted area |v_y| y yank the highlighted area @@ -1163,11 +1169,13 @@ tag command action ~ |:cNfile| :cNf[ile] go to last error in previous file |:cabbrev| :ca[bbrev] like ":abbreviate" but for Command-line mode |:cabclear| :cabc[lear] clear all abbreviations for Command-line mode +|:cabove| :cabo[ve] go to error above current line |:caddbuffer| :cad[dbuffer] add errors from buffer |:caddexpr| :cadde[xpr] add errors from expr |:caddfile| :caddf[ile] add error message to current quickfix list |:call| :cal[l] call a function |:catch| :cat[ch] part of a :try command +|:cbelow| :cbe[low] go to error below current line |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbuffer| :cb[uffer] parse error messages and jump to first error |:cc| :cc go to specific error @@ -1324,12 +1332,14 @@ tag command action ~ |:lNext| :lN[ext] go to previous entry in location list |:lNfile| :lNf[ile] go to last entry in previous file |:list| :l[ist] print lines +|:labove| :lab[ove] go to location above current line |:laddexpr| :lad[dexpr] add locations from expr |:laddbuffer| :laddb[uffer] add locations from buffer |:laddfile| :laddf[ile] add locations to current location list |:last| :la[st] go to the last file in the argument list |:language| :lan[guage] set the language (locale) |:later| :lat[er] go to newer change, redo +|:lbelow| :lbe[low] go to location below current line |:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbuffer| :lb[uffer] parse locations and jump to first location |:lcd| :lc[d] change directory locally diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index fce4d8628e..e53af5074b 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1083,7 +1083,8 @@ items: empty when non-zero this match will be added even when it is an empty string user_data custom data which is associated with the item and - available in |v:completed_item| + available in |v:completed_item|; it can be any type; + defaults to an empty string All of these except "icase", "equal", "dup" and "empty" must be a string. If an item does not meet these requirements then an error message is given and diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 887ef764bd..3c3753df78 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -271,7 +271,7 @@ and <> are part of what you type, the context should make this clear. operator is pending. - Ex commands can be used to move the cursor. This can be used to call a function that does some complicated motion. - The motion is always characterwise exclusive, no matter + The motion is always charwise exclusive, no matter what ":" command is used. This means it's impossible to include the last character of a line without the line break (unless 'virtualedit' is set). @@ -339,6 +339,8 @@ notation meaning equivalent decimal value(s) ~ <EOL> end-of-line (can be <CR>, <LF> or <CR><LF>, depends on system and 'fileformat') *<EOL>* +<Ignore> cancel wait-for-character *<Ignore>* +<NOP> no-op: do nothing (useful in mappings) *<Nop>* <Up> cursor-up *cursor-up* *cursor_up* <Down> cursor-down *cursor-down* *cursor_down* @@ -378,11 +380,11 @@ notation meaning equivalent decimal value(s) ~ <kEqual> keypad = *keypad-equal* <kEnter> keypad Enter *keypad-enter* <k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9* -<S-...> shift-key *shift* *<S-* -<C-...> control-key *control* *ctrl* *<C-* -<M-...> alt-key or meta-key *META* *ALT* *<M-* -<A-...> same as <M-...> *<A-* -<D-...> command-key or "super" key *<D-* +<S-…> shift-key *shift* *<S-* +<C-…> control-key *control* *ctrl* *<C-* +<M-…> alt-key or meta-key *META* *ALT* *<M-* +<A-…> same as <M-…> *<A-* +<D-…> command-key or "super" key *<D-* ----------------------------------------------------------------------- Note: The shifted cursor keys, the help key, and the undo key are only diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt new file mode 100644 index 0000000000..b934d2dfa0 --- /dev/null +++ b/runtime/doc/lsp.txt @@ -0,0 +1,1262 @@ +*lsp.txt* LSP + + + NVIM REFERENCE MANUAL + + +LSP client/framework *lsp* + +Nvim supports the Language Server Protocol (LSP), which means it acts as +a client to LSP servers and includes a Lua framework `vim.lsp` for building +enhanced LSP tools. + https://microsoft.github.io/language-server-protocol/ + +LSP facilitates features like go-to-definition, find-references, hover, +completion, rename, format, refactor, etc., using semantic whole-project +analysis (unlike |ctags|). + + Type |gO| to see the table of contents. + +============================================================================== +QUICKSTART *lsp-quickstart* + +Nvim provides a LSP client, but the servers are provided by third parties. +Follow these steps to get LSP features: + + 1. Install the nvim-lsp plugin. It provides common configuration for + various servers so you can get started quickly. + https://github.com/neovim/nvim-lsp + 2. Install a language server. Try ":LspInstall <tab>" or use your system + package manager to install the relevant language server: + https://microsoft.github.io/language-server-protocol/implementors/servers/ + 3. Add `nvim_lsp.xx.setup{…}` to your vimrc, where "xx" is the name of the + relevant config. See the nvim-lsp README for details. + +To check LSP clients attached to the current buffer: > + + :lua print(vim.inspect(vim.lsp.buf_get_clients())) +< + *lsp-config* +Inline diagnostics are enabled automatically, e.g. syntax errors will be +annotated in the buffer. But you probably want to use other features like +go-to-definition, hover, etc. Example config: > + + nnoremap <silent> gd <cmd>lua vim.lsp.buf.declaration()<CR> + nnoremap <silent> <c-]> <cmd>lua vim.lsp.buf.definition()<CR> + nnoremap <silent> K <cmd>lua vim.lsp.buf.hover()<CR> + nnoremap <silent> gD <cmd>lua vim.lsp.buf.implementation()<CR> + nnoremap <silent> <c-k> <cmd>lua vim.lsp.buf.signature_help()<CR> + nnoremap <silent> 1gD <cmd>lua vim.lsp.buf.type_definition()<CR> + nnoremap <silent> gr <cmd>lua vim.lsp.buf.references()<CR> + nnoremap <silent> g0 <cmd>lua vim.lsp.buf.document_symbol()<CR> + nnoremap <silent> gW <cmd>lua vim.lsp.buf.workspace_symbol()<CR> + +Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows +|i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of +|v:lua| to call Lua from Vimscript): > + + " Use LSP omni-completion in Python files. + autocmd Filetype python setlocal omnifunc=v:lua.vim.lsp.omnifunc + +If a function has a `*_sync` variant, it's primarily intended for being run +automatically on file save. E.g. code formatting: > + + " Auto-format *.rs files prior to saving them + autocmd BufWritePre *.rs lua vim.lsp.buf.formatting_sync(nil, 1000) + +================================================================================ +FAQ *lsp-faq* + +- Q: How to force-reload LSP? + A: Stop all clients, then reload the buffer. > + + :lua vim.lsp.stop_client(vim.lsp.get_active_clients()) + :edit + +- Q: Why isn't completion working? + A: In the buffer where you want to use LSP, check that 'omnifunc' is set to + "v:lua.vim.lsp.omnifunc": > + + :verbose set omnifunc? + +< Some other plugin may be overriding the option. To avoid that, you could + set the option in an |after-directory| ftplugin, e.g. + "after/ftplugin/python.vim". + +================================================================================ +LSP API *lsp-api* + +The `vim.lsp` Lua module is a framework for building LSP plugins. + + 1. Start with |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. + 2. Peek at the API: > + :lua print(vim.inspect(vim.lsp)) +< 3. See |lsp-extension-example| for a full example. + +LSP core API is described at |lsp-core|. Those are the core functions for +creating and managing clients. + +The `vim.lsp.buf_…` functions perform operations for all LSP clients attached +to the given buffer. |lsp-buf| + +LSP request/response handlers are implemented as Lua callbacks. +|lsp-callbacks| The `vim.lsp.callbacks` table defines default callbacks used +when creating a new client. Keys are LSP method names: > + + :lua print(vim.inspect(vim.tbl_keys(vim.lsp.callbacks))) + +These LSP requests/notifications are defined by default: + + textDocument/publishDiagnostics + window/logMessage + window/showMessage + +You can check these via `vim.tbl_keys(vim.lsp.callbacks)`. + +These will be used preferentially in `vim.lsp.buf_…` methods for handling +requests. They will also be used when responding to server requests and +notifications. + +Use cases: +- Users can modify this to customize to their preferences. +- UI plugins can modify this by assigning to + `vim.lsp.callbacks[method]` so as to provide more specialized + handling, allowing you to leverage the UI capabilities available. UIs should + try to be conscientious of any existing changes the user may have set + already by checking for existing values. + +Any callbacks passed directly to `request` methods on a server client will +have the highest precedence, followed by the `callbacks`. + +You can override the default handlers, +- globally: by modifying the `vim.lsp.callbacks` table +- per-client: by passing the {callbacks} table parameter to + |vim.lsp.start_client| + +Each handler has this signature: > + + function(err, method, params, client_id) + +Callbacks are functions which are called in a variety of situations by the +client. Their signature is `function(err, method, params, client_id)` They can +be set by the {callbacks} parameter for |vim.lsp.start_client| or via the +|vim.lsp.callbacks|. + +Handlers are called for: +- Notifications from the server (`err` is always `nil`). +- Requests initiated by the server (`err` is always `nil`). + The handler can respond by returning two values: `result, err` + where `err` must be shaped like an RPC error: + `{ code, message, data? }` + You can use |vim.lsp.rpc_response_error()| to create this object. +- Handling requests initiated by the client if the request doesn't explicitly + specify a callback (such as in |vim.lsp.buf_request|). + + +VIM.LSP.PROTOCOL *vim.lsp.protocol* + +Module `vim.lsp.protocol` defines constants dictated by the LSP specification, +and helper functions for creating protocol-related objects. +https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md + +For example `vim.lsp.protocol.ErrorCodes` allows reverse lookup by number or +name: > + + vim.lsp.protocol.TextDocumentSyncKind.Full == 1 + vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" + +================================================================================ +LSP HIGHLIGHT *lsp-highlight* + + *hl-LspDiagnosticsError* +LspDiagnosticsError used for "Error" diagnostic virtual text + *hl-LspDiagnosticsErrorSign* +LspDiagnosticsErrorSign used for "Error" diagnostic signs in sign + column + *hl-LspDiagnosticsErrorFloating* +LspDiagnosticsErrorFloating used for "Error" diagnostic messages in the + diagnostics float + *hl-LspDiagnosticsWarning* +LspDiagnosticsWarning used for "Warning" diagnostic virtual text + *hl-LspDiagnosticsWarningSign* +LspDiagnosticsWarningSign used for "Warning" diagnostic signs in sign + column + *hl-LspDiagnosticsWarningFloating* +LspDiagnosticsWarningFloating used for "Warning" diagnostic messages in the + diagnostics float + *hl-LspDiagnosticsInformation* +LspDiagnosticsInformation used for "Information" diagnostic virtual text + *hl-LspDiagnosticsInformationSign* +LspDiagnosticsInformationSign used for "Information" signs in sign column + *hl-LspDiagnosticsInformationFloating* +LspDiagnosticsInformationFloating used for "Information" diagnostic messages in + the diagnostics float + *hl-LspDiagnosticsHint* +LspDiagnosticsHint used for "Hint" diagnostic virtual text + *hl-LspDiagnosticsHintSign* +LspDiagnosticsHintSign used for "Hint" diagnostic signs in sign + column + *hl-LspDiagnosticsHintFloating* +LspDiagnosticsHintFloating used for "Hint" diagnostic messages in the + diagnostics float + *hl-LspReferenceText* +LspReferenceText used for highlighting "text" references + *hl-LspReferenceRead* +LspReferenceRead used for highlighting "read" references + *hl-LspReferenceWrite* +LspReferenceWrite used for highlighting "write" references + + +================================================================================ +LSP EXAMPLE *lsp-extension-example* + +This example is for plugin authors or users who want a lot of control. If you +are just getting started see |lsp-quickstart|. + +For more advanced configurations where just filtering by filetype isn't +sufficient, you can use the `vim.lsp.start_client()` and +`vim.lsp.buf_attach_client()` commands to easily customize the configuration +however you please. For example, if you want to do your own filtering, or +start a new LSP client based on the root directory for if you plan to work +with multiple projects in a single session. Below is a fully working Lua +example which can do exactly that. + +The example will: +1. Check for each new buffer whether or not we want to start an LSP client. +2. Try to find a root directory by ascending from the buffer's path. +3. Create a new LSP for that root directory if one doesn't exist. +4. Attach the buffer to the client for that root directory. + +> + -- Some path manipulation utilities + local function is_dir(filename) + local stat = vim.loop.fs_stat(filename) + return stat and stat.type == 'directory' or false + end + + local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" + -- Asumes filepath is a file. + local function dirname(filepath) + local is_changed = false + local result = filepath:gsub(path_sep.."([^"..path_sep.."]+)$", function() + is_changed = true + return "" + end) + return result, is_changed + end + + local function path_join(...) + return table.concat(vim.tbl_flatten {...}, path_sep) + end + + -- Ascend the buffer's path until we find the rootdir. + -- is_root_path is a function which returns bool + local function buffer_find_root_dir(bufnr, is_root_path) + local bufname = vim.api.nvim_buf_get_name(bufnr) + if vim.fn.filereadable(bufname) == 0 then + return nil + end + local dir = bufname + -- Just in case our algo is buggy, don't infinite loop. + for _ = 1, 100 do + local did_change + dir, did_change = dirname(dir) + if is_root_path(dir, bufname) then + return dir, bufname + end + -- If we can't ascend further, then stop looking. + if not did_change then + return nil + end + end + end + + -- A table to store our root_dir to client_id lookup. We want one LSP per + -- root directory, and this is how we assert that. + local javascript_lsps = {} + -- Which filetypes we want to consider. + local javascript_filetypes = { + ["javascript.jsx"] = true; + ["javascript"] = true; + ["typescript"] = true; + ["typescript.jsx"] = true; + } + + -- Create a template configuration for a server to start, minus the root_dir + -- which we will specify later. + local javascript_lsp_config = { + name = "javascript"; + cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") }; + } + + -- This needs to be global so that we can call it from the autocmd. + function check_start_javascript_lsp() + local bufnr = vim.api.nvim_get_current_buf() + -- Filter which files we are considering. + if not javascript_filetypes[vim.api.nvim_buf_get_option(bufnr, 'filetype')] then + return + end + -- Try to find our root directory. We will define this as a directory which contains + -- node_modules. Another choice would be to check for `package.json`, or for `.git`. + local root_dir = buffer_find_root_dir(bufnr, function(dir) + return is_dir(path_join(dir, 'node_modules')) + -- return vim.fn.filereadable(path_join(dir, 'package.json')) == 1 + -- return is_dir(path_join(dir, '.git')) + end) + -- We couldn't find a root directory, so ignore this file. + if not root_dir then return end + + -- Check if we have a client alredy or start and store it. + local client_id = javascript_lsps[root_dir] + if not client_id then + local new_config = vim.tbl_extend("error", javascript_lsp_config, { + root_dir = root_dir; + }) + client_id = vim.lsp.start_client(new_config) + javascript_lsps[root_dir] = client_id + end + -- Finally, attach to the buffer to track changes. This will do nothing if we + -- are already attached. + vim.lsp.buf_attach_client(bufnr, client_id) + end + + vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]] +< + + +============================================================================== +AUTOCOMMANDS *lsp-autocommands* + + *LspDiagnosticsChanged* +LspDiagnosticsChanged After receiving publishDiagnostics server response + + +============================================================================== +Lua module: vim.lsp *lsp-core* + +buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()* + Implements the `textDocument/did…` notifications required to + track a buffer for any language server. + + Without calling this, the server won't be notified of changes + to a buffer. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current + {client_id} (number) Client id + +buf_get_clients({bufnr}) *vim.lsp.buf_get_clients()* + Gets a map of client_id:client pairs for the given buffer, + where each value is a |vim.lsp.client| object. + + Parameters: ~ + {bufnr} (optional, number): Buffer handle, or 0 for + current + +buf_get_full_text({bufnr}) *vim.lsp.buf_get_full_text()* + TODO: Documentation + +buf_is_attached({bufnr}, {client_id}) *vim.lsp.buf_is_attached()* + Checks if a buffer is attached for a particular client. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current + {client_id} (number) the client id + +buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()* + Send a notification to a server + + Parameters: ~ + {bufnr} [number] (optional): The number of the buffer + {method} [string]: Name of the request method + {params} [string]: Arguments to send to the server + + Return: ~ + true if any client returns true; false otherwise + + *vim.lsp.buf_request()* +buf_request({bufnr}, {method}, {params}, {callback}) + Sends an async request for all active clients attached to the + buffer. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current. + {method} (string) LSP method name + {params} (optional, table) Parameters to send to the + server + {callback} (optional, functionnil) Handler + + Return: ~ + 2-tuple: + • Map of client-id:request-id pairs for all successful + requests. + • Function which can be used to cancel all the requests. + You could instead iterate all clients and call their + `cancel_request()` methods. + + *vim.lsp.buf_request_sync()* +buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) + Sends a request to a server and waits for the response. + + Calls |vim.lsp.buf_request()| but blocks Nvim while awaiting + the result. Parameters are the same as |vim.lsp.buf_request()| + but the return result is different. Wait maximum of + {timeout_ms} (default 100) ms. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current. + {method} (string) LSP method name + {params} (optional, table) Parameters to send to the + server + {timeout_ms} (optional, number, default=100) Maximum time + in milliseconds to wait for a result. + + Return: ~ + Map of client_id:request_result. On timeout, cancel or + error, returns `(nil, err)` where `err` is a string + describing the failure reason. + +cancel_request({id}) *vim.lsp.cancel_request()* + TODO: Documentation + +client() *vim.lsp.client* + LSP client object. + + • Methods: + • request(method, params, [callback]) Send a request to the + server. If callback is not specified, it will use + {client.callbacks} to try to find a callback. If one is + not found there, then an error will occur. This is a thin + wrapper around {client.rpc.request} with some additional + checking. Returns a boolean to indicate if the + notification was successful. If it is false, then it will + always be false (the client has shutdown). If it was + successful, then it will return the request id as the + second result. You can use this with `notify("$/cancel", { + id = request_id })` to cancel the request. This helper is + made automatically with |vim.lsp.buf_request()| Returns: + status, [client_id] + • notify(method, params) This is just {client.rpc.notify}() + Returns a boolean to indicate if the notification was + successful. If it is false, then it will always be false + (the client has shutdown). Returns: status + • cancel_request(id) This is just + {client.rpc.notify}("$/cancelRequest", { id = id }) + Returns the same as `notify()` . + • stop([force]) Stop a client, optionally with force. By + default, it will just ask the server to shutdown without + force. If you request to stop a client which has + previously been requested to shutdown, it will + automatically escalate and force shutdown. + • is_stopped() Returns true if the client is fully stopped. + + • Members + • id (number): The id allocated to the client. + • name (string): If a name is specified on creation, that + will be used. Otherwise it is just the client id. This is + used for logs and messages. + • offset_encoding (string): The encoding used for + communicating with the server. You can modify this in the + `on_init` method before text is sent to the server. + • callbacks (table): The callbacks used by the client as + described in |lsp-callbacks|. + • config (table): copy of the table that was passed by the + user to |vim.lsp.start_client()|. + • server_capabilities (table): Response from the server sent + on `initialize` describing the server's capabilities. + • resolved_capabilities (table): Normalized table of + capabilities that we have detected based on the initialize + response from the server in `server_capabilities` . + +client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* + TODO: Documentation + + *vim.lsp.define_default_sign()* +define_default_sign({name}, {properties}) + TODO: Documentation + +err_message({...}) *vim.lsp.err_message()* + TODO: Documentation + + *vim.lsp.for_each_buffer_client()* +for_each_buffer_client({bufnr}, {callback}) + TODO: Documentation + +get_active_clients() *vim.lsp.get_active_clients()* + Gets all active clients. + + Return: ~ + Table of |vim.lsp.client| objects + +get_client_by_id({client_id}) *vim.lsp.get_client_by_id()* + Gets an active client by id, or nil if the id is invalid or + the client is not yet initialized. + + Parameters: ~ + {client_id} client id number + + Return: ~ + |vim.lsp.client| object, or nil + +get_log_path() *vim.lsp.get_log_path()* + TODO: Documentation + +initialize() *vim.lsp.initialize()* + TODO: Documentation + +is_dir({filename}) *vim.lsp.is_dir()* + TODO: Documentation + +is_stopped() *vim.lsp.is_stopped()* + TODO: Documentation + +next_client_id() *vim.lsp.next_client_id()* + TODO: Documentation + +notification({method}, {params}) *vim.lsp.notification()* + TODO: Documentation + +notify({...}) *vim.lsp.notify()* + TODO: Documentation + +omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* + Implements 'omnifunc' compatible LSP completion. + + Parameters: ~ + {findstart} 0 or 1, decides behavior + {base} If findstart=0, text to match against + + Return: ~ + (number) Decided by`findstart`: + • findstart=0: column where the completion starts, or -2 + or -3 + • findstart=1: list of matches (actually just calls + |complete()|) + + See also: ~ + |complete-functions| + |complete-items| + |CompleteDone| + +on_error({code}, {err}) *vim.lsp.on_error()* + TODO: Documentation + +on_exit({code}, {signal}) *vim.lsp.on_exit()* + TODO: Documentation + +once({fn}) *vim.lsp.once()* + TODO: Documentation + +optional_validator({fn}) *vim.lsp.optional_validator()* + TODO: Documentation + +request({method}, {params}, {callback}, {bufnr}) *vim.lsp.request()* + TODO: Documentation + +resolve_bufnr({bufnr}) *vim.lsp.resolve_bufnr()* + TODO: Documentation + +resolve_callback({method}) *vim.lsp.resolve_callback()* + TODO: Documentation + +server_request({method}, {params}) *vim.lsp.server_request()* + TODO: Documentation + +set_log_level({level}) *vim.lsp.set_log_level()* + Sets the global log level for LSP logging. + + Levels by name: "trace", "debug", "info", "warn", "error" + Level numbers begin with "trace" at 0 + + Use `lsp.log_levels` for reverse lookup. + + Parameters: ~ + {level} [number|string] the case insensitive level name + or number + + See also: ~ + |vim.lsp.log_levels| + +start_client({config}) *vim.lsp.start_client()* + Starts and initializes a client with the given configuration. + + Parameters `cmd` and `root_dir` are required. + + Parameters: ~ + {root_dir} (required, string) Directory where the + LSP server will base its rootUri on + initialization. + {cmd} (required, string or list treated like + |jobstart()|) Base command that + initiates the LSP client. + {cmd_cwd} (string, default=|getcwd()|) Directory + to launch the `cmd` process. Not + related to `root_dir` . + {cmd_env} (table) Environment flags to pass to + the LSP on spawn. Can be specified + using keys like a map or as a list with `k=v` pairs or both. Non-string values are + coerced to string. Example: > + + { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } +< + {capabilities} Map overriding the default capabilities + defined by + |vim.lsp.protocol.make_client_capabilities()|, + passed to the language server on + initialization. Hint: use + make_client_capabilities() and modify + its result. + • Note: To send an empty dictionary use + `{[vim.type_idx]=vim.types.dictionary}` + , else it will be encoded as an + array. + {callbacks} Map of language server method names to `function(err, method, params, + client_id)` handler. Invoked for: + • Notifications from the server, where + `err` will always be `nil` . + • Requests initiated by the server. For + these you can respond by returning + two values: `result, err` where err + must be shaped like a RPC error, i.e. + `{ code, message, data? }` . Use + |vim.lsp.rpc_response_error()| to + help with this. + • Default callback for client requests + not explicitly specifying a callback. + {init_options} Values to pass in the initialization + request as `initializationOptions` . + See `initialize` in the LSP spec. + {name} (string, default=client-id) Name in log + messages. + {offset_encoding} (default="utf-16") One of "utf-8", + "utf-16", or "utf-32" which is the + encoding that the LSP server expects. + Client does not verify this is correct. + {on_error} Callback with parameters (code, ...), + invoked when the client operation + throws an error. `code` is a number + describing the error. Other arguments + may be passed depending on the error + kind. See |vim.lsp.client_errors| for + possible errors. Use + `vim.lsp.client_errors[code]` to get + human-friendly name. + {before_init} Callback with parameters + (initialize_params, config) invoked + before the LSP "initialize" phase, + where `params` contains the parameters + being sent to the server and `config` + is the config that was passed to + `start_client()` . You can use this to + modify parameters before they are sent. + {on_init} Callback (client, initialize_result) + invoked after LSP "initialize", where + `result` is a table of `capabilities` + and anything else the server may send. + For example, clangd sends + `initialize_result.offsetEncoding` if + `capabilities.offsetEncoding` was sent + to it. You can only modify the + `client.offset_encoding` here before + any notifications are sent. + {on_exit} Callback (code, signal, client_id) + invoked on client exit. + • code: exit code of the process + • signal: number describing the signal + used to terminate (if any) + • client_id: client handle + {on_attach} Callback (client, bufnr) invoked when + client attaches to a buffer. + {trace} "off" | "messages" | "verbose" | nil + passed directly to the language server + in the initialize request. + Invalid/empty values will default to + "off" + + Return: ~ + Client id. |vim.lsp.get_client_by_id()| Note: client is + only available after it has been initialized, which may + happen after a small delay (or never if there is an + error). Use `on_init` to do any actions once the client + has been initialized. + +stop({force}) *vim.lsp.stop()* + TODO: Documentation + +stop_client({client_id}, {force}) *vim.lsp.stop_client()* + Stops a client(s). + + You can also use the `stop()` function on a |vim.lsp.client| + object. To stop all clients: +> + + vim.lsp.stop_client(lsp.get_active_clients()) +< + + By default asks the server to shutdown, unless stop was + requested already for this client, then force-shutdown is + attempted. + + Parameters: ~ + {client_id} client id or |vim.lsp.client| object, or list + thereof + {force} boolean (optional) shutdown forcefully + + *vim.lsp.text_document_did_open_handler()* +text_document_did_open_handler({bufnr}, {client}) + TODO: Documentation + +unsupported_method({method}) *vim.lsp.unsupported_method()* + TODO: Documentation + +validate_client_config({config}) *vim.lsp.validate_client_config()* + TODO: Documentation + +validate_encoding({encoding}) *vim.lsp.validate_encoding()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.protocol *lsp-protocol* + +ifnil({a}, {b}) *vim.lsp.protocol.ifnil()* + TODO: Documentation + + *vim.lsp.protocol.make_client_capabilities()* +make_client_capabilities() + Gets a new ClientCapabilities object describing the LSP client + capabilities. + + *vim.lsp.protocol.resolve_capabilities()* +resolve_capabilities({server_capabilities}) + `*` to match one or more characters in a path segment `?` to + match on one character in a path segment `**` to match any + number of path segments, including none `{}` to group + conditions (e.g. `**/*.{ts,js}` matches all TypeScript and + JavaScript files) `[]` to declare a range of characters to + match in a path segment (e.g., `example.[0-9]` to match on + `example.0` , `example.1` , …) `[!...]` to negate a range of + characters to match in a path segment (e.g., `example.[!0-9]` + to match on `example.a` , `example.b` , but not `example.0` ) + + *vim.lsp.protocol.transform_schema_comments()* +transform_schema_comments() + TODO: Documentation + + *vim.lsp.protocol.transform_schema_to_table()* +transform_schema_to_table() + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.buf *lsp-buf* + +clear_references() *vim.lsp.buf.clear_references()* + TODO: Documentation + +code_action({context}) *vim.lsp.buf.code_action()* + TODO: Documentation + +completion({context}) *vim.lsp.buf.completion()* + Retrieves the completion items at the current cursor position. + Can only be called in Insert mode. + +declaration() *vim.lsp.buf.declaration()* + Jumps to the declaration of the symbol under the cursor. + +definition() *vim.lsp.buf.definition()* + Jumps to the definition of the symbol under the cursor. + +document_highlight() *vim.lsp.buf.document_highlight()* + Send request to server to resolve document highlights for the + current text document position. This request can be associated + to key mapping or to events such as `CursorHold` , eg: +> + vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] + vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] + vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] +< + +document_symbol() *vim.lsp.buf.document_symbol()* + Lists all symbols in the current buffer in the quickfix + window. + +execute_command({command}) *vim.lsp.buf.execute_command()* + TODO: Documentation + +formatting({options}) *vim.lsp.buf.formatting()* + Formats the current buffer. + + The optional {options} table can be used to specify + FormattingOptions, a list of which is available at https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting . Some unspecified options will be automatically derived from + the current Neovim options. + + *vim.lsp.buf.formatting_sync()* +formatting_sync({options}, {timeout_ms}) + Perform |vim.lsp.buf.formatting()| synchronously. + + Useful for running on save, to make sure buffer is formatted + prior to being saved. {timeout_ms} is passed on to + |vim.lsp.buf_request_sync()|. + +hover() *vim.lsp.buf.hover()* + Displays hover information about the symbol under the cursor + in a floating window. Calling the function twice will jump + into the floating window. + +implementation() *vim.lsp.buf.implementation()* + Lists all the implementations for the symbol under the cursor + in the quickfix window. + +npcall({fn}, {...}) *vim.lsp.buf.npcall()* + TODO: Documentation + +ok_or_nil({status}, {...}) *vim.lsp.buf.ok_or_nil()* + TODO: Documentation + + *vim.lsp.buf.range_formatting()* +range_formatting({options}, {start_pos}, {end_pos}) + TODO: Documentation + +references({context}) *vim.lsp.buf.references()* + Lists all the references to the symbol under the cursor in the + quickfix window. + +rename({new_name}) *vim.lsp.buf.rename()* + Renames all references to the symbol under the cursor. If + {new_name} is not provided, the user will be prompted for a + new name using |input()|. + +request({method}, {params}, {callback}) *vim.lsp.buf.request()* + TODO: Documentation + +server_ready() *vim.lsp.buf.server_ready()* + Return: ~ + `true` if server responds. + +signature_help() *vim.lsp.buf.signature_help()* + Displays signature information about the symbol under the + cursor in a floating window. + +type_definition() *vim.lsp.buf.type_definition()* + Jumps to the definition of the type of the symbol under the + cursor. + +workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* + Lists all symbols in the current workspace in the quickfix + window. + + The list is filtered against the optional argument {query}; if + the argument is omitted from the call, the user is prompted to + enter a string on the command line. An empty string means no + filtering is done. + +incoming_calls() *vim.lsp.buf.incoming_calls()* + Lists all the call sites of the symbol under the cursor in the + |quickfix| window. If the symbol can resolve to multiple + items, the user can pick one in the |inputlist|. + +outgoing_calls() *vim.lsp.buf.outgoing_calls()* + Lists all the items that are called by the symbol under the + cursor in the |quickfix| window. If the symbol can resolve to + multiple items, the user can pick one in the |inputlist|. + + +============================================================================== +Lua module: vim.lsp.callbacks *lsp-callbacks* + +err_message({...}) *vim.lsp.callbacks.err_message()* + TODO: Documentation + + *vim.lsp.callbacks.location_callback()* +location_callback({_}, {method}, {result}) + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.log *lsp-log* + +get_filename() *vim.lsp.log.get_filename()* + TODO: Documentation + +path_join({...}) *vim.lsp.log.path_join()* + TODO: Documentation + +set_level({level}) *vim.lsp.log.set_level()* + TODO: Documentation + +should_log({level}) *vim.lsp.log.should_log()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.rpc *lsp-rpc* + +convert_NIL({v}) *vim.lsp.rpc.convert_NIL()* + TODO: Documentation + + *vim.lsp.rpc.create_and_start_client()* +create_and_start_client({cmd}, {cmd_args}, {handlers}, + {extra_spawn_params}) + TODO: Documentation + +encode_and_send({payload}) *vim.lsp.rpc.encode_and_send()* + TODO: Documentation + +env_merge({env}) *vim.lsp.rpc.env_merge()* + Merges current process env with the given env and returns the + result as a list of "k=v" strings. +> + + Example: +< + + > in: { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } + out: { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } +< + + *vim.lsp.rpc.format_message_with_content_length()* +format_message_with_content_length({encoded_message}) + TODO: Documentation + +format_rpc_error({err}) *vim.lsp.rpc.format_rpc_error()* + TODO: Documentation + +handle_body({body}) *vim.lsp.rpc.handle_body()* + TODO: Documentation + +is_dir({filename}) *vim.lsp.rpc.is_dir()* + TODO: Documentation + +json_decode({data}) *vim.lsp.rpc.json_decode()* + TODO: Documentation + +json_encode({data}) *vim.lsp.rpc.json_encode()* + TODO: Documentation + +notification({method}, {params}) *vim.lsp.rpc.notification()* + TODO: Documentation + +on_error({errkind}, {...}) *vim.lsp.rpc.on_error()* + TODO: Documentation + +on_exit({code}, {signal}) *vim.lsp.rpc.on_exit()* + TODO: Documentation + +onexit({code}, {signal}) *vim.lsp.rpc.onexit()* + TODO: Documentation + +parse_headers({header}) *vim.lsp.rpc.parse_headers()* + TODO: Documentation + + *vim.lsp.rpc.pcall_handler()* +pcall_handler({errkind}, {status}, {head}, {...}) + TODO: Documentation + +request_parser_loop() *vim.lsp.rpc.request_parser_loop()* + TODO: Documentation + + *vim.lsp.rpc.rpc_response_error()* +rpc_response_error({code}, {message}, {data}) + Creates an RPC response object/table. + + Parameters: ~ + {code} RPC error code defined in + `vim.lsp.protocol.ErrorCodes` + {message} (optional) arbitrary message to send to server + {data} (optional) arbitrary data to send to server + +send_notification({method}, {params}) *vim.lsp.rpc.send_notification()* + TODO: Documentation + + *vim.lsp.rpc.send_request()* +send_request({method}, {params}, {callback}) + TODO: Documentation + + *vim.lsp.rpc.send_response()* +send_response({request_id}, {err}, {result}) + TODO: Documentation + +server_request({method}, {params}) *vim.lsp.rpc.server_request()* + TODO: Documentation + +try_call({errkind}, {fn}, {...}) *vim.lsp.rpc.try_call()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.util *lsp-util* + + *vim.lsp.util.apply_syntax_to_region()* +apply_syntax_to_region({ft}, {start}, {finish}) + TODO: Documentation + + *vim.lsp.util.apply_text_document_edit()* +apply_text_document_edit({text_document_edit}) + TODO: Documentation + + *vim.lsp.util.apply_text_edits()* +apply_text_edits({text_edits}, {bufnr}) + TODO: Documentation + + *vim.lsp.util.apply_workspace_edit()* +apply_workspace_edit({workspace_edit}) + TODO: Documentation + +buf_clear_diagnostics({bufnr}) *vim.lsp.util.buf_clear_diagnostics()* + TODO: Documentation + +buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* + TODO: Documentation + +buf_diagnostics_count({kind}) *vim.lsp.util.buf_diagnostics_count()* + Returns the number of diagnostics of given kind for current + buffer. + + Useful for showing diagnostic counts in statusline. eg: +> + + function! LspStatus() abort + let sl = '' + if luaeval('not vim.tbl_isempty(vim.lsp.buf_get_clients(0))') + let sl.='%#MyStatuslineLSP#E:' + let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.util.buf_diagnostics_count([[Error]])")}' + let sl.='%#MyStatuslineLSP# W:' + let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.util.buf_diagnostics_count([[Warning]])")}' + else + let sl.='%#MyStatuslineLSPErrors#off' + endif + return sl + endfunction + let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus() +< + + Parameters: ~ + {kind} Diagnostic severity kind: See + |vim.lsp.protocol.DiagnosticSeverity| + + Return: ~ + Count of diagnostics + + *vim.lsp.util.buf_diagnostics_save_positions()* +buf_diagnostics_save_positions({bufnr}, {diagnostics}) + Saves the diagnostics (Diagnostic[]) into diagnostics_by_buf + + Parameters: ~ + {bufnr} bufnr for which the diagnostics are for. + {diagnostics} Diagnostics[] received from the language + server. + + *vim.lsp.util.buf_diagnostics_signs()* +buf_diagnostics_signs({bufnr}, {diagnostics}) + Place signs for each diagnostic in the sign column. + + Sign characters can be customized with the following commands: +> + sign define LspDiagnosticsErrorSign text=E texthl=LspDiagnosticsError linehl= numhl= + sign define LspDiagnosticsWarningSign text=W texthl=LspDiagnosticsWarning linehl= numhl= + sign define LspDiagnosticsInformationSign text=I texthl=LspDiagnosticsInformation linehl= numhl= + sign define LspDiagnosticsHintSign text=H texthl=LspDiagnosticsHint linehl= numhl= +< + + *vim.lsp.util.buf_diagnostics_underline()* +buf_diagnostics_underline({bufnr}, {diagnostics}) + TODO: Documentation + + *vim.lsp.util.buf_diagnostics_virtual_text()* +buf_diagnostics_virtual_text({bufnr}, {diagnostics}) + TODO: Documentation + + *vim.lsp.util.buf_highlight_references()* +buf_highlight_references({bufnr}, {references}) + TODO: Documentation + +character_offset({buf}, {row}, {col}) *vim.lsp.util.character_offset()* + TODO: Documentation + + *vim.lsp.util.close_preview_autocmd()* +close_preview_autocmd({events}, {winnr}) + TODO: Documentation + + *vim.lsp.util.convert_input_to_markdown_lines()* +convert_input_to_markdown_lines({input}, {contents}) + TODO: Documentation + + *vim.lsp.util.convert_signature_help_to_markdown_lines()* +convert_signature_help_to_markdown_lines({signature_help}) + TODO: Documentation + + *vim.lsp.util.diagnostics_group_by_line()* +diagnostics_group_by_line({diagnostics}) + TODO: Documentation + + *vim.lsp.util.extract_completion_items()* +extract_completion_items({result}) + TODO: Documentation + + *vim.lsp.util.fancy_floating_markdown()* +fancy_floating_markdown({contents}, {opts}) + Convert markdown into syntax highlighted regions by stripping + the code blocks and converting them into highlighted code. + This will by default insert a blank line separator after those + code block regions to improve readability. The result is shown + in a floating preview TODO: refactor to separate + stripping/converting and make use of open_floating_preview + + Parameters: ~ + {contents} table of lines to show in window + {opts} dictionary with optional fields + + Return: ~ + width,height size of float + +find_window_by_var({name}, {value}) *vim.lsp.util.find_window_by_var()* + TODO: Documentation + +focusable_float({unique_name}, {fn}) *vim.lsp.util.focusable_float()* + TODO: Documentation + + *vim.lsp.util.focusable_preview()* +focusable_preview({unique_name}, {fn}) + TODO: Documentation + +get_completion_word({item}) *vim.lsp.util.get_completion_word()* + TODO: Documentation + + *vim.lsp.util.get_current_line_to_cursor()* +get_current_line_to_cursor() + TODO: Documentation + +get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* + Get visual width of tabstop. + + Parameters: ~ + {bufnr} (optional, number): Buffer handle, defaults to + current + + Return: ~ + (number) tabstop visual width + + See also: ~ + |softtabstop| + + *vim.lsp.util.get_line_byte_from_position()* +get_line_byte_from_position({bufnr}, {position}) + TODO: Documentation + +get_line_diagnostics() *vim.lsp.util.get_line_diagnostics()* + TODO: Documentation + + *vim.lsp.util.get_severity_highlight_name()* +get_severity_highlight_name({severity}) + TODO: Documentation + +jump_to_location({location}) *vim.lsp.util.jump_to_location()* + TODO: Documentation + +locations_to_items({locations}) *vim.lsp.util.locations_to_items()* + TODO: Documentation + + *vim.lsp.util.make_floating_popup_options()* +make_floating_popup_options({width}, {height}, {opts}) + TODO: Documentation + + *vim.lsp.util.make_formatting_params()* +make_formatting_params({options}) + TODO: Documentation + +make_position_param() *vim.lsp.util.make_position_param()* + TODO: Documentation + +make_position_params() *vim.lsp.util.make_position_params()* + TODO: Documentation + +make_range_params() *vim.lsp.util.make_range_params()* + TODO: Documentation + +make_text_document_params() *vim.lsp.util.make_text_document_params()* + TODO: Documentation + +npcall({fn}, {...}) *vim.lsp.util.npcall()* + TODO: Documentation + +ok_or_nil({status}, {...}) *vim.lsp.util.ok_or_nil()* + TODO: Documentation + + *vim.lsp.util.open_floating_preview()* +open_floating_preview({contents}, {filetype}, {opts}) + Show contents in a floating window + + Parameters: ~ + {contents} table of lines to show in window + {filetype} string of filetype to set for opened buffer + {opts} dictionary with optional fields + + Return: ~ + bufnr,winnr buffer and window number of floating window or + nil + +parse_snippet({input}) *vim.lsp.util.parse_snippet()* + TODO: Documentation + +parse_snippet_rec({input}, {inner}) *vim.lsp.util.parse_snippet_rec()* + TODO: Documentation + +preview_location({location}) *vim.lsp.util.preview_location()* + Preview a location in a floating windows + + behavior depends on type of location: + • for Location, range is shown (e.g., function definition) + • for LocationLink, targetRange is shown (e.g., body of + function definition) + + Parameters: ~ + {location} a single Location or LocationLink + + Return: ~ + bufnr,winnr buffer and window number of floating window or + nil + + *vim.lsp.util.remove_unmatch_completion_items()* +remove_unmatch_completion_items({items}, {prefix}) + TODO: Documentation + +set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()* + TODO: Documentation + +set_loclist({items}) *vim.lsp.util.set_loclist()* + TODO: Documentation + +set_qflist({items}) *vim.lsp.util.set_qflist()* + TODO: Documentation + +show_line_diagnostics() *vim.lsp.util.show_line_diagnostics()* + TODO: Documentation + +sort_by_key({fn}) *vim.lsp.util.sort_by_key()* + TODO: Documentation + +sort_completion_items({items}) *vim.lsp.util.sort_completion_items()* + TODO: Documentation + +split_lines({value}) *vim.lsp.util.split_lines()* + TODO: Documentation + +symbols_to_items({symbols}, {bufnr}) *vim.lsp.util.symbols_to_items()* + Convert symbols to quickfix list items + + Parameters: ~ + {symbols} DocumentSymbol[] or SymbolInformation[] + + *vim.lsp.util.text_document_completion_list_to_complete_items()* +text_document_completion_list_to_complete_items({result}, {prefix}) + TODO: Documentation + +trim_empty_lines({lines}) *vim.lsp.util.trim_empty_lines()* + TODO: Documentation + + *vim.lsp.util.try_trim_markdown_code_blocks()* +try_trim_markdown_code_blocks({lines}) + TODO: Documentation + + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt new file mode 100644 index 0000000000..60c7a60d25 --- /dev/null +++ b/runtime/doc/lua.txt @@ -0,0 +1,1453 @@ +*lua.txt* Nvim + + + NVIM REFERENCE MANUAL + + +Lua engine *lua* *Lua* + + Type |gO| to see the table of contents. + +============================================================================== +Introduction *lua-intro* + +The Lua 5.1 language is builtin and always available. Try this command to get +an idea of what lurks beneath: > + + :lua print(vim.inspect(package.loaded)) + +Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the +"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can +be used from Lua code. + +Module conflicts are resolved by "last wins". For example if both of these +are on 'runtimepath': + runtime/lua/foo.lua + ~/.config/nvim/lua/foo.lua +then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and +"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim +finds and loads Lua modules. The conventions are similar to VimL plugins, +with some extra features. See |lua-require-example| for a walkthrough. + +============================================================================== +Importing Lua modules *lua-require* + + *lua-package-path* +Nvim automatically adjusts `package.path` and `package.cpath` according to +effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is +changed. `package.path` is adjusted by simply appending `/lua/?.lua` and +`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the +first character of `package.config`). + +Similarly to `package.path`, modified directories from 'runtimepath' are also +added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and +`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of +the existing `package.cpath` are used. Example: + +1. Given that + - 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`; + - initial (defined at compile-time or derived from + `$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains + `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`. +2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in + order: parts of the path starting from the first path component containing + question mark and preceding path separator. +3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same + as the suffix of the first path from `package.path` (i.e. `./?.so`). Which + leaves `/?.so` and `/a?d/j/g.elf`, in this order. +4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The + second one contains semicolon which is a paths separator so it is out, + leaving only `/foo/bar` and `/abc`, in order. +5. The cartesian product of paths from 4. and suffixes from 3. is taken, + giving four variants. In each variant `/lua` path segment is inserted + between path and suffix, leaving + + - `/foo/bar/lua/?.so` + - `/foo/bar/lua/a?d/j/g.elf` + - `/abc/lua/?.so` + - `/abc/lua/a?d/j/g.elf` + +6. New paths are prepended to the original `package.cpath`. + +The result will look like this: + + `/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath') + × `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`) + + = `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` + +Note: + +- To track 'runtimepath' updates, paths added at previous update are + remembered and removed at the next update, while all paths derived from the + new 'runtimepath' are prepended as described above. This allows removing + paths when path is removed from 'runtimepath', adding paths when they are + added and reordering `package.path`/`package.cpath` content if 'runtimepath' + was reordered. + +- Although adjustments happen automatically, Nvim does not track current + values of `package.path` or `package.cpath`. If you happen to delete some + paths from there you can set 'runtimepath' to trigger an update: > + let &runtimepath = &runtimepath + +- Skipping paths from 'runtimepath' which contain semicolons applies both to + `package.path` and `package.cpath`. Given that there are some badly written + plugins using shell which will not work with paths containing semicolons it + is better to not have them in 'runtimepath' at all. + +============================================================================== +Lua Syntax Information *lua-syntax-help* + +While Lua has a simple syntax, there are a few things to understand, +particularly when looking at the documentation above. + + *lua-syntax-call-function* + +Lua functions can be called in multiple ways. Consider the function: > + + local example_func = function(a, b) + print("A is: ", a) + print("B is: ", b) + end + + +The first way to call a function is: > + + example_func(1, 2) + -- ==== Result ==== + -- A is: 1 + -- B is: 2 +< + This way of calling a function is familiar to most scripting languages. + In Lua, it's important to understand that any function arguments that are + not supplied are automatically set to `nil`. For example: > + + example_func(1) + -- ==== Result ==== + -- A is: 1 + -- B is: nil +< + + Additionally, if any extra parameters are passed, they are discarded + completely. + +In Lua, it is also possible (when only one argument is passed) to call the +function without any parentheses. This is most often used to approximate +"keyword"-style arguments with a single dictionary. For example: > + + local func_with_opts = function(opts) + local will_do_foo = opts.foo + local filename = opts.filename + + ... + end + + func_with_opts { foo = true, filename = "hello.world" } +< + + In this style, each "parameter" is passed via keyword. It is still valid + to call the function in this style: > + + func_with_opts({ foo = true, filename = "hello.world" }) +< + + But often in the documentation, you will see the former rather than the + latter style, due to its brevity (this is vim after all!). + + +------------------------------------------------------------------------------ +LUA PLUGIN EXAMPLE *lua-require-example* + +The following example plugin adds a command `:MakeCharBlob` which transforms +current buffer into a long `unsigned char` array. Lua contains transformation +function in a module `lua/charblob.lua` which is imported in +`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed +to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in +this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`). + +autoload/charblob.vim: > + + function charblob#encode_buffer() + call setline(1, luaeval( + \ 'require("charblob").encode(unpack(_A))', + \ [getline(1, '$'), &textwidth, ' '])) + endfunction + +plugin/charblob.vim: > + + if exists('g:charblob_loaded') + finish + endif + let g:charblob_loaded = 1 + + command MakeCharBlob :call charblob#encode_buffer() + +lua/charblob.lua: > + + local function charblob_bytes_iter(lines) + local init_s = { + next_line_idx = 1, + next_byte_idx = 1, + lines = lines, + } + local function next(s, _) + if lines[s.next_line_idx] == nil then + return nil + end + if s.next_byte_idx > #(lines[s.next_line_idx]) then + s.next_line_idx = s.next_line_idx + 1 + s.next_byte_idx = 1 + return ('\n'):byte() + end + local ret = lines[s.next_line_idx]:byte(s.next_byte_idx) + if ret == ('\n'):byte() then + ret = 0 -- See :h NL-used-for-NUL. + end + s.next_byte_idx = s.next_byte_idx + 1 + return ret + end + return next, init_s, nil + end + + local function charblob_encode(lines, textwidth, indent) + local ret = { + 'const unsigned char blob[] = {', + indent, + } + for byte in charblob_bytes_iter(lines) do + -- .- space + number (width 3) + comma + if #(ret[#ret]) + 5 > textwidth then + ret[#ret + 1] = indent + else + ret[#ret] = ret[#ret] .. ' ' + end + ret[#ret] = ret[#ret] .. (('%3u,'):format(byte)) + end + ret[#ret + 1] = '};' + return ret + end + + return { + bytes_iter = charblob_bytes_iter, + encode = charblob_encode, + } + +============================================================================== +Commands *lua-commands* + +These commands execute a Lua chunk from either the command line (:lua, :luado) +or a file (:luafile) on the given line [range]. As always in Lua, each chunk +has its own scope (closure), so only global variables are shared between +command calls. The |lua-stdlib| modules, user modules, and anything else on +|lua-package-path| are available. + +The Lua print() function redirects its output to the Nvim message area, with +arguments separated by " " (space) instead of "\t" (tab). + + *:lua* +:[range]lua {chunk} + Executes Lua chunk {chunk}. + + Examples: > + :lua vim.api.nvim_command('echo "Hello, Nvim!"') +< To see the Lua version: > + :lua print(_VERSION) +< To see the LuaJIT version: > + :lua print(jit.version) +< + *:lua-heredoc* +:[range]lua << [endmarker] +{script} +{endmarker} + Executes Lua script {script} from within Vimscript. + {endmarker} must NOT be preceded by whitespace. You + can omit [endmarker] after the "<<" and use a dot "." + after {script} (similar to |:append|, |:insert|). + + Example: + > + function! CurrentLineInfo() + lua << EOF + local linenr = vim.api.nvim_win_get_cursor(0)[1] + local curline = vim.api.nvim_buf_get_lines( + 0, linenr, linenr + 1, false)[1] + print(string.format("Current line [%d] has %d bytes", + linenr, #curline)) + EOF + endfunction + +< Note that the `local` variables will disappear when + the block finishes. But not globals. + + *:luado* +:[range]luado {body} Executes Lua chunk "function(line, linenr) {body} end" + for each buffer line in [range], where `line` is the + current line text (without <EOL>), and `linenr` is the + current line number. If the function returns a string + that becomes the text of the corresponding buffer + line. Default [range] is the whole file: "1,$". + + Examples: + > + :luado return string.format("%s\t%d", line:reverse(), #line) + + :lua require"lpeg" + :lua -- balanced parenthesis grammar: + :lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" } + :luado if bp:match(line) then return "-->\t" .. line end +< + + *:luafile* +:[range]luafile {file} + Execute Lua script in {file}. + The whole argument is used as a single file name. + + Examples: + > + :luafile script.lua + :luafile % +< + +============================================================================== +luaeval() *lua-eval* *luaeval()* + +The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is +"luaeval". "luaeval" takes an expression string and an optional argument used +for _A inside expression and returns the result of the expression. It is +semantically equivalent in Lua to: +> + local chunkheader = "local _A = select(1, ...) return " + function luaeval (expstr, arg) + local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) + return chunk(arg) -- return typval + end + +Lua nils, numbers, strings, tables and booleans are converted to their +respective VimL types. An error is thrown if conversion of any other Lua types +is attempted. + +The magic global "_A" contains the second argument to luaeval(). + +Example: > + :echo luaeval('_A[1] + _A[2]', [40, 2]) + 42 + :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123') + foo + +Lua tables are used as both dictionaries and lists, so it is impossible to +determine whether empty table is meant to be empty list or empty dictionary. +Additionally Lua does not have integer numbers. To distinguish between these +cases there is the following agreement: + +0. Empty table is empty list. +1. Table with N incrementally growing integral numbers, starting from 1 and + ending with N is considered to be a list. +2. Table with string keys, none of which contains NUL byte, is considered to + be a dictionary. +3. Table with string keys, at least one of which contains NUL byte, is also + considered to be a dictionary, but this time it is converted to + a |msgpack-special-map|. + *lua-special-tbl* +4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point + value: + - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to + a floating-point 1.0. Note that by default integral Lua numbers are + converted to |Number|s, non-integral are converted to |Float|s. This + variant allows integral |Float|s. + - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty + dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is + converted to a dictionary `{'a': 42}`: non-string keys are ignored. + Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. + are errors. + - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well + as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not + form a 1-step sequence from 1 to N are ignored, as well as all + non-integral keys. + +Examples: > + + :echo luaeval('math.pi') + :function Rand(x,y) " random uniform between x and y + : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) + : endfunction + :echo Rand(1,10) + +Note: second argument to `luaeval` undergoes VimL to Lua conversion +("marshalled"), so changes to Lua containers do not affect values in VimL. +Return value is also always converted. When converting, +|msgpack-special-dict|s are treated specially. + +============================================================================== +Vimscript v:lua interface *v:lua-call* + +From Vimscript the special `v:lua` prefix can be used to call Lua functions +which are global or accessible from global tables. The expression > + v:lua.func(arg1, arg2) +is equivalent to the Lua chunk > + return func(...) +where the args are converted to Lua values. The expression > + v:lua.somemod.func(args) +is equivalent to the Lua chunk > + return somemod.func(...) + +You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc. +For example consider the following Lua omnifunc handler: > + + function mymod.omnifunc(findstart, base) + if findstart == 1 then + return 0 + else + return {'stuff', 'steam', 'strange things'} + end + end + vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc') + +Note: the module ("mymod" in the above example) must be a Lua global. + +Note: `v:lua` without a call is not allowed in a Vimscript expression: +|Funcref|s cannot represent Lua functions. The following are errors: > + + let g:Myvar = v:lua.myfunc " Error + call SomeFunc(v:lua.mycallback) " Error + let g:foo = v:lua " Error + let g:foo = v:['lua'] " Error + + +============================================================================== +Lua standard modules *lua-stdlib* + +The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes +various functions and sub-modules. It is always loaded, thus require("vim") +is unnecessary. + +You can peek at the module properties: > + + :lua print(vim.inspect(vim)) + +Result is something like this: > + + { + _os_proc_children = <function 1>, + _os_proc_info = <function 2>, + ... + api = { + nvim__id = <function 5>, + nvim__id_array = <function 6>, + ... + }, + deepcopy = <function 106>, + gsplit = <function 107>, + ... + } + +To find documentation on e.g. the "deepcopy" function: > + + :help vim.deepcopy() + +Note that underscore-prefixed functions (e.g. "_os_proc_children") are +internal/private and must not be used by plugins. + +------------------------------------------------------------------------------ +VIM.LOOP *lua-loop* *vim.loop* + +`vim.loop` exposes all features of the Nvim event-loop. This is a low-level +API that provides functionality for networking, filesystem, and process +management. Try this command to see available functions: > + + :lua print(vim.inspect(vim.loop)) + +Reference: http://docs.libuv.org +Examples: https://github.com/luvit/luv/tree/master/examples + + *E5560* *lua-loop-callbacks* +It is an error to directly invoke `vim.api` functions (except |api-fast|) in +`vim.loop` callbacks. For example, this is an error: > + + local timer = vim.loop.new_timer() + timer:start(1000, 0, function() + vim.api.nvim_command('echomsg "test"') + end) + +To avoid the error use |vim.schedule_wrap()| to defer the callback: > + + local timer = vim.loop.new_timer() + timer:start(1000, 0, vim.schedule_wrap(function() + vim.api.nvim_command('echomsg "test"') + end)) + +(For one-shot timers, see |vim.defer_fn()|, which automatically adds the wrapping.) + +Example: repeating timer + 1. Save this code to a file. + 2. Execute it with ":luafile %". > + + -- Create a timer handle (implementation detail: uv_timer_t). + local timer = vim.loop.new_timer() + local i = 0 + -- Waits 1000ms, then repeats every 750ms until timer:close(). + timer:start(1000, 750, function() + print('timer invoked! i='..tostring(i)) + if i > 4 then + timer:close() -- Always close handles to avoid leaks. + end + i = i + 1 + end) + print('sleeping'); + + +Example: File-change detection *watch-file* + 1. Save this code to a file. + 2. Execute it with ":luafile %". + 3. Use ":Watch %" to watch any file. + 4. Try editing the file from another text editor. + 5. Observe that the file reloads in Nvim (because on_change() calls + |:checktime|). > + + local w = vim.loop.new_fs_event() + local function on_change(err, fname, status) + -- Do work... + vim.api.nvim_command('checktime') + -- Debounce: stop/start. + w:stop() + watch_file(fname) + end + function watch_file(fname) + local fullpath = vim.api.nvim_call_function( + 'fnamemodify', {fname, ':p'}) + w:start(fullpath, {}, vim.schedule_wrap(function(...) + on_change(...) end)) + end + vim.api.nvim_command( + "command! -nargs=1 Watch call luaeval('watch_file(_A)', expand('<args>'))") + + +Example: TCP echo-server *tcp-server* + 1. Save this code to a file. + 2. Execute it with ":luafile %". + 3. Note the port number. + 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): > + + local function create_server(host, port, on_connect) + local server = vim.loop.new_tcp() + server:bind(host, port) + server:listen(128, function(err) + assert(not err, err) -- Check for errors. + local sock = vim.loop.new_tcp() + server:accept(sock) -- Accept client connection. + on_connect(sock) -- Start reading messages. + end) + return server + end + local server = create_server('0.0.0.0', 0, function(sock) + sock:read_start(function(err, chunk) + assert(not err, err) -- Check for errors. + if chunk then + sock:write(chunk) -- Echo received messages to the channel. + else -- EOF (stream closed). + sock:close() -- Always close handles to avoid leaks. + end + end) + end) + print('TCP echo-server listening on port: '..server:getsockname().port) + +------------------------------------------------------------------------------ +VIM.TREESITTER *lua-treesitter* + +Nvim integrates the tree-sitter library for incremental parsing of buffers. + +Currently Nvim does not provide the tree-sitter parsers, instead these must +be built separately, for instance using the tree-sitter utility. The only +exception is a C parser being included in official builds for testing +purposes. Parsers are searched for as `parser/{lang}.*` in any 'runtimepath' +directory. A parser can also be loaded manually using a full path: > + + vim.treesitter.require_language("python", "/path/to/python.so") + +<Create a parser for a buffer and a given language (if another plugin uses the +same buffer/language combination, it will be safely reused). Use > + + parser = vim.treesitter.get_parser(bufnr, lang) + +<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this +doesn't work yet for some filetypes like "cpp") Currently, the parser will be +retained for the lifetime of a buffer but this is subject to change. A plugin +should keep a reference to the parser object as long as it wants incremental +updates. + +Parser methods *lua-treesitter-parser* + +tsparser:parse() *tsparser:parse()* +Whenever you need to access the current syntax tree, parse the buffer: > + + tstree = parser:parse() + +<This will return an immutable tree that represents the current state of the +buffer. When the plugin wants to access the state after a (possible) edit +it should call `parse()` again. If the buffer wasn't edited, the same tree will +be returned again without extra work. If the buffer was parsed before, +incremental parsing will be done of the changed parts. + +NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must +call `get_parser()` before you register your callback. But preferably parsing +shouldn't be done directly in the change callback anyway as they will be very +frequent. Rather a plugin that does any kind of analysis on a tree should use +a timer to throttle too frequent updates. + +tsparser:set_included_ranges(ranges) *tsparser:set_included_ranges()* + Changes the ranges the parser should consider. This is used for + language injection. `ranges` should be of the form (all zero-based): > + { + {start_node, end_node}, + ... + } +< + NOTE: `start_node` and `end_node` are both inclusive. + +Tree methods *lua-treesitter-tree* + +tstree:root() *tstree:root()* + Return the root node of this tree. + + +Node methods *lua-treesitter-node* + +tsnode:parent() *tsnode:parent()* + Get the node's immediate parent. + +tsnode:child_count() *tsnode:child_count()* + Get the node's number of children. + +tsnode:child(N) *tsnode:child()* + Get the node's child at the given index, where zero represents the + first child. + +tsnode:named_child_count() *tsnode:named_child_count()* + Get the node's number of named children. + +tsnode:named_child(N) *tsnode:named_child()* + Get the node's named child at the given index, where zero represents + the first named child. + +tsnode:start() *tsnode:start()* + Get the node's start position. Return three values: the row, column + and total byte count (all zero-based). + +tsnode:end_() *tsnode:end_()* + Get the node's end position. Return three values: the row, column + and total byte count (all zero-based). + +tsnode:range() *tsnode:range()* + Get the range of the node. Return four values: the row, column + of the start position, then the row, column of the end position. + +tsnode:type() *tsnode:type()* + Get the node's type as a string. + +tsnode:symbol() *tsnode:symbol()* + Get the node's type as a numerical id. + +tsnode:named() *tsnode:named()* + Check if the node is named. Named nodes correspond to named rules in + the grammar, whereas anonymous nodes correspond to string literals + in the grammar. + +tsnode:missing() *tsnode:missing()* + Check if the node is missing. Missing nodes are inserted by the + parser in order to recover from certain kinds of syntax errors. + +tsnode:has_error() *tsnode:has_error()* + Check if the node is a syntax error or contains any syntax errors. + +tsnode:sexpr() *tsnode:sexpr()* + Get an S-expression representing the node as a string. + +tsnode:descendant_for_range(start_row, start_col, end_row, end_col) + *tsnode:descendant_for_range()* + Get the smallest node within this node that spans the given range of + (row, column) positions + +tsnode:named_descendant_for_range(start_row, start_col, end_row, end_col) + *tsnode:named_descendant_for_range()* + Get the smallest named node within this node that spans the given + range of (row, column) positions + +Query methods *lua-treesitter-query* + +Tree-sitter queries are supported, with some limitations. Currently, the only +supported match predicate is `eq?` (both comparing a capture against a string +and two captures against each other). + +vim.treesitter.parse_query(lang, query) + *vim.treesitter.parse_query(()* + Parse the query as a string. (If the query is in a file, the caller + should read the contents into a string before calling). + +query:iter_captures(node, bufnr, start_row, end_row) + *query:iter_captures()* + Iterate over all captures from all matches inside a `node`. + `bufnr` is needed if the query contains predicates, then the caller + must ensure to use a freshly parsed tree consistent with the current + text of the buffer. `start_row` and `end_row` can be used to limit + matches inside a row range (this is typically used with root node + as the node, i e to get syntax highlight matches in the current + viewport) + + The iterator returns two values, a numeric id identifying the capture + and the captured node. The following example shows how to get captures + by name: +> + for id, node in query:iter_captures(tree:root(), bufnr, first, last) do + local name = query.captures[id] -- name of the capture in the query + -- typically useful info about the node: + local type = node:type() -- type of the captured node + local row1, col1, row2, col2 = node:range() -- range of the capture + ... use the info here ... + end +< +query:iter_matches(node, bufnr, start_row, end_row) + *query:iter_matches()* + Iterate over all matches within a node. The arguments are the same as + for |query:iter_captures()| but the iterated values are different: + an (1-based) index of the pattern in the query, and a table mapping + capture indices to nodes. If the query has more than one pattern + the capture table might be sparse, and e.g. `pairs` should be used and not + `ipairs`. Here an example iterating over all captures in + every match: +> + for pattern, match in cquery:iter_matches(tree:root(), bufnr, first, last) do + for id,node in pairs(match) do + local name = query.captures[id] + -- `node` was captured by the `name` capture in the match + ... use the info here ... + end + end +> +Treesitter syntax highlighting (WIP) *lua-treesitter-highlight* + +NOTE: This is a partially implemented feature, and not usable as a default +solution yet. What is documented here is a temporary interface indented +for those who want to experiment with this feature and contribute to +its development. + +Highlights are defined in the same query format as in the tree-sitter highlight +crate, which some limitations and additions. Set a highlight query for a +buffer with this code: > + + local query = [[ + "for" @keyword + "if" @keyword + "return" @keyword + + (string_literal) @string + (number_literal) @number + (comment) @comment + + (preproc_function_def name: (identifier) @function) + + ; ... more definitions + ]] + + highlighter = vim.treesitter.TSHighlighter.new(query, bufnr, lang) + -- alternatively, to use the current buffer and its filetype: + -- highlighter = vim.treesitter.TSHighlighter.new(query) + + -- Don't recreate the highlighter for the same buffer, instead + -- modify the query like this: + local query2 = [[ ... ]] + highlighter:set_query(query2) + +As mentioned above the supported predicate is currently only `eq?`. `match?` +predicates behave like matching always fails. As an addition a capture which +begin with an upper-case letter like `@WarningMsg` will map directly to this +highlight group, if defined. Also if the predicate begins with upper-case and +contains a dot only the part before the first will be interpreted as the +highlight group. As an example, this warns of a binary expression with two +identical identifiers, highlighting both as |hl-WarningMsg|: > + + ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) + (eq? @WarningMsg.left @WarningMsg.right)) + +------------------------------------------------------------------------------ +VIM.HIGHLIGHT *lua-highlight* + +Nvim includes a function for highlighting a selection on yank (see for example +https://github.com/machakann/vim-highlightedyank). To enable it, add +> + au TextYankPost * silent! lua vim.highlight.on_yank() +< +to your `init.vim`. You can customize the highlight group and the duration of +the highlight via +> + au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150} +< +If you want to exclude visual selections from highlighting on yank, use +> + au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false} +< + +vim.highlight.on_yank({opts}) *vim.highlight.on_yank()* + Highlights the yanked text. The fields of the optional dict {opts} + control the highlight: + - {higroup} highlight group for yanked region (default `"IncSearch"`) + - {timeout} time in ms before highlight is cleared (default `150`) + - {on_macro} highlight when executing macro (default `false`) + - {on_visual} highlight when yanking visual selection (default `true`) + - {event} event structure (default `vim.v.event`) + +vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {rtype}, {inclusive}) + *vim.highlight.range()* + Highlights the range between {start} and {finish} (tuples of {line,col}) + in buffer {bufnr} with the highlight group {higroup} using the namespace + {ns}. Optional arguments are the type of range (characterwise, linewise, + or blockwise, see |setreg|; default to characterwise) and whether the + range is inclusive (default false). + +------------------------------------------------------------------------------ +VIM.REGEX *lua-regex* + +Vim regexes can be used directly from lua. Currently they only allow +matching within a single line. + +vim.regex({re}) *vim.regex()* + + Parse the regex {re} and return a regex object. 'magic' and + 'ignorecase' options are ignored, lua regexes always defaults to magic + and ignoring case. The behavior can be changed with flags in + the beginning of the string |/magic|. + +Regex objects support the following methods: + +regex:match_str({str}) *regex:match_str()* + Match the string against the regex. If the string should match the + regex precisely, surround the regex with `^` and `$`. + If the was a match, the byte indices for the beginning and end of + the match is returned. When there is no match, `nil` is returned. + As any integer is truth-y, `regex:match()` can be directly used + as a condition in an if-statement. + +regex:match_line({bufnr}, {line_idx}[, {start}, {end}]) *regex:match_line()* + Match line {line_idx} (zero-based) in buffer {bufnr}. If {start} and + {end} are supplied, match only this byte index range. Otherwise see + |regex:match_str()|. If {start} is used, then the returned byte + indices will be relative {start}. + +------------------------------------------------------------------------------ +VIM *lua-builtin* + +vim.api.{func}({...}) *vim.api* + Invokes Nvim |API| function {func} with arguments {...}. + Example: call the "nvim_get_current_line()" API function: > + print(tostring(vim.api.nvim_get_current_line())) + +vim.call({func}, {...}) *vim.call()* + Invokes |vim-function| or |user-function| {func} with arguments {...}. + See also |vim.fn|. Equivalent to: > + vim.fn[func]({...}) + +vim.in_fast_event() *vim.in_fast_event()* + Returns true if the code is executing as part of a "fast" event + handler, where most of the API is disabled. These are low-level events + (e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls + for input. When this is `false` most API functions are callable (but + may be subject to other restrictions such as |textlock|). + +vim.NIL *vim.NIL* + Special value used to represent NIL in msgpack-rpc and |v:null| in + vimL interaction, and similar cases. Lua `nil` cannot be used as + part of a lua table representing a Dictionary or Array, as it + is equivalent to a missing value: `{"foo", nil}` is the same as + `{"foo"}` + +vim.empty_dict() *vim.empty_dict()* + Creates a special table which will be converted to an empty + dictionary when converting lua values to vimL or API types. The + table is empty, and this property is marked using a metatable. An + empty table `{}` without this metatable will default to convert to + an array/list. + + Note: if numeric keys are added to the table, the metatable will be + ignored and the dict converted to a list/array anyway. + +vim.region({bufnr}, {pos1}, {pos2}, {type}, {inclusive}) *vim.region()* + Converts a selection specified by the buffer ({bufnr}), starting + position ({pos1}, a zero-indexed pair `{line1,column1}`), ending + position ({pos2}, same format as {pos1}), the type of the register + for the selection ({type}, see |regtype|), and a boolean indicating + whether the selection is inclusive or not, into a zero-indexed table + of linewise selections of the form `{linenr = {startcol, endcol}}` . + +vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* + Sends {event} to {channel} via |RPC| and returns immediately. + If {channel} is 0, the event is broadcast to all channels. + + This function also works in a fast callback |lua-loop-callbacks|. + +vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()* + Sends a request to {channel} to invoke {method} via + |RPC| and blocks until a response is received. + + Note: NIL values as part of the return value is represented as + |vim.NIL| special value + +vim.stricmp({a}, {b}) *vim.stricmp()* + Compares strings case-insensitively. Returns 0, 1 or -1 if strings + are equal, {a} is greater than {b} or {a} is lesser than {b}, + respectively. + +vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()* + Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not + supplied, the length of the string is used. All indicies are zero-based. + Returns two values: the UTF-32 and UTF-16 indicies respectively. + + Embedded NUL bytes are treated as terminating the string. Invalid + UTF-8 bytes, and embedded surrogates are counted as one code + point each. An {index} in the middle of a UTF-8 sequence is rounded + upwards to the end of that sequence. + +vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()* + Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not + supplied, it defaults to false (use UTF-32). Returns the byte index. + + Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index} + in the middle of a UTF-16 sequence is rounded upwards to the end of that + sequence. + +vim.schedule({callback}) *vim.schedule()* + Schedules {callback} to be invoked soon by the main event-loop. Useful + to avoid |textlock| or other temporary restrictions. + + +vim.defer_fn({fn}, {timeout}) *vim.defer_fn* + Defers calling {fn} until {timeout} ms passes. Use to do a one-shot timer + that calls {fn}. + + Note: The {fn} is |schedule_wrap|ped automatically, so API functions are + safe to call. + + Parameters: ~ + {fn} Callback to call once {timeout} expires + {timeout} Time in ms to wait before calling {fn} + + Returns: ~ + |vim.loop|.new_timer() object + +vim.wait({time}, {callback} [, {interval}]) *vim.wait()* + Wait for {time} in milliseconds until {callback} returns `true`. + + Executes {callback} immediately and at approximately {interval} + milliseconds (default 200). Nvim still processes other events during + this time. + + + Returns: ~ + If {callback} returns `true` during the {time}: + `true, nil` + + If {callback} never returns `true` during the {time}: + `false, -1` + + If {callback} is interrupted during the {time}: + `false, -2` + + If {callback} errors, the error is raised. + + Examples: > + + --- + -- Wait for 100 ms, allowing other events to process + vim.wait(100, function() end) + + --- + -- Wait for 100 ms or until global variable set. + vim.wait(100, function() return vim.g.waiting_for_var end) + + --- + -- Wait for 1 second or until global variable set, checking every ~500 ms + vim.wait(1000, function() return vim.g.waiting_for_var end, 500) + + --- + -- Schedule a function to set a value in 100ms + vim.defer_fn(function() vim.g.timer_result = true end, 100) + + -- Would wait ten seconds if results blocked. Actually only waits 100 ms + if vim.wait(10000, function() return vim.g.timer_result end) then + print('Only waiting a little bit of time!') + end +< + +vim.fn.{func}({...}) *vim.fn* + Invokes |vim-function| or |user-function| {func} with arguments {...}. + To call autoload functions, use the syntax: > + vim.fn['some#function']({...}) +< + Unlike vim.api.|nvim_call_function| this converts directly between Vim + objects and Lua objects. If the Vim function returns a float, it will + be represented directly as a Lua number. Empty lists and dictionaries + both are represented by an empty table. + + Note: |v:null| values as part of the return value is represented as + |vim.NIL| special value + + Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only + enumerates functions that were called at least once. + +vim.type_idx *vim.type_idx* + Type index for use in |lua-special-tbl|. Specifying one of the + values from |vim.types| allows typing the empty table (it is + unclear whether empty Lua table represents empty list or empty array) + and forcing integral numbers to be |Float|. See |lua-special-tbl| for + more details. + +vim.val_idx *vim.val_idx* + Value index for tables representing |Float|s. A table representing + floating-point value 1.0 looks like this: > + { + [vim.type_idx] = vim.types.float, + [vim.val_idx] = 1.0, + } +< See also |vim.type_idx| and |lua-special-tbl|. + +vim.types *vim.types* + Table with possible values for |vim.type_idx|. Contains two sets + of key-value pairs: first maps possible values for |vim.type_idx| + to human-readable strings, second maps human-readable type names to + values for |vim.type_idx|. Currently contains pairs for `float`, + `array` and `dictionary` types. + + Note: one must expect that values corresponding to `vim.types.float`, + `vim.types.array` and `vim.types.dictionary` fall under only two + following assumptions: + 1. Value may serve both as a key and as a value in a table. Given the + properties of Lua tables this basically means “value is not `nil`”. + 2. For each value in `vim.types` table `vim.types[vim.types[value]]` + is the same as `value`. + No other restrictions are put on types, and it is not guaranteed that + values corresponding to `vim.types.float`, `vim.types.array` and + `vim.types.dictionary` will not change or that `vim.types` table will + only contain values for these three types. + +============================================================================== +Vim Internal Variables *lua-vim-internal-variables* + +Built-in Vim dictionaries can be accessed and set idiomatically in Lua by each +of the following tables. + +To set a value: > + + vim.g.my_global_variable = 5 +< + +To read a value: > + + print(vim.g.my_global_variable) +< + +To delete a value: > + + vim.g.my_global_variable = nil +< + +vim.g *vim.g* + Table with values from |g:| + Keys with no values set will result in `nil`. + +vim.b *vim.b* + Gets a buffer-scoped (b:) variable for the current buffer. + Keys with no values set will result in `nil`. + +vim.w *vim.w* + Gets a window-scoped (w:) variable for the current window. + Keys with no values set will result in `nil`. + +vim.t *vim.t* + Gets a tabpage-scoped (t:) variable for the current table. + Keys with no values set will result in `nil`. + +vim.v *vim.v* + Gets a v: variable. + Keys with no values set will result in `nil`. + + +Vim Internal Options *lua-vim-internal-options* + +Read, set and clear vim |options| in Lua by each of the following tables. + + +vim.o *vim.o* + Table with values from |options| + Invalid keys will result in an error. + +vim.bo *vim.bo* + Gets a buffer-scoped option for the current buffer. + Invalid keys will result in an error. + +vim.wo *vim.wo* + Gets a window-scoped option for the current window. + Invalid keys will result in an error. + + +============================================================================== +Lua module: vim *lua-vim* + +inspect({object}, {options}) *vim.inspect()* + Return a human-readable representation of the given object. + + See also: ~ + https://github.com/kikito/inspect.lua + https://github.com/mpeterv/vinspect + +make_meta_accessor({get}, {set}, {del}) *vim.make_meta_accessor()* + TODO: Documentation + +paste({lines}, {phase}) *vim.paste()* + Paste handler, invoked by |nvim_paste()| when a conforming UI + (such as the |TUI|) pastes text into the editor. + + Example: To remove ANSI color codes when pasting: > + + vim.paste = (function(overridden) + return function(lines, phase) + for i,line in ipairs(lines) do + -- Scrub ANSI color codes from paste input. + lines[i] = line:gsub('\27%[[0-9;mK]+', '') + end + overridden(lines, phase) + end + end)(vim.paste) +< + + Parameters: ~ + {lines} |readfile()|-style list of lines to paste. + |channel-lines| + {phase} -1: "non-streaming" paste: the call contains all + lines. If paste is "streamed", `phase` indicates the stream state: + • 1: starts the paste (exactly once) + • 2: continues the paste (zero or more times) + • 3: ends the paste (exactly once) + + Return: ~ + false if client should cancel the paste. + + See also: ~ + |paste| + +schedule_wrap({cb}) *vim.schedule_wrap()* + Defers callback `cb` until the Nvim API is safe to call. + + See also: ~ + |lua-loop-callbacks| + |vim.schedule()| + |vim.in_fast_event()| + + + + +deep_equal({a}, {b}) *vim.deep_equal()* + TODO: Documentation + +deepcopy({orig}) *vim.deepcopy()* + Returns a deep copy of the given object. Non-table objects are + copied as in a typical Lua assignment, whereas table objects + are copied recursively. Functions are naively copied, so + functions in the copied table point to the same functions as + those in the input table. Userdata and threads are not copied + and will throw an error. + + Parameters: ~ + {orig} Table to copy + + Return: ~ + New table of copied keys and (nested) values. + +endswith({s}, {suffix}) *vim.endswith()* + Tests if `s` ends with `suffix` . + + Parameters: ~ + {s} (string) a string + {suffix} (string) a suffix + + Return: ~ + (boolean) true if `suffix` is a suffix of s + +gsplit({s}, {sep}, {plain}) *vim.gsplit()* + Splits a string at each instance of a separator. + + Parameters: ~ + {s} String to split + {sep} Separator string or pattern + {plain} If `true` use `sep` literally (passed to + String.find) + + Return: ~ + Iterator over the split components + + See also: ~ + |vim.split()| + https://www.lua.org/pil/20.2.html + http://lua-users.org/wiki/StringLibraryTutorial + +is_callable({f}) *vim.is_callable()* + Returns true if object `f` can be called as a function. + + Parameters: ~ + {f} Any object + + Return: ~ + true if `f` is callable, else false + +list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* + Extends a list-like table with the values of another list-like + table. + + NOTE: This mutates dst! + + Parameters: ~ + {dst} list which will be modified and appended to. + {src} list from which values will be inserted. + {start} Start index on src. defaults to 1 + {finish} Final index on src. defaults to #src + + Return: ~ + dst + + See also: ~ + |vim.tbl_extend()| + +pesc({s}) *vim.pesc()* + Escapes magic chars in a Lua pattern. + + Parameters: ~ + {s} String to escape + + Return: ~ + %-escaped pattern string + + See also: ~ + https://github.com/rxi/lume + +split({s}, {sep}, {plain}) *vim.split()* + Splits a string at each instance of a separator. + + Examples: > + split(":aa::b:", ":") --> {'','aa','','bb',''} + split("axaby", "ab?") --> {'','x','y'} + split(x*yz*o, "*", true) --> {'x','yz','o'} +< + + Parameters: ~ + {s} String to split + {sep} Separator string or pattern + {plain} If `true` use `sep` literally (passed to + String.find) + + Return: ~ + List-like table of the split components. + + See also: ~ + |vim.gsplit()| + +startswith({s}, {prefix}) *vim.startswith()* + Tests if `s` starts with `prefix` . + + Parameters: ~ + {s} (string) a string + {prefix} (string) a prefix + + Return: ~ + (boolean) true if `prefix` is a prefix of s + +tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* + Add the reverse lookup values to an existing table. For + example: tbl_add_reverse_lookup { A = 1 } == { [1] = 'A , A = 1 }` + + Parameters: ~ + {o} table The table to add the reverse to. + +tbl_contains({t}, {value}) *vim.tbl_contains()* + Checks if a list-like (vector) table contains `value` . + + Parameters: ~ + {t} Table to check + {value} Value to compare + + Return: ~ + true if `t` contains `value` + +tbl_count({t}) *vim.tbl_count()* + Counts the number of non-nil values in table `t` . +> + + vim.tbl_count({ a=1, b=2 }) => 2 + vim.tbl_count({ 1, 2 }) => 2 +< + + Parameters: ~ + {t} Table + + Return: ~ + Number that is the number of the value in table + + See also: ~ + https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua + +tbl_deep_extend({behavior}, {...}) *vim.tbl_deep_extend()* + Merges recursively two or more map-like tables. + + Parameters: ~ + {behavior} Decides what to do if a key is found in more + than one map: + • "error": raise an error + • "keep": use value from the leftmost map + • "force": use value from the rightmost map + {...} Two or more map-like tables. + + See also: ~ + |tbl_extend()| + +tbl_extend({behavior}, {...}) *vim.tbl_extend()* + Merges two or more map-like tables. + + Parameters: ~ + {behavior} Decides what to do if a key is found in more + than one map: + • "error": raise an error + • "keep": use value from the leftmost map + • "force": use value from the rightmost map + {...} Two or more map-like tables. + + See also: ~ + |extend()| + +tbl_filter({func}, {t}) *vim.tbl_filter()* + Filter a table using a predicate function + + Parameters: ~ + {func} function or callable table + {t} table + +tbl_flatten({t}) *vim.tbl_flatten()* + Creates a copy of a list-like table such that any nested + tables are "unrolled" and appended to the result. + + Parameters: ~ + {t} List-like table + + Return: ~ + Flattened copy of the given list-like table. + + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + +tbl_isempty({t}) *vim.tbl_isempty()* + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua@paramt Table to check + +tbl_islist({t}) *vim.tbl_islist()* + Determine whether a Lua table can be treated as an array. + + An empty table `{}` will default to being treated as an array. + Use `vim.emtpy_dict()` to create a table treated as an empty + dict. Empty tables returned by `rpcrequest()` and `vim.fn` + functions can be checked using this function whether they + represent empty API arrays and vimL lists. + + Parameters: ~ + {t} Table + + Return: ~ + `true` if array-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 return table of keys is not guaranteed. + + Parameters: ~ + {t} Table + + Return: ~ + list of keys + + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + +tbl_map({func}, {t}) *vim.tbl_map()* + Apply a function to all values of a table. + + Parameters: ~ + {func} function or callable table + {t} table + +tbl_values({t}) *vim.tbl_values()* + Return a list of all values used in a table. However, the + order of the return table of values is not guaranteed. + + Parameters: ~ + {t} Table + + Return: ~ + list of values + +trim({s}) *vim.trim()* + Trim whitespace (Lua pattern "%s") from both sides of a + string. + + Parameters: ~ + {s} String to trim + + Return: ~ + String with whitespace removed from its beginning and end + + See also: ~ + https://www.lua.org/pil/20.2.html + +validate({opt}) *vim.validate()* + Validates a parameter specification (types and values). + + Usage example: > + + function user.new(name, age, hobbies) + vim.validate{ + name={name, 'string'}, + age={age, 'number'}, + hobbies={hobbies, 'table'}, + } + ... + end +< + + Examples with explicit argument values (can be run directly): > + + vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} + => NOP (success) +< +> + vim.validate{arg1={1, 'table'}} + => error('arg1: expected table, got number') +< +> + vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} + => error('arg1: expected even number, got 3') +< + + Parameters: ~ + {opt} Map of parameter names to validations. Each key is + a parameter name; each value is a tuple in one of + these forms: + 1. (arg_value, type_name, optional) + • arg_value: argument value + • type_name: string type name, one of: ("table", + "t", "string", "s", "number", "n", "boolean", + "b", "function", "f", "nil", "thread", + "userdata") + • optional: (optional) boolean, if true, `nil` + is valid + + 2. (arg_value, fn, msg) + • arg_value: argument value + • fn: any function accepting one argument, + returns true if and only if the argument is + valid + • msg: (optional) error string if validation + fails + + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index abe86749c4..ed31ecc42e 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -522,10 +522,9 @@ single CTRL-V (you have to type CTRL-V two times). You can create an empty {rhs} by typing nothing after a single CTRL-V (you have to type CTRL-V two times). Unfortunately, you cannot do this in a vimrc file. - *<Nop>* + |<Nop>| An easier way to get a mapping that doesn't produce anything, is to use -"<Nop>" for the {rhs}. This only works when the |<>| notation is enabled. -For example, to make sure that function key 8 does nothing at all: > +"<Nop>" for the {rhs}. For example, to disable function key 8: > :map <F8> <Nop> :map! <F8> <Nop> < @@ -786,7 +785,7 @@ g@{motion} Call the function set by the 'operatorfunc' option. character of the text. The function is called with one String argument: "line" {motion} was |linewise| - "char" {motion} was |characterwise| + "char" {motion} was |charwise| "block" {motion} was |blockwise-visual| Although "block" would rarely appear, since it can only result from Visual mode where "g@" is not useful. diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index e8c76adad4..43b1eb5e0c 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -69,7 +69,7 @@ See `:messages` above. LIST OF MESSAGES *E222* *E228* *E232* *E256* *E293* *E298* *E304* *E317* *E318* *E356* *E438* *E439* *E440* *E316* *E320* *E322* - *E323* *E341* *E473* *E570* *E685* *E950* > + *E323* *E341* *E473* *E570* *E685* *E292* > Add to read buffer makemap: Illegal mode Cannot create BalloonEval with both message and callback @@ -556,7 +556,8 @@ allowed for the command that was used. Vim was not able to create a swap file. You can still edit the file, but if Vim unexpectedly exits the changes will be lost. And Vim may consume a lot of memory when editing a big file. You may want to change the 'directory' option -to avoid this error. See |swap-file|. +to avoid this error. This error is not given when 'directory' is empty. See +|swap-file|. *E140* > Use ! to write partial buffer @@ -670,21 +671,20 @@ being disabled. Remove the 'C' flag from the 'cpoptions' option to enable it. *E471* > Argument required -This happens when an Ex command with mandatory argument(s) was executed, but -no argument has been specified. +Ex command was executed without a mandatory argument(s). *E474* *E475* *E983* > Invalid argument Invalid argument: {arg} Duplicate argument: {arg} -An Ex command or function has been executed, but an invalid argument has been -specified. +Ex command or function was given an invalid argument. Or |jobstart()| or +|system()| was given a non-executable command. *E488* > Trailing characters -An argument has been added to an Ex command that does not permit one. +An argument was given to an Ex command that does not permit one. *E477* *E478* > No ! allowed diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt index 03c48b962d..2a10a7051d 100644 --- a/runtime/doc/mlang.txt +++ b/runtime/doc/mlang.txt @@ -185,8 +185,8 @@ you can do it without restarting Vim: > :source $VIMRUNTIME/menu.vim Each part of a menu path is translated separately. The result is that when -"Help" is translated to "Hilfe" and "Overview" to "berblick" then -"Help.Overview" will be translated to "Hilfe.berblick". +"Help" is translated to "Hilfe" and "Overview" to "Überblick" then +"Help.Overview" will be translated to "Hilfe.Überblick". ============================================================================== 3. Scripts *multilang-scripts* diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 97c7d1cc43..a6c072e489 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -60,11 +60,11 @@ After applying the operator the cursor is mostly left at the start of the text that was operated upon. For example, "yfe" doesn't move the cursor, but "yFe" moves the cursor leftwards to the "e" where the yank started. - *linewise* *characterwise* + *linewise* *charwise* *characterwise* The operator either affects whole lines, or the characters between the start and end position. Generally, motions that move between lines affect lines (are linewise), and motions that move within a line affect characters (are -characterwise). However, there are some exceptions. +charwise). However, there are some exceptions. *exclusive* *inclusive* Character motion is either inclusive or exclusive. When inclusive, the @@ -106,10 +106,10 @@ This cannot be repeated: > d:if 1<CR> call search("f")<CR> endif<CR> -Note that when using ":" any motion becomes characterwise exclusive. +Note that when using ":" any motion becomes charwise exclusive. *forced-motion* -FORCING A MOTION TO BE LINEWISE, CHARACTERWISE OR BLOCKWISE +FORCING A MOTION TO BE LINEWISE, CHARWISE OR BLOCKWISE When a motion is not of the type you would like to use, you can force another type by using "v", "V" or CTRL-V just after the operator. @@ -121,22 +121,22 @@ deletes from the cursor position until the character below the cursor > d<C-V>j deletes the character under the cursor and the character below the cursor. > -Be careful with forcing a linewise movement to be used characterwise or -blockwise, the column may not always be defined. +Be careful with forcing a linewise movement to be used charwise or blockwise, +the column may not always be defined. *o_v* v When used after an operator, before the motion command: Force - the operator to work characterwise, also when the motion is + the operator to work charwise, also when the motion is linewise. If the motion was linewise, it will become |exclusive|. - If the motion already was characterwise, toggle + If the motion already was charwise, toggle inclusive/exclusive. This can be used to make an exclusive motion inclusive and an inclusive motion exclusive. *o_V* V When used after an operator, before the motion command: Force the operator to work linewise, also when the motion is - characterwise. + charwise. *o_CTRL-V* CTRL-V When used after an operator, before the motion command: Force @@ -219,6 +219,12 @@ g^ When lines wrap ('wrap' on): To the first non-blank gm Like "g0", but half a screenwidth to the right (or as much as possible). + *gM* +gM Like "g0", but to halfway the text of the line. + With a count: to this percentage of text in the line. + Thus "10gM" is near the start of the text and "90gM" + is near the end of the text. + *g$* *g<End>* g$ or g<End> When lines wrap ('wrap' on): To the last character of the screen line and [count - 1] screen lines downward @@ -412,35 +418,35 @@ between Vi and Vim. 5. Text object motions *object-motions* *(* -( [count] sentences backward. |exclusive| motion. +( [count] |sentence|s backward. |exclusive| motion. *)* -) [count] sentences forward. |exclusive| motion. +) [count] |sentence|s forward. |exclusive| motion. *{* -{ [count] paragraphs backward. |exclusive| motion. +{ [count] |paragraph|s backward. |exclusive| motion. *}* -} [count] paragraphs forward. |exclusive| motion. +} [count] |paragraph|s forward. |exclusive| motion. *]]* -]] [count] sections forward or to the next '{' in the +]] [count] |section|s forward or to the next '{' in the first column. When used after an operator, then also stops below a '}' in the first column. |exclusive| Note that |exclusive-linewise| often applies. *][* -][ [count] sections forward or to the next '}' in the +][ [count] |section|s forward or to the next '}' in the first column. |exclusive| Note that |exclusive-linewise| often applies. *[[* -[[ [count] sections backward or to the previous '{' in +[[ [count] |section|s backward or to the previous '{' in the first column. |exclusive| Note that |exclusive-linewise| often applies. *[]* -[] [count] sections backward or to the previous '}' in +[] [count] |section|s backward or to the previous '}' in the first column. |exclusive| Note that |exclusive-linewise| often applies. @@ -502,36 +508,36 @@ aw "a word", select [count] words (see |word|). Leading or trailing white space is included, but not counted. When used in Visual linewise mode "aw" switches to - Visual characterwise mode. + Visual charwise mode. *v_iw* *iw* iw "inner word", select [count] words (see |word|). White space between words is counted too. When used in Visual linewise mode "iw" switches to - Visual characterwise mode. + Visual charwise mode. *v_aW* *aW* aW "a WORD", select [count] WORDs (see |WORD|). Leading or trailing white space is included, but not counted. When used in Visual linewise mode "aW" switches to - Visual characterwise mode. + Visual charwise mode. *v_iW* *iW* iW "inner WORD", select [count] WORDs (see |WORD|). White space between words is counted too. When used in Visual linewise mode "iW" switches to - Visual characterwise mode. + Visual charwise mode. *v_as* *as* as "a sentence", select [count] sentences (see |sentence|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_is* *is* is "inner sentence", select [count] sentences (see |sentence|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_ap* *ap* ap "a paragraph", select [count] paragraphs (see @@ -552,14 +558,14 @@ a[ "a [] block", select [count] '[' ']' blocks. This goes backwards to the [count] unclosed '[', and finds the matching ']'. The enclosed text is selected, including the '[' and ']'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i] *v_i]* *v_i[* *i]* *i[* i[ "inner [] block", select [count] '[' ']' blocks. This goes backwards to the [count] unclosed '[', and finds the matching ']'. The enclosed text is selected, excluding the '[' and ']'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a) *v_a)* *a)* *a(* a( *vab* *v_ab* *v_a(* *ab* @@ -567,54 +573,54 @@ ab "a block", select [count] blocks, from "[count] [(" to the matching ')', including the '(' and ')' (see |[(|). Does not include white space outside of the parenthesis. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i) *v_i)* *i)* *i(* i( *vib* *v_ib* *v_i(* *ib* ib "inner block", select [count] blocks, from "[count] [(" to the matching ')', excluding the '(' and ')' (see |[(|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a> *v_a>* *v_a<* *a>* *a<* a< "a <> block", select [count] <> blocks, from the [count]'th unmatched '<' backwards to the matching '>', including the '<' and '>'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i> *v_i>* *v_i<* *i>* *i<* i< "inner <> block", select [count] <> blocks, from the [count]'th unmatched '<' backwards to the matching '>', excluding the '<' and '>'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_at* *at* at "a tag block", select [count] tag blocks, from the [count]'th unmatched "<aaa>" backwards to the matching "</aaa>", including the "<aaa>" and "</aaa>". See |tag-blocks| about the details. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_it* *it* it "inner tag block", select [count] tag blocks, from the [count]'th unmatched "<aaa>" backwards to the matching "</aaa>", excluding the "<aaa>" and "</aaa>". See |tag-blocks| about the details. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a} *v_a}* *a}* *a{* a{ *v_aB* *v_a{* *aB* aB "a Block", select [count] Blocks, from "[count] [{" to the matching '}', including the '{' and '}' (see |[{|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i} *v_i}* *i}* *i{* i{ *v_iB* *v_i{* *iB* iB "inner Block", select [count] Blocks, from "[count] [{" to the matching '}', excluding the '{' and '}' (see |[{|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a" *v_aquote* *aquote* a' *v_a'* *a'* @@ -628,7 +634,7 @@ a` *v_a`* *a`* start of the line. Any trailing white space is included, unless there is none, then leading white space is included. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. Repeating this object in Visual mode another string is included. A count is currently not used. @@ -1077,6 +1083,60 @@ When you split a window, the jumplist will be copied to the new window. If you have included the ' item in the 'shada' option the jumplist will be stored in the ShaDa file and restored when starting Vim. + *jumplist-stack* +When jumpoptions includes "stack", the jumplist behaves like the history in a +web browser and like the tag stack. When jumping to a new location from the +middle of the jumplist, the locations after the current position will be +discarded. + +This behavior corresponds to the following situation in a web browser. +Navigate to first.com, second.com, third.com, fourth.com and then fifth.com. +Then navigate backwards twice so that third.com is displayed. At that point, +the history is: +- first.com +- second.com +- third.com <-- +- fourth.com +- fifth.com + +Finally, navigate to a different webpage, new.com. The history is +- first.com +- second.com +- third.com +- new.com <-- + +When the jumpoptions includes "stack", this is the behavior of Nvim as well. +That is, given a jumplist like the following in which CTRL-O has been used to +move back three times to location X + + jump line col file/text + 2 1260 8 src/nvim/mark.c <-- location X-2 + 1 685 0 src/nvim/option_defs.h <-- location X-1 +> 0 462 36 src/nvim/option_defs.h <-- location X + 1 479 39 src/nvim/option_defs.h + 2 213 2 src/nvim/mark.c + 3 181 0 src/nvim/mark.c + +jumping to (new) location Y results in the locations after the current +locations being removed: + + jump line col file/text + 3 1260 8 src/nvim/mark.c + 2 685 0 src/nvim/option_defs.h + 1 462 36 src/nvim/option_defs.h <-- location X +> + +Then, when yet another location Z is jumped to, the new location Y appears +directly after location X in the jumplist and location X remains in the same +position relative to the locations (X-1, X-2, etc., ...) that had been before it +prior to the original jump from X to Y: + + jump line col file/text + 4 1260 8 src/nvim/mark.c <-- location X-2 + 3 685 0 src/nvim/option_defs.h <-- location X-1 + 2 462 36 src/nvim/option_defs.h <-- location X + 1 100 0 src/nvim/option_defs.h <-- location Y +> CHANGE LIST JUMPS *changelist* *change-list-jumps* *E664* diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index f5d42dfeb2..5368cf0f4f 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -1,7 +1,8 @@ - NVIM REFERENCE MANUAL by Thiago de Arruda - - + NVIM REFERENCE MANUAL This document was merged into |api.txt| and |develop.txt|. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 1a5e6421e7..a96d118667 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -50,6 +50,13 @@ mode" in a normal buffer, such as |i| or |:startinsert|. In this mode all keys except <C-\><C-N> are sent to the underlying program. Use <C-\><C-N> to return to normal-mode. |CTRL-\_CTRL-N| +Terminal-mode forces these local options: + + 'nocursorline' + 'nocursorcolumn' + 'scrolloff' = 0 + 'sidescrolloff' = 0 + Terminal-mode has its own |:tnoremap| namespace for mappings, this can be used to automate any terminal interaction. @@ -307,6 +314,23 @@ Other commands ~ isn't one +Prompt mode ~ + *termdebug-prompt* +When on MS-Windows, gdb will run in a buffer with 'buftype' set to "prompt". +This works slightly differently: +- The gdb window will be in Insert mode while typing commands. Go to Normal + mode with <Esc>, then you can move around in the buffer, copy/paste, etc. + Go back to editing the gdb command with any command that starts Insert mode, + such as `a` or `i`. +- The program being debugged will run in a separate window. On MS-Windows + this is a new console window. On Unix, if the |+terminal| feature is + available a Terminal window will be opened to run the debugged program in. + + *termdebug_use_prompt* +Prompt mode can be used even when the |+terminal| feature is present with: > + let g:termdebug_use_prompt = 1 + + Communication ~ *termdebug-communication* There is another, hidden, buffer, which is used for Vim to communicate with diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 188f7fc2e2..e1beea0fed 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -843,6 +843,14 @@ A jump table for the options with a short description can be found at |Q_op|. name, precede it with a backslash. - To include a comma in a directory name precede it with a backslash. - A directory name may end in an '/'. + - For Unix and Win32, if a directory ends in two path separators "//", + the swap file name will be built from the complete path to the file + with all path separators changed to percent '%' signs. This will + ensure file name uniqueness in the backup directory. + On Win32, it is also possible to end with "\\". However, When a + separating comma is following, you must use "//", since "\\" will + include the comma in the file name. Therefore it is recommended to + use '//', instead of '\\'. - Environment variables are expanded |:set_env|. - Careful with '\' characters, type one before a space, type two to get one in the option (see |option-backslash|), for example: > @@ -1085,6 +1093,8 @@ A jump table for the options with a short description can be found at |Q_op|. nowrite buffer will not be written quickfix list of errors |:cwindow| or locations |:lwindow| terminal |terminal-emulator| buffer + prompt buffer where only the last line can be edited, meant + to be used by a plugin, see |prompt-buffer| This option is used together with 'bufhidden' and 'swapfile' to specify special kinds of buffers. See |special-buffers|. @@ -1478,7 +1488,7 @@ A jump table for the options with a short description can be found at |Q_op|. See 'preserveindent'. *'cpoptions'* *'cpo'* *cpo* -'cpoptions' 'cpo' string (Vim default: "aABceFs", +'cpoptions' 'cpo' string (Vim default: "aABceFs_", Vi default: all flags) global A sequence of single character flags. When a character is present @@ -1875,7 +1885,7 @@ A jump table for the options with a short description can be found at |Q_op|. security reasons. *'dip'* *'diffopt'* -'diffopt' 'dip' string (default "internal,filler") +'diffopt' 'dip' string (default "internal,filler,closeoff") global Option settings for diff mode. It can consist of the following items. All are optional. Items must be separated by a comma. @@ -1932,6 +1942,12 @@ A jump table for the options with a short description can be found at |Q_op|. vertical Start diff mode with vertical splits (unless explicitly specified otherwise). + closeoff When a window is closed where 'diff' is set + and there is only one window remaining in the + same tab page with 'diff' set, execute + `:diffoff` in that window. This undoes a + `:diffsplit` command. + hiddenoff Do not use diff mode for a buffer when it becomes hidden. @@ -1978,7 +1994,7 @@ A jump table for the options with a short description can be found at |Q_op|. possible. If it is not possible in any directory, but last directory listed in the option does not exist, it is created. - Empty means that no swap file will be used (recovery is - impossible!). + impossible!) and no |E303| error will be given. - A directory "." means to put the swap file in the same directory as the edited file. On Unix, a dot is prepended to the file name, so it doesn't show in a directory listing. On MS-Windows the "hidden" @@ -1986,12 +2002,14 @@ A jump table for the options with a short description can be found at |Q_op|. - A directory starting with "./" (or ".\" for Windows) means to put the swap file relative to where the edited file is. The leading "." is replaced with the path name of the edited file. - - For Unix and Win32, if a directory ends in two path separators "//" - or "\\", the swap file name will be built from the complete path to - the file with all path separators substituted to percent '%' signs. - This will ensure file name uniqueness in the preserve directory. - On Win32, when a separating comma is following, you must use "//", - since "\\" will include the comma in the file name. + - For Unix and Win32, if a directory ends in two path separators "//", + the swap file name will be built from the complete path to the file + with all path separators substituted to percent '%' signs. This will + ensure file name uniqueness in the preserve directory. + On Win32, it is also possible to end with "\\". However, When a + separating comma is following, you must use "//", since "\\" will + include the comma in the file name. Therefore it is recommended to + use '//', instead of '\\'. - Spaces after the comma are ignored, other spaces are considered part of the directory name. To have a space at the start of a directory name, precede it with a backslash. @@ -2242,8 +2260,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fileformat'* *'ff'* 'fileformat' 'ff' string (Windows default: "dos", - Unix default: "unix", - Macintosh default: "mac") + Unix default: "unix") local to buffer This gives the <EOL> of the current buffer, which is used for reading/writing the buffer from/to a file: @@ -2265,7 +2282,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'fileformats' 'ffs' string (default: Vim+Vi Win32: "dos,unix", Vim Unix: "unix,dos", - Vim Mac: "mac,unix,dos", Vi others: "") global This gives the end-of-line (<EOL>) formats that will be tried when @@ -2348,7 +2364,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fillchars'* *'fcs'* 'fillchars' 'fcs' string (default "") - local to window + global or local to window |global-local| Characters to fill the statuslines and vertical separators. It is a comma separated list of items: @@ -2357,6 +2373,9 @@ A jump table for the options with a short description can be found at |Q_op|. stlnc:c ' ' or '=' statusline of the non-current windows vert:c '│' or '|' vertical separators |:vsplit| fold:c '·' or '-' filling 'foldtext' + foldopen:c '-' mark the beginning of a fold + foldclose:c '+' show a closed fold + foldsep:c '│' or '|' open fold middle marker diff:c '-' deleted lines of the 'diff' option msgsep:c ' ' message separator 'display' eob:c '~' empty lines at the end of a buffer @@ -2365,7 +2384,7 @@ A jump table for the options with a short description can be found at |Q_op|. "stlnc" the space will be used when there is highlighting, '^' or '=' otherwise. - If 'ambiwidth' is "double" then "vert" and "fold" default to + If 'ambiwidth' is "double" then "vert", "foldsep" and "fold" default to single-byte alternatives. Example: > @@ -2402,11 +2421,14 @@ A jump table for the options with a short description can be found at |Q_op|. automatically close when moving out of them. *'foldcolumn'* *'fdc'* -'foldcolumn' 'fdc' number (default 0) +'foldcolumn' 'fdc' string (default "0") local to window - When non-zero, a column with the specified width is shown at the side - of the window which indicates open and closed folds. The maximum - value is 12. + When and how to draw the foldcolumn. Valid values are: + "auto": resize to the maximum amount of folds to display. + "auto:[1-9]": resize to accommodate multiple folds up to the + selected level + 0: to disable foldcolumn + "[1-9]": to display a fixed number of columns See |folding|. *'foldenable'* *'fen'* *'nofoldenable'* *'nofen'* @@ -3443,6 +3465,17 @@ A jump table for the options with a short description can be found at |Q_op|. Unprintable and zero-width Unicode characters are displayed as <xxxx>. There is no option to specify these characters. + *'jumpoptions'* *'jop'* +'jumpoptions' 'jop' string (default "") + global + List of words that change the behavior of the |jumplist|. + stack Make the jumplist behave like the tagstack or like a + web browser. Relative location of entries in the + jumplist is preserved at the cost of discarding + subsequent entries when navigating backwards in the + jumplist and then jumping to a location. + |jumplist-stack| + *'joinspaces'* *'js'* *'nojoinspaces'* *'nojs'* 'joinspaces' 'js' boolean (default on) global @@ -3657,7 +3690,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'listchars'* *'lcs'* 'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+" Vi default: "eol:$") - local to window + global or local to window |global-local| Strings to use in 'list' mode and for the |:list| command. It is a comma separated list of string settings. @@ -3698,9 +3731,9 @@ A jump table for the options with a short description can be found at |Q_op|. off and the line continues beyond the right of the screen. *lcs-precedes* - precedes:c Character to show in the first column, when 'wrap' - is off and there is text preceding the character - visible in the first column. + precedes:c Character to show in the first visible column of the + physical line, when there is text preceding the + character visible in the first column. *lcs-conceal* conceal:c Character to show in place of concealed text, when 'conceallevel' is set to 1. A space when omitted. @@ -3914,6 +3947,8 @@ A jump table for the options with a short description can be found at |Q_op|. When on allow some options that are an expression to be set in the modeline. Check the option for whether it is affected by 'modelineexpr'. Also see |modeline|. + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. *'modelines'* *'mls'* 'modelines' 'mls' number (default 5) @@ -4496,13 +4531,6 @@ A jump table for the options with a short description can be found at |Q_op|. global When on a ":" prompt is used in Ex mode. - *'pumheight'* *'ph'* -'pumheight' 'ph' number (default 0) - global - Determines the maximum number of items to show in the popup menu for - Insert mode completion. When zero as much space as available is used. - |ins-completion-menu|. - *'pumblend'* *'pb'* 'pumblend' 'pb' number (default 0) global @@ -4519,6 +4547,19 @@ A jump table for the options with a short description can be found at |Q_op|. < UI-dependent. Works best with RGB colors. 'termguicolors' + *'pumheight'* *'ph'* +'pumheight' 'ph' number (default 0) + global + Maximum number of items to show in the popup menu + (|ins-completion-menu|). Zero means "use available screen space". + + *'pumwidth'* *'pw'* +'pumwidth' 'pw' number (default 15) + global + Minimum width for the popup menu (|ins-completion-menu|). If the + cursor column + 'pumwidth' exceeds screen width, the popup menu is + nudged to fit on the screen. + *'pyxversion'* *'pyx'* 'pyxversion' 'pyx' number (default depends on the build) global @@ -4583,6 +4624,16 @@ A jump table for the options with a short description can be found at |Q_op|. RedrawDebugRecompose guibg=Red redraw generated by the compositor itself, due to a grid being moved or deleted. + nothrottle Turn off throttling of the message grid. This is an + optimization that joins many small scrolls to one + larger scroll when drawing the message area (with + 'display' msgsep flag active). + invalid Enable stricter checking (abort) of inconsistencies + of the internal screen state. This is mostly + useful when running nvim inside a debugger (and + the test suite). + nodelta Send all internally redrawn cells to the UI, even if + they are unchanged from the already displayed state. *'redrawtime'* *'rdt'* 'redrawtime' 'rdt' number (default 2000) @@ -4851,13 +4902,17 @@ A jump table for the options with a short description can be found at |Q_op|. *'scrolloff'* *'so'* 'scrolloff' 'so' number (default 0) - global + global or local to window |global-local| Minimal number of screen lines to keep above and below the cursor. This will make some context visible around where you are working. If you set it to a very large value (999) the cursor line will always be in the middle of the window (except at the start or end of the file or when long lines wrap). - For scrolling horizontally see 'sidescrolloff'. + After using the local value, go back the global value with one of + these two: > + setlocal scrolloff< + setlocal scrolloff=-1 +< For scrolling horizontally see 'sidescrolloff'. *'scrollopt'* *'sbo'* 'scrollopt' 'sbo' string (default "ver,jump") @@ -5158,8 +5213,9 @@ A jump table for the options with a short description can be found at |Q_op|. Note that such processing is done after |:set| did its own round of unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* - To use powershell (on Windows): > - set shell=powershell shellquote=( shellpipe=\| shellxquote= + To use powershell: > + let &shell = has('win32') ? 'powershell' : 'pwsh' + set shellquote= shellpipe=\| shellxquote= set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command set shellredir=\|\ Out-File\ -Encoding\ UTF8 @@ -5465,7 +5521,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'sidescrolloff'* *'siso'* 'sidescrolloff' 'siso' number (default 0) - global + global or local to window |global-local| The minimal number of screen columns to keep to the left and to the right of the cursor if 'nowrap' is set. Setting this option to a value greater than 0 while having |'sidescroll'| also at a non-zero @@ -5474,7 +5530,11 @@ A jump table for the options with a short description can be found at |Q_op|. to a large value (like 999) has the effect of keeping the cursor horizontally centered in the window, as long as one does not come too close to the beginning of the line. - + After using the local value, go back the global value with one of + these two: > + setlocal sidescrolloff< + setlocal sidescrolloff=-1 +< Example: Try this together with 'sidescroll' and 'listchars' as in the following example to never allow the cursor to move onto the "extends" character: > @@ -5722,7 +5782,7 @@ A jump table for the options with a short description can be found at |Q_op|. current one. |:vsplit| *'startofline'* *'sol'* *'nostartofline'* *'nosol'* -'startofline' 'sol' boolean (default on) +'startofline' 'sol' boolean (default off) global When "on" the commands listed below move the cursor to the first non-blank of the line. When off the cursor is kept in the same column @@ -5749,7 +5809,9 @@ A jump table for the options with a short description can be found at |Q_op|. When the option starts with "%!" then it is used as an expression, evaluated and the result is used as the option value. Example: > :set statusline=%!MyStatusLine() -< The result can contain %{} items that will be evaluated too. +< The *g:statusline_winid* variable will be set to the |window-ID| of the + window that the status line belongs to. + The result can contain %{} items that will be evaluated too. Note that the "%!" expression is evaluated in the context of the current window and buffer, while %{} items are evaluated in the context of the window that the statusline belongs to. @@ -5878,13 +5940,15 @@ A jump table for the options with a short description can be found at |Q_op|. become empty. This will make a group like the following disappear completely from the statusline when none of the flags are set. > :set statusline=...%(\ [%M%R%H]%)... -< *g:actual_curbuf* - Beware that an expression is evaluated each and every time the status - line is displayed. The current buffer and current window will be set - temporarily to that of the window (and buffer) whose statusline is - currently being drawn. The expression will evaluate in this context. - The variable "g:actual_curbuf" is set to the `bufnr()` number of the - real current buffer. +< Beware that an expression is evaluated each and every time the status + line is displayed. + *g:actual_curbuf* *g:actual_curwin* + The current buffer and current window will be set temporarily to that + of the window (and buffer) whose statusline is currently being drawn. + The expression will evaluate in this context. The variable + "g:actual_curbuf" is set to the `bufnr()` number of the real current + buffer and "g:actual_curwin" to the |window-ID| of the real current + window. These values are strings. The 'statusline' option will be evaluated in the |sandbox| if set from a modeline, see |sandbox-option|. @@ -5990,6 +6054,8 @@ A jump table for the options with a short description can be found at |Q_op|. vsplit Just like "split" but split vertically. newtab Like "split", but open a new tab page. Overrules "split" when both are present. + uselast If included, jump to the previously used window when + jumping to errors with |quickfix| commands. *'synmaxcol'* *'smc'* 'synmaxcol' 'smc' number (default 3000) @@ -6149,6 +6215,14 @@ A jump table for the options with a short description can be found at |Q_op|. match Match case smart Ignore case unless an upper case letter is used + *'tagfunc'* *'tfu'* +'tagfunc' 'tfu' string (default: empty) + local to buffer + This option specifies a function to be used to perform tag searches. + The function gets the tag pattern and should return a List of matching + tags. See |tag-function| for an explanation of how to write the + function and an example. + *'taglength'* *'tl'* 'taglength' 'tl' number (default 0) global @@ -6448,9 +6522,11 @@ A jump table for the options with a short description can be found at |Q_op|. >= 1 When the shada file is read or written. >= 2 When a file is ":source"'ed. >= 3 UI info, terminal capabilities + >= 4 Shell commands. >= 5 Every searched tags file and include file. >= 8 Files for which a group of autocommands is executed. >= 9 Every executed autocommand. + >= 11 Finding items in a path >= 12 Every executed function. >= 13 When an exception is thrown, caught, finished, or discarded. >= 14 Anything pending in a ":finally" clause. @@ -6620,23 +6696,23 @@ A jump table for the options with a short description can be found at |Q_op|. *'wildmenu'* *'wmnu'* *'nowildmenu'* *'nowmnu'* 'wildmenu' 'wmnu' boolean (default on) global - When 'wildmenu' is on, command-line completion operates in an enhanced - mode. On pressing 'wildchar' (usually <Tab>) to invoke completion, - the possible matches are shown just above the command line, with the - first match highlighted (overwriting the status line, if there is - one). Keys that show the previous/next match, such as <Tab> or - CTRL-P/CTRL-N, cause the highlight to move to the appropriate match. - When 'wildmode' is used, "wildmenu" mode is used where "full" is - specified. "longest" and "list" do not start "wildmenu" mode. - You can check the current mode with |wildmenumode()|. - If there are more matches than can fit in the line, a ">" is shown on - the right and/or a "<" is shown on the left. The status line scrolls - as needed. - The "wildmenu" mode is abandoned when a key is hit that is not used - for selecting a completion. - While the "wildmenu" is active the following keys have special - meanings: - + Enables "enhanced mode" of command-line completion. When user hits + <Tab> (or 'wildchar') to invoke completion, the possible matches are + shown in a menu just above the command-line (see 'wildoptions'), with + the first match highlighted (overwriting the statusline). Keys that + show the previous/next match (<Tab>/CTRL-P/CTRL-N) highlight the + match. + 'wildmode' must specify "full": "longest" and "list" do not start + 'wildmenu' mode. You can check the current mode with |wildmenumode()|. + The menu is canceled when a key is hit that is not used for selecting + a completion. + + While the menu is active these keys have special meanings: + + CTRL-Y - accept the currently selected match and stop + completion. + CTRL-E - end completion, go back to what was there before + selecting a match. <Left> <Right> - select previous/next match (like CTRL-P/CTRL-N) <Down> - in filename/menu name completion: move into a subdirectory or submenu. @@ -6645,15 +6721,12 @@ A jump table for the options with a short description can be found at |Q_op|. <Up> - in filename/menu name completion: move up into parent directory or parent menu. - This makes the menus accessible from the console |console-menus|. - - If you prefer the <Left> and <Right> keys to move the cursor instead - of selecting a different match, use this: > + If you want <Left> and <Right> to move the cursor instead of selecting + a different match, use this: > :cnoremap <Left> <Space><BS><Left> :cnoremap <Right> <Space><BS><Right> < - The "WildMenu" highlighting is used for displaying the current match - |hl-WildMenu|. + |hl-WildMenu| highlights the current match. *'wildmode'* *'wim'* 'wildmode' 'wim' string (default: "full") @@ -6677,6 +6750,8 @@ A jump table for the options with a short description can be found at |Q_op|. complete first match. "list:longest" When more than one match, list all matches and complete till longest common string. + "list:lastused" When more than one buffer matches, sort buffers + by time last used (other than the current buffer). When there is only a single match, it is fully completed in all cases. Examples: > @@ -6908,7 +6983,6 @@ A jump table for the options with a short description can be found at |Q_op|. global The number of milliseconds to wait for each character sent to the screen. When positive, characters are sent to the UI one by one. - When negative, all redrawn characters cause a delay, even if the - character already was displayed by the UI. For debugging purposes. + See 'redrawdebug' for more options. For debugging purposes. vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index adfab07758..7129c6cd58 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1111,6 +1111,9 @@ x A single character, with no special meaning, matches itself *[:tab:]* [:tab:] the <Tab> character *[:escape:]* [:escape:] the <Esc> character *[:backspace:]* [:backspace:] the <BS> character +*[:ident:]* [:ident:] identifier character (same as "\i") +*[:keyword:]* [:keyword:] keyword character (same as "\k") +*[:fname:]* [:fname:] file name character (same as "\f") The brackets in character class expressions are additional to the brackets delimiting a collection. For example, the following is a plausible pattern for a Unix filename: "[-./[:alnum:]_~]\+" That is, diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 0083bb63a4..0a6cdc60e8 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -58,12 +58,14 @@ If you run into problems, uninstall _both_ then install "pynvim" again: > PYTHON PROVIDER CONFIGURATION ~ *g:python_host_prog* Command to start Python 2 (executable, not directory). Setting this makes -startup faster. Useful for working with virtualenvs. > +startup faster. Useful for working with virtualenvs. Must be set before any +check for has("python2"). > let g:python_host_prog = '/path/to/python' < *g:python3_host_prog* Command to start Python 3 (executable, not directory). Setting this makes -startup faster. Useful for working with virtualenvs. > +startup faster. Useful for working with virtualenvs. Must be set before any +check for has("python3"). > let g:python3_host_prog = '/path/to/python3' < *g:loaded_python_provider* @@ -125,6 +127,31 @@ To use the RVM "system" Ruby installation: > let g:ruby_host_prog = 'rvm system do neovim-ruby-host' ============================================================================== +Perl integration *provider-perl* + +Nvim supports Perl |remote-plugin|s. +https://github.com/jacquesg/p5-Neovim-Ext + + +PERL QUICKSTART~ + +To use perl remote-plugins with Nvim, install the "Neovim::Ext" cpan package: > + cpanm -n Neovim::Ext + +Run |:checkhealth| to see if your system is up-to-date. + + +PERL PROVIDER CONFIGURATION~ + *g:loaded_perl_provider* +To disable Perl support: > + :let g:loaded_perl_provider = 0 +< + *g:perl_host_prog* +Command to start the Perl executable. Must be set before any +check for has("perl"). > + let g:perl_host_prog = '/path/to/perl' +< +============================================================================== Node.js integration *provider-nodejs* Nvim supports Node.js |remote-plugin|s. diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 3ae6d9461f..61e090cc78 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -109,6 +109,36 @@ processing a quickfix or location list command, it will be aborted. list for the current window is used instead of the quickfix list. + *:cabo* *:cabove* +:[count]cabo[ve] Go to the [count] error above the current line in the + current buffer. If [count] is omitted, then 1 is + used. If there are no errors, then an error message + is displayed. Assumes that the entries in a quickfix + list are sorted by their buffer number and line + number. If there are multiple errors on the same line, + then only the first entry is used. If [count] exceeds + the number of entries above the current line, then the + first error in the file is selected. + + *:lab* *:labove* +:[count]lab[ove] Same as ":cabove", except the location list for the + current window is used instead of the quickfix list. + + *:cbe* *:cbelow* +:[count]cbe[low] Go to the [count] error below the current line in the + current buffer. If [count] is omitted, then 1 is + used. If there are no errors, then an error message + is displayed. Assumes that the entries in a quickfix + list are sorted by their buffer number and line + number. If there are multiple errors on the same + line, then only the first entry is used. If [count] + exceeds the number of entries below the current line, + then the last error in the file is selected. + + *:lbe* *:lbelow* +:[count]lbe[low] Same as ":cbelow", except the location list for the + current window is used instead of the quickfix list. + *:cnf* *:cnfile* :[count]cnf[ile][!] Display the first error in the [count] next file in the list that includes a file name. If there are no diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 87cb9b54f5..224f14a18b 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -47,6 +47,7 @@ N is used to indicate an optional count that can be given before the command. |g$| N g$ to last character in screen line (differs from "$" when lines wrap) |gm| gm to middle of the screen line +|gM| gM to middle of the line |bar| N | to column N (default: 1) |f| N f{char} to the Nth occurrence of {char} to the right |F| N F{char} to the Nth occurrence of {char} to the left @@ -742,6 +743,7 @@ Short explanation of each option: *option-list* 'iskeyword' 'isk' characters included in keywords 'isprint' 'isp' printable characters 'joinspaces' 'js' two spaces after a period with a join command +'jumpoptions' 'jop' specifies how jumping is done 'keymap' 'kmp' name of a keyboard mapping 'keymodel' 'km' enable starting/stopping selection with keys 'keywordprg' 'kp' program to use for the "K" command diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 4e0d91dae0..e3ba4ba181 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -176,9 +176,9 @@ See |sign_place()| for the equivalent Vim script function. By default, the sign is assigned a default priority of 10. To assign a different priority value, use "priority={prio}" to - specify a value. The priority is used to determine the - highlight group used when multiple signs are placed on the - same line. + specify a value. The priority is used to determine the sign + that is displayed when multiple signs are placed on the same + line. Examples: > :sign place 5 line=3 name=sign1 file=a.py @@ -198,7 +198,9 @@ See |sign_place()| for the equivalent Vim script function. it (e.g., when the debugger has stopped at a breakpoint). The optional "group={group}" attribute can be used before - "file=" to select a sign in a particular group. + "file=" to select a sign in a particular group. The optional + "priority={prio}" attribute can be used to change the priority + of an existing sign. :sign place {id} name={name} [buffer={nr}] Same, but use buffer {nr}. If the buffer argument is not diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 110c6ef221..b88e26cdff 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -504,7 +504,7 @@ then Vim will try to guess. Multiple {inname} arguments can be given to combine regions into one Vim spell file. Example: > - :mkspell ~/.vim/spell/en /tmp/en_US /tmp/en_CA /tmp/en_AU + :mkspell ~/.config/nvim/spell/en /tmp/en_US /tmp/en_CA /tmp/en_AU < This combines the English word lists for US, CA and AU into one en.spl file. Up to eight regions can be combined. *E754* *E755* @@ -888,9 +888,9 @@ when using "cp1250" on Unix. *spell-LOW* *spell-UPP* Three lines in the affix file are needed. Simplistic example: - FOL ~ - LOW ~ - UPP ~ + FOL áëñ ~ + LOW áëñ ~ + UPP ÁËÑ ~ All three lines must have exactly the same number of characters. @@ -905,9 +905,9 @@ The "UPP" line specifies the characters with upper-case. That is, a character is upper-case where it's different from the character at the same position in "FOL". -An exception is made for the German sharp s . The upper-case version is +An exception is made for the German sharp s ß. The upper-case version is "SS". In the FOL/LOW/UPP lines it should be included, so that it's recognized -as a word character, but use the character in all three. +as a word character, but use the ß character in all three. ASCII characters should be omitted, Vim always handles these in the same way. When the encoding is UTF-8 no word characters need to be specified. @@ -1377,7 +1377,7 @@ suggestions would spend most time trying all kind of weird compound words. *spell-SYLLABLE* The SYLLABLE item defines characters or character sequences that are used to count the number of syllables in a word. Example: - SYLLABLE aeiouy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui ~ + SYLLABLE aáeéiíoóöõuúüûy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui ~ Before the first slash is the set of characters that are counted for one syllable, also when repeated and mixed, until the next character that is not @@ -1458,8 +1458,8 @@ alike. This is mostly used for a letter with different accents. This is used to prefer suggestions with these letters substituted. Example: MAP 2 ~ - MAP e ~ - MAP u ~ + MAP eéëêè ~ + MAP uüùúû ~ The first line specifies the number of MAP lines following. Vim ignores the number, but the line must be there. diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 7dbbb2d424..0ded6a9060 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -90,6 +90,7 @@ argument. --clean Equivalent to "-u NONE -i NONE": - Skips initializations from files and environment variables. - No 'shada' file is read or written. + - Excludes user directories from 'runtimepath' *--noplugin* --noplugin Skip loading plugins. Resets the 'loadplugins' option. @@ -184,12 +185,17 @@ argument. the 'modifiable' and 'write' options can be set to enable changes and writing. - *-Z* *restricted-mode* *E145* + *-Z* *restricted-mode* *E145* *E981* -Z Restricted mode. All commands that make use of an external shell are disabled. This includes suspending with CTRL-Z, - ":sh", filtering, the system() function, backtick expansion, - delete(), rename(), mkdir(), writefile(), libcall(), - jobstart(), etc. + ":sh", filtering, the system() function, backtick expansion + and libcall(). + Also disallowed are delete(), rename(), mkdir(), jobstart(), + etc. + Interfaces, such as Python, Ruby and Lua, are also disabled, + since they could be used to execute shell commands. + Note that the user may still find a loophole to execute a + shell command, it has only been made difficult. -e *-e* *-E* -E Start Nvim in Ex mode |gQ|. @@ -1270,7 +1276,7 @@ exactly four MessagePack objects: Key Type Def Description ~ rt UInteger 0 Register type: No Description ~ - 0 |characterwise-register| + 0 |charwise-register| 1 |linewise-register| 2 |blockwise-register| rw UInteger 0 Register width. Only valid diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 0a4257e2b2..7da886dabd 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -3522,6 +3522,24 @@ DEFINING CASE *:syn-case* *E390* :sy[ntax] case Show either "syntax case match" or "syntax case ignore" (translated). + +DEFINING FOLDLEVEL *:syn-foldlevel* + +:sy[ntax] foldlevel [start | minimum] + This defines how the foldlevel of a line is computed when using + foldmethod=syntax (see |fold-syntax| and |:syn-fold|): + + start: Use level of item containing start of line. + minimum: Use lowest local-minimum level of items on line. + + The default is 'start'. Use 'minimum' to search a line horizontally + for the lowest level contained on the line that is followed by a + higher level. This produces more natural folds when syntax items + may close and open horizontally within a line. + +:sy[ntax] foldlevel + Show either "syntax foldlevel start" or "syntax foldlevel minimum". + SPELL CHECKING *:syn-spell* :sy[ntax] spell [toplevel | notoplevel | default] @@ -3985,6 +4003,8 @@ This will make each {} block form one fold. The fold will start on the line where the item starts, and end where the item ends. If the start and end are within the same line, there is no fold. The 'foldnestmax' option limits the nesting of syntax folds. +See |:syn-foldlevel| to control how the foldlevel of a line is computed +from its syntax items. *:syn-contains* *E405* *E406* *E407* *E408* *E409* @@ -4647,8 +4667,8 @@ in their own color. ":colorscheme" in a color scheme script. To customize a colorscheme use another name, e.g. - "~/.vim/colors/mine.vim", and use `:runtime` to load - the original colorscheme: > + "~/.config/nvim/colors/mine.vim", and use `:runtime` to + load the original colorscheme: > runtime colors/evening.vim hi Statement ctermfg=Blue guifg=Blue @@ -4720,18 +4740,19 @@ the same syntax file on all UIs. *bold* *underline* *undercurl* *inverse* *italic* *standout* - *strikethrough* + *nocombine* *strikethrough* cterm={attr-list} *attr-list* *highlight-cterm* *E418* attr-list is a comma separated list (without spaces) of the following items (in any order): bold underline undercurl curly underline + strikethrough reverse inverse same as reverse italic standout - strikethrough + nocombine override attributes instead of combining them NONE no attributes used (used to reset it) Note that "bold" can be used here and by using a bold font. They @@ -4919,6 +4940,8 @@ Conceal placeholder characters substituted for concealed text (see 'conceallevel') *hl-Cursor* Cursor character under the cursor +lCursor the character under the cursor when |language-mapping| + is used (see 'guicursor') *hl-CursorIM* CursorIM like Cursor, but used when in IME mode |CursorIM| *hl-CursorColumn* diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index d0a5678179..1407fdd968 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -190,6 +190,9 @@ gt *i_CTRL-<PageDown>* *i_<C-PageDown>* {count}<C-PageDown> {count}gt Go to tab page {count}. The first tab page has number one. +CTRL-<Tab> *CTRL-<Tab>* +CTRL-W g<Tab> *g<Tab>* *CTRL-W_g<Tab>* +g<Tab> Go to previous (last accessed) tab page. :tabp[revious] *:tabp* *:tabprevious* *gT* *:tabN* :tabN[ext] *:tabNext* *CTRL-<PageUp>* diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index bb3134feb6..b011db3dd3 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -838,4 +838,70 @@ Common arguments for the commands above: < For a ":djump", ":dsplit", ":dlist" and ":dsearch" command the pattern is used as a literal string, not as a search pattern. +============================================================================== +7. Using 'tagfunc' *tag-function* + +It is possible to provide Vim with a function which will generate a list of +tags used for commands like |:tag|, |:tselect| and Normal mode tag commands +like |CTRL-]|. + +The function used for generating the taglist is specified by setting the +'tagfunc' option. The function will be called with three arguments: + a:pattern The tag identifier used during the tag search. + a:flags List of flags to control the function behavior. + a:info Dict containing the following entries: + buf_ffname Full filename which can be used for priority. + user_data Custom data String, if stored in the tag + stack previously by tagfunc. + +Currently two flags may be passed to the tag function: + 'c' The function was invoked by a normal command being processed + (mnemonic: the tag function may use the context around the + cursor to perform a better job of generating the tag list.) + 'i' In Insert mode, the user was completing a tag (with + |i_CTRL-X_CTRL-]|). + +Note that when 'tagfunc' is set, the priority of the tags described in +|tag-priority| does not apply. Instead, the priority is exactly as the +ordering of the elements in the list returned by the function. + *E987* +The function should return a List of Dict entries. Each Dict must at least +include the following entries and each value must be a string: + name Name of the tag. + filename Name of the file where the tag is defined. It is + either relative to the current directory or a full path. + cmd Ex command used to locate the tag in the file. This + can be either an Ex search pattern or a line number. +Note that the format is similar to that of |taglist()|, which makes it possible +to use its output to generate the result. +The following fields are optional: + kind Type of the tag. + user_data String of custom data stored in the tag stack which + can be used to disambiguate tags between operations. + +If the function returns |v:null| instead of a List, a standard tag lookup will +be performed instead. + +It is not allowed to change the tagstack from inside 'tagfunc'. *E986* + +The following is a hypothetical example of a function used for 'tagfunc'. It +uses the output of |taglist()| to generate the result: a list of tags in the +inverse order of file names. +> + function! TagFunc(pattern, flags, info) + function! CompareFilenames(item1, item2) + let f1 = a:item1['filename'] + let f2 = a:item2['filename'] + return f1 >=# f2 ? + \ -1 : f1 <=# f2 ? 1 : 0 + endfunction + + let result = taglist(a:pattern) + call sort(result, "CompareFilenames") + + return result + endfunc + set tagfunc=TagFunc +< + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 4f4d379f01..6a271c08d3 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -149,6 +149,8 @@ Nvim will adjust the shape of the cursor from a block to a line when in insert mode (or as specified by the 'guicursor' option), on terminals that support it. It uses the same |terminfo| extensions that were pioneered by tmux for this: "Ss" and "Se". +Similarly, if you set the cursor highlight group with blend=100, Nvim hides +the cursor through the "cvvis" and "civis" extensions. If your terminfo definition is missing them, then Nvim will decide whether to add them to your terminfo definition, by looking at $TERM and other diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index a2f19593ae..2817c1015b 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -161,7 +161,9 @@ the editor. `cursor_shape`: "block", "horizontal", "vertical" `cell_percentage`: Cell % occupied by the cursor. `blinkwait`, `blinkon`, `blinkoff`: See |cursor-blinking|. - `attr_id`: Cursor attribute id (defined by `hl_attr_define`) + `attr_id`: Cursor attribute id (defined by `hl_attr_define`). + When attr_id is 0, the background and foreground + colors should be swapped. `attr_id_lm`: Cursor attribute id for when 'langmap' is active. `short_name`: Mode code name, see 'guicursor'. `name`: Mode descriptive name. @@ -201,8 +203,8 @@ the editor. sent from Nvim, like for |ui-cmdline|. ["mode_change", mode, mode_idx] - The mode changed. The first parameter `mode` is a string representing - the current mode. `mode_idx` is an index into the array received in + Editor mode changed. The `mode` parameter is a string representing + the current mode. `mode_idx` is an index into the array emitted in the `mode_info_set` event. UIs should change the cursor style according to the properties specified in the corresponding item. The set of modes reported will change in new versions of Nvim, for @@ -211,11 +213,11 @@ the editor. ["mouse_on"] ["mouse_off"] - Tells the client whether mouse support, as determined by |'mouse'| - option, is considered to be active in the current mode. This is mostly - useful for a terminal frontend, or other situations where Nvim mouse - would conflict with other usages of the mouse. It is safe for a client - to ignore this and always send mouse events. + |'mouse'| was enabled/disabled in the current editor mode. Useful for + a terminal UI, or other situations where Nvim mouse would conflict + with other usages of the mouse. UIs may ignore this and always send + mouse input, because 'mouse' decides the behavior of |nvim_input()| + implicitly. ["busy_start"] ["busy_stop"] @@ -294,7 +296,8 @@ numerical highlight ids to the actual attributes. `underline`: underlined text. The line has `special` color. `undercurl`: undercurled text. The curl has `special` color. `blend`: Blend level (0-100). Could be used by UIs to support - blending floating windows to the background. + blending floating windows to the background or to + signal a transparent cursor. For absent color keys the default color should be used. Don't store the default value in the table, rather a sentinel value, so that @@ -591,6 +594,12 @@ tabs. When |ext_messages| is active, no message grid is used, and this event will not be sent. +["win_viewport", grid, win, topline, botline, curline, curcol] + Indicates the range of buffer text displayed in the window, as well + as the cursor position in the buffer. All positions are zero-based. + `botline` is set to one more than the line count of the buffer, if + there are filler lines past the end. + ============================================================================== Popupmenu Events *ui-popupmenu* diff --git a/runtime/doc/usr_21.txt b/runtime/doc/usr_21.txt index 99a78e7b05..44d653a1a7 100644 --- a/runtime/doc/usr_21.txt +++ b/runtime/doc/usr_21.txt @@ -339,21 +339,6 @@ window, move the cursor to the filename and press "O". Double clicking with the mouse will also do this. -UNIX AND MS-WINDOWS - -Some people have to do work on MS-Windows systems one day and on Unix another -day. If you are one of them, consider adding "slash" and "unix" to -'sessionoptions'. The session files will then be written in a format that can -be used on both systems. This is the command to put in your |init.vim| file: -> - :set sessionoptions+=unix,slash - -Vim will use the Unix format then, because the MS-Windows Vim can read and -write Unix files, but Unix Vim can't read MS-Windows format session files. -Similarly, MS-Windows Vim understands file names with / to separate names, but -Unix Vim doesn't understand \. - - SESSIONS AND SHADA Sessions store many things, but not the position of marks, contents of diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt index 3a58af6412..2efb67e55f 100644 --- a/runtime/doc/usr_25.txt +++ b/runtime/doc/usr_25.txt @@ -346,12 +346,13 @@ scroll: g0 to first visible character in this line g^ to first non-blank visible character in this line - gm to middle of this line + gm to middle of screen line + gM to middle of the text in this line g$ to last visible character in this line - |<-- window -->| - some long text, part of which is visible ~ - g0 g^ gm g$ + |<-- window -->| + some long text, part of which is visible in one line ~ + g0 g^ gm gM g$ BREAKING AT WORDS *edit-no-break* diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index c770950a96..31da51bfbe 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -578,8 +578,10 @@ used for. You can find an alphabetical list here: |functions|. Use CTRL-] on the function name to jump to detailed help on it. String manipulation: *string-functions* - nr2char() get a character by its ASCII value - char2nr() get ASCII value of a character + nr2char() get a character by its number value + list2str() get a character string from a list of numbers + char2nr() get number value of a character + str2list() get list of numbers from a string str2nr() convert a string to a Number str2float() convert a string to a Float printf() format a string according to % items @@ -607,6 +609,7 @@ String manipulation: *string-functions* strcharpart() get part of a string using char index strgetchar() get character from a string using char index expand() expand special keywords + expandcmd() expand a command like done for `:edit` iconv() convert text from one encoding to another byteidx() byte index of a character in a string byteidxcomp() like byteidx() but count composing characters @@ -641,6 +644,7 @@ List manipulation: *list-functions* min() minimum value in a List count() count number of times a value appears in a List repeat() repeat a List multiple times + flatten() flatten a List Dictionary manipulation: *dict-functions* get() get an entry without an error for a wrong key diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 45a94bb961..24b562543e 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -38,7 +38,7 @@ the differences. - 'directory' defaults to ~/.local/share/nvim/swap// (|xdg|), auto-created - 'display' defaults to "lastline,msgsep" - 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding) -- 'fillchars' defaults (in effect) to "vert:│,fold:·" +- 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│" - 'formatoptions' defaults to "tcqj" - 'fsync' is disabled - 'history' defaults to 10000 (the maximum) @@ -55,6 +55,7 @@ the differences. - 'showcmd' is enabled - 'sidescroll' defaults to 1 - 'smarttab' is enabled +- 'startofline' is disabled - 'tabpagemax' defaults to 50 - 'tags' defaults to "./tags;,tags" - 'ttimeoutlen' defaults to 50 @@ -115,6 +116,10 @@ backwards-compatibility cost. Some examples: - Directories for 'directory' and 'undodir' are auto-created. - Terminal features such as 'guicursor' are enabled where possible. +Some features are built in that otherwise required external plugins: + +- Highlighting the yanked region, see |lua-highlight|. + ARCHITECTURE ~ External plugins run in separate processes. |remote-plugin| This improves @@ -158,6 +163,7 @@ Events: |UILeave| |VimResume| |VimSuspend| + |WinClosed| Functions: |dictwatcheradd()| notifies a callback whenever a |Dict| is modified @@ -168,6 +174,7 @@ Functions: |system()|, |systemlist()| can run {cmd} directly (without 'shell') Highlight groups: + |highlight-blend| controls blend level for a highlight group |expr-highlight| highlight groups (prefixed with "Nvim") |hl-NormalFloat| highlights floating window |hl-NormalNC| highlights non-current windows @@ -193,19 +200,21 @@ Normal commands: "Outline": Type |gO| in |:Man| and |:help| pages to see a document outline. Options: - 'cpoptions' flags: |cpo-_| - 'display' flag `msgsep` to minimize scrolling when showing messages - 'guicursor' works in the terminal - 'fillchars' local to window. flags: `msgsep` (see 'display' above) and `eob` - for |hl-EndOfBuffer| marker - 'inccommand' shows interactive results for |:substitute|-like commands - 'listchars' local to window - 'pumblend' pseudo-transparent popupmenu + 'cpoptions' flags: |cpo-_| + 'display' flags: "msgsep" minimizes scrolling when showing messages + 'guicursor' works in the terminal + 'fillchars' flags: "msgsep" (see 'display'), "eob" for |hl-EndOfBuffer| + marker, "foldopen", "foldsep", "foldclose" + 'foldcolumn' supports up to 9 dynamic/fixed columns + 'inccommand' shows interactive results for |:substitute|-like commands + 'listchars' local to window + 'pumblend' pseudo-transparent popupmenu 'scrollback' - 'signcolumn' supports up to 9 dynamic/fixed columns - 'statusline' supports unlimited alignment sections - 'tabline' %@Func@foo%X can call any function on mouse-click - 'wildoptions' `pum` flag to use popupmenu for wildmode completion + 'signcolumn' supports up to 9 dynamic/fixed columns + 'statusline' supports unlimited alignment sections + 'tabline' %@Func@foo%X can call any function on mouse-click + 'wildoptions' "pum" flag to use popupmenu for wildmode completion + 'winblend' pseudo-transparency in floating windows |api-floatwin| 'winhighlight' window-local highlights Signs: @@ -267,11 +276,6 @@ are always available and may be used simultaneously. See |provider-python|. |json_encode()| behaviour slightly changed: now |msgpack-special-dict| values are accepted, but |v:none| is not. -*v:none* variable is absent. In Vim it represents “no value” in “js” strings -like "[,]" parsed as "[v:none]" by |js_decode()|. - -*js_encode()* and *js_decode()* functions are also absent. - Viminfo text files were replaced with binary (messagepack) ShaDa files. Additional differences: @@ -296,7 +300,7 @@ coerced to strings. See |id()| for more details, currently it uses |c_CTRL-R| pasting a non-special register into |cmdline| omits the last <CR>. -Lua interface (|if_lua.txt|): +Lua interface (|lua.txt|): - `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim that prints `a` and `b` on separate lines, exactly like @@ -307,15 +311,15 @@ Lua interface (|if_lua.txt|): - Lua package.path and package.cpath are automatically updated according to 'runtimepath': |lua-require|. -|input()| and |inputdialog()| support for each other’s features (return on -cancel and completion respectively) via dictionary argument (replaces all -other arguments if used). - -|input()| and |inputdialog()| support user-defined cmdline highlighting. - Commands: |:doautocmd| does not warn about "No matching autocommands". +Functions: + |input()| and |inputdialog()| support for each other’s features (return on + cancel and completion respectively) via dictionary argument (replaces all + other arguments if used). + |input()| and |inputdialog()| support user-defined cmdline highlighting. + Highlight groups: |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other groups @@ -338,6 +342,7 @@ Normal commands: Options: 'ttimeout', 'ttimeoutlen' behavior was simplified + |jumpoptions| "stack" behavior Shell: Shell output (|:!|, |:make|, …) is always routed through the UI, so it @@ -399,10 +404,10 @@ VimL (Vim script) compatibility: Some legacy Vim features are not implemented: -- |if_py|: *python-bindeval* *python-Function* are not supported -- |if_lua|: the `vim` object is missing some legacy methods -- *if_perl* +- |if_lua|: Nvim Lua API is not compatible with Vim's "if_lua" - *if_mzscheme* +- *if_perl* +- |if_py|: *python-bindeval* *python-Function* are not supported - *if_tcl* ============================================================================== @@ -439,6 +444,11 @@ Compile-time features: Emacs tags support X11 integration (see |x11-selection|) +Eval: + *js_encode()* + *js_decode()* + *v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead. + Highlight groups: *hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim supports 'winhighlight' window-local highlights. @@ -524,4 +534,4 @@ TUI: always uses 7-bit control sequences. ============================================================================== - vim:tw=78:ts=8:sw=2:noet:ft=help:norl: + vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index ccbbc092ec..0052382044 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -48,7 +48,7 @@ position. ============================================================================== 2. Starting and stopping Visual mode *visual-start* - *v* *characterwise-visual* + *v* *charwise-visual* [count]v Start Visual mode per character. With [count] select the same number of characters or lines as used for the last Visual operation, but at @@ -74,7 +74,7 @@ position. If you use <Esc>, click the left mouse button or use any command that does a jump to another buffer while in Visual mode, the highlighting stops -and no text is affected. Also when you hit "v" in characterwise Visual mode, +and no text is affected. Also when you hit "v" in charwise Visual mode, "CTRL-V" in blockwise Visual mode or "V" in linewise Visual mode. If you hit CTRL-Z the highlighting stops and the editor is suspended or a new shell is started |CTRL-Z|. @@ -477,7 +477,7 @@ Commands in Select mode: Otherwise, typed characters are handled as in Visual mode. When using an operator in Select mode, and the selection is linewise, the -selected lines are operated upon, but like in characterwise selection. For +selected lines are operated upon, but like in charwise selection. For example, when a whole line is deleted, it can later be pasted in the middle of a line. @@ -510,7 +510,7 @@ gV Avoid the automatic reselection of the Visual area selection. *gh* -gh Start Select mode, characterwise. This is like "v", +gh Start Select mode, charwise. This is like "v", but starts Select mode instead of Visual mode. Mnemonic: "get highlighted". diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 76bb096ee3..b5623f4ea4 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -201,9 +201,11 @@ CTRL-W CTRL_N *CTRL-W_CTRL-N* |:find|. Doesn't split if {file} is not found. CTRL-W CTRL-^ *CTRL-W_CTRL-^* *CTRL-W_^* -CTRL-W ^ Does ":split #", split window in two and edit alternate file. - When a count is given, it becomes ":split #N", split window - and edit buffer N. +CTRL-W ^ Split the current window in two and edit the alternate file. + When a count N is given, split the current window and edit + buffer N. Similar to ":sp #" and ":sp #N", but it allows the + other buffer to be unnamed. This command matches the behavior + of |CTRL-^|, except that it splits a window first. CTRL-W ge *CTRL-W_ge* Detach the current window as an external window. @@ -1019,6 +1021,9 @@ list of buffers. |unlisted-buffer| x buffers with a read error % current buffer # alternate buffer + R terminal buffers with a running job + F terminal buffers with a finished job + t show time last used and sort buffers Combining flags means they are "and"ed together, e.g.: h+ hidden buffers which are modified a+ active buffers which are modified diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 6abf9da55f..c0d656107c 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2019 Aug 26 +" Last Change: 2020 Apr 29 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -84,6 +84,9 @@ au BufNewFile,BufRead *.gpr setf ada " AHDL au BufNewFile,BufRead *.tdf setf ahdl +" AIDL +au BufNewFile,BufRead *.aidl setf aidl + " AMPL au BufNewFile,BufRead *.run setf ampl @@ -229,11 +232,14 @@ au BufNewFile,BufRead *.bl setf blank " Blkid cache file au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml +" BSDL +au BufNewFile,BufRead *bsd,*.bsdl setf bsdl + " Bazel (http://bazel.io) autocmd BufRead,BufNewFile *.bzl,WORKSPACE,BUILD.bazel setf bzl if has("fname_case") " There is another check for BUILD further below. - autocmd BufRead,BufNewFile BUILD setf bzl + autocmd BufRead,BufNewFile BUILD setf bzl endif " C or lpc @@ -421,6 +427,9 @@ au BufNewFile,BufRead *.csp,*.fdr setf csp au BufNewFile,BufRead *.pld setf cupl au BufNewFile,BufRead *.si setf cuplsim +" Dart +au BufRead,BufNewfile *.dart,*.drt setf dart + " Debian Control au BufNewFile,BufRead */debian/control setf debcontrol au BufNewFile,BufRead control @@ -452,7 +461,7 @@ au BufNewFile,BufRead *.desc setf desc au BufNewFile,BufRead *.d call dist#ft#DtraceCheck() " Desktop files -au BufNewFile,BufRead *.desktop,.directory setf desktop +au BufNewFile,BufRead *.desktop,*.directory setf desktop " Dict config au BufNewFile,BufRead dict.conf,.dictrc setf dictconf @@ -484,7 +493,7 @@ au BufNewFile,BufRead *.rul au BufNewFile,BufRead *.com call dist#ft#BindzoneCheck('dcl') " DOT -au BufNewFile,BufRead *.dot setf dot +au BufNewFile,BufRead *.dot,*.gv setf dot " Dylan - lid files au BufNewFile,BufRead *.lid setf dylanlid @@ -532,11 +541,14 @@ au BufNewFile,BufRead *.ecd setf ecd au BufNewFile,BufRead *.e,*.E call dist#ft#FTe() " Elinks configuration -au BufNewFile,BufRead */etc/elinks.conf,*/.elinks/elinks.conf setf elinks +au BufNewFile,BufRead elinks.conf setf elinks " ERicsson LANGuage; Yaws is erlang too au BufNewFile,BufRead *.erl,*.hrl,*.yaws setf erlang +" Elm +au BufNewFile,BufRead *.elm setf elm + " Elm Filter Rules file au BufNewFile,BufRead filter-rules setf elmfilt @@ -793,8 +805,8 @@ au BufNewFile,BufRead *.java,*.jav setf java " JavaCC au BufNewFile,BufRead *.jj,*.jjt setf javacc -" JavaScript, ECMAScript -au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs setf javascript +" JavaScript, ECMAScript, ES module script, CommonJS script +au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs,*.cjs setf javascript " JavaScript with React au BufNewFile,BufRead *.jsx setf javascriptreact @@ -826,6 +838,9 @@ au BufNewFile,BufRead *.k setf kwt " Kivy au BufNewFile,BufRead *.kv setf kivy +" Kotlin +au BufNewFile,BufRead *.kt,*.ktm,*.kts setf kotlin + " KDE script au BufNewFile,BufRead *.ks setf kscript @@ -871,11 +886,12 @@ au BufNewFile,BufRead *.ll setf lifelines " Lilo: Linux loader au BufNewFile,BufRead lilo.conf setf lilo -" Lisp (*.el = ELisp, *.cl = Common Lisp, *.jl = librep Lisp) +" Lisp (*.el = ELisp, *.cl = Common Lisp) +" *.jl was removed, it's also used for Julia, better skip than guess wrong. if has("fname_case") - au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.jl,*.L,.emacs,.sawfishrc setf lisp + au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.L,.emacs,.sawfishrc setf lisp else - au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.jl,.emacs,.sawfishrc setf lisp + au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,.emacs,.sawfishrc setf lisp endif " SBCL implementation of Common Lisp @@ -975,6 +991,9 @@ au BufNewFile,BufRead hg-editor-*.txt setf hgcommit " Mercurial config (looks like generic config file) au BufNewFile,BufRead *.hgrc,*hgrc setf cfg +" Meson Build system config +au BufNewFile,BufRead meson.build,meson_options.txt setf meson + " Messages (logs mostly) au BufNewFile,BufRead */log/{auth,cron,daemon,debug,kern,lpr,mail,messages,news/news,syslog,user}{,.log,.err,.info,.warn,.crit,.notice}{,.[0-9]*,-[0-9]*} setf messages @@ -1114,8 +1133,20 @@ au BufNewFile,BufRead *.ora setf ora " Packet filter conf au BufNewFile,BufRead pf.conf setf pf +" Pacman Config (close enough to dosini) +au BufNewFile,BufRead */etc/pacman.conf setf dosini + +" Pacman hooks +au BufNewFile,BufRead *.hook + \ if getline(1) == '[Trigger]' | + \ setf dosini | + \ endif + " Pam conf -au BufNewFile,BufRead */etc/pam.conf setf pamconf +au BufNewFile,BufRead */etc/pam.conf setf pamconf + +" Pam environment +au BufNewFile,BufRead pam_env.conf,.pam_environment setf pamenv " PApp au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp @@ -1143,6 +1174,7 @@ else endif au BufNewFile,BufRead *.plx,*.al,*.psgi setf perl au BufNewFile,BufRead *.p6,*.pm6,*.pl6 setf perl6 +au BufNewFile,BufRead *.raku,*.rakumod setf perl6 " Perl, XPM or XPM2 au BufNewFile,BufRead *.pm @@ -1272,7 +1304,8 @@ au BufNewFile,BufRead *.pyx,*.pxd setf pyrex " Python, Python Shell Startup and Python Stub Files " Quixote (Python-based web framework) -au BufNewFile,BufRead *.py,*.pyw,.pythonstartup,.pythonrc,*.ptl,*.pyi setf python +au BufNewFile,BufRead *.py,*.pyw,.pythonstartup,.pythonrc setf python +au BufNewFile,BufRead *.ptl,*.pyi,SConstruct setf python " Radiance au BufNewFile,BufRead *.rad,*.mat setf radiance @@ -1293,6 +1326,9 @@ au BufNewFile,BufRead *.reg " Renderman Interface Bytestream au BufNewFile,BufRead *.rib setf rib +" Rego Policy Language +au BufNewFile,BufRead *.rego setf rego + " Rexx au BufNewFile,BufRead *.rex,*.orx,*.rxo,*.rxj,*.jrexx,*.rexxj,*.rexx,*.testGroup,*.testUnit setf rexx @@ -1591,10 +1627,12 @@ au BufNewFile,BufRead *.sqlj setf sqlj au BufNewFile,BufRead *.sqr,*.sqi setf sqr " OpenSSH configuration -au BufNewFile,BufRead ssh_config,*/.ssh/config setf sshconfig +au BufNewFile,BufRead ssh_config,*/.ssh/config setf sshconfig +au BufNewFile,BufRead */etc/ssh/ssh_config.d/*.conf setf sshconfig " OpenSSH server configuration -au BufNewFile,BufRead sshd_config setf sshdconfig +au BufNewFile,BufRead sshd_config setf sshdconfig +au BufNewFile,BufRead */etc/ssh/sshd_config.d/*.conf setf sshdconfig " Stata au BufNewFile,BufRead *.ado,*.do,*.imata,*.mata setf stata @@ -1614,15 +1652,27 @@ au BufNewFile,BufRead *.sml setf sml " Sratus VOS command macro au BufNewFile,BufRead *.cm setf voscm +" Swift +au BufNewFile,BufRead *.swift setf swift +au BufNewFile,BufRead *.swift.gyb setf swiftgyb + +" Swift Intermediate Language +au BufNewFile,BufRead *.sil setf sil + " Sysctl au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl " Systemd unit files -au BufNewFile,BufRead */systemd/*.{automount,mount,path,service,socket,swap,target,timer} setf systemd +au BufNewFile,BufRead */systemd/*.{automount,dnssd,link,mount,netdev,network,nspawn,path,service,slice,socket,swap,target,timer} setf systemd " Systemd overrides -au BufNewFile,BufRead /etc/systemd/system/*.d/*.conf setf systemd +au BufNewFile,BufRead */etc/systemd/*.conf.d/*.conf setf systemd +au BufNewFile,BufRead */etc/systemd/system/*.d/*.conf setf systemd +au BufNewFile,BufRead */.config/systemd/user/*.d/*.conf setf systemd " Systemd temp files -au BufNewFile,BufRead /etc/systemd/system/*.d/.#* setf systemd +au BufNewFile,BufRead */etc/systemd/system/*.d/.#* setf systemd +au BufNewFile,BufRead */etc/systemd/system/.#* setf systemd +au BufNewFile,BufRead */.config/systemd/user/*.d/.#* setf systemd +au BufNewFile,BufRead */.config/systemd/user/.#* setf systemd " Synopsys Design Constraints au BufNewFile,BufRead *.sdc setf sdc @@ -1753,7 +1803,7 @@ au BufNewFile,BufRead *.va,*.vams setf verilogams au BufNewFile,BufRead *.sv,*.svh setf systemverilog " VHDL -au BufNewFile,BufRead *.hdl,*.vhd,*.vhdl,*.vbe,*.vst setf vhdl +au BufNewFile,BufRead *.hdl,*.vhd,*.vhdl,*.vbe,*.vst,*.vho setf vhdl " Vim script au BufNewFile,BufRead *.vim,*.vba,.exrc,_exrc setf vim diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 6c7f095f62..0416e41368 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> " Previous Maintainer: SungHyun Nam <goweol@gmail.com> if exists('b:did_ftplugin') || &filetype !=# 'man' @@ -6,7 +6,7 @@ if exists('b:did_ftplugin') || &filetype !=# 'man' endif let b:did_ftplugin = 1 -let s:pager = get(s:, 'pager', 0) || !exists('b:man_sect') +let s:pager = !exists('b:man_sect') if s:pager call man#init_pager() @@ -20,14 +20,13 @@ setlocal wrap breakindent linebreak setlocal nonumber norelativenumber setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable +setlocal tagfunc=man#goto_tag + if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') nnoremap <silent> <buffer> j gj nnoremap <silent> <buffer> k gk nnoremap <silent> <buffer> gO :call man#show_toc()<CR> - nnoremap <silent> <buffer> <C-]> :Man<CR> - nnoremap <silent> <buffer> K :Man<CR> - nnoremap <silent> <buffer> <C-T> :call man#pop_tag()<CR> - if 1 == bufnr('%') || s:pager + if s:pager nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR> else nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c diff --git a/runtime/indent/Makefile b/runtime/indent/Makefile new file mode 100644 index 0000000000..d192605527 --- /dev/null +++ b/runtime/indent/Makefile @@ -0,0 +1,14 @@ +# Portable Makefile for running indent tests. + +VIM = vim +VIMRUNTIME = .. + +# Run the tests that didn't run yet or failed previously. +# If a test succeeds a testdir/*.out file will be written. +# If a test fails a testdir/*.fail file will be written. +test: + VIMRUNTIME=$(VIMRUNTIME) $(VIM) --clean --not-a-term -u testdir/runtest.vim + + +clean: + rm -f testdir/*.fail testdir/*.out diff --git a/runtime/indent/README.txt b/runtime/indent/README.txt index a424b4f8c0..8b114365c6 100644 --- a/runtime/indent/README.txt +++ b/runtime/indent/README.txt @@ -43,3 +43,5 @@ running. Add a test if the function exists and use ":finish", like this: The user may have several options set unlike you, try to write the file such that it works with any option settings. Also be aware of certain features not being compiled in. + +To test the indent file, see testdir/README.txt. diff --git a/runtime/indent/testdir/README.txt b/runtime/indent/testdir/README.txt new file mode 100644 index 0000000000..65975605c2 --- /dev/null +++ b/runtime/indent/testdir/README.txt @@ -0,0 +1,97 @@ +TESTING INDENT SCRIPTS + +We'll use FILETYPE for the filetype name here. + + +FORMAT OF THE FILETYPE.IN FILE + +First of all, create a FILETYPE.in file. It should contain: + +- A modeline setting the 'filetype' and any other option values. + This must work like a comment for FILETYPE. E.g. for vim: + " vim: set ft=vim sw=4 : + +- At least one block of lines to indent, prefixed with START_INDENT and + followed by END_INDENT. These lines must also look like a comment for your + FILETYPE. You would normally leave out all indent, so that the effect of + the indent command results in adding indent. Example: + + " START_INDENT + func Some() + let x = 1 + endfunc + " END_INDENT + + If you just want to test normal indenting with default options, you can make + this a large number of lines. Just add all kinds of language constructs, + nested statements, etc. with valid syntax. + +- Optionally, add lines with INDENT_EXE after START_INDENT, followed by a Vim + command. This will be executed before indenting the lines. Example: + + " START_INDENT + " INDENT_EXE let g:vim_indent_cont = 6 + let cmd = + \ 'some ' + \ 'string' + " END_INDENT + + Note that the command is not undone, you may need to reverse the effect for + the next block of lines. + +- Alternatively to indenting all the lines between START_INDENT and + END_INDENT, use an INDENT_AT line, which specifies a pattern to find the + line to indent. Example: + + " START_INDENT + " INDENT_AT this-line + func Some() + let f = x " this-line + endfunc + " END_INDENT + + Alternatively you can use INDENT_NEXT to indent the line below the matching + pattern. Keep in mind that quite often it will indent relative to the + matching line: + + " START_INDENT + " INDENT_NEXT next-line + func Some() + " next-line + let f = x + endfunc + " END_INDENT + + Or use INDENT_PREV to indent the line above the matching pattern: + + " START_INDENT + " INDENT_PREV prev-line + func Some() + let f = x + " prev-line + endfunc + " END_INDENT + +It's best to keep the whole file valid for FILETYPE, so that syntax +highlighting works normally, and any indenting that depends on the syntax +highlighting also works. + + +RUNNING THE TEST + +Before running the test, create a FILETYPE.ok file. You can leave it empty at +first. + +Now run "make test" from the parent directory. After Vim has done the +indenting you will see a FILETYPE.fail file. This contains the actual result +of indenting, and it's different from the FILETYPE.ok file. + +Check the contents of the FILETYPE.fail file. If it is perfectly OK, then +rename it to overwrite the FILETYPE.ok file. If you now run "make test" again, +the test will pass and create a FILETYPE.out file, which is identical to the +FILETYPE.ok file. The FILETYPE.fail file will be deleted. + +If you try to run "make test" again you will notice that nothing happens, +because the FILETYPE.out file already exists. Delete it, or do "make clean", +so that the text runs again. If you edit the FILETYPE.in file, so that it's +newer than the FILETYPE.out file, the test will also run. diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim new file mode 100644 index 0000000000..945c2753e9 --- /dev/null +++ b/runtime/indent/testdir/runtest.vim @@ -0,0 +1,133 @@ +" Runs all the indent tests for which there is no .out file. +" +" Current directory must be runtime/indent. + +" Only do this with the +eval feature +if 1 + +set nocp +filetype indent on +syn on +set nowrapscan +set report=9999 +set modeline + +au! SwapExists * call HandleSwapExists() +func HandleSwapExists() + " Ignore finding a swap file for the test input and output, the user might be + " editing them and that's OK. + if expand('<afile>') =~ '.*\.\(in\|out\|fail\|ok\)' + let v:swapchoice = 'e' + endif +endfunc + +let failed_count = 0 +for fname in glob('testdir/*.in', 1, 1) + let root = substitute(fname, '\.in', '', '') + + " Execute the test if the .out file does not exist of when the .in file is + " newer. + let in_time = getftime(fname) + let out_time = getftime(root . '.out') + if out_time < 0 || in_time > out_time + call delete(root . '.fail') + call delete(root . '.out') + + set sw& ts& filetype= + exe 'split ' . fname + + let did_some = 0 + let failed = 0 + let end = 1 + while 1 + " Indent all the lines between "START_INDENT" and "END_INDENT" + exe end + let start = search('\<START_INDENT\>') + let end = search('\<END_INDENT\>') + if start <= 0 || end <= 0 || end <= start + if did_some == 0 + call append(0, 'ERROR: START_INDENT and/or END_INDENT not found') + let failed = 1 + endif + break + else + let did_some = 1 + + " Execute all commands marked with INDENT_EXE and find any pattern. + let lnum = start + let pattern = '' + let at = '' + while 1 + exe lnum + 1 + let lnum_exe = search('\<INDENT_EXE\>') + exe lnum + 1 + let indent_at = search('\<INDENT_\(AT\|NEXT\|PREV\)\>') + if lnum_exe > 0 && lnum_exe < end && (indent_at <= 0 || lnum_exe < indent_at) + exe substitute(getline(lnum_exe), '.*INDENT_EXE', '', '') + let lnum = lnum_exe + let start = lnum + elseif indent_at > 0 && indent_at < end + if pattern != '' + call append(indent_at, 'ERROR: duplicate pattern') + let failed = 1 + break + endif + let text = getline(indent_at) + let pattern = substitute(text, '.*INDENT_\S*\s*', '', '') + let at = substitute(text, '.*INDENT_\(\S*\).*', '\1', '') + let lnum = indent_at + let start = lnum + else + break + endif + endwhile + + exe start + 1 + if pattern == '' + exe 'normal =' . (end - 1) . 'G' + else + let lnum = search(pattern) + if lnum <= 0 + call append(indent_at, 'ERROR: pattern not found: ' . pattern) + let failed = 1 + break + endif + if at == 'AT' + exe lnum + elseif at == 'NEXT' + exe lnum + 1 + else + exe lnum - 1 + endif + normal == + endif + endif + endwhile + + if !failed + " Check the resulting text equals the .ok file. + if getline(1, '$') != readfile(root . '.ok') + let failed = 1 + endif + endif + + if failed + let failed_count += 1 + exe 'write ' . root . '.fail' + echoerr 'Test ' . fname . ' FAILED!' + else + exe 'write ' . root . '.out' + endif + + quit! " close the indented file + endif +endfor + +" Matching "if 1" at the start. +endif + +if failed_count > 0 + " have make report an error + cquit +endif +qall! diff --git a/runtime/indent/testdir/vim.in b/runtime/indent/testdir/vim.in new file mode 100644 index 0000000000..ca105f6eda --- /dev/null +++ b/runtime/indent/testdir/vim.in @@ -0,0 +1,46 @@ +" vim: set ft=vim sw=4 : + +" START_INDENT + +func Some() +let x = 1 +endfunc + +let cmd = +\ 'some ' +\ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 6 + +let cmd = +\ 'some ' +\ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet g:vim_indent_cont +" INDENT_AT this-line +func Some() +let f = x " this-line +endfunc +" END_INDENT + +" START_INDENT +" INDENT_NEXT next-line +func Some() + " next-line +let f = x +endfunc +" END_INDENT + +" START_INDENT +" INDENT_PREV prev-line +func Some() +let f = x +" prev-line +endfunc +" END_INDENT diff --git a/runtime/indent/testdir/vim.ok b/runtime/indent/testdir/vim.ok new file mode 100644 index 0000000000..542861ec9c --- /dev/null +++ b/runtime/indent/testdir/vim.ok @@ -0,0 +1,46 @@ +" vim: set ft=vim sw=4 : + +" START_INDENT + +func Some() + let x = 1 +endfunc + +let cmd = + \ 'some ' + \ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 6 + +let cmd = + \ 'some ' + \ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet g:vim_indent_cont +" INDENT_AT this-line +func Some() + let f = x " this-line +endfunc +" END_INDENT + +" START_INDENT +" INDENT_NEXT next-line +func Some() + " next-line + let f = x +endfunc +" END_INDENT + +" START_INDENT +" INDENT_PREV prev-line +func Some() + let f = x +" prev-line +endfunc +" END_INDENT diff --git a/runtime/indent/tex.vim b/runtime/indent/tex.vim index a748cfbb40..8a44ade1ac 100644 --- a/runtime/indent/tex.vim +++ b/runtime/indent/tex.vim @@ -64,14 +64,17 @@ " style) is supported. Thanks Miles Wheeler for reporting. " 2018/02/07 by Yichao Zhou <broken.zhou AT gmail.com> " (*) Make indentation more smart in the normal mode +" 2020/04/26 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Fix a bug related to \[ & \]. Thanks Manuel Boni for +" reporting. " " }}} " Document: {{{ " -" To set the following options (ok, currently it's just one), add a line like -" let g:tex_indent_items = 1 -" to your ~/.vimrc. +" For proper latex experience, please put +" let g:tex_flavor = "latex" +" into your vimrc. " " * g:tex_indent_brace " @@ -184,13 +187,18 @@ function! GetTeXIndent() " {{{ let line = substitute(getline(lnum), '\s*%.*', '','g') " last line let cline = substitute(getline(v:lnum), '\s*%.*', '', 'g') " current line + let ccol = 1 + while cline[ccol] =~ '\s' + let ccol += 1 + endwhile + " We are in verbatim, so do what our user what. - if synIDattr(synID(v:lnum, indent(v:lnum), 1), "name") == "texZone" + if synIDattr(synID(v:lnum, ccol, 1), "name") == "texZone" if empty(cline) return indent(lnum) else return indent(v:lnum) - end + endif endif if lnum == 0 @@ -253,13 +261,13 @@ function! GetTeXIndent() " {{{ let stay = 0 endif - if cline =~ '^\s*\\\?[\]}]' && s:CheckPairedIsLastCharacter(v:lnum, indent(v:lnum)) + if cline =~ '^\s*\\\?[\]}]' && s:CheckPairedIsLastCharacter(v:lnum, ccol) let ind -= shiftwidth() let stay = 0 endif if line !~ '^\s*\\\?[\]}]' - for i in range(indent(lnum)+1, strlen(line)-1) + for i in range(1, strlen(line)-1) let char = line[i] if char == ']' || char == '}' if s:CheckPairedIsLastCharacter(lnum, i) diff --git a/runtime/keymap/russian-jcukenwintype.vim b/runtime/keymap/russian-jcukenwintype.vim new file mode 100644 index 0000000000..25d6286e24 --- /dev/null +++ b/runtime/keymap/russian-jcukenwintype.vim @@ -0,0 +1,112 @@ +" Vim Keymap file for russian characters, layout 'jcuken', MS Windows +" Typewriter variant (slightly incompatible with XFree86 'ru' keymap - +" makes use of NUMERO SIGN) +" Useful mainly with utf-8 but may work with other encodings + +" Derived from russian-jcuken.vim by Artem Chuprina <ran@ran.pp.ru> +" Typewriter variant of standart russian layout +" Maintainer: Denis Proskurin <danwerspb@gmail.com> +" Last Changed: 2015 May 15 + +" All characters are given literally, conversion to another encoding (e.g., +" UTF-8) should work. + +scriptencoding utf-8 + +let b:keymap_name = "ru" + +loadkeymap +F А CYRILLIC CAPITAL LETTER A +< Б CYRILLIC CAPITAL LETTER BE +D В CYRILLIC CAPITAL LETTER VE +U Г CYRILLIC CAPITAL LETTER GHE +L Д CYRILLIC CAPITAL LETTER DE +T Е CYRILLIC CAPITAL LETTER IE +? Ё CYRILLIC CAPITAL LETTER IO +: Ж CYRILLIC CAPITAL LETTER ZHE +P З CYRILLIC CAPITAL LETTER ZE +B И CYRILLIC CAPITAL LETTER I +Q Й CYRILLIC CAPITAL LETTER SHORT I +R К CYRILLIC CAPITAL LETTER KA +K Л CYRILLIC CAPITAL LETTER EL +V М CYRILLIC CAPITAL LETTER EM +Y Н CYRILLIC CAPITAL LETTER EN +J О CYRILLIC CAPITAL LETTER O +G П CYRILLIC CAPITAL LETTER PE +H Р CYRILLIC CAPITAL LETTER ER +C С CYRILLIC CAPITAL LETTER ES +N Т CYRILLIC CAPITAL LETTER TE +E У CYRILLIC CAPITAL LETTER U +A Ф CYRILLIC CAPITAL LETTER EF +{ Х CYRILLIC CAPITAL LETTER HA +W Ц CYRILLIC CAPITAL LETTER TSE +X Ч CYRILLIC CAPITAL LETTER CHE +I Ш CYRILLIC CAPITAL LETTER SHA +O Щ CYRILLIC CAPITAL LETTER SHCHA +} Ъ CYRILLIC CAPITAL LETTER HARD SIGN +S Ы CYRILLIC CAPITAL LETTER YERU +M Ь CYRILLIC CAPITAL LETTER SOFT SIGN +\" Э CYRILLIC CAPITAL LETTER E +> Ю CYRILLIC CAPITAL LETTER YU +Z Я CYRILLIC CAPITAL LETTER YA +f а CYRILLIC SMALL LETTER A +, б CYRILLIC SMALL LETTER BE +d в CYRILLIC SMALL LETTER VE +u г CYRILLIC SMALL LETTER GHE +l д CYRILLIC SMALL LETTER DE +t е CYRILLIC SMALL LETTER IE +/ ё CYRILLIC SMALL LETTER IO +; ж CYRILLIC SMALL LETTER ZHE +p з CYRILLIC SMALL LETTER ZE +b и CYRILLIC SMALL LETTER I +q й CYRILLIC SMALL LETTER SHORT I +r к CYRILLIC SMALL LETTER KA +k л CYRILLIC SMALL LETTER EL +v м CYRILLIC SMALL LETTER EM +y н CYRILLIC SMALL LETTER EN +j о CYRILLIC SMALL LETTER O +g п CYRILLIC SMALL LETTER PE +h р CYRILLIC SMALL LETTER ER +c с CYRILLIC SMALL LETTER ES +n т CYRILLIC SMALL LETTER TE +e у CYRILLIC SMALL LETTER U +a ф CYRILLIC SMALL LETTER EF +[ х CYRILLIC SMALL LETTER HA +w ц CYRILLIC SMALL LETTER TSE +x ч CYRILLIC SMALL LETTER CHE +i ш CYRILLIC SMALL LETTER SHA +o щ CYRILLIC SMALL LETTER SHCHA +] ъ CYRILLIC SMALL LETTER HARD SIGN +s ы CYRILLIC SMALL LETTER YERU +m ь CYRILLIC SMALL LETTER SOFT SIGN +' э CYRILLIC SMALL LETTER E +. ю CYRILLIC SMALL LETTER YU +z я CYRILLIC SMALL LETTER YA +` | +1 № +2 - +3 / +4 " +5 : +6 , +7 . +8 _ +9 ? +0 % +- ! += ; +~ + +! 1 +@ 2 +# 3 +$ 4 +% 5 +^ 6 +& 7 +* 8 +( 9 +) 0 +_ = ++ \\ +\\ ) +\| ( diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua new file mode 100644 index 0000000000..ce0a3de520 --- /dev/null +++ b/runtime/lua/vim/highlight.lua @@ -0,0 +1,77 @@ +local api = vim.api + +local highlight = {} + +--- Highlight range between two positions +--- +--@param bufnr number of buffer to apply highlighting to +--@param ns namespace to add highlight to +--@param higroup highlight group to use for highlighting +--@param rtype type of range (:help setreg, default charwise) +--@param inclusive boolean indicating whether the range is end-inclusive (default false) +function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive) + rtype = rtype or 'v' + inclusive = inclusive or false + + -- sanity check + if start[2] < 0 or finish[2] < start[2] then return end + + local region = vim.region(bufnr, start, finish, rtype, inclusive) + for linenr, cols in pairs(region) do + api.nvim_buf_add_highlight(bufnr, ns, higroup, linenr, cols[1], cols[2]) + end + +end + +local yank_ns = api.nvim_create_namespace('hlyank') +--- Highlight the yanked region +--- +--- use from init.vim via +--- au TextYankPost * lua vim.highlight.on_yank() +--- customize highlight group and timeout via +--- au TextYankPost * lua vim.highlight.on_yank {higroup="IncSearch", timeout=150} +--- customize conditions (here: do not highlight a visual selection) via +--- au TextYankPost * lua vim.highlight.on_yank {on_visual=false} +--- +-- @param opts dictionary with options controlling the highlight: +-- - higroup highlight group for yanked region (default "IncSearch") +-- - timeout time in ms before highlight is cleared (default 150) +-- - on_macro highlight when executing macro (default false) +-- - on_visual highlight when yanking visual selection (default true) +-- - event event structure (default vim.v.event) +function highlight.on_yank(opts) + vim.validate { + opts = { opts, + function(t) if t == nil then return true else return type(t) == 'table' end end, + 'a table or nil to configure options (see `:h highlight.on_yank`)', + }} + opts = opts or {} + local event = opts.event or vim.v.event + local on_macro = opts.on_macro or false + local on_visual = (opts.on_visual ~= false) + + if (not on_macro) and vim.fn.reg_executing() ~= '' then return end + if event.operator ~= 'y' or event.regtype == '' then return end + if (not on_visual) and event.visual then return end + + local higroup = opts.higroup or "IncSearch" + local timeout = opts.timeout or 150 + + local bufnr = api.nvim_get_current_buf() + api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) + + 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]} + + highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive) + + vim.defer_fn( + function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end, + timeout + ) +end + +return highlight diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index 7cb40ca64d..0448ea487f 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -244,6 +244,11 @@ function Inspector:putTable(t) local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) local mt = getmetatable(t) + if (vim and sequenceLength == 0 and nonSequentialKeysLength == 0 + and mt == vim._empty_dict_mt) then + self:puts(tostring(t)) + return + end self:puts('{') self:down(function() @@ -289,7 +294,7 @@ function Inspector:putValue(v) if tv == 'string' then self:puts(smartQuote(escape(v))) elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or - tv == 'cdata' or tv == 'ctype' then + tv == 'cdata' or tv == 'ctype' or (vim and v == vim.NIL) then self:puts(tostring(v)) elseif tv == 'table' then self:putTable(v) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua new file mode 100644 index 0000000000..6fe1d15b7e --- /dev/null +++ b/runtime/lua/vim/lsp.lua @@ -0,0 +1,1013 @@ +local default_callbacks = require 'vim.lsp.callbacks' +local log = require 'vim.lsp.log' +local lsp_rpc = require 'vim.lsp.rpc' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' + +local vim = vim +local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option + = vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option +local uv = vim.loop +local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend +local validate = vim.validate + +local lsp = { + protocol = protocol; + callbacks = default_callbacks; + buf = require'vim.lsp.buf'; + util = util; + -- Allow raw RPC access. + rpc = lsp_rpc; + -- Export these directly from rpc. + rpc_response_error = lsp_rpc.rpc_response_error; + -- You probably won't need this directly, since __tostring is set for errors + -- by the RPC. + -- format_rpc_error = lsp_rpc.format_rpc_error; +} + +-- TODO improve handling of scratch buffers with LSP attached. + +local function err_message(...) + nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + nvim_command("redraw") +end + +local function resolve_bufnr(bufnr) + validate { bufnr = { bufnr, 'n', true } } + if bufnr == nil or bufnr == 0 then + return vim.api.nvim_get_current_buf() + end + return bufnr +end + +local function is_dir(filename) + validate{filename={filename,'s'}} + local stat = uv.fs_stat(filename) + return stat and stat.type == 'directory' or false +end + +local wait_result_reason = { [-1] = "timeout"; [-2] = "interrupted"; [-3] = "error" } + +local valid_encodings = { + ["utf-8"] = 'utf-8'; ["utf-16"] = 'utf-16'; ["utf-32"] = 'utf-32'; + ["utf8"] = 'utf-8'; ["utf16"] = 'utf-16'; ["utf32"] = 'utf-32'; + UTF8 = 'utf-8'; UTF16 = 'utf-16'; UTF32 = 'utf-32'; +} + +local client_index = 0 +local function next_client_id() + client_index = client_index + 1 + return client_index +end +-- Tracks all clients created via lsp.start_client +local active_clients = {} +local all_buffer_active_clients = {} +local uninitialized_clients = {} + +local function for_each_buffer_client(bufnr, callback) + validate { + callback = { callback, 'f' }; + } + bufnr = resolve_bufnr(bufnr) + local client_ids = all_buffer_active_clients[bufnr] + if not client_ids or tbl_isempty(client_ids) then + return + end + for client_id in pairs(client_ids) do + local client = active_clients[client_id] + if client then + callback(client, client_id, bufnr) + end + end +end + +-- Error codes to be used with `on_error` from |vim.lsp.start_client|. +-- Can be used to look up the string from a the number or the number +-- from the string. +lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_reverse_lookup { + ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1; +}) + +local function validate_encoding(encoding) + validate { + encoding = { encoding, 's' }; + } + return valid_encodings[encoding:lower()] + or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) +end + +function lsp._cmd_parts(input) + vim.validate{cmd={ + input, + function() return vim.tbl_islist(input) end, + "list"}} + + local cmd = input[1] + local cmd_args = {} + -- Don't mutate our input. + for i, v in ipairs(input) do + vim.validate{["cmd argument"]={v, "s"}} + if i > 1 then + table.insert(cmd_args, v) + end + end + return cmd, cmd_args +end + +local function optional_validator(fn) + return function(v) + return v == nil or fn(v) + end +end + +local function validate_client_config(config) + validate { + config = { config, 't' }; + } + validate { + root_dir = { config.root_dir, is_dir, "directory" }; + callbacks = { config.callbacks, "t", true }; + capabilities = { config.capabilities, "t", true }; + cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; + cmd_env = { config.cmd_env, "t", true }; + name = { config.name, 's', true }; + on_error = { config.on_error, "f", true }; + on_exit = { config.on_exit, "f", true }; + on_init = { config.on_init, "f", true }; + before_init = { config.before_init, "f", true }; + offset_encoding = { config.offset_encoding, "s", true }; + } + local cmd, cmd_args = lsp._cmd_parts(config.cmd) + local offset_encoding = valid_encodings.UTF16 + if config.offset_encoding then + offset_encoding = validate_encoding(config.offset_encoding) + end + return { + cmd = cmd; cmd_args = cmd_args; + offset_encoding = offset_encoding; + } +end + +local function buf_get_full_text(bufnr) + local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') + if nvim_buf_get_option(bufnr, 'eol') then + text = text .. '\n' + end + return text +end + +local function text_document_did_open_handler(bufnr, client) + if not client.resolved_capabilities.text_document_open_close then + return + end + if not vim.api.nvim_buf_is_loaded(bufnr) then + return + end + local params = { + textDocument = { + version = 0; + uri = vim.uri_from_bufnr(bufnr); + -- TODO make sure our filetypes are compatible with languageId names. + languageId = nvim_buf_get_option(bufnr, 'filetype'); + text = buf_get_full_text(bufnr); + } + } + client.notify('textDocument/didOpen', params) + util.buf_versions[bufnr] = params.textDocument.version +end + +--- LSP client object. +--- +--- - Methods: +--- +--- - request(method, params, [callback]) +--- Send a request to the server. If callback is not specified, it will use +--- {client.callbacks} to try to find a callback. If one is not found there, +--- then an error will occur. +--- This is a thin wrapper around {client.rpc.request} with some additional +--- checking. +--- Returns a boolean to indicate if the notification was successful. If it +--- is false, then it will always be false (the client has shutdown). +--- If it was successful, then it will return the request id as the second +--- result. You can use this with `notify("$/cancel", { id = request_id })` +--- to cancel the request. This helper is made automatically with +--- |vim.lsp.buf_request()| +--- Returns: status, [client_id] +--- +--- - notify(method, params) +--- This is just {client.rpc.notify}() +--- Returns a boolean to indicate if the notification was successful. If it +--- is false, then it will always be false (the client has shutdown). +--- Returns: status +--- +--- - cancel_request(id) +--- This is just {client.rpc.notify}("$/cancelRequest", { id = id }) +--- Returns the same as `notify()`. +--- +--- - stop([force]) +--- Stop a client, optionally with force. +--- By default, it will just ask the server to shutdown without force. +--- If you request to stop a client which has previously been requested to +--- shutdown, it will automatically escalate and force shutdown. +--- +--- - is_stopped() +--- Returns true if the client is fully stopped. +--- +--- - Members +--- - id (number): The id allocated to the client. +--- +--- - name (string): If a name is specified on creation, that will be +--- used. Otherwise it is just the client id. This is used for +--- logs and messages. +--- +--- - offset_encoding (string): The encoding used for communicating +--- with the server. You can modify this in the `on_init` method +--- before text is sent to the server. +--- +--- - callbacks (table): The callbacks used by the client as +--- described in |lsp-callbacks|. +--- +--- - config (table): copy of the table that was passed by the user +--- to |vim.lsp.start_client()|. +--- +--- - server_capabilities (table): Response from the server sent on +--- `initialize` describing the server's capabilities. +--- +--- - resolved_capabilities (table): Normalized table of +--- capabilities that we have detected based on the initialize +--- response from the server in `server_capabilities`. +function lsp.client() + error() +end + +--- Starts and initializes a client with the given configuration. +--- +--- Parameters `cmd` and `root_dir` are required. +--- +--@param root_dir: (required, string) Directory where the LSP server will base +--- its rootUri on initialization. +--- +--@param cmd: (required, string or list treated like |jobstart()|) Base command +--- that initiates the LSP client. +--- +--@param cmd_cwd: (string, default=|getcwd()|) Directory to launch +--- the `cmd` process. Not related to `root_dir`. +--- +--@param cmd_env: (table) Environment flags to pass to the LSP on +--- spawn. Can be specified using keys like a map or as a list with `k=v` +--- pairs or both. Non-string values are coerced to string. +--- Example: +--- <pre> +--- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } +--- </pre> +--- +--@param capabilities Map overriding the default capabilities defined by +--- |vim.lsp.protocol.make_client_capabilities()|, passed to the language +--- server on initialization. Hint: use make_client_capabilities() and modify +--- its result. +--- - Note: To send an empty dictionary use +--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an +--- array. +--- +--@param callbacks Map of language server method names to +--- `function(err, method, params, client_id)` handler. Invoked for: +--- - Notifications from the server, where `err` will always be `nil`. +--- - Requests initiated by the server. For these you can respond by returning +--- two values: `result, err` where err must be shaped like a RPC error, +--- i.e. `{ code, message, data? }`. Use |vim.lsp.rpc_response_error()| to +--- help with this. +--- - Default callback for client requests not explicitly specifying +--- a callback. +--- +--@param init_options Values to pass in the initialization request +--- as `initializationOptions`. See `initialize` in the LSP spec. +--- +--@param name (string, default=client-id) Name in log messages. +--- +--@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", +--- or "utf-32" which is the encoding that the LSP server expects. Client does +--- not verify this is correct. +--- +--@param on_error Callback with parameters (code, ...), invoked +--- when the client operation throws an error. `code` is a number describing +--- the error. Other arguments may be passed depending on the error kind. See +--- |vim.lsp.client_errors| for possible errors. +--- Use `vim.lsp.client_errors[code]` to get human-friendly name. +--- +--@param before_init Callback with parameters (initialize_params, config) +--- invoked before the LSP "initialize" phase, where `params` contains the +--- parameters being sent to the server and `config` is the config that was +--- passed to `start_client()`. You can use this to modify parameters before +--- they are sent. +--- +--@param on_init Callback (client, initialize_result) invoked after LSP +--- "initialize", where `result` is a table of `capabilities` and anything else +--- the server may send. For example, clangd sends +--- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was +--- sent to it. You can only modify the `client.offset_encoding` here before +--- any notifications are sent. +--- +--@param on_exit Callback (code, signal, client_id) invoked on client +--- exit. +--- - code: exit code of the process +--- - signal: number describing the signal used to terminate (if any) +--- - client_id: client handle +--- +--@param on_attach Callback (client, bufnr) invoked when client +--- attaches to a buffer. +--- +--@param trace: "off" | "messages" | "verbose" | nil passed directly to the language +--- server in the initialize request. Invalid/empty values will default to "off" +--- +--@returns Client id. |vim.lsp.get_client_by_id()| Note: client is only +--- available after it has been initialized, which may happen after a small +--- delay (or never if there is an error). Use `on_init` to do any actions once +--- the client has been initialized. +function lsp.start_client(config) + local cleaned_config = validate_client_config(config) + local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding + + local client_id = next_client_id() + + local callbacks = config.callbacks or {} + local name = config.name or tostring(client_id) + local log_prefix = string.format("LSP[%s]", name) + + local handlers = {} + + local function resolve_callback(method) + return callbacks[method] or default_callbacks[method] + end + + function handlers.notification(method, params) + local _ = log.debug() and log.debug('notification', method, params) + local callback = resolve_callback(method) + if callback then + -- Method name is provided here for convenience. + callback(nil, method, params, client_id) + end + end + + function handlers.server_request(method, params) + local _ = log.debug() and log.debug('server_request', method, params) + local callback = resolve_callback(method) + if callback then + local _ = log.debug() and log.debug("server_request: found callback for", method) + return callback(nil, method, params, client_id) + end + local _ = log.debug() and log.debug("server_request: no callback found for", method) + return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) + end + + function handlers.on_error(code, err) + local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) + err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) + if config.on_error then + local status, usererr = pcall(config.on_error, code, err) + if not status then + local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr }) + err_message(log_prefix, ' user on_error failed: ', tostring(usererr)) + end + end + end + + function handlers.on_exit(code, signal) + active_clients[client_id] = nil + uninitialized_clients[client_id] = nil + local active_buffers = {} + for bufnr, client_ids in pairs(all_buffer_active_clients) do + if client_ids[client_id] then + table.insert(active_buffers, bufnr) + end + client_ids[client_id] = nil + end + -- Buffer level cleanup + vim.schedule(function() + for _, bufnr in ipairs(active_buffers) do + util.buf_clear_diagnostics(bufnr) + end + end) + if config.on_exit then + pcall(config.on_exit, code, signal, client_id) + end + end + + -- Start the RPC client. + local rpc = lsp_rpc.start(cmd, cmd_args, handlers, { + cwd = config.cmd_cwd; + env = config.cmd_env; + }) + + local client = { + id = client_id; + name = name; + rpc = rpc; + offset_encoding = offset_encoding; + callbacks = callbacks; + config = config; + } + + -- Store the uninitialized_clients for cleanup in case we exit before + -- initialize finishes. + uninitialized_clients[client_id] = client; + + local function initialize() + local valid_traces = { + off = 'off'; messages = 'messages'; verbose = 'verbose'; + } + local initialize_params = { + -- The process Id of the parent process that started the server. Is null if + -- the process has not been started by another process. If the parent + -- process is not alive then the server should exit (see exit notification) + -- its process. + processId = uv.getpid(); + -- The rootPath of the workspace. Is null if no folder is open. + -- + -- @deprecated in favour of rootUri. + rootPath = config.root_dir; + -- The rootUri of the workspace. Is null if no folder is open. If both + -- `rootPath` and `rootUri` are set `rootUri` wins. + rootUri = vim.uri_from_fname(config.root_dir); + -- User provided initialization options. + initializationOptions = config.init_options; + -- The capabilities provided by the client (editor or tool) + capabilities = config.capabilities or protocol.make_client_capabilities(); + -- The initial trace setting. If omitted trace is disabled ("off"). + -- trace = "off" | "messages" | "verbose"; + trace = valid_traces[config.trace] or 'off'; + -- The workspace folders configured in the client when the server starts. + -- This property is only available if the client supports workspace folders. + -- It can be `null` if the client supports workspace folders but none are + -- configured. + -- + -- Since 3.6.0 + -- workspaceFolders?: WorkspaceFolder[] | null; + -- export interface WorkspaceFolder { + -- -- The associated URI for this workspace folder. + -- uri + -- -- The name of the workspace folder. Used to refer to this + -- -- workspace folder in the user interface. + -- name + -- } + workspaceFolders = nil; + } + if config.before_init then + -- TODO(ashkan) handle errors here. + pcall(config.before_init, initialize_params, config) + end + local _ = log.debug() and log.debug(log_prefix, "initialize_params", initialize_params) + rpc.request('initialize', initialize_params, function(init_err, result) + assert(not init_err, tostring(init_err)) + assert(result, "server sent empty result") + rpc.notify('initialized', {[vim.type_idx]=vim.types.dictionary}) + client.initialized = true + uninitialized_clients[client_id] = nil + client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities") + -- These are the cleaned up capabilities we use for dynamically deciding + -- when to send certain events to clients. + client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities) + if config.on_init then + local status, err = pcall(config.on_init, client, result) + if not status then + pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err) + end + end + local _ = log.debug() and log.debug(log_prefix, "server_capabilities", client.server_capabilities) + local _ = log.info() and log.info(log_prefix, "initialized", { resolved_capabilities = client.resolved_capabilities }) + + -- Only assign after initialized. + active_clients[client_id] = client + -- If we had been registered before we start, then send didOpen This can + -- happen if we attach to buffers before initialize finishes or if + -- someone restarts a client. + for bufnr, client_ids in pairs(all_buffer_active_clients) do + if client_ids[client_id] then + client._on_attach(bufnr) + end + end + end) + end + + local function unsupported_method(method) + local msg = "server doesn't support "..method + local _ = log.warn() and log.warn(msg) + err_message(msg) + return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) + end + + --- Checks capabilities before rpc.request-ing. + function client.request(method, params, callback, bufnr) + if not callback then + callback = resolve_callback(method) + or error(string.format("not found: %q request callback for client %q.", method, client.name)) + end + local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr) + -- TODO keep these checks or just let it go anyway? + if (not client.resolved_capabilities.hover and method == 'textDocument/hover') + or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp') + or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') + or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') + or (not client.resolved_capabilities.declaration and method == 'textDocument/declaration') + or (not client.resolved_capabilities.type_definition and method == 'textDocument/typeDefinition') + or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') + or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol') + or (not client.resolved_capabilities.call_hierarchy and method == 'textDocument/prepareCallHierarchy') + then + callback(unsupported_method(method), method, nil, client_id, bufnr) + return + end + return rpc.request(method, params, function(err, result) + callback(err, method, result, client_id, bufnr) + end) + end + + function client.notify(...) + return rpc.notify(...) + end + + function client.cancel_request(id) + validate{id = {id, 'n'}} + return rpc.notify("$/cancelRequest", { id = id }) + end + + -- Track this so that we can escalate automatically if we've alredy tried a + -- graceful shutdown + local tried_graceful_shutdown = false + function client.stop(force) + local handle = rpc.handle + if handle:is_closing() then + return + end + if force or (not client.initialized) or tried_graceful_shutdown then + handle:kill(15) + return + end + tried_graceful_shutdown = true + -- Sending a signal after a process has exited is acceptable. + rpc.request('shutdown', nil, function(err, _) + if err == nil then + rpc.notify('exit') + else + -- If there was an error in the shutdown request, then term to be safe. + handle:kill(15) + end + end) + end + + function client.is_stopped() + return rpc.handle:is_closing() + end + + function client._on_attach(bufnr) + text_document_did_open_handler(bufnr, client) + if config.on_attach then + -- TODO(ashkan) handle errors. + pcall(config.on_attach, client, bufnr) + end + end + + initialize() + + return client_id +end + +local function once(fn) + local value + return function(...) + if not value then value = fn(...) end + return value + end +end + +local text_document_did_change_handler +do + local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; } + text_document_did_change_handler = function(_, bufnr, changedtick, + firstline, lastline, new_lastline, old_byte_size, old_utf32_size, + old_utf16_size) + local _ = log.debug() and log.debug("on_lines", bufnr, changedtick, firstline, + lastline, new_lastline, old_byte_size, old_utf32_size, old_utf16_size, nvim_buf_get_lines(bufnr, firstline, new_lastline, true)) + + -- Don't do anything if there are no clients attached. + if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then + return + end + + util.buf_versions[bufnr] = changedtick + -- Lazy initialize these because clients may not even need them. + local incremental_changes = once(function(client) + local size_index = encoding_index[client.offset_encoding] + local length = select(size_index, old_byte_size, old_utf16_size, old_utf32_size) + local lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true) + -- This is necessary because we are specifying the full line including the + -- newline in range. Therefore, we must replace the newline as well. + if #lines > 0 then + table.insert(lines, '') + end + return { + range = { + start = { line = firstline, character = 0 }; + ["end"] = { line = lastline, character = 0 }; + }; + rangeLength = length; + text = table.concat(lines, '\n'); + }; + end) + local full_changes = once(function() + return { + text = buf_get_full_text(bufnr); + }; + end) + local uri = vim.uri_from_bufnr(bufnr) + for_each_buffer_client(bufnr, function(client, _client_id) + local text_document_did_change = client.resolved_capabilities.text_document_did_change + local changes + if text_document_did_change == protocol.TextDocumentSyncKind.None then + return + --[=[ TODO(ashkan) there seem to be problems with the byte_sizes sent by + -- neovim right now so only send the full content for now. In general, we + -- can assume that servers *will* support both versions anyway, as there + -- is no way to specify the sync capability by the client. + -- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both. + --]=] + elseif true or text_document_did_change == protocol.TextDocumentSyncKind.Full then + changes = full_changes(client) + elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then + changes = incremental_changes(client) + end + client.notify("textDocument/didChange", { + textDocument = { + uri = uri; + version = changedtick; + }; + contentChanges = { changes; } + }) + end) + end +end + +-- Buffer lifecycle handler for textDocument/didSave +function lsp._text_document_did_save_handler(bufnr) + bufnr = resolve_bufnr(bufnr) + local uri = vim.uri_from_bufnr(bufnr) + local text = once(function() + return table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n') + end) + for_each_buffer_client(bufnr, function(client, _client_id) + if client.resolved_capabilities.text_document_save then + local included_text + if client.resolved_capabilities.text_document_save_include_text then + included_text = text() + end + client.notify('textDocument/didSave', { + textDocument = { + uri = uri; + text = included_text; + } + }) + end + end) +end + +--- Implements the `textDocument/did…` notifications required to track a buffer +--- for any language server. +--- +--- Without calling this, the server won't be notified of changes to a buffer. +--- +--- @param bufnr (number) Buffer handle, or 0 for current +--- @param client_id (number) Client id +function lsp.buf_attach_client(bufnr, client_id) + validate { + bufnr = {bufnr, 'n', true}; + client_id = {client_id, 'n'}; + } + bufnr = resolve_bufnr(bufnr) + local buffer_client_ids = all_buffer_active_clients[bufnr] + -- This is our first time attaching to this buffer. + if not buffer_client_ids then + buffer_client_ids = {} + all_buffer_active_clients[bufnr] = buffer_client_ids + + local uri = vim.uri_from_bufnr(bufnr) + nvim_command(string.format("autocmd BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0)", bufnr)) + -- First time, so attach and set up stuff. + vim.api.nvim_buf_attach(bufnr, false, { + on_lines = text_document_did_change_handler; + on_detach = function() + local params = { textDocument = { uri = uri; } } + for_each_buffer_client(bufnr, function(client, _client_id) + if client.resolved_capabilities.text_document_open_close then + client.notify('textDocument/didClose', params) + end + end) + util.buf_versions[bufnr] = nil + all_buffer_active_clients[bufnr] = nil + end; + -- TODO if we know all of the potential clients ahead of time, then we + -- could conditionally set this. + -- utf_sizes = size_index > 1; + utf_sizes = true; + }) + end + if buffer_client_ids[client_id] then return end + -- This is our first time attaching this client to this buffer. + buffer_client_ids[client_id] = true + + local client = active_clients[client_id] + -- Send didOpen for the client if it is initialized. If it isn't initialized + -- then it will send didOpen on initialize. + if client then + client._on_attach(bufnr) + end + return true +end + +--- Checks if a buffer is attached for a particular client. +--- +---@param bufnr (number) Buffer handle, or 0 for current +---@param client_id (number) the client id +function lsp.buf_is_attached(bufnr, client_id) + return (all_buffer_active_clients[bufnr] or {})[client_id] == true +end + +--- Gets an active client by id, or nil if the id is invalid or the +--- client is not yet initialized. +--- +--@param client_id client id number +--- +--@return |vim.lsp.client| object, or nil +function lsp.get_client_by_id(client_id) + return active_clients[client_id] +end + +--- Stops a client(s). +--- +--- You can also use the `stop()` function on a |vim.lsp.client| object. +--- To stop all clients: +--- +--- <pre> +--- vim.lsp.stop_client(lsp.get_active_clients()) +--- </pre> +--- +--- By default asks the server to shutdown, unless stop was requested +--- already for this client, then force-shutdown is attempted. +--- +--@param client_id client id or |vim.lsp.client| object, or list thereof +--@param force boolean (optional) shutdown forcefully +function lsp.stop_client(client_id, force) + local ids = type(client_id) == 'table' and client_id or {client_id} + for _, id in ipairs(ids) do + if type(id) == 'table' and id.stop ~= nil then + id.stop(force) + elseif active_clients[id] then + active_clients[id].stop(force) + elseif uninitialized_clients[id] then + uninitialized_clients[id].stop(true) + end + end +end + +--- Gets all active clients. +--- +--@return Table of |vim.lsp.client| objects +function lsp.get_active_clients() + return vim.tbl_values(active_clients) +end + +function lsp._vim_exit_handler() + log.info("exit_handler", active_clients) + for _, client in pairs(uninitialized_clients) do + client.stop(true) + end + -- TODO handle v:dying differently? + if tbl_isempty(active_clients) then + return + end + for _, client in pairs(active_clients) do + client.stop() + end + + if not vim.wait(500, function() return tbl_isempty(active_clients) end, 50) then + for _, client in pairs(active_clients) do + client.stop(true) + end + end +end + +nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") + + +--- Sends an async request for all active clients attached to the +--- buffer. +--- +--@param bufnr (number) Buffer handle, or 0 for current. +--@param method (string) LSP method name +--@param params (optional, table) Parameters to send to the server +--@param callback (optional, functionnil) Handler +-- `function(err, method, params, client_id)` for this request. Defaults +-- to the client callback in `client.callbacks`. See |lsp-callbacks|. +-- +--@returns 2-tuple: +--- - Map of client-id:request-id pairs for all successful requests. +--- - Function which can be used to cancel all the requests. You could instead +--- iterate all clients and call their `cancel_request()` methods. +function lsp.buf_request(bufnr, method, params, callback) + validate { + bufnr = { bufnr, 'n', true }; + method = { method, 's' }; + callback = { callback, 'f', true }; + } + local client_request_ids = {} + for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr) + local request_success, request_id = client.request(method, params, callback, resolved_bufnr) + + -- This could only fail if the client shut down in the time since we looked + -- it up and we did the request, which should be rare. + if request_success then + client_request_ids[client_id] = request_id + end + end) + + local function _cancel_all_requests() + for client_id, request_id in pairs(client_request_ids) do + local client = active_clients[client_id] + client.cancel_request(request_id) + end + end + + return client_request_ids, _cancel_all_requests +end + +--- Sends a request to a server and waits for the response. +--- +--- Calls |vim.lsp.buf_request()| but blocks Nvim while awaiting the result. +--- Parameters are the same as |vim.lsp.buf_request()| but the return result is +--- different. Wait maximum of {timeout_ms} (default 100) ms. +--- +--@param bufnr (number) Buffer handle, or 0 for current. +--@param method (string) LSP method name +--@param params (optional, table) Parameters to send to the server +--@param timeout_ms (optional, number, default=100) Maximum time in +--- milliseconds to wait for a result. +--- +--@returns Map of client_id:request_result. On timeout, cancel or error, +--- returns `(nil, err)` where `err` is a string describing the failure +--- reason. +function lsp.buf_request_sync(bufnr, method, params, timeout_ms) + local request_results = {} + local result_count = 0 + local function _callback(err, _method, result, client_id) + request_results[client_id] = { error = err, result = result } + result_count = result_count + 1 + end + local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, _callback) + local expected_result_count = 0 + for _ in pairs(client_request_ids) do + expected_result_count = expected_result_count + 1 + end + + local wait_result, reason = vim.wait(timeout_ms or 100, function() + return result_count >= expected_result_count + end, 10) + + if not wait_result then + cancel() + return nil, wait_result_reason[reason] + end + return request_results +end + +--- Send a notification to a server +--@param bufnr [number] (optional): The number of the buffer +--@param method [string]: Name of the request method +--@param params [string]: Arguments to send to the server +--- +--@returns true if any client returns true; false otherwise +function lsp.buf_notify(bufnr, method, params) + validate { + bufnr = { bufnr, 'n', true }; + method = { method, 's' }; + } + local resp = false + for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr) + if client.rpc.notify(method, params) then resp = true end + end) + return resp +end + +--- Implements 'omnifunc' compatible LSP completion. +--- +--@see |complete-functions| +--@see |complete-items| +--@see |CompleteDone| +--- +--@param findstart 0 or 1, decides behavior +--@param base If findstart=0, text to match against +--- +--@return (number) Decided by `findstart`: +--- - findstart=0: column where the completion starts, or -2 or -3 +--- - findstart=1: list of matches (actually just calls |complete()|) +function lsp.omnifunc(findstart, base) + local _ = log.debug() and log.debug("omnifunc.findstart", { findstart = findstart, base = base }) + + local bufnr = resolve_bufnr() + local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {}) + if not has_buffer_clients then + if findstart == 1 then + return -1 + else + return {} + end + end + + -- Then, perform standard completion request + local _ = log.info() and log.info("base ", base) + + local pos = vim.api.nvim_win_get_cursor(0) + local line = vim.api.nvim_get_current_line() + local line_to_cursor = line:sub(1, pos[2]) + local _ = log.trace() and log.trace("omnifunc.line", pos, line) + + -- Get the start position of the current keyword + local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local prefix = line_to_cursor:sub(textMatch+1) + + local params = util.make_position_params() + + local items = {} + lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, _, result) + if err or not result then return end + local matches = util.text_document_completion_list_to_complete_items(result, prefix) + -- TODO(ashkan): is this the best way to do this? + vim.list_extend(items, matches) + vim.fn.complete(textMatch+1, items) + end) + + -- Return -2 to signal that we should continue completion so that we can + -- async complete. + return -2 +end + +function lsp.client_is_stopped(client_id) + return active_clients[client_id] == nil +end + +--- Gets a map of client_id:client pairs for the given buffer, where each value +--- is a |vim.lsp.client| object. +--- +--@param bufnr (optional, number): Buffer handle, or 0 for current +function lsp.buf_get_clients(bufnr) + bufnr = resolve_bufnr(bufnr) + local result = {} + for_each_buffer_client(bufnr, function(client, client_id) + result[client_id] = client + end) + return result +end + +-- Log level dictionary with reverse lookup as well. +-- +-- Can be used to lookup the number from the name or the +-- name from the number. +-- Levels by name: "trace", "debug", "info", "warn", "error" +-- Level numbers begin with "trace" at 0 +lsp.log_levels = log.levels + +--- Sets the global log level for LSP logging. +--- +--- Levels by name: "trace", "debug", "info", "warn", "error" +--- Level numbers begin with "trace" at 0 +--- +--- Use `lsp.log_levels` for reverse lookup. +--- +--@see |vim.lsp.log_levels| +--- +--@param level [number|string] the case insensitive level name or number +function lsp.set_log_level(level) + if type(level) == 'string' or type(level) == 'number' then + log.set_level(level) + else + error(string.format("Invalid log level: %q", level)) + end +end + +--- Gets the path of the logfile used by the LSP client. +function lsp.get_log_path() + return log.get_filename() +end + +-- Define the LspDiagnostics signs if they're not defined already. +do + local function define_default_sign(name, properties) + if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then + vim.fn.sign_define(name, properties) + end + end + define_default_sign('LspDiagnosticsErrorSign', {text='E', texthl='LspDiagnosticsErrorSign', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsWarningSign', {text='W', texthl='LspDiagnosticsWarningSign', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsInformationSign', {text='I', texthl='LspDiagnosticsInformationSign', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsHintSign', {text='H', texthl='LspDiagnosticsHintSign', linehl='', numhl=''}) +end + +return lsp +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua new file mode 100644 index 0000000000..2e27617997 --- /dev/null +++ b/runtime/lua/vim/lsp/buf.lua @@ -0,0 +1,251 @@ +local vim = vim +local validate = vim.validate +local api = vim.api +local vfn = vim.fn +local util = require 'vim.lsp.util' +local list_extend = vim.list_extend + +local M = {} + +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end + +local function request(method, params, callback) + validate { + method = {method, 's'}; + callback = {callback, 'f', true}; + } + return vim.lsp.buf_request(0, method, params, callback) +end + +--- Sends a notification through all clients associated with current buffer. +-- +--@return `true` if server responds. +function M.server_ready() + return not not vim.lsp.buf_notify(0, "window/progress", {}) +end + +--- Displays hover information about the symbol under the cursor in a floating +--- window. Calling the function twice will jump into the floating window. +function M.hover() + local params = util.make_position_params() + request('textDocument/hover', params) +end + +--- Jumps to the declaration of the symbol under the cursor. +--- +function M.declaration() + local params = util.make_position_params() + request('textDocument/declaration', params) +end + +--- Jumps to the definition of the symbol under the cursor. +--- +function M.definition() + local params = util.make_position_params() + request('textDocument/definition', params) +end + +--- Jumps to the definition of the type of the symbol under the cursor. +--- +function M.type_definition() + local params = util.make_position_params() + request('textDocument/typeDefinition', params) +end + +--- Lists all the implementations for the symbol under the cursor in the +--- quickfix window. +function M.implementation() + local params = util.make_position_params() + request('textDocument/implementation', params) +end + +--- Displays signature information about the symbol under the cursor in a +--- floating window. +function M.signature_help() + local params = util.make_position_params() + request('textDocument/signatureHelp', params) +end + +--- Retrieves the completion items at the current cursor position. Can only be +--- called in Insert mode. +function M.completion(context) + local params = util.make_position_params() + params.context = context + return request('textDocument/completion', params) +end + +--- Formats the current buffer. +--- +--- The optional {options} table can be used to specify FormattingOptions, a +--- list of which is available at +--- https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting. +--- Some unspecified options will be automatically derived from the current +--- Neovim options. +function M.formatting(options) + local params = util.make_formatting_params(options) + return request('textDocument/formatting', params) +end + +--- Perform |vim.lsp.buf.formatting()| synchronously. +--- +--- Useful for running on save, to make sure buffer is formatted prior to being +--- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. +function M.formatting_sync(options, timeout_ms) + local params = util.make_formatting_params(options) + local result = vim.lsp.buf_request_sync(0, "textDocument/formatting", params, timeout_ms) + if not result then return end + result = result[1].result + vim.lsp.util.apply_text_edits(result) +end + +function M.range_formatting(options, start_pos, end_pos) + validate { + options = {options, 't', true}; + start_pos = {start_pos, 't', true}; + end_pos = {end_pos, 't', true}; + } + local sts = vim.bo.softtabstop; + options = vim.tbl_extend('keep', options or {}, { + tabSize = (sts > 0 and sts) or (sts < 0 and vim.bo.shiftwidth) or vim.bo.tabstop; + insertSpaces = vim.bo.expandtab; + }) + local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) + local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>')) + -- convert to 0-index + A[1] = A[1] - 1 + B[1] = B[1] - 1 + -- account for encoding. + if A[2] > 0 then + A = {A[1], util.character_offset(0, A[1], A[2])} + end + if B[2] > 0 then + B = {B[1], util.character_offset(0, B[1], B[2])} + end + local params = { + textDocument = { uri = vim.uri_from_bufnr(0) }; + range = { + start = { line = A[1]; character = A[2]; }; + ["end"] = { line = B[1]; character = B[2]; }; + }; + options = options; + } + return request('textDocument/rangeFormatting', params) +end + +--- Renames all references to the symbol under the cursor. If {new_name} is not +--- provided, the user will be prompted for a new name using |input()|. +function M.rename(new_name) + -- TODO(ashkan) use prepareRename + -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. + local params = util.make_position_params() + new_name = new_name or npcall(vfn.input, "New Name: ", vfn.expand('<cword>')) + if not (new_name and #new_name > 0) then return end + params.newName = new_name + request('textDocument/rename', params) +end + +--- Lists all the references to the symbol under the cursor in the quickfix window. +--- +function M.references(context) + validate { context = { context, 't', true } } + local params = util.make_position_params() + params.context = context or { + includeDeclaration = true; + } + params[vim.type_idx] = vim.types.dictionary + request('textDocument/references', params) +end + +--- Lists all symbols in the current buffer in the quickfix window. +--- +function M.document_symbol() + local params = { textDocument = util.make_text_document_params() } + request('textDocument/documentSymbol', params) +end + +local function pick_call_hierarchy_item(call_hierarchy_items) + if not call_hierarchy_items then return end + if #call_hierarchy_items == 1 then + return call_hierarchy_items[1] + end + local items = {} + for i, item in ipairs(call_hierarchy_items) do + local entry = item.detail or item.name + table.insert(items, string.format("%d. %s", i, entry)) + end + local choice = vim.fn.inputlist(items) + if choice < 1 or choice > #items then + return + end + return choice +end + +function M.incoming_calls() + local params = util.make_position_params() + request('textDocument/prepareCallHierarchy', params, function(_, _, result) + local call_hierarchy_item = pick_call_hierarchy_item(result) + vim.lsp.buf_request(0, 'callHierarchy/incomingCalls', { item = call_hierarchy_item }) + end) +end + +function M.outgoing_calls() + local params = util.make_position_params() + request('textDocument/prepareCallHierarchy', params, function(_, _, result) + local call_hierarchy_item = pick_call_hierarchy_item(result) + vim.lsp.buf_request(0, 'callHierarchy/outgoingCalls', { item = call_hierarchy_item }) + end) +end + +--- Lists all symbols in the current workspace in the quickfix window. +--- +--- The list is filtered against the optional argument {query}; +--- if the argument is omitted from the call, the user is prompted to enter a string on the command line. +--- An empty string means no filtering is done. +function M.workspace_symbol(query) + query = query or npcall(vfn.input, "Query: ") + local params = {query = query} + request('workspace/symbol', params) +end + +--- Send request to server to resolve document highlights for the +--- current text document position. This request can be associated +--- to key mapping or to events such as `CursorHold`, eg: +--- +--- <pre> +--- vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] +--- vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] +--- vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] +--- </pre> +function M.document_highlight() + local params = util.make_position_params() + request('textDocument/documentHighlight', params) +end + +function M.clear_references() + util.buf_clear_references() +end + +function M.code_action(context) + validate { context = { context, 't', true } } + context = context or { diagnostics = util.get_line_diagnostics() } + local params = util.make_range_params() + params.context = context + request('textDocument/codeAction', params) +end + +function M.execute_command(command) + validate { + command = { command.command, 's' }, + arguments = { command.arguments, 't', true } + } + request('workspace/executeCommand', command) +end + +return M +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua new file mode 100644 index 0000000000..1ed58995d0 --- /dev/null +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -0,0 +1,293 @@ +local log = require 'vim.lsp.log' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' +local vim = vim +local api = vim.api +local buf = require 'vim.lsp.buf' + +local M = {} + +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + +M['workspace/executeCommand'] = function(err, _) + if err then + error("Could not execute code action: "..err.message) + end +end + +M['textDocument/codeAction'] = function(_, _, actions) + if actions == nil or vim.tbl_isempty(actions) then + print("No code actions available") + return + end + + local option_strings = {"Code Actions:"} + for i, action in ipairs(actions) do + local title = action.title:gsub('\r\n', '\\r\\n') + title = title:gsub('\n', '\\n') + table.insert(option_strings, string.format("%d. %s", i, title)) + end + + local choice = vim.fn.inputlist(option_strings) + if choice < 1 or choice > #actions then + return + end + local action_chosen = actions[choice] + -- textDocument/codeAction can return either Command[] or CodeAction[]. + -- If it is a CodeAction, it can have either an edit, a command or both. + -- Edits should be executed first + if action_chosen.edit or type(action_chosen.command) == "table" then + if action_chosen.edit then + util.apply_workspace_edit(action_chosen.edit) + end + if type(action_chosen.command) == "table" then + buf.execute_command(action_chosen.command) + end + else + buf.execute_command(action_chosen) + end +end + +M['workspace/applyEdit'] = function(_, _, workspace_edit) + if not workspace_edit then return end + -- TODO(ashkan) Do something more with label? + if workspace_edit.label then + print("Workspace edit", workspace_edit.label) + end + local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit) + return { + applied = status; + failureReason = result; + } +end + +M['textDocument/publishDiagnostics'] = function(_, _, result) + if not result then return end + local uri = result.uri + local bufnr = vim.uri_to_bufnr(uri) + if not bufnr then + err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) + return + end + + -- Unloaded buffers should not handle diagnostics. + -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. + -- This should trigger another publish of the diagnostics. + -- + -- In particular, this stops a ton of spam when first starting a server for current + -- unloaded buffers. + if not api.nvim_buf_is_loaded(bufnr) then + return + end + + util.buf_clear_diagnostics(bufnr) + + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic + -- The diagnostic's severity. Can be omitted. If omitted it is up to the + -- client to interpret diagnostics as error, warning, info or hint. + -- TODO: Replace this with server-specific heuristics to infer severity. + for _, diagnostic in ipairs(result.diagnostics) do + if diagnostic.severity == nil then + diagnostic.severity = protocol.DiagnosticSeverity.Error + end + end + + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + util.buf_diagnostics_underline(bufnr, result.diagnostics) + util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + util.buf_diagnostics_signs(bufnr, result.diagnostics) + vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") +end + +M['textDocument/references'] = function(_, _, result) + if not result then return end + util.set_qflist(util.locations_to_items(result)) + api.nvim_command("copen") + api.nvim_command("wincmd p") +end + +local symbol_callback = function(_, _, result, _, bufnr) + if not result or vim.tbl_isempty(result) then return end + + util.set_qflist(util.symbols_to_items(result, bufnr)) + api.nvim_command("copen") + api.nvim_command("wincmd p") +end +M['textDocument/documentSymbol'] = symbol_callback +M['workspace/symbol'] = symbol_callback + +M['textDocument/rename'] = function(_, _, result) + if not result then return end + util.apply_workspace_edit(result) +end + +M['textDocument/rangeFormatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/formatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/completion'] = function(_, _, result) + if vim.tbl_isempty(result or {}) then return end + local row, col = unpack(api.nvim_win_get_cursor(0)) + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) + local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local prefix = line_to_cursor:sub(textMatch+1) + + local matches = util.text_document_completion_list_to_complete_items(result, prefix) + vim.fn.complete(textMatch+1, matches) +end + +M['textDocument/hover'] = function(_, method, result) + util.focusable_float(method, function() + if not (result and result.contents) then + -- return { 'No information available' } + return + end + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + -- return { 'No information available' } + return + end + local bufnr, winnr = util.fancy_floating_markdown(markdown_lines, { + pad_left = 1; pad_right = 1; + }) + util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, winnr) + return bufnr, winnr + end) +end + +local function location_callback(_, method, result) + if result == nil or vim.tbl_isempty(result) then + local _ = log.info() and log.info(method, 'No location found') + return nil + end + + -- textDocument/definition can return Location or Location[] + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition + + if vim.tbl_islist(result) then + util.jump_to_location(result[1]) + + if #result > 1 then + util.set_qflist(util.locations_to_items(result)) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end + else + util.jump_to_location(result) + end +end + +M['textDocument/declaration'] = location_callback +M['textDocument/definition'] = location_callback +M['textDocument/typeDefinition'] = location_callback +M['textDocument/implementation'] = location_callback + +M['textDocument/signatureHelp'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.signatures and result.signatures[1]) then + return { 'No signature available' } + end + -- TODO show popup when signatures is empty? + local lines = util.convert_signature_help_to_markdown_lines(result) + lines = util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then + return { 'No signature available' } + end + return lines, util.try_trim_markdown_code_blocks(lines) + end) +end + +M['textDocument/documentHighlight'] = function(_, _, result, _) + if not result then return end + local bufnr = api.nvim_get_current_buf() + util.buf_highlight_references(bufnr, result) +end + +-- direction is "from" for incoming calls and "to" for outgoing calls +local make_call_hierarchy_callback = function(direction) + -- result is a CallHierarchy{Incoming,Outgoing}Call[] + return function(_, _, result) + if not result then return end + local items = {} + for _, call_hierarchy_call in pairs(result) do + local call_hierarchy_item = call_hierarchy_call[direction] + for _, range in pairs(call_hierarchy_call.fromRanges) do + table.insert(items, { + filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)), + text = call_hierarchy_item.name, + lnum = range.start.line + 1, + col = range.start.character + 1, + }) + end + end + util.set_qflist(items) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end +end + +M['callHierarchy/incomingCalls'] = make_call_hierarchy_callback('from') + +M['callHierarchy/outgoingCalls'] = make_call_hierarchy_callback('to') + +M['window/logMessage'] = function(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + log.error(message) + elseif message_type == protocol.MessageType.Warning then + log.warn(message) + elseif message_type == protocol.MessageType.Info then + log.info(message) + else + log.debug(message) + end + return result +end + +M['window/showMessage'] = function(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + err_message("LSP[", client_name, "] ", message) + else + local message_type_name = protocol.MessageType[message_type] + api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) + end + return result +end + +-- Add boilerplate error validation and logging for all of these. +for k, fn in pairs(M) do + M[k] = function(err, method, params, client_id, bufnr) + log.debug('default_callback', method, { params = params, client_id = client_id, err = err, bufnr = bufnr }) + if err then + error(tostring(err)) + end + return fn(err, method, params, client_id, bufnr) + end +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 new file mode 100644 index 0000000000..696ce43a59 --- /dev/null +++ b/runtime/lua/vim/lsp/log.lua @@ -0,0 +1,94 @@ +-- Logger for language client plugin. + +local log = {} + +-- Log level dictionary with reverse lookup as well. +-- +-- Can be used to lookup the number from the name or the name from the number. +-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error' +-- Level numbers begin with 'trace' at 0 +log.levels = { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARN = 3; + ERROR = 4; +} + +-- Default log level is warn. +local current_log_level = log.levels.WARN +local log_date_format = "%FT%H:%M:%S%z" + +do + local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" + local function path_join(...) + return table.concat(vim.tbl_flatten{...}, path_sep) + end + local logfilename = path_join(vim.fn.stdpath('data'), 'lsp.log') + + --- Return the log filename. + function log.get_filename() + return logfilename + end + + vim.fn.mkdir(vim.fn.stdpath('data'), "p") + local logfile = assert(io.open(logfilename, "a+")) + for level, levelnr in pairs(log.levels) do + -- Also export the log level on the root object. + log[level] = levelnr + -- Set the lowercase name as the main use function. + -- If called without arguments, it will check whether the log level is + -- greater than or equal to this one. When called with arguments, it will + -- log at that level (if applicable, it is checked either way). + -- + -- Recommended usage: + -- ``` + -- local _ = log.warn() and log.warn("123") + -- ``` + -- + -- This way you can avoid string allocations if the log level isn't high enough. + log[level:lower()] = function(...) + local argc = select("#", ...) + if levelnr < current_log_level then return false end + if argc == 0 then return true end + local info = debug.getinfo(2, "Sl") + local fileinfo = string.format("%s:%s", info.short_src, info.currentline) + local parts = { table.concat({"[", level, "]", os.date(log_date_format), "]", fileinfo, "]"}, " ") } + for i = 1, argc do + local arg = select(i, ...) + if arg == nil then + table.insert(parts, "nil") + else + table.insert(parts, vim.inspect(arg, {newline=''})) + end + end + logfile:write(table.concat(parts, '\t'), "\n") + logfile:flush() + end + end + -- Add some space to make it easier to distinguish different neovim runs. + logfile:write("\n") +end + +-- This is put here on purpose after the loop above so that it doesn't +-- interfere with iterating the levels +vim.tbl_add_reverse_lookup(log.levels) + +function log.set_level(level) + if type(level) == 'string' then + current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) + else + assert(type(level) == 'number', "level must be a number or string") + assert(log.levels[level], string.format("Invalid log level: %d", level)) + current_log_level = level + end +end + +-- Return whether the level is sufficient for logging. +-- @param level number log level +function log.should_log(level) + return level >= current_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 new file mode 100644 index 0000000000..ef5e08680e --- /dev/null +++ b/runtime/lua/vim/lsp/protocol.lua @@ -0,0 +1,988 @@ +-- Protocol for the Microsoft Language Server Protocol (mslsp) + +local protocol = {} + +local function ifnil(a, b) + if a == nil then return b end + return a +end + + +--[=[ +-- Useful for interfacing with: +-- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md +function transform_schema_comments() + nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] + nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]] +end +function transform_schema_to_table() + transform_schema_comments() + nvim.command [[silent! '<,'>s/: \S\+//]] + nvim.command [[silent! '<,'>s/export const //]] + nvim.command [[silent! '<,'>s/export namespace \(\S*\)\s*{/protocol.\1 = {/]] + nvim.command [[silent! '<,'>s/namespace \(\S*\)\s*{/protocol.\1 = {/]] +end +--]=] + +local constants = { + DiagnosticSeverity = { + -- Reports an error. + Error = 1; + -- Reports a warning. + Warning = 2; + -- Reports an information. + Information = 3; + -- Reports a hint. + Hint = 4; + }; + + MessageType = { + -- An error message. + Error = 1; + -- A warning message. + Warning = 2; + -- An information message. + Info = 3; + -- A log message. + Log = 4; + }; + + -- The file event type. + FileChangeType = { + -- The file got created. + Created = 1; + -- The file got changed. + Changed = 2; + -- The file got deleted. + Deleted = 3; + }; + + -- The kind of a completion entry. + CompletionItemKind = { + Text = 1; + Method = 2; + Function = 3; + Constructor = 4; + Field = 5; + Variable = 6; + Class = 7; + Interface = 8; + Module = 9; + Property = 10; + Unit = 11; + Value = 12; + Enum = 13; + Keyword = 14; + Snippet = 15; + Color = 16; + File = 17; + Reference = 18; + Folder = 19; + EnumMember = 20; + Constant = 21; + Struct = 22; + Event = 23; + Operator = 24; + TypeParameter = 25; + }; + + -- How a completion was triggered + CompletionTriggerKind = { + -- Completion was triggered by typing an identifier (24x7 code + -- complete), manual invocation (e.g Ctrl+Space) or via API. + Invoked = 1; + -- Completion was triggered by a trigger character specified by + -- the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + TriggerCharacter = 2; + -- Completion was re-triggered as the current completion list is incomplete. + TriggerForIncompleteCompletions = 3; + }; + + -- A document highlight kind. + DocumentHighlightKind = { + -- A textual occurrence. + Text = 1; + -- Read-access of a symbol, like reading a variable. + Read = 2; + -- Write-access of a symbol, like writing to a variable. + Write = 3; + }; + + -- A symbol kind. + SymbolKind = { + File = 1; + Module = 2; + Namespace = 3; + Package = 4; + Class = 5; + Method = 6; + Property = 7; + Field = 8; + Constructor = 9; + Enum = 10; + Interface = 11; + Function = 12; + Variable = 13; + Constant = 14; + String = 15; + Number = 16; + Boolean = 17; + Array = 18; + Object = 19; + Key = 20; + Null = 21; + EnumMember = 22; + Struct = 23; + Event = 24; + Operator = 25; + TypeParameter = 26; + }; + + -- Represents reasons why a text document is saved. + TextDocumentSaveReason = { + -- Manually triggered, e.g. by the user pressing save, by starting debugging, + -- or by an API call. + Manual = 1; + -- Automatic after a delay. + AfterDelay = 2; + -- When the editor lost focus. + FocusOut = 3; + }; + + ErrorCodes = { + -- Defined by JSON RPC + ParseError = -32700; + InvalidRequest = -32600; + MethodNotFound = -32601; + InvalidParams = -32602; + InternalError = -32603; + serverErrorStart = -32099; + serverErrorEnd = -32000; + ServerNotInitialized = -32002; + UnknownErrorCode = -32001; + -- Defined by the protocol. + RequestCancelled = -32800; + ContentModified = -32801; + }; + + -- Describes the content type that a client supports in various + -- result literals like `Hover`, `ParameterInfo` or `CompletionItem`. + -- + -- Please note that `MarkupKinds` must not start with a `$`. This kinds + -- are reserved for internal usage. + MarkupKind = { + -- Plain text is supported as a content format + PlainText = 'plaintext'; + -- Markdown is supported as a content format + Markdown = 'markdown'; + }; + + ResourceOperationKind = { + -- Supports creating new files and folders. + Create = 'create'; + -- Supports renaming existing files and folders. + Rename = 'rename'; + -- Supports deleting existing files and folders. + Delete = 'delete'; + }; + + FailureHandlingKind = { + -- Applying the workspace change is simply aborted if one of the changes provided + -- fails. All operations executed before the failing operation stay executed. + Abort = 'abort'; + -- All operations are executed transactionally. That means they either all + -- succeed or no changes at all are applied to the workspace. + Transactional = 'transactional'; + -- If the workspace edit contains only textual file changes they are executed transactionally. + -- If resource changes (create, rename or delete file) are part of the change the failure + -- handling strategy is abort. + TextOnlyTransactional = 'textOnlyTransactional'; + -- The client tries to undo the operations already executed. But there is no + -- guarantee that this succeeds. + Undo = 'undo'; + }; + + -- Known error codes for an `InitializeError`; + InitializeError = { + -- If the protocol version provided by the client can't be handled by the server. + -- @deprecated This initialize error got replaced by client capabilities. There is + -- no version handshake in version 3.0x + unknownProtocolVersion = 1; + }; + + -- Defines how the host (editor) should sync document changes to the language server. + TextDocumentSyncKind = { + -- Documents should not be synced at all. + None = 0; + -- Documents are synced by always sending the full content + -- of the document. + Full = 1; + -- Documents are synced by sending the full content on open. + -- After that only incremental updates to the document are + -- send. + Incremental = 2; + }; + + WatchKind = { + -- Interested in create events. + Create = 1; + -- Interested in change events + Change = 2; + -- Interested in delete events + Delete = 4; + }; + + -- Defines whether the insert text in a completion item should be interpreted as + -- plain text or a snippet. + InsertTextFormat = { + -- The primary text to be inserted is treated as a plain string. + PlainText = 1; + -- The primary text to be inserted is treated as a snippet. + -- + -- A snippet can define tab stops and placeholders with `$1`, `$2` + -- and `${3:foo};`. `$0` defines the final tab stop, it defaults to + -- the end of the snippet. Placeholders with equal identifiers are linked, + -- that is typing in one will update others too. + Snippet = 2; + }; + + -- A set of predefined code action kinds + CodeActionKind = { + -- Empty kind. + Empty = ''; + -- Base kind for quickfix actions + QuickFix = 'quickfix'; + -- Base kind for refactoring actions + Refactor = 'refactor'; + -- Base kind for refactoring extraction actions + -- + -- Example extract actions: + -- + -- - Extract method + -- - Extract function + -- - Extract variable + -- - Extract interface from class + -- - ... + RefactorExtract = 'refactor.extract'; + -- Base kind for refactoring inline actions + -- + -- Example inline actions: + -- + -- - Inline function + -- - Inline variable + -- - Inline constant + -- - ... + RefactorInline = 'refactor.inline'; + -- Base kind for refactoring rewrite actions + -- + -- Example rewrite actions: + -- + -- - Convert JavaScript function to class + -- - Add or remove parameter + -- - Encapsulate field + -- - Make method static + -- - Move method to base class + -- - ... + RefactorRewrite = 'refactor.rewrite'; + -- Base kind for source actions + -- + -- Source code actions apply to the entire file. + Source = 'source'; + -- Base kind for an organize imports source action + SourceOrganizeImports = 'source.organizeImports'; + }; +} + +for k, v in pairs(constants) do + vim.tbl_add_reverse_lookup(v) + protocol[k] = v +end + +--[=[ +--Text document specific client capabilities. +export interface TextDocumentClientCapabilities { + synchronization?: { + --Whether text document synchronization supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports sending will save notifications. + willSave?: boolean; + --The client supports sending a will save request and + --waits for a response providing text edits which will + --be applied to the document before it is saved. + willSaveWaitUntil?: boolean; + --The client supports did save notifications. + didSave?: boolean; + } + --Capabilities specific to the `textDocument/completion` + completion?: { + --Whether completion supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports the following `CompletionItem` specific + --capabilities. + completionItem?: { + --The client supports snippets as insert text. + -- + --A snippet can define tab stops and placeholders with `$1`, `$2` + --and `${3:foo}`. `$0` defines the final tab stop, it defaults to + --the end of the snippet. Placeholders with equal identifiers are linked, + --that is typing in one will update others too. + snippetSupport?: boolean; + --The client supports commit characters on a completion item. + commitCharactersSupport?: boolean + --The client supports the following content formats for the documentation + --property. The order describes the preferred format of the client. + documentationFormat?: MarkupKind[]; + --The client supports the deprecated property on a completion item. + deprecatedSupport?: boolean; + --The client supports the preselect property on a completion item. + preselectSupport?: boolean; + } + completionItemKind?: { + --The completion item kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + -- + --If this property is not present the client only supports + --the completion items kinds from `Text` to `Reference` as defined in + --the initial version of the protocol. + valueSet?: CompletionItemKind[]; + }, + --The client supports to send additional context information for a + --`textDocument/completion` request. + contextSupport?: boolean; + }; + --Capabilities specific to the `textDocument/hover` + hover?: { + --Whether hover supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports the follow content formats for the content + --property. The order describes the preferred format of the client. + contentFormat?: MarkupKind[]; + }; + --Capabilities specific to the `textDocument/signatureHelp` + signatureHelp?: { + --Whether signature help supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports the following `SignatureInformation` + --specific properties. + signatureInformation?: { + --The client supports the follow content formats for the documentation + --property. The order describes the preferred format of the client. + documentationFormat?: MarkupKind[]; + --Client capabilities specific to parameter information. + parameterInformation?: { + --The client supports processing label offsets instead of a + --simple label string. + -- + --Since 3.14.0 + labelOffsetSupport?: boolean; + } + }; + }; + --Capabilities specific to the `textDocument/references` + references?: { + --Whether references supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentHighlight` + documentHighlight?: { + --Whether document highlight supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentSymbol` + documentSymbol?: { + --Whether document symbol supports dynamic registration. + dynamicRegistration?: boolean; + --Specific capabilities for the `SymbolKind`. + symbolKind?: { + --The symbol kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + -- + --If this property is not present the client only supports + --the symbol kinds from `File` to `Array` as defined in + --the initial version of the protocol. + valueSet?: SymbolKind[]; + } + --The client supports hierarchical document symbols. + hierarchicalDocumentSymbolSupport?: boolean; + }; + --Capabilities specific to the `textDocument/formatting` + formatting?: { + --Whether formatting supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/rangeFormatting` + rangeFormatting?: { + --Whether range formatting supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/onTypeFormatting` + onTypeFormatting?: { + --Whether on type formatting supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/declaration` + declaration?: { + --Whether declaration supports dynamic registration. If this is set to `true` + --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of declaration links. + -- + --Since 3.14.0 + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/definition`. + -- + --Since 3.14.0 + definition?: { + --Whether definition supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of definition links. + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/typeDefinition` + -- + --Since 3.6.0 + typeDefinition?: { + --Whether typeDefinition supports dynamic registration. If this is set to `true` + --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of definition links. + -- + --Since 3.14.0 + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/implementation`. + -- + --Since 3.6.0 + implementation?: { + --Whether implementation supports dynamic registration. If this is set to `true` + --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of definition links. + -- + --Since 3.14.0 + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/codeAction` + codeAction?: { + --Whether code action supports dynamic registration. + dynamicRegistration?: boolean; + --The client support code action literals as a valid + --response of the `textDocument/codeAction` request. + -- + --Since 3.8.0 + codeActionLiteralSupport?: { + --The code action kind is support with the following value + --set. + codeActionKind: { + --The code action kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + valueSet: CodeActionKind[]; + }; + }; + }; + --Capabilities specific to the `textDocument/codeLens` + codeLens?: { + --Whether code lens supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentLink` + documentLink?: { + --Whether document link supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentColor` and the + --`textDocument/colorPresentation` request. + -- + --Since 3.6.0 + colorProvider?: { + --Whether colorProvider supports dynamic registration. If this is set to `true` + --the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + } + --Capabilities specific to the `textDocument/rename` + rename?: { + --Whether rename supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports testing for validity of rename operations + --before execution. + prepareSupport?: boolean; + }; + --Capabilities specific to `textDocument/publishDiagnostics`. + publishDiagnostics?: { + --Whether the clients accepts diagnostics with related information. + relatedInformation?: boolean; + }; + --Capabilities specific to `textDocument/foldingRange` requests. + -- + --Since 3.10.0 + foldingRange?: { + --Whether implementation supports dynamic registration for folding range providers. If this is set to `true` + --the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The maximum number of folding ranges that the client prefers to receive per document. The value serves as a + --hint, servers are free to follow the limit. + rangeLimit?: number; + --If set, the client signals that it only supports folding complete lines. If set, client will + --ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. + lineFoldingOnly?: boolean; + }; +} +--]=] + +--[=[ +--Workspace specific client capabilities. +export interface WorkspaceClientCapabilities { + --The client supports applying batch edits to the workspace by supporting + --the request 'workspace/applyEdit' + applyEdit?: boolean; + --Capabilities specific to `WorkspaceEdit`s + workspaceEdit?: { + --The client supports versioned document changes in `WorkspaceEdit`s + documentChanges?: boolean; + --The resource operations the client supports. Clients should at least + --support 'create', 'rename' and 'delete' files and folders. + resourceOperations?: ResourceOperationKind[]; + --The failure handling strategy of a client if applying the workspace edit + --fails. + failureHandling?: FailureHandlingKind; + }; + --Capabilities specific to the `workspace/didChangeConfiguration` notification. + didChangeConfiguration?: { + --Did change configuration notification supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + didChangeWatchedFiles?: { + --Did change watched files notification supports dynamic registration. Please note + --that the current protocol doesn't support static configuration for file changes + --from the server side. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `workspace/symbol` request. + symbol?: { + --Symbol request supports dynamic registration. + dynamicRegistration?: boolean; + --Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. + symbolKind?: { + --The symbol kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + -- + --If this property is not present the client only supports + --the symbol kinds from `File` to `Array` as defined in + --the initial version of the protocol. + valueSet?: SymbolKind[]; + } + }; + --Capabilities specific to the `workspace/executeCommand` request. + executeCommand?: { + --Execute command supports dynamic registration. + dynamicRegistration?: boolean; + }; + --The client has support for workspace folders. + -- + --Since 3.6.0 + workspaceFolders?: boolean; + --The client supports `workspace/configuration` requests. + -- + --Since 3.6.0 + configuration?: boolean; +} +--]=] + +--- Gets a new ClientCapabilities object describing the LSP client +--- capabilities. +function protocol.make_client_capabilities() + return { + textDocument = { + synchronization = { + dynamicRegistration = false; + + -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre) + willSave = false; + + -- TODO(ashkan) Implement textDocument/willSaveWaitUntil + willSaveWaitUntil = false; + + -- Send textDocument/didSave after saving (BufWritePost) + didSave = true; + }; + codeAction = { + dynamicRegistration = false; + + codeActionLiteralSupport = { + codeActionKind = { + valueSet = {}; + }; + }; + }; + completion = { + dynamicRegistration = false; + completionItem = { + + snippetSupport = true; + commitCharactersSupport = false; + preselectSupport = false; + deprecatedSupport = false; + documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + }; + completionItemKind = { + valueSet = (function() + local res = {} + for k in pairs(protocol.CompletionItemKind) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + + -- TODO(tjdevries): Implement this + contextSupport = false; + }; + declaration = { + linkSupport = true; + }; + definition = { + linkSupport = true; + }; + implementation = { + linkSupport = true; + }; + typeDefinition = { + linkSupport = true; + }; + hover = { + dynamicRegistration = false; + contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + }; + signatureHelp = { + dynamicRegistration = false; + signatureInformation = { + documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + -- parameterInformation = { + -- labelOffsetSupport = false; + -- }; + }; + }; + references = { + dynamicRegistration = false; + }; + documentHighlight = { + dynamicRegistration = false + }; + documentSymbol = { + dynamicRegistration = false; + symbolKind = { + valueSet = (function() + local res = {} + for k in pairs(protocol.SymbolKind) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + hierarchicalDocumentSymbolSupport = true; + }; + }; + workspace = { + symbol = { + dynamicRegistration = false; + symbolKind = { + valueSet = (function() + local res = {} + for k in pairs(protocol.SymbolKind) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + hierarchicalWorkspaceSymbolSupport = true; + }; + applyEdit = true; + }; + callHierarchy = { + dynamicRegistration = false; + }; + experimental = nil; + } +end + +--[=[ +export interface DocumentFilter { + --A language id, like `typescript`. + language?: string; + --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + scheme?: string; + --A glob pattern, like `*.{ts,js}`. + -- + --Glob patterns can have the following syntax: + --- `*` to match one or more characters in a path segment + --- `?` to match on one character in a path segment + --- `**` to match any number of path segments, including none + --- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) + --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + pattern?: string; +} +--]=] + +--[[ +--Static registration options to be returned in the initialize request. +interface StaticRegistrationOptions { + --The id used to register the request. The id can be used to deregister + --the request again. See also Registration#id. + id?: string; +} + +export interface DocumentFilter { + --A language id, like `typescript`. + language?: string; + --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + scheme?: string; + --A glob pattern, like `*.{ts,js}`. + -- + --Glob patterns can have the following syntax: + --- `*` to match one or more characters in a path segment + --- `?` to match on one character in a path segment + --- `**` to match any number of path segments, including none + --- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) + --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + pattern?: string; +} +export type DocumentSelector = DocumentFilter[]; +export interface TextDocumentRegistrationOptions { + --A document selector to identify the scope of the registration. If set to null + --the document selector provided on the client side will be used. + documentSelector: DocumentSelector | null; +} + +--Code Action options. +export interface CodeActionOptions { + --CodeActionKinds that this server may return. + -- + --The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server + --may list out every specific kind they provide. + codeActionKinds?: CodeActionKind[]; +} + +interface ServerCapabilities { + --Defines how text documents are synced. Is either a detailed structure defining each notification or + --for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`. + textDocumentSync?: TextDocumentSyncOptions | number; + --The server provides hover support. + hoverProvider?: boolean; + --The server provides completion support. + completionProvider?: CompletionOptions; + --The server provides signature help support. + signatureHelpProvider?: SignatureHelpOptions; + --The server provides goto definition support. + definitionProvider?: boolean; + --The server provides Goto Type Definition support. + -- + --Since 3.6.0 + typeDefinitionProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides Goto Implementation support. + -- + --Since 3.6.0 + implementationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides find references support. + referencesProvider?: boolean; + --The server provides document highlight support. + documentHighlightProvider?: boolean; + --The server provides document symbol support. + documentSymbolProvider?: boolean; + --The server provides workspace symbol support. + workspaceSymbolProvider?: boolean; + --The server provides code actions. The `CodeActionOptions` return type is only + --valid if the client signals code action literal support via the property + --`textDocument.codeAction.codeActionLiteralSupport`. + codeActionProvider?: boolean | CodeActionOptions; + --The server provides code lens. + codeLensProvider?: CodeLensOptions; + --The server provides document formatting. + documentFormattingProvider?: boolean; + --The server provides document range formatting. + documentRangeFormattingProvider?: boolean; + --The server provides document formatting on typing. + documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions; + --The server provides rename support. RenameOptions may only be + --specified if the client states that it supports + --`prepareSupport` in its initial `initialize` request. + renameProvider?: boolean | RenameOptions; + --The server provides document link support. + documentLinkProvider?: DocumentLinkOptions; + --The server provides color provider support. + -- + --Since 3.6.0 + colorProvider?: boolean | ColorProviderOptions | (ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides folding provider support. + -- + --Since 3.10.0 + foldingRangeProvider?: boolean | FoldingRangeProviderOptions | (FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides go to declaration support. + -- + --Since 3.14.0 + declarationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides execute command support. + executeCommandProvider?: ExecuteCommandOptions; + --Workspace specific server capabilities + workspace?: { + --The server supports workspace folder. + -- + --Since 3.6.0 + workspaceFolders?: { + * The server has support for workspace folders + supported?: boolean; + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a strings is provided the string is treated as a ID + * under which the notification is registered on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + changeNotifications?: string | boolean; + } + } + --Experimental server capabilities. + experimental?: any; +} +--]] + +--- Creates a normalized object describing LSP server capabilities. +function protocol.resolve_capabilities(server_capabilities) + local general_properties = {} + local text_document_sync_properties + do + local TextDocumentSyncKind = protocol.TextDocumentSyncKind + local textDocumentSync = server_capabilities.textDocumentSync + if textDocumentSync == nil then + -- Defaults if omitted. + text_document_sync_properties = { + text_document_open_close = false; + text_document_did_change = TextDocumentSyncKind.None; +-- text_document_did_change = false; + text_document_will_save = false; + text_document_will_save_wait_until = false; + text_document_save = false; + text_document_save_include_text = false; + } + elseif type(textDocumentSync) == 'number' then + -- Backwards compatibility + if not TextDocumentSyncKind[textDocumentSync] then + return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" + end + text_document_sync_properties = { + text_document_open_close = true; + text_document_did_change = textDocumentSync; + text_document_will_save = false; + text_document_will_save_wait_until = false; + text_document_save = false; + text_document_save_include_text = false; + } + elseif type(textDocumentSync) == 'table' then + text_document_sync_properties = { + text_document_open_close = ifnil(textDocumentSync.openClose, false); + text_document_did_change = ifnil(textDocumentSync.change, TextDocumentSyncKind.None); + text_document_will_save = ifnil(textDocumentSync.willSave, false); + text_document_will_save_wait_until = ifnil(textDocumentSync.willSaveWaitUntil, false); + text_document_save = ifnil(textDocumentSync.save, false); + text_document_save_include_text = ifnil(type(textDocumentSync.save) == 'table' + and textDocumentSync.save.includeText, false); + } + else + return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync)) + end + end + general_properties.hover = server_capabilities.hoverProvider or false + general_properties.goto_definition = server_capabilities.definitionProvider or false + general_properties.find_references = server_capabilities.referencesProvider or false + general_properties.document_highlight = server_capabilities.documentHighlightProvider or false + general_properties.document_symbol = server_capabilities.documentSymbolProvider or false + general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false + general_properties.document_formatting = server_capabilities.documentFormattingProvider or false + general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false + general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false + + if server_capabilities.codeActionProvider == nil then + general_properties.code_action = false + elseif type(server_capabilities.codeActionProvider) == 'boolean' then + general_properties.code_action = server_capabilities.codeActionProvider + elseif type(server_capabilities.codeActionProvider) == 'table' then + -- TODO(ashkan) support CodeActionKind + general_properties.code_action = false + else + error("The server sent invalid codeActionProvider") + end + + if server_capabilities.declarationProvider == nil then + general_properties.declaration = false + elseif type(server_capabilities.declarationProvider) == 'boolean' then + general_properties.declaration = server_capabilities.declarationProvider + elseif type(server_capabilities.declarationProvider) == 'table' then + -- TODO: support more detailed declarationProvider options. + general_properties.declaration = false + else + error("The server sent invalid declarationProvider") + end + + if server_capabilities.typeDefinitionProvider == nil then + general_properties.type_definition = false + elseif type(server_capabilities.typeDefinitionProvider) == 'boolean' then + general_properties.type_definition = server_capabilities.typeDefinitionProvider + elseif type(server_capabilities.typeDefinitionProvider) == 'table' then + -- TODO: support more detailed typeDefinitionProvider options. + general_properties.type_definition = false + else + error("The server sent invalid typeDefinitionProvider") + end + + if server_capabilities.implementationProvider == nil then + general_properties.implementation = false + elseif type(server_capabilities.implementationProvider) == 'boolean' then + general_properties.implementation = server_capabilities.implementationProvider + elseif type(server_capabilities.implementationProvider) == 'table' then + -- TODO(ashkan) support more detailed implementation options. + general_properties.implementation = false + else + error("The server sent invalid implementationProvider") + end + + local signature_help_properties + if server_capabilities.signatureHelpProvider == nil then + signature_help_properties = { + signature_help = false; + signature_help_trigger_characters = {}; + } + elseif type(server_capabilities.signatureHelpProvider) == 'table' then + signature_help_properties = { + signature_help = true; + -- The characters that trigger signature help automatically. + signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {}; + } + else + error("The server sent invalid signatureHelpProvider") + end + + return vim.tbl_extend("error" + , text_document_sync_properties + , signature_help_properties + , general_properties + ) +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 new file mode 100644 index 0000000000..81c92bfe05 --- /dev/null +++ b/runtime/lua/vim/lsp/rpc.lua @@ -0,0 +1,472 @@ +local vim = vim +local uv = vim.loop +local log = require('vim.lsp.log') +local protocol = require('vim.lsp.protocol') +local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap + +-- TODO replace with a better implementation. +local function json_encode(data) + local status, result = pcall(vim.fn.json_encode, data) + if status then + return result + else + return nil, result + end +end +local function json_decode(data) + local status, result = pcall(vim.fn.json_decode, data) + if status then + return result + else + return nil, result + end +end + +local function is_dir(filename) + local stat = vim.loop.fs_stat(filename) + return stat and stat.type == 'directory' or false +end + +local NIL = vim.NIL +local function convert_NIL(v) + if v == NIL then return nil end + return v +end + +--- Merges current process env with the given env and returns the result as +--- a list of "k=v" strings. +--- +--- <pre> +--- Example: +--- +--- in: { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } +--- out: { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } +--- </pre> +local function env_merge(env) + if env == nil then + return env + end + -- Merge. + env = vim.tbl_extend('force', vim.fn.environ(), env) + local final_env = {} + for k,v in pairs(env) do + assert(type(k) == 'string', 'env must be a dict') + table.insert(final_env, k..'='..tostring(v)) + end + return final_env +end + +local function format_message_with_content_length(encoded_message) + return table.concat { + 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; + encoded_message; + } +end + +--- Parse an LSP Message's header +-- @param header: The header to parse. +local function parse_headers(header) + if type(header) ~= 'string' then + return nil + end + local headers = {} + for line in vim.gsplit(header, '\r\n', true) do + if line == '' then + break + end + local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$") + if key then + key = key:lower():gsub('%-', '_') + headers[key] = value + else + local _ = log.error() and log.error("invalid header line %q", line) + error(string.format("invalid header line %q", line)) + end + end + headers.content_length = tonumber(headers.content_length) + or error(string.format("Content-Length not found in headers. %q", header)) + return headers +end + +-- This is the start of any possible header patterns. The gsub converts it to a +-- case insensitive pattern. +local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) + +local function request_parser_loop() + local buffer = '' + while true do + -- A message can only be complete if it has a double CRLF and also the full + -- payload, so first let's check for the CRLFs + local start, finish = buffer:find('\r\n\r\n', 1, true) + -- Start parsing the headers + if start then + -- This is a workaround for servers sending initial garbage before + -- sending headers, such as if a bash script sends stdout. It assumes + -- that we know all of the headers ahead of time. At this moment, the + -- only valid headers start with "Content-*", so that's the thing we will + -- be searching for. + -- TODO(ashkan) I'd like to remove this, but it seems permanent :( + local buffer_start = buffer:find(header_start_pattern) + local headers = parse_headers(buffer:sub(buffer_start, start-1)) + buffer = buffer:sub(finish+1) + local content_length = headers.content_length + -- Keep waiting for data until we have enough. + while #buffer < content_length do + buffer = buffer..(coroutine.yield() + or error("Expected more data for the body. The server may have died.")) -- TODO hmm. + end + local body = buffer:sub(1, content_length) + buffer = buffer:sub(content_length + 1) + -- Yield our data. + buffer = buffer..(coroutine.yield(headers, body) + or error("Expected more data for the body. The server may have died.")) -- TODO hmm. + else + -- Get more data since we don't have enough. + buffer = buffer..(coroutine.yield() + or error("Expected more data for the header. The server may have died.")) -- TODO hmm. + end + end +end + +local client_errors = vim.tbl_add_reverse_lookup { + INVALID_SERVER_MESSAGE = 1; + INVALID_SERVER_JSON = 2; + NO_RESULT_CALLBACK_FOUND = 3; + READ_ERROR = 4; + NOTIFICATION_HANDLER_ERROR = 5; + SERVER_REQUEST_HANDLER_ERROR = 6; + SERVER_RESULT_CALLBACK_ERROR = 7; +} + +local function format_rpc_error(err) + validate { + err = { err, 't' }; + } + + -- There is ErrorCodes in the LSP specification, + -- but in ResponseError.code it is not used and the actual type is number. + local code + if protocol.ErrorCodes[err.code] then + code = string.format("code_name = %s,", protocol.ErrorCodes[err.code]) + else + code = string.format("code_name = unknown, code = %s,", err.code) + end + + local message_parts = {"RPC[Error]", code} + if err.message then + table.insert(message_parts, "message =") + table.insert(message_parts, string.format("%q", err.message)) + end + if err.data then + table.insert(message_parts, "data =") + table.insert(message_parts, vim.inspect(err.data)) + end + return table.concat(message_parts, ' ') +end + +--- Creates an RPC response object/table. +--- +--@param code RPC error code defined in `vim.lsp.protocol.ErrorCodes` +--@param message (optional) arbitrary message to send to server +--@param data (optional) arbitrary data to send to server +local function rpc_response_error(code, message, data) + -- TODO should this error or just pick a sane error (like InternalError)? + local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code') + return setmetatable({ + code = code; + message = message or code_name; + data = data; + }, { + __tostring = format_rpc_error; + }) +end + +local default_handlers = {} +function default_handlers.notification(method, params) + local _ = log.debug() and log.debug('notification', method, params) +end +function default_handlers.server_request(method, params) + local _ = log.debug() and log.debug('server_request', method, params) + return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound) +end +function default_handlers.on_exit(code, signal) + local _ = log.info() and log.info("client exit", { code = code, signal = signal }) +end +function default_handlers.on_error(code, err) + local _ = log.error() and log.error('client_error:', client_errors[code], err) +end + +--- Create and start an RPC client. +-- @param cmd [ +local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_params) + local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params}) + validate { + cmd = { cmd, 's' }; + cmd_args = { cmd_args, 't' }; + handlers = { handlers, 't', true }; + } + + if not (vim.fn.executable(cmd) == 1) then + error(string.format("The given command %q is not executable.", cmd)) + end + if handlers then + local user_handlers = handlers + handlers = {} + for handle_name, default_handler in pairs(default_handlers) do + local user_handler = user_handlers[handle_name] + if user_handler then + if type(user_handler) ~= 'function' then + error(string.format("handler.%s must be a function", handle_name)) + end + -- server_request is wrapped elsewhere. + if not (handle_name == 'server_request' + or handle_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. + then + user_handler = schedule_wrap(user_handler) + end + handlers[handle_name] = user_handler + else + handlers[handle_name] = default_handler + end + end + else + handlers = default_handlers + end + + local stdin = uv.new_pipe(false) + local stdout = uv.new_pipe(false) + local stderr = uv.new_pipe(false) + + local message_index = 0 + local message_callbacks = {} + + local handle, pid + do + local function onexit(code, signal) + stdin:close() + stdout:close() + stderr:close() + handle:close() + -- Make sure that message_callbacks can be gc'd. + message_callbacks = nil + handlers.on_exit(code, signal) + end + local spawn_params = { + args = cmd_args; + stdio = {stdin, stdout, stderr}; + } + if extra_spawn_params then + spawn_params.cwd = extra_spawn_params.cwd + if spawn_params.cwd then + assert(is_dir(spawn_params.cwd), "cwd must be a directory") + end + spawn_params.env = env_merge(extra_spawn_params.env) + end + handle, pid = uv.spawn(cmd, spawn_params, onexit) + end + + local function encode_and_send(payload) + local _ = log.debug() and log.debug("rpc.send.payload", payload) + if handle:is_closing() then return false end + -- TODO(ashkan) remove this once we have a Lua json_encode + schedule(function() + local encoded = assert(json_encode(payload)) + stdin:write(format_message_with_content_length(encoded)) + end) + return true + end + + local function send_notification(method, params) + local _ = log.debug() and log.debug("rpc.notify", method, params) + return encode_and_send { + jsonrpc = "2.0"; + method = method; + params = params; + } + end + + local function send_response(request_id, err, result) + return encode_and_send { + id = request_id; + jsonrpc = "2.0"; + error = err; + result = result; + } + end + + local function send_request(method, params, callback) + validate { + callback = { callback, 'f' }; + } + message_index = message_index + 1 + local message_id = message_index + local result = encode_and_send { + id = message_id; + jsonrpc = "2.0"; + method = method; + params = params; + } + if result then + message_callbacks[message_id] = schedule_wrap(callback) + return result, message_id + else + return false + end + end + + stderr:read_start(function(_err, chunk) + if chunk then + local _ = log.error() and log.error("rpc", cmd, "stderr", chunk) + end + end) + + local function on_error(errkind, ...) + assert(client_errors[errkind]) + -- TODO what to do if this fails? + pcall(handlers.on_error, errkind, ...) + end + local function pcall_handler(errkind, status, head, ...) + if not status then + on_error(errkind, head, ...) + return status, head + end + return status, head, ... + end + local function try_call(errkind, fn, ...) + return pcall_handler(errkind, pcall(fn, ...)) + end + + -- TODO periodically check message_callbacks for old requests past a certain + -- time and log them. This would require storing the timestamp. I could call + -- them with an error then, perhaps. + + local function handle_body(body) + local decoded, err = json_decode(body) + if not decoded then + on_error(client_errors.INVALID_SERVER_JSON, err) + return + end + local _ = log.debug() and log.debug("decoded", decoded) + + if type(decoded.method) == 'string' and decoded.id then + -- Server Request + decoded.params = convert_NIL(decoded.params) + -- Schedule here so that the users functions don't trigger an error and + -- we can still use the result. + schedule(function() + local status, result + status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR, + handlers.server_request, decoded.method, decoded.params) + local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err }) + if status then + if not (result or err) then + -- TODO this can be a problem if `null` is sent for result. needs vim.NIL + error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method)) + end + if err then + assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.") + local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.") + err.message = err.message or code_name + end + else + -- On an exception, result will contain the error message. + err = rpc_response_error(protocol.ErrorCodes.InternalError, result) + result = nil + end + send_response(decoded.id, err, result) + end) + -- This works because we are expecting vim.NIL here + elseif decoded.id and (decoded.result or decoded.error) then + -- Server Result + decoded.error = convert_NIL(decoded.error) + decoded.result = convert_NIL(decoded.result) + + -- Do not surface RequestCancelled to users, it is RPC-internal. + if decoded.error + and decoded.error.code == protocol.ErrorCodes.RequestCancelled then + local _ = log.debug() and log.debug("Received cancellation ack", decoded) + local result_id = tonumber(decoded.id) + -- Clear any callback since this is cancelled now. + -- This is safe to do assuming that these conditions hold: + -- - The server will not send a result callback after this cancellation. + -- - If the server sent this cancellation ACK after sending the result, the user of this RPC + -- client will ignore the result themselves. + if result_id then + message_callbacks[result_id] = nil + end + return + end + + -- We sent a number, so we expect a number. + local result_id = tonumber(decoded.id) + local callback = message_callbacks[result_id] + if callback then + message_callbacks[result_id] = nil + validate { + callback = { callback, 'f' }; + } + if decoded.error then + decoded.error = setmetatable(decoded.error, { + __tostring = format_rpc_error; + }) + end + try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, + callback, decoded.error, decoded.result) + else + on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) + local _ = log.error() and log.error("No callback found for server response id "..result_id) + end + elseif type(decoded.method) == 'string' then + -- Notification + decoded.params = convert_NIL(decoded.params) + try_call(client_errors.NOTIFICATION_HANDLER_ERROR, + handlers.notification, decoded.method, decoded.params) + else + -- Invalid server message + on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) + end + end + -- TODO(ashkan) remove this once we have a Lua json_decode + handle_body = schedule_wrap(handle_body) + + local request_parser = coroutine.wrap(request_parser_loop) + request_parser() + stdout:read_start(function(err, chunk) + if err then + -- TODO better handling. Can these be intermittent errors? + on_error(client_errors.READ_ERROR, err) + return + end + -- This should signal that we are done reading from the client. + if not chunk then return end + -- Flush anything in the parser by looping until we don't get a result + -- anymore. + while true do + local headers, body = request_parser(chunk) + -- If we successfully parsed, then handle the response. + if headers then + handle_body(body) + -- Set chunk to empty so that we can call request_parser to get + -- anything existing in the parser to flush. + chunk = '' + else + break + end + end + end) + + return { + pid = pid; + handle = handle; + request = send_request; + notify = send_notification; + } +end + +return { + start = create_and_start_client; + rpc_response_error = rpc_response_error; + format_rpc_error = format_rpc_error; + client_errors = client_errors; +} +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua new file mode 100644 index 0000000000..d286f28d0c --- /dev/null +++ b/runtime/lua/vim/lsp/util.lua @@ -0,0 +1,1360 @@ +local protocol = require 'vim.lsp.protocol' +local vim = vim +local validate = vim.validate +local api = vim.api +local list_extend = vim.list_extend +local highlight = require 'vim.highlight' + +local M = {} + +--- Diagnostics received from the server via `textDocument/publishDiagnostics` +-- by buffer. +-- +-- {<bufnr>: {diagnostics}} +-- +-- This contains only entries for active buffers. Entries for detached buffers +-- are discarded. +-- +-- If you override the `textDocument/publishDiagnostic` callback, +-- this will be empty unless you call `buf_diagnostics_save_positions`. +-- +-- +-- Diagnostic is: +-- +-- { +-- range: Range +-- message: string +-- severity?: DiagnosticSeverity +-- code?: number | string +-- source?: string +-- tags?: DiagnosticTag[] +-- relatedInformation?: DiagnosticRelatedInformation[] +-- } +M.diagnostics_by_buf = {} + +local split = vim.split +local function split_lines(value) + return split(value, '\n', true) +end + +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end + +function M.set_lines(lines, A, B, new_lines) + -- 0-indexing to 1-indexing + local i_0 = A[1] + 1 + -- If it extends past the end, truncate it to the end. This is because the + -- way the LSP describes the range including the last newline is by + -- specifying a line number after what we would call the last line. + local i_n = math.min(B[1] + 1, #lines) + if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then + error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines}) + end + local prefix = "" + local suffix = lines[i_n]:sub(B[2]+1) + if A[2] > 0 then + prefix = lines[i_0]:sub(1, A[2]) + end + local n = i_n - i_0 + 1 + if n ~= #new_lines then + for _ = 1, n - #new_lines do table.remove(lines, i_0) end + for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end + end + for i = 1, #new_lines do + lines[i - 1 + i_0] = new_lines[i] + end + if #suffix > 0 then + local i = i_0 + #new_lines - 1 + lines[i] = lines[i]..suffix + end + if #prefix > 0 then + lines[i_0] = prefix..lines[i_0] + end + return lines +end + +local function sort_by_key(fn) + return function(a,b) + local ka, kb = fn(a), fn(b) + assert(#ka == #kb) + for i = 1, #ka do + if ka[i] ~= kb[i] then + return ka[i] < kb[i] + end + end + -- every value must have been equal here, which means it's not less than. + return false + end +end +local edit_sort_key = sort_by_key(function(e) + return {e.A[1], e.A[2], e.i} +end) + +--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position +-- Returns a zero-indexed column, since set_lines() does the conversion to +-- 1-indexed +local function get_line_byte_from_position(bufnr, position) + -- LSP's line and characters are 0-indexed + -- Vim's line and columns are 1-indexed + local col = position.character + -- When on the first character, we can ignore the difference between byte and + -- character + if col > 0 then + local line = position.line + local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false) + if #lines > 0 then + return vim.str_byteindex(lines[1], col) + end + end + return col +end + +function M.apply_text_edits(text_edits, bufnr) + if not next(text_edits) then return end + if not api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end + api.nvim_buf_set_option(bufnr, 'buflisted', true) + local start_line, finish_line = math.huge, -1 + local cleaned = {} + for i, e in ipairs(text_edits) do + -- adjust start and end column for UTF-16 encoding of non-ASCII characters + local start_row = e.range.start.line + local start_col = get_line_byte_from_position(bufnr, e.range.start) + local end_row = e.range["end"].line + local end_col = get_line_byte_from_position(bufnr, e.range['end']) + start_line = math.min(e.range.start.line, start_line) + finish_line = math.max(e.range["end"].line, finish_line) + -- TODO(ashkan) sanity check ranges for overlap. + table.insert(cleaned, { + i = i; + A = {start_row; start_col}; + B = {end_row; end_col}; + lines = vim.split(e.newText, '\n', true); + }) + end + + -- Reverse sort the orders so we can apply them without interfering with + -- eachother. Also add i as a sort key to mimic a stable sort. + table.sort(cleaned, edit_sort_key) + local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) + local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol') + local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) <= finish_line + 1 + if set_eol and #lines[#lines] ~= 0 then + table.insert(lines, '') + end + + for i = #cleaned, 1, -1 do + local e = cleaned[i] + local A = {e.A[1] - start_line, e.A[2]} + local B = {e.B[1] - start_line, e.B[2]} + lines = M.set_lines(lines, A, B, e.lines) + end + if set_eol and #lines[#lines] == 0 then + table.remove(lines) + end + api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines) +end + +-- local valid_windows_path_characters = "[^<>:\"/\\|?*]" +-- local valid_unix_path_characters = "[^/]" +-- https://github.com/davidm/lua-glob-pattern +-- https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names +-- function M.glob_to_regex(glob) +-- end + +-- textDocument/completion response returns one of CompletionItem[], CompletionList or null. +-- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion +function M.extract_completion_items(result) + if type(result) == 'table' and result.items then + return result.items + elseif result ~= nil then + return result + else + return {} + end +end + +--- Apply the TextDocumentEdit response. +-- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification +function M.apply_text_document_edit(text_document_edit) + local text_document = text_document_edit.textDocument + local bufnr = vim.uri_to_bufnr(text_document.uri) + if text_document.version then + -- `VersionedTextDocumentIdentifier`s version may be null https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier + if text_document.version ~= vim.NIL and M.buf_versions[bufnr] ~= nil and M.buf_versions[bufnr] > text_document.version then + print("Buffer ", text_document.uri, " newer than edits.") + return + end + end + M.apply_text_edits(text_document_edit.edits, bufnr) +end + +function M.get_current_line_to_cursor() + local pos = api.nvim_win_get_cursor(0) + local line = assert(api.nvim_buf_get_lines(0, pos[1]-1, pos[1], false)[1]) + return line:sub(pos[2]+1) +end + +local function parse_snippet_rec(input, inner) + local res = "" + + local close, closeend = nil, nil + if inner then + close, closeend = input:find("}", 1, true) + while close ~= nil and input:sub(close-1,close-1) == "\\" do + close, closeend = input:find("}", closeend+1, true) + end + end + + local didx = input:find('$', 1, true) + if didx == nil and close == nil then + return input, "" + elseif close ~=nil and (didx == nil or close < didx) then + -- No inner placeholders + return input:sub(0, close-1), input:sub(closeend+1) + end + + res = res .. input:sub(0, didx-1) + input = input:sub(didx+1) + + local tabstop, tabstopend = input:find('^%d+') + local placeholder, placeholderend = input:find('^{%d+:') + local choice, choiceend = input:find('^{%d+|') + + if tabstop then + input = input:sub(tabstopend+1) + elseif choice then + input = input:sub(choiceend+1) + close, closeend = input:find("|}", 1, true) + + res = res .. input:sub(0, close-1) + input = input:sub(closeend+1) + elseif placeholder then + -- TODO: add support for variables + input = input:sub(placeholderend+1) + + -- placeholders and variables are recursive + while input ~= "" do + local r, tail = parse_snippet_rec(input, true) + r = r:gsub("\\}", "}") + + res = res .. r + input = tail + end + else + res = res .. "$" + end + + return res, input +end + +-- Parse completion entries, consuming snippet tokens +function M.parse_snippet(input) + local res, _ = parse_snippet_rec(input, false) + + return res +end + +-- Sort by CompletionItem.sortText +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +local function sort_completion_items(items) + if items[1] and items[1].sortText then + table.sort(items, function(a, b) return a.sortText < b.sortText + end) + end +end + +-- Returns text that should be inserted when selecting completion item. The precedence is as follows: +-- textEdit.newText > insertText > label +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +local function get_completion_word(item) + if item.textEdit ~= nil and item.textEdit.newText ~= nil then + if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then + return item.textEdit.newText + else + return M.parse_snippet(item.textEdit.newText) + end + elseif item.insertText ~= nil then + if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then + return item.insertText + else + return M.parse_snippet(item.insertText) + end + end + return item.label +end + +-- Some language servers return complementary candidates whose prefixes do not match are also returned. +-- So we exclude completion candidates whose prefix does not match. +local function remove_unmatch_completion_items(items, prefix) + return vim.tbl_filter(function(item) + local word = get_completion_word(item) + return vim.startswith(word, prefix) + end, items) +end + +-- Acording to LSP spec, if the client set "completionItemKind.valueSet", +-- the client must handle it properly even if it receives a value outside the specification. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +function M._get_completion_item_kind_name(completion_item_kind) + return protocol.CompletionItemKind[completion_item_kind] or "Unknown" +end + +--- Getting vim complete-items with incomplete flag. +-- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) +-- @return { matches = complete-items table, incomplete = boolean } +function M.text_document_completion_list_to_complete_items(result, prefix) + local items = M.extract_completion_items(result) + if vim.tbl_isempty(items) then + return {} + end + + items = remove_unmatch_completion_items(items, prefix) + sort_completion_items(items) + + local matches = {} + + for _, completion_item in ipairs(items) do + local info = ' ' + local documentation = completion_item.documentation + if documentation then + if type(documentation) == 'string' and documentation ~= '' then + info = documentation + elseif type(documentation) == 'table' and type(documentation.value) == 'string' then + info = documentation.value + -- else + -- TODO(ashkan) Validation handling here? + end + end + + local word = get_completion_word(completion_item) + table.insert(matches, { + word = word, + abbr = completion_item.label, + kind = M._get_completion_item_kind_name(completion_item.kind), + menu = completion_item.detail or '', + info = info, + icase = 1, + dup = 1, + empty = 1, + user_data = { + nvim = { + lsp = { + completion_item = completion_item + } + } + }, + }) + end + + return matches +end + +-- @params WorkspaceEdit [table] see https://microsoft.github.io/language-server-protocol/specification +function M.apply_workspace_edit(workspace_edit) + if workspace_edit.documentChanges then + for _, change in ipairs(workspace_edit.documentChanges) do + if change.kind then + -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile + error(string.format("Unsupported change: %q", vim.inspect(change))) + else + M.apply_text_document_edit(change) + end + end + return + end + + local all_changes = workspace_edit.changes + if not (all_changes and not vim.tbl_isempty(all_changes)) then + return + end + + for uri, changes in pairs(all_changes) do + local bufnr = vim.uri_to_bufnr(uri) + M.apply_text_edits(changes, bufnr) + end +end + +--- Convert any of MarkedString | MarkedString[] | MarkupContent into markdown text lines +-- see https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_hover +-- Useful for textDocument/hover, textDocument/signatureHelp, and potentially others. +function M.convert_input_to_markdown_lines(input, contents) + contents = contents or {} + -- MarkedString variation 1 + if type(input) == 'string' then + list_extend(contents, split_lines(input)) + else + assert(type(input) == 'table', "Expected a table for Hover.contents") + -- MarkupContent + if input.kind then + -- The kind can be either plaintext or markdown. However, either way we + -- will just be rendering markdown, so we handle them both the same way. + -- TODO these can have escaped/sanitized html codes in markdown. We + -- should make sure we handle this correctly. + + -- Some servers send input.value as empty, so let's ignore this :( + -- assert(type(input.value) == 'string') + list_extend(contents, split_lines(input.value or '')) + -- MarkupString variation 2 + elseif input.language then + -- Some servers send input.value as empty, so let's ignore this :( + -- assert(type(input.value) == 'string') + table.insert(contents, "```"..input.language) + list_extend(contents, split_lines(input.value or '')) + table.insert(contents, "```") + -- By deduction, this must be MarkedString[] + else + -- Use our existing logic to handle MarkedString + for _, marked_string in ipairs(input) do + M.convert_input_to_markdown_lines(marked_string, contents) + end + end + end + if (contents[1] == '' or contents[1] == nil) and #contents == 1 then + return {} + end + return contents +end + +--- Convert SignatureHelp response to markdown lines. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +function M.convert_signature_help_to_markdown_lines(signature_help) + if not signature_help.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = signature_help.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #signature_help.signatures then + active_signature = 0 + end + local signature = signature_help.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, vim.split(signature.label, '\n', true)) + if signature.documentation then + M.convert_input_to_markdown_lines(signature.documentation, contents) + end + if signature_help.parameters then + local active_parameter = signature_help.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #signature_help.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + M.convert_input_help_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + +function M.make_floating_popup_options(width, height, opts) + validate { + opts = { opts, 't', true }; + } + opts = opts or {} + validate { + ["opts.offset_x"] = { opts.offset_x, 'n', true }; + ["opts.offset_y"] = { opts.offset_y, 'n', true }; + } + + local anchor = '' + local row, col + + local lines_above = vim.fn.winline() - 1 + local lines_below = vim.fn.winheight(0) - lines_above + + if lines_above < lines_below then + anchor = anchor..'N' + height = math.min(lines_below, height) + row = 1 + else + anchor = anchor..'S' + height = math.min(lines_above, height) + row = 0 + end + + if vim.fn.wincol() + width <= api.nvim_get_option('columns') then + anchor = anchor..'W' + col = 0 + else + anchor = anchor..'E' + col = 1 + end + + return { + anchor = anchor, + col = col + (opts.offset_x or 0), + height = height, + relative = 'cursor', + row = row + (opts.offset_y or 0), + style = 'minimal', + width = width, + } +end + +function M.jump_to_location(location) + -- location may be Location or LocationLink + local uri = location.uri or location.targetUri + if uri == nil then return end + local bufnr = vim.uri_to_bufnr(uri) + -- Save position in jumplist + vim.cmd "normal! m'" + + -- Push a new item into tagstack + local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0} + local items = {{tagname=vim.fn.expand('<cword>'), from=from}} + vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't') + + --- Jump to new location (adjusting for UTF-16 encoding of characters) + api.nvim_set_current_buf(bufnr) + api.nvim_buf_set_option(0, 'buflisted', true) + local range = location.range or location.targetSelectionRange + local row = range.start.line + local col = get_line_byte_from_position(0, range.start) + api.nvim_win_set_cursor(0, {row + 1, col}) + return true +end + +--- Preview a location in a floating windows +--- +--- behavior depends on type of location: +--- - for Location, range is shown (e.g., function definition) +--- - for LocationLink, targetRange is shown (e.g., body of function definition) +--- +--@param location a single Location or LocationLink +--@return bufnr,winnr buffer and window number of floating window or nil +function M.preview_location(location) + -- location may be LocationLink or Location (more useful for the former) + local uri = location.targetUri or location.uri + if uri == nil then return end + local bufnr = vim.uri_to_bufnr(uri) + if not api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end + local range = location.targetRange or location.range + local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false) + local filetype = api.nvim_buf_get_option(bufnr, 'filetype') + return M.open_floating_preview(contents, filetype) +end + +local function find_window_by_var(name, value) + for _, win in ipairs(api.nvim_list_wins()) do + if npcall(api.nvim_win_get_var, win, name) == value then + return win + end + end +end + +-- Check if a window with `unique_name` tagged is associated with the current +-- buffer. If not, make a new preview. +-- +-- fn()'s return bufnr, winnr +-- case that a new floating window should be created. +function M.focusable_float(unique_name, fn) + if npcall(api.nvim_win_get_var, 0, unique_name) then + return api.nvim_command("wincmd p") + end + local bufnr = api.nvim_get_current_buf() + do + local win = find_window_by_var(unique_name, bufnr) + if win then + api.nvim_set_current_win(win) + api.nvim_command("stopinsert") + return + end + end + local pbufnr, pwinnr = fn() + if pbufnr then + api.nvim_win_set_var(pwinnr, unique_name, bufnr) + return pbufnr, pwinnr + end +end + +-- Check if a window with `unique_name` tagged is associated with the current +-- buffer. If not, make a new preview. +-- +-- fn()'s return values will be passed directly to open_floating_preview in the +-- case that a new floating window should be created. +function M.focusable_preview(unique_name, fn) + return M.focusable_float(unique_name, function() + return M.open_floating_preview(fn()) + end) +end + +--- Trim empty lines from input and pad left and right with spaces +--- +--@param contents table of lines to trim and pad +--@param opts dictionary with optional fields +-- - pad_left number of columns to pad contents at left (default 1) +-- - pad_right number of columns to pad contents at right (default 1) +-- - pad_top number of lines to pad contents at top (default 0) +-- - pad_bottom number of lines to pad contents at bottom (default 0) +--@return contents table of trimmed and padded lines +function M._trim_and_pad(contents, opts) + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + local left_padding = (" "):rep(opts.pad_left or 1) + local right_padding = (" "):rep(opts.pad_right or 1) + contents = M.trim_empty_lines(contents) + for i, line in ipairs(contents) do + contents[i] = string.format('%s%s%s', left_padding, line:gsub("\r", ""), right_padding) + end + if opts.pad_top then + for _ = 1, opts.pad_top do + table.insert(contents, 1, "") + end + end + if opts.pad_bottom then + for _ = 1, opts.pad_bottom do + table.insert(contents, "") + end + end + return contents +end + + + +--- Convert markdown into syntax highlighted regions by stripping the code +--- blocks and converting them into highlighted code. +--- This will by default insert a blank line separator after those code block +--- regions to improve readability. +--- The result is shown in a floating preview +--- TODO: refactor to separate stripping/converting and make use of open_floating_preview +--- +--@param contents table of lines to show in window +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - max_width maximal width of floating window +-- - max_height maximal height of floating window +-- - pad_left number of columns to pad contents at left +-- - pad_right number of columns to pad contents at right +-- - pad_top number of lines to pad contents at top +-- - pad_bottom number of lines to pad contents at bottom +-- - separator insert separator after code block +--@return width,height size of float +function M.fancy_floating_markdown(contents, opts) + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + + local stripped = {} + local highlights = {} + do + local i = 1 + while i <= #contents do + local line = contents[i] + -- TODO(ashkan): use a more strict regex for filetype? + local ft = line:match("^```([a-zA-Z0-9_]*)$") + -- local ft = line:match("^```(.*)$") + -- TODO(ashkan): validate the filetype here. + if ft then + local start = #stripped + i = i + 1 + while i <= #contents do + line = contents[i] + if line == "```" then + i = i + 1 + break + end + table.insert(stripped, line) + i = i + 1 + end + table.insert(highlights, { + ft = ft; + start = start + 1; + finish = #stripped + 1 - 1; + }) + else + table.insert(stripped, line) + i = i + 1 + end + end + end + -- Clean up and add padding + stripped = M._trim_and_pad(stripped, opts) + + -- Compute size of float needed to show (wrapped) lines + opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + local width, height = M._make_floating_popup_size(stripped, opts) + + -- Insert blank line separator after code block + local insert_separator = opts.separator or true + if insert_separator then + for i, h in ipairs(highlights) do + h.start = h.start + i - 1 + h.finish = h.finish + i - 1 + if h.finish + 1 <= #stripped then + table.insert(stripped, h.finish + 1, string.rep("─", width)) + height = height + 1 + end + end + end + + -- Make the floating window. + local bufnr = api.nvim_create_buf(false, true) + local winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) + api.nvim_buf_set_option(bufnr, 'modifiable', false) + + -- Switch to the floating window to apply the syntax highlighting. + -- This is because the syntax command doesn't accept a target. + local cwin = vim.api.nvim_get_current_win() + vim.api.nvim_set_current_win(winnr) + + vim.cmd("ownsyntax markdown") + local idx = 1 + local function apply_syntax_to_region(ft, start, finish) + if ft == '' then return end + local name = ft..idx + idx = idx + 1 + local lang = "@"..ft:upper() + -- TODO(ashkan): better validation before this. + if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then + return + end + vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s", name, start, finish + 1, lang)) + end + -- Previous highlight region. + -- TODO(ashkan): this wasn't working for some reason, but I would like to + -- make sure that regions between code blocks are definitely markdown. + -- local ph = {start = 0; finish = 1;} + for _, h in ipairs(highlights) do + -- apply_syntax_to_region('markdown', ph.finish, h.start) + apply_syntax_to_region(h.ft, h.start, h.finish) + -- ph = h + end + + vim.api.nvim_set_current_win(cwin) + return bufnr, winnr +end + +function M.close_preview_autocmd(events, winnr) + api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") +end + +--- Compute size of float needed to show contents (with optional wrapping) +--- +--@param contents table of lines to show in window +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - max_width maximal width of floating window +-- - max_height maximal height of floating window +--@return width,height size of float +function M._make_floating_popup_size(contents, opts) + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + + local width = opts.width + local height = opts.height + local wrap_at = opts.wrap_at + local max_width = opts.max_width + local max_height = opts.max_height + local line_widths = {} + + if not width then + width = 0 + for i, line in ipairs(contents) do + -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. + line_widths[i] = vim.fn.strdisplaywidth(line) + width = math.max(line_widths[i], width) + end + end + if max_width then + width = math.min(width, max_width) + wrap_at = math.min(wrap_at or max_width, max_width) + end + + if not height then + height = #contents + if wrap_at and width >= wrap_at then + height = 0 + if vim.tbl_isempty(line_widths) then + for _, line in ipairs(contents) do + local line_width = vim.fn.strdisplaywidth(line) + height = height + math.ceil(line_width/wrap_at) + end + else + for i = 1, #contents do + height = height + math.max(1, math.ceil(line_widths[i]/wrap_at)) + end + end + end + end + if max_height then + height = math.min(height, max_height) + end + + return width, height +end + +--- Show contents in a floating window +--- +--@param contents table of lines to show in window +--@param filetype string of filetype to set for opened buffer +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - max_width maximal width of floating window +-- - max_height maximal height of floating window +-- - pad_left number of columns to pad contents at left +-- - pad_right number of columns to pad contents at right +-- - pad_top number of lines to pad contents at top +-- - pad_bottom number of lines to pad contents at bottom +--@return bufnr,winnr buffer and window number of floating window or nil +function M.open_floating_preview(contents, filetype, opts) + validate { + contents = { contents, 't' }; + filetype = { filetype, 's', true }; + opts = { opts, 't', true }; + } + opts = opts or {} + + -- Clean up input: trim empty lines from the end, pad + contents = M._trim_and_pad(contents, opts) + + -- Compute size of float needed to show (wrapped) lines + opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + local width, height = M._make_floating_popup_size(contents, opts) + + local floating_bufnr = api.nvim_create_buf(false, true) + if filetype then + api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) + end + local float_option = M.make_floating_popup_options(width, height, opts) + local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + if filetype == 'markdown' then + api.nvim_win_set_option(floating_winnr, 'conceallevel', 2) + end + api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) + api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) + M.close_preview_autocmd({"CursorMoved", "CursorMovedI", "BufHidden", "BufLeave"}, floating_winnr) + return floating_bufnr, floating_winnr +end + +do + local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") + local reference_ns = api.nvim_create_namespace("vim_lsp_references") + local sign_ns = 'vim_lsp_signs' + local underline_highlight_name = "LspDiagnosticsUnderline" + vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) + for kind, _ in pairs(protocol.DiagnosticSeverity) do + if type(kind) == 'string' then + vim.cmd(string.format("highlight default link %s%s %s", underline_highlight_name, kind, underline_highlight_name)) + end + end + + local severity_highlights = {} + + local severity_floating_highlights = {} + + local default_severity_highlight = { + [protocol.DiagnosticSeverity.Error] = { guifg = "Red" }; + [protocol.DiagnosticSeverity.Warning] = { guifg = "Orange" }; + [protocol.DiagnosticSeverity.Information] = { guifg = "LightBlue" }; + [protocol.DiagnosticSeverity.Hint] = { guifg = "LightGrey" }; + } + + -- Initialize default severity highlights + for severity, hi_info in pairs(default_severity_highlight) do + local severity_name = protocol.DiagnosticSeverity[severity] + local highlight_name = "LspDiagnostics"..severity_name + local floating_highlight_name = highlight_name.."Floating" + -- Try to fill in the foreground color with a sane default. + local cmd_parts = {"highlight", "default", highlight_name} + for k, v in pairs(hi_info) do + table.insert(cmd_parts, k.."="..v) + end + api.nvim_command(table.concat(cmd_parts, ' ')) + api.nvim_command('highlight link ' .. highlight_name .. 'Sign ' .. highlight_name) + api.nvim_command('highlight link ' .. highlight_name .. 'Floating ' .. highlight_name) + severity_highlights[severity] = highlight_name + severity_floating_highlights[severity] = floating_highlight_name + end + + function M.buf_clear_diagnostics(bufnr) + validate { bufnr = {bufnr, 'n', true} } + bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + + -- clear sign group + vim.fn.sign_unplace(sign_ns, {buffer=bufnr}) + + -- clear virtual text namespace + api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) + end + + function M.get_severity_highlight_name(severity) + return severity_highlights[severity] + end + + function M.get_line_diagnostics() + local bufnr = api.nvim_get_current_buf() + local linenr = api.nvim_win_get_cursor(0)[1] - 1 + + local buffer_diagnostics = M.diagnostics_by_buf[bufnr] + + if not buffer_diagnostics then + return {} + end + + local diagnostics_by_line = M.diagnostics_group_by_line(buffer_diagnostics) + return diagnostics_by_line[linenr] or {} + end + + function M.show_line_diagnostics() + -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {}) + -- if #marks == 0 then + -- return + -- end + local lines = {"Diagnostics:"} + local highlights = {{0, "Bold"}} + local line_diagnostics = M.get_line_diagnostics() + if vim.tbl_isempty(line_diagnostics) then return end + + for i, diagnostic in ipairs(line_diagnostics) do + -- for i, mark in ipairs(marks) do + -- local mark_id = mark[1] + -- local diagnostic = buffer_diagnostics[mark_id] + + -- TODO(ashkan) make format configurable? + local prefix = string.format("%d. ", i) + local hiname = severity_floating_highlights[diagnostic.severity] + assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity)) + local message_lines = split_lines(diagnostic.message) + table.insert(lines, prefix..message_lines[1]) + table.insert(highlights, {#prefix + 1, hiname}) + for j = 2, #message_lines do + table.insert(lines, message_lines[j]) + table.insert(highlights, {0, hiname}) + end + end + local popup_bufnr, winnr = M.open_floating_preview(lines, 'plaintext') + for i, hi in ipairs(highlights) do + local prefixlen, hiname = unpack(hi) + -- Start highlight after the prefix + api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1) + end + return popup_bufnr, winnr + end + + --- Saves the diagnostics (Diagnostic[]) into diagnostics_by_buf + --- + --@param bufnr bufnr for which the diagnostics are for. + --@param diagnostics Diagnostics[] received from the language server. + function M.buf_diagnostics_save_positions(bufnr, diagnostics) + validate { + bufnr = {bufnr, 'n', true}; + diagnostics = {diagnostics, 't', true}; + } + if not diagnostics then return end + bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + + if not M.diagnostics_by_buf[bufnr] then + -- Clean up our data when the buffer unloads. + api.nvim_buf_attach(bufnr, false, { + on_detach = function(b) + M.diagnostics_by_buf[b] = nil + end + }) + end + M.diagnostics_by_buf[bufnr] = diagnostics + end + + function M.buf_diagnostics_underline(bufnr, diagnostics) + for _, diagnostic in ipairs(diagnostics) do + local start = diagnostic.range["start"] + local finish = diagnostic.range["end"] + + local hlmap = { + [protocol.DiagnosticSeverity.Error]='Error', + [protocol.DiagnosticSeverity.Warning]='Warning', + [protocol.DiagnosticSeverity.Information]='Information', + [protocol.DiagnosticSeverity.Hint]='Hint', + } + + highlight.range(bufnr, diagnostic_ns, + underline_highlight_name..hlmap[diagnostic.severity], + {start.line, start.character}, + {finish.line, finish.character} + ) + end + end + + function M.buf_clear_references(bufnr) + validate { bufnr = {bufnr, 'n', true} } + api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) + end + + function M.buf_highlight_references(bufnr, references) + validate { bufnr = {bufnr, 'n', true} } + for _, reference in ipairs(references) do + local start_pos = {reference["range"]["start"]["line"], reference["range"]["start"]["character"]} + local end_pos = {reference["range"]["end"]["line"], reference["range"]["end"]["character"]} + local document_highlight_kind = { + [protocol.DocumentHighlightKind.Text] = "LspReferenceText"; + [protocol.DocumentHighlightKind.Read] = "LspReferenceRead"; + [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; + } + local kind = reference["kind"] or protocol.DocumentHighlightKind.Text + highlight.range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos) + end + end + + function M.diagnostics_group_by_line(diagnostics) + if not diagnostics then return end + local diagnostics_by_line = {} + for _, diagnostic in ipairs(diagnostics) do + local start = diagnostic.range.start + local line_diagnostics = diagnostics_by_line[start.line] + if not line_diagnostics then + line_diagnostics = {} + diagnostics_by_line[start.line] = line_diagnostics + end + table.insert(line_diagnostics, diagnostic) + end + return diagnostics_by_line + end + + function M.buf_diagnostics_virtual_text(bufnr, diagnostics) + if not diagnostics then + return + end + local buffer_line_diagnostics = M.diagnostics_group_by_line(diagnostics) + for line, line_diags in pairs(buffer_line_diagnostics) do + local virt_texts = {} + for i = 1, #line_diags - 1 do + table.insert(virt_texts, {"■", severity_highlights[line_diags[i].severity]}) + end + local last = line_diags[#line_diags] + -- TODO(ashkan) use first line instead of subbing 2 spaces? + table.insert(virt_texts, {"■ "..last.message:gsub("\r", ""):gsub("\n", " "), severity_highlights[last.severity]}) + api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {}) + end + end + + --- Returns the number of diagnostics of given kind for current buffer. + --- + --- Useful for showing diagnostic counts in statusline. eg: + --- + --- <pre> + --- function! LspStatus() abort + --- let sl = '' + --- if luaeval('not vim.tbl_isempty(vim.lsp.buf_get_clients(0))') + --- let sl.='%#MyStatuslineLSP#E:' + --- let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.util.buf_diagnostics_count([[Error]])")}' + --- let sl.='%#MyStatuslineLSP# W:' + --- let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.util.buf_diagnostics_count([[Warning]])")}' + --- else + --- let sl.='%#MyStatuslineLSPErrors#off' + --- endif + --- return sl + --- endfunction + --- let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus() + --- </pre> + --- + --@param kind Diagnostic severity kind: See |vim.lsp.protocol.DiagnosticSeverity| + --- + --@return Count of diagnostics + function M.buf_diagnostics_count(kind) + local bufnr = vim.api.nvim_get_current_buf() + local diagnostics = M.diagnostics_by_buf[bufnr] + if not diagnostics then return end + local count = 0 + for _, diagnostic in pairs(diagnostics) do + if protocol.DiagnosticSeverity[kind] == diagnostic.severity then + count = count + 1 + end + end + return count + end + + local diagnostic_severity_map = { + [protocol.DiagnosticSeverity.Error] = "LspDiagnosticsErrorSign"; + [protocol.DiagnosticSeverity.Warning] = "LspDiagnosticsWarningSign"; + [protocol.DiagnosticSeverity.Information] = "LspDiagnosticsInformationSign"; + [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; + } + + --- Place signs for each diagnostic in the sign column. + --- + --- Sign characters can be customized with the following commands: + --- + --- <pre> + --- sign define LspDiagnosticsErrorSign text=E texthl=LspDiagnosticsError linehl= numhl= + --- sign define LspDiagnosticsWarningSign text=W texthl=LspDiagnosticsWarning linehl= numhl= + --- sign define LspDiagnosticsInformationSign text=I texthl=LspDiagnosticsInformation linehl= numhl= + --- sign define LspDiagnosticsHintSign text=H texthl=LspDiagnosticsHint linehl= numhl= + --- </pre> + function M.buf_diagnostics_signs(bufnr, diagnostics) + for _, diagnostic in ipairs(diagnostics) do + vim.fn.sign_place(0, sign_ns, diagnostic_severity_map[diagnostic.severity], bufnr, {lnum=(diagnostic.range.start.line+1)}) + end + end +end + +local position_sort = sort_by_key(function(v) + return {v.start.line, v.start.character} +end) + +-- Returns the items with the byte position calculated correctly and in sorted +-- order. +function M.locations_to_items(locations) + local items = {} + local grouped = setmetatable({}, { + __index = function(t, k) + local v = {} + rawset(t, k, v) + return v + end; + }) + for _, d in ipairs(locations) do + -- locations may be Location or LocationLink + local uri = d.uri or d.targetUri + local range = d.range or d.targetSelectionRange + table.insert(grouped[uri], {start = range.start}) + end + + + local keys = vim.tbl_keys(grouped) + table.sort(keys) + -- TODO(ashkan) I wish we could do this lazily. + for _, uri in ipairs(keys) do + local rows = grouped[uri] + table.sort(rows, position_sort) + local bufnr = vim.uri_to_bufnr(uri) + vim.fn.bufload(bufnr) + local filename = vim.uri_to_fname(uri) + for _, temp in ipairs(rows) do + local pos = temp.start + local row = pos.line + local line = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1] + local col = M.character_offset(bufnr, row, pos.character) + table.insert(items, { + filename = filename, + lnum = row + 1, + col = col + 1; + text = line; + }) + end + end + return items +end + +function M.set_loclist(items) + vim.fn.setloclist(0, {}, ' ', { + title = 'Language Server'; + items = items; + }) +end + +function M.set_qflist(items) + vim.fn.setqflist({}, ' ', { + title = 'Language Server'; + items = items; + }) +end + +-- Acording to LSP spec, if the client set "symbolKind.valueSet", +-- the client must handle it properly even if it receives a value outside the specification. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol +function M._get_symbol_kind_name(symbol_kind) + return protocol.SymbolKind[symbol_kind] or "Unknown" +end + +--- Convert symbols to quickfix list items +--- +--@param symbols DocumentSymbol[] or SymbolInformation[] +function M.symbols_to_items(symbols, bufnr) + local function _symbols_to_items(_symbols, _items, _bufnr) + for _, symbol in ipairs(_symbols) do + if symbol.location then -- SymbolInformation type + local range = symbol.location.range + local kind = M._get_symbol_kind_name(symbol.kind) + table.insert(_items, { + filename = vim.uri_to_fname(symbol.location.uri), + lnum = range.start.line + 1, + col = range.start.character + 1, + kind = kind, + text = '['..kind..'] '..symbol.name, + }) + elseif symbol.range then -- DocumentSymbole type + local kind = M._get_symbol_kind_name(symbol.kind) + table.insert(_items, { + -- bufnr = _bufnr, + filename = vim.api.nvim_buf_get_name(_bufnr), + lnum = symbol.range.start.line + 1, + col = symbol.range.start.character + 1, + kind = kind, + text = '['..kind..'] '..symbol.name + }) + if symbol.children then + for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do + vim.list_extend(_items, v) + end + end + end + end + return _items + end + return _symbols_to_items(symbols, {}, bufnr) +end + +-- Remove empty lines from the beginning and end. +function M.trim_empty_lines(lines) + local start = 1 + for i = 1, #lines do + if #lines[i] > 0 then + start = i + break + end + end + local finish = 1 + for i = #lines, 1, -1 do + if #lines[i] > 0 then + finish = i + break + end + end + return vim.list_extend({}, lines, start, finish) +end + +-- Accepts markdown lines and tries to reduce it to a filetype if it is +-- just a single code block. +-- Note: This modifies the input. +-- +-- Returns: filetype or 'markdown' if it was unchanged. +function M.try_trim_markdown_code_blocks(lines) + local language_id = lines[1]:match("^```(.*)") + if language_id then + local has_inner_code_fence = false + for i = 2, (#lines - 1) do + local line = lines[i] + if line:sub(1,3) == '```' then + has_inner_code_fence = true + break + end + end + -- No inner code fences + starting with code fence = hooray. + if not has_inner_code_fence then + table.remove(lines, 1) + table.remove(lines) + return language_id + end + end + return 'markdown' +end + +local str_utfindex = vim.str_utfindex +local function make_position_param() + local row, col = unpack(api.nvim_win_get_cursor(0)) + row = row - 1 + local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + col = str_utfindex(line, col) + return { line = row; character = col; } +end + +function M.make_position_params() + return { + textDocument = M.make_text_document_params(); + position = make_position_param() + } +end + +function M.make_range_params() + local position = make_position_param() + return { + textDocument = { uri = vim.uri_from_bufnr(0) }, + range = { start = position; ["end"] = position; } + } +end + +function M.make_text_document_params() + return { uri = vim.uri_from_bufnr(0) } +end + +--- Get visual width of tabstop. +--- +--@see |softtabstop| +--@param bufnr (optional, number): Buffer handle, defaults to current +--@returns (number) tabstop visual width +function M.get_effective_tabstop(bufnr) + validate { bufnr = {bufnr, 'n', true} } + local bo = bufnr and vim.bo[bufnr] or vim.bo + local sts = bo.softtabstop + return (sts > 0 and sts) or (sts < 0 and bo.shiftwidth) or bo.tabstop +end + +function M.make_formatting_params(options) + validate { options = {options, 't', true} } + options = vim.tbl_extend('keep', options or {}, { + tabSize = M.get_effective_tabstop(); + insertSpaces = vim.bo.expandtab; + }) + return { + textDocument = { uri = vim.uri_from_bufnr(0) }; + options = options; + } +end + +-- @param buf buffer handle or 0 for current. +-- @param row 0-indexed line +-- @param col 0-indexed byte offset in line +function M.character_offset(buf, row, col) + local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] + -- If the col is past the EOL, use the line length. + if col > #line then + return str_utfindex(line) + end + return str_utfindex(line, col) +end + +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 cd6f8a04d8..6e427665f2 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -4,34 +4,51 @@ -- test-suite. If, in the future, Nvim itself is used to run the test-suite -- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua +local vim = vim or {} --- Returns a deep copy of the given object. Non-table objects are copied as --- in a typical Lua assignment, whereas table objects are copied recursively. +--- Functions are naively copied, so functions in the copied table point to the +--- same functions as those in the input table. Userdata and threads are not +--- copied and will throw an error. --- --@param orig Table to copy --@returns New table of copied keys and (nested) values. -local function deepcopy(orig) - error(orig) -end -local function _id(v) - return v -end -local deepcopy_funcs = { - table = function(orig) - local copy = {} - for k, v in pairs(orig) do - copy[deepcopy(k)] = deepcopy(v) +function vim.deepcopy(orig) end -- luacheck: no unused +vim.deepcopy = (function() + local function _id(v) + return v + end + + local deepcopy_funcs = { + table = function(orig) + local copy = {} + + if vim._empty_dict_mt ~= nil and getmetatable(orig) == vim._empty_dict_mt then + copy = vim.empty_dict() + end + + for k, v in pairs(orig) do + copy[vim.deepcopy(k)] = vim.deepcopy(v) + end + return copy + end, + number = _id, + string = _id, + ['nil'] = _id, + boolean = _id, + ['function'] = _id, + } + + return function(orig) + local f = deepcopy_funcs[type(orig)] + if f then + return f(orig) + else + error("Cannot deepcopy object of type "..type(orig)) end - return copy - end, - number = _id, - string = _id, - ['nil'] = _id, - boolean = _id, -} -deepcopy = function(orig) - return deepcopy_funcs[type(orig)](orig) -end + end +end)() --- Splits a string at each instance of a separator. --- @@ -43,10 +60,8 @@ end --@param sep Separator string or pattern --@param plain If `true` use `sep` literally (passed to String.find) --@returns Iterator over the split components -local function gsplit(s, sep, plain) - assert(type(s) == "string") - assert(type(sep) == "string") - assert(type(plain) == "boolean" or type(plain) == "nil") +function vim.gsplit(s, sep, plain) + vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}} local start = 1 local done = false @@ -64,7 +79,7 @@ local function gsplit(s, sep, plain) end return function() - if done then + if done or (s == '' and sep == '') then return end if sep == '' then @@ -92,20 +107,81 @@ end --@param sep Separator string or pattern --@param plain If `true` use `sep` literally (passed to String.find) --@returns List-like table of the split components. -local function split(s,sep,plain) - local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end +function vim.split(s,sep,plain) + local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end return t end +--- Return a list of all keys used in a table. +--- However, the order of the return table of keys is not guaranteed. +--- +--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +--- +--@param t Table +--@returns list of keys +function vim.tbl_keys(t) + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + + local keys = {} + for k, _ in pairs(t) do + table.insert(keys, k) + end + return keys +end + +--- Return a list of all values used in a table. +--- However, the order of the return table of values is not guaranteed. +--- +--@param t Table +--@returns list of values +function vim.tbl_values(t) + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + + local values = {} + for _, v in pairs(t) do + table.insert(values, v) + end + return values +end + +--- Apply a function to all values of a table. +--- +--@param func function or callable table +--@param t table +function vim.tbl_map(func, t) + vim.validate{func={func,'c'},t={t,'t'}} + + local rettab = {} + for k, v in pairs(t) do + rettab[k] = func(v) + end + return rettab +end + +--- Filter a table using a predicate function +--- +--@param func function or callable table +--@param t table +function vim.tbl_filter(func, t) + vim.validate{func={func,'c'},t={t,'t'}} + + local rettab = {} + for _, entry in pairs(t) do + if func(entry) then + table.insert(rettab, entry) + end + end + return rettab +end + --- Checks if a list-like (vector) table contains `value`. --- --@param t Table to check --@param value Value to compare --@returns true if `t` contains `value` -local function tbl_contains(t, value) - if type(t) ~= 'table' then - error('t must be a table') - end +function vim.tbl_contains(t, value) + vim.validate{t={t,'t'}} + for _,v in ipairs(t) do if v == value then return true @@ -114,25 +190,38 @@ local function tbl_contains(t, value) return false end ---- Merges two or more map-like tables. ---- ---@see |extend()| ---- ---@param behavior Decides what to do if a key is found in more than one map: ---- - "error": raise an error ---- - "keep": use value from the leftmost map ---- - "force": use value from the rightmost map ---@param ... Two or more map-like tables. -local function tbl_extend(behavior, ...) +-- Returns true if the table is empty, and contains no indexed or keyed values. +-- +--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +-- +--@param t Table to check +function vim.tbl_isempty(t) + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + return next(t) == nil +end + +local function tbl_extend(behavior, deep_extend, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) end + + if select('#', ...) < 2 then + error('wrong number of arguments (given '..tostring(1 + select('#', ...))..', expected at least 3)') + end + local ret = {} + if vim._empty_dict_mt ~= nil and getmetatable(select(1, ...)) == vim._empty_dict_mt then + ret = vim.empty_dict() + end + for i = 1, select('#', ...) do local tbl = select(i, ...) + vim.validate{["after the second argument"] = {tbl,'t'}} if tbl then for k, v in pairs(tbl) do - if behavior ~= 'force' and ret[k] ~= nil then + if type(v) == 'table' and deep_extend and not vim.tbl_islist(v) then + ret[k] = tbl_extend(behavior, true, ret[k] or vim.empty_dict(), v) + elseif behavior ~= 'force' and ret[k] ~= nil then if behavior == 'error' then error('key found in more than one map: '..k) end -- Else behavior is "keep". @@ -145,13 +234,103 @@ local function tbl_extend(behavior, ...) return ret end +--- Merges two or more map-like tables. +--- +--@see |extend()| +--- +--@param behavior Decides what to do if a key is found in more than one map: +--- - "error": raise an error +--- - "keep": use value from the leftmost map +--- - "force": use value from the rightmost map +--@param ... Two or more map-like tables. +function vim.tbl_extend(behavior, ...) + return tbl_extend(behavior, false, ...) +end + +--- Merges recursively two or more map-like tables. +--- +--@see |tbl_extend()| +--- +--@param behavior Decides what to do if a key is found in more than one map: +--- - "error": raise an error +--- - "keep": use value from the leftmost map +--- - "force": use value from the rightmost map +--@param ... Two or more map-like tables. +function vim.tbl_deep_extend(behavior, ...) + return tbl_extend(behavior, true, ...) +end + +--- Deep compare values for equality +function vim.deep_equal(a, b) + if a == b then return true end + if type(a) ~= type(b) then return false end + if type(a) == 'table' then + -- TODO improve this algorithm's performance. + for k, v in pairs(a) do + if not vim.deep_equal(v, b[k]) then + return false + end + end + for k, v in pairs(b) do + if not vim.deep_equal(v, a[k]) then + return false + end + end + return true + end + return false +end + +--- Add the reverse lookup values to an existing table. +--- For example: +--- `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }` +-- +--Do note that it *modifies* the input. +--@param o table The table to add the reverse to. +function vim.tbl_add_reverse_lookup(o) + local keys = vim.tbl_keys(o) + for _, k in ipairs(keys) do + local v = o[k] + if o[v] then + error(string.format("The reverse lookup found an existing value for %q while processing key %q", tostring(v), tostring(k))) + end + o[v] = k + end + return o +end + +--- Extends a list-like table with the values of another list-like table. +--- +--- NOTE: This mutates dst! +--- +--@see |vim.tbl_extend()| +--- +--@param dst list which will be modified and appended to. +--@param src list from which values will be inserted. +--@param start Start index on src. defaults to 1 +--@param finish Final index on src. defaults to #src +--@returns dst +function vim.list_extend(dst, src, start, finish) + vim.validate { + dst = {dst, 't'}; + src = {src, 't'}; + start = {start, 'n', true}; + finish = {finish, 'n', true}; + } + for i = start or 1, finish or #src do + table.insert(dst, src[i]) + end + return dst +end + --- Creates a copy of a list-like table such that any nested tables are --- "unrolled" and appended to the result. --- +--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +--- --@param t List-like table --@returns Flattened copy of the given list-like table. -local function tbl_flatten(t) - -- From https://github.com/premake/premake-core/blob/master/src/base/table.lua +function vim.tbl_flatten(t) local result = {} local function _tbl_flatten(_t) local n = #_t @@ -168,34 +347,190 @@ local function tbl_flatten(t) return result end +--- Determine whether a Lua table can be treated as an array. +--- +--- An empty table `{}` will default to being treated as an array. +--- Use `vim.emtpy_dict()` to create a table treated as an +--- empty dict. Empty tables returned by `rpcrequest()` and +--- `vim.fn` functions can be checked using this function +--- whether they represent empty API arrays and vimL lists. +--- +--@param t Table +--@returns `true` if array-like table, else `false`. +function vim.tbl_islist(t) + if type(t) ~= 'table' then + return false + end + + local count = 0 + + for k, _ in pairs(t) do + if type(k) == "number" then + count = count + 1 + else + return false + end + end + + if count > 0 then + return true + else + -- 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 + end +end + +--- Counts the number of non-nil values in table `t`. +--- +--- <pre> +--- vim.tbl_count({ a=1, b=2 }) => 2 +--- vim.tbl_count({ 1, 2 }) => 2 +--- </pre> +--- +--@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua +--@param t Table +--@returns Number that is the number of the value in table +function vim.tbl_count(t) + vim.validate{t={t,'t'}} + + local count = 0 + for _ in pairs(t) do count = count + 1 end + return count +end + --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- --@see https://www.lua.org/pil/20.2.html --@param s String to trim --@returns String with whitespace removed from its beginning and end -local function trim(s) - assert(type(s) == 'string', 'Only strings can be trimmed') +function vim.trim(s) + vim.validate{s={s,'s'}} return s:match('^%s*(.*%S)') or '' end ---- Escapes magic chars in a Lua pattern string. +--- Escapes magic chars in a Lua pattern. --- --@see https://github.com/rxi/lume --@param s String to escape --@returns %-escaped pattern string -local function pesc(s) - assert(type(s) == 'string') +function vim.pesc(s) + vim.validate{s={s,'s'}} return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end -local module = { - deepcopy = deepcopy, - gsplit = gsplit, - pesc = pesc, - split = split, - tbl_contains = tbl_contains, - tbl_extend = tbl_extend, - tbl_flatten = tbl_flatten, - trim = trim, -} -return module +--- Tests if `s` starts with `prefix`. +--- +--@param s (string) a string +--@param prefix (string) a prefix +--@return (boolean) true if `prefix` is a prefix of s +function vim.startswith(s, prefix) + vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } + return s:sub(1, #prefix) == prefix +end + +--- Tests if `s` ends with `suffix`. +--- +--@param s (string) a string +--@param suffix (string) a suffix +--@return (boolean) true if `suffix` is a suffix of s +function vim.endswith(s, suffix) + vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } + return #suffix == 0 or s:sub(-#suffix) == suffix +end + +--- Validates a parameter specification (types and values). +--- +--- Usage example: +--- <pre> +--- function user.new(name, age, hobbies) +--- vim.validate{ +--- name={name, 'string'}, +--- age={age, 'number'}, +--- hobbies={hobbies, 'table'}, +--- } +--- ... +--- end +--- </pre> +--- +--- Examples with explicit argument values (can be run directly): +--- <pre> +--- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} +--- => NOP (success) +--- +--- vim.validate{arg1={1, 'table'}} +--- => error('arg1: expected table, got number') +--- +--- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} +--- => error('arg1: expected even number, got 3') +--- </pre> +--- +--@param opt Map of parameter names to validations. Each key is a parameter +--- name; each value is a tuple in one of these forms: +--- 1. (arg_value, type_name, optional) +--- - arg_value: argument value +--- - type_name: string type name, one of: ("table", "t", "string", +--- "s", "number", "n", "boolean", "b", "function", "f", "nil", +--- "thread", "userdata") +--- - optional: (optional) boolean, if true, `nil` is valid +--- 2. (arg_value, fn, msg) +--- - arg_value: argument value +--- - fn: any function accepting one argument, returns true if and +--- only if the argument is valid +--- - msg: (optional) error string if validation fails +function vim.validate(opt) end -- luacheck: no unused +vim.validate = (function() + local type_names = { + t='table', s='string', n='number', b='boolean', f='function', c='callable', + ['table']='table', ['string']='string', ['number']='number', + ['boolean']='boolean', ['function']='function', ['callable']='callable', + ['nil']='nil', ['thread']='thread', ['userdata']='userdata', + } + local function _type_name(t) + local tname = type_names[t] + if tname == nil then + error(string.format('invalid type name: %s', tostring(t))) + end + return tname + end + local function _is_type(val, t) + return t == 'callable' and vim.is_callable(val) or type(val) == t + end + + return function(opt) + assert(type(opt) == 'table', string.format('opt: expected table, got %s', type(opt))) + for param_name, spec in pairs(opt) do + assert(type(spec) == 'table', string.format('%s: expected table, got %s', param_name, type(spec))) + + local val = spec[1] -- Argument value. + local t = spec[2] -- Type name, or callable. + local optional = (true == spec[3]) + + if not vim.is_callable(t) then -- Check type name. + if (not optional or val ~= nil) and not _is_type(val, _type_name(t)) then + error(string.format("%s: expected %s, got %s", param_name, _type_name(t), type(val))) + end + elseif not t(val) then -- Check user-provided validation function. + error(string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val)) + end + end + return true + end +end)() + +--- Returns true if object `f` can be called as a function. +--- +--@param f Any object +--@return true if `f` is callable, else false +function vim.is_callable(f) + if type(f) == 'function' then return true end + local m = getmetatable(f) + if m == nil then return false end + return type(m.__call) == 'function' +end + +return vim +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua new file mode 100644 index 0000000000..927456708c --- /dev/null +++ b/runtime/lua/vim/treesitter.lua @@ -0,0 +1,260 @@ +local a = vim.api + +-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. +-- Consider use weak references to release parser if all plugins are done with +-- it. +local parsers = {} + +local Parser = {} +Parser.__index = Parser + +function Parser:parse() + if self.valid then + return self.tree + end + local changes + self.tree, changes = self._parser:parse_buf(self.bufnr) + self.valid = true + + if not vim.tbl_isempty(changes) then + for _, cb in ipairs(self.changedtree_cbs) do + cb(changes) + end + end + + return self.tree, changes +end + +function Parser:_on_lines(bufnr, changed_tick, start_row, old_stop_row, stop_row, old_byte_size) + local start_byte = a.nvim_buf_get_offset(bufnr,start_row) + local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) + local old_stop_byte = start_byte + old_byte_size + self._parser:edit(start_byte,old_stop_byte,stop_byte, + start_row,0,old_stop_row,0,stop_row,0) + self.valid = false + + for _, cb in ipairs(self.lines_cbs) do + cb(bufnr, changed_tick, start_row, old_stop_row, stop_row, old_byte_size) + end +end + +function Parser:set_included_ranges(ranges) + self._parser:set_included_ranges(ranges) + -- The buffer will need to be parsed again later + self.valid = false +end + +local M = { + parse_query = vim._ts_parse_query, +} + +setmetatable(M, { + __index = function (t, k) + if k == "TSHighlighter" then + t[k] = require'vim.tshighlighter' + return t[k] + end + end + }) + +function M.require_language(lang, path) + if vim._ts_has_language(lang) then + return true + end + if path == nil then + local fname = 'parser/' .. lang .. '.*' + local paths = a.nvim_get_runtime_file(fname, false) + if #paths == 0 then + -- TODO(bfredl): help tag? + error("no parser for '"..lang.."' language") + end + path = paths[1] + end + vim._ts_add_language(path, lang) +end + +function M.inspect_language(lang) + M.require_language(lang) + return vim._ts_inspect_language(lang) +end + +function M.create_parser(bufnr, lang, id) + M.require_language(lang) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + + vim.fn.bufload(bufnr) + + local self = setmetatable({bufnr=bufnr, lang=lang, valid=false}, Parser) + self._parser = vim._create_ts_parser(lang) + self.changedtree_cbs = {} + self.lines_cbs = {} + self:parse() + -- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is + -- using it. + local function lines_cb(_, ...) + return self:_on_lines(...) + end + local detach_cb = nil + if id ~= nil then + detach_cb = function() + if parsers[id] == self then + parsers[id] = nil + end + end + end + a.nvim_buf_attach(self.bufnr, false, {on_lines=lines_cb, on_detach=detach_cb}) + return self +end + +function M.get_parser(bufnr, ft, buf_attach_cbs) + if bufnr == nil or bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + if ft == nil then + ft = a.nvim_buf_get_option(bufnr, "filetype") + end + local id = tostring(bufnr)..'_'..ft + + if parsers[id] == nil then + parsers[id] = M.create_parser(bufnr, ft, id) + end + + if buf_attach_cbs and buf_attach_cbs.on_changedtree then + table.insert(parsers[id].changedtree_cbs, buf_attach_cbs.on_changedtree) + end + + if buf_attach_cbs and buf_attach_cbs.on_lines then + table.insert(parsers[id].lines_cbs, buf_attach_cbs.on_lines) + end + + return parsers[id] +end + +-- query: pattern matching on trees +-- predicate matching is implemented in lua +local Query = {} +Query.__index = Query + +local magic_prefixes = {['\\v']=true, ['\\m']=true, ['\\M']=true, ['\\V']=true} +local function check_magic(str) + if string.len(str) < 2 or magic_prefixes[string.sub(str,1,2)] then + return str + end + return '\\v'..str +end + +function M.parse_query(lang, query) + M.require_language(lang) + local self = setmetatable({}, Query) + self.query = vim._ts_parse_query(lang, vim.fn.escape(query,'\\')) + self.info = self.query:inspect() + self.captures = self.info.captures + self.regexes = {} + for id,preds in pairs(self.info.patterns) do + local regexes = {} + for i, pred in ipairs(preds) do + if (pred[1] == "match?" and type(pred[2]) == "number" + and type(pred[3]) == "string") then + regexes[i] = vim.regex(check_magic(pred[3])) + end + end + if next(regexes) then + self.regexes[id] = regexes + end + end + return self +end + +local function get_node_text(node, bufnr) + local start_row, start_col, end_row, end_col = node:range() + if start_row ~= end_row then + return nil + end + local line = a.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1] + return string.sub(line, start_col+1, end_col) +end + +function Query:match_preds(match, pattern, bufnr) + local preds = self.info.patterns[pattern] + if not preds then + return true + end + local regexes = self.regexes[pattern] + for i, pred in pairs(preds) do + -- Here we only want to return if a predicate DOES NOT match, and + -- continue on the other case. This way unknown predicates will not be considered, + -- which allows some testing and easier user extensibility (#12173). + -- Also, tree-sitter strips the leading # from predicates for us. + if pred[1] == "eq?" then + local node = match[pred[2]] + local node_text = get_node_text(node, bufnr) + + local str + if type(pred[3]) == "string" then + -- (#eq? @aa "foo") + str = pred[3] + else + -- (#eq? @aa @bb) + str = get_node_text(match[pred[3]], bufnr) + end + + if node_text ~= str or str == nil then + return false + end + elseif pred[1] == "match?" then + if not regexes or not regexes[i] then + return false + end + local node = match[pred[2]] + local start_row, start_col, end_row, end_col = node:range() + if start_row ~= end_row then + return false + end + if not regexes[i]:match_line(bufnr, start_row, start_col, end_col) then + return false + end + end + end + return true +end + +function Query:iter_captures(node, bufnr, start, stop) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local raw_iter = node:_rawquery(self.query,true,start,stop) + local function iter() + local capture, captured_node, match = raw_iter() + if match ~= nil then + local active = self:match_preds(match, match.pattern, bufnr) + match.active = active + if not active then + return iter() -- tail call: try next match + end + end + return capture, captured_node + end + return iter +end + +function Query:iter_matches(node, bufnr, start, stop) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local raw_iter = node:_rawquery(self.query,false,start,stop) + local function iter() + local pattern, match = raw_iter() + if match ~= nil then + local active = self:match_preds(match, pattern, bufnr) + if not active then + return iter() -- tail call: try next match + end + end + return pattern, match + end + return iter +end + +return M diff --git a/runtime/lua/vim/tshighlighter.lua b/runtime/lua/vim/tshighlighter.lua new file mode 100644 index 0000000000..6465751ae8 --- /dev/null +++ b/runtime/lua/vim/tshighlighter.lua @@ -0,0 +1,116 @@ +local a = vim.api + +-- support reload for quick experimentation +local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} +TSHighlighter.__index = TSHighlighter +local ts_hs_ns = a.nvim_create_namespace("treesitter_hl") + +-- These are conventions defined by tree-sitter, though it +-- needs to be user extensible also. +-- TODO(bfredl): this is very much incomplete, we will need to +-- go through a few tree-sitter provided queries and decide +-- on translations that makes the most sense. +TSHighlighter.hl_map = { + keyword="Keyword", + string="String", + type="Type", + comment="Comment", + constant="Constant", + operator="Operator", + number="Number", + label="Label", + ["function"]="Function", + ["function.special"]="Function", +} + +function TSHighlighter.new(query, bufnr, ft) + local self = setmetatable({}, TSHighlighter) + self.parser = vim.treesitter.get_parser( + bufnr, + ft, + { + on_changedtree = function(...) self:on_changedtree(...) end, + on_lines = function() self.root = self.parser:parse():root() end + } + ) + + self.buf = self.parser.bufnr + + local tree = self.parser:parse() + self.root = tree:root() + self:set_query(query) + self.edit_count = 0 + self.redraw_count = 0 + self.line_count = {} + a.nvim_buf_set_option(self.buf, "syntax", "") + + -- Tricky: if syntax hasn't been enabled, we need to reload color scheme + -- but use synload.vim rather than syntax.vim to not enable + -- syntax FileType autocmds. Later on we should integrate with the + -- `:syntax` and `set syntax=...` machinery properly. + if vim.g.syntax_on ~= 1 then + vim.api.nvim_command("runtime! syntax/synload.vim") + end + return self +end + +local function is_highlight_name(capture_name) + local firstc = string.sub(capture_name, 1, 1) + return firstc ~= string.lower(firstc) +end + +function TSHighlighter:get_hl_from_capture(capture) + + local name = self.query.captures[capture] + + if is_highlight_name(name) then + -- From "Normal.left" only keep "Normal" + return vim.split(name, '.', true)[1] + else + -- Default to false to avoid recomputing + return TSHighlighter.hl_map[name] + end +end + +function TSHighlighter:set_query(query) + if type(query) == "string" then + query = vim.treesitter.parse_query(self.parser.lang, query) + end + self.query = query + + self.hl_cache = setmetatable({}, { + __index = function(table, capture) + local hl = self:get_hl_from_capture(capture) + rawset(table, capture, hl) + + return hl + end + }) + + self:on_changedtree({{self.root:range()}}) +end + +function TSHighlighter:on_changedtree(changes) + -- Get a fresh root + self.root = self.parser.tree:root() + + for _, ch in ipairs(changes or {}) do + -- Try to be as exact as possible + local changed_node = self.root:descendant_for_range(ch[1], ch[2], ch[3], ch[4]) + + a.nvim_buf_clear_namespace(self.buf, ts_hs_ns, ch[1], ch[3]) + + for capture, node in self.query:iter_captures(changed_node, self.buf, ch[1], ch[3] + 1) do + local start_row, start_col, end_row, end_col = node:range() + local hl = self.hl_cache[capture] + if hl then + a.nvim__buf_add_decoration(self.buf, ts_hs_ns, hl, + start_row, start_col, + end_row, end_col, + {}) + end + end + end +end + +return TSHighlighter diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua new file mode 100644 index 0000000000..9c3535c676 --- /dev/null +++ b/runtime/lua/vim/uri.lua @@ -0,0 +1,112 @@ +--- TODO: This is implemented only for files now. +-- https://tools.ietf.org/html/rfc3986 +-- https://tools.ietf.org/html/rfc2732 +-- https://tools.ietf.org/html/rfc2396 + + +local uri_decode +do + local schar = string.char + local function hex_to_char(hex) + return schar(tonumber(hex, 16)) + end + uri_decode = function(str) + return str:gsub("%%([a-fA-F0-9][a-fA-F0-9])", hex_to_char) + end +end + +local uri_encode +do + local PATTERNS = { + --- RFC 2396 + -- https://tools.ietf.org/html/rfc2396#section-2.2 + rfc2396 = "^A-Za-z0-9%-_.!~*'()"; + --- RFC 2732 + -- https://tools.ietf.org/html/rfc2732 + rfc2732 = "^A-Za-z0-9%-_.!~*'()[]"; + --- RFC 3986 + -- https://tools.ietf.org/html/rfc3986#section-2.2 + rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/"; + } + local sbyte, tohex = string.byte + if jit then + tohex = require'bit'.tohex + else + tohex = function(b) return string.format("%02x", b) end + end + local function percent_encode_char(char) + return "%"..tohex(sbyte(char), 2) + end + uri_encode = function(text, rfc) + if not text then return end + local pattern = PATTERNS[rfc] or PATTERNS.rfc3986 + return text:gsub("(["..pattern.."])", percent_encode_char) + end +end + + +local function is_windows_file_uri(uri) + return uri:match('^file:///[a-zA-Z]:') ~= nil +end + +local function uri_from_fname(path) + local volume_path, fname = path:match("^([a-zA-Z]:)(.*)") + local is_windows = volume_path ~= nil + if is_windows then + path = volume_path..uri_encode(fname:gsub("\\", "/")) + else + path = uri_encode(path) + end + local uri_parts = {"file://"} + if is_windows then + table.insert(uri_parts, "/") + end + table.insert(uri_parts, path) + return table.concat(uri_parts) +end + +local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*)://.*' + +local function uri_from_bufnr(bufnr) + local fname = vim.api.nvim_buf_get_name(bufnr) + local scheme = fname:match(URI_SCHEME_PATTERN) + if scheme then + return fname + else + return uri_from_fname(fname) + end +end + +local function uri_to_fname(uri) + local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) + if scheme ~= 'file' then + return uri + end + uri = uri_decode(uri) + -- TODO improve this. + if is_windows_file_uri(uri) then + uri = uri:gsub('^file:///', '') + uri = uri:gsub('/', '\\') + else + uri = uri:gsub('^file://', '') + end + return uri +end + +-- Return or create a buffer for a uri. +local function uri_to_bufnr(uri) + local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) + if scheme == 'file' then + return vim.fn.bufadd(uri_to_fname(uri)) + else + return vim.fn.bufadd(uri) + end +end + +return { + uri_from_fname = uri_from_fname, + uri_from_bufnr = uri_from_bufnr, + uri_to_fname = uri_to_fname, + uri_to_bufnr = uri_to_bufnr, +} +-- vim:sw=2 ts=2 et diff --git a/runtime/makemenu.vim b/runtime/makemenu.vim index ff29d59063..27fa46fe25 100644 --- a/runtime/makemenu.vim +++ b/runtime/makemenu.vim @@ -1,6 +1,6 @@ " Script to define the syntax menu in synmenu.vim " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2018 May 17 +" Last Change: 2019 Dec 07 " This is used by "make menu" in the src directory. edit <sfile>:p:h/synmenu.vim @@ -101,6 +101,7 @@ SynMenu AB.AYacc:ayacc SynMenu AB.B:b SynMenu AB.Baan:baan +SynMenu AB.Bash:bash SynMenu AB.Basic.FreeBasic:freebasic SynMenu AB.Basic.IBasic:ibasic SynMenu AB.Basic.QBasic:basic @@ -128,8 +129,9 @@ SynMenu C.Century\ Term:cterm SynMenu C.CH\ script:ch SynMenu C.ChaiScript:chaiscript SynMenu C.ChangeLog:changelog -SynMenu C.Cheetah\ template:cheetah SynMenu C.CHILL:chill +SynMenu C.Cheetah\ template:cheetah +SynMenu C.Chicken:chicken SynMenu C.ChordPro:chordpro SynMenu C.Clean:clean SynMenu C.Clever:cl @@ -160,6 +162,7 @@ SynMenu C.Cyn++:cynpp SynMenu C.Cynlib:cynlib SynMenu DE.D:d +SynMenu DE.Dart:dart SynMenu DE.Datascript:datascript SynMenu DE.Debian.Debian\ ChangeLog:debchangelog SynMenu DE.Debian.Debian\ Control:debcontrol @@ -192,12 +195,14 @@ SynMenu DE.DTD:dtd SynMenu DE.DTML\ (Zope):dtml SynMenu DE.DTrace:dtrace SynMenu DE.Dts/dtsi:dts +SynMenu DE.Dune:dune SynMenu DE.Dylan.Dylan:dylan SynMenu DE.Dylan.Dylan\ interface:dylanintr SynMenu DE.Dylan.Dylan\ lid:dylanlid SynMenu DE.EDIF:edif SynMenu DE.Eiffel:eiffel +SynMenu DE.Eight:8th SynMenu DE.Elinks\ config:elinks SynMenu DE.Elm\ filter\ rules:elmfilt SynMenu DE.Embedix\ Component\ Description:ecd @@ -307,6 +312,7 @@ SynMenu HIJK.Java.JavaCC:javacc SynMenu HIJK.Java.Java\ Server\ Pages:jsp SynMenu HIJK.Java.Java\ Properties:jproperties SynMenu HIJK.JavaScript:javascript +SynMenu HIJK.JavaScriptReact:javascriptreact SynMenu HIJK.Jess:jess SynMenu HIJK.Jgraph:jgraph SynMenu HIJK.Jovial:jovial @@ -365,6 +371,7 @@ SynMenu M.Mathematica:mma SynMenu M.Matlab:matlab SynMenu M.Maxima:maxima SynMenu M.MEL\ (for\ Maya):mel +SynMenu M.Meson:meson SynMenu M.Messages\ (/var/log):messages SynMenu M.Metafont:mf SynMenu M.MetaPost:mp @@ -467,6 +474,7 @@ SynMenu R.R.R\ help:rhelp SynMenu R.R.R\ noweb:rnoweb SynMenu R.Racc\ input:racc SynMenu R.Radiance:radiance +SynMenu R.Raml:raml SynMenu R.Ratpoison:ratpoison SynMenu R.RCS.RCS\ log\ output:rcslog SynMenu R.RCS.RCS\ file:rcs @@ -609,6 +617,8 @@ SynMenu T.Trustees:trustees SynMenu T.TSS.Command\ Line:tsscl SynMenu T.TSS.Geometry:tssgm SynMenu T.TSS.Optics:tssop +SynMenu T.Typescript:typescript +SynMenu T.TypescriptReact:typescriptreact SynMenu UV.Udev\ config:udevconf SynMenu UV.Udev\ permissions:udevperm @@ -637,6 +647,7 @@ SynMenu UV.VSE\ JCL:vsejcl SynMenu WXYZ.WEB.CWEB:cweb SynMenu WXYZ.WEB.WEB:web SynMenu WXYZ.WEB.WEB\ Changes:change +SynMenu WXYZ.WebAssembly:wast SynMenu WXYZ.Webmacro:webmacro SynMenu WXYZ.Website\ MetaLanguage:wml SynMenu WXYZ.wDiff:wdiff diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 32d3c523c6..025de1b5a9 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,10 @@ </screenshots> <releases> + <release date="2019-11-06" version="0.4.3"/> + <release date="2019-09-15" version="0.4.2"/> + <release date="2019-09-15" version="0.4.1"/> + <release date="2019-09-15" version="0.4.0"/> <release date="2019-07-03" version="0.3.8"/> <release date="2019-04-29" version="0.3.5"/> <release date="2019-01-13" version="0.3.4"/> diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 51b2bbb583..6b3328a5d4 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -300,6 +300,11 @@ call append("$", "tagstack\ta :tag command will use the tagstack") call <SID>BinOptionG("tgst", &tgst) call append("$", "showfulltag\twhen completing tags in Insert mode show more info") call <SID>BinOptionG("sft", &sft) +if has("eval") + call append("$", "tagfunc\ta function to be used to perform tag searches") + call append("$", "\t(local to buffer)") + call <SID>OptionL("tfu") +endif if has("cscope") call append("$", "cscopeprg\tcommand for executing cscope") call <SID>OptionG("csprg", &csprg) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index a97461ad69..6870bcec75 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -37,7 +37,7 @@ " For neovim compatibility, the vim specific calls were replaced with neovim " specific calls: " term_start -> term_open -" term_sendkeys -> jobsend +" term_sendkeys -> chansend " term_getline -> getbufline " job_info && term_getjob -> using linux command ps to get the tty " balloon -> nvim floating window @@ -47,8 +47,6 @@ " https://github.com/autozimu/LanguageClient-neovim/blob/0ed9b69dca49c415390a8317b19149f97ae093fa/autoload/LanguageClient.vim#L304 " " Neovim terminal also works seamlessly on windows, which is why the ability -" to use the prompt buffer was removed. -" " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" @@ -57,6 +55,12 @@ if exists(':Termdebug') finish endif +" The terminal feature does not work with gdb on win32. +if !has('win32') + let s:way = 'terminal' +else + let s:way = 'prompt' +endif let s:keepcpo = &cpo set cpo&vim @@ -67,8 +71,8 @@ command -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-ar command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) " Name of the gdb command, defaults to "gdb". -if !exists('termdebugger') - let termdebugger = 'gdb' +if !exists('g:termdebugger') + let g:termdebugger = 'gdb' endif let s:pc_id = 12 @@ -106,9 +110,14 @@ endfunc func s:StartDebug_internal(dict) if exists('s:gdbwin') - echoerr 'Terminal debugger already running' + echoerr 'Terminal debugger already running, cannot run two' + return + endif + if !executable(g:termdebugger) + echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' return endif + let s:ptywin = 0 let s:pid = 0 @@ -133,7 +142,19 @@ func s:StartDebug_internal(dict) let s:vertical = 0 endif - call s:StartDebug_term(a:dict) + " Override using a terminal window by setting g:termdebug_use_prompt to 1. + let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt + if !has('win32') && !use_prompt + let s:way = 'terminal' + else + let s:way = 'prompt' + endif + + if s:way == 'prompt' + call s:StartDebug_prompt(a:dict) + else + call s:StartDebug_term(a:dict) + endif endfunc " Use when debugger didn't start or ended. @@ -209,11 +230,11 @@ func s:StartDebug_term(dict) " Set arguments to be run if len(proc_args) - call jobsend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") + call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") endif " Connect gdb to the communication pty, using the GDB/MI interface - call jobsend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") + call chansend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") " Wait for the response to show up, users may not notice the error and wonder " why the debugger doesn't work. @@ -226,10 +247,12 @@ func s:StartDebug_term(dict) endif let response = '' - for lnum in range(1,200) - if len(getbufline(s:gdbbuf, lnum)) > 0 && getbufline(s:gdbbuf, lnum)[0] =~ 'new-ui mi ' + for lnum in range(1, 200) + let line1 = get(getbufline(s:gdbbuf, lnum), 0, '') + let line2 = get(getbufline(s:gdbbuf, lnum + 1), 0, '') + if line1 =~ 'new-ui mi ' " response can be in the same line or the next line - let response = getbufline(s:gdbbuf, lnum)[0] . getbufline(s:gdbbuf, lnum + 1)[0] + let response = line1 . line2 if response =~ 'Undefined command' echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' call s:CloseBuffers() @@ -239,10 +262,9 @@ func s:StartDebug_term(dict) " Success! break endif - if response =~ 'Reading symbols from' && response !~ 'new-ui' - " Reading symbols might take a while - let try_count -= 1 - endif + elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' + " Reading symbols might take a while, try more times + let try_count -= 1 endif endfor if response =~ 'New UI allocated' @@ -270,6 +292,100 @@ func s:StartDebug_term(dict) call s:StartDebugCommon(a:dict) endfunc +func s:StartDebug_prompt(dict) + " Open a window with a prompt buffer to run gdb in. + if s:vertical + vertical new + else + new + endif + let s:gdbwin = win_getid(winnr()) + let s:promptbuf = bufnr('') + call prompt_setprompt(s:promptbuf, 'gdb> ') + set buftype=prompt + file gdb + call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) + call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) + + if s:vertical + " Assuming the source code window will get a signcolumn, use two more + " columns for that, thus one less for the terminal window. + exe (&columns / 2 - 1) . "wincmd |" + endif + + " Add -quiet to avoid the intro message causing a hit-enter prompt. + let gdb_args = get(a:dict, 'gdb_args', []) + let proc_args = get(a:dict, 'proc_args', []) + + let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args + "call ch_log('executing "' . join(cmd) . '"') + + let s:gdbjob = jobstart(cmd, { + \ 'on_exit': function('s:EndPromptDebug'), + \ 'on_stdout': function('s:GdbOutCallback'), + \ }) + if s:gdbjob == 0 + echoerr 'invalid argument (or job table is full) while starting gdb job' + exe 'bwipe! ' . s:ptybuf + return + elseif s:gdbjob == -1 + echoerr 'Failed to start the gdb job' + call s:CloseBuffers() + return + endif + + " Interpret commands while the target is running. This should usualy only + " be exec-interrupt, since many commands don't work properly while the + " target is running. + call s:SendCommand('-gdb-set mi-async on') + " Older gdb uses a different command. + call s:SendCommand('-gdb-set target-async on') + + let s:ptybuf = 0 + if has('win32') + " MS-Windows: run in a new console window for maximum compatibility + call s:SendCommand('set new-console on') + else + " Unix: Run the debugged program in a terminal window. Open it below the + " gdb window. + execute 'new' + wincmd x | wincmd j + belowright let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') + if s:pty_job_id == 0 + echoerr 'invalid argument (or job table is full) while opening terminal window' + return + elseif s:pty_job_id == -1 + echoerr 'Failed to open the program terminal window' + return + endif + let pty_job_info = nvim_get_chan_info(s:pty_job_id) + let s:ptybuf = pty_job_info['buffer'] + let pty = pty_job_info['pty'] + let s:ptywin = win_getid(winnr()) + call s:SendCommand('tty ' . pty) + + " Since GDB runs in a prompt window, the environment has not been set to + " match a terminal window, need to do that now. + call s:SendCommand('set env TERM = xterm-color') + call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) + call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) + call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) + call s:SendCommand('set env COLORS = ' . &t_Co) + call s:SendCommand('set env VIM_TERMINAL = ' . v:version) + endif + call s:SendCommand('set print pretty on') + call s:SendCommand('set breakpoint pending on') + " Disable pagination, it causes everything to stop at the gdb + call s:SendCommand('set pagination off') + + " Set arguments to be run + if len(proc_args) + call s:SendCommand('set args ' . join(proc_args)) + endif + + call s:StartDebugCommon(a:dict) + startinsert +endfunc func s:StartDebugCommon(dict) " Sign used to highlight the line where the program has stopped. @@ -311,23 +427,104 @@ endfunc " Send a command to gdb. "cmd" is the string without line terminator. func s:SendCommand(cmd) "call ch_log('sending to gdb: ' . a:cmd) - call jobsend(s:comm_job_id, a:cmd . "\r") + if s:way == 'prompt' + call chansend(s:gdbjob, a:cmd . "\n") + else + call chansend(s:comm_job_id, a:cmd . "\r") + endif endfunc " This is global so that a user can create their mappings with this. func TermDebugSendCommand(cmd) - let do_continue = 0 - if !s:stopped - let do_continue = 1 - call s:SendCommand('-exec-interrupt') - sleep 10m + if s:way == 'prompt' + call chansend(s:gdbjob, a:cmd . "\n") + else + let do_continue = 0 + if !s:stopped + let do_continue = 1 + if s:way == 'prompt' + " Need to send a signal to get the UI to listen. Strangely this is only + " needed once. + call jobstop(s:gdbjob) + else + call s:SendCommand('-exec-interrupt') + endif + sleep 10m + endif + call chansend(s:gdb_job_id, a:cmd . "\r") + if do_continue + Continue + endif endif - call jobsend(s:gdb_job_id, a:cmd . "\r") - if do_continue - Continue +endfunc + +" Function called when entering a line in the prompt buffer. +func s:PromptCallback(text) + call s:SendCommand(a:text) +endfunc + +" Function called when pressing CTRL-C in the prompt buffer and when placing a +" breakpoint. +func s:PromptInterrupt() + " call ch_log('Interrupting gdb') + if has('win32') + " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to + " the debugger program so that gdb responds again. + if s:pid == 0 + echoerr 'Cannot interrupt gdb, did not find a process ID' + else + call debugbreak(s:pid) + endif + else + call jobstop(s:gdbjob) endif endfunc +" Function called when gdb outputs text. +func s:GdbOutCallback(job_id, msgs, event) + "call ch_log('received from gdb: ' . a:text) + + " Drop the gdb prompt, we have our own. + " Drop status and echo'd commands. + call filter(a:msgs, { index, val -> + \ val !=# '(gdb)' && val !=# '^done' && val[0] !=# '&'}) + + let lines = [] + let index = 0 + + for msg in a:msgs + if msg =~ '^^error,msg=' + if exists('s:evalexpr') + \ && s:DecodeMessage(msg[11:]) + \ =~ 'A syntax error in expression, near\|No symbol .* in current context' + " Silently drop evaluation errors. + call remove(a:msgs, index) + unlet s:evalexpr + continue + endif + elseif msg[0] == '~' + call add(lines, s:DecodeMessage(msg[1:])) + call remove(a:msgs, index) + continue + endif + let index += 1 + endfor + + let curwinid = win_getid(winnr()) + call win_gotoid(s:gdbwin) + + " Add the output above the current prompt. + for line in lines + call append(line('$') - 1, line) + endfor + if !empty(lines) + set modified + endif + + call win_gotoid(curwinid) + call s:CommOutput(a:job_id, a:msgs, a:event) +endfunc + " Decode a message from gdb. quotedText starts with a ", return the text up " to the next ", unescaping characters. func s:DecodeMessage(quotedText) @@ -391,6 +588,19 @@ func s:EndDebugCommon() au! TermDebug endfunc +func s:EndPromptDebug(job_id, exit_code, event) + let curwinid = win_getid(winnr()) + call win_gotoid(s:gdbwin) + close + if curwinid != s:gdbwin + call win_gotoid(curwinid) + endif + + call s:EndDebugCommon() + unlet s:gdbwin + "call ch_log("Returning from EndPromptDebug()") +endfunc + func s:CommOutput(job_id, msgs, event) for msg in a:msgs @@ -431,7 +641,11 @@ func s:InstallCommands() command Stop call s:SendCommand('-exec-interrupt') " using -exec-continue results in CTRL-C in gdb window not working - command Continue call jobsend(s:gdb_job_id, "continue\r") + if s:way == 'prompt' + command Continue call s:SendCommand('continue') + else + command Continue call chansend(s:gdb_job_id, "continue\r") + endif command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) command Gdb call win_gotoid(s:gdbwin) @@ -445,7 +659,20 @@ func s:InstallCommands() let &cpo = save_cpo endfunc -let s:winbar_winids = [] +" let s:winbar_winids = [] + +" Install the window toolbar in the current window. +func s:InstallWinbar() + " if has('menu') && &mouse != '' + " nnoremenu WinBar.Step :Step<CR> + " nnoremenu WinBar.Next :Over<CR> + " nnoremenu WinBar.Finish :Finish<CR> + " nnoremenu WinBar.Cont :Continue<CR> + " nnoremenu WinBar.Stop :Stop<CR> + " nnoremenu WinBar.Eval :Evaluate<CR> + " call add(s:winbar_winids, win_getid(winnr())) + " endif +endfunc " Delete installed debugger commands in the current window. func s:DeleteCommands() @@ -489,7 +716,11 @@ func s:SetBreakpoint() let do_continue = 0 if !s:stopped let do_continue = 1 - call s:SendCommand('-exec-interrupt') + if s:way == 'prompt' + call s:PromptInterrupt() + else + call s:SendCommand('-exec-interrupt') + endif sleep 10m endif " Use the fname:lnum format, older gdb can't handle --source. @@ -578,6 +809,7 @@ func s:HandleEvaluate(msg) endif let s:evalFromBalloonExprResult = split(s:evalFromBalloonExprResult, '\\n') call s:OpenHoverPreview(s:evalFromBalloonExprResult, v:null) + let s:evalFromBalloonExprResult = '' else echomsg '"' . s:evalexpr . '": ' . value endif diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index e18a5528bb..e762eb3664 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> if exists('g:loaded_man') finish diff --git a/runtime/scripts.vim b/runtime/scripts.vim index a690431014..6aae2b1ec3 100644 --- a/runtime/scripts.vim +++ b/runtime/scripts.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types in scripts " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last change: 2019 Jun 25 +" Last change: 2020 Jun 07 " This file is called by an autocommand for every file that has just been " loaded into a buffer. It checks if the type of file can be recognized by @@ -35,10 +35,12 @@ let s:line1 = getline(1) if s:line1 =~# "^#!" " A script that starts with "#!". - " Check for a line like "#!/usr/bin/env VAR=val bash". Turn it into + " Check for a line like "#!/usr/bin/env {options} bash". Turn it into " "#!/usr/bin/bash" to make matching easier. + " Recognize only a few {options} that are commonly used. if s:line1 =~# '^#!\s*\S*\<env\s' let s:line1 = substitute(s:line1, '\S\+=\S\+', '', 'g') + let s:line1 = substitute(s:line1, '\(-[iS]\|--ignore-environment\|--split-string\)', '', '') let s:line1 = substitute(s:line1, '\<env\s\+', '', '') endif @@ -376,6 +378,10 @@ else elseif s:line1 =~? '-\*-.*erlang.*-\*-' set ft=erlang + " YAML + elseif s:line1 =~# '^%YAML' + set ft=yaml + " CVS diff else let s:lnum = 1 diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim index 70e0e83ba7..3367c68d3b 100644 --- a/runtime/synmenu.vim +++ b/runtime/synmenu.vim @@ -87,19 +87,20 @@ an 50.10.580 &Syntax.AB.Awk :cal SetSyn("awk")<CR> an 50.10.590 &Syntax.AB.AYacc :cal SetSyn("ayacc")<CR> an 50.10.610 &Syntax.AB.B :cal SetSyn("b")<CR> an 50.10.620 &Syntax.AB.Baan :cal SetSyn("baan")<CR> -an 50.10.630 &Syntax.AB.Basic.FreeBasic :cal SetSyn("freebasic")<CR> -an 50.10.640 &Syntax.AB.Basic.IBasic :cal SetSyn("ibasic")<CR> -an 50.10.650 &Syntax.AB.Basic.QBasic :cal SetSyn("basic")<CR> -an 50.10.660 &Syntax.AB.Basic.Visual\ Basic :cal SetSyn("vb")<CR> -an 50.10.670 &Syntax.AB.Bazaar\ commit\ file :cal SetSyn("bzr")<CR> -an 50.10.680 &Syntax.AB.Bazel :cal SetSyn("bzl")<CR> -an 50.10.690 &Syntax.AB.BC\ calculator :cal SetSyn("bc")<CR> -an 50.10.700 &Syntax.AB.BDF\ font :cal SetSyn("bdf")<CR> -an 50.10.710 &Syntax.AB.BibTeX.Bibliography\ database :cal SetSyn("bib")<CR> -an 50.10.720 &Syntax.AB.BibTeX.Bibliography\ Style :cal SetSyn("bst")<CR> -an 50.10.730 &Syntax.AB.BIND.BIND\ config :cal SetSyn("named")<CR> -an 50.10.740 &Syntax.AB.BIND.BIND\ zone :cal SetSyn("bindzone")<CR> -an 50.10.750 &Syntax.AB.Blank :cal SetSyn("blank")<CR> +an 50.10.630 &Syntax.AB.Bash :cal SetSyn("bash")<CR> +an 50.10.640 &Syntax.AB.Basic.FreeBasic :cal SetSyn("freebasic")<CR> +an 50.10.650 &Syntax.AB.Basic.IBasic :cal SetSyn("ibasic")<CR> +an 50.10.660 &Syntax.AB.Basic.QBasic :cal SetSyn("basic")<CR> +an 50.10.670 &Syntax.AB.Basic.Visual\ Basic :cal SetSyn("vb")<CR> +an 50.10.680 &Syntax.AB.Bazaar\ commit\ file :cal SetSyn("bzr")<CR> +an 50.10.690 &Syntax.AB.Bazel :cal SetSyn("bzl")<CR> +an 50.10.700 &Syntax.AB.BC\ calculator :cal SetSyn("bc")<CR> +an 50.10.710 &Syntax.AB.BDF\ font :cal SetSyn("bdf")<CR> +an 50.10.720 &Syntax.AB.BibTeX.Bibliography\ database :cal SetSyn("bib")<CR> +an 50.10.730 &Syntax.AB.BibTeX.Bibliography\ Style :cal SetSyn("bst")<CR> +an 50.10.740 &Syntax.AB.BIND.BIND\ config :cal SetSyn("named")<CR> +an 50.10.750 &Syntax.AB.BIND.BIND\ zone :cal SetSyn("bindzone")<CR> +an 50.10.760 &Syntax.AB.Blank :cal SetSyn("blank")<CR> an 50.20.100 &Syntax.C.C :cal SetSyn("c")<CR> an 50.20.110 &Syntax.C.C++ :cal SetSyn("cpp")<CR> an 50.20.120 &Syntax.C.C# :cal SetSyn("cs")<CR> @@ -113,89 +114,93 @@ an 50.20.190 &Syntax.C.Century\ Term :cal SetSyn("cterm")<CR> an 50.20.200 &Syntax.C.CH\ script :cal SetSyn("ch")<CR> an 50.20.210 &Syntax.C.ChaiScript :cal SetSyn("chaiscript")<CR> an 50.20.220 &Syntax.C.ChangeLog :cal SetSyn("changelog")<CR> -an 50.20.230 &Syntax.C.Cheetah\ template :cal SetSyn("cheetah")<CR> -an 50.20.240 &Syntax.C.CHILL :cal SetSyn("chill")<CR> -an 50.20.250 &Syntax.C.ChordPro :cal SetSyn("chordpro")<CR> -an 50.20.260 &Syntax.C.Clean :cal SetSyn("clean")<CR> -an 50.20.270 &Syntax.C.Clever :cal SetSyn("cl")<CR> -an 50.20.280 &Syntax.C.Clipper :cal SetSyn("clipper")<CR> -an 50.20.290 &Syntax.C.Clojure :cal SetSyn("clojure")<CR> -an 50.20.300 &Syntax.C.Cmake :cal SetSyn("cmake")<CR> -an 50.20.310 &Syntax.C.Cmod :cal SetSyn("cmod")<CR> -an 50.20.320 &Syntax.C.Cmusrc :cal SetSyn("cmusrc")<CR> -an 50.20.330 &Syntax.C.Cobol :cal SetSyn("cobol")<CR> -an 50.20.340 &Syntax.C.Coco/R :cal SetSyn("coco")<CR> -an 50.20.350 &Syntax.C.Cold\ Fusion :cal SetSyn("cf")<CR> -an 50.20.360 &Syntax.C.Conary\ Recipe :cal SetSyn("conaryrecipe")<CR> -an 50.20.370 &Syntax.C.Config.Cfg\ Config\ file :cal SetSyn("cfg")<CR> -an 50.20.380 &Syntax.C.Config.Configure\.in :cal SetSyn("config")<CR> -an 50.20.390 &Syntax.C.Config.Generic\ Config\ file :cal SetSyn("conf")<CR> -an 50.20.400 &Syntax.C.CRM114 :cal SetSyn("crm")<CR> -an 50.20.410 &Syntax.C.Crontab :cal SetSyn("crontab")<CR> -an 50.20.420 &Syntax.C.CSDL :cal SetSyn("csdl")<CR> -an 50.20.430 &Syntax.C.CSP :cal SetSyn("csp")<CR> -an 50.20.440 &Syntax.C.Ctrl-H :cal SetSyn("ctrlh")<CR> -an 50.20.450 &Syntax.C.Cucumber :cal SetSyn("cucumber")<CR> -an 50.20.460 &Syntax.C.CUDA :cal SetSyn("cuda")<CR> -an 50.20.470 &Syntax.C.CUPL.CUPL :cal SetSyn("cupl")<CR> -an 50.20.480 &Syntax.C.CUPL.Simulation :cal SetSyn("cuplsim")<CR> -an 50.20.490 &Syntax.C.CVS.commit\ file :cal SetSyn("cvs")<CR> -an 50.20.500 &Syntax.C.CVS.cvsrc :cal SetSyn("cvsrc")<CR> -an 50.20.510 &Syntax.C.Cyn++ :cal SetSyn("cynpp")<CR> -an 50.20.520 &Syntax.C.Cynlib :cal SetSyn("cynlib")<CR> +an 50.20.230 &Syntax.C.CHILL :cal SetSyn("chill")<CR> +an 50.20.240 &Syntax.C.Cheetah\ template :cal SetSyn("cheetah")<CR> +an 50.20.250 &Syntax.C.Chicken :cal SetSyn("chicken")<CR> +an 50.20.260 &Syntax.C.ChordPro :cal SetSyn("chordpro")<CR> +an 50.20.270 &Syntax.C.Clean :cal SetSyn("clean")<CR> +an 50.20.280 &Syntax.C.Clever :cal SetSyn("cl")<CR> +an 50.20.290 &Syntax.C.Clipper :cal SetSyn("clipper")<CR> +an 50.20.300 &Syntax.C.Clojure :cal SetSyn("clojure")<CR> +an 50.20.310 &Syntax.C.Cmake :cal SetSyn("cmake")<CR> +an 50.20.320 &Syntax.C.Cmod :cal SetSyn("cmod")<CR> +an 50.20.330 &Syntax.C.Cmusrc :cal SetSyn("cmusrc")<CR> +an 50.20.340 &Syntax.C.Cobol :cal SetSyn("cobol")<CR> +an 50.20.350 &Syntax.C.Coco/R :cal SetSyn("coco")<CR> +an 50.20.360 &Syntax.C.Cold\ Fusion :cal SetSyn("cf")<CR> +an 50.20.370 &Syntax.C.Conary\ Recipe :cal SetSyn("conaryrecipe")<CR> +an 50.20.380 &Syntax.C.Config.Cfg\ Config\ file :cal SetSyn("cfg")<CR> +an 50.20.390 &Syntax.C.Config.Configure\.in :cal SetSyn("config")<CR> +an 50.20.400 &Syntax.C.Config.Generic\ Config\ file :cal SetSyn("conf")<CR> +an 50.20.410 &Syntax.C.CRM114 :cal SetSyn("crm")<CR> +an 50.20.420 &Syntax.C.Crontab :cal SetSyn("crontab")<CR> +an 50.20.430 &Syntax.C.CSDL :cal SetSyn("csdl")<CR> +an 50.20.440 &Syntax.C.CSP :cal SetSyn("csp")<CR> +an 50.20.450 &Syntax.C.Ctrl-H :cal SetSyn("ctrlh")<CR> +an 50.20.460 &Syntax.C.Cucumber :cal SetSyn("cucumber")<CR> +an 50.20.470 &Syntax.C.CUDA :cal SetSyn("cuda")<CR> +an 50.20.480 &Syntax.C.CUPL.CUPL :cal SetSyn("cupl")<CR> +an 50.20.490 &Syntax.C.CUPL.Simulation :cal SetSyn("cuplsim")<CR> +an 50.20.500 &Syntax.C.CVS.commit\ file :cal SetSyn("cvs")<CR> +an 50.20.510 &Syntax.C.CVS.cvsrc :cal SetSyn("cvsrc")<CR> +an 50.20.520 &Syntax.C.Cyn++ :cal SetSyn("cynpp")<CR> +an 50.20.530 &Syntax.C.Cynlib :cal SetSyn("cynlib")<CR> an 50.30.100 &Syntax.DE.D :cal SetSyn("d")<CR> -an 50.30.110 &Syntax.DE.Datascript :cal SetSyn("datascript")<CR> -an 50.30.120 &Syntax.DE.Debian.Debian\ ChangeLog :cal SetSyn("debchangelog")<CR> -an 50.30.130 &Syntax.DE.Debian.Debian\ Control :cal SetSyn("debcontrol")<CR> -an 50.30.140 &Syntax.DE.Debian.Debian\ Copyright :cal SetSyn("debcopyright")<CR> -an 50.30.150 &Syntax.DE.Debian.Debian\ Sources\.list :cal SetSyn("debsources")<CR> -an 50.30.160 &Syntax.DE.Denyhosts :cal SetSyn("denyhosts")<CR> -an 50.30.170 &Syntax.DE.Desktop :cal SetSyn("desktop")<CR> -an 50.30.180 &Syntax.DE.Dict\ config :cal SetSyn("dictconf")<CR> -an 50.30.190 &Syntax.DE.Dictd\ config :cal SetSyn("dictdconf")<CR> -an 50.30.200 &Syntax.DE.Diff :cal SetSyn("diff")<CR> -an 50.30.210 &Syntax.DE.Digital\ Command\ Lang :cal SetSyn("dcl")<CR> -an 50.30.220 &Syntax.DE.Dircolors :cal SetSyn("dircolors")<CR> -an 50.30.230 &Syntax.DE.Dirpager :cal SetSyn("dirpager")<CR> -an 50.30.240 &Syntax.DE.Django\ template :cal SetSyn("django")<CR> -an 50.30.250 &Syntax.DE.DNS/BIND\ zone :cal SetSyn("bindzone")<CR> -an 50.30.260 &Syntax.DE.Dnsmasq\ config :cal SetSyn("dnsmasq")<CR> -an 50.30.270 &Syntax.DE.DocBook.auto-detect :cal SetSyn("docbk")<CR> -an 50.30.280 &Syntax.DE.DocBook.SGML :cal SetSyn("docbksgml")<CR> -an 50.30.290 &Syntax.DE.DocBook.XML :cal SetSyn("docbkxml")<CR> -an 50.30.300 &Syntax.DE.Dockerfile :cal SetSyn("dockerfile")<CR> -an 50.30.310 &Syntax.DE.Dot :cal SetSyn("dot")<CR> -an 50.30.320 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen")<CR> -an 50.30.330 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen")<CR> -an 50.30.340 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen")<CR> -an 50.30.350 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen")<CR> -an 50.30.360 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen")<CR> -an 50.30.370 &Syntax.DE.Dracula :cal SetSyn("dracula")<CR> -an 50.30.380 &Syntax.DE.DSSSL :cal SetSyn("dsl")<CR> -an 50.30.390 &Syntax.DE.DTD :cal SetSyn("dtd")<CR> -an 50.30.400 &Syntax.DE.DTML\ (Zope) :cal SetSyn("dtml")<CR> -an 50.30.410 &Syntax.DE.DTrace :cal SetSyn("dtrace")<CR> -an 50.30.420 &Syntax.DE.Dts/dtsi :cal SetSyn("dts")<CR> -an 50.30.430 &Syntax.DE.Dylan.Dylan :cal SetSyn("dylan")<CR> -an 50.30.440 &Syntax.DE.Dylan.Dylan\ interface :cal SetSyn("dylanintr")<CR> -an 50.30.450 &Syntax.DE.Dylan.Dylan\ lid :cal SetSyn("dylanlid")<CR> -an 50.30.470 &Syntax.DE.EDIF :cal SetSyn("edif")<CR> -an 50.30.480 &Syntax.DE.Eiffel :cal SetSyn("eiffel")<CR> -an 50.30.490 &Syntax.DE.Elinks\ config :cal SetSyn("elinks")<CR> -an 50.30.500 &Syntax.DE.Elm\ filter\ rules :cal SetSyn("elmfilt")<CR> -an 50.30.510 &Syntax.DE.Embedix\ Component\ Description :cal SetSyn("ecd")<CR> -an 50.30.520 &Syntax.DE.ERicsson\ LANGuage :cal SetSyn("erlang")<CR> -an 50.30.530 &Syntax.DE.ESMTP\ rc :cal SetSyn("esmtprc")<CR> -an 50.30.540 &Syntax.DE.ESQL-C :cal SetSyn("esqlc")<CR> -an 50.30.550 &Syntax.DE.Essbase\ script :cal SetSyn("csc")<CR> -an 50.30.560 &Syntax.DE.Esterel :cal SetSyn("esterel")<CR> -an 50.30.570 &Syntax.DE.Eterm\ config :cal SetSyn("eterm")<CR> -an 50.30.580 &Syntax.DE.Euphoria\ 3 :cal SetSyn("euphoria3")<CR> -an 50.30.590 &Syntax.DE.Euphoria\ 4 :cal SetSyn("euphoria4")<CR> -an 50.30.600 &Syntax.DE.Eviews :cal SetSyn("eviews")<CR> -an 50.30.610 &Syntax.DE.Exim\ conf :cal SetSyn("exim")<CR> -an 50.30.620 &Syntax.DE.Expect :cal SetSyn("expect")<CR> -an 50.30.630 &Syntax.DE.Exports :cal SetSyn("exports")<CR> +an 50.30.110 &Syntax.DE.Dart :cal SetSyn("dart")<CR> +an 50.30.120 &Syntax.DE.Datascript :cal SetSyn("datascript")<CR> +an 50.30.130 &Syntax.DE.Debian.Debian\ ChangeLog :cal SetSyn("debchangelog")<CR> +an 50.30.140 &Syntax.DE.Debian.Debian\ Control :cal SetSyn("debcontrol")<CR> +an 50.30.150 &Syntax.DE.Debian.Debian\ Copyright :cal SetSyn("debcopyright")<CR> +an 50.30.160 &Syntax.DE.Debian.Debian\ Sources\.list :cal SetSyn("debsources")<CR> +an 50.30.170 &Syntax.DE.Denyhosts :cal SetSyn("denyhosts")<CR> +an 50.30.180 &Syntax.DE.Desktop :cal SetSyn("desktop")<CR> +an 50.30.190 &Syntax.DE.Dict\ config :cal SetSyn("dictconf")<CR> +an 50.30.200 &Syntax.DE.Dictd\ config :cal SetSyn("dictdconf")<CR> +an 50.30.210 &Syntax.DE.Diff :cal SetSyn("diff")<CR> +an 50.30.220 &Syntax.DE.Digital\ Command\ Lang :cal SetSyn("dcl")<CR> +an 50.30.230 &Syntax.DE.Dircolors :cal SetSyn("dircolors")<CR> +an 50.30.240 &Syntax.DE.Dirpager :cal SetSyn("dirpager")<CR> +an 50.30.250 &Syntax.DE.Django\ template :cal SetSyn("django")<CR> +an 50.30.260 &Syntax.DE.DNS/BIND\ zone :cal SetSyn("bindzone")<CR> +an 50.30.270 &Syntax.DE.Dnsmasq\ config :cal SetSyn("dnsmasq")<CR> +an 50.30.280 &Syntax.DE.DocBook.auto-detect :cal SetSyn("docbk")<CR> +an 50.30.290 &Syntax.DE.DocBook.SGML :cal SetSyn("docbksgml")<CR> +an 50.30.300 &Syntax.DE.DocBook.XML :cal SetSyn("docbkxml")<CR> +an 50.30.310 &Syntax.DE.Dockerfile :cal SetSyn("dockerfile")<CR> +an 50.30.320 &Syntax.DE.Dot :cal SetSyn("dot")<CR> +an 50.30.330 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen")<CR> +an 50.30.340 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen")<CR> +an 50.30.350 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen")<CR> +an 50.30.360 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen")<CR> +an 50.30.370 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen")<CR> +an 50.30.380 &Syntax.DE.Dracula :cal SetSyn("dracula")<CR> +an 50.30.390 &Syntax.DE.DSSSL :cal SetSyn("dsl")<CR> +an 50.30.400 &Syntax.DE.DTD :cal SetSyn("dtd")<CR> +an 50.30.410 &Syntax.DE.DTML\ (Zope) :cal SetSyn("dtml")<CR> +an 50.30.420 &Syntax.DE.DTrace :cal SetSyn("dtrace")<CR> +an 50.30.430 &Syntax.DE.Dts/dtsi :cal SetSyn("dts")<CR> +an 50.30.440 &Syntax.DE.Dune :cal SetSyn("dune")<CR> +an 50.30.450 &Syntax.DE.Dylan.Dylan :cal SetSyn("dylan")<CR> +an 50.30.460 &Syntax.DE.Dylan.Dylan\ interface :cal SetSyn("dylanintr")<CR> +an 50.30.470 &Syntax.DE.Dylan.Dylan\ lid :cal SetSyn("dylanlid")<CR> +an 50.30.490 &Syntax.DE.EDIF :cal SetSyn("edif")<CR> +an 50.30.500 &Syntax.DE.Eiffel :cal SetSyn("eiffel")<CR> +an 50.30.510 &Syntax.DE.Eight :cal SetSyn("8th")<CR> +an 50.30.520 &Syntax.DE.Elinks\ config :cal SetSyn("elinks")<CR> +an 50.30.530 &Syntax.DE.Elm\ filter\ rules :cal SetSyn("elmfilt")<CR> +an 50.30.540 &Syntax.DE.Embedix\ Component\ Description :cal SetSyn("ecd")<CR> +an 50.30.550 &Syntax.DE.ERicsson\ LANGuage :cal SetSyn("erlang")<CR> +an 50.30.560 &Syntax.DE.ESMTP\ rc :cal SetSyn("esmtprc")<CR> +an 50.30.570 &Syntax.DE.ESQL-C :cal SetSyn("esqlc")<CR> +an 50.30.580 &Syntax.DE.Essbase\ script :cal SetSyn("csc")<CR> +an 50.30.590 &Syntax.DE.Esterel :cal SetSyn("esterel")<CR> +an 50.30.600 &Syntax.DE.Eterm\ config :cal SetSyn("eterm")<CR> +an 50.30.610 &Syntax.DE.Euphoria\ 3 :cal SetSyn("euphoria3")<CR> +an 50.30.620 &Syntax.DE.Euphoria\ 4 :cal SetSyn("euphoria4")<CR> +an 50.30.630 &Syntax.DE.Eviews :cal SetSyn("eviews")<CR> +an 50.30.640 &Syntax.DE.Exim\ conf :cal SetSyn("exim")<CR> +an 50.30.650 &Syntax.DE.Expect :cal SetSyn("expect")<CR> +an 50.30.660 &Syntax.DE.Exports :cal SetSyn("exports")<CR> an 50.40.100 &Syntax.FG.Falcon :cal SetSyn("falcon")<CR> an 50.40.110 &Syntax.FG.Fantom :cal SetSyn("fan")<CR> an 50.40.120 &Syntax.FG.Fetchmail :cal SetSyn("fetchmail")<CR> @@ -259,43 +264,44 @@ an 50.50.290 &Syntax.HIJK.HTML.XHTML :cal SetSyn("xhtml")<CR> an 50.50.300 &Syntax.HIJK.Host\.conf :cal SetSyn("hostconf")<CR> an 50.50.310 &Syntax.HIJK.Hosts\ access :cal SetSyn("hostsaccess")<CR> an 50.50.320 &Syntax.HIJK.Hyper\ Builder :cal SetSyn("hb")<CR> -an 50.50.330 &Syntax.HIJK.Icewm\ menu :cal SetSyn("icemenu")<CR> -an 50.50.340 &Syntax.HIJK.Icon :cal SetSyn("icon")<CR> -an 50.50.350 &Syntax.HIJK.IDL\Generic\ IDL :cal SetSyn("idl")<CR> -an 50.50.360 &Syntax.HIJK.IDL\Microsoft\ IDL :cal SetSyn("msidl")<CR> -an 50.50.370 &Syntax.HIJK.Indent\ profile :cal SetSyn("indent")<CR> -an 50.50.380 &Syntax.HIJK.Inform :cal SetSyn("inform")<CR> -an 50.50.390 &Syntax.HIJK.Informix\ 4GL :cal SetSyn("fgl")<CR> -an 50.50.400 &Syntax.HIJK.Initng :cal SetSyn("initng")<CR> -an 50.50.410 &Syntax.HIJK.Inittab :cal SetSyn("inittab")<CR> -an 50.50.420 &Syntax.HIJK.Inno\ setup :cal SetSyn("iss")<CR> -an 50.50.430 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ dat :cal SetSyn("upstreamdat")<CR> -an 50.50.440 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ log :cal SetSyn("upstreamlog")<CR> -an 50.50.450 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ rpt :cal SetSyn("upstreamrpt")<CR> -an 50.50.460 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ Install\ log :cal SetSyn("upstreaminstalllog")<CR> -an 50.50.470 &Syntax.HIJK.Innovation\ Data\ Processing.Usserver\ log :cal SetSyn("usserverlog")<CR> -an 50.50.480 &Syntax.HIJK.Innovation\ Data\ Processing.USW2KAgt\ log :cal SetSyn("usw2kagtlog")<CR> -an 50.50.490 &Syntax.HIJK.InstallShield\ script :cal SetSyn("ishd")<CR> -an 50.50.500 &Syntax.HIJK.Interactive\ Data\ Lang :cal SetSyn("idlang")<CR> -an 50.50.510 &Syntax.HIJK.IPfilter :cal SetSyn("ipfilter")<CR> -an 50.50.530 &Syntax.HIJK.J :cal SetSyn("j")<CR> -an 50.50.540 &Syntax.HIJK.JAL :cal SetSyn("jal")<CR> -an 50.50.550 &Syntax.HIJK.JAM :cal SetSyn("jam")<CR> -an 50.50.560 &Syntax.HIJK.Jargon :cal SetSyn("jargon")<CR> -an 50.50.570 &Syntax.HIJK.Java.Java :cal SetSyn("java")<CR> -an 50.50.580 &Syntax.HIJK.Java.JavaCC :cal SetSyn("javacc")<CR> -an 50.50.590 &Syntax.HIJK.Java.Java\ Server\ Pages :cal SetSyn("jsp")<CR> -an 50.50.600 &Syntax.HIJK.Java.Java\ Properties :cal SetSyn("jproperties")<CR> -an 50.50.610 &Syntax.HIJK.JavaScript :cal SetSyn("javascript")<CR> -an 50.50.620 &Syntax.HIJK.Jess :cal SetSyn("jess")<CR> -an 50.50.630 &Syntax.HIJK.Jgraph :cal SetSyn("jgraph")<CR> -an 50.50.640 &Syntax.HIJK.Jovial :cal SetSyn("jovial")<CR> -an 50.50.650 &Syntax.HIJK.JSON :cal SetSyn("json")<CR> -an 50.50.670 &Syntax.HIJK.Kconfig :cal SetSyn("kconfig")<CR> -an 50.50.680 &Syntax.HIJK.KDE\ script :cal SetSyn("kscript")<CR> -an 50.50.690 &Syntax.HIJK.Kimwitu++ :cal SetSyn("kwt")<CR> -an 50.50.700 &Syntax.HIJK.Kivy :cal SetSyn("kivy")<CR> -an 50.50.710 &Syntax.HIJK.KixTart :cal SetSyn("kix")<CR> +an 50.50.340 &Syntax.HIJK.Icewm\ menu :cal SetSyn("icemenu")<CR> +an 50.50.350 &Syntax.HIJK.Icon :cal SetSyn("icon")<CR> +an 50.50.360 &Syntax.HIJK.IDL\Generic\ IDL :cal SetSyn("idl")<CR> +an 50.50.370 &Syntax.HIJK.IDL\Microsoft\ IDL :cal SetSyn("msidl")<CR> +an 50.50.380 &Syntax.HIJK.Indent\ profile :cal SetSyn("indent")<CR> +an 50.50.390 &Syntax.HIJK.Inform :cal SetSyn("inform")<CR> +an 50.50.400 &Syntax.HIJK.Informix\ 4GL :cal SetSyn("fgl")<CR> +an 50.50.410 &Syntax.HIJK.Initng :cal SetSyn("initng")<CR> +an 50.50.420 &Syntax.HIJK.Inittab :cal SetSyn("inittab")<CR> +an 50.50.430 &Syntax.HIJK.Inno\ setup :cal SetSyn("iss")<CR> +an 50.50.440 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ dat :cal SetSyn("upstreamdat")<CR> +an 50.50.450 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ log :cal SetSyn("upstreamlog")<CR> +an 50.50.460 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ rpt :cal SetSyn("upstreamrpt")<CR> +an 50.50.470 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ Install\ log :cal SetSyn("upstreaminstalllog")<CR> +an 50.50.480 &Syntax.HIJK.Innovation\ Data\ Processing.Usserver\ log :cal SetSyn("usserverlog")<CR> +an 50.50.490 &Syntax.HIJK.Innovation\ Data\ Processing.USW2KAgt\ log :cal SetSyn("usw2kagtlog")<CR> +an 50.50.500 &Syntax.HIJK.InstallShield\ script :cal SetSyn("ishd")<CR> +an 50.50.510 &Syntax.HIJK.Interactive\ Data\ Lang :cal SetSyn("idlang")<CR> +an 50.50.520 &Syntax.HIJK.IPfilter :cal SetSyn("ipfilter")<CR> +an 50.50.540 &Syntax.HIJK.J :cal SetSyn("j")<CR> +an 50.50.550 &Syntax.HIJK.JAL :cal SetSyn("jal")<CR> +an 50.50.560 &Syntax.HIJK.JAM :cal SetSyn("jam")<CR> +an 50.50.570 &Syntax.HIJK.Jargon :cal SetSyn("jargon")<CR> +an 50.50.580 &Syntax.HIJK.Java.Java :cal SetSyn("java")<CR> +an 50.50.590 &Syntax.HIJK.Java.JavaCC :cal SetSyn("javacc")<CR> +an 50.50.600 &Syntax.HIJK.Java.Java\ Server\ Pages :cal SetSyn("jsp")<CR> +an 50.50.610 &Syntax.HIJK.Java.Java\ Properties :cal SetSyn("jproperties")<CR> +an 50.50.620 &Syntax.HIJK.JavaScript :cal SetSyn("javascript")<CR> +an 50.50.630 &Syntax.HIJK.JavaScriptReact :cal SetSyn("javascriptreact")<CR> +an 50.50.640 &Syntax.HIJK.Jess :cal SetSyn("jess")<CR> +an 50.50.650 &Syntax.HIJK.Jgraph :cal SetSyn("jgraph")<CR> +an 50.50.660 &Syntax.HIJK.Jovial :cal SetSyn("jovial")<CR> +an 50.50.670 &Syntax.HIJK.JSON :cal SetSyn("json")<CR> +an 50.50.690 &Syntax.HIJK.Kconfig :cal SetSyn("kconfig")<CR> +an 50.50.700 &Syntax.HIJK.KDE\ script :cal SetSyn("kscript")<CR> +an 50.50.710 &Syntax.HIJK.Kimwitu++ :cal SetSyn("kwt")<CR> +an 50.50.720 &Syntax.HIJK.Kivy :cal SetSyn("kivy")<CR> +an 50.50.730 &Syntax.HIJK.KixTart :cal SetSyn("kix")<CR> an 50.60.100 &Syntax.L.Lace :cal SetSyn("lace")<CR> an 50.60.110 &Syntax.L.LamdaProlog :cal SetSyn("lprolog")<CR> an 50.60.120 &Syntax.L.Latte :cal SetSyn("latte")<CR> @@ -343,34 +349,35 @@ an 50.70.240 &Syntax.M.Mathematica :cal SetSyn("mma")<CR> an 50.70.250 &Syntax.M.Matlab :cal SetSyn("matlab")<CR> an 50.70.260 &Syntax.M.Maxima :cal SetSyn("maxima")<CR> an 50.70.270 &Syntax.M.MEL\ (for\ Maya) :cal SetSyn("mel")<CR> -an 50.70.280 &Syntax.M.Messages\ (/var/log) :cal SetSyn("messages")<CR> -an 50.70.290 &Syntax.M.Metafont :cal SetSyn("mf")<CR> -an 50.70.300 &Syntax.M.MetaPost :cal SetSyn("mp")<CR> -an 50.70.310 &Syntax.M.MGL :cal SetSyn("mgl")<CR> -an 50.70.320 &Syntax.M.MIX :cal SetSyn("mix")<CR> -an 50.70.330 &Syntax.M.MMIX :cal SetSyn("mmix")<CR> -an 50.70.340 &Syntax.M.Modconf :cal SetSyn("modconf")<CR> -an 50.70.350 &Syntax.M.Model :cal SetSyn("model")<CR> -an 50.70.360 &Syntax.M.Modsim\ III :cal SetSyn("modsim3")<CR> -an 50.70.370 &Syntax.M.Modula\ 2 :cal SetSyn("modula2")<CR> -an 50.70.380 &Syntax.M.Modula\ 3 :cal SetSyn("modula3")<CR> -an 50.70.390 &Syntax.M.Monk :cal SetSyn("monk")<CR> -an 50.70.400 &Syntax.M.Motorola\ S-Record :cal SetSyn("srec")<CR> -an 50.70.410 &Syntax.M.Mplayer\ config :cal SetSyn("mplayerconf")<CR> -an 50.70.420 &Syntax.M.MOO :cal SetSyn("moo")<CR> -an 50.70.430 &Syntax.M.Mrxvtrc :cal SetSyn("mrxvtrc")<CR> -an 50.70.440 &Syntax.M.MS-DOS/Windows.4DOS\ \.bat\ file :cal SetSyn("btm")<CR> -an 50.70.450 &Syntax.M.MS-DOS/Windows.\.bat\/\.cmd\ file :cal SetSyn("dosbatch")<CR> -an 50.70.460 &Syntax.M.MS-DOS/Windows.\.ini\ file :cal SetSyn("dosini")<CR> -an 50.70.470 &Syntax.M.MS-DOS/Windows.Message\ text :cal SetSyn("msmessages")<CR> -an 50.70.480 &Syntax.M.MS-DOS/Windows.Module\ Definition :cal SetSyn("def")<CR> -an 50.70.490 &Syntax.M.MS-DOS/Windows.Registry :cal SetSyn("registry")<CR> -an 50.70.500 &Syntax.M.MS-DOS/Windows.Resource\ file :cal SetSyn("rc")<CR> -an 50.70.510 &Syntax.M.Msql :cal SetSyn("msql")<CR> -an 50.70.520 &Syntax.M.MuPAD :cal SetSyn("mupad")<CR> -an 50.70.530 &Syntax.M.Murphi :cal SetSyn("murphi")<CR> -an 50.70.540 &Syntax.M.MUSHcode :cal SetSyn("mush")<CR> -an 50.70.550 &Syntax.M.Muttrc :cal SetSyn("muttrc")<CR> +an 50.70.280 &Syntax.M.Meson :cal SetSyn("meson")<CR> +an 50.70.290 &Syntax.M.Messages\ (/var/log) :cal SetSyn("messages")<CR> +an 50.70.300 &Syntax.M.Metafont :cal SetSyn("mf")<CR> +an 50.70.310 &Syntax.M.MetaPost :cal SetSyn("mp")<CR> +an 50.70.320 &Syntax.M.MGL :cal SetSyn("mgl")<CR> +an 50.70.330 &Syntax.M.MIX :cal SetSyn("mix")<CR> +an 50.70.340 &Syntax.M.MMIX :cal SetSyn("mmix")<CR> +an 50.70.350 &Syntax.M.Modconf :cal SetSyn("modconf")<CR> +an 50.70.360 &Syntax.M.Model :cal SetSyn("model")<CR> +an 50.70.370 &Syntax.M.Modsim\ III :cal SetSyn("modsim3")<CR> +an 50.70.380 &Syntax.M.Modula\ 2 :cal SetSyn("modula2")<CR> +an 50.70.390 &Syntax.M.Modula\ 3 :cal SetSyn("modula3")<CR> +an 50.70.400 &Syntax.M.Monk :cal SetSyn("monk")<CR> +an 50.70.410 &Syntax.M.Motorola\ S-Record :cal SetSyn("srec")<CR> +an 50.70.420 &Syntax.M.Mplayer\ config :cal SetSyn("mplayerconf")<CR> +an 50.70.430 &Syntax.M.MOO :cal SetSyn("moo")<CR> +an 50.70.440 &Syntax.M.Mrxvtrc :cal SetSyn("mrxvtrc")<CR> +an 50.70.450 &Syntax.M.MS-DOS/Windows.4DOS\ \.bat\ file :cal SetSyn("btm")<CR> +an 50.70.460 &Syntax.M.MS-DOS/Windows.\.bat\/\.cmd\ file :cal SetSyn("dosbatch")<CR> +an 50.70.470 &Syntax.M.MS-DOS/Windows.\.ini\ file :cal SetSyn("dosini")<CR> +an 50.70.480 &Syntax.M.MS-DOS/Windows.Message\ text :cal SetSyn("msmessages")<CR> +an 50.70.490 &Syntax.M.MS-DOS/Windows.Module\ Definition :cal SetSyn("def")<CR> +an 50.70.500 &Syntax.M.MS-DOS/Windows.Registry :cal SetSyn("registry")<CR> +an 50.70.510 &Syntax.M.MS-DOS/Windows.Resource\ file :cal SetSyn("rc")<CR> +an 50.70.520 &Syntax.M.Msql :cal SetSyn("msql")<CR> +an 50.70.530 &Syntax.M.MuPAD :cal SetSyn("mupad")<CR> +an 50.70.540 &Syntax.M.Murphi :cal SetSyn("murphi")<CR> +an 50.70.550 &Syntax.M.MUSHcode :cal SetSyn("mush")<CR> +an 50.70.560 &Syntax.M.Muttrc :cal SetSyn("muttrc")<CR> an 50.80.100 &Syntax.NO.N1QL :cal SetSyn("n1ql")<CR> an 50.80.110 &Syntax.NO.Nanorc :cal SetSyn("nanorc")<CR> an 50.80.120 &Syntax.NO.Nastran\ input/DMAP :cal SetSyn("nastran")<CR> @@ -442,25 +449,26 @@ an 50.100.110 &Syntax.R.R.R\ help :cal SetSyn("rhelp")<CR> an 50.100.120 &Syntax.R.R.R\ noweb :cal SetSyn("rnoweb")<CR> an 50.100.130 &Syntax.R.Racc\ input :cal SetSyn("racc")<CR> an 50.100.140 &Syntax.R.Radiance :cal SetSyn("radiance")<CR> -an 50.100.150 &Syntax.R.Ratpoison :cal SetSyn("ratpoison")<CR> -an 50.100.160 &Syntax.R.RCS.RCS\ log\ output :cal SetSyn("rcslog")<CR> -an 50.100.170 &Syntax.R.RCS.RCS\ file :cal SetSyn("rcs")<CR> -an 50.100.180 &Syntax.R.Readline\ config :cal SetSyn("readline")<CR> -an 50.100.190 &Syntax.R.Rebol :cal SetSyn("rebol")<CR> -an 50.100.200 &Syntax.R.ReDIF :cal SetSyn("redif")<CR> -an 50.100.210 &Syntax.R.Relax\ NG :cal SetSyn("rng")<CR> -an 50.100.220 &Syntax.R.Remind :cal SetSyn("remind")<CR> -an 50.100.230 &Syntax.R.Relax\ NG\ compact :cal SetSyn("rnc")<CR> -an 50.100.240 &Syntax.R.Renderman.Renderman\ Shader\ Lang :cal SetSyn("sl")<CR> -an 50.100.250 &Syntax.R.Renderman.Renderman\ Interface\ Bytestream :cal SetSyn("rib")<CR> -an 50.100.260 &Syntax.R.Resolv\.conf :cal SetSyn("resolv")<CR> -an 50.100.270 &Syntax.R.Reva\ Forth :cal SetSyn("reva")<CR> -an 50.100.280 &Syntax.R.Rexx :cal SetSyn("rexx")<CR> -an 50.100.290 &Syntax.R.Robots\.txt :cal SetSyn("robots")<CR> -an 50.100.300 &Syntax.R.RockLinux\ package\ desc\. :cal SetSyn("desc")<CR> -an 50.100.310 &Syntax.R.Rpcgen :cal SetSyn("rpcgen")<CR> -an 50.100.320 &Syntax.R.RPL/2 :cal SetSyn("rpl")<CR> -an 50.100.330 &Syntax.R.ReStructuredText :cal SetSyn("rst")<CR> +an 50.100.150 &Syntax.R.Raml :cal SetSyn("raml")<CR> +an 50.100.160 &Syntax.R.Ratpoison :cal SetSyn("ratpoison")<CR> +an 50.100.170 &Syntax.R.RCS.RCS\ log\ output :cal SetSyn("rcslog")<CR> +an 50.100.180 &Syntax.R.RCS.RCS\ file :cal SetSyn("rcs")<CR> +an 50.100.190 &Syntax.R.Readline\ config :cal SetSyn("readline")<CR> +an 50.100.200 &Syntax.R.Rebol :cal SetSyn("rebol")<CR> +an 50.100.210 &Syntax.R.ReDIF :cal SetSyn("redif")<CR> +an 50.100.220 &Syntax.R.Relax\ NG :cal SetSyn("rng")<CR> +an 50.100.230 &Syntax.R.Remind :cal SetSyn("remind")<CR> +an 50.100.240 &Syntax.R.Relax\ NG\ compact :cal SetSyn("rnc")<CR> +an 50.100.250 &Syntax.R.Renderman.Renderman\ Shader\ Lang :cal SetSyn("sl")<CR> +an 50.100.260 &Syntax.R.Renderman.Renderman\ Interface\ Bytestream :cal SetSyn("rib")<CR> +an 50.100.270 &Syntax.R.Resolv\.conf :cal SetSyn("resolv")<CR> +an 50.100.280 &Syntax.R.Reva\ Forth :cal SetSyn("reva")<CR> +an 50.100.290 &Syntax.R.Rexx :cal SetSyn("rexx")<CR> +an 50.100.300 &Syntax.R.Robots\.txt :cal SetSyn("robots")<CR> +an 50.100.310 &Syntax.R.RockLinux\ package\ desc\. :cal SetSyn("desc")<CR> +an 50.100.320 &Syntax.R.Rpcgen :cal SetSyn("rpcgen")<CR> +an 50.100.330 &Syntax.R.RPL/2 :cal SetSyn("rpl")<CR> +an 50.100.340 &Syntax.R.ReStructuredText :cal SetSyn("rst")<CR> an 50.110.100 &Syntax.M.ReStructuredText\ with\ R\ statements :cal SetSyn("rrst")<CR> an 50.120.100 &Syntax.R.RTF :cal SetSyn("rtf")<CR> an 50.120.110 &Syntax.R.Ruby :cal SetSyn("ruby")<CR> @@ -581,6 +589,8 @@ an 50.150.370 &Syntax.T.Trustees :cal SetSyn("trustees")<CR> an 50.150.380 &Syntax.T.TSS.Command\ Line :cal SetSyn("tsscl")<CR> an 50.150.390 &Syntax.T.TSS.Geometry :cal SetSyn("tssgm")<CR> an 50.150.400 &Syntax.T.TSS.Optics :cal SetSyn("tssop")<CR> +an 50.150.410 &Syntax.T.Typescript :cal SetSyn("typescript")<CR> +an 50.150.420 &Syntax.T.TypescriptReact :cal SetSyn("typescriptreact")<CR> an 50.160.100 &Syntax.UV.Udev\ config :cal SetSyn("udevconf")<CR> an 50.160.110 &Syntax.UV.Udev\ permissions :cal SetSyn("udevperm")<CR> an 50.160.120 &Syntax.UV.Udev\ rules :cal SetSyn("udevrules")<CR> @@ -607,32 +617,33 @@ an 50.160.330 &Syntax.UV.VSE\ JCL :cal SetSyn("vsejcl")<CR> an 50.170.100 &Syntax.WXYZ.WEB.CWEB :cal SetSyn("cweb")<CR> an 50.170.110 &Syntax.WXYZ.WEB.WEB :cal SetSyn("web")<CR> an 50.170.120 &Syntax.WXYZ.WEB.WEB\ Changes :cal SetSyn("change")<CR> -an 50.170.130 &Syntax.WXYZ.Webmacro :cal SetSyn("webmacro")<CR> -an 50.170.140 &Syntax.WXYZ.Website\ MetaLanguage :cal SetSyn("wml")<CR> -an 50.170.160 &Syntax.WXYZ.wDiff :cal SetSyn("wdiff")<CR> -an 50.170.180 &Syntax.WXYZ.Wget\ config :cal SetSyn("wget")<CR> -an 50.170.190 &Syntax.WXYZ.Whitespace\ (add) :cal SetSyn("whitespace")<CR> -an 50.170.200 &Syntax.WXYZ.WildPackets\ EtherPeek\ Decoder :cal SetSyn("dcd")<CR> -an 50.170.210 &Syntax.WXYZ.WinBatch/Webbatch :cal SetSyn("winbatch")<CR> -an 50.170.220 &Syntax.WXYZ.Windows\ Scripting\ Host :cal SetSyn("wsh")<CR> -an 50.170.230 &Syntax.WXYZ.WSML :cal SetSyn("wsml")<CR> -an 50.170.240 &Syntax.WXYZ.WvDial :cal SetSyn("wvdial")<CR> -an 50.170.260 &Syntax.WXYZ.X\ Keyboard\ Extension :cal SetSyn("xkb")<CR> -an 50.170.270 &Syntax.WXYZ.X\ Pixmap :cal SetSyn("xpm")<CR> -an 50.170.280 &Syntax.WXYZ.X\ Pixmap\ (2) :cal SetSyn("xpm2")<CR> -an 50.170.290 &Syntax.WXYZ.X\ resources :cal SetSyn("xdefaults")<CR> -an 50.170.300 &Syntax.WXYZ.XBL :cal SetSyn("xbl")<CR> -an 50.170.310 &Syntax.WXYZ.Xinetd\.conf :cal SetSyn("xinetd")<CR> -an 50.170.320 &Syntax.WXYZ.Xmodmap :cal SetSyn("xmodmap")<CR> -an 50.170.330 &Syntax.WXYZ.Xmath :cal SetSyn("xmath")<CR> -an 50.170.340 &Syntax.WXYZ.XML :cal SetSyn("xml")<CR> -an 50.170.350 &Syntax.WXYZ.XML\ Schema\ (XSD) :cal SetSyn("xsd")<CR> -an 50.170.360 &Syntax.WXYZ.XQuery :cal SetSyn("xquery")<CR> -an 50.170.370 &Syntax.WXYZ.Xslt :cal SetSyn("xslt")<CR> -an 50.170.380 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf")<CR> -an 50.170.400 &Syntax.WXYZ.YAML :cal SetSyn("yaml")<CR> -an 50.170.410 &Syntax.WXYZ.Yacc :cal SetSyn("yacc")<CR> -an 50.170.430 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu")<CR> +an 50.170.130 &Syntax.WXYZ.WebAssembly :cal SetSyn("wast")<CR> +an 50.170.140 &Syntax.WXYZ.Webmacro :cal SetSyn("webmacro")<CR> +an 50.170.150 &Syntax.WXYZ.Website\ MetaLanguage :cal SetSyn("wml")<CR> +an 50.170.170 &Syntax.WXYZ.wDiff :cal SetSyn("wdiff")<CR> +an 50.170.190 &Syntax.WXYZ.Wget\ config :cal SetSyn("wget")<CR> +an 50.170.200 &Syntax.WXYZ.Whitespace\ (add) :cal SetSyn("whitespace")<CR> +an 50.170.210 &Syntax.WXYZ.WildPackets\ EtherPeek\ Decoder :cal SetSyn("dcd")<CR> +an 50.170.220 &Syntax.WXYZ.WinBatch/Webbatch :cal SetSyn("winbatch")<CR> +an 50.170.230 &Syntax.WXYZ.Windows\ Scripting\ Host :cal SetSyn("wsh")<CR> +an 50.170.240 &Syntax.WXYZ.WSML :cal SetSyn("wsml")<CR> +an 50.170.250 &Syntax.WXYZ.WvDial :cal SetSyn("wvdial")<CR> +an 50.170.270 &Syntax.WXYZ.X\ Keyboard\ Extension :cal SetSyn("xkb")<CR> +an 50.170.280 &Syntax.WXYZ.X\ Pixmap :cal SetSyn("xpm")<CR> +an 50.170.290 &Syntax.WXYZ.X\ Pixmap\ (2) :cal SetSyn("xpm2")<CR> +an 50.170.300 &Syntax.WXYZ.X\ resources :cal SetSyn("xdefaults")<CR> +an 50.170.310 &Syntax.WXYZ.XBL :cal SetSyn("xbl")<CR> +an 50.170.320 &Syntax.WXYZ.Xinetd\.conf :cal SetSyn("xinetd")<CR> +an 50.170.330 &Syntax.WXYZ.Xmodmap :cal SetSyn("xmodmap")<CR> +an 50.170.340 &Syntax.WXYZ.Xmath :cal SetSyn("xmath")<CR> +an 50.170.350 &Syntax.WXYZ.XML :cal SetSyn("xml")<CR> +an 50.170.360 &Syntax.WXYZ.XML\ Schema\ (XSD) :cal SetSyn("xsd")<CR> +an 50.170.370 &Syntax.WXYZ.XQuery :cal SetSyn("xquery")<CR> +an 50.170.380 &Syntax.WXYZ.Xslt :cal SetSyn("xslt")<CR> +an 50.170.390 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf")<CR> +an 50.170.410 &Syntax.WXYZ.YAML :cal SetSyn("yaml")<CR> +an 50.170.420 &Syntax.WXYZ.Yacc :cal SetSyn("yacc")<CR> +an 50.170.440 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu")<CR> " The End Of The Syntax Menu diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index 6afe56a6e3..7ac02c3f63 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi <anmol@aubble.com> +" Maintainer: Anmol Sethi <hi@nhooyr.io> " Previous Maintainer: SungHyun Nam <goweol@gmail.com> if exists('b:current_syntax') @@ -30,6 +30,7 @@ endif if !exists('b:man_sect') call man#init_pager() endif + if b:man_sect =~# '^[023]' syntax case match syntax include @c $VIMRUNTIME/syntax/c.vim diff --git a/runtime/syntax/tex.vim b/runtime/syntax/tex.vim index 363c781d0f..b3a8f96b6b 100644 --- a/runtime/syntax/tex.vim +++ b/runtime/syntax/tex.vim @@ -1,8 +1,8 @@ " Vim syntax file " Language: TeX -" Maintainer: Charles E. Campbell <NdrchipO@ScampbellPfamily.AbizM> -" Last Change: May 14, 2019 -" Version: 114 +" Maintainer: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> +" Last Change: Jun 07, 2020 +" Version: 118 " URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TEX " " Notes: {{{1 @@ -147,6 +147,11 @@ if exists("g:tex_nospell") && g:tex_nospell else let s:tex_nospell = 0 endif +if exists("g:tex_excludematcher") + let s:tex_excludematcher= g:tex_excludematcher +else + let s:tex_excludematcher= 0 +endif " Clusters: {{{1 " -------- @@ -156,8 +161,12 @@ if !s:tex_no_error endif syn cluster texEnvGroup contains=texMatcher,texMathDelim,texSpecialChar,texStatement syn cluster texFoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texItalStyle,texEmphStyle,texNoSpell -syn cluster texBoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texBoldItalStyle,texNoSpell -syn cluster texItalGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMatcher,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texItalStyle,texEmphStyle,texItalBoldStyle,texNoSpell +syn cluster texBoldGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texBoldStyle,texBoldItalStyle,texNoSpell +syn cluster texItalGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texInputFile,texLength,texLigature,texMathZoneV,texMathZoneW,texMathZoneX,texMathZoneY,texMathZoneZ,texNewCmd,texNewEnv,texOnlyMath,texOption,texParen,texRefZone,texSection,texBeginEnd,texSectionZone,texSpaceCode,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,@texMathZones,texTitle,texAbstract,texItalStyle,texEmphStyle,texItalBoldStyle,texNoSpell +if !s:tex_excludematcher + syn cluster texBoldGroup add=texMatcher + syn cluster texItalGroup add=texMatcher +endif if !s:tex_nospell syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell @@ -305,11 +314,6 @@ if s:tex_conceal !~# 'b' endif syn match texTypeStyle "\\textmd\>" syn match texTypeStyle "\\textrm\>" -syn match texTypeStyle "\\textsc\>" -syn match texTypeStyle "\\textsf\>" -syn match texTypeStyle "\\textsl\>" -syn match texTypeStyle "\\texttt\>" -syn match texTypeStyle "\\textup\>" syn match texTypeStyle "\\mathbb\>" syn match texTypeStyle "\\mathbf\>" @@ -386,12 +390,18 @@ if s:tex_fast =~# 'b' syn region texItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup,@Spell syn region texItalBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup,@Spell syn region texEmphStyle matchgroup=texTypeStyle start="\\emph\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup,@Spell + syn region texEmphStyle matchgroup=texTypeStyle start="\\texts[cfl]\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup,@Spell + syn region texEmphStyle matchgroup=texTypeStyle start="\\textup\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup,@Spell + syn region texEmphStyle matchgroup=texTypeStyle start="\\texttt\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup,@Spell else syn region texBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup syn region texBoldItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup syn region texItalStyle matchgroup=texTypeStyle start="\\textit\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup syn region texItalBoldStyle matchgroup=texTypeStyle start="\\textbf\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texBoldGroup syn region texEmphStyle matchgroup=texTypeStyle start="\\emph\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texItalGroup + syn region texEmphStyle matchgroup=texTypeStyle start="\\texts[cfl]\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texEmphGroup + syn region texEmphStyle matchgroup=texTypeStyle start="\\textup\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texEmphGroup + syn region texEmphStyle matchgroup=texTypeStyle start="\\texttt\s*{" matchgroup=texTypeStyle end="}" concealends contains=@texEmphGroup endif endif endif @@ -624,7 +634,7 @@ if s:tex_fast =~# 'r' syn region texRefOption contained matchgroup=Delimiter start='\[' end=']' contains=@texRefGroup,texRefZone nextgroup=texRefOption,texCite syn region texCite contained matchgroup=Delimiter start='{' end='}' contains=@texRefGroup,texRefZone,texCite endif -syn match texRefZone '\\cite\%([tp]\*\=\)\=' nextgroup=texRefOption,texCite +syn match texRefZone '\\cite\%([tp]\*\=\)\=\>' nextgroup=texRefOption,texCite " Handle newcommand, newenvironment : {{{1 syn match texNewCmd "\\newcommand\>" nextgroup=texCmdName skipwhite skipnl @@ -745,6 +755,8 @@ if has("conceal") && &enc == 'utf-8' \ ['lceil' , '⌈'], \ ['ldots' , '…'], \ ['le' , '≤'], + \ ['left|' , '|'], + \ ['left\|' , '‖'], \ ['left(' , '('], \ ['left\[' , '['], \ ['left\\{' , '{'], @@ -795,6 +807,8 @@ if has("conceal") && &enc == 'utf-8' \ ['quad' , ' '], \ ['qquad' , ' '], \ ['rfloor' , '⌋'], + \ ['right|' , '|'], + \ ['right\\|' , '‖'], \ ['right)' , ')'], \ ['right]' , ']'], \ ['right\\}' , '}'], @@ -1047,6 +1061,7 @@ if has("conceal") && &enc == 'utf-8' call s:SuperSub('texSuperscript','\^','R','ᴿ') call s:SuperSub('texSuperscript','\^','T','ᵀ') call s:SuperSub('texSuperscript','\^','U','ᵁ') + call s:SuperSub('texSuperscript','\^','V','ⱽ') call s:SuperSub('texSuperscript','\^','W','ᵂ') call s:SuperSub('texSuperscript','\^',',','︐') call s:SuperSub('texSuperscript','\^',':','︓') diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim index 6305eef734..83ca547fdd 100644 --- a/runtime/syntax/tutor.vim +++ b/runtime/syntax/tutor.vim @@ -33,16 +33,16 @@ syn keyword tutorMarks Todo Note Tip Excersise syn region tutorCodeblock matchgroup=Delimiter start=/^\~\{3}.*$/ end=/^\~\{3}/ -syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL +syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL concealends syn match tutorShellPrompt /\(^\s*\)\@<=[$#]/ contained containedin=tutorShell -syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/ +syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/ concealends -syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM -syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM +syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM concealends +syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*`{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM concealends keepend -syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL -syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL +syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL concealends +syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*`{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL concealends keepend syn match tutorInlineType /{\(normal\|vim\)}/ contained conceal diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index b177fe641d..6f0818c845 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -558,7 +558,7 @@ syn match vimHiGuiFontname contained "'[a-zA-Z\-* ]\+'" syn match vimHiGuiRgb contained "#\x\{6}" " Highlighting: hi group key=arg ... {{{2 -syn cluster vimHiCluster contains=vimGroup,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation +syn cluster vimHiCluster contains=vimGroup,vimHiBlend,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation syn region vimHiKeyList contained oneline start="\i\+" skip="\\\\\|\\|" end="$\||" contains=@vimHiCluster if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_vimhikeyerror") syn match vimHiKeyError contained "\i\+="he=e-1 @@ -571,6 +571,7 @@ syn match vimHiGui contained "\cgui="he=e-1 nextgroup=vimHiAttribList syn match vimHiGuiFont contained "\cfont="he=e-1 nextgroup=vimHiFontname syn match vimHiGuiFgBg contained "\cgui\%([fb]g\|sp\)="he=e-1 nextgroup=vimHiGroup,vimHiGuiFontname,vimHiGuiRgb,vimFgBgAttrib syn match vimHiTermcap contained "\S\+" contains=vimNotation +syn match vimHiBlend contained "\cblend="he=e-1 nextgroup=vimHiNmbr syn match vimHiNmbr contained '\d\+' " Highlight: clear {{{2 @@ -850,6 +851,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimGroupSpecial Special hi def link vimGroup Type hi def link vimHiAttrib PreProc + hi def link vimHiBlend vimHiTerm hi def link vimHiClear vimHighlight hi def link vimHiCtermFgBg vimHiTerm hi def link vimHiCTerm vimHiTerm diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor index 4e6154b24a..5ae0fde0da 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor +++ b/runtime/tutor/en/vim-01-beginner.tutor @@ -91,7 +91,7 @@ NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you ** Press `x`{normal} to delete the character under the cursor. ** - 1. Move the cursor to the line below marked --->. + 1. Move the cursor to the line below marked ✗. 2. To fix the errors, move the cursor until it is on top of the character to be deleted. @@ -111,7 +111,7 @@ NOTE: As you go through this tutor, do not try to memorize, learn by ** Press `i`{normal} to insert text. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. 2. To make the first line the same as the second, move the cursor on top of the first character AFTER where the text is to be inserted. @@ -130,7 +130,7 @@ There is some text missing from this line. ** Press `A`{normal} to append text. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. It does not matter on what character the cursor is in that line. 2. Press [A](A) and type in the necessary additions. @@ -138,7 +138,7 @@ There is some text missing from this line. 3. As the text has been appended press `<Esc>`{normal} to return to Normal mode. - 4. Move the cursor to the second line marked ---> and repeat + 4. Move the cursor to the second line marked ✗ and repeat steps 2 and 3 to correct this sentence. There is some text missing from th @@ -211,7 +211,7 @@ Now continue with Lesson 2. 1. Press `<Esc>`{normal} to make sure you are in Normal mode. - 2. Move the cursor to the line below marked --->. + 2. Move the cursor to the line below marked ✗. 3. Move the cursor to the beginning of a word that needs to be deleted. @@ -227,7 +227,7 @@ There are a some words fun that don't belong paper in this sentence. 1. Press `<Esc>`{normal} to make sure you are in Normal mode. - 2. Move the cursor to the line below marked --->. + 2. Move the cursor to the line below marked ✗. 3. Move the cursor to the end of the correct line (AFTER the first . ). @@ -263,7 +263,7 @@ NOTE: Pressing just the motion while in Normal mode without an operator ** Typing a number before a motion repeats it that many times. ** - 1. Move the cursor to the start of the line marked ---> below. + 1. Move the cursor to the start of the line marked ✓ below. 2. Type `2w`{normal} to move the cursor two words forward. @@ -285,7 +285,7 @@ In the combination of the delete operator and a motion mentioned above you insert a count before the motion to delete more: d number motion - 1. Move the cursor to the first UPPER CASE word in the line marked --->. + 1. Move the cursor to the first UPPER CASE word in the line marked ✗. 2. Type `d2w`{normal} to delete the two UPPER CASE words @@ -318,7 +318,7 @@ it would be easier to simply type two d's to delete a line. ** Press `u`{normal} to undo the last commands, `U`{normal} to fix a whole line. ** - 1. Move the cursor to the line below marked ---> and place it on the + 1. Move the cursor to the line below marked ✗ and place it on the first error. 2. Type `x`{normal} to delete the first unwanted character. 3. Now type `u`{normal} to undo the last command executed. @@ -359,7 +359,7 @@ Fiix the errors oon thhis line and reeplace them witth undo. ** Type `p`{normal} to put previously deleted text after the cursor. ** - 1. Move the cursor to the first ---> line below. + 1. Move the cursor to the first ✓ line below. 2. Type `dd`{normal} to delete the line and store it in a Vim register. @@ -378,7 +378,7 @@ a) Roses are red, ** Type `rx`{normal} to replace the character at the cursor with x. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. 2. Move the cursor so that it is on top of the first error. @@ -397,7 +397,7 @@ NOTE: Remember that you should be learning by doing, not memorization. ** To change until the end of a word, type `ce`{normal}. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. 2. Place the cursor on the "u" in "lubw". @@ -423,7 +423,7 @@ Notice that [c](c)e deletes the word and places you in Insert mode. 2. The motions are the same, such as `w`{normal} (word) and `$`{normal} (end of line). - 3. Move to the first line below marked --->. + 3. Move to the first line below marked ✗. 4. Move the cursor to the first error. @@ -503,7 +503,7 @@ NOTE: When the search reaches the end of the file it will continue at the ** Type `%`{normal} to find a matching ),], or }. ** - 1. Place the cursor on any (, [, or { in the line below marked --->. + 1. Place the cursor on any (, [, or { in the line below marked ✓. 2. Now type the [%](%) character. @@ -521,7 +521,7 @@ NOTE: This is very useful in debugging a program with unmatched parentheses! ** Type `:s/old/new/g` to substitute "new" for "old". ** - 1. Move the cursor to the line below marked --->. + 1. Move the cursor to the line below marked ✗. 2. Type ~~~ cmd @@ -725,7 +725,7 @@ NOTE: You can also read the output of an external command. For example, ** Type `o`{normal} to open a line below the cursor and place you in Insert mode. ** - 1. Move the cursor to the line below marked --->. + 1. Move the cursor to the line below marked ✓. 2. Type the lowercase letter `o`{normal} to [open](o) up a line BELOW the cursor and place you in Insert mode. @@ -743,7 +743,7 @@ Open up a line above this by typing O while the cursor is on this line. ** Type `a`{normal} to insert text AFTER the cursor. ** - 1. Move the cursor to the start of the line below marked --->. + 1. Move the cursor to the start of the line below marked ✗. 2. Press `e`{normal} until the cursor is on the end of "li". @@ -766,7 +766,7 @@ NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only ** Type a capital `R`{normal} to replace more than one character. ** - 1. Move the cursor to the first line below marked --->. Move the cursor to + 1. Move the cursor to the first line below marked ✗. Move the cursor to the beginning of the first "xxx". 2. Now press `R`{normal} ([capital R](R)) and type the number below it in the @@ -787,7 +787,7 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an ** Use the `y`{normal} operator to copy text and `p`{normal} to paste it. ** - 1. Go to the line marked with ---> below and place the cursor after "a)". + 1. Go to the line marked with ✓ below and place the cursor after "a)". 2. Start Visual mode with `v`{normal} and move the cursor to just before "first". @@ -805,7 +805,7 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an end of the next line with `j$`{normal} and put the text there with `p`{normal} a) This is the first item. - b) +b) NOTE: you can use `y`{normal} as an operator: `yw`{normal} yanks one word. diff --git a/runtime/tutor/en/vim-01-beginner.tutor.json b/runtime/tutor/en/vim-01-beginner.tutor.json index 2f87d7543f..af22cf2aca 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor.json +++ b/runtime/tutor/en/vim-01-beginner.tutor.json @@ -1,43 +1,45 @@ { - "expect": { - "24": -1, - "103": "The cow jumped over the moon.", - "124": "There is some text missing from this line.", - "125": "There is some text missing from this line.", - "144": "There is some text missing from this line.", - "145": "There is some text missing from this line.", - "146": "There is also some text missing here.", - "147": "There is also some text missing here.", - "220": "There are some words that don't belong in this sentence.", - "236": "Somebody typed the end of this line twice.", - "276": -1, - "295": "This line of words is cleaned up.", - "309": -1, - "310": -1, - "311": -1, - "312": -1, - "313": -1, - "314": -1, - "315": -1, - "332": "Fix the errors on this line and replace them with undo.", - "372": -1, - "373": -1, - "374": -1, - "375": -1, - "389": "When this line was typed in, someone pressed some wrong keys!", - "390": "When this line was typed in, someone pressed some wrong keys!", - "411": "This line has a few words that need changing using the change operator.", - "412": "This line has a few words that need changing using the change operator.", - "432": "The end of this line needs to be corrected using the c$ command.", - "433": "The end of this line needs to be corrected using the c$ command.", - "497": -1, - "516": -1, - "541": "Usually the best time to see the flowers is in the spring.", - "759": "This line will allow you to practice appending text to a line.", - "760": "This line will allow you to practice appending text to a line.", - "780": "Adding 123 to 456 gives you 579.", - "781": "Adding 123 to 456 gives you 579.", - "807": "a) This is the first item.", - "808": " b) This is the second item." - } + "expect": { + "24": -1, + "103": "The cow jumped over the moon.", + "124": "There is some text missing from this line.", + "125": "There is some text missing from this line.", + "144": "There is some text missing from this line.", + "145": "There is some text missing from this line.", + "146": "There is also some text missing here.", + "147": "There is also some text missing here.", + "220": "There are some words that don't belong in this sentence.", + "236": "Somebody typed the end of this line twice.", + "276": -1, + "295": "This line of words is cleaned up.", + "309": -1, + "310": -1, + "311": -1, + "312": -1, + "313": -1, + "314": -1, + "315": -1, + "332": "Fix the errors on this line and replace them with undo.", + "372": -1, + "373": -1, + "374": -1, + "375": -1, + "389": "When this line was typed in, someone pressed some wrong keys!", + "390": "When this line was typed in, someone pressed some wrong keys!", + "411": "This line has a few words that need changing using the change operator.", + "412": "This line has a few words that need changing using the change operator.", + "432": "The end of this line needs to be corrected using the `c$` command.", + "433": "The end of this line needs to be corrected using the `c$` command.", + "497": -1, + "516": -1, + "541": "Usually the best time to see the flowers is in the spring.", + "735": -1, + "740": -1, + "759": "This line will allow you to practice appending text to a line.", + "760": "This line will allow you to practice appending text to a line.", + "780": "Adding 123 to 456 gives you 579.", + "781": "Adding 123 to 456 gives you 579.", + "807": "a) This is the first item.", + "808": "b) This is the second item." + } } diff --git a/runtime/tutor/tutor.tutor b/runtime/tutor/tutor.tutor index c937bd686a..b46fcc4836 100644 --- a/runtime/tutor/tutor.tutor +++ b/runtime/tutor/tutor.tutor @@ -118,7 +118,7 @@ and are hidden by default. Links to them look like \[label\]\(\*anchor\*\) -6. Add the appropiate link: +6. Add the appropriate link: A link to the Links section A link to the [Links](*links*) section |