diff --git a/autoload/go/tags.vim b/autoload/go/tags.vim new file mode 100644 index 0000000000..cbd058d56c --- /dev/null +++ b/autoload/go/tags.vim @@ -0,0 +1,206 @@ +function! go#tags#Add(start, end, count, ...) abort + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + if &modified + " Write current unsaved buffer to a temp file and use the modified content + let l:tmpname = tempname() + call writefile(getline(1, '$'), l:tmpname) + let fname = l:tmpname + endif + + let offset = 0 + if a:count == -1 + let offset = go#util#OffsetCursor() + endif + + let test_mode = 0 + call call("go#tags#run", [a:start, a:end, offset, "add", fname, test_mode] + a:000) + + " if exists, delete it as we don't need it anymore + if exists("l:tmpname") + call delete(l:tmpname) + endif +endfunction + +function! go#tags#Remove(start, end, count, ...) abort + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + if &modified + " Write current unsaved buffer to a temp file and use the modified content + let l:tmpname = tempname() + call writefile(getline(1, '$'), l:tmpname) + let fname = l:tmpname + endif + + let offset = 0 + if a:count == -1 + let offset = go#util#OffsetCursor() + endif + + let test_mode = 0 + call call("go#tags#run", [a:start, a:end, offset, "remove", fname, test_mode] + a:000) + + " if exists, delete it as we don't need it anymore + if exists("l:tmpname") + call delete(l:tmpname) + endif +endfunction + +" run runs gomodifytag. This is an internal test so we can test it +function! go#tags#run(start, end, offset, mode, fname, test_mode, ...) abort + " do not split this into multiple lines, somehow tests fail in that case + let args = {'mode': a:mode,'start': a:start,'end': a:end,'offset': a:offset,'fname': a:fname,'cmd_args': a:000} + + let result = s:create_cmd(args) + if has_key(result, 'err') + call go#util#EchoError(result.err) + return -1 + endif + + let command = join(result.cmd, " ") + + call go#cmd#autowrite() + let out = go#util#System(command) + if go#util#ShellError() != 0 + call go#util#EchoError(out) + return + endif + + if a:test_mode + exe 'edit ' . a:fname + endif + + call s:write_out(out) + + if a:test_mode + exe 'write! ' . a:fname + endif +endfunc + + +" write_out writes back the given output to the current buffer +func s:write_out(out) abort + " not a json output + if a:out[0] !=# '{' + return + endif + + " nothing to do + if empty(a:out) || type(a:out) != type("") + return + endif + + let result = json_decode(a:out) + if type(result) != type({}) + call go#util#EchoError(printf("malformed output from gomodifytags: %s", a:out)) + return + endif + + let lines = result['lines'] + let start_line = result['start'] + let end_line = result['end'] + + let index = 0 + for line in range(start_line, end_line) + call setline(line, lines[index]) + let index += 1 + endfor +endfunc + + +" create_cmd returns a dict that contains the command to execute gomodifytags +func s:create_cmd(args) abort + if !exists("*json_decode") + return {'err': "requires 'json_decode'. Update your Vim/Neovim version."} + endif + + let bin_path = go#path#CheckBinPath('gomodifytags') + if empty(bin_path) + return {'err': "gomodifytags does not exist"} + endif + + let l:start = a:args.start + let l:end = a:args.end + let l:offset = a:args.offset + let l:mode = a:args.mode + let l:cmd_args = a:args.cmd_args + + " start constructing the command + let cmd = [bin_path] + call extend(cmd, ["-format", "json"]) + call extend(cmd, ["-file", a:args.fname]) + + if l:offset != 0 + call extend(cmd, ["-offset", l:offset]) + else + let range = printf("%d,%d", l:start, l:end) + call extend(cmd, ["-line", range]) + endif + + if l:mode == "add" + let l:tags = [] + let l:options = [] + + if !empty(l:cmd_args) + for item in l:cmd_args + let splitted = split(item, ",") + + " tag only + if len(splitted) == 1 + call add(l:tags, splitted[0]) + endif + + " options only + if len(splitted) == 2 + call add(l:tags, splitted[0]) + call add(l:options, printf("%s=%s", splitted[0], splitted[1])) + endif + endfor + endif + + " default value + if empty(l:tags) + let l:tags = ["json"] + endif + + " construct tags + call extend(cmd, ["-add-tags", join(l:tags, ",")]) + + " construct options + if !empty(l:options) + call extend(cmd, ["-add-options", join(l:options, ",")]) + endif + elseif l:mode == "remove" + if empty(l:cmd_args) + call add(cmd, "-clear-tags") + else + let l:tags = [] + let l:options = [] + for item in l:cmd_args + let splitted = split(item, ",") + + " tag only + if len(splitted) == 1 + call add(l:tags, splitted[0]) + endif + + " options only + if len(splitted) == 2 + call add(l:options, printf("%s=%s", splitted[0], splitted[1])) + endif + endfor + + " construct tags + if !empty(l:tags) + call extend(cmd, ["-remove-tags", join(l:tags, ",")]) + endif + + " construct options + if !empty(l:options) + call extend(cmd, ["-remove-options", join(l:options, ",")]) + endif + endif + else + return {'err': printf("unknown mode: %s", l:mode)} + endif + + return {'cmd': cmd} +endfunc diff --git a/autoload/go/tags_test.vim b/autoload/go/tags_test.vim new file mode 100644 index 0000000000..202aa0b38e --- /dev/null +++ b/autoload/go/tags_test.vim @@ -0,0 +1,30 @@ +func Test_add_tags() + let input_file = tempname() + call writefile(readfile("test-fixtures/tags/add_all_input.go"), input_file) + + let expected = join(readfile("test-fixtures/tags/add_all_golden.go"), "\n") + + " run for offset 40, which is inside the struct + call go#tags#run(0, 0, 40, "add", input_file, 1) + + let actual = join(readfile(input_file), "\n") + + call assert_equal(expected, actual) +endfunc + + +func Test_remove_tags() + let input_file = tempname() + call writefile(readfile("test-fixtures/tags/remove_all_input.go"), input_file) + + let expected = join(readfile("test-fixtures/tags/remove_all_golden.go"), "\n") + + " run for offset 40, which is inside the struct + call go#tags#run(0, 0, 40, "remove", input_file, 1) + + let actual = join(readfile(input_file), "\n") + + call assert_equal(expected, actual) +endfunc + + diff --git a/autoload/go/test-fixtures/tags/add_all_golden.go b/autoload/go/test-fixtures/tags/add_all_golden.go new file mode 100644 index 0000000000..eaa3e7b3b4 --- /dev/null +++ b/autoload/go/test-fixtures/tags/add_all_golden.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string `json:"name"` + ID int `json:"id"` + MyHomeAddress string `json:"my_home_address"` + SubDomains []string `json:"sub_domains"` + Empty string `json:"empty"` + Example int64 `json:"example"` + Example2 string `json:"example_2"` + Bar struct { + Four string `json:"four"` + Five string `json:"five"` + } `json:"bar"` + Lala interface{} `json:"lala"` +} diff --git a/autoload/go/test-fixtures/tags/add_all_input.go b/autoload/go/test-fixtures/tags/add_all_input.go new file mode 100644 index 0000000000..bfc4d3e50b --- /dev/null +++ b/autoload/go/test-fixtures/tags/add_all_input.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string + ID int + MyHomeAddress string + SubDomains []string + Empty string + Example int64 + Example2 string + Bar struct { + Four string + Five string + } + Lala interface{} +} diff --git a/autoload/go/test-fixtures/tags/remove_all_golden.go b/autoload/go/test-fixtures/tags/remove_all_golden.go new file mode 100644 index 0000000000..de57085887 --- /dev/null +++ b/autoload/go/test-fixtures/tags/remove_all_golden.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string + ID int + MyHomeAddress string + SubDomains []string + Empty string + Example int64 + Example2 string + Bar struct { + Four string + Five string + } + Lala interface{} +} diff --git a/autoload/go/test-fixtures/tags/remove_all_input.go b/autoload/go/test-fixtures/tags/remove_all_input.go new file mode 100644 index 0000000000..eaa3e7b3b4 --- /dev/null +++ b/autoload/go/test-fixtures/tags/remove_all_input.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string `json:"name"` + ID int `json:"id"` + MyHomeAddress string `json:"my_home_address"` + SubDomains []string `json:"sub_domains"` + Empty string `json:"empty"` + Example int64 `json:"example"` + Example2 string `json:"example_2"` + Bar struct { + Four string `json:"four"` + Five string `json:"five"` + } `json:"bar"` + Lala interface{} `json:"lala"` +} diff --git a/ftplugin/go/commands.vim b/ftplugin/go/commands.vim index d8bff6655f..6f108a6755 100644 --- a/ftplugin/go/commands.vim +++ b/ftplugin/go/commands.vim @@ -14,14 +14,15 @@ command! -range=% GoChannelPeers call go#guru#ChannelPeers() command! -range=% GoReferrers call go#guru#Referrers() command! -nargs=? GoGuruTags call go#guru#Tags() - -command! -nargs=* -range GoAddTags call go#util#AddTags(, , ) - command! -range=0 GoSameIds call go#guru#SameIds() command! -range=0 GoSameIdsClear call go#guru#ClearSameIds() 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 GoRemoveTags call go#tags#Remove(, , , ) + " -- tool command! -nargs=0 GoFiles echo go#tool#Files() command! -nargs=0 GoDeps echo go#tool#Deps() diff --git a/plugin/go.vim b/plugin/go.vim index 26547310a2..8a38145341 100644 --- a/plugin/go.vim +++ b/plugin/go.vim @@ -18,6 +18,7 @@ let s:packages = [ \ "github.com/jstemmer/gotags", \ "github.com/klauspost/asmfmt/cmd/asmfmt", \ "github.com/fatih/motion", + \ "github.com/fatih/gomodifytags", \ "github.com/zmb3/gogetdoc", \ "github.com/josharian/impl", \ ]