vim-polyglot/autoload/rustfmt.vim

253 lines
7.3 KiB
VimL
Raw Permalink Normal View History

2015-12-06 05:31:38 -05:00
if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'rust') == -1
" Author: Stephen Sugden <stephen@stephensugden.com>
"
" Adapted from https://github.com/fatih/vim-go
2017-09-27 13:57:29 -04:00
" For bugs, patches and license go to https://github.com/rust-lang/rust.vim
2015-12-06 05:31:38 -05:00
if !exists("g:rustfmt_autosave")
2018-07-08 09:16:28 -04:00
let g:rustfmt_autosave = 0
2015-12-06 05:31:38 -05:00
endif
if !exists("g:rustfmt_command")
2018-07-08 09:16:28 -04:00
let g:rustfmt_command = "rustfmt"
2015-12-06 05:31:38 -05:00
endif
if !exists("g:rustfmt_options")
2018-07-08 09:16:28 -04:00
let g:rustfmt_options = ""
2015-12-06 05:31:38 -05:00
endif
if !exists("g:rustfmt_fail_silently")
2018-07-08 09:16:28 -04:00
let g:rustfmt_fail_silently = 0
endif
function! rustfmt#DetectVersion()
" Save rustfmt '--help' for feature inspection
silent let s:rustfmt_help = system(g:rustfmt_command . " --help")
2018-12-26 04:41:57 -05:00
let s:rustfmt_unstable_features = s:rustfmt_help =~# "--unstable-features"
2018-07-08 09:16:28 -04:00
" Build a comparable rustfmt version varible out of its `--version` output:
2018-12-26 04:41:57 -05:00
silent let l:rustfmt_version_full = system(g:rustfmt_command . " --version")
let l:rustfmt_version_list = matchlist(l:rustfmt_version_full,
\ '\vrustfmt ([0-9]+[.][0-9]+[.][0-9]+)')
if len(l:rustfmt_version_list) < 3
2018-07-08 09:16:28 -04:00
let s:rustfmt_version = "0"
else
2018-12-26 04:41:57 -05:00
let s:rustfmt_version = l:rustfmt_version_list[1]
2018-07-08 09:16:28 -04:00
endif
return s:rustfmt_version
endfunction
call rustfmt#DetectVersion()
if !exists("g:rustfmt_emit_files")
let g:rustfmt_emit_files = s:rustfmt_version >= "0.8.2"
endif
if !exists("g:rustfmt_file_lines")
2018-12-26 04:41:57 -05:00
let g:rustfmt_file_lines = s:rustfmt_help =~# "--file-lines JSON"
2015-12-06 05:31:38 -05:00
endif
let s:got_fmt_error = 0
2018-07-08 09:16:28 -04:00
function! rustfmt#Load()
" Utility call to get this script loaded, for debugging
endfunction
function! s:RustfmtWriteMode()
if g:rustfmt_emit_files
return "--emit=files"
else
return "--write-mode=overwrite"
endif
endfunction
2018-10-08 13:00:59 -04:00
function! s:RustfmtConfig()
let l:rustfmt_toml = findfile('rustfmt.toml', expand('%:p:h') . ';')
if l:rustfmt_toml !=# ''
return '--config-path '.l:rustfmt_toml
endif
let l:_rustfmt_toml = findfile('.rustfmt.toml', expand('%:p:h') . ';')
if l:_rustfmt_toml !=# ''
return '--config-path '.l:_rustfmt_toml
endif
return ''
endfunction
2016-12-20 14:57:20 -05:00
function! s:RustfmtCommandRange(filename, line1, line2)
2018-07-08 09:16:28 -04:00
if g:rustfmt_file_lines == 0
echo "--file-lines is not supported in the installed `rustfmt` executable"
return
endif
let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]}
let l:write_mode = s:RustfmtWriteMode()
2018-10-08 13:00:59 -04:00
let l:rustfmt_config = s:RustfmtConfig()
2018-07-08 09:16:28 -04:00
2018-12-26 04:41:57 -05:00
" FIXME: When --file-lines gets to be stable, add version range checking
2018-07-08 09:16:28 -04:00
" accordingly.
2018-12-26 04:41:57 -05:00
let l:unstable_features = s:rustfmt_unstable_features ? '--unstable-features' : ''
2018-07-08 09:16:28 -04:00
2018-10-08 13:00:59 -04:00
let l:cmd = printf("%s %s %s %s %s --file-lines '[%s]' %s", g:rustfmt_command,
\ l:write_mode, g:rustfmt_options,
\ l:unstable_features, l:rustfmt_config,
\ json_encode(l:arg), shellescape(a:filename))
2018-07-08 09:16:28 -04:00
return l:cmd
2016-12-20 14:57:20 -05:00
endfunction
2015-12-06 05:31:38 -05:00
2018-10-08 13:00:59 -04:00
function! s:RustfmtCommand()
if g:rustfmt_emit_files
let l:write_mode = "--emit=stdout"
else
let l:write_mode = "--write-mode=display"
endif
" rustfmt will pick on the right config on its own due to the
" current directory change.
return g:rustfmt_command . " ". l:write_mode . " " . g:rustfmt_options
endfunction
function! s:DeleteLines(start, end) abort
silent! execute a:start . ',' . a:end . 'delete _'
2016-12-20 14:57:20 -05:00
endfunction
2015-12-06 05:31:38 -05:00
2018-07-08 09:16:28 -04:00
function! s:RunRustfmt(command, tmpname, fail_silently)
mkview!
2018-10-08 13:00:59 -04:00
let l:stderr_tmpname = tempname()
2018-12-26 04:41:57 -05:00
call writefile([], l:stderr_tmpname)
2018-10-08 13:00:59 -04:00
let l:command = a:command . ' 2> ' . l:stderr_tmpname
if a:tmpname ==# ''
" Rustfmt in stdin/stdout mode
" chdir to the directory of the file
let l:has_lcd = haslocaldir()
let l:prev_cd = getcwd()
execute 'lchdir! '.expand('%:h')
let l:buffer = getline(1, '$')
if exists("*systemlist")
silent let out = systemlist(l:command, l:buffer)
else
2018-12-26 04:41:57 -05:00
silent let out = split(system(l:command,
\ join(l:buffer, "\n")), '\r\?\n')
2018-10-08 13:00:59 -04:00
endif
2018-07-08 09:16:28 -04:00
else
2018-10-08 13:00:59 -04:00
if exists("*systemlist")
silent let out = systemlist(l:command)
else
silent let out = split(system(l:command), '\r\?\n')
endif
2018-07-08 09:16:28 -04:00
endif
2018-10-08 13:00:59 -04:00
let l:stderr = readfile(l:stderr_tmpname)
call delete(l:stderr_tmpname)
if v:shell_error == 0
2018-07-08 09:16:28 -04:00
" remove undo point caused via BufWritePre
try | silent undojoin | catch | endtry
2018-10-08 13:00:59 -04:00
if a:tmpname ==# ''
let l:content = l:out
else
" take the tmpfile's content, this is better than rename
" because it preserves file modes.
let l:content = readfile(a:tmpname)
endif
call s:DeleteLines(len(l:content), line('$'))
2018-07-08 09:16:28 -04:00
call setline(1, l:content)
" 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 && a:fail_silently == 0
" otherwise get the errors and put them in the location list
2018-10-08 13:00:59 -04:00
let l:errors = []
2018-07-08 09:16:28 -04:00
2018-10-08 13:00:59 -04:00
let l:prev_line = ""
for l:line in l:stderr
2018-07-08 09:16:28 -04:00
" error: expected one of `;` or `as`, found `extern`
" --> src/main.rs:2:1
2018-10-08 13:00:59 -04:00
let tokens = matchlist(l:line, '^\s\+-->\s\(.\{-}\):\(\d\+\):\(\d\+\)$')
2018-07-08 09:16:28 -04:00
if !empty(tokens)
2018-10-08 13:00:59 -04:00
call add(l:errors, {"filename": @%,
2018-07-08 09:16:28 -04:00
\"lnum": tokens[2],
\"col": tokens[3],
2018-10-08 13:00:59 -04:00
\"text": l:prev_line})
2018-07-08 09:16:28 -04:00
endif
2018-10-08 13:00:59 -04:00
let l:prev_line = l:line
2018-07-08 09:16:28 -04:00
endfor
2018-10-08 13:00:59 -04:00
if !empty(l:errors)
call setloclist(0, l:errors, 'r')
2018-07-08 09:16:28 -04:00
echohl Error | echomsg "rustfmt returned error" | echohl None
2018-10-08 13:00:59 -04:00
else
echo "rust.vim: was not able to parse rustfmt messages. Here is the raw output:"
echo "\n"
for l:line in l:stderr
echo l:line
endfor
2018-07-08 09:16:28 -04:00
endif
let s:got_fmt_error = 1
lwindow
endif
2018-10-08 13:00:59 -04:00
" Restore the current directory if needed
if a:tmpname ==# ''
if l:has_lcd
execute 'lchdir! '.l:prev_cd
else
execute 'chdir! '.l:prev_cd
endif
endif
2018-07-08 09:16:28 -04:00
silent! loadview
2016-12-20 14:57:20 -05:00
endfunction
2018-10-08 13:00:59 -04:00
function! rustfmt#FormatRange(line1, line2)
2018-07-08 09:16:28 -04:00
let l:tmpname = tempname()
call writefile(getline(1, '$'), l:tmpname)
let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2)
call s:RunRustfmt(command, l:tmpname, 0)
2018-10-08 13:00:59 -04:00
call delete(l:tmpname)
2016-12-20 14:57:20 -05:00
endfunction
function! rustfmt#Format()
2018-10-08 13:00:59 -04:00
call s:RunRustfmt(s:RustfmtCommand(), '', 0)
endfunction
function! rustfmt#Cmd()
" Mainly for debugging
return s:RustfmtCommand()
2018-07-08 09:16:28 -04:00
endfunction
2016-12-20 14:57:20 -05:00
2018-07-08 09:16:28 -04:00
function! rustfmt#PreWrite()
if rust#GetConfigVar('rustfmt_autosave_if_config_present', 0)
2018-10-08 13:00:59 -04:00
if findfile('rustfmt.toml', '.;') !=# '' || findfile('.rustfmt.toml', '.;') !=# ''
2018-07-08 09:16:28 -04:00
let b:rustfmt_autosave = 1
let b:rustfmt_autosave_because_of_config = 1
endif
endif
if !rust#GetConfigVar("rustfmt_autosave", 0)
return
endif
2018-10-08 13:00:59 -04:00
call s:RunRustfmt(s:RustfmtCommand(), '', 1)
2015-12-06 05:31:38 -05:00
endfunction
2018-07-08 09:16:28 -04:00
" vim: set et sw=4 sts=4 ts=8:
2015-12-06 05:31:38 -05:00
endif