# HG changeset patch # User Christian Brabandt # Date 1490121006 -3600 # Node ID 146a1e213b600ef477b029d254c4c36f5ac27414 # Parent 641b9824914520bc809f4716ea9b44a1bc203d33 Update runtime files. Add Rust support. commit https://github.com/vim/vim/commit/3c2881dc1195f53ebafc387378399ddd6cb677a7 Author: Bram Moolenaar Date: Tue Mar 21 19:18:29 2017 +0100 Update runtime files. Add Rust support. diff --git a/runtime/autoload/rust.vim b/runtime/autoload/rust.vim new file mode 100644 --- /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 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 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 --- /dev/null +++ b/runtime/autoload/rustfmt.vim @@ -0,0 +1,107 @@ +" Author: Stephen Sugden +" +" 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/compiler/cargo.vim b/runtime/compiler/cargo.vim new file mode 100644 --- /dev/null +++ b/runtime/compiler/cargo.vim @@ -0,0 +1,35 @@ +" Vim compiler file +" Compiler: Cargo Compiler +" Maintainer: Damien Radtke +" Latest Revision: 2014 Sep 24 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +if exists('current_compiler') + finish +endif +runtime compiler/rustc.vim +let current_compiler = "cargo" + +let s:save_cpo = &cpo +set cpo&vim + +if exists(':CompilerSet') != 2 + command -nargs=* CompilerSet setlocal +endif + +if exists('g:cargo_makeprg_params') + execute 'CompilerSet makeprg=cargo\ '.escape(g:cargo_makeprg_params, ' \|"').'\ $*' +else + CompilerSet makeprg=cargo\ $* +endif + +" Ignore general cargo progress messages +CompilerSet errorformat+= + \%-G%\\s%#Downloading%.%#, + \%-G%\\s%#Compiling%.%#, + \%-G%\\s%#Finished%.%#, + \%-G%\\s%#error:\ Could\ not\ compile\ %.%#, + \%-G%\\s%#To\ learn\ more\\,%.%# + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/compiler/rustc.vim b/runtime/compiler/rustc.vim new file mode 100644 --- /dev/null +++ b/runtime/compiler/rustc.vim @@ -0,0 +1,46 @@ +" Vim compiler file +" Compiler: Rust Compiler +" Maintainer: Chris Morgan +" Latest Revision: 2013 Jul 12 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +if exists("current_compiler") + finish +endif +let current_compiler = "rustc" + +let s:cpo_save = &cpo +set cpo&vim + +if exists(":CompilerSet") != 2 + command -nargs=* CompilerSet setlocal +endif + +if exists("g:rustc_makeprg_no_percent") && g:rustc_makeprg_no_percent != 0 + CompilerSet makeprg=rustc +else + CompilerSet makeprg=rustc\ \% +endif + +" Old errorformat (before nightly 2016/08/10) +CompilerSet errorformat= + \%f:%l:%c:\ %t%*[^:]:\ %m, + \%f:%l:%c:\ %*\\d:%*\\d\ %t%*[^:]:\ %m, + \%-G%f:%l\ %s, + \%-G%*[\ ]^, + \%-G%*[\ ]^%*[~], + \%-G%*[\ ]... + +" New errorformat (after nightly 2016/08/10) +CompilerSet errorformat+= + \%-G, + \%-Gerror:\ aborting\ %.%#, + \%-Gerror:\ Could\ not\ compile\ %.%#, + \%Eerror:\ %m, + \%Eerror[E%n]:\ %m, + \%Wwarning:\ %m, + \%Inote:\ %m, + \%C\ %#-->\ %f:%l:%c + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/doc/Makefile b/runtime/doc/Makefile --- a/runtime/doc/Makefile +++ b/runtime/doc/Makefile @@ -30,6 +30,7 @@ DOCS = \ filetype.txt \ fold.txt \ ft_ada.txt \ + ft_rust.txt \ ft_sql.txt \ gui.txt \ gui_w32.txt \ @@ -165,6 +166,7 @@ HTMLS = \ filetype.html \ fold.html \ ft_ada.html \ + ft_rust.html \ ft_sql.html \ gui.html \ gui_w32.html \ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.0. Last change: 2017 Mar 18 +*eval.txt* For Vim version 8.0. Last change: 2017 Mar 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2261,12 +2261,13 @@ readfile({fname} [, {binary} [, {max}]]) reltime([{start} [, {end}]]) List get time value reltimefloat({time}) Float turn the time value into a Float reltimestr({time}) String turn time value into a String -remote_expr({server}, {string} [, {idvar}]) +remote_expr({server}, {string} [, {idvar} [, {timeout}]]) String send expression remote_foreground({server}) Number bring Vim server to the foreground remote_peek({serverid} [, {retvar}]) Number check for reply string -remote_read({serverid}) String read reply string +remote_read({serverid} [, {timeout}]) + String read reply string remote_send({server}, {string} [, {idvar}]) String send key sequence remote_startserver({name}) none become server {name} diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 8.0. Last change: 2017 Jan 04 +*filetype.txt* For Vim version 8.0. Last change: 2017 Mar 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -663,6 +663,12 @@ Since the text for this plugin is rather file: |pi_spec.txt|. +RUST *ft-rust* + +Since the text for this plugin is rather long it has been put in a separate +file: |ft_rust.txt|. + + SQL *ft-sql* Since the text for this plugin is rather long it has been put in a separate diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -1,4 +1,4 @@ -*fold.txt* For Vim version 8.0. Last change: 2016 Jan 02 +*fold.txt* For Vim version 8.0. Last change: 2017 Mar 18 VIM REFERENCE MANUAL by Bram Moolenaar @@ -79,7 +79,7 @@ This will call a function to compute the :set foldexpr=MyFoldLevel(v:lnum) This will make a fold out of paragraphs separated by blank lines: > :set foldexpr=getline(v:lnum)=~'^\\s*$'&&getline(v:lnum+1)=~'\\S'?'<1':1 -this does the same: > +This does the same: > :set foldexpr=getline(v:lnum-1)=~'^\\s*$'&&getline(v:lnum)=~'\\S'?'>1':1 Note that backslashes must be used to escape characters that ":set" handles @@ -203,7 +203,7 @@ and the level given by the marker: 1. If a marker with the same fold level is encountered, the previous fold ends and another fold with the same level starts. 2. If a marker with a higher fold level is found, a nested fold is started. -3. if a marker with a lower fold level is found, all folds up to and including +3. If a marker with a lower fold level is found, all folds up to and including this level end and a fold with the specified level starts. The number indicates the fold level. A zero cannot be used (a marker with diff --git a/runtime/doc/ft_rust.txt b/runtime/doc/ft_rust.txt new file mode 100644 --- /dev/null +++ b/runtime/doc/ft_rust.txt @@ -0,0 +1,237 @@ +*ft_rust.txt* Filetype plugin for Rust + +============================================================================== +CONTENTS *rust* *ft-rust* + +1. Introduction |rust-intro| +2. Settings |rust-settings| +3. Commands |rust-commands| +4. Mappings |rust-mappings| + +============================================================================== +INTRODUCTION *rust-intro* + +This plugin provides syntax and supporting functionality for the Rust +filetype. + +============================================================================== +SETTINGS *rust-settings* + +This plugin has a few variables you can define in your vimrc that change the +behavior of the plugin. + + *g:rustc_path* +g:rustc_path~ + Set this option to the path to rustc for use in the |:RustRun| and + |:RustExpand| commands. If unset, "rustc" will be located in $PATH: > + let g:rustc_path = $HOME."/bin/rustc" +< + + *g:rustc_makeprg_no_percent* +g:rustc_makeprg_no_percent~ + Set this option to 1 to have 'makeprg' default to "rustc" instead of + "rustc %": > + let g:rustc_makeprg_no_percent = 1 +< + + *g:rust_conceal* +g:rust_conceal~ + Set this option to turn on the basic |conceal| support: > + let g:rust_conceal = 1 +< + + *g:rust_conceal_mod_path* +g:rust_conceal_mod_path~ + Set this option to turn on |conceal| for the path connecting token + "::": > + let g:rust_conceal_mod_path = 1 +< + + *g:rust_conceal_pub* +g:rust_conceal_pub~ + Set this option to turn on |conceal| for the "pub" token: > + let g:rust_conceal_pub = 1 +< + + *g:rust_recommended_style* +g:rust_recommended_style~ + Set this option to enable vim indentation and textwidth settings to + conform to style conventions of the rust standard library (i.e. use 4 + spaces for indents and sets 'textwidth' to 99). This option is enabled + by default. To disable it: > + let g:rust_recommended_style = 0 +< + + *g:rust_fold* +g:rust_fold~ + Set this option to turn on |folding|: > + let g:rust_fold = 1 +< + Value Effect ~ + 0 No folding + 1 Braced blocks are folded. All folds are open by + default. + 2 Braced blocks are folded. 'foldlevel' is left at the + global value (all folds are closed by default). + + *g:rust_bang_comment_leader* +g:rust_bang_comment_leader~ + Set this option to 1 to preserve the leader on multi-line doc comments + using the /*! syntax: > + let g:rust_bang_comment_leader = 1 +< + + *g:ftplugin_rust_source_path* +g:ftplugin_rust_source_path~ + Set this option to a path that should be prepended to 'path' for Rust + source files: > + let g:ftplugin_rust_source_path = $HOME.'/dev/rust' +< + + *g:rustfmt_command* +g:rustfmt_command~ + Set this option to the name of the 'rustfmt' executable in your $PATH. If + not specified it defaults to 'rustfmt' : > + let g:rustfmt_command = 'rustfmt' +< + *g:rustfmt_autosave* +g:rustfmt_autosave~ + Set this option to 1 to run |:RustFmt| automatically when saving a + buffer. If not specified it defaults to 0 : > + let g:rustfmt_autosave = 0 +< + *g:rustfmt_fail_silently* +g:rustfmt_fail_silently~ + Set this option to 1 to prevent 'rustfmt' from populating the + |location-list| with errors. If not specified it defaults to 0: > + let g:rustfmt_fail_silently = 0 +< + *g:rustfmt_options* +g:rustfmt_options~ + Set this option to a string of options to pass to 'rustfmt'. The + write-mode is already set to 'overwrite'. If not specified it + defaults to '' : > + let g:rustfmt_options = '' +< + + *g:rust_playpen_url* +g:rust_playpen_url~ + Set this option to override the url for the playpen to use: > + let g:rust_playpen_url = 'https://play.rust-lang.org/' +< + + *g:rust_shortener_url* +g:rust_shortener_url~ + Set this option to override the url for the url shortener: > + let g:rust_shortener_url = 'https://is.gd/' +< + + +============================================================================== +COMMANDS *rust-commands* + +:RustRun [args] *:RustRun* +:RustRun! [rustc-args] [--] [args] + Compiles and runs the current file. If it has unsaved changes, + it will be saved first using |:update|. If the current file is + an unnamed buffer, it will be written to a temporary file + first. The compiled binary is always placed in a temporary + directory, but is run from the current directory. + + The arguments given to |:RustRun| will be passed to the + compiled binary. + + If ! is specified, the arguments are passed to rustc instead. + A "--" argument will separate the rustc arguments from the + arguments passed to the binary. + + If |g:rustc_path| is defined, it is used as the path to rustc. + Otherwise it is assumed rustc can be found in $PATH. + +:RustExpand [args] *:RustExpand* +:RustExpand! [TYPE] [args] + Expands the current file using --pretty and displays the + results in a new split. If the current file has unsaved + changes, it will be saved first using |:update|. If the + current file is an unnamed buffer, it will be written to a + temporary file first. + + The arguments given to |:RustExpand| will be passed to rustc. + This is largely intended for specifying various --cfg + configurations. + + If ! is specified, the first argument is the expansion type to + pass to rustc --pretty. Otherwise it will default to + "expanded". + + If |g:rustc_path| is defined, it is used as the path to rustc. + Otherwise it is assumed rustc can be found in $PATH. + +:RustEmitIr [args] *:RustEmitIr* + Compiles the current file to LLVM IR and displays the results + in a new split. If the current file has unsaved changes, it + will be saved first using |:update|. If the current file is an + unnamed buffer, it will be written to a temporary file first. + + The arguments given to |:RustEmitIr| will be passed to rustc. + + If |g:rustc_path| is defined, it is used as the path to rustc. + Otherwise it is assumed rustc can be found in $PATH. + +:RustEmitAsm [args] *:RustEmitAsm* + Compiles the current file to assembly and displays the results + in a new split. If the current file has unsaved changes, it + will be saved first using |:update|. If the current file is an + unnamed buffer, it will be written to a temporary file first. + + The arguments given to |:RustEmitAsm| will be passed to rustc. + + If |g:rustc_path| is defined, it is used as the path to rustc. + Otherwise it is assumed rustc can be found in $PATH. + +:RustPlay *:RustPlay* + This command will only work if you have web-api.vim installed + (available at https://github.com/mattn/webapi-vim). It sends the + current selection, or if nothing is selected, the entirety of the + current buffer to the Rust playpen, and emits a message with the + shortened URL to the playpen. + + |g:rust_playpen_url| is the base URL to the playpen, by default + "https://play.rust-lang.org/". + + |g:rust_shortener_url| is the base url for the shorterner, by + default "https://is.gd/" + +:RustFmt *:RustFmt* + Runs |g:rustfmt_command| on the current buffer. If + |g:rustfmt_options| is set then those will be passed to the + executable. + + If |g:rustfmt_fail_silently| is 0 (the default) then it + will populate the |location-list| with the errors from + |g:rustfmt_command|. If |g:rustfmt_fail_silently| is set to 1 + then it will not populate the |location-list|. + +:RustFmtRange *:RustFmtRange* + Runs |g:rustfmt_command| with selected range. See + |:RustFmt| for any other information. + +============================================================================== +MAPPINGS *rust-mappings* + +This plugin defines mappings for |[[| and |]]| to support hanging indents. + +It also has a few other mappings: + + *rust_* + Executes |:RustRun| with no arguments. + Note: This binding is only available in MacVim. + + *rust_* + Populates the command line with |:RustRun|! using the + arguments given to the last invocation, but does not + execute it. + Note: This binding is only available in MacVim. + +============================================================================== + vim:tw=78:sw=4:noet:ts=8:ft=help:norl: diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -1,4 +1,4 @@ -*helphelp.txt* For Vim version 8.0. Last change: 2017 Feb 09 +*helphelp.txt* For Vim version 8.0. Last change: 2017 Mar 19 VIM REFERENCE MANUAL by Bram Moolenaar @@ -142,7 +142,8 @@ 1. Help commands *online-help* already opened, then the location list for that window is used. Otherwise, a new help window is opened and the location list for that window is set. The - location list for the current window is not changed. + location list for the current window is not changed + then. *:exu* *:exusage* :exu[sage] Show help on Ex commands. Added to simulate the Nvi diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -1,4 +1,4 @@ -*remote.txt* For Vim version 8.0. Last change: 2015 Mar 01 +*remote.txt* For Vim version 8.0. Last change: 2017 Mar 18 VIM REFERENCE MANUAL by Bram Moolenaar @@ -149,6 +149,7 @@ the description in |eval.txt| or use CTR the full explanation. synopsis explanation ~ + remote_startserver( name) run a server remote_expr( server, string, idvar) send expression remote_send( server, string, idvar) send key sequence serverlist() get a list of available servers diff --git a/runtime/doc/tags b/runtime/doc/tags --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1927,6 +1927,13 @@ 90.5 usr_90.txt /*90.5* :Print various.txt /*:Print* :Rexplore pi_netrw.txt /*:Rexplore* :RmVimball pi_vimball.txt /*:RmVimball* +:RustEmitAsm ft_rust.txt /*:RustEmitAsm* +:RustEmitIr ft_rust.txt /*:RustEmitIr* +:RustExpand ft_rust.txt /*:RustExpand* +:RustFmt ft_rust.txt /*:RustFmt* +:RustFmtRange ft_rust.txt /*:RustFmtRange* +:RustPlay ft_rust.txt /*:RustPlay* +:RustRun ft_rust.txt /*:RustRun* :Sexplore pi_netrw.txt /*:Sexplore* :TOhtml syntax.txt /*:TOhtml* :TarDiff pi_tar.txt /*:TarDiff* @@ -4505,6 +4512,8 @@ E938 eval.txt /*E938* E939 change.txt /*E939* E94 windows.txt /*E94* E940 eval.txt /*E940* +E941 eval.txt /*E941* +E942 eval.txt /*E942* E95 message.txt /*E95* E96 diff.txt /*E96* E97 diff.txt /*E97* @@ -5013,6 +5022,7 @@ assert_inrange() eval.txt /*assert_inran assert_match() eval.txt /*assert_match()* assert_notequal() eval.txt /*assert_notequal()* assert_notmatch() eval.txt /*assert_notmatch()* +assert_report() eval.txt /*assert_report()* assert_true() eval.txt /*assert_true()* at motion.txt /*at* atan() eval.txt /*atan()* @@ -6128,6 +6138,8 @@ ft-rexx-syntax syntax.txt /*ft-rexx-synt ft-rst-syntax syntax.txt /*ft-rst-syntax* ft-ruby-omni insert.txt /*ft-ruby-omni* ft-ruby-syntax syntax.txt /*ft-ruby-syntax* +ft-rust filetype.txt /*ft-rust* +ft-rust ft_rust.txt /*ft-rust* ft-scheme-syntax syntax.txt /*ft-scheme-syntax* ft-sdl-syntax syntax.txt /*ft-sdl-syntax* ft-sed-syntax syntax.txt /*ft-sed-syntax* @@ -6160,6 +6172,7 @@ ft-xpm-syntax syntax.txt /*ft-xpm-syntax ft-yaml-syntax syntax.txt /*ft-yaml-syntax* ft-zsh-syntax syntax.txt /*ft-zsh-syntax* ft_ada.txt ft_ada.txt /*ft_ada.txt* +ft_rust.txt ft_rust.txt /*ft_rust.txt* ft_sql.txt ft_sql.txt /*ft_sql.txt* ftdetect filetype.txt /*ftdetect* ftp pi_netrw.txt /*ftp* @@ -6237,6 +6250,7 @@ g:decada.Make_Command ft_ada.txt /*g:dec g:decada.Unit_Name() ft_ada.txt /*g:decada.Unit_Name()* g:filetype_csh syntax.txt /*g:filetype_csh* g:filetype_r syntax.txt /*g:filetype_r* +g:ftplugin_rust_source_path ft_rust.txt /*g:ftplugin_rust_source_path* g:gnat ft_ada.txt /*g:gnat* g:gnat.Error_Format ft_ada.txt /*g:gnat.Error_Format* g:gnat.Find() ft_ada.txt /*g:gnat.Find()* @@ -6366,6 +6380,20 @@ g:netrw_win95ftp pi_netrw.txt /*g:netrw_ g:netrw_winsize pi_netrw.txt /*g:netrw_winsize* g:netrw_wiw pi_netrw.txt /*g:netrw_wiw* g:netrw_xstrlen pi_netrw.txt /*g:netrw_xstrlen* +g:rust_bang_comment_leader ft_rust.txt /*g:rust_bang_comment_leader* +g:rust_conceal ft_rust.txt /*g:rust_conceal* +g:rust_conceal_mod_path ft_rust.txt /*g:rust_conceal_mod_path* +g:rust_conceal_pub ft_rust.txt /*g:rust_conceal_pub* +g:rust_fold ft_rust.txt /*g:rust_fold* +g:rust_playpen_url ft_rust.txt /*g:rust_playpen_url* +g:rust_recommended_style ft_rust.txt /*g:rust_recommended_style* +g:rust_shortener_url ft_rust.txt /*g:rust_shortener_url* +g:rustc_makeprg_no_percent ft_rust.txt /*g:rustc_makeprg_no_percent* +g:rustc_path ft_rust.txt /*g:rustc_path* +g:rustfmt_autosave ft_rust.txt /*g:rustfmt_autosave* +g:rustfmt_command ft_rust.txt /*g:rustfmt_command* +g:rustfmt_fail_silently ft_rust.txt /*g:rustfmt_fail_silently* +g:rustfmt_options ft_rust.txt /*g:rustfmt_options* g:syntax_on syntax.txt /*g:syntax_on* g:tar_browseoptions pi_tar.txt /*g:tar_browseoptions* g:tar_cmd pi_tar.txt /*g:tar_cmd* @@ -8019,6 +8047,7 @@ remote_foreground() eval.txt /*remote_fo remote_peek() eval.txt /*remote_peek()* remote_read() eval.txt /*remote_read()* remote_send() eval.txt /*remote_send()* +remote_startserver() eval.txt /*remote_startserver()* remove() eval.txt /*remove()* remove-filetype filetype.txt /*remove-filetype* remove-option-flags options.txt /*remove-option-flags* @@ -8074,6 +8103,13 @@ russian-issues russian.txt /*russian-iss russian-keymap russian.txt /*russian-keymap* russian-l18n russian.txt /*russian-l18n* russian.txt russian.txt /*russian.txt* +rust ft_rust.txt /*rust* +rust-commands ft_rust.txt /*rust-commands* +rust-intro ft_rust.txt /*rust-intro* +rust-mappings ft_rust.txt /*rust-mappings* +rust-settings ft_rust.txt /*rust-settings* +rust_ ft_rust.txt /*rust_* +rust_ ft_rust.txt /*rust_* rview starting.txt /*rview* rvim starting.txt /*rvim* rxvt syntax.txt /*rxvt* diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 8.0. Last change: 2017 Mar 16 +*todo.txt* For Vim version 8.0. Last change: 2017 Mar 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -35,12 +35,6 @@ entered there will not be repeated below *known-bugs* -------------------- Known bugs and current work ----------------------- -Using "g<" after ":for x in [1,2,3]|echom x|endfor" looks wrong. (Marcin -Szamotulski, 2017 Mar 12) -Also, using Ctrl-C clears the last part. - -Test 45 fails on MS-Windows only before resetting 'sw'. Why? - +channel: - Try out background make plugin: https://github.com/AndrewVos/vim-make-background @@ -116,13 +110,8 @@ manager. Problem with Motif? Memory leak in test97? The string is actually freed. Weird. -Include rust files. Got all permissions? - -dict_add_list and dict_add_dict may free item when failing. (2017 Mar 12, -Nikolai Pavlov, #1555) - -Patch to add buffer name argument to taglist(). -Ordering of tags in result of taglist call. (Duncan McDougall, #1194) +Patch to make "start" work better: Use ShellExecute in !start (Katsuya Hino, +#1570) Add a toolbar in the terminal. Can be global, above all windows, or specific for one window. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1,4 +1,4 @@ -*usr_41.txt* For Vim version 8.0. Last change: 2017 Mar 09 +*usr_41.txt* For Vim version 8.0. Last change: 2017 Mar 18 VIM USER MANUAL - by Bram Moolenaar @@ -889,6 +889,7 @@ GUI: *gui-functions* Vim server: *server-functions* serverlist() return the list of server names + remote_startserve() run a server remote_send() send command characters to a Vim server remote_expr() evaluate an expression in a Vim server server2client() send a reply to a client of a Vim server @@ -920,6 +921,7 @@ Testing: *test-functions* assert_true() assert that an expression is true assert_exception() assert that a command throws an exception assert_fails() assert that a function call fails + assert_report() report a test failure test_alloc_fail() make memory allocation fail test_autochdir() enable 'autochdir' during startup test_override() test with Vim internal overrides diff --git a/runtime/ftplugin/hamster.vim b/runtime/ftplugin/hamster.vim --- a/runtime/ftplugin/hamster.vim +++ b/runtime/ftplugin/hamster.vim @@ -2,7 +2,7 @@ " Language: Hamster Script " Version: 2.0.6.0 " Maintainer: David Fishburn -" Last Change: 2017 Mar 07 +" Last Change: 2017 Mar 18 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -14,7 +14,6 @@ let b:did_ftplugin = 1 let s:cpo_save = &cpo set cpo&vim -set cpo-=C let b:undo_ftplugin = "setl fo< com< tw< commentstring<" \ . "| unlet! b:match_ignorecase b:match_words b:match_skip" diff --git a/runtime/ftplugin/rust.vim b/runtime/ftplugin/rust.vim new file mode 100644 --- /dev/null +++ b/runtime/ftplugin/rust.vim @@ -0,0 +1,197 @@ +" Language: Rust +" Description: Vim ftplugin for Rust +" Maintainer: Chris Morgan +" Maintainer: Kevin Ballard +" Last Change: June 08, 2016 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:save_cpo = &cpo +set cpo&vim + +augroup rust.vim +autocmd! + +" Variables {{{1 + +" The rust source code at present seems to typically omit a leader on /*! +" comments, so we'll use that as our default, but make it easy to switch. +" This does not affect indentation at all (I tested it with and without +" leader), merely whether a leader is inserted by default or not. +if exists("g:rust_bang_comment_leader") && g:rust_bang_comment_leader != 0 + " Why is the `,s0:/*,mb:\ ,ex:*/` there, you ask? I don't understand why, + " but without it, */ gets indented one space even if there were no + " leaders. I'm fairly sure that's a Vim bug. + setlocal comments=s1:/*,mb:*,ex:*/,s0:/*,mb:\ ,ex:*/,:///,://!,:// +else + setlocal comments=s0:/*!,m:\ ,ex:*/,s1:/*,mb:*,ex:*/,:///,://!,:// +endif +setlocal commentstring=//%s +setlocal formatoptions-=t formatoptions+=croqnl +" j was only added in 7.3.541, so stop complaints about its nonexistence +silent! setlocal formatoptions+=j + +" smartindent will be overridden by indentexpr if filetype indent is on, but +" otherwise it's better than nothing. +setlocal smartindent nocindent + +if !exists("g:rust_recommended_style") || g:rust_recommended_style != 0 + setlocal tabstop=4 shiftwidth=4 softtabstop=4 expandtab + setlocal textwidth=99 +endif + +" This includeexpr isn't perfect, but it's a good start +setlocal includeexpr=substitute(v:fname,'::','/','g') + +setlocal suffixesadd=.rs + +if exists("g:ftplugin_rust_source_path") + let &l:path=g:ftplugin_rust_source_path . ',' . &l:path +endif + +if exists("g:loaded_delimitMate") + if exists("b:delimitMate_excluded_regions") + let b:rust_original_delimitMate_excluded_regions = b:delimitMate_excluded_regions + endif + + let s:delimitMate_extra_excluded_regions = ',rustLifetimeCandidate,rustGenericLifetimeCandidate' + + " For this buffer, when delimitMate issues the `User delimitMate_map` + " event in the autocommand system, add the above-defined extra excluded + " regions to delimitMate's state, if they have not already been added. + autocmd User + \ if expand('') ==# 'delimitMate_map' && match( + \ delimitMate#Get("excluded_regions"), + \ s:delimitMate_extra_excluded_regions) == -1 + \| let b:delimitMate_excluded_regions = + \ delimitMate#Get("excluded_regions") + \ . s:delimitMate_extra_excluded_regions + \|endif + + " For this buffer, when delimitMate issues the `User delimitMate_unmap` + " event in the autocommand system, delete the above-defined extra excluded + " regions from delimitMate's state (the deletion being idempotent and + " having no effect if the extra excluded regions are not present in the + " targeted part of delimitMate's state). + autocmd User + \ if expand('') ==# 'delimitMate_unmap' + \| let b:delimitMate_excluded_regions = substitute( + \ delimitMate#Get("excluded_regions"), + \ '\C\V' . s:delimitMate_extra_excluded_regions, + \ '', 'g') + \|endif +endif + +if has("folding") && exists('g:rust_fold') && g:rust_fold != 0 + let b:rust_set_foldmethod=1 + setlocal foldmethod=syntax + if g:rust_fold == 2 + setlocal foldlevel< + else + setlocal foldlevel=99 + endif +endif + +if has('conceal') && exists('g:rust_conceal') && g:rust_conceal != 0 + let b:rust_set_conceallevel=1 + setlocal conceallevel=2 +endif + +" Motion Commands {{{1 + +" Bind motion commands to support hanging indents +nnoremap [[ :call rust#Jump('n', 'Back') +nnoremap ]] :call rust#Jump('n', 'Forward') +xnoremap [[ :call rust#Jump('v', 'Back') +xnoremap ]] :call rust#Jump('v', 'Forward') +onoremap [[ :call rust#Jump('o', 'Back') +onoremap ]] :call rust#Jump('o', 'Forward') + +" Commands {{{1 + +" See |:RustRun| for docs +command! -nargs=* -complete=file -bang -buffer RustRun call rust#Run(0, ) + +" See |:RustExpand| for docs +command! -nargs=* -complete=customlist,rust#CompleteExpand -bang -buffer RustExpand call rust#Expand(0, ) + +" See |:RustEmitIr| for docs +command! -nargs=* -buffer RustEmitIr call rust#Emit("llvm-ir", ) + +" See |:RustEmitAsm| for docs +command! -nargs=* -buffer RustEmitAsm call rust#Emit("asm", ) + +" See |:RustPlay| for docs +command! -range=% RustPlay :call rust#Play(, , , ) + +" See |:RustFmt| for docs +command! -buffer RustFmt call rustfmt#Format() + +" See |:RustFmtRange| for docs +command! -range -buffer RustFmtRange call rustfmt#FormatRange(, ) + +" Mappings {{{1 + +" Bind ⌘R in MacVim to :RustRun +nnoremap :RustRun +" Bind ⌘⇧R in MacVim to :RustRun! pre-filled with the last args +nnoremap :RustRun! =join(b:rust_last_rustc_args)erust#AppendCmdLine(' -- ' . join(b:rust_last_args)) + +if !exists("b:rust_last_rustc_args") || !exists("b:rust_last_args") + let b:rust_last_rustc_args = [] + let b:rust_last_args = [] +endif + +" Cleanup {{{1 + +let b:undo_ftplugin = " + \ setlocal formatoptions< comments< commentstring< includeexpr< suffixesadd< + \|setlocal tabstop< shiftwidth< softtabstop< expandtab< textwidth< + \|if exists('b:rust_original_delimitMate_excluded_regions') + \|let b:delimitMate_excluded_regions = b:rust_original_delimitMate_excluded_regions + \|unlet b:rust_original_delimitMate_excluded_regions + \|else + \|unlet! b:delimitMate_excluded_regions + \|endif + \|if exists('b:rust_set_foldmethod') + \|setlocal foldmethod< foldlevel< + \|unlet b:rust_set_foldmethod + \|endif + \|if exists('b:rust_set_conceallevel') + \|setlocal conceallevel< + \|unlet b:rust_set_conceallevel + \|endif + \|unlet! b:rust_last_rustc_args b:rust_last_args + \|delcommand RustRun + \|delcommand RustExpand + \|delcommand RustEmitIr + \|delcommand RustEmitAsm + \|delcommand RustPlay + \|nunmap + \|nunmap + \|nunmap [[ + \|nunmap ]] + \|xunmap [[ + \|xunmap ]] + \|ounmap [[ + \|ounmap ]] + \|set matchpairs-=<:> + \" + +" }}}1 + +" Code formatting on save +if get(g:, "rustfmt_autosave", 0) + autocmd BufWritePre *.rs silent! call rustfmt#Format() +endif + +augroup END + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim: set noet sw=8 ts=8: diff --git a/runtime/indent/javascript.vim b/runtime/indent/javascript.vim --- a/runtime/indent/javascript.vim +++ b/runtime/indent/javascript.vim @@ -2,7 +2,7 @@ " Language: Javascript " Maintainer: Chris Paul ( https://github.com/bounceme ) " URL: https://github.com/pangloss/vim-javascript -" Last Change: December 31, 2016 +" Last Change: March 21, 2017 " Only load this indent file when no other was loaded. if exists('b:did_indent') @@ -14,6 +14,10 @@ let b:did_indent = 1 setlocal indentexpr=GetJavascriptIndent() setlocal autoindent nolisp nosmartindent setlocal indentkeys+=0],0) +" Testable with something like: +" vim -eNs "+filetype plugin indent on" "+syntax on" "+set ft=javascript" \ +" "+norm! gg=G" '+%print' '+:q!' testfile.js \ +" | diff -uBZ testfile.js - let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<' @@ -32,10 +36,14 @@ if exists('*shiftwidth') endfunction else function s:sw() - return &sw + return &l:shiftwidth == 0 ? &l:tabstop : &l:shiftwidth endfunction endif +" Performance for forwards search(): start search at pos rather than masking +" matches before pos. +let s:z = has('patch-7.4.984') ? 'z' : '' + " searchpair() wrapper if has('reltime') function s:GetPair(start,end,flags,skip,time,...) @@ -48,34 +56,41 @@ else endif " Regex of syntax group names that are or delimit string or are comments. -let s:syng_strcom = 'string\|comment\|regex\|special\|doc\|template' -let s:syng_str = 'string\|template' +let s:syng_strcom = 'string\|comment\|regex\|special\|doc\|template\%(braces\)\@!' +let s:syng_str = 'string\|template\|special' let s:syng_com = 'comment\|doc' " Expression used to check whether we should skip a match with searchpair(). let s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" +function s:parse_cino(f) abort + return float2nr(eval(substitute(substitute(join(split( + \ matchstr(&cino,'.*'.a:f.'\zs[^,]*'), 's',1), '*'.s:W) + \ , '^-\=\zs\*','',''), '^-\=\zs\.','0.',''))) +endfunction + function s:skip_func() - if !s:free || search('\m`\|\*\/','nW',s:looksyn) - let s:free = !eval(s:skip_expr) - let s:looksyn = s:free ? line('.') : s:looksyn - return !s:free + if getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$' + return eval(s:skip_expr) + elseif s:checkIn || search('\m`\|\${\|\*\/','nW'.s:z,s:looksyn) + let s:checkIn = eval(s:skip_expr) endif let s:looksyn = line('.') - return (search('\m\/','nbW',s:looksyn) || search('\m[''"]\|\\$','nW',s:looksyn)) && eval(s:skip_expr) + return s:checkIn endfunction function s:alternatePair(stop) let pos = getpos('.')[1:2] - while search('\m[][(){}]','bW',a:stop) - if !s:skip_func() - let idx = stridx('])}',s:looking_at()) - if idx + 1 - if !s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,a:stop) - break - endif - else - return + let pat = '[][(){};]' + while search('\m'.pat,'bW',a:stop) + if s:skip_func() | continue | endif + let idx = stridx('])};',s:looking_at()) + if idx is 3 | let pat = '[{}()]' | continue | endif + if idx + 1 + if s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,a:stop) <= 0 + break endif + else + return endif endwhile call call('cursor',pos) @@ -100,93 +115,91 @@ function s:token() return s:looking_at() =~ '\k' ? expand('') : s:looking_at() endfunction -function s:b_token() - if s:looking_at() =~ '\k' - call search('\m\<','cbW') - endif - return search('\m\S','bW') -endfunction - function s:previous_token() - let l:n = line('.') - while s:b_token() - if (s:looking_at() == '/' || line('.') != l:n && search('\m\/\/','nbW', - \ line('.'))) && s:syn_at(line('.'),col('.')) =~? s:syng_com - call search('\m\_[^/]\zs\/[/*]','bW') + let l:pos = getpos('.')[1:2] + if search('\m\k\{1,}\zs\k\|\S','bW') + if (getline('.')[col('.')-2:col('.')-1] == '*/' || line('.') != l:pos[0] && + \ getline('.') =~ '\%<'.col('.').'c\/\/') && s:syn_at(line('.'),col('.')) =~? s:syng_com + while search('\m\S\ze\_s*\/[/*]','bW') + if s:syn_at(line('.'),col('.')) !~? s:syng_com + return s:token() + endif + endwhile else return s:token() endif - endwhile + endif + call call('cursor',l:pos) return '' endfunction -function s:others(p) - return "((line2byte(line('.')) + col('.')) <= ".(line2byte(a:p[0]) + a:p[1]).") || ".s:skip_expr -endfunction - -function s:tern_skip(p) - return s:GetPair('{','}','nbW',s:others(a:p),200,a:p[0]) > 0 -endfunction - -function s:tern_col(p) - return s:GetPair('?',':\@ 0 -endfunction - -function s:label_col() - let pos = getpos('.')[1:2] - let [s:looksyn,s:free] = pos - call s:alternatePair(0) - if s:save_pos('s:IsBlock') - let poss = getpos('.')[1:2] - return call('cursor',pos) || !s:tern_col(poss) - elseif s:looking_at() == ':' - return !s:tern_col([0,0]) +function s:expr_col() + if getline('.')[col('.')-2] == ':' + return 1 endif + let bal = 0 + while search('\m[{}?:;]','bW') + if eval(s:skip_expr) | continue | endif + " switch (looking_at()) + exe { '}': "if s:GetPair('{','}','bW',s:skip_expr,200) <= 0 | return | endif", + \ ';': "return", + \ '{': "return getpos('.')[1:2] != b:js_cache[1:] && !s:IsBlock()", + \ ':': "let bal -= getline('.')[max([col('.')-2,0]):col('.')] !~ '::'", + \ '?': "let bal += 1 | if bal > 0 | return 1 | endif" }[s:looking_at()] + endwhile endfunction " configurable regexes that define continuation lines, not including (, {, or [. let s:opfirst = '^' . get(g:,'javascript_opfirst', - \ '\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)') + \ '\C\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)') let s:continuation = get(g:,'javascript_continuation', - \ '\%([<=,.~!?/*^%|&:]\|+\@\|\<\%(typeof\|delete\|void\|in\|instanceof\)\)') . '$' + \ '\C\%([-+<>=,.~!?/*^%|&:]\|\<\%(typeof\|new\|delete\|void\|in\|instanceof\|await\)\)') . '$' function s:continues(ln,con) - return !cursor(a:ln, match(' '.a:con,s:continuation)) && - \ eval((['s:syn_at(line("."),col(".")) !~? "regex"'] + - \ repeat(['s:previous_token() != "."'],5) + [1])[ - \ index(split('/ typeof in instanceof void delete'),s:token())]) + if !cursor(a:ln, match(' '.a:con,s:continuation)) + let teol = s:looking_at() + if teol == '/' + return s:syn_at(line('.'),col('.')) !~? 'regex' + elseif teol =~ '[-+>]' + return getline('.')[col('.')-2] != tr(teol,'>','=') + elseif teol =~ '\l' + return s:previous_token() != '.' + elseif teol == ':' + return s:expr_col() + endif + return 1 + endif endfunction " get the line of code stripped of comments and move cursor to the last " non-comment char. function s:Trim(ln) let pline = substitute(getline(a:ln),'\s*$','','') - let l:max = max([match(pline,'.*[^/]\zs\/[/*]'),0]) - while l:max && s:syn_at(a:ln, strlen(pline)) =~? s:syng_com - let pline = substitute(strpart(pline, 0, l:max),'\s*$','','') - let l:max = max([match(pline,'.*[^/]\zs\/[/*]'),0]) + let l:max = max([strridx(pline,'//'), strridx(pline,'/*')]) + while l:max != -1 && s:syn_at(a:ln, strlen(pline)) =~? s:syng_com + let pline = pline[: l:max] + let l:max = max([strridx(pline,'//'), strridx(pline,'/*')]) + let pline = substitute(pline[:-2],'\s*$','','') endwhile - return cursor(a:ln,strlen(pline)) ? pline : pline + return pline is '' || cursor(a:ln,strlen(pline)) ? pline : pline endfunction " Find line above 'lnum' that isn't empty or in a comment function s:PrevCodeLine(lnum) - let l:n = prevnonblank(a:lnum) + let [l:pos, l:n] = [getpos('.')[1:2], prevnonblank(a:lnum)] while l:n if getline(l:n) =~ '^\s*\/[/*]' - if (stridx(getline(l:n),'`') > 0 || getline(l:n-1)[-1:] == '\') && - \ s:syn_at(l:n,1) =~? s:syng_str - return l:n - endif let l:n = prevnonblank(l:n-1) - elseif s:syn_at(l:n,1) =~? s:syng_com - let l:n = s:save_pos('eval', - \ 'cursor('.l:n.',1) + search(''\m\/\*'',"bW")') + elseif stridx(getline(l:n), '*/') + 1 && s:syn_at(l:n,1) =~? s:syng_com + call cursor(l:n,1) + keepjumps norm! [* + let l:n = search('\m\S','nbW') else - return l:n + break endif endwhile + call call('cursor',l:pos) + return l:n endfunction " Check if line 'lnum' has a balanced amount of parentheses. @@ -201,7 +214,9 @@ function s:Balanced(lnum) return endif endif - let pos = match(l:line, '[][(){}]', pos + 1) + let pos = match(l:line, (l:open ? + \ '['.escape(tr(l:line[pos],'({[]})',')}][{(').l:line[pos],']').']' : + \ '[][(){}]'), pos + 1) endwhile return !l:open endfunction @@ -210,11 +225,11 @@ function s:OneScope(lnum) let pline = s:Trim(a:lnum) let kw = 'else do' if pline[-1:] == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0 - call s:previous_token() - let kw = 'for if let while with' - if index(split('await each'),s:token()) + 1 + if s:previous_token() =~# '^\%(await\|each\)$' call s:previous_token() let kw = 'for' + else + let kw = 'for if let while with' endif endif return pline[-2:] == '=>' || index(split(kw),s:token()) + 1 && @@ -246,18 +261,23 @@ function s:IsBlock() if s:looking_at() == '{' let l:n = line('.') let char = s:previous_token() - let syn = char =~ '[{>/]' ? s:syn_at(line('.'),col('.')-(char == '{')) : '' - if syn =~? 'xml\|jsx' + if match(s:stack,'\cxml\|jsx') + 1 && s:syn_at(line('.'),col('.')-1) =~? 'xml\|jsx' return char != '{' elseif char =~ '\k' - return index(split('return const let import export yield default delete var await void typeof throw case new in instanceof') - \ ,char) < (line('.') != l:n) || s:previous_token() == '.' + if char ==# 'type' + return s:previous_token() !~# '^\%(im\|ex\)port$' + endif + return index(split('return const let import export extends yield default delete var await void typeof throw case new of in instanceof') + \ ,char) < (line('.') != l:n) || s:save_pos('s:previous_token') == '.' elseif char == '>' - return getline('.')[col('.')-2] == '=' || syn =~? '^jsflow' + return getline('.')[col('.')-2] == '=' || s:syn_at(line('.'),col('.')) =~? '^jsflow' elseif char == ':' - return getline('.')[col('.')-2] != ':' && s:label_col() + return !s:save_pos('s:expr_col') + elseif char == '/' + return s:syn_at(line('.'),col('.')) =~? 'regex' endif - return syn =~? 'regex' || char !~ '[-=~!<*+,/?^%|&([]' + return char !~ '[=~!<*,?^%|&([]' && + \ (char !~ '[-+]' || l:n != line('.') && getline('.')[col('.')-2] == char) endif endfunction @@ -266,7 +286,9 @@ function GetJavascriptIndent() " Get the current line. call cursor(v:lnum,1) let l:line = getline('.') - let syns = s:syn_at(v:lnum, 1) + " use synstack as it validates syn state and works in an empty line + let s:stack = map(synstack(v:lnum,1),"synIDattr(v:val,'name')") + let syns = get(s:stack,-1,'') " start with strings,comments,etc. if syns =~? s:syng_com @@ -275,7 +297,7 @@ function GetJavascriptIndent() elseif l:line !~ '^\s*\/[/*]' return -1 endif - elseif syns =~? s:syng_str && l:line !~ '^[''"]' + elseif syns =~? s:syng_str if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1) let b:js_cache[0] = v:lnum endif @@ -295,69 +317,60 @@ function GetJavascriptIndent() endif " the containing paren, bracket, or curly. Many hacks for performance - let idx = strlen(l:line) ? stridx('])}',l:line[0]) : -1 + let idx = index([']',')','}'],l:line[0]) if b:js_cache[0] >= l:lnum && b:js_cache[0] < v:lnum && \ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum)) call call('cursor',b:js_cache[1:]) else - let [s:looksyn, s:free, top] = [v:lnum - 1, 1, (!indent(l:lnum) && + let [s:looksyn, s:checkIn, top] = [v:lnum - 1, 0, (!indent(l:lnum) && \ s:syn_at(l:lnum,1) !~? s:syng_str) * l:lnum] if idx + 1 - call s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,top) - elseif indent(v:lnum) && syns =~? 'block' + call s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:skip_func()',2000,top) + elseif getline(v:lnum) !~ '^\S' && syns =~? 'block' call s:GetPair('{','}','bW','s:skip_func()',2000,top) else call s:alternatePair(top) endif endif - if idx + 1 || l:line[:1] == '|}' - if idx == 2 && search('\m\S','bW',line('.')) && s:looking_at() == ')' - call s:GetPair('(',')','bW',s:skip_expr,200) - endif - return indent('.') - endif - let b:js_cache = [v:lnum] + (line('.') == v:lnum ? [0,0] : getpos('.')[1:2]) let num = b:js_cache[1] let [s:W, isOp, bL, switch_offset] = [s:sw(),0,0,0] if !num || s:IsBlock() + let ilnum = line('.') let pline = s:save_pos('s:Trim',l:lnum) if num && s:looking_at() == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0 - let num = line('.') - if s:previous_token() ==# 'switch' && s:previous_token() != '.' - if &cino !~ ':' || !has('float') + let num = ilnum == num ? line('.') : num + if idx < 0 && s:previous_token() ==# 'switch' && s:previous_token() != '.' + if &cino !~ ':' let switch_offset = s:W else - let cinc = matchlist(&cino,'.*:\(-\)\=\([0-9.]*\)\(s\)\=\C') - let switch_offset = float2nr(str2float(cinc[1].(strlen(cinc[2]) ? cinc[2] : strlen(cinc[3]))) - \ * (strlen(cinc[3]) ? s:W : 1)) + let switch_offset = max([-indent(num),s:parse_cino(':')]) endif if pline[-1:] != '.' && l:line =~# '^\%(default\|case\)\>' return indent(num) + switch_offset endif endif endif - if pline[-1:] !~ '[{;]' - if pline =~# ':\@ +" Last Change: 2017 Mar 21 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal cindent +setlocal cinoptions=L0,(0,Ws,J1,j1 +setlocal cinkeys=0{,0},!^F,o,O,0[,0] +" Don't think cinwords will actually do anything at all... never mind +setlocal cinwords=for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fn,extern + +" Some preliminary settings +setlocal nolisp " Make sure lisp indenting doesn't supersede us +setlocal autoindent " indentexpr isn't much help otherwise +" Also do indentkeys, otherwise # gets shoved to column 0 :-/ +setlocal indentkeys=0{,0},!^F,o,O,0[,0] + +setlocal indentexpr=GetRustIndent(v:lnum) + +" Only define the function once. +if exists("*GetRustIndent") + finish +endif + +let s:save_cpo = &cpo +set cpo&vim + +" Come here when loading the script the first time. + +function! s:get_line_trimmed(lnum) + " Get the line and remove a trailing comment. + " Use syntax highlighting attributes when possible. + " NOTE: this is not accurate; /* */ or a line continuation could trick it + let line = getline(a:lnum) + let line_len = strlen(line) + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(a:lnum, line_len, 1), "name") =~ 'Comment\|Todo' + let min = 1 + let max = line_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(a:lnum, col, 1), "name") =~ 'Comment\|Todo' + let max = col + else + let min = col + 1 + endif + endwhile + let line = strpart(line, 0, min - 1) + endif + return substitute(line, "\s*$", "", "") + else + " Sorry, this is not complete, nor fully correct (e.g. string "//"). + " Such is life. + return substitute(line, "\s*//.*$", "", "") + endif +endfunction + +function! s:is_string_comment(lnum, col) + if has('syntax_items') + for id in synstack(a:lnum, a:col) + let synname = synIDattr(id, "name") + if synname == "rustString" || synname =~ "^rustComment" + return 1 + endif + endfor + else + " without syntax, let's not even try + return 0 + endif +endfunction + +function GetRustIndent(lnum) + + " Starting assumption: cindent (called at the end) will do it right + " normally. We just want to fix up a few cases. + + let line = getline(a:lnum) + + if has('syntax_items') + let synname = synIDattr(synID(a:lnum, 1, 1), "name") + if synname == "rustString" + " If the start of the line is in a string, don't change the indent + return -1 + elseif synname =~ '\(Comment\|Todo\)' + \ && line !~ '^\s*/\*' " not /* opening line + if synname =~ "CommentML" " multi-line + if line !~ '^\s*\*' && getline(a:lnum - 1) =~ '^\s*/\*' + " This is (hopefully) the line after a /*, and it has no + " leader, so the correct indentation is that of the + " previous line. + return GetRustIndent(a:lnum - 1) + endif + endif + " If it's in a comment, let cindent take care of it now. This is + " for cases like "/*" where the next line should start " * ", not + " "* " as the code below would otherwise cause for module scope + " Fun fact: " /*\n*\n*/" takes two calls to get right! + return cindent(a:lnum) + endif + endif + + " cindent gets second and subsequent match patterns/struct members wrong, + " as it treats the comma as indicating an unfinished statement:: + " + " match a { + " b => c, + " d => e, + " f => g, + " }; + + " Search backwards for the previous non-empty line. + let prevlinenum = prevnonblank(a:lnum - 1) + let prevline = s:get_line_trimmed(prevlinenum) + while prevlinenum > 1 && prevline !~ '[^[:blank:]]' + let prevlinenum = prevnonblank(prevlinenum - 1) + let prevline = s:get_line_trimmed(prevlinenum) + endwhile + + " Handle where clauses nicely: subsequent values should line up nicely. + if prevline[len(prevline) - 1] == "," + \ && prevline =~# '^\s*where\s' + return indent(prevlinenum) + 6 + endif + + if prevline[len(prevline) - 1] == "," + \ && s:get_line_trimmed(a:lnum) !~ '^\s*[\[\]{}]' + \ && prevline !~ '^\s*fn\s' + \ && prevline !~ '([^()]\+,$' + \ && s:get_line_trimmed(a:lnum) !~ '^\s*\S\+\s*=>' + " Oh ho! The previous line ended in a comma! I bet cindent will try to + " take this too far... For now, let's normally use the previous line's + " indent. + + " One case where this doesn't work out is where *this* line contains + " square or curly brackets; then we normally *do* want to be indenting + " further. + " + " Another case where we don't want to is one like a function + " definition with arguments spread over multiple lines: + " + " fn foo(baz: Baz, + " baz: Baz) // <-- cindent gets this right by itself + " + " Another case is similar to the previous, except calling a function + " instead of defining it, or any conditional expression that leaves + " an open paren: + " + " foo(baz, + " baz); + " + " if baz && (foo || + " bar) { + " + " Another case is when the current line is a new match arm. + " + " There are probably other cases where we don't want to do this as + " well. Add them as needed. + return indent(prevlinenum) + endif + + if !has("patch-7.4.355") + " cindent before 7.4.355 doesn't do the module scope well at all; e.g.:: + " + " static FOO : &'static [bool] = [ + " true, + " false, + " false, + " true, + " ]; + " + " uh oh, next statement is indented further! + + " Note that this does *not* apply the line continuation pattern properly; + " that's too hard to do correctly for my liking at present, so I'll just + " start with these two main cases (square brackets and not returning to + " column zero) + + call cursor(a:lnum, 1) + if searchpair('{\|(', '', '}\|)', 'nbW', + \ 's:is_string_comment(line("."), col("."))') == 0 + if searchpair('\[', '', '\]', 'nbW', + \ 's:is_string_comment(line("."), col("."))') == 0 + " Global scope, should be zero + return 0 + else + " At the module scope, inside square brackets only + "if getline(a:lnum)[0] == ']' || search('\[', '', '\]', 'nW') == a:lnum + if line =~ "^\\s*]" + " It's the closing line, dedent it + return 0 + else + return &shiftwidth + endif + endif + endif + endif + + " Fall back on cindent, which does it mostly right + return cindent(a:lnum) +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/syntax/rust.vim b/runtime/syntax/rust.vim new file mode 100644 --- /dev/null +++ b/runtime/syntax/rust.vim @@ -0,0 +1,295 @@ +" Vim syntax file +" Language: Rust +" Maintainer: Patrick Walton +" Maintainer: Ben Blum +" Maintainer: Chris Morgan +" Last Change: Feb 24, 2016 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +" Syntax definitions {{{1 +" Basic keywords {{{2 +syn keyword rustConditional match if else +syn keyword rustRepeat for loop while +syn keyword rustTypedef type nextgroup=rustIdentifier skipwhite skipempty +syn keyword rustStructure struct enum nextgroup=rustIdentifier skipwhite skipempty +syn keyword rustUnion union nextgroup=rustIdentifier skipwhite skipempty contained +syn match rustUnionContextual /\/ + +syn keyword rustInvalidBareKeyword crate + +syn keyword rustPubScopeCrate crate contained +syn match rustPubScopeDelim /[()]/ contained +syn match rustPubScope /([^()]*)/ contained contains=rustPubScopeDelim,rustPubScopeCrate,rustSuper,rustModPath,rustModPathSep,rustSelf transparent + +syn keyword rustExternCrate crate contained nextgroup=rustIdentifier,rustExternCrateString skipwhite skipempty +" This is to get the `bar` part of `extern crate "foo" as bar;` highlighting. +syn match rustExternCrateString /".*"\_s*as/ contained nextgroup=rustIdentifier skipwhite transparent skipempty contains=rustString,rustOperator +syn keyword rustObsoleteExternMod mod contained nextgroup=rustIdentifier skipwhite skipempty + +syn match rustIdentifier contains=rustIdentifierPrime "\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" display contained +syn match rustFuncName "\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" display contained + +syn region rustBoxPlacement matchgroup=rustBoxPlacementParens start="(" end=")" contains=TOP contained +" Ideally we'd have syntax rules set up to match arbitrary expressions. Since +" we don't, we'll just define temporary contained rules to handle balancing +" delimiters. +syn region rustBoxPlacementBalance start="(" end=")" containedin=rustBoxPlacement transparent +syn region rustBoxPlacementBalance start="\[" end="\]" containedin=rustBoxPlacement transparent +" {} are handled by rustFoldBraces + +syn region rustMacroRepeat matchgroup=rustMacroRepeatDelimiters start="$(" end=")" contains=TOP nextgroup=rustMacroRepeatCount +syn match rustMacroRepeatCount ".\?[*+]" contained +syn match rustMacroVariable "$\w\+" + +" Reserved (but not yet used) keywords {{{2 +syn keyword rustReservedKeyword alignof become do offsetof priv pure sizeof typeof unsized yield abstract virtual final override macro + +" Built-in types {{{2 +syn keyword rustType isize usize char bool u8 u16 u32 u64 u128 f32 +syn keyword rustType f64 i8 i16 i32 i64 i128 str Self + +" Things from the libstd v1 prelude (src/libstd/prelude/v1.rs) {{{2 +" This section is just straight transformation of the contents of the prelude, +" to make it easy to update. + +" Reexported core operators {{{3 +syn keyword rustTrait Copy Send Sized Sync +syn keyword rustTrait Drop Fn FnMut FnOnce + +" Reexported functions {{{3 +" There’s no point in highlighting these; when one writes drop( or drop::< it +" gets the same highlighting anyway, and if someone writes `let drop = …;` we +" don’t really want *that* drop to be highlighted. +"syn keyword rustFunction drop + +" Reexported types and traits {{{3 +syn keyword rustTrait Box +syn keyword rustTrait ToOwned +syn keyword rustTrait Clone +syn keyword rustTrait PartialEq PartialOrd Eq Ord +syn keyword rustTrait AsRef AsMut Into From +syn keyword rustTrait Default +syn keyword rustTrait Iterator Extend IntoIterator +syn keyword rustTrait DoubleEndedIterator ExactSizeIterator +syn keyword rustEnum Option +syn keyword rustEnumVariant Some None +syn keyword rustEnum Result +syn keyword rustEnumVariant Ok Err +syn keyword rustTrait SliceConcatExt +syn keyword rustTrait String ToString +syn keyword rustTrait Vec + +" Other syntax {{{2 +syn keyword rustSelf self +syn keyword rustBoolean true false + +" If foo::bar changes to foo.bar, change this ("::" to "\."). +" If foo::bar changes to Foo::bar, change this (first "\w" to "\u"). +syn match rustModPath "\w\(\w\)*::[^<]"he=e-3,me=e-3 +syn match rustModPathSep "::" + +syn match rustFuncCall "\w\(\w\)*("he=e-1,me=e-1 +syn match rustFuncCall "\w\(\w\)*::<"he=e-3,me=e-3 " foo::(); + +" This is merely a convention; note also the use of [A-Z], restricting it to +" latin identifiers rather than the full Unicode uppercase. I have not used +" [:upper:] as it depends upon 'noignorecase' +"syn match rustCapsIdent display "[A-Z]\w\(\w\)*" + +syn match rustOperator display "\%(+\|-\|/\|*\|=\|\^\|&\||\|!\|>\|<\|%\)=\?" +" This one isn't *quite* right, as we could have binary-& with a reference +syn match rustSigil display /&\s\+[&~@*][^)= \t\r\n]/he=e-1,me=e-1 +syn match rustSigil display /[&~@*][^)= \t\r\n]/he=e-1,me=e-1 +" This isn't actually correct; a closure with no arguments can be `|| { }`. +" Last, because the & in && isn't a sigil +syn match rustOperator display "&&\|||" +" This is rustArrowCharacter rather than rustArrow for the sake of matchparen, +" so it skips the ->; see http://stackoverflow.com/a/30309949 for details. +syn match rustArrowCharacter display "->" +syn match rustQuestionMark display "?\([a-zA-Z]\+\)\@!" + +syn match rustMacro '\w\(\w\)*!' contains=rustAssert,rustPanic +syn match rustMacro '#\w\(\w\)*' contains=rustAssert,rustPanic + +syn match rustEscapeError display contained /\\./ +syn match rustEscape display contained /\\\([nrt0\\'"]\|x\x\{2}\)/ +syn match rustEscapeUnicode display contained /\\u{\x\{1,6}}/ +syn match rustStringContinuation display contained /\\\n\s*/ +syn region rustString start=+b"+ skip=+\\\\\|\\"+ end=+"+ contains=rustEscape,rustEscapeError,rustStringContinuation +syn region rustString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=rustEscape,rustEscapeUnicode,rustEscapeError,rustStringContinuation,@Spell +syn region rustString start='b\?r\z(#*\)"' end='"\z1' contains=@Spell + +syn region rustAttribute start="#!\?\[" end="\]" contains=rustString,rustDerive,rustCommentLine,rustCommentBlock,rustCommentLineDocError,rustCommentBlockDocError +syn region rustDerive start="derive(" end=")" contained contains=rustDeriveTrait +" This list comes from src/libsyntax/ext/deriving/mod.rs +" Some are deprecated (Encodable, Decodable) or to be removed after a new snapshot (Show). +syn keyword rustDeriveTrait contained Clone Hash RustcEncodable RustcDecodable Encodable Decodable PartialEq Eq PartialOrd Ord Rand Show Debug Default FromPrimitive Send Sync Copy + +" Number literals +syn match rustDecNumber display "\<[0-9][0-9_]*\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" +syn match rustHexNumber display "\<0x[a-fA-F0-9_]\+\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" +syn match rustOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" +syn match rustBinNumber display "\<0b[01_]\+\%([iu]\%(size\|8\|16\|32\|64\|128\)\)\=" + +" Special case for numbers of the form "1." which are float literals, unless followed by +" an identifier, which makes them integer literals with a method call or field access, +" or by another ".", which makes them integer literals followed by the ".." token. +" (This must go first so the others take precedence.) +syn match rustFloat display "\<[0-9][0-9_]*\.\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\|\.\)\@!" +" To mark a number as a normal float, it must have at least one of the three things integral values don't have: +" a decimal point and more numbers; an exponent; and a type suffix. +syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\%([eE][+-]\=[0-9_]\+\)\=\(f32\|f64\)\=" +syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\=\%([eE][+-]\=[0-9_]\+\)\(f32\|f64\)\=" +syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\=\%([eE][+-]\=[0-9_]\+\)\=\(f32\|f64\)" + +" For the benefit of delimitMate +syn region rustLifetimeCandidate display start=/&'\%(\([^'\\]\|\\\(['nrt0\\\"]\|x\x\{2}\|u{\x\{1,6}}\)\)'\)\@!/ end=/[[:cntrl:][:space:][:punct:]]\@=\|$/ contains=rustSigil,rustLifetime +syn region rustGenericRegion display start=/<\%('\|[^[cntrl:][:space:][:punct:]]\)\@=')\S\@=/ end=/>/ contains=rustGenericLifetimeCandidate +syn region rustGenericLifetimeCandidate display start=/\%(<\|,\s*\)\@<='/ end=/[[:cntrl:][:space:][:punct:]]\@=\|$/ contains=rustSigil,rustLifetime + +"rustLifetime must appear before rustCharacter, or chars will get the lifetime highlighting +syn match rustLifetime display "\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" +syn match rustLabel display "\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*:" +syn match rustCharacterInvalid display contained /b\?'\zs[\n\r\t']\ze'/ +" The groups negated here add up to 0-255 but nothing else (they do not seem to go beyond ASCII). +syn match rustCharacterInvalidUnicode display contained /b'\zs[^[:cntrl:][:graph:][:alnum:][:space:]]\ze'/ +syn match rustCharacter /b'\([^\\]\|\\\(.\|x\x\{2}\)\)'/ contains=rustEscape,rustEscapeError,rustCharacterInvalid,rustCharacterInvalidUnicode +syn match rustCharacter /'\([^\\]\|\\\(.\|x\x\{2}\|u{\x\{1,6}}\)\)'/ contains=rustEscape,rustEscapeUnicode,rustEscapeError,rustCharacterInvalid + +syn match rustShebang /\%^#![^[].*/ +syn region rustCommentLine start="//" end="$" contains=rustTodo,@Spell +syn region rustCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=rustTodo,@Spell +syn region rustCommentLineDocError start="//\%(//\@!\|!\)" end="$" contains=rustTodo,@Spell contained +syn region rustCommentBlock matchgroup=rustCommentBlock start="/\*\%(!\|\*[*/]\@!\)\@!" end="\*/" contains=rustTodo,rustCommentBlockNest,@Spell +syn region rustCommentBlockDoc matchgroup=rustCommentBlockDoc start="/\*\%(!\|\*[*/]\@!\)" end="\*/" contains=rustTodo,rustCommentBlockDocNest,@Spell +syn region rustCommentBlockDocError matchgroup=rustCommentBlockDocError start="/\*\%(!\|\*[*/]\@!\)" end="\*/" contains=rustTodo,rustCommentBlockDocNestError,@Spell contained +syn region rustCommentBlockNest matchgroup=rustCommentBlock start="/\*" end="\*/" contains=rustTodo,rustCommentBlockNest,@Spell contained transparent +syn region rustCommentBlockDocNest matchgroup=rustCommentBlockDoc start="/\*" end="\*/" contains=rustTodo,rustCommentBlockDocNest,@Spell contained transparent +syn region rustCommentBlockDocNestError matchgroup=rustCommentBlockDocError start="/\*" end="\*/" contains=rustTodo,rustCommentBlockDocNestError,@Spell contained transparent +" FIXME: this is a really ugly and not fully correct implementation. Most +" importantly, a case like ``/* */*`` should have the final ``*`` not being in +" a comment, but in practice at present it leaves comments open two levels +" deep. But as long as you stay away from that particular case, I *believe* +" the highlighting is correct. Due to the way Vim's syntax engine works +" (greedy for start matches, unlike Rust's tokeniser which is searching for +" the earliest-starting match, start or end), I believe this cannot be solved. +" Oh you who would fix it, don't bother with things like duplicating the Block +" rules and putting ``\*\@