diff options
Diffstat (limited to 'runtime')
| -rw-r--r-- | runtime/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | runtime/autoload/health.vim | 49 | ||||
| -rw-r--r-- | runtime/autoload/health/nvim.vim | 377 | ||||
| -rw-r--r-- | runtime/autoload/health/provider.vim | 382 | ||||
| -rw-r--r-- | runtime/autoload/man.vim | 157 | ||||
| -rw-r--r-- | runtime/doc/api.txt | 33 | ||||
| -rw-r--r-- | runtime/doc/develop.txt | 50 | ||||
| -rw-r--r-- | runtime/doc/eval.txt | 29 | ||||
| -rw-r--r-- | runtime/doc/filetype.txt | 9 | ||||
| -rw-r--r-- | runtime/doc/map.txt | 21 | ||||
| -rw-r--r-- | runtime/doc/msgpack_rpc.txt | 225 | ||||
| -rw-r--r-- | runtime/doc/quickfix.txt | 18 | ||||
| -rw-r--r-- | runtime/ftplugin/c.vim | 2 | ||||
| -rw-r--r-- | runtime/ftplugin/man.vim | 8 | ||||
| -rw-r--r-- | runtime/plugin/man.vim | 4 | ||||
| -rw-r--r-- | runtime/syntax/man.vim | 6 |
16 files changed, 820 insertions, 554 deletions
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 0dd8b07b7a..cced1a8d04 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -3,6 +3,7 @@ set(GENERATED_RUNTIME_DIR ${PROJECT_BINARY_DIR}/runtime) set(GENERATED_SYN_VIM ${GENERATED_RUNTIME_DIR}/syntax/vim/generated.vim) set(GENERATED_HELP_TAGS ${GENERATED_RUNTIME_DIR}/doc/tags) set(GENERATED_PACKAGE_DIR ${GENERATED_RUNTIME_DIR}/pack/dist/opt) +set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) file(MAKE_DIRECTORY ${GENERATED_RUNTIME_DIR}) file(MAKE_DIRECTORY ${GENERATED_RUNTIME_DIR}/syntax) @@ -10,13 +11,14 @@ file(MAKE_DIRECTORY ${GENERATED_RUNTIME_DIR}/syntax/vim) add_custom_command(OUTPUT ${GENERATED_SYN_VIM} COMMAND ${LUA_PRG} ${SYN_VIM_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_SYN_VIM} + ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_SYN_VIM} ${FUNCS_DATA} DEPENDS ${SYN_VIM_GENERATOR} ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua ${PROJECT_SOURCE_DIR}/src/nvim/options.lua ${PROJECT_SOURCE_DIR}/src/nvim/eval.c + ${FUNCS_DATA} ) if(POLICY CMP0054) diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 6ba9405e0a..783c30cbf6 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -11,47 +11,50 @@ function! s:enhance_syntax() abort syntax keyword healthSuccess SUCCESS highlight link healthSuccess Function - syntax keyword healthSuggestion SUGGESTION + syntax keyword healthSuggestion SUGGESTIONS highlight link healthSuggestion String endfunction " Runs the specified healthchecks. " Runs all discovered healthchecks if a:plugin_names is empty. function! health#check(plugin_names) abort - let report = '' - let healthchecks = empty(a:plugin_names) \ ? s:discover_health_checks() \ : s:to_fn_names(a:plugin_names) + tabnew + setlocal filetype=markdown bufhidden=wipe + call s:enhance_syntax() + if empty(healthchecks) - let report = "ERROR: No healthchecks found." + call setline(1, 'ERROR: No healthchecks found.') else + redraw|echo 'Running healthchecks...' for c in healthchecks - let report .= printf("\n%s\n%s", c, repeat('=',80)) + let output = '' + call append('$', split(printf("\n%s\n%s", c, repeat('=',80)), "\n")) try - let report .= execute('call '.c.'()') - catch /^Vim\%((\a\+)\)\=:E117/ - let report .= execute( - \ 'call health#report_error(''No healthcheck found for "' - \ .s:to_plugin_name(c) - \ .'" plugin.'')') + let output = "\n\n".execute('call '.c.'()') catch - let report .= execute( - \ 'call health#report_error(''Failed to run healthcheck for "' - \ .s:to_plugin_name(c) - \ .'" plugin. Exception:''."\n".v:exception)') + if v:exception =~# '^Vim\%((\a\+)\)\=:E117.*\V'.c + let output = execute( + \ 'call health#report_error(''No healthcheck found for "' + \ .s:to_plugin_name(c) + \ .'" plugin.'')') + else + let output = execute( + \ 'call health#report_error(''Failed to run healthcheck for "' + \ .s:to_plugin_name(c) + \ .'" plugin. Exception:''."\n".v:exception)') + endif endtry - let report .= "\n" + call append('$', split(output, "\n") + ['']) + redraw endfor endif - tabnew - setlocal bufhidden=wipe - set filetype=markdown - call s:enhance_syntax() - call setline(1, split(report, "\n")) setlocal nomodified + redraw|echo '' endfunction " Starts a new report. @@ -86,10 +89,10 @@ function! s:format_report_message(status, msg, ...) abort " {{{ " Report each suggestion if len(suggestions) > 0 - let output .= "\n - SUGGESTIONS:" + let output .= "\n - SUGGESTIONS:" endif for suggestion in suggestions - let output .= "\n - " . s:indent_after_line1(suggestion, 10) + let output .= "\n - " . s:indent_after_line1(suggestion, 10) endfor return output diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index 7865634313..d769525373 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -1,150 +1,3 @@ -let s:bad_responses = [ - \ 'unable to parse python response', - \ 'unable to parse', - \ 'unable to get pypi response', - \ 'unable to get neovim executable', - \ 'unable to find neovim version' - \ ] - -function! s:is_bad_response(s) abort - return index(s:bad_responses, a:s) >= 0 -endfunction - -function! s:trim(s) abort - return substitute(a:s, '^\_s*\|\_s*$', '', 'g') -endfunction - -" Simple version comparison. -function! s:version_cmp(a, b) abort - let a = split(a:a, '\.') - let b = split(a:b, '\.') - - for i in range(len(a)) - if a[i] > b[i] - return 1 - elseif a[i] < b[i] - return -1 - endif - endfor - - return 0 -endfunction - -" Fetch the contents of a URL. -function! s:download(url) abort - let content = '' - if executable('curl') - let content = system(['curl', '-sL', "'", a:url, "'"]) - endif - - if empty(content) && executable('python') - let script = " - \try:\n - \ from urllib.request import urlopen\n - \except ImportError:\n - \ from urllib2 import urlopen\n - \\n - \try:\n - \ response = urlopen('".a:url."')\n - \ print(response.read().decode('utf8'))\n - \except Exception:\n - \ pass\n - \" - let content = system(['python', '-c', "'", script, "'", '2>/dev/null']) - endif - - return content -endfunction - - -" Get the latest Neovim Python client version from PyPI. Result is cached. -function! s:latest_pypi_version() abort - if exists('s:pypi_version') - return s:pypi_version - endif - - let s:pypi_version = 'unable to get pypi response' - let pypi_info = s:download('https://pypi.python.org/pypi/neovim/json') - if !empty(pypi_info) - let pypi_data = json_decode(pypi_info) - let s:pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse') - return s:pypi_version - endif -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. -" -" Returns [ -" python executable version, -" current nvim version, -" current pypi nvim status, -" installed version status -" ] -function! s:version_info(python) abort - let pypi_version = s:latest_pypi_version() - let python_version = s:trim(system([ - \ a:python, - \ '-c', - \ 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))', - \ ])) - - if empty(python_version) - let python_version = 'unable to parse python response' - endif - - let nvim_path = s:trim(system([ - \ a:python, - \ '-c', - \ 'import neovim; print(neovim.__file__)', - \ '2>/dev/null'])) - - let nvim_path = s:trim(system([ - \ 'python3', - \ '-c', - \ 'import neovim; print(neovim.__file__)' - \ ])) - " \ '2>/dev/null'])) - - if empty(nvim_path) - return [python_version, 'unable to find neovim executable', pypi_version, 'unable to get neovim executable'] - endif - - let nvim_version = 'unable to find neovim version' - let base = fnamemodify(nvim_path, ':h') - for meta in glob(base.'-*/METADATA', 1, 1) + glob(base.'-*/PKG-INFO', 1, 1) - for meta_line in readfile(meta) - if meta_line =~# '^Version:' - let nvim_version = matchstr(meta_line, '^Version: \zs\S\+') - endif - endfor - endfor - - let version_status = 'unknown' - if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version) - if s:version_cmp(nvim_version, pypi_version) == -1 - let version_status = 'outdated' - else - let version_status = 'up to date' - endif - endif - - return [python_version, nvim_version, pypi_version, version_status] -endfunction - -" Check the Python interpreter's usability. -function! s:check_bin(bin) abort - if !filereadable(a:bin) - call health#report_error(printf('"%s" was not found.', a:bin)) - return 0 - elseif executable(a:bin) != 1 - call health#report_error(printf('"%s" is not executable.', a:bin)) - return 0 - endif - return 1 -endfunction - " Load the remote plugin manifest file and check for unregistered plugins function! s:check_manifest() abort call health#report_start('Remote Plugins') @@ -204,236 +57,6 @@ function! s:check_manifest() abort endif endfunction - -function! s:check_python(version) abort - call health#report_start('Python ' . a:version . ' provider') - - let python_bin_name = 'python'.(a:version == 2 ? '2' : '3') - let pyenv = resolve(exepath('pyenv')) - let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : 'n' - let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' - let host_prog_var = python_bin_name.'_host_prog' - let host_skip_var = python_bin_name.'_host_skip_check' - let python_bin = '' - let python_multiple = [] - - if exists('g:'.host_prog_var) - call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) - endif - - let [python_bin_name, pythonx_errs] = provider#pythonx#Detect(a:version) - if empty(python_bin_name) - call health#report_warn('No Python interpreter was found with the neovim ' - \ . 'module. Using the first available for diagnostics.') - if !empty(pythonx_errs) - call health#report_warn(pythonx_errs) - endif - let old_skip = get(g:, host_skip_var, 0) - let g:[host_skip_var] = 1 - let [python_bin_name, pythonx_errs] = provider#pythonx#Detect(a:version) - let g:[host_skip_var] = old_skip - endif - - if !empty(python_bin_name) - if exists('g:'.host_prog_var) - let python_bin = exepath(python_bin_name) - endif - let python_bin_name = fnamemodify(python_bin_name, ':t') - endif - - if !empty(pythonx_errs) - call health#report_error('Python provider error', pythonx_errs) - endif - - if !empty(python_bin_name) && empty(python_bin) && empty(pythonx_errs) - if !exists('g:'.host_prog_var) - call health#report_info(printf('`g:%s` is not set. Searching for ' - \ . '%s in the environment.', host_prog_var, python_bin_name)) - endif - - if !empty(pyenv) - if empty(pyenv_root) - call health#report_warn( - \ 'pyenv was found, but $PYENV_ROOT is not set.', - \ ['Did you follow the final install instructions?'] - \ ) - else - call health#report_ok(printf('pyenv found: "%s"', pyenv)) - endif - - let python_bin = s:trim(system( - \ printf('"%s" which %s 2>/dev/null', pyenv, python_bin_name))) - - if empty(python_bin) - call health#report_warn(printf('pyenv couldn''t find %s.', python_bin_name)) - endif - endif - - if empty(python_bin) - let python_bin = exepath(python_bin_name) - - if exists('$PATH') - for path in split($PATH, ':') - let path_bin = path.'/'.python_bin_name - if path_bin != python_bin && index(python_multiple, path_bin) == -1 - \ && executable(path_bin) - call add(python_multiple, path_bin) - endif - endfor - - if len(python_multiple) - " This is worth noting since the user may install something - " that changes $PATH, like homebrew. - call health#report_info(printf('There are multiple %s executables found. ' - \ . 'Set "g:%s" to avoid surprises.', python_bin_name, host_prog_var)) - endif - - if python_bin =~# '\<shims\>' - call health#report_warn(printf('"%s" appears to be a pyenv shim.', python_bin), [ - \ 'The "pyenv" executable is not in $PATH,', - \ 'Your pyenv installation is broken. You should set ' - \ . '"g:'.host_prog_var.'" to avoid surprises.', - \ ]) - endif - endif - endif - endif - - if !empty(python_bin) - if empty(venv) && !empty(pyenv) && !exists('g:'.host_prog_var) - \ && !empty(pyenv_root) && resolve(python_bin) !~# '^'.pyenv_root.'/' - call health#report_warn('pyenv is not set up optimally.', [ - \ printf('Suggestion: Create a virtualenv specifically ' - \ . 'for Neovim using pyenv and use "g:%s". This will avoid ' - \ . 'the need to install Neovim''s Python client in each ' - \ . 'version/virtualenv.', host_prog_var) - \ ]) - elseif !empty(venv) && exists('g:'.host_prog_var) - if !empty(pyenv_root) - let venv_root = pyenv_root - else - let venv_root = fnamemodify(venv, ':h') - endif - - if resolve(python_bin) !~# '^'.venv_root.'/' - call health#report_warn('Your virtualenv is not set up optimally.', [ - \ printf('Suggestion: Create a virtualenv specifically ' - \ . 'for Neovim and use "g:%s". This will avoid ' - \ . 'the need to install Neovim''s Python client in each ' - \ . 'virtualenv.', host_prog_var) - \ ]) - endif - endif - endif - - if empty(python_bin) && !empty(python_bin_name) - " An error message should have already printed. - call health#report_error(printf('"%s" was not found.', python_bin_name)) - elseif !empty(python_bin) && !s:check_bin(python_bin) - let python_bin = '' - endif - - " Check if $VIRTUAL_ENV is active - let virtualenv_inactive = 0 - - if exists('$VIRTUAL_ENV') - if !empty(pyenv) - let pyenv_prefix = resolve(s:trim(system([pyenv, 'prefix']))) - if $VIRTUAL_ENV != pyenv_prefix - let virtualenv_inactive = 1 - endif - elseif !empty(python_bin_name) && exepath(python_bin_name) !~# '^'.$VIRTUAL_ENV.'/' - let virtualenv_inactive = 1 - endif - endif - - if virtualenv_inactive - let suggestions = [ - \ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229', - \ ] - call health#report_warn( - \ '$VIRTUAL_ENV exists but appears to be inactive. ' - \ . 'This could lead to unexpected results.', - \ suggestions) - endif - - " Diagnostic output - call health#report_info('Executable: ' . (empty(python_bin) ? 'Not found' : python_bin)) - if len(python_multiple) - for path_bin in python_multiple - call health#report_info('Other python executable: ' . path_bin) - endfor - endif - - if !empty(python_bin) - let [pyversion, current, latest, status] = s:version_info(python_bin) - if a:version != str2nr(pyversion) - call health#report_warn('Got an unexpected version of Python.' . - \ ' This could lead to confusing error messages.') - endif - if a:version == 3 && str2float(pyversion) < 3.3 - call health#report_warn('Python 3.3+ is recommended.') - endif - - call health#report_info('Python'.a:version.' version: ' . pyversion) - call health#report_info(printf('%s-neovim Version: %s', python_bin_name, current)) - - if s:is_bad_response(current) - let suggestions = [ - \ 'Error found was: ' . current, - \ 'Use the command `$ pip' . a:version . ' install neovim`', - \ ] - call health#report_error( - \ 'Neovim Python client is not installed.', - \ suggestions) - endif - - if s:is_bad_response(latest) - call health#report_warn('Unable to fetch latest Neovim Python client version.') - endif - - if s:is_bad_response(status) - call health#report_warn('Latest Neovim Python client versions: ('.latest.')') - else - call health#report_ok('Latest Neovim Python client is installed: ('.status.')') - endif - endif - -endfunction - -function! s:check_ruby() abort - call health#report_start('Ruby provider') - let min_version = "0.2.4" - let ruby_version = systemlist('ruby -v')[0] - let ruby_prog = provider#ruby#Detect() - let suggestions = - \ ['Install or upgrade the neovim RubyGem using `gem install neovim`.'] - - if empty(ruby_prog) - let ruby_prog = 'not found' - let prog_vers = 'not found' - call health#report_error('Missing Neovim RubyGem', suggestions) - else - silent let prog_vers = systemlist(ruby_prog . ' --version')[0] - if v:shell_error - let prog_vers = 'outdated' - call health#report_warn('Neovim RubyGem is not up-to-date', suggestions) - elseif s:version_cmp(prog_vers, min_version) == -1 - let prog_vers .= ' (outdated)' - call health#report_warn('Neovim RubyGem is not up-to-date', suggestions) - else - call health#report_ok('Found Neovim RubyGem') - endif - endif - - call health#report_info('Ruby Version: ' . ruby_version) - call health#report_info('Host Executable: ' . ruby_prog) - call health#report_info('Host Version: ' . prog_vers) -endfunction - function! health#nvim#check() abort call s:check_manifest() - call s:check_python(2) - call s:check_python(3) - call s:check_ruby() endfunction diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim new file mode 100644 index 0000000000..8fa281e7e3 --- /dev/null +++ b/runtime/autoload/health/provider.vim @@ -0,0 +1,382 @@ +let s:bad_responses = [ + \ 'unable to parse python response', + \ 'unable to parse', + \ 'unable to get pypi response', + \ 'unable to get neovim executable', + \ 'unable to find neovim version' + \ ] + +function! s:is_bad_response(s) abort + return index(s:bad_responses, a:s) >= 0 +endfunction + +function! s:trim(s) abort + return substitute(a:s, '^\_s*\|\_s*$', '', 'g') +endfunction + +" Simple version comparison. +function! s:version_cmp(a, b) abort + let a = split(a:a, '\.') + let b = split(a:b, '\.') + + for i in range(len(a)) + if a[i] > b[i] + return 1 + elseif a[i] < b[i] + return -1 + endif + endfor + + return 0 +endfunction + +" Fetch the contents of a URL. +function! s:download(url) abort + let content = '' + if executable('curl') + let content = system(['curl', '-sL', "'", a:url, "'"]) + endif + + if empty(content) && executable('python') + let script = " + \try:\n + \ from urllib.request import urlopen\n + \except ImportError:\n + \ from urllib2 import urlopen\n + \\n + \try:\n + \ response = urlopen('".a:url."')\n + \ print(response.read().decode('utf8'))\n + \except Exception:\n + \ pass\n + \" + let content = system(['python', '-c', "'", script, "'", '2>/dev/null']) + endif + + return content +endfunction + + +" Get the latest Neovim Python client version from PyPI. Result is cached. +function! s:latest_pypi_version() abort + if exists('s:pypi_version') + return s:pypi_version + endif + + let s:pypi_version = 'unable to get pypi response' + let pypi_info = s:download('https://pypi.python.org/pypi/neovim/json') + if !empty(pypi_info) + let pypi_data = json_decode(pypi_info) + let s:pypi_version = get(get(pypi_data, 'info', {}), 'version', 'unable to parse') + return s:pypi_version + endif +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. +" +" Returns: [ +" {python executable version}, +" {current nvim version}, +" {current pypi nvim status}, +" {installed version status} +" ] +function! s:version_info(python) abort + let pypi_version = s:latest_pypi_version() + let python_version = s:trim(system([ + \ a:python, + \ '-c', + \ 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))', + \ ])) + + if empty(python_version) + let python_version = 'unable to parse python response' + endif + + let nvim_path = s:trim(system([ + \ a:python, + \ '-c', + \ 'import neovim; print(neovim.__file__)', + \ '2>/dev/null'])) + + let nvim_path = s:trim(system([ + \ 'python3', + \ '-c', + \ 'import neovim; print(neovim.__file__)' + \ ])) + " \ '2>/dev/null'])) + + if empty(nvim_path) + return [python_version, 'unable to find neovim executable', pypi_version, 'unable to get neovim executable'] + endif + + let nvim_version = 'unable to find neovim version' + let base = fnamemodify(nvim_path, ':h') + for meta in glob(base.'-*/METADATA', 1, 1) + glob(base.'-*/PKG-INFO', 1, 1) + for meta_line in readfile(meta) + if meta_line =~# '^Version:' + let nvim_version = matchstr(meta_line, '^Version: \zs\S\+') + endif + endfor + endfor + + let version_status = 'unknown' + if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version) + if s:version_cmp(nvim_version, pypi_version) == -1 + let version_status = 'outdated' + else + let version_status = 'up to date' + endif + endif + + return [python_version, nvim_version, pypi_version, version_status] +endfunction + +" Check the Python interpreter's usability. +function! s:check_bin(bin) abort + if !filereadable(a:bin) + call health#report_error(printf('"%s" was not found.', a:bin)) + return 0 + elseif executable(a:bin) != 1 + call health#report_error(printf('"%s" is not executable.', a:bin)) + return 0 + endif + return 1 +endfunction + +function! s:check_python(version) abort + call health#report_start('Python ' . a:version . ' provider') + + let python_bin_name = 'python'.(a:version == 2 ? '2' : '3') + let pyenv = resolve(exepath('pyenv')) + let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : 'n' + let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' + let host_prog_var = python_bin_name.'_host_prog' + let host_skip_var = python_bin_name.'_host_skip_check' + let python_bin = '' + let python_multiple = [] + + if exists('g:'.host_prog_var) + call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) + endif + + let [python_bin_name, pythonx_errs] = provider#pythonx#Detect(a:version) + if empty(python_bin_name) + call health#report_warn('No Python interpreter was found with the neovim ' + \ . 'module. Using the first available for diagnostics.') + if !empty(pythonx_errs) + call health#report_warn(pythonx_errs) + endif + let old_skip = get(g:, host_skip_var, 0) + let g:[host_skip_var] = 1 + let [python_bin_name, pythonx_errs] = provider#pythonx#Detect(a:version) + let g:[host_skip_var] = old_skip + endif + + if !empty(python_bin_name) + if exists('g:'.host_prog_var) + let python_bin = exepath(python_bin_name) + endif + let python_bin_name = fnamemodify(python_bin_name, ':t') + endif + + if !empty(pythonx_errs) + call health#report_error('Python provider error', pythonx_errs) + endif + + if !empty(python_bin_name) && empty(python_bin) && empty(pythonx_errs) + if !exists('g:'.host_prog_var) + call health#report_info(printf('`g:%s` is not set. Searching for ' + \ . '%s in the environment.', host_prog_var, python_bin_name)) + endif + + if !empty(pyenv) + if empty(pyenv_root) + call health#report_warn( + \ 'pyenv was found, but $PYENV_ROOT is not set.', + \ ['Did you follow the final install instructions?'] + \ ) + else + call health#report_ok(printf('pyenv found: "%s"', pyenv)) + endif + + let python_bin = s:trim(system( + \ printf('"%s" which %s 2>/dev/null', pyenv, python_bin_name))) + + if empty(python_bin) + call health#report_warn(printf('pyenv couldn''t find %s.', python_bin_name)) + endif + endif + + if empty(python_bin) + let python_bin = exepath(python_bin_name) + + if exists('$PATH') + for path in split($PATH, ':') + let path_bin = path.'/'.python_bin_name + if path_bin != python_bin && index(python_multiple, path_bin) == -1 + \ && executable(path_bin) + call add(python_multiple, path_bin) + endif + endfor + + if len(python_multiple) + " This is worth noting since the user may install something + " that changes $PATH, like homebrew. + call health#report_info(printf('There are multiple %s executables found. ' + \ . 'Set "g:%s" to avoid surprises.', python_bin_name, host_prog_var)) + endif + + if python_bin =~# '\<shims\>' + call health#report_warn(printf('"%s" appears to be a pyenv shim.', python_bin), [ + \ 'The "pyenv" executable is not in $PATH,', + \ 'Your pyenv installation is broken. You should set ' + \ . '"g:'.host_prog_var.'" to avoid surprises.', + \ ]) + endif + endif + endif + endif + + if !empty(python_bin) + if empty(venv) && !empty(pyenv) && !exists('g:'.host_prog_var) + \ && !empty(pyenv_root) && resolve(python_bin) !~# '^'.pyenv_root.'/' + call health#report_warn('pyenv is not set up optimally.', [ + \ printf('Suggestion: Create a virtualenv specifically ' + \ . 'for Neovim using pyenv and use "g:%s". This will avoid ' + \ . 'the need to install Neovim''s Python client in each ' + \ . 'version/virtualenv.', host_prog_var) + \ ]) + elseif !empty(venv) && exists('g:'.host_prog_var) + if !empty(pyenv_root) + let venv_root = pyenv_root + else + let venv_root = fnamemodify(venv, ':h') + endif + + if resolve(python_bin) !~# '^'.venv_root.'/' + call health#report_warn('Your virtualenv is not set up optimally.', [ + \ printf('Suggestion: Create a virtualenv specifically ' + \ . 'for Neovim and use "g:%s". This will avoid ' + \ . 'the need to install Neovim''s Python client in each ' + \ . 'virtualenv.', host_prog_var) + \ ]) + endif + endif + endif + + if empty(python_bin) && !empty(python_bin_name) + " An error message should have already printed. + call health#report_error(printf('"%s" was not found.', python_bin_name)) + elseif !empty(python_bin) && !s:check_bin(python_bin) + let python_bin = '' + endif + + " Check if $VIRTUAL_ENV is active + let virtualenv_inactive = 0 + + if exists('$VIRTUAL_ENV') + if !empty(pyenv) + let pyenv_prefix = resolve(s:trim(system([pyenv, 'prefix']))) + if $VIRTUAL_ENV != pyenv_prefix + let virtualenv_inactive = 1 + endif + elseif !empty(python_bin_name) && exepath(python_bin_name) !~# '^'.$VIRTUAL_ENV.'/' + let virtualenv_inactive = 1 + endif + endif + + if virtualenv_inactive + let suggestions = [ + \ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229', + \ ] + call health#report_warn( + \ '$VIRTUAL_ENV exists but appears to be inactive. ' + \ . 'This could lead to unexpected results.', + \ suggestions) + endif + + " Diagnostic output + call health#report_info('Executable: ' . (empty(python_bin) ? 'Not found' : python_bin)) + if len(python_multiple) + for path_bin in python_multiple + call health#report_info('Other python executable: ' . path_bin) + endfor + endif + + if !empty(python_bin) + let [pyversion, current, latest, status] = s:version_info(python_bin) + if a:version != str2nr(pyversion) + call health#report_warn('Got an unexpected version of Python.' . + \ ' This could lead to confusing error messages.') + endif + if a:version == 3 && str2float(pyversion) < 3.3 + call health#report_warn('Python 3.3+ is recommended.') + endif + + call health#report_info('Python'.a:version.' version: ' . pyversion) + call health#report_info(printf('%s-neovim Version: %s', python_bin_name, current)) + + if s:is_bad_response(current) + let suggestions = [ + \ 'Error found was: ' . current, + \ 'Use the command `$ pip' . a:version . ' install neovim`', + \ ] + call health#report_error( + \ 'Neovim Python client is not installed.', + \ suggestions) + endif + + if s:is_bad_response(latest) + call health#report_warn('Unable to fetch latest Neovim Python client version.') + endif + + if s:is_bad_response(status) + call health#report_warn('Latest Neovim Python client versions: ('.latest.')') + else + call health#report_ok('Latest Neovim Python client is installed: ('.status.')') + endif + endif + +endfunction + +function! s:check_ruby() abort + call health#report_start('Ruby provider') + let ruby_version = systemlist('ruby -v')[0] + let ruby_prog = provider#ruby#Detect() + let suggestions = + \ ['Install or upgrade the neovim RubyGem using `gem install neovim`.'] + + if empty(ruby_prog) + let ruby_prog = 'not found' + let prog_vers = 'not found' + call health#report_error('Missing Neovim RubyGem', suggestions) + else + silent let latest_gem = get(systemlist("gem list -ra '^neovim$' 2>/dev/null | " . + \ "awk -F'[()]' '{print $2}' | " . + \ 'cut -d, -f1'), 0, 'not found') + let latest_desc = ' (latest: ' . latest_gem . ')' + + silent let prog_vers = systemlist(ruby_prog . ' --version')[0] + if v:shell_error + let prog_vers = 'not found' . latest_desc + call health#report_warn('Neovim RubyGem is not up-to-date.', suggestions) + elseif s:version_cmp(prog_vers, latest_gem) == -1 + let prog_vers .= latest_desc + call health#report_warn('Neovim RubyGem is not up-to-date.', suggestions) + else + call health#report_ok('Found up-to-date neovim RubyGem') + endif + endif + + call health#report_info('Ruby Version: ' . ruby_version) + call health#report_info('Host Executable: ' . ruby_prog) + call health#report_info('Host Version: ' . prog_vers) +endfunction + +function! health#provider#check() abort + call s:check_python(2) + call s:check_python(3) + call s:check_ruby() +endfunction diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 6c2d4eae8e..bac88fc99e 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -1,19 +1,15 @@ " Maintainer: Anmol Sethi <anmol@aubble.com> -" Ensure Vim is not recursively invoked (man-db does this) -" by forcing man to use cat as the pager. -" More info here http://comments.gmane.org/gmane.editors.vim.devel/29085 if &shell =~# 'fish$' - let s:man_cmd = 'man -P cat ^/dev/null' + let s:man_cmd = 'man ^/dev/null' else - let s:man_cmd = 'man -P cat 2>/dev/null' + let s:man_cmd = 'man 2>/dev/null' endif let s:man_find_arg = "-w" -" TODO(nhooyr) I do not think completion will work on SunOS because I'm not sure if `man -l` -" displays the list of directories that are searched by man for manpages. -" I also do not think Solaris supports the '-P' flag used above and uses only $PAGER. +" TODO(nhooyr) Completion may work on SunOS; I'm not sure if `man -l` displays +" the list of searched directories. try if !has('win32') && $OSTYPE !~? 'cygwin\|linux' && system('uname -s') =~? 'SunOS' && system('uname -r') =~# '^5' let s:man_find_arg = '-l' @@ -22,77 +18,71 @@ catch /E145:/ " Ignore the error in restricted mode endtry -" We need count and count1 to ensure the section was explicitly set -" by the user. count defaults to 0 which is a valid section and -" count1 defaults to 1 which is also a valid section. Only when they -" are equal was the count explicitly set. -function! man#open_page(count, count1, ...) abort +function! man#open_page(count, count1, mods, ...) abort if a:0 > 2 call s:error('too many arguments') return - elseif a:0 ==# 1 - if empty(a:1) + elseif a:0 == 0 + let ref = &filetype ==# 'man' ? expand('<cWORD>') : expand('<cword>') + if empty(ref) call s:error('no identifier under cursor') return endif + elseif a:0 ==# 1 let ref = a:1 else - " We combine the name and sect into a manpage reference so that all + " Combine the name and sect into a manpage reference so that all " verification/extraction can be kept in a single function. " If a:2 is a reference as well, that is fine because it is the only " reference that will match. let ref = a:2.'('.a:1.')' endif try - let [sect, name] = s:extract_sect_and_name_ref(ref) + let [sect, name] = man#extract_sect_and_name_ref(ref) if a:count ==# a:count1 - " user explicitly set a count + " 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] = s:verify_exists(sect, name) + let [sect, name, path] = s:verify_exists(sect, name) catch call s:error(v:exception) return endtry call s:push_tag() let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')') - let found_man = s:find_man() - if getbufvar(bufname, 'manwidth') ==# s:manwidth() - if found_man - silent execute 'buf' bufnr(bufname) - else - execute 'split' bufname - endif - keepjumps 1 - return - endif - if found_man - noautocmd execute 'edit' bufname + if a:mods !~# 'tab' && s:find_man() + noautocmd execute 'silent edit' bufname else - noautocmd execute 'split' bufname + noautocmd execute 'silent' a:mods 'split' bufname endif - call s:read_page(sect, name) + let b:man_sect = sect + call s:read_page(path) endfunction function! man#read_page(ref) abort try - let [sect, name] = s:extract_sect_and_name_ref(a:ref) - let [sect, name] = s:verify_exists(sect, name) + let [sect, name] = man#extract_sect_and_name_ref(a:ref) + let [b:man_sect, name, path] = s:verify_exists(sect, name) catch - call s:error(v:exception) + " call to s:error() is unnecessary return endtry - call s:read_page(sect, name) + call s:read_page(path) endfunction -function! s:read_page(sect, name) abort +function! s:read_page(path) abort setlocal modifiable setlocal noreadonly - keepjumps %delete _ - let b:manwidth = s:manwidth() - silent execute 'read!env MANWIDTH='.b:manwidth s:man_cmd s:man_args(a:sect, a:name) + silent keepjumps %delete _ + " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). + " http://comments.gmane.org/gmane.editors.vim.devel/29085 + " Respect $MANWIDTH, or default to window width. + let cmd = 'env MANPAGER=cat'.(empty($MANWIDTH) ? ' MANWIDTH='.winwidth(0) : '') + let cmd .= ' '.s:man_cmd.' '.shellescape(a:path) + silent put =system(cmd) " remove all the backspaced text - silent execute 'keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g') + execute 'silent keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g') while getline(1) =~# '^\s*$' silent keepjumps 1delete _ endwhile @@ -101,17 +91,17 @@ endfunction " attempt to extract the name and sect out of 'name(sect)' " otherwise just return the largest string of valid characters in ref -function! s:extract_sect_and_name_ref(ref) abort +function! man#extract_sect_and_name_ref(ref) abort if a:ref[0] ==# '-' " try ':Man -pandoc' with this disabled. - throw 'manpage name starts with ''-''' + throw 'manpage name cannot start with ''-''' endif let ref = matchstr(a:ref, '[^()]\+([^()]\+)') if empty(ref) let name = matchstr(a:ref, '[^()]\+') if empty(name) - throw 'manpage reference contains only parantheses' + throw 'manpage reference cannot contain only parentheses' endif - return ['', name] + return [get(b:, 'man_default_sects', ''), name] endif let left = split(ref, '(') " see ':Man 3X curses' on why tolower. @@ -120,21 +110,29 @@ function! s:extract_sect_and_name_ref(ref) abort return [tolower(split(left[1], ')')[0]), left[0]] endfunction -function! s:verify_exists(sect, name) abort - let path = system(s:man_cmd.' '.s:man_find_arg.' '.s:man_args(a:sect, a:name)) - if path !~# '^\/' - if empty(a:sect) - throw 'no manual entry for '.a:name - endif +function! s:get_path(sect, name) abort + if empty(a:sect) let path = system(s:man_cmd.' '.s:man_find_arg.' '.shellescape(a:name)) if path !~# '^\/' - throw 'no manual entry for '.a:name.'('.a:sect.') or '.a:name + throw 'no manual entry for '.a:name endif + return path endif - if a:name =~# '\/' - " We do not need to extract the section/name from the path if the name is - " just a path. - return ['', a:name] + " '-s' flag handles: + " - tokens like 'printf(echo)' + " - sections starting with '-' + " - 3pcap section (found on macOS) + " - commas between sections (for section priority) + return system(s:man_cmd.' '.s:man_find_arg.' -s '.shellescape(a:sect).' '.shellescape(a:name)) +endfunction + +function! s:verify_exists(sect, name) abort + let path = s:get_path(a:sect, a:name) + if path !~# '^\/' + let path = s:get_path(get(b:, 'man_default_sects', ''), a:name) + if path !~# '^\/' + let path = s:get_path('', a:name) + endif endif " We need to extract the section from the path because sometimes " the actual section of the manpage is more specific than the section @@ -142,7 +140,8 @@ function! s:verify_exists(sect, name) abort " Also on linux, it seems that the name is case insensitive. So if one does " ':Man PRIntf', we still want the name of the buffer to be 'printf' or " whatever the correct capitilization is. - return s:extract_sect_and_name_path(path[:len(path)-2]) + let path = path[:len(path)-2] + return s:extract_sect_and_name_path(path) + [path] endfunction let s:tag_stack = [] @@ -158,7 +157,7 @@ endfunction function! man#pop_tag() abort if !empty(s:tag_stack) let tag = remove(s:tag_stack, -1) - execute tag['buf'].'b' + silent execute tag['buf'].'buffer' call cursor(tag['lnum'], tag['col']) endif endfunction @@ -170,13 +169,15 @@ function! s:extract_sect_and_name_path(path) abort let tail = fnamemodify(tail, ':r') endif let sect = matchstr(tail, '\.\zs[^.]\+$') - let name = matchstr(tail, '^.\+\ze\.[^.]\+$') + let name = matchstr(tail, '^.\+\ze\.') return [sect, name] endfunction function! s:find_man() abort if &filetype ==# 'man' return 1 + elseif winnr('$') ==# 1 + return 0 endif let thiswin = winnr() while 1 @@ -189,32 +190,6 @@ function! s:find_man() abort endwhile endfunction -function! s:manwidth() abort - " The reason for respecting $MANWIDTH even if it is wider/smaller than the - " current window is that the current window might only be temporarily - " narrow/wide. Since we don't reflow, we should just assume the - " user knows what they're doing and respect $MANWIDTH. - if empty($MANWIDTH) - " If $MANWIDTH is not set, we do not assign directly to $MANWIDTH because - " then $MANWIDTH will always stay the same value as we only use - " winwidth(0) when $MANWIDTH is empty. Instead we set it locally for the command. - return winwidth(0) - endif - return $MANWIDTH -endfunction - -function! s:man_args(sect, name) abort - if empty(a:sect) - return shellescape(a:name) - endif - " The '-s' flag is very useful. - " We do not need to worry about stuff like 'printf(echo)' - " (two manpages would be interpreted by man without -s) - " We do not need to check if the sect starts with '-' - " Lastly, the 3pcap section on macOS doesn't work without -s - return '-s '.shellescape(a:sect).' '.shellescape(a:name) -endfunction - function! s:error(msg) abort redraw echohl ErrorMsg @@ -224,7 +199,7 @@ endfunction let s:mandirs = join(split(system(s:man_cmd.' '.s:man_find_arg), ':\|\n'), ',') -" see s:extract_sect_and_name_ref on why tolower(sect) +" see man#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 l = len(args) @@ -271,14 +246,14 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort return uniq(sort(map(globpath(s:mandirs,'man?/'.name.'*.'.sect.'*', 0, 1), 's:format_candidate(v:val, sect)'), 'i')) endfunction -function! s:format_candidate(c, sect) abort - if a:c =~# '\.\%(pdf\|in\)$' " invalid extensions +function! s:format_candidate(path, sect) abort + if a:path =~# '\.\%(pdf\|in\)$' " invalid extensions return endif - let [sect, name] = s:extract_sect_and_name_path(a:c) + let [sect, name] = s:extract_sect_and_name_path(a:path) if sect ==# a:sect return name - elseif sect =~# a:sect.'[^.]\+$' + elseif sect =~# a:sect.'.\+$' " We include the section if the user provided section is a prefix " of the actual section. return name.'('.sect.')' diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index bdeca367b1..c3d7fdb35b 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -14,13 +14,13 @@ C API for Nvim *API* *api* ============================================================================== 1. Introduction *api-intro* -Nvim exposes a public API for external code to interact with the Nvim core. In -the present version of Nvim the API is primarily used by external processes to -interact with Nvim using the msgpack-rpc protocol, see |msgpack-rpc|. The API -will also be used from vimscript to access new Nvim core features, but this is -not implemented yet. Later on, Nvim might be embeddable in C applications as -libnvim, and the application will then control the embedded instance by -calling the C API directly. +Nvim exposes a public API for external code to interact with the Nvim core. +The API is used by external processes to interact with Nvim using the +msgpack-rpc protocol, see |msgpack-rpc|. The API is used from vimscript to +access some new Nvim core features. See |eval-api| for how api functions are +called from vimscript. Later on, Nvim might be embeddable in C applications as +libnvim, and the application will then control the embedded instance by calling +the C API directly. ============================================================================== 2. API Types *api-types* @@ -73,10 +73,10 @@ Another use case are plugins that show output in an append-only buffer, and want to add highlights to the outputs. Highlight data cannot be preserved on writing and loading a buffer to file, nor in undo/redo cycles. -Highlights are registered using the |buffer_add_highlight| function, see the +Highlights are registered using the |nvim_buf_add_highlight| function, see the generated API documentation for details. If an external highlighter plugin is adding a large number of highlights in a batch, performance can be improved by -calling |buffer_add_highlight| as an asynchronous notification, after first +calling |nvim_buf_add_highlight| as an asynchronous notification, after first (synchronously) reqesting a source id. Here is an example using wrapper functions in the python client: > @@ -91,10 +91,19 @@ functions in the python client: buf.clear_highlight(src) < If the highlights don't need to be deleted or updated, just pass -1 as -src_id (this is the default in python). |buffer_clear_highlight| can be used -to clear highligts from a specific source, in a specific line range or the -entire buffer by passing in the line range 0, -1 (the later is the default +src_id (this is the default in python). |nvim_buf_clear_highlight| can be used +to clear highlights from a specific source, in a specific line range or the +entire buffer by passing in the line range 0, -1 (the latter is the default in python as used above). +An example of calling the api from vimscript: > + + call nvim_buf_set_lines(0, 0, 0, v:true, ["test text"]) + let src = nvim_buf_add_highlight(0, 0, "String", 1, 0, 4) + call nvim_buf_add_highlight(0, src, "Identifier", 0, 5, -1) + + " later + call nvim_buf_clear_highlight(0, src, 0, -1) +> ============================================================================== vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 6ecbf9fb0d..ddec137079 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -124,13 +124,26 @@ include the kitchen sink... but you can use it for plumbing." ============================================================================== 2. Design decisions *design-decisions* -Jargon *dev-jargon* +JARGON *dev-jargon* + +API client ~ +All external UIs and remote plugins (as opposed to regular Vim plugins) are +"clients" in general; but we call something an "API client" if its purpose is +to abstract or wrap the RPC API for the convenience of other applications +(just like a REST client or SDK such as boto3 for AWS: you can speak AWS REST +using an HTTP client like curl, but boto3 wraps that in a convenient python +interface). For example, the Nvim lua-client is an API client: + https://github.com/neovim/lua-client Host ~ A plugin "host" is both a client (of the Nvim API) and a server (of an external platform, e.g. python). It is a remote plugin that hosts other plugins. +Remote plugin ~ +Arbitrary code registered via |:UpdateRemotePlugins|, that runs in a separate +process and communicates with Nvim via the |api|. + Window ~ The word "window" is commonly used for several things: A window on the screen, the xterm window, a window inside Vim to view a buffer. @@ -145,7 +158,7 @@ window View on a buffer. There can be several windows in Vim, together with the command line, menubar, toolbar, etc. they fit in the shell. -Providers *dev-provider* +PROVIDERS *dev-provider* A goal of Nvim is to allow extension of the editor without special knowledge in the core. But some Vim components are too tightly coupled; in those cases @@ -189,8 +202,35 @@ Python host isn't installed then the plugin will "think" it is running in a Vim compiled without the |+python| feature. -RPC API -API client -remote plugin +API *dev-api* + +Use this pattern to name new API functions: + nvim_{thing}_{action}_{arbitrary-qualifiers} + +If the function acts on an object then {thing} is the name of that object +(e.g. "buf" or "win"). If the function operates in a "global" context then +{thing} is usually omitted (but consider "namespacing" your global operations +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 subset of things by some query) + set set a thing + del delete a thing (or group of things) + list get all things + +Use consistent names for {thing} in all API function. E.g. a buffer is called +"buf" everywhere, not "buffer" in some places and "buf" in others. + +Example: `nvim_get_current_line` acts on the global editor state; the common +{action} "get" is used but {thing} is omitted. + +Example: `nvim_buf_add_highlight` acts on a `Buffer` object (the first +parameter) and uses the common {action} "add". + +Example: `nvim_list_bufs` operates in a global context (first parameter is +_not_ a Buffer). The common {action} "list" indicates that it lists all +bufs (plural) in the global context. + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 03d8f84aa6..d97a1400ce 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2009,6 +2009,7 @@ msgpackdump({list}) List dump a list of objects to msgpack msgpackparse({list}) List parse msgpack to a list of objects nextnonblank({lnum}) Number line nr of non-blank line >= {lnum} nr2char({expr}[, {utf8}]) String single char with ASCII/UTF8 value {expr} +nvim_...({args}...) any call nvim |api| functions or({expr}, {expr}) Number bitwise OR pathshorten({expr}) String shorten directory names in a path pow({x}, {y}) Float {x} to the power of {y} @@ -2147,6 +2148,7 @@ values({dict}) List values in {dict} virtcol({expr}) Number screen column of cursor or mark visualmode([expr]) String last visual mode used wildmenumode() Number whether 'wildmenu' mode is active +win_findbuf( {bufnr}) List find windows containing {bufnr} win_getid( [{win} [, {tab}]]) Number get window ID for {win} in {tab} win_gotoid( {expr}) Number go to window with ID {expr} win_id2tabwin( {expr}) List get tab window nr from window ID @@ -2208,11 +2210,9 @@ and({expr}, {expr}) *and()* Example: > :let flag = and(bits, 0x80) - api_info() *api_info()* Returns Dictionary of |api-metadata|. - append({lnum}, {expr}) *append()* When {expr} is a |List|: Append each item of the |List| as a text line below line {lnum} in the current buffer. @@ -5172,6 +5172,17 @@ nr2char({expr}[, {utf8}]) *nr2char()* characters. nr2char(0) is a real NUL and terminates the string, thus results in an empty string. +nvim_...({...}) *nvim_...()* *eval-api* + Call nvim |api| functions. The type checking of arguments will + be stricter than for most other builtins. For instance, + if Integer is expected, a |Number| must be passed in, a + |String| will not be autoconverted. + Buffer numbers, as returned by |bufnr()| could be used as + first argument to nvim_buf_... functions. All functions + expecting an object (buffer, window or tabpage) can + also take the numerical value 0 to indicate the current + (focused) object. + or({expr}, {expr}) *or()* Bitwise OR on the two arguments. The arguments are converted to a number. A List, Dict or Float argument causes an error. @@ -5470,14 +5481,20 @@ reltime([{start} [, {end}]]) *reltime()* the item depends on the system. It can be passed to |reltimestr()| to convert it to a string or |reltimefloat()| to convert to a float. - Without an argument it returns the current time. - With one argument is returns the time passed since the time + + Without an argument it returns the current "relative time", an + implementation-defined value meaningful only when used as an + argument to |reltime()|, |reltimestr()| and |reltimefloat()|. + + With one argument it returns the time passed since the time specified in the argument. With two arguments it returns the time passed between {start} and {end}. The {start} and {end} arguments must be values returned by reltime(). + Note: |localtime()| returns the current (non-relative) time. + reltimefloat({time}) *reltimefloat()* Return a Float that represents the time value of {time}. Unit of time is seconds. @@ -7218,6 +7235,10 @@ wildmenumode() *wildmenumode()* (Note, this needs the 'wildcharm' option set appropriately). +win_findbuf({bufnr}) *win_findbuf()* + Returns a list with window IDs for windows that contain buffer + {bufnr}. When there is none the list is empty. + win_getid([{win} [, {tab}]]) *win_getid()* Get the window ID for the specified window. When {win} is missing use the current window. diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index bbe2bbe50f..df6b55cfe7 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -529,14 +529,12 @@ Man {sect} {name}({sect}) Used during completion to show the real section of when the provided section is a prefix, e.g. 1m vs 1. Man {path} Open the manpage specified by path. Prepend "./" if page is in the current directory. +Man Open the manpage for the <cWORD> (man buffers) + or <cword> (non-man buffers) under the cursor. |:Man| accepts command modifiers. For example, to use a vertical split: > :vertical Man printf -Global Mappings: -<Plug>(Man) Jump to the manpage for the <cWORD> under the - cursor. Takes a count for the section. - Local mappings: K or CTRL-] Jump to the manpage for the <cWORD> under the cursor. Takes a count for the section. @@ -547,6 +545,9 @@ q :quit if invoked as $MANPAGER, otherwise :close. Variables: *g:no_man_maps* Do not create mappings in manpage buffers. *g:ft_man_folding_enable* Fold manpages with foldmethod=indent foldnestmax=1. +*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' PDF *ft-pdf-plugin* diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index c1eef398e2..4561020d22 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1406,6 +1406,27 @@ The valid escape sequences are <bang> (See the '-bang' attribute) Expands to a ! if the command was executed with a ! modifier, otherwise expands to nothing. + *<mods>* + <mods> The command modifiers, if specified. Otherwise, expands to + nothing. Supported modifiers are |aboveleft|, |belowright|, + |botright|, |browse|, |confirm|, |hide|, |keepalt|, + |keepjumps|, |keepmarks|, |keeppatterns|, |lockmarks|, + |noswapfile|, |silent|, |tab|, |topleft|, |verbose|, and + |vertical|. + Examples: > + command! -nargs=+ -complete=file MyEdit + \ for f in expand(<q-args>, 0, 1) | + \ exe '<mods> split ' . f | + \ endfor + + function! SpecialEdit(files, mods) + for f in expand(a:files, 0, 1) + exe a:mods . ' split ' . f + endfor + endfunction + command! -nargs=+ -complete=file Sedit + \ call SpecialEdit(<q-args>, <q-mods>) +< *<reg>* *<register>* <reg> (See the '-register' attribute) The optional register, if specified. Otherwise, expands to nothing. <register> diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index 18c0ff8a58..b3fed9e756 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -11,6 +11,7 @@ RPC API for Nvim *RPC* *rpc* *msgpack-rpc* 3. Connecting |rpc-connecting| 4. Clients |rpc-api-client| 5. Types |rpc-types| +6. Remote UIs |rpc-remote-ui| ============================================================================== 1. Introduction *rpc-intro* @@ -46,7 +47,7 @@ instance. There are three ways to obtain API metadata: - 1. Connect to a running Nvim instance and call `vim_get_api_info` via + 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 can define functions at runtime. @@ -104,7 +105,7 @@ Nvim instance: require 'msgpack/rpc/transport/unix' nvim = MessagePack::RPC::Client.new(MessagePack::RPC::UNIXTransport.new, ENV['NVIM_LISTEN_ADDRESS']) - result = nvim.call(:vim_command, 'echo "hello world!"') + result = nvim.call(:nvim_command, 'echo "hello world!"') < A better way is to use the Python REPL with the `neovim` package, where API functions can be called interactively: @@ -116,24 +117,23 @@ functions can be called interactively: You can also embed an Nvim instance via |jobstart()|, and communicate using |rpcrequest()| and |rpcnotify()|: > - let vim = jobstart(['nvim', '--embed'], {'rpc': v:true}) - echo rpcrequest(vim, 'vim_eval', '"Hello " . "world!"') - call jobstop(vim) + let nvim = jobstart(['nvim', '--embed'], {'rpc': v:true}) + echo rpcrequest(nvim, 'nvim_eval', '"Hello " . "world!"') + call jobstop(nvim) < ============================================================================== 4. Implementing API clients *rpc-api-client* *api-client* -All external UIs and remote plugins (as opposed to regular Vim plugins) are -"clients" in general; but we call something an "API client" if its purpose is -to abstract or wrap the RPC API for the convenience of other applications -(just like a REST client or SDK such as boto3 for AWS: you can speak AWS REST -using an HTTP client like curl, but boto3 wraps that in a convenient python -interface). For example, the lua-client is an API client: - https://github.com/neovim/lua-client - -The Python client (pip package "neovim") is the reference implementation of an -API client. It is always up-to-date with the Nvim API, so its source code and -test suite are an authoritative reference. +"API clients" wrap the Nvim API to provide idiomatic "SDKs" for their +respective platforms (see |dev-jargon|). You can build a new API client for +your favorite platform or programming language. + +Existing API clients are listed here: + https://github.com/neovim/neovim/wiki/Related-projects#api-clients + +The Python client is the reference implementation for API clients. It is +always up-to-date with the Nvim API, so its source code and test suite are +authoritative references. https://github.com/neovim/python-client API client implementation guidelines ~ @@ -176,15 +176,20 @@ contains information that makes this task easier (see also |rpc-types|): - Container types may be decorated with type/size constraints, e.g. ArrayOf(Buffer) or ArrayOf(Integer, 2). This can be useful to generate even more strongly-typed APIs. - - Methods that operate on instances of Nvim special types (msgpack EXT) are - prefixed with the type name in lower case, e.g. `buffer_get_line` - represents the `get_line` method of a Buffer instance. - - Global methods are prefixed with `vim`, e.g. `vim_get_buffers`. + - Functions that are considered to be methods that operate on instances of + Nvim special types (msgpack EXT) will have the `"method"` attribute set to + `true`. The reciever type is the type of the first argument. The method + names are prefixed with `nvim_` plus a shortened type name, e.g. + `nvim_buf_get_lines` represents the `get_lines` method of a Buffer instance. + - Global functions have `"method"` set to `false` and are prefixed with just + `nvim_`, e.g. `nvim_get_buffers`. So for an object-oriented language, an API client contains the classes representing Nvim special types, and the methods of each class could be -defined by inspecting the method name prefix. There could also be a singleton -Vim class with methods mapped to functions prefixed with `vim_`. +defined by stripping the prefix for the type as defined in the `types` metadata +(this will always be the first two "_"-separated parts of the function name). +There could also be a singleton Vim class with methods where the `nvim_` +prefix is stripped off. ============================================================================== 5. Types *rpc-types* @@ -218,18 +223,21 @@ an integer, but not a Window or Tabpage. The most reliable way of determining the type codes for the special Nvim types is to inspect the `types` key of metadata dictionary returned by the -`vim_get_api_info` method at runtime. Here's a sample JSON representation of +`nvim_get_api_info` method at runtime. Here's a sample JSON representation of the `types` object: > "types": { "Buffer": { - "id": 0 + "id": 0, + "prefix": "nvim_buf_" }, "Window": { - "id": 1 + "id": 1, + "prefix": "nvim_win_" }, "Tabpage": { - "id": 2 + "id": 2, + "prefix": "nvim_tabpage_" } } < @@ -238,4 +246,169 @@ the type codes, because a client may be built against one Nvim version but connect to another with different type codes. ============================================================================== +6. Remote UIs *rpc-remote-ui* + +Nvim allows Graphical user interfaces to be implemented by separate processes +communicating with Nvim over the RPC API. Currently the ui model conists of a +terminal-like grid with one single, monospace font size, with a few elements +that could be drawn separately from the grid (for the momemnt only the popup +menu) + +After connecting to a nvim instance (typically a spawned, embedded instance) +use the |nvim_ui_attach|(width, height, options) API method to tell nvim that your +program wants to draw the nvim screen on a grid with "width" times +"height" cells. "options" should be a dictionary with the following (all +optional) keys: + `rgb`: Controls what color format to use. + Set to true (default) to use 24-bit rgb + colors. + Set to false to use terminal color codes (at + most 256 different colors). + `popupmenu_external`: Instead of drawing the completion popupmenu on + the grid, Nvim will send higher-level events to + the ui and let it draw the popupmenu. + Defaults to false. + +Nvim will then send msgpack-rpc notifications, with the method name "redraw" +and a single argument, an array of screen updates (described below). +These should be processed in order. Preferably the user should only be able to +see the screen state after all updates are processed (not any intermediate +state after processing only a part of the array). + +Screen updates are arrays. The first element a string describing the kind +of update. + +["resize", width, height] + The grid is resized to `width` and `height` cells. + +["clear"] + Clear the screen. + +["eol_clear"] + Clear from the cursor position to the end of the current line. + +["cursor_goto", row, col] + Move the cursor to position (row, col). Currently, the same cursor is + used to define the position for text insertion and the visible cursor. + However, only the last cursor position, after processing the entire + array in the "redraw" event, is intended to be a visible cursor + position. + +["update_fg", color] +["update_bg", color] +["update_sp", color] + Set the default foreground, background and special colors + respectively. + +["highlight_set", attrs] + Set the attributes that the next text put on the screen will have. + `attrs` is a dict with the keys below. Any absent key is reset + to its default value. Color defaults are set by the `update_fg` etc + updates. All boolean keys default to false. + + `foreground`: foreground color. + `background`: backround color. + `special`: color to use for underline and undercurl, when present. + `reverse`: reverse video. Foreground and background colors are + switched. + `italic`: italic text. + `bold`: bold text. + `underline`: underlined text. The line has `special` color. + `undercurl`: undercurled text. The curl has `special` color. + +["put", text] + The (utf-8 encoded) string `text` is put at the cursor position + (and the cursor is advanced), with the highlights as set by the + last `highlight_set` update. + +["set_scroll_region", top, bot, left, right] + Define the scroll region used by `scroll` below. + +["scroll", count] + Scroll the text in the scroll region. The diagrams below illustrate + what will happen, depending on the scroll direction. "=" is used to + represent the SR(scroll region) boundaries and "-" the moved rectangles. + Note that dst and src share a common region. + + If count is bigger than 0, move a rectangle in the SR up, this can + happen while scrolling down. +> + +-------------------------+ + | (clipped above SR) | ^ + |=========================| dst_top | + | dst (still in SR) | | + +-------------------------+ src_top | + | src (moved up) and dst | | + |-------------------------| dst_bot | + | src (cleared) | | + +=========================+ src_bot +< + If count is less than zero, move a rectangle in the SR down, this can + happen while scrolling up. +> + +=========================+ src_top + | src (cleared) | | + |------------------------ | dst_top | + | src (moved down) and dst| | + +-------------------------+ src_bot | + | dst (still in SR) | | + |=========================| dst_bot | + | (clipped below SR) | v + +-------------------------+ +< +["set_title", title] +["set_icon", icon] + Set the window title, and icon (minimized) window title, respectively. + In windowing systems not distinguishing between the two, "set_icon" + can be ignored. + +["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. + +["busy_on"] +["busy_off"] + Nvim started or stopped being busy, and possibly not responsible to user + input. This could be indicated to the user by hiding the cursor. + +["suspend"] + |:suspend| command or |Ctrl-Z| mapping is used. A terminal client (or other + client where it makes sense) could suspend itself. Other clients can + safely ignore it. + +["bell"] +["visual_bell"] + Notify the user with an audible or visual bell, respectively. + +["update_menu"] + The menu mappings changed. + +["mode_change", mode] + The mode changed. Currently sent when "insert", "replace" and "normal" + modes are entered. A client could for instance change the cursor shape. + +["popupmenu_show", items, selected, row, col] + When `popupmenu_external` is set to true, nvim will not draw the + popupmenu on the grid, instead when the popupmenu is to be displayed + this update is sent. `items` is an array of the items to show, the + items are themselves arrays of the form [word, kind, menu, info] + as defined at |complete-items|, except that `word` is replaced by + `abbr` if present. `selected` is the initially selected item, either a + zero-based index into the array of items, or -1 if no item is + selected. `row` and `col` is the anchor position, where the first + character of the completed word will be. + +["popupmenu_select", selected] + An item in the currently displayed popupmenu is selected. `selected` + is either a zero-based index into the array of items from the last + `popupmenu_show` event, or -1 if no item is selected. + +["popupmenu_hide"] + The popupmenu is hidden. + +============================================================================== vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 3b54faf18e..e2a44541ae 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1,4 +1,4 @@ -*quickfix.txt* For Vim version 7.4. Last change: 2016 Mar 19 +*quickfix.txt* For Vim version 7.4. Last change: 2016 Jul 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -259,9 +259,23 @@ location list command, it will be aborted. The 'switchbuf' settings are respected when jumping to a buffer. +:cl[ist] +{count} List the current and next {count} valid errors. This + is similar to ":clist from from+count", where "from" + is the current error position. + :cl[ist]! [from] [, [to]] List all errors. +:cl[ist]! +{count} List the current and next {count} error lines. This + is useful to see unrecognized lines after the current + one. For example, if ":clist" shows: + 8384 testje.java:252: error: cannot find symbol ~ + Then using ":cl! +3" shows the reason: + 8384 testje.java:252: error: cannot find symbol ~ + 8385: ZexitCode = Fmainx(); ~ + 8386: ^ ~ + 8387: symbol: method Fmainx() ~ + *:lli* *:llist* :lli[st] [from] [, [to]] Same as ":clist", except the location list for the @@ -306,7 +320,7 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST: etc. < When the current file can't be |abandon|ed and the [!] is not present, the command fails. - When an error is detected excecution stops. + When an error is detected execution stops. The last buffer (or where an error occurred) becomes the current buffer. {cmd} can contain '|' to concatenate several commands. diff --git a/runtime/ftplugin/c.vim b/runtime/ftplugin/c.vim index 487ce7a165..d1b2a4941e 100644 --- a/runtime/ftplugin/c.vim +++ b/runtime/ftplugin/c.vim @@ -55,5 +55,7 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") endif endif +let b:man_default_sects = '3,2' + let &cpo = s:cpo_save unlet s:cpo_save diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 6a9ad27956..02d2b4e557 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -19,7 +19,9 @@ if has('vim_starting') endif " This is not perfect. See `man glDrawArraysInstanced`. Since the title is " all caps it is impossible to tell what the original capitilization was. - execute 'file man://'.tolower(matchstr(getline(1), '^\S\+')) + let ref = tolower(matchstr(getline(1), '^\S\+')) + let b:man_sect = man#extract_sect_and_name_ref(ref)[0] + execute 'file man://'.ref endif setlocal buftype=nofile @@ -41,8 +43,8 @@ setlocal nolist setlocal nofoldenable if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') - nmap <silent> <buffer> <C-]> <Plug>(Man) - nmap <silent> <buffer> K <Plug>(Man) + nmap <silent> <buffer> <C-]> :Man<CR> + nmap <silent> <buffer> K :Man<CR> nnoremap <silent> <buffer> <C-T> :call man#pop_tag()<CR> if s:pager nnoremap <silent> <buffer> <nowait> q :q<CR> diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index d49276047f..63faa15213 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -5,9 +5,7 @@ if exists('g:loaded_man') endif let g:loaded_man = 1 -command! -range=0 -complete=customlist,man#complete -nargs=+ Man call man#open_page(v:count, v:count1, <f-args>) - -nnoremap <silent> <Plug>(Man) :<C-U>call man#open_page(v:count, v:count1, &filetype ==# 'man' ? expand('<cWORD>') : expand('<cword>'))<CR> +command! -range=0 -complete=customlist,man#complete -nargs=* Man call man#open_page(v:count, v:count1, <q-mods>, <f-args>) augroup man autocmd! diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index 5dd41b3af5..4a527dd350 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -7,10 +7,10 @@ endif syntax case ignore syntax match manReference display '[^()[:space:]]\+([0-9nx][a-z]*)' -syntax match manSectionHeading display '^\%(\S.*\)\=\S$' +syntax match manSectionHeading display '^\S.*$' syntax match manTitle display '^\%1l.*$' syntax match manSubHeading display '^ \{3\}\S.*$' -syntax match manOptionDesc display '^\s\+\%(+\|--\=\)\S\+' +syntax match manOptionDesc display '^\s\+\%(+\|-\)\S\+' highlight default link manTitle Title highlight default link manSectionHeading Statement @@ -18,7 +18,7 @@ highlight default link manOptionDesc Constant highlight default link manReference PreProc highlight default link manSubHeading Function -if getline(1) =~# '^[^()[:space:]]\+([23].*' +if b:man_sect =~# '^[23]' syntax include @c $VIMRUNTIME/syntax/c.vim syntax match manCFuncDefinition display '\<\h\w*\>\ze\(\s\|\n\)*(' contained syntax region manSynopsis start='^\%( |