aboutsummaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/CMakeLists.txt4
-rw-r--r--runtime/autoload/health.vim49
-rw-r--r--runtime/autoload/health/nvim.vim377
-rw-r--r--runtime/autoload/health/provider.vim382
-rw-r--r--runtime/autoload/man.vim157
-rw-r--r--runtime/doc/api.txt33
-rw-r--r--runtime/doc/develop.txt50
-rw-r--r--runtime/doc/eval.txt29
-rw-r--r--runtime/doc/filetype.txt9
-rw-r--r--runtime/doc/map.txt21
-rw-r--r--runtime/doc/msgpack_rpc.txt225
-rw-r--r--runtime/doc/quickfix.txt18
-rw-r--r--runtime/ftplugin/c.vim2
-rw-r--r--runtime/ftplugin/man.vim8
-rw-r--r--runtime/plugin/man.vim4
-rw-r--r--runtime/syntax/man.vim6
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='^\%(