diff --git a/src/etc/vim/autoload/rust.vim b/src/etc/vim/autoload/rust.vim new file mode 100644 index 00000000000..688c1f690eb --- /dev/null +++ b/src/etc/vim/autoload/rust.vim @@ -0,0 +1,192 @@ +" Author: Kevin Ballard +" Description: Helper functions for Rust commands/mappings +" Last Modified: May 27, 2014 + +" 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) + if a:bang + let idx = index(a:args, '--') + if idx != -1 + let rustc_args = idx == 0 ? [] : a:args[:idx-1] + let args = a:args[idx+1:] + else + let rustc_args = a:args + let args = [] + endif + else + let rustc_args = [] + let args = a:args + endif + + let b:rust_last_rustc_args = rustc_args + let b:rust_last_args = args + + call s:WithPath(function("s:Run"), rustc_args, args) +endfunction + +function! s:Run(path, rustc_args, args) + try + let exepath = tempname() + if has('win32') + let exepath .= '.exe' + endif + + let rustc_args = [a:path, '-o', exepath] + a:rustc_args + + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + let output = system(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 + finally + if exists("exepath") + silent! call delete(exepath) + endif + endtry +endfunction + +" Expand {{{1 + +function! rust#Expand(bang, args) + if a:bang && !empty(a:args) + let pretty = a:args[0] + let args = a:args[1:] + else + let pretty = "expanded" + let args = a:args + endif + call s:WithPath(function("s:Expand"), pretty, args) +endfunction + +function! s:Expand(path, pretty, args) + try + let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc" + + let args = [a:path, '--pretty', a:pretty] + a:args + let output = system(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 + endif + endtry +endfunction + +function! rust#CompleteExpand(lead, line, pos) + if a:line[: a:pos-1] =~ '^Expand!\s*\S*$' + " first argument and it has a ! + let list = ["normal", "expanded", "typed", "expanded,identified", "flowgraph="] + 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 + +" Utility functions {{{1 + +function! s:WithPath(func, ...) + try + let save_write = &write + set write + let path = expand('%') + let pathisempty = empty(path) + if pathisempty || !save_write + " use a temporary file named 'unnamed.rs' inside a temporary + " directory. This produces better error messages + let tmpdir = tempname() + call mkdir(tmpdir) + + let save_cwd = getcwd() + silent exe 'lcd' tmpdir + + let path = 'unnamed.rs' + + let save_mod = &mod + set nomod + + silent exe 'keepalt write! ' . path + if pathisempty + silent keepalt 0file + endif + else + update + endif + + call call(a:func, [path] + a:000) + finally + if exists("save_mod") | let &mod = save_mod | endif + if exists("save_write") | let &write = save_write | endif + if exists("save_cwd") | silent exe 'lcd' save_cwd | endif + if exists("tmpdir") | silent call s:RmDir(tmpdir) | endif + endtry +endfunction + +function! rust#AppendCmdLine(text) + call setcmdpos(getcmdpos()) + let cmd = getcmdline() . a:text + return cmd +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 + silent exe "!rm -rf " . shellescape(a:path) +endfunction + +" }}}1 + +" vim: set noet sw=4 ts=4: diff --git a/src/etc/vim/ftplugin/rust.vim b/src/etc/vim/ftplugin/rust.vim index b70cda9b998..fee59b58687 100644 --- a/src/etc/vim/ftplugin/rust.vim +++ b/src/etc/vim/ftplugin/rust.vim @@ -1,13 +1,19 @@ -" Vim syntax file " Language: Rust +" Description: Vim syntax file for Rust " Maintainer: Chris Morgan -" Last Change: 2014 Feb 27 +" Maintainer: Kevin Ballard +" Last Change: May 27, 2014 if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1 +let s:save_cpo = &cpo +set cpo&vim + +" 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 @@ -42,22 +48,74 @@ if exists("g:loaded_delimitMate") let b:delimitMate_excluded_regions = delimitMate#Get("excluded_regions") . ',rustLifetimeCandidate,rustGenericLifetimeCandidate' 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') +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 + +" :Run will compile and run the current file. If it has unsaved changes, they +" will be saved first. If it has no path, it will be written to a temporary +" file first. The generated binary is always placed in a temporary directory, +" but run from the current directory. +" +" The arguments passed to :Run will be passed to the generated binary. +" +" If ! is specified, the arguments are given to rustc as well. A -- argument +" separates rustc args from the args passed to the binary. +" +" If g:rustc_path is defined, it is used as the path to rustc. Otherwise it is +" assumed that rustc is in $PATH. +command! -nargs=* -complete=file -bang -bar -buffer Run call rust#Run(0, []) + +" :Expand will expand the current file using --pretty. +" +" Any arguments given to :Expand will be passed to rustc. This is largely so +" you can pass various --cfg configurations. +" +" If ! is specified, the first argument will be interpreted as the --pretty +" type. Otherwise it will default to 'expanded'. +" +" If the current file has unsaved changes, it will be saved first. If it's an +" unnamed buffer, it will be written to a temporary file. +" +" If g:rustc_path is defined, it is used as the path to rustc. Otherwise it is +" assumed that rustc is in $PATH. +command! -nargs=* -complete=customlist,rust#CompleteExpand -bang -bar -buffer Expand call rust#Expand(0, []) + +" Mappings {{{1 + +" Bind ⌘R in MacVim to :Run +nnoremap :Run +" Bind ⌘⇧R in MacVim to :Run! pre-filled with the last args +nnoremap :Run! =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< \|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 - \|elseif exists('b:delimitMate_excluded_regions') - \|unlet b:delimitMate_excluded_regions + \|else + \|unlet! b:delimitMate_excluded_regions \|endif + \|unlet! b:rust_last_rustc_args b:rust_last_args + \|delcommand Run + \|delcommand Expand + \|nunmap + \|nunmap \|nunmap [[ \|nunmap ]] \|xunmap [[ @@ -66,31 +124,9 @@ let b:undo_ftplugin = " \|ounmap ]] \" -if exists('*Rust_Jump') | finish | endif +" }}}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 Rust_Jump_" . a:function . "()" - let cnt = cnt - 1 - endwhile - let &foldenable = foldenable -endfunction +let &cpo = s:save_cpo +unlet s:save_cpo -function! Rust_Jump_Back() - call search('{', 'b') - keepjumps normal! w99[{ -endfunction - -function! Rust_Jump_Forward() - normal! j0 - call search('{', 'b') - keepjumps normal! w99[{% - call search('{') -endfunction +" vim: set noet sw=4 ts=4: