|
| 1 | +function! go#mod#Format() abort |
| 2 | + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') |
| 3 | + |
| 4 | + " Save cursor position and many other things. |
| 5 | + let l:curw = winsaveview() |
| 6 | + |
| 7 | + " Write current unsaved buffer to a temp file |
| 8 | + let l:tmpname = tempname() . '.go' |
| 9 | + call writefile(go#util#GetLines(), l:tmpname) |
| 10 | + if go#util#IsWin() |
| 11 | + let l:tmpname = tr(l:tmpname, '\', '/') |
| 12 | + endif |
| 13 | + |
| 14 | + let current_col = col('.') |
| 15 | + let l:args = ['go', 'mod', 'edit', '--fmt', l:tmpname] |
| 16 | + let [l:out, l:err] = go#util#Exec(l:args) |
| 17 | + let diff_offset = len(readfile(l:tmpname)) - line('$') |
| 18 | + |
| 19 | + if l:err == 0 |
| 20 | + call go#mod#update_file(l:tmpname, fname) |
| 21 | + else |
| 22 | + let errors = s:parse_errors(fname, l:out) |
| 23 | + call s:show_errors(errors) |
| 24 | + endif |
| 25 | + |
| 26 | + " We didn't use the temp file, so clean up |
| 27 | + call delete(l:tmpname) |
| 28 | + |
| 29 | + " Restore our cursor/windows positions. |
| 30 | + call winrestview(l:curw) |
| 31 | + |
| 32 | + " be smart and jump to the line the new statement was added/removed |
| 33 | + call cursor(line('.') + diff_offset, current_col) |
| 34 | + |
| 35 | + " Syntax highlighting breaks less often. |
| 36 | + syntax sync fromstart |
| 37 | +endfunction |
| 38 | + |
| 39 | +" update_file updates the target file with the given formatted source |
| 40 | +function! go#mod#update_file(source, target) |
| 41 | + " remove undo point caused via BufWritePre |
| 42 | + try | silent undojoin | catch | endtry |
| 43 | + |
| 44 | + let old_fileformat = &fileformat |
| 45 | + if exists("*getfperm") |
| 46 | + " save file permissions |
| 47 | + let original_fperm = getfperm(a:target) |
| 48 | + endif |
| 49 | + |
| 50 | + call rename(a:source, a:target) |
| 51 | + |
| 52 | + " restore file permissions |
| 53 | + if exists("*setfperm") && original_fperm != '' |
| 54 | + call setfperm(a:target , original_fperm) |
| 55 | + endif |
| 56 | + |
| 57 | + " reload buffer to reflect latest changes |
| 58 | + silent edit! |
| 59 | + |
| 60 | + let &fileformat = old_fileformat |
| 61 | + let &syntax = &syntax |
| 62 | + |
| 63 | + let l:listtype = go#list#Type("GoModFmt") |
| 64 | + |
| 65 | + " the title information was introduced with 7.4-2200 |
| 66 | + " https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640 |
| 67 | + if has('patch-7.4.2200') |
| 68 | + " clean up previous list |
| 69 | + if l:listtype == "quickfix" |
| 70 | + let l:list_title = getqflist({'title': 1}) |
| 71 | + else |
| 72 | + let l:list_title = getloclist(0, {'title': 1}) |
| 73 | + endif |
| 74 | + else |
| 75 | + " can't check the title, so assume that the list was for go fmt. |
| 76 | + let l:list_title = {'title': 'Format'} |
| 77 | + endif |
| 78 | + |
| 79 | + if has_key(l:list_title, "title") && l:list_title['title'] == "Format" |
| 80 | + call go#list#Clean(l:listtype) |
| 81 | + endif |
| 82 | +endfunction |
| 83 | + |
| 84 | +" parse_errors parses the given errors and returns a list of parsed errors |
| 85 | +function! s:parse_errors(filename, content) abort |
| 86 | + let splitted = split(a:content, '\n') |
| 87 | + |
| 88 | + " list of errors to be put into location list |
| 89 | + let errors = [] |
| 90 | + for line in splitted |
| 91 | + let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)') |
| 92 | + if !empty(tokens) |
| 93 | + call add(errors,{ |
| 94 | + \"filename": a:filename, |
| 95 | + \"lnum": tokens[2], |
| 96 | + \"col": tokens[3], |
| 97 | + \"text": tokens[4], |
| 98 | + \ }) |
| 99 | + endif |
| 100 | + endfor |
| 101 | + |
| 102 | + return errors |
| 103 | +endfunction |
| 104 | + |
| 105 | +" show_errors opens a location list and shows the given errors. If the given |
| 106 | +" errors is empty, it closes the the location list |
| 107 | +function! s:show_errors(errors) abort |
| 108 | + let l:listtype = go#list#Type("GoModFmt") |
| 109 | + if !empty(a:errors) |
| 110 | + call go#list#Populate(l:listtype, a:errors, 'Format') |
| 111 | + echohl Error | echomsg "GoModFmt returned error" | echohl None |
| 112 | + endif |
| 113 | + |
| 114 | + " this closes the window if there are no errors or it opens |
| 115 | + " it if there is any |
| 116 | + call go#list#Window(l:listtype, len(a:errors)) |
| 117 | +endfunction |
| 118 | + |
| 119 | +function! go#mod#ToggleModfmtAutoSave() abort |
| 120 | + if go#config#ModfmtAutosave() |
| 121 | + call go#config#SetModfmtAutosave(0) |
| 122 | + call go#util#EchoProgress("auto mod fmt disabled") |
| 123 | + return |
| 124 | + end |
| 125 | + |
| 126 | + call go#config#SetModfmtAutosave(1) |
| 127 | + call go#util#EchoProgress("auto fmt enabled") |
| 128 | +endfunction |
0 commit comments