diff --git a/autoload/go/config.vim b/autoload/go/config.vim index 8e72b1a83a..9a073eb6c8 100644 --- a/autoload/go/config.vim +++ b/autoload/go/config.vim @@ -278,6 +278,14 @@ function! go#config#SetAsmfmtAutosave(value) abort let g:go_asmfmt_autosave = a:value endfunction +function! go#config#ModfmtAutosave() abort + return get(g:, "go_modfmt_autosave", 1) +endfunction + +function! go#config#SetModfmtAutosave(value) abort + let g:go_modfmt_autosave = a:value +endfunction + function! go#config#DocMaxHeight() abort return get(g:, "go_doc_max_height", 20) endfunction diff --git a/autoload/go/fix.vim b/autoload/go/fix.vim new file mode 120000 index 0000000000..e04e24e9d1 --- /dev/null +++ b/autoload/go/fix.vim @@ -0,0 +1 @@ +/Users/fatih/go/src/github.com/fatih/goautofix/vim/fix.vim \ No newline at end of file diff --git a/autoload/go/mod.vim b/autoload/go/mod.vim new file mode 100644 index 0000000000..1c5d940685 --- /dev/null +++ b/autoload/go/mod.vim @@ -0,0 +1,128 @@ +function! go#mod#Format() abort + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + + " Save cursor position and many other things. + let l:curw = winsaveview() + + " Write current unsaved buffer to a temp file + let l:tmpname = tempname() . '.go' + call writefile(go#util#GetLines(), l:tmpname) + if go#util#IsWin() + let l:tmpname = tr(l:tmpname, '\', '/') + endif + + let current_col = col('.') + let l:args = ['go', 'mod', 'edit', '--fmt', l:tmpname] + let [l:out, l:err] = go#util#Exec(l:args) + let diff_offset = len(readfile(l:tmpname)) - line('$') + + if l:err == 0 + call go#mod#update_file(l:tmpname, fname) + else + let errors = s:parse_errors(fname, l:out) + call s:show_errors(errors) + endif + + " We didn't use the temp file, so clean up + call delete(l:tmpname) + + " Restore our cursor/windows positions. + call winrestview(l:curw) + + " 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! go#mod#update_file(source, target) + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry + + let old_fileformat = &fileformat + if exists("*getfperm") + " save file permissions + let original_fperm = getfperm(a:target) + endif + + call rename(a:source, a:target) + + " restore file permissions + if exists("*setfperm") && original_fperm != '' + call setfperm(a:target , original_fperm) + endif + + " reload buffer to reflect latest changes + silent edit! + + let &fileformat = old_fileformat + let &syntax = &syntax + + let l:listtype = go#list#Type("GoModFmt") + + " 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 go#list#Clean(l:listtype) + endif +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 = go#list#Type("GoModFmt") + if !empty(a:errors) + call go#list#Populate(l:listtype, a:errors, 'Format') + echohl Error | echomsg "GoModFmt returned error" | echohl None + endif + + " this closes the window if there are no errors or it opens + " it if there is any + call go#list#Window(l:listtype, len(a:errors)) +endfunction + +function! go#mod#ToggleModfmtAutoSave() abort + if go#config#ModfmtAutosave() + call go#config#SetModfmtAutosave(0) + call go#util#EchoProgress("auto mod fmt disabled") + return + end + + call go#config#SetModfmtAutosave(1) + call go#util#EchoProgress("auto fmt enabled") +endfunction diff --git a/ftdetect/gofiletype.vim b/ftdetect/gofiletype.vim index d3662f4be1..8cd1148d84 100644 --- a/ftdetect/gofiletype.vim +++ b/ftdetect/gofiletype.vim @@ -31,4 +31,8 @@ au BufReadPost *.s call s:gofiletype_post() au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl +au BufNewFile *.mod setfiletype gomod | setlocal fileencoding=utf-8 fileformat=unix +au BufRead *.mod call s:gofiletype_pre("gomod") +au BufReadPost *.mod call s:gofiletype_post() + " vim: sw=2 ts=2 et diff --git a/ftplugin/go/commands-fix.vim b/ftplugin/go/commands-fix.vim new file mode 120000 index 0000000000..c5f8a762e1 --- /dev/null +++ b/ftplugin/go/commands-fix.vim @@ -0,0 +1 @@ +/Users/fatih/go/src/github.com/fatih/goautofix/vim/commands-fix.vim \ No newline at end of file diff --git a/ftplugin/go/commands.vim b/ftplugin/go/commands.vim index ba29c59c07..8852c5ea54 100644 --- a/ftplugin/go/commands.vim +++ b/ftplugin/go/commands.vim @@ -20,9 +20,12 @@ command! -range=0 GoSameIdsToggle call go#guru#ToggleSameIds() command! -range=0 GoSameIdsAutoToggle call go#guru#AutoToogleSameIds() " -- tags -command! -nargs=* -range GoAddTags call go#tags#Add(, , , ) +command! -nargs=? -range GoAddTags call go#tags#Add(, , , ) command! -nargs=* -range GoRemoveTags call go#tags#Remove(, , , ) +" -- mod +command! -nargs=0 -range GoModFmt call go#mod#Format() + " -- tool command! -nargs=* -complete=customlist,go#tool#ValidFiles GoFiles echo go#tool#Files() command! -nargs=0 GoDeps echo go#tool#Deps() diff --git a/ftplugin/gomod.vim b/ftplugin/gomod.vim new file mode 100644 index 0000000000..5f3dc412a9 --- /dev/null +++ b/ftplugin/gomod.vim @@ -0,0 +1,15 @@ +" gomod.vim: Vim filetype plugin for Go assembler. + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl fo< com< cms<" + +setlocal formatoptions-=t + +setlocal comments=s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s + +" vim: sw=2 ts=2 et diff --git a/ftplugin/gomod/commands.vim b/ftplugin/gomod/commands.vim new file mode 100644 index 0000000000..d04fd50a81 --- /dev/null +++ b/ftplugin/gomod/commands.vim @@ -0,0 +1 @@ +command! -nargs=0 -range GoModFmt call go#mod#Format() diff --git a/ftplugin/gomod/mappings.vim b/ftplugin/gomod/mappings.vim new file mode 100644 index 0000000000..c8664f648b --- /dev/null +++ b/ftplugin/gomod/mappings.vim @@ -0,0 +1 @@ +nnoremap (go-mod-fmt) :call go#mod#Format() diff --git a/plugin/go.vim b/plugin/go.vim index 01e19b7d93..a19175e3bb 100644 --- a/plugin/go.vim +++ b/plugin/go.vim @@ -226,6 +226,13 @@ function! s:asmfmt_autosave() endif endfunction +function! s:modfmt_autosave() + " go.mod code formatting on save + if get(g:, "go_modfmt_autosave", 1) + call go#mod#Format() + endif +endfunction + function! s:metalinter_autosave() " run gometalinter on save if get(g:, "go_metalinter_autosave", 0) @@ -253,6 +260,7 @@ augroup vim-go endif autocmd BufWritePre *.go call s:fmt_autosave() + autocmd BufWritePre *.mod call s:modfmt_autosave() autocmd BufWritePre *.s call s:asmfmt_autosave() autocmd BufWritePost *.go call s:metalinter_autosave() autocmd BufNewFile *.go call s:template_autocreate() diff --git a/syntax/gomod.vim b/syntax/gomod.vim new file mode 100644 index 0000000000..969df5172f --- /dev/null +++ b/syntax/gomod.vim @@ -0,0 +1,45 @@ +" gomod.vim: Vim syntax file for go.mod file +" +" Quit when a (custom) syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +syntax case match + +" match keywords +syntax keyword gomodModule module +syntax keyword gomodRequire require +syntax keyword gomodExclude exclude +syntax keyword gomodReplace replace + +" require, exclude and replace can be also grouped into block +syntax region gomodRequire start='require (' end=')' transparent contains=gomodRequire,gomodVersion +syntax region gomodExclude start='exclude (' end=')' transparent contains=gomodExclude,gomodVersion +syntax region gomodReplace start='replace (' end=')' transparent contains=gomodReplace,gomodVersion + +" set highlights +highlight default link gomodModule Keyword +highlight default link gomodRequire Keyword +highlight default link gomodExclude Keyword +highlight default link gomodReplace Keyword + +" comments are always in form of // ... +syntax region gomodComment start="//" end="$" contains=@Spell +highlight default link gomodComment Comment + +" make sure quoted import paths are higlighted +syntax region gomodString start=+"+ skip=+\\\\\|\\"+ end=+"+ +highlight default link gomodString String + +" replace operator is in the form of '=>' +syntax match gomodReplaceOperator "\v\=\>" +highlight default link gomodReplaceOperator Operator + + +" highlight semver, note that this is very simple. But it works for now +syntax match gomodVersion "v\d\.\d\.\d" +syntax match gomodVersion "v\d\.\d\.\d-.*" +highlight default link gomodVersion Identifier + +let b:current_syntax = "gomod"