diff options
author | ZyX <kp-pav@yandex.ru> | 2017-12-03 16:49:30 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2017-12-03 16:49:30 +0300 |
commit | c49e22d3964d6c7ae1c24e8ad01b5fec4ca40b57 (patch) | |
tree | b7e59c416d1435725c65f8952b6e55c70544d97e /runtime/autoload | |
parent | 62108c3b0be46936c83f6d4c98b44ceb5e6f77fd (diff) | |
parent | 27a577586eace687c47e7398845178208cae524a (diff) | |
download | rneovim-c49e22d3964d6c7ae1c24e8ad01b5fec4ca40b57.tar.gz rneovim-c49e22d3964d6c7ae1c24e8ad01b5fec4ca40b57.tar.bz2 rneovim-c49e22d3964d6c7ae1c24e8ad01b5fec4ca40b57.zip |
Merge branch 'master' into s-dash-stdin
Diffstat (limited to 'runtime/autoload')
31 files changed, 1430 insertions, 347 deletions
diff --git a/runtime/autoload/ada.vim b/runtime/autoload/ada.vim index cc5191fa43..d04feb9250 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 , '\') . + \ " <Leader>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 b0791eb19d..53d45afc2e 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -1,24 +1,24 @@ function! s:enhance_syntax() abort syntax case match - syntax keyword healthError ERROR + syntax keyword healthError ERROR[:] \ containedin=markdownCodeBlock,mkdListItemLine - highlight link healthError Error + highlight default link healthError Error - syntax keyword healthWarning WARNING + syntax keyword healthWarning WARNING[:] \ containedin=markdownCodeBlock,mkdListItemLine - highlight link healthWarning WarningMsg + highlight default link healthWarning WarningMsg - syntax keyword healthSuccess SUCCESS + syntax keyword healthSuccess OK[:] \ containedin=markdownCodeBlock,mkdListItemLine - highlight healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 + highlight default healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 syntax match healthHelp "|.\{-}|" contains=healthBar \ containedin=markdownCodeBlock,mkdListItemLine syntax match healthBar "|" contained conceal - highlight link healthHelp Identifier + highlight default link healthHelp Identifier - " We do not care about markdown syntax errors in :CheckHealth output. + " We do not care about markdown syntax errors in :checkhealth output. highlight! link markdownError Normal endfunction @@ -34,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) @@ -66,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 @@ -88,27 +90,27 @@ 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 function! s:format_report_message(status, msg, ...) abort " {{{ let output = ' - ' . a:status . ': ' . s:indent_after_line1(a:msg, 4) - let suggestions = [] + let advice = [] " Optional parameters if a:0 > 0 - let suggestions = type(a:1) == type("") ? [a:1] : a:1 - if type(suggestions) != type([]) - echoerr "Expected String or List" + let advice = type(a:1) == type("") ? [a:1] : a:1 + if type(advice) != type([]) + throw "Expected String or List" endif endif " Report each suggestion - if len(suggestions) > 0 - let output .= "\n - SUGGESTIONS:" + if len(advice) > 0 + let output .= "\n - ADVICE:" endif - for suggestion in suggestions + for suggestion in advice let output .= "\n - " . s:indent_after_line1(suggestion, 10) endfor @@ -122,7 +124,7 @@ endfunction " }}} " Reports a successful healthcheck. function! health#report_ok(msg) abort " {{{ - echo s:format_report_message('SUCCESS', a:msg) + echo s:format_report_message('OK', a:msg) endfunction " }}} " Reports a health warning. @@ -157,7 +159,10 @@ endfunction " Translates a list of plugin names to healthcheck function names. function! s:to_fn_names(plugin_names) abort let healthchecks = [] - for p in a:plugin_names + let plugin_names = type('') ==# type(a:plugin_names) + \ ? split(a:plugin_names, '', v:false) + \ : a:plugin_names + for p in plugin_names call add(healthchecks, 'health#'.p.'#check') endfor return healthchecks diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index a1bf9b21cf..58033f0405 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -1,15 +1,31 @@ -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 $VIM is empty we don't care. Else make sure it is valid. + if !empty($VIM) && !filereadable($VIM.'/runtime/doc/nvim.txt') + let ok = v:false + call health#report_error("$VIM is invalid: ".$VIM) + endif + + 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 &paste + let ok = v:false + call health#report_error("'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.", + \ [ 'Remove `set paste` from your init.vim, if applicable.', + \ 'Check `:verbose set paste?` to see if a plugin or script set the option.', ]) + 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 @@ -42,7 +58,7 @@ function! s:check_rplugin_manifest() abort let contents = join(readfile(script)) if contents =~# '\<\%(from\|import\)\s\+neovim\>' if script =~# '[\/]__init__\.py$' - let script = fnamemodify(script, ':h') + let script = tr(fnamemodify(script, ':h'), '\', '/') endif if !has_key(existing_rplugins, script) @@ -118,6 +134,12 @@ function! s:check_tmux() abort 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 @@ -151,6 +173,11 @@ function! s:check_terminal() abort call health#report_info('key_dc (kdch1) terminfo entry: ' \ .(empty(kbs_entry) ? '? (not found)' : kdch1_entry)) endif + for env_var in ['XTERM_VERSION', 'VTE_VERSION', 'TERM_PROGRAM', 'COLORTERM', 'SSH_TTY'] + if !exists('$'.env_var) + call health#report_info(printf("$%s='%s'", env_var, eval('$'.env_var))) + endif + endfor endfunction function! health#nvim#check() abort diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 57dd508f96..0201ed8062 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,11 +235,11 @@ 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 pyname = 'python'.(a:version == 2 ? '' : '3') let pyenv = resolve(exepath('pyenv')) - let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : 'n' + let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : '' let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' let host_prog_var = pyname.'_host_prog' let loaded_var = 'g:loaded_'.pyname.'_provider' @@ -235,6 +251,19 @@ function! s:check_python(version) abort return 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?', + \ 'If you use a shell "framework" like Prezto or Oh My Zsh, try without.', + \ 'Try a different shell (bash).'] + \ ) + else + call health#report_ok(printf('pyenv found: "%s"', pyenv)) + endif + 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 @@ -266,15 +295,6 @@ function! s:check_python(version) abort 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(s:system([pyenv, 'which', pyname], '', 1)) if empty(python_bin) @@ -287,8 +307,9 @@ function! s:check_python(version) abort if exists('$PATH') for path in split($PATH, has('win32') ? ';' : ':') - let path_bin = path.'/'.pyname - if path_bin != python_bin && index(python_multiple, path_bin) == -1 + 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 @@ -303,9 +324,8 @@ function! s:check_python(version) abort 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.', + \ '`pyenv` is not in $PATH, your pyenv installation is broken. ' + \ .'Set `g:'.host_prog_var.'` to avoid surprises.', \ ]) endif endif @@ -318,7 +338,7 @@ function! s:check_python(version) abort call health#report_warn('pyenv is not set up optimally.', [ \ printf('Create a virtualenv specifically ' \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid ' - \ . 'the need to install Neovim''s Python module in each ' + \ . 'the need to install the Neovim Python module in each ' \ . 'version/virtualenv.', host_prog_var) \ ]) elseif !empty(venv) && exists('g:'.host_prog_var) @@ -413,7 +433,7 @@ function! s:check_python(version) abort 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') @@ -423,8 +443,8 @@ function! s:check_ruby() abort 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')) @@ -439,21 +459,21 @@ function! s:check_ruby() abort 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 @@ -467,9 +487,71 @@ function! s:check_ruby() abort endif endfunction +function! s:check_node() abort + call health#report_start('Node provider (optional)') + + let loaded_var = 'g:loaded_node_provider' + if exists(loaded_var) && !exists('*provider#node#Call') + call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + return + endif + + if !executable('node') || !executable('npm') + call health#report_warn( + \ '`node` and `npm` must be in $PATH.', + \ ['Install Node.js and verify that `node` and `npm` commands work.']) + return + endif + call health#report_info('Node: '. s:system('node -v')) + + let host = provider#node#Detect() + if empty(host) + call health#report_warn('Missing "neovim" npm package.', + \ ['Run in shell: npm install -g neovim', + \ 'Is the npm bin directory in $PATH?']) + return + endif + call health#report_info('Host: '. host) + + let latest_npm_cmd = has('win32') ? 'cmd /c npm info neovim --json' : 'npm info neovim --json' + let latest_npm = s:system(split(latest_npm_cmd)) + if s:shell_error || empty(latest_npm) + call health#report_error('Failed to run: '. latest_npm_cmd, + \ ["Make sure you're connected to the internet.", + \ 'Are you behind a firewall or proxy?']) + return + endif + if !empty(latest_npm) + try + let pkg_data = json_decode(latest_npm) + catch /E474/ + return 'error: '.latest_npm + endtry + let latest_npm = get(get(pkg_data, 'dist-tags', {}), 'latest', 'unable to parse') + endif + + let current_npm_cmd = host .' --version' + let current_npm = s:system(current_npm_cmd) + if s:shell_error + call health#report_error('Failed to run: '. current_npm_cmd, + \ ['Report this issue with the output of: ', current_npm_cmd]) + return + endif + + if s:version_cmp(current_npm, latest_npm) == -1 + call health#report_warn( + \ printf('Package "neovim" is out-of-date. Installed: %s, latest: %s', + \ current_npm, latest_npm), + \ ['Run in shell: npm update neovim']) + else + call health#report_ok('Latest "neovim" npm is installed: '. current_npm) + endif +endfunction + function! health#provider#check() abort call s:check_clipboard() call s:check_python(2) call s:check_python(3) call s:check_ruby() + call s:check_node() endfunction diff --git a/runtime/autoload/javascriptcomplete.vim b/runtime/autoload/javascriptcomplete.vim index 2abe41b463..14bc3d7ce3 100644 --- a/runtime/autoload/javascriptcomplete.vim +++ b/runtime/autoload/javascriptcomplete.vim @@ -1,7 +1,7 @@ " Vim completion script " Language: Java Script " Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) -" Last Change: 2006 Apr 30 +" Last Change: 2017 Mar 04 function! javascriptcomplete#CompleteJS(findstart, base) if a:findstart @@ -563,7 +563,7 @@ function! javascriptcomplete#CompleteJS(findstart, base) for i in arguments let g:ia = i let f_elements = matchlist(i, 'function\s\+\(\k\+\)\s*(\(.\{-}\))') - if len(f_elements) == 3 + if len(f_elements) >= 3 let b:js_menuinfo[f_elements[1].'('] = f_elements[2] endif endfor diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 4352a8c782..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 @@ -79,7 +94,7 @@ function! man#read_page(ref) abort 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 @@ -88,10 +103,8 @@ 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 @@ -118,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)) @@ -135,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 @@ -151,6 +165,31 @@ function! s:put_page(page) abort 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 @@ -174,14 +213,14 @@ 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 @@ -306,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 @@ -348,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/paste.vim b/runtime/autoload/paste.vim index fcf06ecdf9..dd7b3ae54a 100644 --- a/runtime/autoload/paste.vim +++ b/runtime/autoload/paste.vim @@ -1,6 +1,6 @@ " Vim support file to help with paste mappings and menus " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2006 Jun 23 +" Last Change: 2017 Aug 30 " Define the string to use for items that are present both in Edit, Popup and " Toolbar menu. Also used in mswin.vim and macmap.vim. @@ -12,7 +12,7 @@ if has("virtualedit") let paste#paste_cmd = {'n': ":call paste#Paste()<CR>"} let paste#paste_cmd['v'] = '"-c<Esc>' . paste#paste_cmd['n'] - let paste#paste_cmd['i'] = 'x<BS><Esc>' . paste#paste_cmd['n'] . 'gi' + let paste#paste_cmd['i'] = "\<c-\>\<c-o>\"+gP" func! paste#Paste() let ove = &ve 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 1bcc1dea74..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 has('mac') && 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['+'] @@ -84,26 +103,33 @@ function! provider#clipboard#Executable() abort 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['+'] @@ -124,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/node.vim b/runtime/autoload/provider/node.vim new file mode 100644 index 0000000000..b08ad4f316 --- /dev/null +++ b/runtime/autoload/provider/node.vim @@ -0,0 +1,80 @@ +if exists('g:loaded_node_provider') + finish +endif +let g:loaded_node_provider = 1 + +let s:job_opts = {'rpc': v:true, 'on_stderr': function('provider#stderr_collector')} + +function! provider#node#Detect() abort + return has('win32') ? exepath('neovim-node-host.cmd') : exepath('neovim-node-host') +endfunction + +function! provider#node#Prog() + return s:prog +endfunction + +function! provider#node#Require(host) abort + if s:err != '' + echoerr s:err + return + endif + + if has('win32') + let args = provider#node#Prog() + else + let args = ['node'] + + if !empty($NVIM_NODE_HOST_DEBUG) + call add(args, '--inspect-brk') + endif + + call add(args , provider#node#Prog()) + endif + + try + let channel_id = jobstart(args, s:job_opts) + if rpcrequest(channel_id, 'poll') ==# 'ok' + return channel_id + endif + catch + echomsg v:throwpoint + echomsg v:exception + for row in provider#get_stderr(channel_id) + echomsg row + endfor + endtry + finally + call provider#clear_stderr(channel_id) + endtry + throw remote#host#LoadErrorForHost(a:host.orig_name, '$NVIM_NODE_LOG_FILE') +endfunction + +function! provider#node#Call(method, args) + if s:err != '' + echoerr s:err + return + endif + + if !exists('s:host') + try + let s:host = remote#host#Require('node') + catch + let s:err = v:exception + echohl WarningMsg + echomsg v:exception + echohl None + return + endtry + endif + return call('rpcrequest', insert(insert(a:args, 'node_'.a:method), s:host)) +endfunction + + +let s:err = '' +let s:prog = provider#node#Detect() + +if empty(s:prog) + let s:err = 'Cannot find the "neovim" node package. Try :CheckHealth' +endif + +call remote#host#RegisterPlugin('node-provider', 'node', []) 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..da73a0dfc0 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 has('win32') ? exepath('neovim-ruby-host.bat') : 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 @@ -71,7 +75,7 @@ let s:prog = provider#ruby#Detect() let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb' if empty(s:prog) - let s:err = 'Cannot find the neovim RubyGem. Try :CheckHealth' + let s:err = 'Cannot find the neovim RubyGem. Try :checkhealth' endif call remote#host#RegisterClone('legacy-ruby-provider', 'ruby') 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/define.vim b/runtime/autoload/remote/define.vim index b04a5d2280..2688a62a82 100644 --- a/runtime/autoload/remote/define.vim +++ b/runtime/autoload/remote/define.vim @@ -89,7 +89,8 @@ endfunction function! remote#define#AutocmdOnHost(host, method, sync, name, opts) let group = s:GetNextAutocmdGroup() - let forward = '"doau '.group.' '.a:name.' ".'.'expand("<amatch>")' + let forward = '"doau '.group.' '.a:name.' ".' + \ . 'fnameescape(expand("<amatch>"))' let a:opts.group = group let bootstrap_def = s:GetAutocmdPrefix(a:name, a:opts) \ .' call remote#define#AutocmdBootstrap("'.a:host.'"' @@ -168,14 +169,40 @@ function! remote#define#FunctionOnChannel(channel, method, sync, name, opts) exe function_def endfunction +let s:busy = {} +let s:pending_notifications = {} function! s:GetRpcFunction(sync) - if a:sync - return 'rpcrequest' + if a:sync ==# 'urgent' + return 'rpcnotify' + elseif a:sync + return 'remote#define#request' endif - return 'rpcnotify' + return 'remote#define#notify' endfunction +function! remote#define#notify(chan, ...) + if get(s:busy, a:chan, 0) > 0 + let pending = get(s:pending_notifications, a:chan, []) + call add(pending, deepcopy(a:000)) + let s:pending_notifications[a:chan] = pending + else + call call('rpcnotify', [a:chan] + a:000) + endif +endfunction + +function! remote#define#request(chan, ...) + let s:busy[a:chan] = get(s:busy, a:chan, 0)+1 + let val = call('rpcrequest', [a:chan]+a:000) + let s:busy[a:chan] -= 1 + if s:busy[a:chan] == 0 + for msg in get(s:pending_notifications, a:chan, []) + call call('rpcnotify', [a:chan] + msg) + endfor + let s:pending_notifications[a:chan] = [] + endif + return val +endfunction function! s:GetCommandPrefix(name, opts) return 'command!'.s:StringifyOpts(a:opts, ['nargs', 'complete', 'range', diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index e695fb7df7..dfaab7d246 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -199,3 +199,7 @@ call remote#host#Register('python3', '*', " Ruby call remote#host#Register('ruby', '*.rb', \ function('provider#ruby#Require')) + +" nodejs +call remote#host#Register('node', '*', + \ function('provider#node#Require')) 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/rust.vim b/runtime/autoload/rust.vim new file mode 100644 index 0000000000..34a3b41773 --- /dev/null +++ b/runtime/autoload/rust.vim @@ -0,0 +1,415 @@ +" Author: Kevin Ballard +" Description: Helper functions for Rust commands/mappings +" Last Modified: May 27, 2014 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +" Jump {{{1 + +function! rust#Jump(mode, function) range + let cnt = v:count1 + normal! m' + if a:mode ==# 'v' + norm! gv + endif + let foldenable = &foldenable + set nofoldenable + while cnt > 0 + execute "call <SID>Jump_" . a:function . "()" + let cnt = cnt - 1 + endwhile + let &foldenable = foldenable +endfunction + +function! s:Jump_Back() + call search('{', 'b') + keepjumps normal! w99[{ +endfunction + +function! s:Jump_Forward() + normal! j0 + call search('{', 'b') + keepjumps normal! w99[{% + call search('{') +endfunction + +" Run {{{1 + +function! rust#Run(bang, args) + let args = s:ShellTokenize(a:args) + if a:bang + let idx = index(l:args, '--') + if idx != -1 + let rustc_args = idx == 0 ? [] : l:args[:idx-1] + let args = l:args[idx+1:] + else + let rustc_args = l:args + let args = [] + endif + else + let rustc_args = [] + endif + + let b:rust_last_rustc_args = l:rustc_args + let b:rust_last_args = l:args + + call s:WithPath(function("s:Run"), rustc_args, args) +endfunction + +function! s:Run(dict, rustc_args, args) + let exepath = a:dict.tmpdir.'/'.fnamemodify(a:dict.path, ':t:r') + if has('win32') + let exepath .= '.exe' + endif + + let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) + let rustc_args = [relpath, '-o', exepath] + a:rustc_args + + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + let pwd = a:dict.istemp ? a:dict.tmpdir : '' + let output = s:system(pwd, shellescape(rustc) . " " . join(map(rustc_args, 'shellescape(v:val)'))) + if output != '' + echohl WarningMsg + echo output + echohl None + endif + if !v:shell_error + exe '!' . shellescape(exepath) . " " . join(map(a:args, 'shellescape(v:val)')) + endif +endfunction + +" Expand {{{1 + +function! rust#Expand(bang, args) + let args = s:ShellTokenize(a:args) + if a:bang && !empty(l:args) + let pretty = remove(l:args, 0) + else + let pretty = "expanded" + endif + call s:WithPath(function("s:Expand"), pretty, args) +endfunction + +function! s:Expand(dict, pretty, args) + try + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + if a:pretty =~? '^\%(everybody_loops$\|flowgraph=\)' + let flag = '--xpretty' + else + let flag = '--pretty' + endif + let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) + let args = [relpath, '-Z', 'unstable-options', l:flag, a:pretty] + a:args + let pwd = a:dict.istemp ? a:dict.tmpdir : '' + let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) + if v:shell_error + echohl WarningMsg + echo output + echohl None + else + new + silent put =output + 1 + d + setl filetype=rust + setl buftype=nofile + setl bufhidden=hide + setl noswapfile + " give the buffer a nice name + let suffix = 1 + let basename = fnamemodify(a:dict.path, ':t:r') + while 1 + let bufname = basename + if suffix > 1 | let bufname .= ' ('.suffix.')' | endif + let bufname .= '.pretty.rs' + if bufexists(bufname) + let suffix += 1 + continue + endif + exe 'silent noautocmd keepalt file' fnameescape(bufname) + break + endwhile + endif + endtry +endfunction + +function! rust#CompleteExpand(lead, line, pos) + if a:line[: a:pos-1] =~ '^RustExpand!\s*\S*$' + " first argument and it has a ! + let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph=", "everybody_loops"] + if !empty(a:lead) + call filter(list, "v:val[:len(a:lead)-1] == a:lead") + endif + return list + endif + + return glob(escape(a:lead, "*?[") . '*', 0, 1) +endfunction + +" Emit {{{1 + +function! rust#Emit(type, args) + let args = s:ShellTokenize(a:args) + call s:WithPath(function("s:Emit"), a:type, args) +endfunction + +function! s:Emit(dict, type, args) + try + let output_path = a:dict.tmpdir.'/output' + + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + let relpath = get(a:dict, 'tmpdir_relpath', a:dict.path) + let args = [relpath, '--emit', a:type, '-o', output_path] + a:args + let pwd = a:dict.istemp ? a:dict.tmpdir : '' + let output = s:system(pwd, shellescape(rustc) . " " . join(map(args, 'shellescape(v:val)'))) + if output != '' + echohl WarningMsg + echo output + echohl None + endif + if !v:shell_error + new + exe 'silent keepalt read' fnameescape(output_path) + 1 + d + if a:type == "llvm-ir" + setl filetype=llvm + let extension = 'll' + elseif a:type == "asm" + setl filetype=asm + let extension = 's' + endif + setl buftype=nofile + setl bufhidden=hide + setl noswapfile + if exists('l:extension') + " give the buffer a nice name + let suffix = 1 + let basename = fnamemodify(a:dict.path, ':t:r') + while 1 + let bufname = basename + if suffix > 1 | let bufname .= ' ('.suffix.')' | endif + let bufname .= '.'.extension + if bufexists(bufname) + let suffix += 1 + continue + endif + exe 'silent noautocmd keepalt file' fnameescape(bufname) + break + endwhile + endif + endif + endtry +endfunction + +" Utility functions {{{1 + +" Invokes func(dict, ...) +" Where {dict} is a dictionary with the following keys: +" 'path' - The path to the file +" 'tmpdir' - The path to a temporary directory that will be deleted when the +" function returns. +" 'istemp' - 1 if the path is a file inside of {dict.tmpdir} or 0 otherwise. +" If {istemp} is 1 then an additional key is provided: +" 'tmpdir_relpath' - The {path} relative to the {tmpdir}. +" +" {dict.path} may be a path to a file inside of {dict.tmpdir} or it may be the +" existing path of the current buffer. If the path is inside of {dict.tmpdir} +" then it is guaranteed to have a '.rs' extension. +function! s:WithPath(func, ...) + let buf = bufnr('') + let saved = {} + let dict = {} + try + let saved.write = &write + set write + let dict.path = expand('%') + let pathisempty = empty(dict.path) + + " Always create a tmpdir in case the wrapped command wants it + let dict.tmpdir = tempname() + call mkdir(dict.tmpdir) + + if pathisempty || !saved.write + let dict.istemp = 1 + " if we're doing this because of nowrite, preserve the filename + if !pathisempty + let filename = expand('%:t:r').".rs" + else + let filename = 'unnamed.rs' + endif + let dict.tmpdir_relpath = filename + let dict.path = dict.tmpdir.'/'.filename + + let saved.mod = &mod + set nomod + + silent exe 'keepalt write! ' . fnameescape(dict.path) + if pathisempty + silent keepalt 0file + endif + else + let dict.istemp = 0 + update + endif + + call call(a:func, [dict] + a:000) + finally + if bufexists(buf) + for [opt, value] in items(saved) + silent call setbufvar(buf, '&'.opt, value) + unlet value " avoid variable type mismatches + endfor + endif + if has_key(dict, 'tmpdir') | silent call s:RmDir(dict.tmpdir) | endif + endtry +endfunction + +function! rust#AppendCmdLine(text) + call setcmdpos(getcmdpos()) + let cmd = getcmdline() . a:text + return cmd +endfunction + +" Tokenize the string according to sh parsing rules +function! s:ShellTokenize(text) + " states: + " 0: start of word + " 1: unquoted + " 2: unquoted backslash + " 3: double-quote + " 4: double-quoted backslash + " 5: single-quote + let l:state = 0 + let l:current = '' + let l:args = [] + for c in split(a:text, '\zs') + if l:state == 0 || l:state == 1 " unquoted + if l:c ==# ' ' + if l:state == 0 | continue | endif + call add(l:args, l:current) + let l:current = '' + let l:state = 0 + elseif l:c ==# '\' + let l:state = 2 + elseif l:c ==# '"' + let l:state = 3 + elseif l:c ==# "'" + let l:state = 5 + else + let l:current .= l:c + let l:state = 1 + endif + elseif l:state == 2 " unquoted backslash + if l:c !=# "\n" " can it even be \n? + let l:current .= l:c + endif + let l:state = 1 + elseif l:state == 3 " double-quote + if l:c ==# '\' + let l:state = 4 + elseif l:c ==# '"' + let l:state = 1 + else + let l:current .= l:c + endif + elseif l:state == 4 " double-quoted backslash + if stridx('$`"\', l:c) >= 0 + let l:current .= l:c + elseif l:c ==# "\n" " is this even possible? + " skip it + else + let l:current .= '\'.l:c + endif + let l:state = 3 + elseif l:state == 5 " single-quoted + if l:c == "'" + let l:state = 1 + else + let l:current .= l:c + endif + endif + endfor + if l:state != 0 + call add(l:args, l:current) + endif + return l:args +endfunction + +function! s:RmDir(path) + " sanity check; make sure it's not empty, /, or $HOME + if empty(a:path) + echoerr 'Attempted to delete empty path' + return 0 + elseif a:path == '/' || a:path == $HOME + echoerr 'Attempted to delete protected path: ' . a:path + return 0 + endif + return system("rm -rf " . shellescape(a:path)) +endfunction + +" Executes {cmd} with the cwd set to {pwd}, without changing Vim's cwd. +" If {pwd} is the empty string then it doesn't change the cwd. +function! s:system(pwd, cmd) + let cmd = a:cmd + if !empty(a:pwd) + let cmd = 'cd ' . shellescape(a:pwd) . ' && ' . cmd + endif + return system(cmd) +endfunction + +" Playpen Support {{{1 +" Parts of gist.vim by Yasuhiro Matsumoto <mattn.jp@gmail.com> reused +" gist.vim available under the BSD license, available at +" http://github.com/mattn/gist-vim +function! s:has_webapi() + if !exists("*webapi#http#post") + try + call webapi#http#post() + catch + endtry + endif + return exists("*webapi#http#post") +endfunction + +function! rust#Play(count, line1, line2, ...) abort + redraw + + let l:rust_playpen_url = get(g:, 'rust_playpen_url', 'https://play.rust-lang.org/') + let l:rust_shortener_url = get(g:, 'rust_shortener_url', 'https://is.gd/') + + if !s:has_webapi() + echohl ErrorMsg | echomsg ':RustPlay depends on webapi.vim (https://github.com/mattn/webapi-vim)' | echohl None + return + endif + + let bufname = bufname('%') + if a:count < 1 + let content = join(getline(a:line1, a:line2), "\n") + else + let save_regcont = @" + let save_regtype = getregtype('"') + silent! normal! gvy + let content = @" + call setreg('"', save_regcont, save_regtype) + endif + + let body = l:rust_playpen_url."?code=".webapi#http#encodeURI(content) + + if strlen(body) > 5000 + echohl ErrorMsg | echomsg 'Buffer too large, max 5000 encoded characters ('.strlen(body).')' | echohl None + return + endif + + let payload = "format=simple&url=".webapi#http#encodeURI(body) + let res = webapi#http#post(l:rust_shortener_url.'create.php', payload, {}) + let url = res.content + + redraw | echomsg 'Done: '.url +endfunction + +" }}}1 + +" vim: set noet sw=8 ts=8: diff --git a/runtime/autoload/rustfmt.vim b/runtime/autoload/rustfmt.vim new file mode 100644 index 0000000000..a689b5e00d --- /dev/null +++ b/runtime/autoload/rustfmt.vim @@ -0,0 +1,107 @@ +" Author: Stephen Sugden <stephen@stephensugden.com> +" +" Adapted from https://github.com/fatih/vim-go +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +if !exists("g:rustfmt_autosave") + let g:rustfmt_autosave = 0 +endif + +if !exists("g:rustfmt_command") + let g:rustfmt_command = "rustfmt" +endif + +if !exists("g:rustfmt_options") + let g:rustfmt_options = "" +endif + +if !exists("g:rustfmt_fail_silently") + let g:rustfmt_fail_silently = 0 +endif + +let s:got_fmt_error = 0 + +function! s:RustfmtCommandRange(filename, line1, line2) + let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]} + return printf("%s %s --write-mode=overwrite --file-lines '[%s]'", g:rustfmt_command, g:rustfmt_options, json_encode(l:arg)) +endfunction + +function! s:RustfmtCommand(filename) + return g:rustfmt_command . " --write-mode=overwrite " . g:rustfmt_options . " " . shellescape(a:filename) +endfunction + +function! s:RunRustfmt(command, curw, tmpname) + if exists("*systemlist") + let out = systemlist(a:command) + else + let out = split(system(a:command), '\r\?\n') + endif + + if v:shell_error == 0 || v:shell_error == 3 + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry + + " Replace current file with temp file, then reload buffer + call rename(a:tmpname, expand('%')) + silent edit! + let &syntax = &syntax + + " only clear location list if it was previously filled to prevent + " clobbering other additions + if s:got_fmt_error + let s:got_fmt_error = 0 + call setloclist(0, []) + lwindow + endif + elseif g:rustfmt_fail_silently == 0 + " otherwise get the errors and put them in the location list + let errors = [] + + for line in out + " src/lib.rs:13:5: 13:10 error: expected `,`, or `}`, found `value` + let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\):\s*\(\d\+:\d\+\s*\)\?\s*error: \(.*\)') + if !empty(tokens) + call add(errors, {"filename": @%, + \"lnum": tokens[2], + \"col": tokens[3], + \"text": tokens[5]}) + endif + endfor + + if empty(errors) + % | " Couldn't detect rustfmt error format, output errors + endif + + if !empty(errors) + call setloclist(0, errors, 'r') + echohl Error | echomsg "rustfmt returned error" | echohl None + endif + + let s:got_fmt_error = 1 + lwindow + " We didn't use the temp file, so clean up + call delete(a:tmpname) + endif + + call winrestview(a:curw) +endfunction + +function! rustfmt#FormatRange(line1, line2) + let l:curw = winsaveview() + let l:tmpname = expand("%:p:h") . "/." . expand("%:p:t") . ".rustfmt" + call writefile(getline(1, '$'), l:tmpname) + + let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2) + + call s:RunRustfmt(command, l:curw, l:tmpname) +endfunction + +function! rustfmt#Format() + let l:curw = winsaveview() + let l:tmpname = expand("%:p:h") . "/." . expand("%:p:t") . ".rustfmt" + call writefile(getline(1, '$'), l:tmpname) + + let command = s:RustfmtCommand(l:tmpname) + + call s:RunRustfmt(command, l:curw, l:tmpname) +endfunction 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/spellfile.vim b/runtime/autoload/spellfile.vim index a5ffa514ea..84584c6e29 100644 --- a/runtime/autoload/spellfile.vim +++ b/runtime/autoload/spellfile.vim @@ -88,8 +88,8 @@ function! spellfile#LoadFile(lang) endif endif if newbufnr == winbufnr(0) - " We are back the old buffer, remove any (half-finished) download. - g/^/d_ + " We are back to the old buffer, remove any (half-finished) download. + keeppatterns g/^/d_ else let newbufnr = winbufnr(0) endif @@ -127,7 +127,7 @@ function! spellfile#LoadFile(lang) exe "write " . dirname . '/' . fname " Also download the .sug file. - g/^/d_ + keeppatterns g/^/d_ let fname = substitute(fname, '\.spl$', '.sug', '') echo 'Downloading ' . fname . '...' call spellfile#Nread(fname) @@ -197,7 +197,7 @@ function! spellfile#WritableSpellDir() " Always use the $XDG_DATA_HOME/nvim/site directory if exists('$XDG_DATA_HOME') return $XDG_DATA_HOME . "/nvim/site/spell" - else + elseif !(has('win32') || has('win64')) return $HOME . "/.local/share/nvim/site/spell" endif for dir in split(&rtp, ',') diff --git a/runtime/autoload/sqlcomplete.vim b/runtime/autoload/sqlcomplete.vim index e80729add4..ea0d8c2de9 100644 --- a/runtime/autoload/sqlcomplete.vim +++ b/runtime/autoload/sqlcomplete.vim @@ -2,7 +2,7 @@ " Language: SQL " Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> " Version: 16.0 -" Last Change: 2015 Dec 29 +" Last Change: 2017 Oct 15 " Homepage: http://www.vim.org/scripts/script.php?script_id=1572 " Usage: For detailed help " ":help sql.txt" 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.")") |