From 2389eb7bec3f7dee9c26e1d52f3944f32cd13b44 Mon Sep 17 00:00:00 2001 From: Stephen Sugden Date: Sat, 3 Oct 2015 17:52:02 +0200 Subject: [PATCH 1/9] Added support for rustfmt before save I really enjoyed this feature from [vim-go](https://github.com/fatih/vim-go) so I copied it for rust & rustfmt. --- README.md | 7 +++- autoload/rustfmt.vim | 80 ++++++++++++++++++++++++++++++++++++++++++++ plugin/rustfmt.vim | 6 ++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 autoload/rustfmt.vim create mode 100644 plugin/rustfmt.vim diff --git a/README.md b/README.md index 296a3fa4..f158b62a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Description -This is a vim plugin that provides [Rust][r] file detection and syntax highlighting. +This is a vim plugin that provides [Rust][r] file detection, syntax highlighting, and (optional) autoformatting. ## Installation @@ -31,3 +31,8 @@ git clone --depth=1 https://github.com/rust-lang/rust.vim.git ~/.vim/bundle/rust 1. Add `NeoBundle 'rust-lang/rust.vim'` to `~/.vimrc` 2. Re-open vim or execute `:source ~/.vimrc` + +## Enabling autoformat + +This plugin can optionally format your code using [rustfmt][rfmt] every time a +buffer is written. Simple put `let g:rust_fmt_autosave = 1` in your `.vimrc`. diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim new file mode 100644 index 00000000..ef2780ac --- /dev/null +++ b/autoload/rustfmt.vim @@ -0,0 +1,80 @@ +" WTF License +" Author: stephen@stephensugden.com +" +" Directly based on the go#fmt#Format plugin from fatih/vim-go + +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! rustfmt#Format() + let l:curw = winsaveview() + let l:tmpname = tempname() + call writefile(getline(1, '$'), l:tmpname) + + let command = g:rustfmt_command . " --write-mode=replace " + + let out = system(command . g:rustfmt_options . " " . l:tmpname) + + "if there is no error on the temp file replace the output with the current + "file (if this fails, we can always check the outputs first line with: + "splitted =~ 'package \w\+') + if v:shell_error == 0 + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry + + " Replace current file with temp file, then reload buffer + call rename(l:tmpname, expand('%')) + silent edit! + let &syntax = &syntax + + " only clear quickfix if it was previously set, this prevents closing + " other quickfixes + if s:got_fmt_error + let s:got_fmt_error = 0 + call setqflist([]) + cwindow + endif + elseif g:rustfmt_fail_silently == 0 + let splitted = split(out, '\n') + "otherwise get the errors and put them to quickfix window + let errors = [] + for line in splitted + " 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 setqflist(errors, 'r') + echohl Error | echomsg "rustfmt returned error" | echohl None + endif + let s:got_fmt_error = 1 + cwindow + " We didn't use the temp file, so clean up + call delete(l:tmpname) + endif + + call winrestview(l:curw) +endfunction diff --git a/plugin/rustfmt.vim b/plugin/rustfmt.vim new file mode 100644 index 00000000..be2353c7 --- /dev/null +++ b/plugin/rustfmt.vim @@ -0,0 +1,6 @@ +augroup rustfmt + " code formatting on save + if get(g:, "rustfmt_autosave", 1) + autocmd BufWritePre *.rs call rustfmt#Format() + endif +augroup END From b68da6e3501058f63802075e95ff6ed9ef49ce40 Mon Sep 17 00:00:00 2001 From: Stephen Sugden Date: Sat, 3 Oct 2015 19:09:25 +0200 Subject: [PATCH 2/9] Fix rustfmt for multi-file crates --- autoload/rustfmt.vim | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim index ef2780ac..0a6f8a4f 100644 --- a/autoload/rustfmt.vim +++ b/autoload/rustfmt.vim @@ -23,16 +23,13 @@ let s:got_fmt_error = 0 function! rustfmt#Format() let l:curw = winsaveview() - let l:tmpname = tempname() + let l:tmpname = expand("%:p:h") . "/." . expand("%:p:t") . ".rustfmt" call writefile(getline(1, '$'), l:tmpname) - let command = g:rustfmt_command . " --write-mode=replace " + let command = g:rustfmt_command . " --write-mode=overwrite " - let out = system(command . g:rustfmt_options . " " . l:tmpname) + let out = systemlist(command . g:rustfmt_options . " " . shellescape(l:tmpname)) - "if there is no error on the temp file replace the output with the current - "file (if this fails, we can always check the outputs first line with: - "splitted =~ 'package \w\+') if v:shell_error == 0 " remove undo point caused via BufWritePre try | silent undojoin | catch | endtry @@ -50,17 +47,18 @@ function! rustfmt#Format() cwindow endif elseif g:rustfmt_fail_silently == 0 - let splitted = split(out, '\n') "otherwise get the errors and put them to quickfix window let errors = [] - for line in splitted + 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: \(.*\)') + 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]}) + else + echo line endif endfor if empty(errors) From 65549fb45036008564b7179d9147a53ac52f5846 Mon Sep 17 00:00:00 2001 From: Stephen Sugden Date: Sun, 4 Oct 2015 00:27:18 +0200 Subject: [PATCH 3/9] Remove silly license from rustfmt.vim --- autoload/rustfmt.vim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim index 0a6f8a4f..2db813f3 100644 --- a/autoload/rustfmt.vim +++ b/autoload/rustfmt.vim @@ -1,7 +1,6 @@ -" WTF License -" Author: stephen@stephensugden.com +" Author: Stephen Sugden " -" Directly based on the go#fmt#Format plugin from fatih/vim-go +" Adapted from https://github.com/fatih/vim-go if !exists("g:rustfmt_autosave") let g:rustfmt_autosave = 0 From 0981f9e4824d2090ff105ea6ef493d18ff37332f Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Wed, 11 Nov 2015 22:06:13 -0800 Subject: [PATCH 4/9] Add new RustFmt command and fix bugs --- autoload/rustfmt.vim | 24 ++++++++++++++---------- ftplugin/rust.vim | 3 +++ plugin/rustfmt.vim | 4 +++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim index 2db813f3..bbc9b825 100644 --- a/autoload/rustfmt.vim +++ b/autoload/rustfmt.vim @@ -26,7 +26,7 @@ function! rustfmt#Format() call writefile(getline(1, '$'), l:tmpname) let command = g:rustfmt_command . " --write-mode=overwrite " - + let out = systemlist(command . g:rustfmt_options . " " . shellescape(l:tmpname)) if v:shell_error == 0 @@ -38,16 +38,17 @@ function! rustfmt#Format() silent edit! let &syntax = &syntax - " only clear quickfix if it was previously set, this prevents closing - " other quickfixes - if s:got_fmt_error + " 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 setqflist([]) - cwindow + call setloclist([]) + lwindow endif - elseif g:rustfmt_fail_silently == 0 - "otherwise get the errors and put them to quickfix window + 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: \(.*\)') @@ -60,15 +61,18 @@ function! rustfmt#Format() echo line endif endfor + if empty(errors) % | " Couldn't detect rustfmt error format, output errors endif + if !empty(errors) - call setqflist(errors, 'r') + call setloclist(errors, 'r') echohl Error | echomsg "rustfmt returned error" | echohl None endif + let s:got_fmt_error = 1 - cwindow + lwindow " We didn't use the temp file, so clean up call delete(l:tmpname) endif diff --git a/ftplugin/rust.vim b/ftplugin/rust.vim index 1013a570..8bcfef7b 100644 --- a/ftplugin/rust.vim +++ b/ftplugin/rust.vim @@ -109,6 +109,9 @@ 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() + " Mappings {{{1 " Bind ⌘R in MacVim to :RustRun diff --git a/plugin/rustfmt.vim b/plugin/rustfmt.vim index be2353c7..9db1f6e7 100644 --- a/plugin/rustfmt.vim +++ b/plugin/rustfmt.vim @@ -1,6 +1,8 @@ augroup rustfmt + autocmd! + " code formatting on save - if get(g:, "rustfmt_autosave", 1) + if get(g:, "rustfmt_autosave", 0) autocmd BufWritePre *.rs call rustfmt#Format() endif augroup END From 89e5a08f4cd890b6422e734e570bde33ef3844c3 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Wed, 11 Nov 2015 22:16:06 -0800 Subject: [PATCH 5/9] Properly populate loclist --- autoload/rustfmt.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim index bbc9b825..d9435e13 100644 --- a/autoload/rustfmt.vim +++ b/autoload/rustfmt.vim @@ -42,7 +42,7 @@ function! rustfmt#Format() " clobbering other additions if s:got_fmt_error let s:got_fmt_error = 0 - call setloclist([]) + call setloclist(0, []) lwindow endif elseif g:rustfmt_fail_silently == 0 @@ -67,7 +67,7 @@ function! rustfmt#Format() endif if !empty(errors) - call setloclist(errors, 'r') + call setloclist(0, errors, 'r') echohl Error | echomsg "rustfmt returned error" | echohl None endif From 8e90d4f107259037b0fb280a00e400ef913fe0d9 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Wed, 11 Nov 2015 22:19:10 -0800 Subject: [PATCH 6/9] Don't echo errors we can't parse --- autoload/rustfmt.vim | 2 -- 1 file changed, 2 deletions(-) diff --git a/autoload/rustfmt.vim b/autoload/rustfmt.vim index d9435e13..fbbac65d 100644 --- a/autoload/rustfmt.vim +++ b/autoload/rustfmt.vim @@ -57,8 +57,6 @@ function! rustfmt#Format() \"lnum": tokens[2], \"col": tokens[3], \"text": tokens[5]}) - else - echo line endif endfor From cf7a668791b3ebb55ac77e55156a96b953c1bf98 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Wed, 11 Nov 2015 22:37:22 -0800 Subject: [PATCH 7/9] Documentation written --- doc/rust.txt | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/doc/rust.txt b/doc/rust.txt index 99d64792..7a4091da 100644 --- a/doc/rust.txt +++ b/doc/rust.txt @@ -88,11 +88,37 @@ g:ftplugin_rust_source_path~ let g:ftplugin_rust_source_path = $HOME.'/dev/rust' < - *g:cargo_manifest_name* + *g:cargo_manifest_name* g:cargo_manifest_name~ Set this option to the name of the manifest file for your projects. If not specified it defaults to 'Cargo.toml' : > - let g:cargo_manifest_name = 'Cargo.toml' + let g:cargo_manifest_name = 'Cargo.toml' +< + + *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:rustmft_options = '' < *g:rust_playpen_url* @@ -183,6 +209,15 @@ COMMANDS *rust-commands* |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|. ============================================================================== MAPPINGS *rust-mappings* From 08fbc676433977f9f041f3da47886a90ff1f0196 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Wed, 11 Nov 2015 22:38:18 -0800 Subject: [PATCH 8/9] Point out help file --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f158b62a..41ada149 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,7 @@ git clone --depth=1 https://github.com/rust-lang/rust.vim.git ~/.vim/bundle/rust This plugin can optionally format your code using [rustfmt][rfmt] every time a buffer is written. Simple put `let g:rust_fmt_autosave = 1` in your `.vimrc`. + +## Help + +Further help can be found in the documentation with `:help rust`. From 15081c82c103aa2dbd9509e3422a5e6076c68776 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Sat, 14 Nov 2015 08:24:16 -0800 Subject: [PATCH 9/9] Fix README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41ada149..258e3774 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ git clone --depth=1 https://github.com/rust-lang/rust.vim.git ~/.vim/bundle/rust ## Enabling autoformat This plugin can optionally format your code using [rustfmt][rfmt] every time a -buffer is written. Simple put `let g:rust_fmt_autosave = 1` in your `.vimrc`. +buffer is written. Simple put `let g:rustfmt_autosave = 1` in your `.vimrc`. ## Help