diff options
Diffstat (limited to 'runtime/autoload')
| -rw-r--r-- | runtime/autoload/ada.vim | 2 | ||||
| -rw-r--r-- | runtime/autoload/health.vim | 54 | ||||
| -rw-r--r-- | runtime/autoload/health/nvim.vim | 26 | ||||
| -rw-r--r-- | runtime/autoload/health/provider.vim | 171 | ||||
| -rw-r--r-- | runtime/autoload/javascriptcomplete.vim | 4 | ||||
| -rw-r--r-- | runtime/autoload/man.vim | 19 | ||||
| -rw-r--r-- | runtime/autoload/paste.vim | 4 | ||||
| -rw-r--r-- | runtime/autoload/provider.vim | 21 | ||||
| -rw-r--r-- | runtime/autoload/provider/clipboard.vim | 61 | ||||
| -rw-r--r-- | runtime/autoload/provider/node.vim | 107 | ||||
| -rw-r--r-- | runtime/autoload/provider/python.vim | 6 | ||||
| -rw-r--r-- | runtime/autoload/provider/python3.vim | 6 | ||||
| -rw-r--r-- | runtime/autoload/provider/pythonx.vim | 27 | ||||
| -rw-r--r-- | runtime/autoload/provider/ruby.vim | 33 | ||||
| -rw-r--r-- | runtime/autoload/remote/define.vim | 35 | ||||
| -rw-r--r-- | runtime/autoload/remote/host.vim | 4 | ||||
| -rw-r--r-- | runtime/autoload/rust.vim | 415 | ||||
| -rw-r--r-- | runtime/autoload/rustfmt.vim | 107 | ||||
| -rw-r--r-- | runtime/autoload/spellfile.vim | 8 | ||||
| -rw-r--r-- | runtime/autoload/sqlcomplete.vim | 2 | ||||
| -rw-r--r-- | runtime/autoload/tutor.vim | 200 |
21 files changed, 977 insertions, 335 deletions
diff --git a/runtime/autoload/ada.vim b/runtime/autoload/ada.vim index ce3a19369a..d04feb9250 100644 --- a/runtime/autoload/ada.vim +++ b/runtime/autoload/ada.vim @@ -591,7 +591,7 @@ function ada#Map_Menu (Text, Keys, Command) \" :" . a:Command execute \ "inoremap <buffer>" . - \ " <Learder>a" . a:Keys . + \ " <Leader>a" . a:Keys . \" <C-O>:" . a:Command endif return diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index 1d8cae7d19..56ae2071e9 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 @@ -33,7 +33,8 @@ function! health#check(plugin_names) abort setlocal wrap breakindent setlocal filetype=markdown setlocal conceallevel=2 concealcursor=nc - setlocal keywordprg=:help iskeyword=@,48-57,_,192-255,-,# + setlocal keywordprg=:help + let &l:iskeyword='!-~,^*,^|,^",192-255' call s:enhance_syntax() if empty(healthchecks) @@ -89,29 +90,29 @@ endfunction " Changes ':h clipboard' to ':help |clipboard|'. function! s:help_to_link(s) abort - return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n]+)', ':help |\1|', 'g') + return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n ]+)', ':help |\1|', 'g') endfunction -" Format a message for a specific report item +" Format a message for a specific report item. +" a:1: Optional advice (string or list) function! s:format_report_message(status, msg, ...) abort " {{{ let output = ' - ' . a:status . ': ' . s:indent_after_line1(a:msg, 4) - let suggestions = [] " 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 'a:1: expected String or List' endif - endif - " Report each suggestion - if len(suggestions) > 0 - let output .= "\n - SUGGESTIONS:" + " Report each suggestion + if !empty(advice) + let output .= "\n - ADVICE:" + for suggestion in advice + let output .= "\n - " . s:indent_after_line1(suggestion, 10) + endfor + endif endif - for suggestion in suggestions - let output .= "\n - " . s:indent_after_line1(suggestion, 10) - endfor return s:help_to_link(output) endfunction " }}} @@ -123,10 +124,11 @@ 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. +" a:1: Optional advice (string or list) function! health#report_warn(msg, ...) abort " {{{ if a:0 > 0 echo s:format_report_message('WARNING', a:msg, a:1) @@ -136,6 +138,7 @@ function! health#report_warn(msg, ...) abort " {{{ endfunction " }}} " Reports a failed healthcheck. +" a:1: Optional advice (string or list) function! health#report_error(msg, ...) abort " {{{ if a:0 > 0 echo s:format_report_message('ERROR', a:msg, a:1) @@ -158,7 +161,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 3834cbd054..d09a714719 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -4,13 +4,26 @@ function! s:check_config() abort let ok = v:true call health#report_start('Configuration') + " 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+", + 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') endif @@ -45,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) @@ -87,8 +100,8 @@ function! s:check_performance() abort else call health#report_info(buildtype) call health#report_warn( - \ "Non-optimized build-type. Nvim will be slower.", - \ ["Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.", + \ 'Non-optimized build-type. Nvim will be slower.', + \ ['Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.', \ s:suggest_faq]) endif endfunction @@ -160,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 31a235a397..4adab1aa76 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -13,6 +13,12 @@ function! s:normalize_path(s) abort return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g') endfunction +" Returns TRUE if `cmd` exits with success, else FALSE. +function! s:cmd_ok(cmd) abort + call system(a:cmd) + return v:shell_error == 0 +endfunction + " Simple version comparison. function! s:version_cmp(a, b) abort let a = split(a:a, '\.', 0) @@ -120,10 +126,21 @@ endfunction function! s:check_clipboard() abort call health#report_start('Clipboard (optional)') + if !empty($TMUX) && executable('tmux') && executable('pbpaste') && !s:cmd_ok('pbpaste') + let tmux_version = matchstr(system('tmux -V'), '\d\+\.\d\+') + call health#report_error('pbcopy does not work with tmux version: '.tmux_version, + \ ['Install tmux 2.6+. https://superuser.com/q/231130', + \ 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233']) + endif + 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.', + \ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.', \ [':help clipboard']) else call health#report_ok('Clipboard tool found: '. clipboard_tool) @@ -235,7 +252,7 @@ function! s:check_python(version) abort 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' @@ -243,8 +260,24 @@ function! s:check_python(version) abort let python_multiple = [] if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') - call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) - return + call health#report_info('Disabled ('.loaded_var.'='.eval(loaded_var).'). This might be due to some previous error.') + endif + + if !empty(pyenv) + if empty(pyenv_root) + call health#report_info( + \ 'pyenv was found, but $PYENV_ROOT is not set. `pyenv root` will be used.' + \ .' If you run into problems, try setting $PYENV_ROOT explicitly.' + \ ) + let pyenv_root = s:trim(s:system([pyenv, 'root'])) + endif + + if !isdirectory(pyenv_root) + call health#report_error('Invalid pyenv root: '.pyenv_root) + else + call health#report_info(printf('pyenv: %s', pyenv)) + call health#report_info(printf('pyenv root: %s', pyenv_root)) + endif endif if exists('g:'.host_prog_var) @@ -255,9 +288,6 @@ function! s:check_python(version) abort 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) - call health#report_warn(pythonx_errs) - endif endif if !empty(pyname) @@ -278,15 +308,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) @@ -316,25 +337,24 @@ 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 endif endif - if !empty(python_bin) - if empty(venv) && !empty(pyenv) && !exists('g:'.host_prog_var) + if !empty(python_bin) && !exists('g:'.host_prog_var) + if empty(venv) && !empty(pyenv) \ && !empty(pyenv_root) && resolve(python_bin) !~# '^'.pyenv_root.'/' call health#report_warn('pyenv is not set up optimally.', [ \ printf('Create a virtualenv specifically ' \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid ' - \ . '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) + elseif !empty(venv) if !empty(pyenv_root) let venv_root = pyenv_root else @@ -359,27 +379,16 @@ function! s:check_python(version) abort let python_bin = '' endif - " Check if $VIRTUAL_ENV is active - let virtualenv_inactive = 0 - + " Check if $VIRTUAL_ENV is valid. if exists('$VIRTUAL_ENV') - if !empty(pyenv) - let pyenv_prefix = resolve(s:trim(s:system([pyenv, 'prefix']))) - if $VIRTUAL_ENV != pyenv_prefix - let virtualenv_inactive = 1 - endif - elseif !empty(pyname) && exepath(pyname) !~# '^'.$VIRTUAL_ENV.'/' - let virtualenv_inactive = 1 + if !empty(pyname) && $VIRTUAL_ENV !=# matchstr(exepath(pyname), '^\V'.$VIRTUAL_ENV) + call health#report_warn( + \ '$VIRTUAL_ENV exists but appears to be inactive. ' + \ . 'This could lead to unexpected results.', + \ [ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654' ]) endif endif - if virtualenv_inactive - call health#report_warn( - \ '$VIRTUAL_ENV exists but appears to be inactive. ' - \ . 'This could lead to unexpected results.', - \ [ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229' ]) - endif - " Diagnostic output call health#report_info('Executable: ' . (empty(python_bin) ? 'Not found' : python_bin)) if len(python_multiple) @@ -444,10 +453,11 @@ 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', - \ 'Is the gem bin directory in $PATH? Check `gem environment`.', - \ 'If you are using rvm/rbenv/chruby, try "rehashing".']) + call health#report_warn('`neovim-ruby-host` not found.', + \ ['Run `gem install neovim` to ensure the neovim RubyGem is installed.', + \ 'Run `gem environment` to ensure the gem bin directory is in $PATH.', + \ 'If you are using rvm/rbenv/chruby, try "rehashing".', + \ 'See :help g:ruby_host_prog for non-standard gem installations.']) return endif call health#report_info('Host: '. host) @@ -480,9 +490,80 @@ function! s:check_ruby() abort endif endfunction +function! s:check_node() abort + call health#report_start('Node.js provider (optional)') + + let loaded_var = 'g:loaded_node_provider' + if exists(loaded_var) && !exists('*provider#node#Call') + call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + 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 + let node_v = get(split(s:system('node -v'), "\n"), 0, '') + call health#report_info('Node.js: '. node_v) + if !s:shell_error && s:version_cmp(node_v[1:], '6.0.0') < 0 + call health#report_warn('Neovim node.js host does not support '.node_v) + " Skip further checks, they are nonsense if nodejs is too old. + return + endif + if !provider#node#can_inspect() + call health#report_warn('node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.') + endif + + 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('Neovim node.js 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 = ['node', host, '--version'] + let current_npm = s:system(current_npm_cmd) + if s:shell_error + call health#report_error('Failed to run: '. string(current_npm_cmd), + \ ['Report this issue with the output of: ', string(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 package 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 dd71ede680..4d43a4582b 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -65,9 +65,9 @@ function! man#open_page(count, count1, mods, ...) abort try set eventignore+=BufReadCmd if a:mods !~# 'tab' && s:find_man() - execute 'silent edit' fnameescape(bufname) + execute 'silent keepalt edit' fnameescape(bufname) else - execute 'silent' a:mods 'split' fnameescape(bufname) + execute 'silent keepalt' a:mods 'split' fnameescape(bufname) endif finally set eventignore-=BufReadCmd @@ -148,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 - let cmd = ['env', 'MANPAGER=cat', 'MANWIDTH='.manwidth, 'man'] + " Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. + let cmd = ['env', 'MANPAGER=cat', 'MANWIDTH='.manwidth, 'MAN_KEEP_FORMATTING=1', 'man'] return s:system(cmd + (s:localfile_arg ? ['-l', a:path] : [a:path])) endfunction @@ -157,11 +158,10 @@ function! s:put_page(page) abort setlocal noreadonly silent keepjumps %delete _ silent put =a:page - " 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 + lua require("man").highlight_man_page() setlocal filetype=man endfunction @@ -299,6 +299,12 @@ endfunction " see man#extract_sect_and_name_ref on why tolower(sect) function! man#complete(arg_lead, cmd_line, cursor_pos) abort let args = split(a:cmd_line) + let cmd_offset = index(args, 'Man') + if cmd_offset > 0 + " Prune all arguments up to :Man itself. Otherwise modifier commands like + " :tab, :vertical, etc. would lead to a wrong length. + let args = args[cmd_offset:] + endif let l = len(args) if l > 3 return @@ -370,13 +376,12 @@ function! s:format_candidate(path, psect) abort endfunction function! man#init_pager() abort - " 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 keepjumps 1 endif + lua require("man").highlight_man_page() " This is not perfect. See `man glDrawArraysInstanced`. Since the title is " all caps it is impossible to tell what the original capitilization was. let ref = substitute(matchstr(getline(1), '^[^)]\+)'), ' ', '_', 'g') 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/provider.vim b/runtime/autoload/provider.vim new file mode 100644 index 0000000000..dc24e801d0 --- /dev/null +++ b/runtime/autoload/provider.vim @@ -0,0 +1,21 @@ +" Common functions for providers + +" Start the provider and perform a 'poll' request +" +" Returns a valid channel on success +function! provider#Poll(argv, orig_name, log_env) abort + let job = {'rpc': v:true, 'stderr_buffered': v:true} + try + let channel_id = jobstart(a:argv, job) + if channel_id > 0 && rpcrequest(channel_id, 'poll') ==# 'ok' + return channel_id + endif + catch + echomsg v:throwpoint + echomsg v:exception + for row in get(job, 'stderr', []) + echomsg row + endfor + endtry + throw remote#host#LoadErrorForHost(a:orig_name, a:log_env) +endfunction diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index a67681d28e..87a0315073 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -3,10 +3,11 @@ " 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': [], 'stderr_buffered': v:true } function! s:selection.on_exit(jobid, data, event) abort " At this point this nvim instance might already have launched @@ -14,17 +15,22 @@ function! s:selection.on_exit(jobid, data, event) abort if self.owner == a:jobid let self.owner = 0 endif + if a:data != 0 + echohl WarningMsg + echomsg 'clipboard: error invoking '.get(self.argv, 0, '?').': '.join(self.stderr) + echohl None + endif endfunction -let s:selections = { '*': s:selection, '+': copy(s:selection)} +let s:selections = { '*': s:selection, '+': copy(s:selection) } function! s:try_cmd(cmd, ...) abort let argv = split(a:cmd, " ") - let out = a:0 ? systemlist(argv, a:1, 1) : systemlist(argv, [''], 1) + let out = systemlist(argv, (a:0 ? a:1 : ['']), 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 @@ -48,11 +54,17 @@ endfunction function! provider#clipboard#Executable() abort 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', 1) + let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0) return get(g:clipboard, 'name', 'g:clipboard') - elseif has('mac') && executable('pbcopy') + elseif has('mac') && executable('pbpaste') && s:cmd_ok('pbpaste') let s:copy['+'] = 'pbcopy' let s:paste['+'] = 'pbpaste' let s:copy['*'] = s:copy['+'] @@ -97,16 +109,17 @@ function! provider#clipboard#Executable() abort return 'tmux' endif - let s:err = 'clipboard: No clipboard tool available. :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) abort if s:selections[a:reg].owner > 0 return s:selections[a:reg].data @@ -127,28 +140,40 @@ function! s:clipboard.set(lines, regtype, reg) abort return 0 end - let selection = s:selections[a:reg] - if selection.owner > 0 + if s:selections[a:reg].owner > 0 " The previous provider instance should exit when the new one takes " ownership, but kill it to be sure we don't fill up the job table. - call jobstop(selection.owner) + call jobstop(s:selections[a:reg].owner) end + let s:selections[a:reg] = copy(s:selection) + let selection = s:selections[a:reg] 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) abort - return call(s:clipboard[a:method],a:args,s:clipboard) + 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..39b5dc63b8 --- /dev/null +++ b/runtime/autoload/provider/node.vim @@ -0,0 +1,107 @@ +if exists('g:loaded_node_provider') + finish +endif +let g:loaded_node_provider = 1 + +function! s:is_minimum_version(version, min_major, min_minor) abort + if empty(a:version) + let nodejs_version = get(split(system(['node', '-v']), "\n"), 0, '') + if v:shell_error || nodejs_version[0] !=# 'v' + return 0 + endif + else + let nodejs_version = a:version + endif + " Remove surrounding junk. Example: 'v4.12.0' => '4.12.0' + let nodejs_version = matchstr(nodejs_version, '\(\d\.\?\)\+') + " [major, minor, patch] + let v_list = split(nodejs_version, '\.') + return len(v_list) == 3 + \ && ((str2nr(v_list[0]) > str2nr(a:min_major)) + \ || (str2nr(v_list[0]) == str2nr(a:min_major) + \ && str2nr(v_list[1]) >= str2nr(a:min_minor))) +endfunction + +" Support for --inspect-brk requires node 6.12+ or 7.6+ or 8+ +" Return 1 if it is supported +" Return 0 otherwise +function! provider#node#can_inspect() abort + if !executable('node') + return 0 + endif + let ver = get(split(system(['node', '-v']), "\n"), 0, '') + if v:shell_error || ver[0] !=# 'v' + return 0 + endif + return (ver[1] ==# '6' && s:is_minimum_version(ver, 6, 12)) + \ || s:is_minimum_version(ver, 7, 6) +endfunction + +function! provider#node#Detect() abort + if exists('g:node_host_prog') + return g:node_host_prog + endif + let global_modules = get(split(system('npm root -g'), "\n"), 0, '') + if v:shell_error || !isdirectory(global_modules) + return '' + endif + if !s:is_minimum_version(v:null, 6, 0) + return '' + endif + let entry_point = glob(global_modules . '/neovim/bin/cli.js') + if !filereadable(entry_point) + return '' + endif + return entry_point +endfunction + +function! provider#node#Prog() abort + return s:prog +endfunction + +function! provider#node#Require(host) abort + if s:err != '' + echoerr s:err + return + endif + + let args = ['node'] + + if !empty($NVIM_NODE_HOST_DEBUG) && provider#node#can_inspect() + call add(args, '--inspect-brk') + endif + + call add(args, provider#node#Prog()) + + return provider#Poll(args, a:host.orig_name, '$NVIM_NODE_LOG_FILE') +endfunction + +function! provider#node#Call(method, args) abort + 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 81fe194cb9..a06cbe4814 100644 --- a/runtime/autoload/provider/python.vim +++ b/runtime/autoload/provider/python.vim @@ -11,11 +11,11 @@ let g:loaded_python_provider = 1 let [s:prog, s:err] = provider#pythonx#Detect(2) -function! provider#python#Prog() +function! provider#python#Prog() abort return s:prog endfunction -function! provider#python#Error() +function! provider#python#Error() abort return s:err endfunction @@ -29,7 +29,7 @@ endif call remote#host#RegisterClone('legacy-python-provider', 'python') call remote#host#RegisterPlugin('legacy-python-provider', 'script_host.py', []) -function! provider#python#Call(method, args) +function! provider#python#Call(method, args) abort if s:err != '' return endif diff --git a/runtime/autoload/provider/python3.vim b/runtime/autoload/provider/python3.vim index 0c3b75b73d..242a224cb3 100644 --- a/runtime/autoload/provider/python3.vim +++ b/runtime/autoload/provider/python3.vim @@ -11,11 +11,11 @@ let g:loaded_python3_provider = 1 let [s:prog, s:err] = provider#pythonx#Detect(3) -function! provider#python3#Prog() +function! provider#python3#Prog() abort return s:prog endfunction -function! provider#python3#Error() +function! provider#python3#Error() abort return s:err endfunction @@ -29,7 +29,7 @@ endif call remote#host#RegisterClone('legacy-python3-provider', 'python3') call remote#host#RegisterPlugin('legacy-python3-provider', 'script_host.py', []) -function! provider#python3#Call(method, args) +function! provider#python3#Call(method, args) abort if s:err != '' return endif diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 2f64c22c71..b51c398410 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -5,18 +5,6 @@ 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 - function! provider#pythonx#Require(host) abort let ver = (a:host.orig_name ==# 'python') ? 2 : 3 @@ -30,20 +18,7 @@ function! provider#pythonx#Require(host) abort call add(args, plugin.path) endfor - 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 get(s:stderr, channel_id, []) - echomsg row - endfor - endtry - throw remote#host#LoadErrorForHost(a:host.orig_name, - \ '$NVIM_PYTHON_LOG_FILE') + return provider#Poll(args, a:host.orig_name, '$NVIM_PYTHON_LOG_FILE') endfunction function! provider#pythonx#Detect(major_ver) abort diff --git a/runtime/autoload/provider/ruby.vim b/runtime/autoload/provider/ruby.vim index 91b7fb9f2c..2fe3817512 100644 --- a/runtime/autoload/provider/ruby.vim +++ b/runtime/autoload/provider/ruby.vim @@ -4,26 +4,15 @@ if exists('g:loaded_ruby_provider') endif let g:loaded_ruby_provider = 1 -let s:stderr = {} -let s:job_opts = {'rpc': v:true} - -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 - function! provider#ruby#Detect() abort if exists("g:ruby_host_prog") return g:ruby_host_prog else - return exepath('neovim-ruby-host') + return has('win32') ? exepath('neovim-ruby-host.bat') : exepath('neovim-ruby-host') end endfunction -function! provider#ruby#Prog() +function! provider#ruby#Prog() abort return s:prog endfunction @@ -35,22 +24,10 @@ function! provider#ruby#Require(host) abort let prog .= " " . shellescape(plugin.path) endfor - try - let channel_id = jobstart(prog, s:job_opts) - if rpcrequest(channel_id, 'poll') ==# 'ok' - return channel_id - endif - catch - echomsg v:throwpoint - echomsg v:exception - for row in get(s:stderr, channel_id, []) - echomsg row - endfor - endtry - throw remote#host#LoadErrorForHost(a:host.orig_name, '$NVIM_RUBY_LOG_FILE') + return provider#Poll(prog, a:host.orig_name, '$NVIM_RUBY_LOG_FILE') endfunction -function! provider#ruby#Call(method, args) +function! provider#ruby#Call(method, args) abort if s:err != '' echoerr s:err return @@ -75,7 +52,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/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/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/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/tutor.vim b/runtime/autoload/tutor.vim index 43d8a87886..0f01190b9b 100644 --- a/runtime/autoload/tutor.vim +++ b/runtime/autoload/tutor.vim @@ -2,9 +2,6 @@ " Setup: {{{1 function! tutor#SetupVim() - if &columns < 90 - set columns=90 - endif if !exists('g:did_load_ftplugin') || g:did_load_ftplugin != 1 filetype plugin on endif @@ -15,30 +12,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 +30,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 +43,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 +85,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 |
