diff options
Diffstat (limited to 'runtime/autoload')
23 files changed, 761 insertions, 434 deletions
diff --git a/runtime/autoload/ada.vim b/runtime/autoload/ada.vim index cc5191fa43..ce3a19369a 100644 --- a/runtime/autoload/ada.vim +++ b/runtime/autoload/ada.vim @@ -2,12 +2,13 @@ " Description: Perform Ada specific completion & tagging. " Language: Ada (2005) " $Id: ada.vim 887 2008-07-08 14:29:01Z krischik $ -" Maintainer: Martin Krischik <krischik@users.sourceforge.net> +" Maintainer: Mathias Brousset <mathiasb17@gmail.com> +" Martin Krischik <krischik@users.sourceforge.net> " Taylor Venable <taylor@metasyntax.net> " Neil Bird <neil@fnxweb.com> " Ned Okie <nokie@radford.edu> " $Author: krischik $ -" $Date: 2008-07-08 16:29:01 +0200 (Di, 08 Jul 2008) $ +" $Date: 2017-01-31 20:20:05 +0200 (Mon, 01 Jan 2017) $ " Version: 4.6 " $Revision: 887 $ " $HeadURL: https://gnuada.svn.sourceforge.net/svnroot/gnuada/trunk/tools/vim/autoload/ada.vim $ @@ -23,6 +24,7 @@ " 09.05.2007 MK Session just won't work no matter how much " tweaking is done " 19.09.2007 NO still some mapleader problems +" 31.01.2017 MB fix more mapleader problems " Help Page: ft-ada-functions "------------------------------------------------------------------------------ @@ -585,11 +587,11 @@ function ada#Map_Menu (Text, Keys, Command) \ " :" . a:Command . "<CR>" execute \ "nnoremap <buffer>" . - \ escape(l:leader . "a" . a:Keys , '\') . + \ " <Leader>a" . a:Keys . \" :" . a:Command execute \ "inoremap <buffer>" . - \ escape(l:leader . "a" . a:Keys , '\') . + \ " <Learder>a" . a:Keys . \" <C-O>:" . a:Command endif return diff --git a/runtime/autoload/context.vim b/runtime/autoload/context.vim new file mode 100644 index 0000000000..254d710c01 --- /dev/null +++ b/runtime/autoload/context.vim @@ -0,0 +1,184 @@ +" Language: ConTeXt typesetting engine +" Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com> +" Latest Revision: 2016 Oct 21 + +let s:keepcpo= &cpo +set cpo&vim + +" Helper functions {{{ +function! s:context_echo(message, mode) + redraw + echo "\r" + execute 'echohl' a:mode + echomsg '[ConTeXt]' a:message + echohl None +endf + +function! s:sh() + return has('win32') || has('win64') || has('win16') || has('win95') + \ ? ['cmd.exe', '/C'] + \ : ['/bin/sh', '-c'] +endfunction + +" For backward compatibility +if exists('*win_getid') + + function! s:win_getid() + return win_getid() + endf + + function! s:win_id2win(winid) + return win_id2win(a:winid) + endf + +else + + function! s:win_getid() + return winnr() + endf + + function! s:win_id2win(winnr) + return a:winnr + endf + +endif +" }}} + +" ConTeXt jobs {{{ +if has('job') + + let g:context_jobs = [] + + " Print the status of ConTeXt jobs + function! context#job_status() + let l:jobs = filter(g:context_jobs, 'job_status(v:val) == "run"') + let l:n = len(l:jobs) + call s:context_echo( + \ 'There '.(l:n == 1 ? 'is' : 'are').' '.(l:n == 0 ? 'no' : l:n) + \ .' job'.(l:n == 1 ? '' : 's').' running' + \ .(l:n == 0 ? '.' : ' (' . join(l:jobs, ', ').').'), + \ 'ModeMsg') + endfunction + + " Stop all ConTeXt jobs + function! context#stop_jobs() + let l:jobs = filter(g:context_jobs, 'job_status(v:val) == "run"') + for job in l:jobs + call job_stop(job) + endfor + sleep 1 + let l:tmp = [] + for job in l:jobs + if job_status(job) == "run" + call add(l:tmp, job) + endif + endfor + let g:context_jobs = l:tmp + if empty(g:context_jobs) + call s:context_echo('Done. No jobs running.', 'ModeMsg') + else + call s:context_echo('There are still some jobs running. Please try again.', 'WarningMsg') + endif + endfunction + + function! context#callback(path, job, status) + if index(g:context_jobs, a:job) != -1 && job_status(a:job) != 'run' " just in case + call remove(g:context_jobs, index(g:context_jobs, a:job)) + endif + call s:callback(a:path, a:job, a:status) + endfunction + + function! context#close_cb(channel) + call job_status(ch_getjob(a:channel)) " Trigger exit_cb's callback for faster feedback + endfunction + + function! s:typeset(path) + call add(g:context_jobs, + \ job_start(add(s:sh(), context#command() . ' ' . shellescape(fnamemodify(a:path, ":t"))), { + \ 'close_cb' : 'context#close_cb', + \ 'exit_cb' : function(get(b:, 'context_callback', get(g:, 'context_callback', 'context#callback')), + \ [a:path]), + \ 'in_io' : 'null' + \ })) + endfunction + +else " No jobs + + function! context#job_status() + call s:context_echo('Not implemented', 'WarningMsg') + endfunction! + + function! context#stop_jobs() + call s:context_echo('Not implemented', 'WarningMsg') + endfunction + + function! context#callback(path, job, status) + call s:callback(a:path, a:job, a:status) + endfunction + + function! s:typeset(path) + execute '!' . context#command() . ' ' . shellescape(fnamemodify(a:path, ":t")) + call call(get(b:, 'context_callback', get(g:, 'context_callback', 'context#callback')), + \ [a:path, 0, v:shell_error]) + endfunction + +endif " has('job') + +function! s:callback(path, job, status) abort + if a:status < 0 " Assume the job was terminated + return + endif + " Get info about the current window + let l:winid = s:win_getid() " Save window id + let l:efm = &l:errorformat " Save local errorformat + let l:cwd = fnamemodify(getcwd(), ":p") " Save local working directory + " Set errorformat to parse ConTeXt errors + execute 'setl efm=' . escape(b:context_errorformat, ' ') + try " Set cwd to expand error file correctly + execute 'lcd' fnameescape(fnamemodify(a:path, ':h')) + catch /.*/ + execute 'setl efm=' . escape(l:efm, ' ') + throw v:exception + endtry + try + execute 'cgetfile' fnameescape(fnamemodify(a:path, ':r') . '.log') + botright cwindow + finally " Restore cwd and errorformat + execute s:win_id2win(l:winid) . 'wincmd w' + execute 'lcd ' . fnameescape(l:cwd) + execute 'setl efm=' . escape(l:efm, ' ') + endtry + if a:status == 0 + call s:context_echo('Success!', 'ModeMsg') + else + call s:context_echo('There are errors. ', 'ErrorMsg') + endif +endfunction + +function! context#command() + return get(b:, 'context_mtxrun', get(g:, 'context_mtxrun', 'mtxrun')) + \ . ' --script context --autogenerate --nonstopmode' + \ . ' --synctex=' . (get(b:, 'context_synctex', get(g:, 'context_synctex', 0)) ? '1' : '0') + \ . ' ' . get(b:, 'context_extra_options', get(g:, 'context_extra_options', '')) +endfunction + +" Accepts an optional path (useful for big projects, when the file you are +" editing is not the project's root document). If no argument is given, uses +" the path of the current buffer. +function! context#typeset(...) abort + let l:path = fnamemodify(strlen(a:000[0]) > 0 ? a:1 : expand("%"), ":p") + let l:cwd = fnamemodify(getcwd(), ":p") " Save local working directory + call s:context_echo('Typesetting...', 'ModeMsg') + execute 'lcd' fnameescape(fnamemodify(l:path, ":h")) + try + call s:typeset(l:path) + finally " Restore local working directory + execute 'lcd ' . fnameescape(l:cwd) + endtry +endfunction! +"}}} + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim: sw=2 fdm=marker diff --git a/runtime/autoload/contextcomplete.vim b/runtime/autoload/contextcomplete.vim new file mode 100644 index 0000000000..5b93bb0986 --- /dev/null +++ b/runtime/autoload/contextcomplete.vim @@ -0,0 +1,25 @@ +" Language: ConTeXt typesetting engine +" Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com> +" Latest Revision: 2016 Oct 15 + +let s:keepcpo= &cpo +set cpo&vim + +" Complete keywords in MetaPost blocks +function! contextcomplete#Complete(findstart, base) + if a:findstart == 1 + if len(synstack(line('.'), 1)) > 0 && + \ synIDattr(synstack(line('.'), 1)[0], "name") ==# 'contextMPGraphic' + return syntaxcomplete#Complete(a:findstart, a:base) + else + return -3 + endif + else + return syntaxcomplete#Complete(a:findstart, a:base) + endif +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim: sw=2 fdm=marker diff --git a/runtime/autoload/gzip.vim b/runtime/autoload/gzip.vim index a6b4605b06..e4adec0947 100644 --- a/runtime/autoload/gzip.vim +++ b/runtime/autoload/gzip.vim @@ -1,6 +1,6 @@ " Vim autoload file for editing compressed files. " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2014 Nov 05 +" Last Change: 2016 Sep 28 " These functions are used by the gzip plugin. @@ -63,6 +63,9 @@ fun gzip#read(cmd) " set 'modifiable' let ma_save = &ma setlocal ma + " set 'write' + let write_save = &write + set write " Reset 'foldenable', otherwise line numbers get adjusted. if has("folding") let fen_save = &fen @@ -127,6 +130,7 @@ fun gzip#read(cmd) let &pm = pm_save let &cpo = cpo_save let &l:ma = ma_save + let &write = write_save if has("folding") let &l:fen = fen_save endif diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 93ca4dfc54..bd99a0e104 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -9,17 +9,9 @@ function! s:enhance_syntax() abort \ containedin=markdownCodeBlock,mkdListItemLine highlight link healthWarning WarningMsg - syntax keyword healthInfo INFO - \ containedin=markdownCodeBlock,mkdListItemLine - highlight link healthInfo ModeMsg - syntax keyword healthSuccess SUCCESS \ containedin=markdownCodeBlock,mkdListItemLine - highlight link healthSuccess ModeMsg - - syntax keyword healthSuggestion SUGGESTIONS - \ containedin=markdownCodeBlock,mkdListItemLine - highlight link healthSuggestion String + highlight healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 syntax match healthHelp "|.\{-}|" contains=healthBar \ containedin=markdownCodeBlock,mkdListItemLine @@ -42,6 +34,7 @@ function! health#check(plugin_names) abort setlocal filetype=markdown setlocal conceallevel=2 concealcursor=nc setlocal keywordprg=:help + let &l:iskeyword='!-~,^*,^|,^",192-255' call s:enhance_syntax() if empty(healthchecks) @@ -74,6 +67,7 @@ function! health#check(plugin_names) abort " needed for plasticboy/vim-markdown, because it uses fdm=expr normal! zR setlocal nomodified + setlocal bufhidden=hide redraw|echo '' endfunction @@ -96,7 +90,7 @@ endfunction " Changes ':h clipboard' to ':help |clipboard|'. function! s:help_to_link(s) abort - return substitute(a:s, '\v[''"]?:h%[elp] ([^''"]+)[''"]?', '":help |\1|"', 'g') + return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n ]+)', ':help |\1|', 'g') endfunction " Format a message for a specific report item diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index e2ad9f7ccb..3834cbd054 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -1,15 +1,18 @@ -let s:suggest_faq = 'See https://github.com/neovim/neovim/wiki/FAQ' +let s:suggest_faq = 'https://github.com/neovim/neovim/wiki/FAQ' function! s:check_config() abort + let ok = v:true call health#report_start('Configuration') - if !get(g:, 'loaded_sensible', 0) + + if exists('$NVIM_TUI_ENABLE_CURSOR_SHAPE') + let ok = v:false + call health#report_warn("$NVIM_TUI_ENABLE_CURSOR_SHAPE is ignored in Nvim 0.2+", + \ [ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'", + \ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ]) + endif + + if ok call health#report_ok('no issues found') - else - let sensible_pi = globpath(&runtimepath, '**/sensible.vim', 1, 1) - call health#report_info("found sensible.vim plugin:\n".join(sensible_pi, "\n")) - call health#report_error("sensible.vim plugin is not needed; Nvim has the same defaults built-in." - \ ." Also, sensible.vim sets 'ttimeoutlen' to a sub-optimal value.", - \ ["Remove sensible.vim plugin, or wrap it in a `if !has('nvim')` check."]) endif endfunction @@ -113,11 +116,27 @@ function! s:check_tmux() abort call health#report_ok('escape-time: '.tmux_esc_time.'ms') endif - " check $TERM + " check default-terminal and $TERM call health#report_info('$TERM: '.$TERM) - if $TERM !~# '\v(tmux-256color|screen-256color)' + let cmd = 'tmux show-option -qvg default-terminal' + let out = system(cmd) + let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g') + if empty(tmux_default_term) + let cmd = 'tmux show-option -qvgs default-terminal' + let out = system(cmd) + let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g') + endif + + if v:shell_error + call health#report_error('command failed: '.cmd."\n".out) + elseif tmux_default_term !=# $TERM + call health#report_info('default-terminal: '.tmux_default_term) + call health#report_error( + \ '$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.', + \ ['$TERM may have been set by some rc (.bashrc, .zshrc, ...).']) + elseif $TERM !~# '\v(tmux-256color|screen-256color)' call health#report_error( - \ '$TERM should be "screen-256color" or "tmux-256color" when running tmux.', + \ '$TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.', \ ["Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal \"screen-256color\"", \ s:suggest_faq]) endif diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 417426c101..26db5b77b7 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -8,6 +8,11 @@ function! s:trim(s) abort return substitute(a:s, '^\_s*\|\_s*$', '', 'g') endfunction +" Convert '\' to '/'. Collapse '//' and '/./'. +function! s:normalize_path(s) abort + return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g') +endfunction + " Simple version comparison. function! s:version_cmp(a, b) abort let a = split(a:a, '\.', 0) @@ -26,13 +31,23 @@ endfunction " Handler for s:system() function. function! s:system_handler(jobid, data, event) dict abort - if a:event == 'stdout' || a:event == 'stderr' + if a:event ==# 'stdout' || a:event ==# 'stderr' let self.output .= join(a:data, '') - elseif a:event == 'exit' + elseif a:event ==# 'exit' let s:shell_error = a:data endif endfunction +" Attempts to construct a shell command from an args list. +" Only for display, to help users debug a failed command. +function! s:shellify(cmd) abort + if type(a:cmd) != type([]) + return a:cmd + endif + return join(map(copy(a:cmd), + \'v:val =~# ''\m[\-.a-zA-Z_/]'' ? shellescape(v:val) : v:val'), ' ') +endfunction + " Run a system command and timeout after 30 seconds. function! s:system(cmd, ...) abort let stdin = a:0 ? a:1 : '' @@ -49,8 +64,7 @@ function! s:system(cmd, ...) abort let jobid = jobstart(a:cmd, opts) if jobid < 1 - call health#report_error(printf('Command error %d: %s', jobid, - \ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd))) + call health#report_error(printf('Command error (job=%d): %s', jobid, s:shellify(a:cmd))) let s:shell_error = 1 return opts.output endif @@ -61,13 +75,11 @@ function! s:system(cmd, ...) abort let res = jobwait([jobid], 30000) if res[0] == -1 - call health#report_error(printf('Command timed out: %s', - \ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd)) + call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd))) call jobstop(jobid) elseif s:shell_error != 0 && !ignore_error - call health#report_error(printf("Command error (%d) %s: %s", jobid, - \ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd, - \ opts.output)) + call health#report_error(printf("Command error (job=%d): %s\nOutput: %s", jobid, + \ s:shellify(a:cmd), opts.output)) endif return opts.output @@ -106,13 +118,17 @@ endfunction " Check for clipboard tools. function! s:check_clipboard() abort - call health#report_start('Clipboard') + call health#report_start('Clipboard (optional)') let clipboard_tool = provider#clipboard#Executable() - if empty(clipboard_tool) + if exists('g:clipboard') && empty(clipboard_tool) + call health#report_error( + \ provider#clipboard#Error(), + \ ["Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."]) + elseif empty(clipboard_tool) call health#report_warn( - \ "No clipboard tool found. Clipboard registers will not work.", - \ ['See ":help clipboard".']) + \ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.', + \ [':help clipboard']) else call health#report_ok('Clipboard tool found: '. clipboard_tool) endif @@ -152,7 +168,7 @@ function! s:version_info(python) abort \ ])) if empty(python_version) - let python_version = 'unable to parse python response' + let python_version = 'unable to parse '.a:python.' response' endif let nvim_path = s:trim(s:system([ @@ -164,14 +180,14 @@ function! s:version_info(python) abort " Assuming that multiple versions of a package are installed, sort them " numerically in descending order. - function! s:compare(metapath1, metapath2) + function! s:compare(metapath1, metapath2) abort let a = matchstr(fnamemodify(a:metapath1, ':p:h:t'), '[0-9.]\+') let b = matchstr(fnamemodify(a:metapath2, ':p:h:t'), '[0-9.]\+') return a == b ? 0 : a > b ? 1 : -1 endfunction " Try to get neovim.VERSION (added in 0.1.11dev). - let nvim_version = s:system(['python', '-c', + let nvim_version = s:system([a:python, '-c', \ 'from neovim import VERSION as v; '. \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'], \ '', 1, 1) @@ -208,7 +224,7 @@ endfunction " Check the Python interpreter's usability. function! s:check_bin(bin) abort - if !filereadable(a:bin) + if !filereadable(a:bin) && (!has('win32') || !filereadable(a:bin.'.exe')) call health#report_error(printf('"%s" was not found.', a:bin)) return 0 elseif executable(a:bin) != 1 @@ -219,22 +235,28 @@ function! s:check_bin(bin) abort endfunction function! s:check_python(version) abort - call health#report_start('Python ' . a:version . ' provider') + call health#report_start('Python ' . a:version . ' provider (optional)') - let python_bin_name = 'python'.(a:version == 2 ? '' : '3') + let pyname = 'python'.(a:version == 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_prog_var = pyname.'_host_prog' + let loaded_var = 'g:loaded_'.pyname.'_provider' let python_bin = '' let python_multiple = [] + if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') + call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + return + endif + 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) + let [pyname, pythonx_errs] = provider#pythonx#Detect(a:version) + if empty(pyname) call health#report_warn('No Python interpreter was found with the neovim ' \ . 'module. Using the first available for diagnostics.') if !empty(pythonx_errs) @@ -242,21 +264,21 @@ function! s:check_python(version) abort endif endif - if !empty(python_bin_name) + if !empty(pyname) if exists('g:'.host_prog_var) - let python_bin = exepath(python_bin_name) + let python_bin = exepath(pyname) endif - let python_bin_name = fnamemodify(python_bin_name, ':t') + let pyname = fnamemodify(pyname, ':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 !empty(pyname) && 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)) + \ . '%s in the environment.', host_prog_var, pyname)) endif if !empty(pyenv) @@ -269,20 +291,21 @@ function! s:check_python(version) abort call health#report_ok(printf('pyenv found: "%s"', pyenv)) endif - let python_bin = s:trim(s:system([pyenv, 'which', python_bin_name], '', 1)) + let python_bin = s:trim(s:system([pyenv, 'which', pyname], '', 1)) if empty(python_bin) - call health#report_warn(printf('pyenv could not find %s.', python_bin_name)) + call health#report_warn(printf('pyenv could not find %s.', pyname)) endif endif if empty(python_bin) - let python_bin = exepath(python_bin_name) + let python_bin = exepath(pyname) 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 + for path in split($PATH, has('win32') ? ';' : ':') + let path_bin = s:normalize_path(path.'/'.pyname) + if path_bin != s:normalize_path(python_bin) + \ && index(python_multiple, path_bin) == -1 \ && executable(path_bin) call add(python_multiple, path_bin) endif @@ -292,7 +315,7 @@ function! s:check_python(version) abort " This is worth noting since the user may install something " that changes $PATH, like homebrew. call health#report_info(printf('Multiple %s executables found. ' - \ . 'Set `g:%s` to avoid surprises.', python_bin_name, host_prog_var)) + \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var)) endif if python_bin =~# '\<shims\>' @@ -333,9 +356,9 @@ function! s:check_python(version) abort endif endif - if empty(python_bin) && !empty(python_bin_name) + if empty(python_bin) && !empty(pyname) " An error message should have already printed. - call health#report_error(printf('`%s` was not found.', python_bin_name)) + call health#report_error(printf('`%s` was not found.', pyname)) elseif !empty(python_bin) && !s:check_bin(python_bin) let python_bin = '' endif @@ -349,7 +372,7 @@ function! s:check_python(version) abort if $VIRTUAL_ENV != pyenv_prefix let virtualenv_inactive = 1 endif - elseif !empty(python_bin_name) && exepath(python_bin_name) !~# '^'.$VIRTUAL_ENV.'/' + elseif !empty(pyname) && exepath(pyname) !~# '^'.$VIRTUAL_ENV.'/' let virtualenv_inactive = 1 endif endif @@ -381,9 +404,9 @@ function! s:check_python(version) abort call health#report_info('Python'.a:version.' version: ' . pyversion) if s:is_bad_response(status) - call health#report_info(printf('%s-neovim version: %s (%s)', python_bin_name, current, status)) + call health#report_info(printf('%s-neovim version: %s (%s)', pyname, current, status)) else - call health#report_info(printf('%s-neovim version: %s', python_bin_name, current)) + call health#report_info(printf('%s-neovim version: %s', pyname, current)) endif if s:is_bad_response(current) @@ -397,22 +420,28 @@ function! s:check_python(version) abort call health#report_error('HTTP request failed: '.latest) elseif s:is_bad_response(status) call health#report_warn(printf('Latest %s-neovim is NOT installed: %s', - \ python_bin_name, latest)) + \ pyname, latest)) elseif !s:is_bad_response(current) call health#report_ok(printf('Latest %s-neovim is installed: %s', - \ python_bin_name, latest)) + \ pyname, latest)) endif endif endfunction function! s:check_ruby() abort - call health#report_start('Ruby provider') + call health#report_start('Ruby provider (optional)') + + let loaded_var = 'g:loaded_ruby_provider' + if exists(loaded_var) && !exists('*provider#ruby#Call') + call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + return + endif if !executable('ruby') || !executable('gem') call health#report_warn( - \ "`ruby` and `gem` must be in $PATH.", - \ ["Install Ruby and verify that `ruby` and `gem` commands work."]) + \ '`ruby` and `gem` must be in $PATH.', + \ ['Install Ruby and verify that `ruby` and `gem` commands work.']) return endif call health#report_info('Ruby: '. s:system('ruby -v')) @@ -420,26 +449,28 @@ function! s:check_ruby() abort let host = provider#ruby#Detect() if empty(host) call health#report_warn('Missing "neovim" gem.', - \ ['Run in shell: gem install neovim']) + \ ['Run in shell: gem install neovim', + \ 'Is the gem bin directory in $PATH? Check `gem environment`.', + \ 'If you are using rvm/rbenv/chruby, try "rehashing".']) return endif call health#report_info('Host: '. host) - let latest_gem_cmd = 'gem list -ra ^neovim$' + let latest_gem_cmd = has('win32') ? 'cmd /c gem list -ra ^^neovim$' : 'gem list -ra ^neovim$' let latest_gem = s:system(split(latest_gem_cmd)) if s:shell_error || empty(latest_gem) call health#report_error('Failed to run: '. latest_gem_cmd, \ ["Make sure you're connected to the internet.", - \ "Are you behind a firewall or proxy?"]) + \ 'Are you behind a firewall or proxy?']) return endif - let latest_gem = get(split(latest_gem, ' (\|, \|)$' ), 1, 'not found') + let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 1, 'not found') let current_gem_cmd = host .' --version' let current_gem = s:system(current_gem_cmd) if s:shell_error call health#report_error('Failed to run: '. current_gem_cmd, - \ ["Report this issue with the output of: ", current_gem_cmd]) + \ ['Report this issue with the output of: ', current_gem_cmd]) return endif @@ -449,7 +480,7 @@ function! s:check_ruby() abort \ current_gem, latest_gem), \ ['Run in shell: gem update neovim']) else - call health#report_ok('Gem "neovim" is up-to-date: '. current_gem) + call health#report_ok('Latest "neovim" gem is installed: '. current_gem) endif endfunction diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 251e6eee41..dd71ede680 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -1,16 +1,31 @@ " Maintainer: Anmol Sethi <anmol@aubble.com> -let s:man_find_arg = "-w" +let s:find_arg = '-w' +let s:localfile_arg = v:true " Always use -l if possible. #6683 +let s:section_arg = '-s' -" 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' +function! s:init_section_flag() + call system(['env', 'MANPAGER=cat', 'man', s:section_arg, '1', 'man']) + if v:shell_error + let s:section_arg = '-S' endif -catch /E145:/ - " Ignore the error in restricted mode -endtry +endfunction + +function! s:init() abort + call s:init_section_flag() + " TODO(nhooyr): Does `man -l` on SunOS list searched directories? + try + if !has('win32') && $OSTYPE !~? 'cygwin\|linux' && system('uname -s') =~? 'SunOS' && system('uname -r') =~# '^5' + let s:find_arg = '-l' + endif + " Check for -l support. + call s:get_page(s:get_path('', 'man')[0:-2]) + catch /E145:/ + " Ignore the error in restricted mode + catch /command error .*/ + let s:localfile_arg = v:false + endtry +endfunction function! man#open_page(count, count1, mods, ...) abort if a:0 > 2 @@ -39,7 +54,6 @@ function! man#open_page(count, count1, mods, ...) abort let sect = string(a:count) endif let [sect, name, path] = s:verify_exists(sect, name) - let page = s:get_page(path) catch call s:error(v:exception) return @@ -47,11 +61,29 @@ function! man#open_page(count, count1, mods, ...) abort call s:push_tag() let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')') - if a:mods !~# 'tab' && s:find_man() - noautocmd execute 'silent edit' fnameescape(bufname) - else - noautocmd execute 'silent' a:mods 'split' fnameescape(bufname) - endif + + try + set eventignore+=BufReadCmd + if a:mods !~# 'tab' && s:find_man() + execute 'silent edit' fnameescape(bufname) + else + execute 'silent' a:mods 'split' fnameescape(bufname) + endif + finally + set eventignore-=BufReadCmd + endtry + + try + let page = s:get_page(path) + catch + if a:mods =~# 'tab' || !s:find_man() + " a new window was opened + close + endif + call s:error(v:exception) + return + endtry + let b:man_sect = sect call s:put_page(page) endfunction @@ -59,21 +91,20 @@ endfunction function! man#read_page(ref) abort try let [sect, name] = man#extract_sect_and_name_ref(a:ref) - let [b:man_sect, name, path] = s:verify_exists(sect, name) + let [sect, name, path] = s:verify_exists(sect, name) let page = s:get_page(path) catch - " call to s:error() is unnecessary + call s:error(v:exception) return endtry + let b:man_sect = sect call s:put_page(page) endfunction " Handler for s:system() function. function! s:system_handler(jobid, data, event) dict abort - if a:event == 'stdout' - let self.stdout .= join(a:data, "\n") - elseif a:event == 'stderr' - let self.stderr .= join(a:data, "\n") + if a:event is# 'stdout' || a:event is# 'stderr' + let self[a:event] .= join(a:data, "\n") else let self.exit_code = a:data endif @@ -100,7 +131,7 @@ function! s:system(cmd, ...) abort try call jobstop(jobid) throw printf('command timed out: %s', join(a:cmd)) - catch /^Vim\%((\a\+)\)\=:E900/ + catch /^Vim(call):E900:/ endtry elseif res[0] == -2 throw printf('command interrupted: %s', join(a:cmd)) @@ -117,7 +148,8 @@ function! s:get_page(path) abort let manwidth = empty($MANWIDTH) ? winwidth(0) : $MANWIDTH " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). " http://comments.gmane.org/gmane.editors.vim.devel/29085 - return s:system(['env', 'MANPAGER=cat', 'MANWIDTH='.manwidth, 'man', a:path]) + let cmd = ['env', 'MANPAGER=cat', 'MANWIDTH='.manwidth, 'man'] + return s:system(cmd + (s:localfile_arg ? ['-l', a:path] : [a:path])) endfunction function! s:put_page(page) abort @@ -125,14 +157,39 @@ function! s:put_page(page) abort setlocal noreadonly silent keepjumps %delete _ silent put =a:page - " Remove all backspaced characters. - execute 'silent keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g') + " Remove all backspaced/escape characters. + execute 'silent keeppatterns keepjumps %substitute,.\b\|\e\[\d\+m,,e'.(&gdefault?'':'g') while getline(1) =~# '^\s*$' silent keepjumps 1delete _ endwhile setlocal filetype=man endfunction +function! man#show_toc() abort + let bufname = bufname('%') + let info = getloclist(0, {'winid': 1}) + if !empty(info) && getwinvar(info.winid, 'qf_toc') ==# bufname + lopen + return + endif + + let toc = [] + let lnum = 2 + let last_line = line('$') - 1 + while lnum && lnum < last_line + let text = getline(lnum) + if text =~# '^\%( \{3\}\)\=\S.*$' + call add(toc, {'bufnr': bufnr('%'), 'lnum': lnum, 'text': text}) + endif + let lnum = nextnonblank(lnum + 1) + endwhile + + call setloclist(0, toc, ' ') + call setloclist(0, [], 'a', {'title': 'Man TOC'}) + lopen + let w:qf_toc = bufname +endfunction + " attempt to extract the name and sect out of 'name(sect)' " otherwise just return the largest string of valid characters in ref function! man#extract_sect_and_name_ref(ref) abort @@ -156,24 +213,26 @@ endfunction function! s:get_path(sect, name) abort if empty(a:sect) - return s:system(['man', s:man_find_arg, a:name]) + return s:system(['man', s:find_arg, a:name]) endif " '-s' flag handles: " - tokens like 'printf(echo)' " - sections starting with '-' " - 3pcap section (found on macOS) " - commas between sections (for section priority) - return s:system(['man', s:man_find_arg, '-s', a:sect, a:name]) + return s:system(['man', s:find_arg, s:section_arg, a:sect, 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 !~# '^\/' + try + let path = s:get_path(a:sect, a:name) + catch /^command error (/ + try + let path = s:get_path(get(b:, 'man_default_sects', ''), a:name) + catch /^command error (/ let path = s:get_path('', a:name) - endif - endif + endtry + endtry " We need to extract the section from the path because sometimes " the actual section of the manpage is more specific than the section " we provided to `man`. Try ':Man 3 App::CLI'. @@ -286,7 +345,7 @@ endfunction function! s:complete(sect, psect, name) abort try - let mandirs = join(split(s:system(['man', s:man_find_arg]), ':\|\n'), ',') + let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',') catch call s:error(v:exception) return @@ -311,8 +370,8 @@ function! s:format_candidate(path, psect) abort endfunction function! man#init_pager() abort - " Remove all backspaced characters. - execute 'silent keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g') + " Remove all backspaced/escape characters. + execute 'silent keeppatterns keepjumps %substitute,.\b\|\e\[\d\+m,,e'.(&gdefault?'':'g') if getline(1) =~# '^\s*$' silent keepjumps 1delete _ else @@ -328,3 +387,5 @@ function! man#init_pager() abort endtry execute 'silent file man://'.fnameescape(ref) endfunction + +call s:init() diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index de85844d5d..76485c2f38 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -22,8 +22,8 @@ if &cp || exists("g:loaded_netrw") finish endif -" netrw requires vim having patch 213; netrw will benefit from vim's having patch#656, too -if v:version < 704 || !has("patch213") +" netrw requires vim having patch 7.4.213; netrw will benefit from vim's having patch#656, too +if v:version < 704 || (v:version == 704 && !has("patch213")) if !exists("s:needpatch213") unsilent echomsg "***sorry*** this version of netrw requires vim v7.4 with patch 213" endif diff --git a/runtime/autoload/phpcomplete.vim b/runtime/autoload/phpcomplete.vim index 7f25d9df33..8e38867a77 100644 --- a/runtime/autoload/phpcomplete.vim +++ b/runtime/autoload/phpcomplete.vim @@ -3,7 +3,7 @@ " Maintainer: Dávid Szabó ( complex857 AT gmail DOT com ) " Previous Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) " URL: https://github.com/shawncplus/phpcomplete.vim -" Last Change: 2015 Jul 13 +" Last Change: 2016 Oct 10 " " OPTIONS: " @@ -195,6 +195,8 @@ function! phpcomplete#CompletePHP(findstart, base) " {{{ " }}} elseif context =~? 'implements' return phpcomplete#CompleteClassName(a:base, ['i'], current_namespace, imports) + elseif context =~? 'instanceof' + return phpcomplete#CompleteClassName(a:base, ['c', 'n'], current_namespace, imports) elseif context =~? 'extends\s\+.\+$' && a:base == '' return ['implements'] elseif context =~? 'extends' @@ -787,6 +789,8 @@ function! phpcomplete#CompleteClassName(base, kinds, current_namespace, imports) if kinds == ['c', 'i'] let filterstr = 'v:val =~? "\\(class\\|interface\\)\\s\\+[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*"' + elseif kinds == ['c', 'n'] + let filterstr = 'v:val =~? "\\(class\\|namespace\\)\\s\\+[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*"' elseif kinds == ['c'] let filterstr = 'v:val =~? "class\\s\\+[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*"' elseif kinds == ['i'] @@ -931,7 +935,7 @@ function! phpcomplete#EvaluateModifiers(modifiers, required_modifiers, prohibite endfor for modifier in a:modifiers - " if the modifier is prohibited its a no match + " if the modifier is prohibited it's a no match if index(a:prohibited_modifiers, modifier) != -1 return 0 endif @@ -996,7 +1000,7 @@ function! phpcomplete#CompleteUserClass(context, base, sccontent, visibility) " let required_modifiers += ['static'] endif let all_variable = filter(deepcopy(a:sccontent), - \ 'v:val =~ "^\\s*\\(var\\s\\+\\|public\\s\\+\\|protected\\s\\+\\|private\\s\\+\\|final\\s\\+\\|abstract\\s\\+\\|static\\s\\+\\)\\+\\$"') + \ 'v:val =~ "\\(^\\s*\\(var\\s\\+\\|public\\s\\+\\|protected\\s\\+\\|private\\s\\+\\|final\\s\\+\\|abstract\\s\\+\\|static\\s\\+\\)\\+\\$\\|^\\s*\\(\\/\\|\\*\\)*\\s*@property\\s\\+\\S\\+\\s\\S\\{-}\\s*$\\)"') let variables = [] for i in all_variable @@ -1160,6 +1164,14 @@ function! phpcomplete#GetTaglist(pattern) " {{{ endif let tags = taglist(a:pattern) + for tag in tags + for prop in keys(tag) + if prop == 'cmd' || prop == 'static' || prop == 'kind' || prop == 'builtin' + continue + endif + let tag[prop] = substitute(tag[prop], '\\\\', '\\', 'g') + endfor + endfor let s:cache_tags[a:pattern] = tags let has_key = has_key(s:cache_tags, a:pattern) let s:cache_tags_checksum = cache_checksum @@ -1379,7 +1391,7 @@ function! phpcomplete#GetCallChainReturnType(classname_candidate, class_candidat " Get Structured information of all classes and subclasses including namespace and includes " try to find the method's return type in docblock comment for classstructure in classcontents - let docblock_target_pattern = 'function\s\+&\?'.method.'\|\(public\|private\|protected\|var\).\+\$'.method + let docblock_target_pattern = 'function\s\+&\?'.method.'\>\|\(public\|private\|protected\|var\).\+\$'.method.'\>\|@property.\+\$'.method.'\>' let doc_str = phpcomplete#GetDocBlock(split(classstructure.content, '\n'), docblock_target_pattern) if doc_str != '' break @@ -1387,8 +1399,17 @@ function! phpcomplete#GetCallChainReturnType(classname_candidate, class_candidat endfor if doc_str != '' let docblock = phpcomplete#ParseDocBlock(doc_str) - if has_key(docblock.return, 'type') || has_key(docblock.var, 'type') - let type = has_key(docblock.return, 'type') ? docblock.return.type : docblock.var.type + if has_key(docblock.return, 'type') || has_key(docblock.var, 'type') || len(docblock.properties) > 0 + let type = has_key(docblock.return, 'type') ? docblock.return.type : has_key(docblock.var, 'type') ? docblock.var.type : '' + + if type == '' + for property in docblock.properties + if property.description =~? method + let type = property.type + break + endif + endfor + endif " there's a namespace in the type, threat the type as FQCN if type =~ '\\' @@ -1554,6 +1575,9 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor elseif get(methodstack, 0) =~# function_invocation_pattern let function_name = matchstr(methodstack[0], '^\s*\zs'.function_name_pattern) let function_file = phpcomplete#GetFunctionLocation(function_name, a:current_namespace) + if function_file == '' + let function_file = phpcomplete#GetFunctionLocation(function_name, '\') + endif if function_file == 'VIMPHP_BUILTINFUNCTION' " built in function, grab the return type from the info string @@ -1569,7 +1593,7 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor let [class_candidate_namespace, function_imports] = phpcomplete#GetCurrentNameSpace(file_lines) " try to expand the classname of the returned type with the context got from the function's source file - let [classname_candidate, unused] = phpcomplete#ExpandClassName(classname_candidate, class_candidate_namespace, function_imports) + let [classname_candidate, class_candidate_namespace] = phpcomplete#ExpandClassName(classname_candidate, class_candidate_namespace, function_imports) endif endif if classname_candidate != '' @@ -1650,9 +1674,10 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor let sub_methodstack = phpcomplete#GetMethodStack(matchstr(line, '^\s*'.object.'\s*=&\?\s*\s\+\zs.*')) let [classname_candidate, class_candidate_namespace] = phpcomplete#GetCallChainReturnType( \ classname, - \ a:current_namespace, + \ namespace_for_class, \ a:imports, \ sub_methodstack) + return (class_candidate_namespace == '\' || class_candidate_namespace == '') ? classname_candidate : class_candidate_namespace.'\'.classname_candidate endif endif @@ -1783,6 +1808,9 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor let [function_name, function_namespace] = phpcomplete#ExpandClassName(function_name, a:current_namespace, a:imports) let function_file = phpcomplete#GetFunctionLocation(function_name, function_namespace) + if function_file == '' + let function_file = phpcomplete#GetFunctionLocation(function_name, '\') + endif if function_file == 'VIMPHP_BUILTINFUNCTION' " built in function, grab the return type from the info string @@ -1798,7 +1826,7 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor let classname_candidate = docblock.return.type let [class_candidate_namespace, function_imports] = phpcomplete#GetCurrentNameSpace(file_lines) " try to expand the classname of the returned type with the context got from the function's source file - let [classname_candidate, unused] = phpcomplete#ExpandClassName(classname_candidate, class_candidate_namespace, function_imports) + let [classname_candidate, class_candidate_namespace] = phpcomplete#ExpandClassName(classname_candidate, class_candidate_namespace, function_imports) break endif endif @@ -1861,6 +1889,8 @@ function! phpcomplete#GetClassName(start_line, context, current_namespace, impor for tag in tags if tag.kind ==? 'v' && tag.cmd =~? '=\s*new\s\+\zs'.class_name_pattern.'\ze' let classname = matchstr(tag.cmd, '=\s*new\s\+\zs'.class_name_pattern.'\ze') + " unescape the classname, it would have "\" doubled since it is an ex command + let classname = substitute(classname, '\\\(\_.\)', '\1', 'g') return classname endif endfor @@ -2077,6 +2107,19 @@ function! phpcomplete#GetClassContentsStructure(file_path, file_lines, class_nam endif call searchpair('{', '', '}', 'W') let class_closing_bracket_line = line('.') + + " Include class docblock + let doc_line = cfline - 1 + if getline(doc_line) =~? '^\s*\*/' + while doc_line != 0 + if getline(doc_line) =~? '^\s*/\*\*' + let cfline = doc_line + break + endif + let doc_line -= 1 + endwhile + endif + let classcontent = join(getline(cfline, class_closing_bracket_line), "\n") let used_traits = [] @@ -2241,8 +2284,19 @@ function! phpcomplete#GetDocBlock(sccontent, search) " {{{ let line = a:sccontent[i] " search for a function declaration if line =~? a:search - let l = i - 1 - " start backward serch for the comment block + if line =~? '@property' + let doc_line = i + while doc_line != sccontent_len - 1 + if a:sccontent[doc_line] =~? '^\s*\*/' + let l = doc_line + break + endif + let doc_line += 1 + endwhile + else + let l = i - 1 + endif + " start backward search for the comment block while l != 0 let line = a:sccontent[l] " if it's a one line docblock like comment and we can just return it right away @@ -2263,7 +2317,7 @@ function! phpcomplete#GetDocBlock(sccontent, search) " {{{ return '' end - while l != 0 + while l >= 0 let line = a:sccontent[l] if line =~? '^\s*/\*\*' let comment_start = l @@ -2297,9 +2351,10 @@ function! phpcomplete#ParseDocBlock(docblock) " {{{ \ 'return': {}, \ 'throws': [], \ 'var': {}, + \ 'properties': [], \ } - let res.description = substitute(matchstr(a:docblock, '\zs\_.\{-}\ze\(@var\|@param\|@return\|$\)'), '\(^\_s*\|\_s*$\)', '', 'g') + let res.description = substitute(matchstr(a:docblock, '\zs\_.\{-}\ze\(@type\|@var\|@param\|@return\|$\)'), '\(^\_s*\|\_s*$\)', '', 'g') let docblock_lines = split(a:docblock, "\n") let param_lines = filter(copy(docblock_lines), 'v:val =~? "^@param"') @@ -2334,15 +2389,26 @@ function! phpcomplete#ParseDocBlock(docblock) " {{{ endif endfor - let var_line = filter(copy(docblock_lines), 'v:val =~? "^@var"') + let var_line = filter(copy(docblock_lines), 'v:val =~? "^\\(@var\\|@type\\)"') if len(var_line) > 0 - let var_parts = matchlist(var_line[0], '@var\s\+\(\S\+\)\s*\(.*\)') + let var_parts = matchlist(var_line[0], '\(@var\|@type\)\s\+\(\S\+\)\s*\(.*\)') let res['var'] = { \ 'line': var_parts[0], - \ 'type': phpcomplete#GetTypeFromDocBlockParam(get(var_parts, 1, '')), - \ 'description': get(var_parts, 2, '')} + \ 'type': phpcomplete#GetTypeFromDocBlockParam(get(var_parts, 2, '')), + \ 'description': get(var_parts, 3, '')} endif + let property_lines = filter(copy(docblock_lines), 'v:val =~? "^@property"') + for property_line in property_lines + let parts = matchlist(property_line, '\(@property\)\s\+\(\S\+\)\s*\(.*\)') + if len(parts) > 0 + call add(res.properties, { + \ 'line': parts[0], + \ 'type': phpcomplete#GetTypeFromDocBlockParam(get(parts, 2, '')), + \ 'description': get(parts, 3, '')}) + endif + endfor + return res endfunction " }}} @@ -2498,6 +2564,7 @@ function! phpcomplete#GetCurrentNameSpace(file_lines) " {{{ let name = matchstr(name, '\\\zs[^\\]\+\ze$') endif endif + " leading slash is not required use imports are always absolute let imports[name] = {'name': object, 'kind': ''} endfor @@ -2533,6 +2600,7 @@ function! phpcomplete#GetCurrentNameSpace(file_lines) " {{{ elseif !exists('no_namespace_candidate') " save the first namespacless match to be used if no better " candidate found later on + let tag.namespace = namespace_for_classes let no_namespace_candidate = tag endif endif diff --git a/runtime/autoload/provider.vim b/runtime/autoload/provider.vim new file mode 100644 index 0000000000..e6514f5ba8 --- /dev/null +++ b/runtime/autoload/provider.vim @@ -0,0 +1,20 @@ +" Common functionality for providers + +let s:stderr = {} + +function! provider#stderr_collector(chan_id, data, event) + let stderr = get(s:stderr, a:chan_id, ['']) + let stderr[-1] .= a:data[0] + call extend(stderr, a:data[1:]) + let s:stderr[a:chan_id] = stderr +endfunction + +function! provider#clear_stderr(chan_id) + if has_key(s:stderr, a:chan_id) + call remove(s:stderr, a:chan_id) + endif +endfunction + +function! provider#get_stderr(chan_id) + return get(s:stderr, a:chan_id, []) +endfunction diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index b8baaa8c64..6454a01c2a 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -3,28 +3,36 @@ " available. let s:copy = {} let s:paste = {} +let s:clipboard = {} " When caching is enabled, store the jobid of the xclip/xsel process keeping " ownership of the selection, so we know how long the cache is valid. -let s:selection = { 'owner': 0, 'data': [] } +let s:selection = { 'owner': 0, 'data': [], 'on_stderr': function('provider#stderr_collector') } -function! s:selection.on_exit(jobid, data, event) +function! s:selection.on_exit(jobid, data, event) abort " At this point this nvim instance might already have launched " a new provider instance. Don't drop ownership in this case. if self.owner == a:jobid let self.owner = 0 endif + if a:data != 0 + let stderr = provider#get_stderr(a:jobid) + echohl WarningMsg + echomsg 'clipboard: error invoking '.get(self.argv, 0, '?').': '.join(stderr) + echohl None + endif + call provider#clear_stderr(a:jobid) endfunction -let s:selections = { '*': s:selection, '+': copy(s:selection)} +let s:selections = { '*': s:selection, '+': copy(s:selection) } -function! s:try_cmd(cmd, ...) +function! s:try_cmd(cmd, ...) abort let argv = split(a:cmd, " ") let out = a:0 ? systemlist(argv, a:1, 1) : systemlist(argv, [''], 1) if v:shell_error if !exists('s:did_error_try_cmd') echohl WarningMsg - echomsg "clipboard: error: ".(len(out) ? out[0] : '') + echomsg "clipboard: error: ".(len(out) ? out[0] : v:shell_error) echohl None let s:did_error_try_cmd = 1 endif @@ -34,7 +42,7 @@ function! s:try_cmd(cmd, ...) endfunction " Returns TRUE if `cmd` exits with success, else FALSE. -function! s:cmd_ok(cmd) +function! s:cmd_ok(cmd) abort call system(a:cmd) return v:shell_error == 0 endfunction @@ -47,7 +55,18 @@ function! provider#clipboard#Error() abort endfunction function! provider#clipboard#Executable() abort - if executable('pbcopy') + if exists('g:clipboard') + if type({}) isnot# type(g:clipboard) + \ || type({}) isnot# type(get(g:clipboard, 'copy', v:null)) + \ || type({}) isnot# type(get(g:clipboard, 'paste', v:null)) + let s:err = 'clipboard: invalid g:clipboard' + return '' + endif + let s:copy = get(g:clipboard, 'copy', { '+': v:null, '*': v:null }) + let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null }) + let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0) + return get(g:clipboard, 'name', 'g:clipboard') + elseif has('mac') && executable('pbcopy') let s:copy['+'] = 'pbcopy' let s:paste['+'] = 'pbpaste' let s:copy['*'] = s:copy['+'] @@ -78,26 +97,39 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'doitclient' + elseif executable('win32yank') + let s:copy['+'] = 'win32yank -i --crlf' + let s:paste['+'] = 'win32yank -o --lf' + let s:copy['*'] = s:copy['+'] + let s:paste['*'] = s:paste['+'] + return 'win32yank' + elseif exists('$TMUX') && executable('tmux') + let s:copy['+'] = 'tmux load-buffer -' + let s:paste['+'] = 'tmux save-buffer -' + let s:copy['*'] = s:copy['+'] + let s:paste['*'] = s:paste['+'] + return 'tmux' endif - let s:err = 'clipboard: No clipboard tool available. See :help clipboard' + let s:err = 'clipboard: No clipboard tool. :help clipboard' return '' endfunction if empty(provider#clipboard#Executable()) + " provider#clipboard#Call() *must not* be defined if the provider is broken. + " Otherwise eval_has_provider() thinks the clipboard provider is + " functioning, and eval_call_provider() will happily call it. finish endif -let s:clipboard = {} - -function! s:clipboard.get(reg) +function! s:clipboard.get(reg) abort if s:selections[a:reg].owner > 0 return s:selections[a:reg].data end return s:try_cmd(s:paste[a:reg]) endfunction -function! s:clipboard.set(lines, regtype, reg) +function! s:clipboard.set(lines, regtype, reg) abort if a:reg == '"' call s:clipboard.set(a:lines,a:regtype,'+') if s:copy['*'] != s:copy['+'] @@ -118,20 +150,31 @@ function! s:clipboard.set(lines, regtype, reg) end let selection.data = [a:lines, a:regtype] let argv = split(s:copy[a:reg], " ") + let selection.argv = argv let selection.detach = s:cache_enabled let selection.cwd = "/" let jobid = jobstart(argv, selection) - if jobid <= 0 + if jobid > 0 + call jobsend(jobid, a:lines) + call jobclose(jobid, 'stdin') + let selection.owner = jobid + else echohl WarningMsg - echo "clipboard: error when invoking provider" + echomsg 'clipboard: failed to execute: '.(s:copy[a:reg]) echohl None return 0 endif - call jobsend(jobid, a:lines) - call jobclose(jobid, 'stdin') - let selection.owner = jobid + return 1 endfunction -function! provider#clipboard#Call(method, args) - return call(s:clipboard[a:method],a:args,s:clipboard) +function! provider#clipboard#Call(method, args) abort + if get(s:, 'here', v:false) " Clipboard provider must not recurse. #7184 + return 0 + endif + let s:here = v:true + try + return call(s:clipboard[a:method],a:args,s:clipboard) + finally + let s:here = v:false + endtry endfunction diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim index b99a046375..81fe194cb9 100644 --- a/runtime/autoload/provider/python.vim +++ b/runtime/autoload/provider/python.vim @@ -1,5 +1,5 @@ " The Python provider uses a Python host to emulate an environment for running -" python-vim plugins. See ":help provider". +" python-vim plugins. :help provider " " Associating the plugin with the Python host is the first step because plugins " will be passed as command-line arguments diff --git a/runtime/autoload/provider/python3.vim b/runtime/autoload/provider/python3.vim index 4f47a03a9b..0c3b75b73d 100644 --- a/runtime/autoload/provider/python3.vim +++ b/runtime/autoload/provider/python3.vim @@ -1,5 +1,5 @@ " The Python3 provider uses a Python3 host to emulate an environment for running -" python3 plugins. See ":help provider". +" python3 plugins. :help provider " " Associating the plugin with the Python3 host is the first step because " plugins will be passed as command-line arguments diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 08a0f39b01..7285ed43ea 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -5,17 +5,7 @@ endif let s:loaded_pythonx_provider = 1 -let s:stderr = {} -let s:job_opts = {'rpc': v:true} - -" TODO(bfredl): this logic is common and should be builtin -function! s:job_opts.on_stderr(chan_id, data, event) - let stderr = get(s:stderr, a:chan_id, ['']) - let last = remove(stderr, -1) - let a:data[0] = last.a:data[0] - call extend(stderr, a:data) - let s:stderr[a:chan_id] = stderr -endfunction +let s:job_opts = {'rpc': v:true, 'on_stderr': function('provider#stderr_collector')} function! provider#pythonx#Require(host) abort let ver = (a:host.orig_name ==# 'python') ? 2 : 3 @@ -38,9 +28,11 @@ function! provider#pythonx#Require(host) abort catch echomsg v:throwpoint echomsg v:exception - for row in get(s:stderr, channel_id, []) + for row in provider#get_stderr(channel_id) echomsg row endfor + finally + call provider#clear_stderr(channel_id) endtry throw remote#host#LoadErrorForHost(a:host.orig_name, \ '$NVIM_PYTHON_LOG_FILE') @@ -112,15 +104,14 @@ function! s:check_interpreter(prog, major_ver) abort endif if v:shell_error == 2 - return [0, prog_path . ' does not have the neovim module installed. ' - \ . 'See ":help provider-python".'] + return [0, prog_path.' does not have the "neovim" module. :help provider-python'] elseif v:shell_error == 127 " This can happen with pyenv's shims. return [0, prog_path . ' does not exist: ' . prog_ver] elseif v:shell_error return [0, 'Checking ' . prog_path . ' caused an unknown error. ' \ . '(' . v:shell_error . ', output: ' . prog_ver . ')' - \ . ' Please report this at github.com/neovim/neovim.'] + \ . ' Report this at https://github.com/neovim/neovim'] endif return [1, ''] diff --git a/runtime/autoload/provider/ruby.vim b/runtime/autoload/provider/ruby.vim index c8ede20a75..91b7fb9f2c 100644 --- a/runtime/autoload/provider/ruby.vim +++ b/runtime/autoload/provider/ruby.vim @@ -16,7 +16,11 @@ function! s:job_opts.on_stderr(chan_id, data, event) endfunction function! provider#ruby#Detect() abort - return exepath('neovim-ruby-host') + if exists("g:ruby_host_prog") + return g:ruby_host_prog + else + return exepath('neovim-ruby-host') + end endfunction function! provider#ruby#Prog() @@ -24,15 +28,15 @@ function! provider#ruby#Prog() endfunction function! provider#ruby#Require(host) abort - let args = [provider#ruby#Prog()] + let prog = provider#ruby#Prog() let ruby_plugins = remote#host#PluginsForHost(a:host.name) for plugin in ruby_plugins - call add(args, plugin.path) + let prog .= " " . shellescape(plugin.path) endfor try - let channel_id = jobstart(args, s:job_opts) + let channel_id = jobstart(prog, s:job_opts) if rpcrequest(channel_id, 'poll') ==# 'ok' return channel_id endif diff --git a/runtime/autoload/provider/script_host.rb b/runtime/autoload/provider/script_host.rb index 1dade766c7..a1c58bde85 100644 --- a/runtime/autoload/provider/script_host.rb +++ b/runtime/autoload/provider/script_host.rb @@ -1,8 +1,6 @@ begin - require "neovim/ruby_provider" + require 'neovim/ruby_provider' rescue LoadError - warn( - "Your neovim RubyGem is missing or out of date. " + - "Install the latest version using `gem install neovim`." - ) + warn('Your neovim RubyGem is missing or out of date.', + 'Install the latest version using `gem install neovim`.') end diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index 51f7e5886f..e695fb7df7 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -2,7 +2,6 @@ let s:hosts = {} let s:plugin_patterns = {} let s:plugins_for_host = {} - " Register a host by associating it with a factory(funcref) function! remote#host#Register(name, pattern, factory) abort let s:hosts[a:name] = {'factory': a:factory, 'channel': 0, 'initialized': 0} @@ -13,7 +12,6 @@ function! remote#host#Register(name, pattern, factory) abort endif endfunction - " Register a clone to an existing host. The new host will use the same factory " as `source`, but it will run as a different process. This can be used by " plugins that should run isolated from other plugins created for the same host @@ -31,12 +29,8 @@ function! remote#host#RegisterClone(name, orig_name) abort \ } endfunction - " Get a host channel, bootstrapping it if necessary function! remote#host#Require(name) abort - if empty(s:plugins_for_host) - call remote#host#LoadRemotePlugins() - endif if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' endif @@ -52,7 +46,6 @@ function! remote#host#Require(name) abort return host.channel endfunction - function! remote#host#IsRunning(name) abort if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' @@ -60,7 +53,6 @@ function! remote#host#IsRunning(name) abort return s:hosts[a:name].channel != 0 endfunction - " Example of registering a Python plugin with two commands (one async), one " autocmd (async) and one function (sync): " @@ -117,73 +109,6 @@ function! remote#host#RegisterPlugin(host, path, specs) abort call add(plugins, {'path': a:path, 'specs': a:specs}) endfunction - -" Get the path to the rplugin manifest file. -function! s:GetManifestPath() abort - let manifest_base = '' - - if exists('$NVIM_RPLUGIN_MANIFEST') - return fnamemodify($NVIM_RPLUGIN_MANIFEST, ':p') - endif - - let dest = has('win32') ? '$LOCALAPPDATA' : '$XDG_DATA_HOME' - if !exists(dest) - let dest = has('win32') ? '~/AppData/Local' : '~/.local/share' - endif - - let dest = fnamemodify(expand(dest), ':p') - if !empty(dest) && !filereadable(dest) - let dest .= ('/' ==# dest[-1:] ? '' : '/') . 'nvim' - call mkdir(dest, 'p', 0700) - let manifest_base = dest - endif - - return manifest_base.'/rplugin.vim' -endfunction - - -" Old manifest file based on known script locations. -function! s:GetOldManifestPath() abort - let prefix = exists('$MYVIMRC') - \ ? $MYVIMRC - \ : matchstr(get(split(execute('scriptnames'), '\n'), 0, ''), '\f\+$') - return fnamemodify(expand(prefix, 1), ':h') - \.'/.'.fnamemodify(prefix, ':t').'-rplugin~' -endfunction - - -function! s:GetManifest() abort - let manifest = s:GetManifestPath() - - if !filereadable(manifest) - " Check if an old manifest file exists and move it to the new location. - let old_manifest = s:GetOldManifestPath() - if filereadable(old_manifest) - call rename(old_manifest, manifest) - endif - endif - - return manifest -endfunction - - -function! remote#host#LoadRemotePlugins() abort - let manifest = s:GetManifest() - if filereadable(manifest) - execute 'source' fnameescape(manifest) - endif -endfunction - - -function! remote#host#LoadRemotePluginsEvent(event, pattern) abort - autocmd! nvim-rplugin - call remote#host#LoadRemotePlugins() - if exists('#'.a:event.'#'.a:pattern) " Avoid 'No matching autocommands'. - execute 'silent doautocmd <nomodeline>' a:event a:pattern - endif -endfunction - - function! s:RegistrationCommands(host) abort " Register a temporary host clone for discovering specs let host_id = a:host.'-registration-clone' @@ -228,7 +153,6 @@ function! s:RegistrationCommands(host) abort return lines endfunction - function! remote#host#UpdateRemotePlugins() abort let commands = [] let hosts = keys(s:hosts) @@ -245,12 +169,11 @@ function! remote#host#UpdateRemotePlugins() abort endtry endif endfor - call writefile(commands, s:GetManifest()) + call writefile(commands, g:loaded_remote_plugins) echomsg printf('remote/host: generated rplugin manifest: %s', - \ s:GetManifest()) + \ g:loaded_remote_plugins) endfunction - function! remote#host#PluginsForHost(host) abort if !has_key(s:plugins_for_host, a:host) let s:plugins_for_host[a:host] = [] @@ -258,7 +181,6 @@ function! remote#host#PluginsForHost(host) abort return s:plugins_for_host[a:host] endfunction - function! remote#host#LoadErrorForHost(host, log) abort return 'Failed to load '. a:host . ' host. '. \ 'You can try to see what happened by starting nvim with '. @@ -266,7 +188,6 @@ function! remote#host#LoadErrorForHost(host, log) abort \ ' Also, the host stderr is available in messages.' endfunction - " Registration of standard hosts " Python/Python3 diff --git a/runtime/autoload/rubycomplete.vim b/runtime/autoload/rubycomplete.vim index e1064c8a58..40b87f4cbe 100644 --- a/runtime/autoload/rubycomplete.vim +++ b/runtime/autoload/rubycomplete.vim @@ -93,7 +93,7 @@ function! s:GetBufferRubyEntity( name, type, ... ) let stopline = 1 - let crex = '^\s*\<' . a:type . '\>\s*\<' . a:name . '\>\s*\(<\s*.*\s*\)\?' + let crex = '^\s*\<' . a:type . '\>\s*\<' . escape(a:name, '*') . '\>\s*\(<\s*.*\s*\)\?' let [lnum,lcol] = searchpos( crex, 'w' ) "let [lnum,lcol] = searchpairpos( crex . '\zs', '', '\(end\|}\)', 'w' ) @@ -149,7 +149,7 @@ function! s:GetRubyVarType(v) let ctors = ctors.'\)' let fstr = '=\s*\([^ \t]\+.' . ctors .'\>\|[\[{"''/]\|%[xwQqr][(\[{@]\|[A-Za-z0-9@:\-()\.]\+...\?\|lambda\|&\)' - let sstr = ''.a:v.'\>\s*[+\-*/]*'.fstr + let sstr = ''.escape(a:v, '*').'\>\s*[+\-*/]*'.fstr let [lnum,lcol] = searchpos(sstr,'nb',stopline) if lnum != 0 && lcol != 0 let str = matchstr(getline(lnum),fstr,lcol) @@ -196,7 +196,7 @@ function! rubycomplete#Complete(findstart, base) if c =~ '\w' continue elseif ! c =~ '\.' - idx = -1 + let idx = -1 break else break @@ -266,6 +266,28 @@ class VimRubyCompletion end end + def load_gems + fpath = VIM::evaluate("get(g:, 'rubycomplete_gemfile_path', 'Gemfile')") + return unless File.file?(fpath) && File.readable?(fpath) + want_bundler = VIM::evaluate("get(g:, 'rubycomplete_use_bundler')") + parse_file = !want_bundler + begin + require 'bundler' + Bundler.setup + Bundler.require + rescue Exception + parse_file = true + end + if parse_file + File.new(fpath).each_line do |line| + begin + require $1 if /\s*gem\s*['"]([^'"]+)/.match(line) + rescue Exception + end + end + end + end + def load_buffer_class(name) dprint "load_buffer_class(%s) START" % name classdef = get_buffer_entity(name, 's:GetBufferRubyClass("%s")') @@ -588,6 +610,10 @@ class VimRubyCompletion load_rails end + want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')") + load_gems unless want_gems.to_i.zero? + + input = VIM::Buffer.current.line cpos = VIM::Window.current.cursor[1] - 1 input = input[0..cpos] @@ -678,7 +704,9 @@ class VimRubyCompletion cv = eval("self.class.constants") vartype = get_var_type( receiver ) dprint "vartype: %s" % vartype - if vartype != '' + + invalid_vartype = ['', "gets"] + if !invalid_vartype.include?(vartype) load_buffer_class( vartype ) begin @@ -706,7 +734,7 @@ class VimRubyCompletion methods.concat m.instance_methods(false) } end - variables += add_rails_columns( "#{vartype}" ) if vartype && vartype.length > 0 + variables += add_rails_columns( "#{vartype}" ) if vartype && !invalid_vartype.include?(vartype) when /^\(?\s*[A-Za-z0-9:^@.%\/+*\(\)]+\.\.\.?[A-Za-z0-9:^@.%\/+*\(\)]+\s*\)?\.([^.]*)/ message = $1 diff --git a/runtime/autoload/shada.vim b/runtime/autoload/shada.vim index cf27ee608a..87acc515ee 100644 --- a/runtime/autoload/shada.vim +++ b/runtime/autoload/shada.vim @@ -45,7 +45,7 @@ call map(copy(s:SHADA_ENTRY_NAMES), let s:SHADA_MAP_ENTRIES = { \'search_pattern': ['sp', 'sh', 'ss', 'sb', 'sm', 'sc', 'sl', 'se', 'so', \ 'su'], - \'register': ['n', 'rc', 'rw', 'rt'], + \'register': ['n', 'rc', 'rw', 'rt', 'ru'], \'global_mark': ['n', 'f', 'l', 'c'], \'local_mark': ['f', 'n', 'l', 'c'], \'jump': ['f', 'l', 'c'], @@ -139,6 +139,7 @@ let s:SHADA_STANDARD_KEYS = { \'rt': ['type', 'regtype', s:SHADA_ENUMS.regtype.CHARACTERWISE], \'rw': ['block width', 'uint', 0], \'rc': ['contents', 'binarray', s:SHADA_REQUIRED], + \'ru': ['is_unnamed', 'boolean', g:msgpack#false], \'n': ['name', 'intchar', char2nr('"')], \'l': ['line number', 'uint', 1], \'c': ['column', 'uint', 0], diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 5e76870cce..9c518cb9d0 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -117,7 +117,7 @@ fun! tar#Browse(tarfile) if !filereadable(a:tarfile) " call Decho('a:tarfile<'.a:tarfile.'> not filereadable') if a:tarfile !~# '^\a\+://' - " if its an url, don't complain, let url-handlers such as vim do its thing + " if it's an url, don't complain, let url-handlers such as vim do its thing redraw! echohl Error | echo "***error*** (tar#Browse) File not readable<".a:tarfile.">" | echohl None endif diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim index 43d8a87886..56e2283465 100644 --- a/runtime/autoload/tutor.vim +++ b/runtime/autoload/tutor.vim @@ -15,30 +15,17 @@ function! tutor#SetupVim() endif endfunction -" Mappings: {{{1 - -function! s:CheckMaps() - nmap +" Loads metadata file, if available +function! tutor#LoadMetadata() + let b:tutor_metadata = json_decode(join(readfile(expand('%').'.json'), "\n")) endfunction -function! s:MapKeyWithRedirect(key, cmd) - if maparg(a:key) !=# '' - redir => l:keys - silent call s:CheckMaps() - redir END - let l:key_list = split(l:keys, '\n') - - let l:raw_map = filter(copy(l:key_list), "v:val =~# '\\* ".a:key."'") - if len(l:raw_map) == 0 - exe "nnoremap <buffer> <expr> ".a:key." ".a:cmd - return - endif - let l:map_data = split(l:raw_map[0], '\s*') +" Mappings: {{{1 - exe "nnoremap <buffer> <expr> ".l:map_data[0]." ".a:cmd - else - exe "nnoremap <buffer> <expr> ".a:key." ".a:cmd - endif +function! tutor#SetNormalMappings() + nnoremap <silent> <buffer> <CR> :call tutor#FollowLink(0)<cr> + nnoremap <silent> <buffer> <2-LeftMouse> :call tutor#MouseDoubleClick()<cr> + nnoremap <buffer> >> :call tutor#InjectCommand()<cr> endfunction function! tutor#MouseDoubleClick() @@ -46,7 +33,7 @@ function! tutor#MouseDoubleClick() normal! zo else if match(getline('.'), '^#\{1,} ') > -1 - normal! zc + silent normal! zc else call tutor#FollowLink(0) endif @@ -59,114 +46,6 @@ function! tutor#InjectCommand() redraw | echohl WarningMsg | echon "tutor: ran" | echohl None | echon " " | echohl Statement | echon l:cmd endfunction -function! tutor#SetNormalMappings() - call s:MapKeyWithRedirect('l', 'tutor#ForwardSkipConceal(v:count1)') - call s:MapKeyWithRedirect('h', 'tutor#BackwardSkipConceal(v:count1)') - call s:MapKeyWithRedirect('<right>', 'tutor#ForwardSkipConceal(v:count1)') - call s:MapKeyWithRedirect('<left>', 'tutor#BackwardSkipConceal(v:count1)') - - nnoremap <silent> <buffer> <CR> :call tutor#FollowLink(0)<cr> - nnoremap <silent> <buffer> <2-LeftMouse> :call tutor#MouseDoubleClick()<cr> - nnoremap <buffer> >> :call tutor#InjectCommand()<cr> -endfunction - -function! tutor#SetSampleTextMappings() - noremap <silent> <buffer> A :if match(getline('.'), '^--->') > -1 \| call search('\s{\@=', 'Wc') \| startinsert \| else \| startinsert! \| endif<cr> - noremap <silent> <buffer> $ :if match(getline('.'), '^--->') > -1 \| call search('.\s{\@=', 'Wc') \| else \| call search('$', 'Wc') \| endif<cr> - onoremap <silent> <buffer> $ :if match(getline('.'), '^--->') > -1 \| call search('.\s{\@=', 'Wc') \| else \| call search('$', 'Wc') \| endif<cr> - noremap <silent> <buffer> ^ :if match(getline('.'), '^--->') > -1 \| call search('\(--->\s\)\@<=.', 'bcW') \| else \| call search('^', 'bcW') \|endif<cr> - onoremap <silent> <buffer> ^ :if match(getline('.'), '^--->') > -1 \| call search('\(--->\s\)\@<=.', 'bcW') \| else \| call search('^', 'bcW') \|endif<cr> - nmap <silent> <buffer> 0 ^<esc> - nmap <silent> <buffer> <Home> ^<esc> - nmap <silent> <buffer> <End> $ - imap <silent> <buffer> <Home> <esc>^<esc>:startinsert<cr> - imap <silent> <buffer> <End> <esc>$:startinsert<cr> - noremap <silent> <buffer> I :exe "normal! 0" \| startinsert<cr> -endfunction - -" Navigation: {{{1 - -" taken from http://stackoverflow.com/a/24224578 - -function! tutor#ForwardSkipConceal(count) - let cnt=a:count - let mvcnt=0 - let c=col('.') - let l=line('.') - let lc=col('$') - let line=getline('.') - while cnt - if c>=lc - let mvcnt+=cnt - break - endif - if stridx(&concealcursor, 'n')==-1 - let isconcealed=0 - else - let [isconcealed, cchar, group] = synconcealed(l, c) - endif - if isconcealed - let cnt-=strchars(cchar) - let oldc=c - let c+=1 - while c < lc - let [isconcealed2, cchar2, group2] = synconcealed(l, c) - if !isconcealed2 || cchar2 != cchar - break - endif - let c+= 1 - endwhile - let mvcnt+=strchars(line[oldc-1:c-2]) - else - let cnt-=1 - let mvcnt+=1 - let c+=len(matchstr(line[c-1:], '.')) - endif - endwhile - return mvcnt.'l' -endfunction - -function! tutor#BackwardSkipConceal(count) - let cnt=a:count - let mvcnt=0 - let c=col('.') - let l=line('.') - let lc=0 - let line=getline('.') - while cnt - if c<=1 - let mvcnt+=cnt - break - endif - if stridx(&concealcursor, 'n')==-1 || c == 0 - let isconcealed=0 - else - let [isconcealed, cchar, group]=synconcealed(l, c-1) - endif - if isconcealed - let cnt-=strchars(cchar) - let oldc=c - let c-=1 - while c>1 - let [isconcealed2, cchar2, group2] = synconcealed(l, c-1) - if !isconcealed2 || cchar2 != cchar - break - endif - let c-=1 - endwhile - let c = max([c, 1]) - let mvcnt+=strchars(line[c-1:oldc-2]) - else - let cnt-=1 - let mvcnt+=1 - let c-=len(matchstr(line[:c-2], '.$')) - endif - endwhile - return mvcnt.'h' -endfunction - -" Hypertext: {{{1 - function! tutor#FollowLink(force) let l:stack_s = join(map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")'), '') if l:stack_s =~# 'tutorLink' @@ -209,42 +88,40 @@ function! tutor#InfoText() return join(l:info_parts, " ") endfunction -" Marks {{{1 -function! tutor#PlaceXMarks() - call cursor(1, 1) - let b:tutor_sign_id = 1 - while search('^--->', 'W') > 0 - call tutor#CheckText(getline('.')) - let b:tutor_sign_id+=1 - endwhile - call cursor(1, 1) + +" Marks: {{{1 + +function! tutor#ApplyMarks() + hi! link tutorExpect Special + if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect') + let b:tutor_sign_id = 1 + for expct in keys(b:tutor_metadata['expect']) + let lnum = eval(expct) + call matchaddpos('tutorExpect', [lnum]) + call tutor#CheckLine(lnum) + endfor + endif endfunction -function! tutor#CheckText(text) - if match(a:text, '{expect:ANYTHING}\s*$') == -1 - if match(getline('.'), '^--->\s*$') > -1 - exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorbad buffer=".bufnr('%') - else - if match(getline('.'), '|expect:.\+|') == -1 - let l:cur_text = matchstr(a:text, '---> \zs.\{-}\ze {expect:') - let l:expected_text = matchstr(a:text, '{expect:\zs.*\ze}\s*$') - else - let l:cur_text = matchstr(a:text, '---> \zs.\{-}\ze |expect:') - let l:expected_text = matchstr(a:text, '|expect:\zs.*\ze|\s*$') - endif - if l:cur_text ==# l:expected_text - exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorok buffer=".bufnr('%') - else - exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorbad buffer=".bufnr('%') - endif +function! tutor#ApplyMarksOnChanged() + if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect') + let lnum = line('.') + if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1 + call tutor#CheckLine(lnum) endif endif endfunction -function! tutor#OnTextChanged() - let l:text = getline('.') - if match(l:text, '^--->') > -1 - call tutor#CheckText(l:text) +function! tutor#CheckLine(line) + if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect') + let bufn = bufnr('%') + let ctext = getline(a:line) + if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# b:tutor_metadata['expect'][string(a:line)] + exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok buffer=".bufn + else + exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad buffer=".bufn + endif + let b:tutor_sign_id+=1 endif endfunction diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim index 687500ebe3..ea086e0882 100644 --- a/runtime/autoload/zip.vim +++ b/runtime/autoload/zip.vim @@ -1,7 +1,7 @@ " zip.vim: Handles browsing zipfiles " AUTOLOAD PORTION -" Date: Jul 02, 2013 -" Version: 27 +" Date: Sep 13, 2016 +" Version: 28 " Maintainer: Charles E Campbell <NdrOchip@ScampbellPfamily.AbizM-NOSPAM> " License: Vim License (see vim's :help license) " Copyright: Copyright (C) 2005-2013 Charles E. Campbell {{{1 @@ -20,10 +20,10 @@ if &cp || exists("g:loaded_zip") finish endif -let g:loaded_zip= "v27" +let g:loaded_zip= "v28" if v:version < 702 echohl WarningMsg - echo "***warning*** this version of zip needs vim 7.2" + echo "***warning*** this version of zip needs vim 7.2 or later" echohl Normal finish endif @@ -53,6 +53,9 @@ endif if !exists("g:zip_unzipcmd") let g:zip_unzipcmd= "unzip" endif +if !exists("g:zip_extractcmd") + let g:zip_extractcmd= g:zip_unzipcmd +endif " ---------------- " Functions: {{{1 @@ -62,14 +65,14 @@ endif " zip#Browse: {{{2 fun! zip#Browse(zipfile) " call Dfunc("zip#Browse(zipfile<".a:zipfile.">)") - " sanity check: insure that the zipfile has "PK" as its first two letters + " sanity check: ensure that the zipfile has "PK" as its first two letters " (zipped files have a leading PK as a "magic cookie") if !filereadable(a:zipfile) || readfile(a:zipfile, "", 1)[0] !~ '^PK' exe "noautocmd e ".fnameescape(a:zipfile) " call Dret("zip#Browse : not a zipfile<".a:zipfile.">") return " else " Decho -" call Decho("zip#Browse: a:zipfile<".a:zipfile."> passed PK test - its a zip file") +" call Decho("zip#Browse: a:zipfile<".a:zipfile."> passed PK test - it's a zip file") endif let repkeep= &report @@ -92,7 +95,7 @@ fun! zip#Browse(zipfile) endif if !filereadable(a:zipfile) if a:zipfile !~# '^\a\+://' - " if its an url, don't complain, let url-handlers such as vim do its thing + " if it's an url, don't complain, let url-handlers such as vim do its thing redraw! echohl Error | echo "***error*** (zip#Browse) File not readable<".a:zipfile.">" | echohl None " call inputsave()|call input("Press <cr> to continue")|call inputrestore() @@ -136,8 +139,10 @@ fun! zip#Browse(zipfile) return endif + " Maps associated with zip plugin setlocal noma nomod ro - noremap <silent> <buffer> <cr> :call <SID>ZipBrowseSelect()<cr> + noremap <silent> <buffer> <cr> :call <SID>ZipBrowseSelect()<cr> + noremap <silent> <buffer> x :call zip#Extract()<cr> let &report= repkeep " call Dret("zip#Browse") @@ -204,6 +209,15 @@ fun! zip#Read(fname,mode) endif " call Decho("zipfile<".zipfile.">") " call Decho("fname <".fname.">") + " sanity check + if !executable(substitute(g:zip_unzipcmd,'\s\+.*$','','')) + redraw! + echohl Error | echo "***error*** (zip#Read) sorry, your system doesn't appear to have the ".g:zip_unzipcmd." program" | echohl None +" call inputsave()|call input("Press <cr> to continue")|call inputrestore() + let &report= repkeep +" call Dret("zip#Write") + return + endif " the following code does much the same thing as " exe "keepj sil! r! ".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1) @@ -236,9 +250,9 @@ fun! zip#Write(fname) set report=10 " sanity checks - if !executable(g:zip_zipcmd) + if !executable(substitute(g:zip_zipcmd,'\s\+.*$','','')) redraw! - echohl Error | echo "***error*** (zip#Write) sorry, your system doesn't appear to have the zip pgm" | echohl None + echohl Error | echo "***error*** (zip#Write) sorry, your system doesn't appear to have the ".g:zip_zipcmd." program" | echohl None " call inputsave()|call input("Press <cr> to continue")|call inputrestore() let &report= repkeep " call Dret("zip#Write") @@ -345,6 +359,48 @@ fun! zip#Write(fname) endfun " --------------------------------------------------------------------- +" zip#Extract: extract a file from a zip archive {{{2 +fun! zip#Extract() +" call Dfunc("zip#Extract()") + + let repkeep= &report + set report=10 + let fname= getline(".") +" call Decho("fname<".fname.">") + + " sanity check + if fname =~ '^"' + let &report= repkeep +" call Dret("zip#Extract") + return + endif + if fname =~ '/$' + redraw! + echohl Error | echo "***error*** (zip#Extract) Please specify a file, not a directory" | echohl None + let &report= repkeep +" call Dret("zip#Extract") + return + endif + + " extract the file mentioned under the cursor +" call Decho("system(".g:zip_extractcmd." ".shellescape(b:zipfile)." ".shellescape(shell).")") + call system(g:zip_extractcmd." ".shellescape(b:zipfile)." ".shellescape(shell)) +" call Decho("zipfile<".b:zipfile.">") + if v:shell_error != 0 + echohl Error | echo "***error*** ".g:zip_extractcmd." ".b:zipfile." ".fname.": failed!" | echohl NONE + elseif !filereadable(fname) + echohl Error | echo "***error*** attempted to extract ".fname." but it doesn't appear to be present!" + else + echo "***note*** successfully extracted ".fname + endif + + " restore option + let &report= repkeep + +" call Dret("zip#Extract") +endfun + +" --------------------------------------------------------------------- " s:Escape: {{{2 fun! s:Escape(fname,isfilt) " call Dfunc("QuoteFileDir(fname<".a:fname."> isfilt=".a:isfilt.")") |