summaryrefslogtreecommitdiffstats
path: root/autoload/zig/fmt.vim
blob: cc7862c71149cef05d70fe2744b9e88dc9636669 (plain) (blame)
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'zig') != -1
  finish
endif

" Adapted from fatih/vim-go: autoload/go/fmt.vim
"
" Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file.
"
function! zig#fmt#Format() abort
  if zig#config#FmtExperimental()
    " Using winsaveview to save/restore cursor state has the problem of
    " closing folds on save:
    "   https://github.com/fatih/vim-go/issues/502
    " One fix is to use mkview instead. Unfortunately, this sometimes causes
    " other bad side effects:
    "   https://github.com/fatih/vim-go/issues/728
    " and still closes all folds if foldlevel>0:
    "   https://github.com/fatih/vim-go/issues/732
    let l:curw = {}
    try
      mkview!
    catch
      let l:curw = winsaveview()
    endtry

    " save our undo file to be restored after we are done. This is needed to
    " prevent an additional undo jump due to BufWritePre auto command and also
    " restore 'redo' history because it's getting being destroyed every
    " BufWritePre
    let tmpundofile = tempname()
    exe 'wundo! ' . tmpundofile
  else
    " Save cursor position and many other things.
    let l:curw = winsaveview()
  endif

  " Save cursor position and many other things.
  let l:curw = winsaveview()

  let bin_name = zig#config#FmtCommand()

  " Get current position in file
  let current_col = col('.')
  let orig_line_count = line('$')

  " Save current buffer first, else fmt will run on the original file and we
  " will lose our changes.
  silent! execute 'write' expand('%')

  let [l:out, l:err] = zig#fmt#run(bin_name, expand('%'))

  if l:err == 0
    call zig#fmt#update_file(expand('%'))
  elseif !zig#config#FmtFailSilently()
    let errors = s:parse_errors(expand('%'), out)
    call s:show_errors(errors)
  endif

  let diff_offset = line('$') - orig_line_count

  if zig#config#FmtExperimental()
    " restore our undo history
    silent! exe 'rundo ' . tmpundofile
    call delete(tmpundofile)

    " Restore our cursor/windows positions, folds, etc.
    if empty(l:curw)
      silent! loadview
    else
      call winrestview(l:curw)
    endif
  else
    " Restore our cursor/windows positions.
    call winrestview(l:curw)
  endif

  " be smart and jump to the line the new statement was added/removed
  call cursor(line('.') + diff_offset, current_col)

  " Syntax highlighting breaks less often.
  syntax sync fromstart
endfunction

" update_file updates the target file with the given formatted source
function! zig#fmt#update_file(target)
  " remove undo point caused via BufWritePre
  try | silent undojoin | catch | endtry

  " reload buffer to reflect latest changes
  silent edit!

  let l:listtype = zig#list#Type("ZigFmt")

  " the title information was introduced with 7.4-2200
  " https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640
  if has('patch-7.4.2200')
    " clean up previous list
    if l:listtype == "quickfix"
      let l:list_title = getqflist({'title': 1})
    else
      let l:list_title = getloclist(0, {'title': 1})
    endif
  else
    " can't check the title, so assume that the list was for go fmt.
    let l:list_title = {'title': 'Format'}
  endif

  if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
    call zig#list#Clean(l:listtype)
  endif
endfunction

" run runs the gofmt/goimport command for the given source file and returns
" the output of the executed command. Target is the real file to be formatted.
function! zig#fmt#run(bin_name, target)
  let l:cmd = []
  call extend(cmd, a:bin_name)
  call extend(cmd, [a:target])
  return zig#util#Exec(l:cmd)
endfunction

" parse_errors parses the given errors and returns a list of parsed errors
function! s:parse_errors(filename, content) abort
  let splitted = split(a:content, '\n')

  " list of errors to be put into location list
  let errors = []
  for line in splitted
    let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)')
    if !empty(tokens)
      call add(errors,{
            \"filename": a:filename,
            \"lnum":     tokens[2],
            \"col":      tokens[3],
            \"text":     tokens[4],
            \ })
    endif
  endfor

  return errors
endfunction

" show_errors opens a location list and shows the given errors. If the given
" errors is empty, it closes the the location list
function! s:show_errors(errors) abort
  let l:listtype = zig#list#Type("ZigFmt")
  if !empty(a:errors)
    call zig#list#Populate(l:listtype, a:errors, 'Format')
    echohl Error | echomsg "zig fmt returned error" | echohl None
  endif

  " this closes the window if there are no errors or it opens
  " it if there is any
  call zig#list#Window(l:listtype, len(a:errors))
endfunction

function! zig#fmt#ToggleFmtAutoSave() abort
  if zig#config#FmtAutosave()
    call zig#config#SetFmtAutosave(0)
    call zig#util#EchoProgress("auto fmt disabled")
    return
  end

  call zig#config#SetFmtAutosave(1)
  call zig#util#EchoProgress("auto fmt enabled")
endfunction

" vim: sw=2 ts=2 et