Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vim: Add :RustRun and associated commands #14480

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions src/etc/vim/autoload/rust.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
" 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 <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)
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] =~ '^RustExpand!\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

" Emit {{{1

function! rust#Emit(type, args)
call s:WithPath(function("s:Emit"), a:type, a:args)
endfunction

function! s:Emit(path, type, args)
try
let rustc = exists("g:rustc_path") ? g:rustc_path : "rustc"

let args = [a:path, '--emit', a:type, '-o', '-'] + 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
if a:type == "ir"
setl filetype=llvm
elseif a:type == "asm"
setl filetype=asm
endif
setl buftype=nofile
setl bufhidden=hide
setl noswapfile
endif
endtry
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:
150 changes: 150 additions & 0 deletions src/etc/vim/doc/rust.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
*rust.txt* Filetype plugin for Rust

==============================================================================
CONTENTS *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_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'
<

==============================================================================
COMMANDS *rust-commands*

:RustRun [args] *:RustRun*
:RustRun! [rustc-args] [--] [args]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not always allow a --? I know that all that is said about the bang at the end of an Ex command is that it “makes the command behave in a different way” (:_! help), but this case doesn’t feel right to me; the bang’s existence or non-existence is merely trivial sugar.

I’d just as soon see it unified:

:RustRun [[rustc-args] --] [args]

Which would allow

:RustRun args
:RustRun -- args
:RustRun rustc-args --
:RustRun rustc-args -- args

These are achievable in the current behaviour thus:

:RustRun args
:RustRun args
:RustRun! rustc-args
:RustRun! rustc-args -- args

I think the precedent in bang behaviour is more along the lines of whether it should :update or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that it's more common to want to pass flags to rustc than to the generated binary. I'd rather type :RustRun! -O than :RustRun -O --. To that end, if I were to get rid of the bang, I'd have :RustRun args be equivalent to the current :RustRun! args and require :RustRun -- args to pass args to the binary. But I also thought that was a bit nonintuitive. For someone who knows the :RustRun command is there but hasn't read the docs in detail will likely assume that argument are passed to the binary.

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.

==============================================================================
MAPPINGS *rust-mappings*

This plugin defines mappings for |[[| and |]]| to support hanging indents.

It also has a few other mappings:

*rust_<D-r>*
<D-r> Executes |:RustRun| with no arguments.
Note: This binding is only available in MacVim.

*rust_<D-R>*
<D-R> 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:
Loading