summaryrefslogtreecommitdiff
path: root/vim/bundle/rust.vim/autoload/rustfmt.vim
blob: e5f9830a6fce9477f8ac70f2d809d9d5df57b40a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
" Author: Stephen Sugden <stephen@stephensugden.com>
"
" Adapted from https://github.com/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! 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