Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use new 'goimport' tool instead of VimScript for :GoImport and :GoDrop #1559

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 43 additions & 198 deletions autoload/go/import.vim
Original file line number Diff line number Diff line change
@@ -1,213 +1,58 @@
" 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.
" Change imports of the current buffer.
"
" Check out the docs for more information at /doc/vim-go.txt
"
function! go#import#SwitchImport(enabled, localname, path, bang) abort
let view = winsaveview()
let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '')

" Quotes are not necessary, so remove them if provided.
if path[0] == '"'
let path = strpart(path, 1)
endif
if path[len(path)-1] == '"'
let path = strpart(path, 0, len(path) - 1)
" add: add import; if 0 it will remove the import
" alias: import alias
" path: import path
" bang: go get if packages don't exist
function! go#import#SwitchImport(add, alias, path, bang) abort
let l:path = a:path
if l:path is# ''
call go#util#EchoError('import path not provided')
return
endif

" if given a trailing slash, eg. `github.com/user/pkg/`, remove it
if path[len(path)-1] == '/'
let path = strpart(path, 0, len(path) - 1)
let l:cmd = ['goimport', '-json', (a:add ? '-replace' : '-rm'),
\ (a:alias isnot? '' ? a:path . ':' . a:alias : a:path)]
if a:bang is# '!'
let l:cmd += ['-get']
endif

if path == ''
call s:Error('Import path not provided')
let [l:out, l:err] = go#util#Exec(l:cmd, join(go#util#GetLines(), "\n"))
if l:err
call go#util#EchoError(l:out)
return
endif

if a:bang == "!"
let out = go#util#System("go get -u -v ".shellescape(path))
if go#util#ShellError() != 0
call s:Error("Can't find import: " . path . ":" . out)
endif
endif
let exists = go#tool#Exists(path)
if exists == -1
call s:Error("Can't find import: " . path)
try
let l:json = json_decode(l:out)
catch
call go#util#EchoError(l:out)
return
endif

" Extract any site prefix (e.g. github.com/).
" If other imports with the same prefix are grouped separately,
" we will add this new import with them.
" Only up to and including the first slash is used.
let siteprefix = matchstr(path, "^[^/]*/")

let qpath = '"' . path . '"'
if a:localname != ''
let qlocalpath = a:localname . ' ' . qpath
else
let qlocalpath = qpath
endif
let indentstr = 0
let packageline = -1 " Position of package name statement
let appendline = -1 " Position to introduce new import
let deleteline = -1 " Position of line with existing import
let linesdelta = 0 " Lines added/removed

" Find proper place to add/remove import.
let line = 0
while line <= line('$')
let linestr = getline(line)

if linestr =~# '^package\s'
let packageline = line
let appendline = line

elseif linestr =~# '^import\s\+('
let appendstr = qlocalpath
let indentstr = 1
let appendline = line
let firstblank = -1
let lastprefix = ""
while line <= line("$")
let line = line + 1
let linestr = getline(line)
let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\S*\s*\)"\(.\+\)"\)')
if empty(m)
if siteprefix == "" && a:enabled
" must be in the first group
break
endif
" record this position, but keep looking
if firstblank < 0
let firstblank = line
endif
continue
endif
if m[1] == ')'
" if there's no match, add it to the first group
if appendline < 0 && firstblank >= 0
let appendline = firstblank
endif
break
endif
let lastprefix = matchstr(m[4], "^[^/]*/")
if a:localname != '' && m[3] != ''
let qlocalpath = printf('%-' . (len(m[3])-1) . 's %s', a:localname, qpath)
endif
let appendstr = m[2] . qlocalpath
let indentstr = 0
if m[4] == path
let appendline = -1
let deleteline = line
break
elseif m[4] < path
" don't set candidate position if we have a site prefix,
" we've passed a blank line, and this doesn't share the same
" site prefix.
if siteprefix == "" || firstblank < 0 || match(m[4], "^" . siteprefix) >= 0
let appendline = line
endif
elseif siteprefix != "" && match(m[4], "^" . siteprefix) >= 0
" first entry of site group
let appendline = line - 1
break
endif
endwhile
break

elseif linestr =~# '^import '
if appendline == packageline
let appendstr = 'import ' . qlocalpath
let appendline = line - 1
endif
let m = matchlist(linestr, '^import\(\s\+\)\(\S*\s*\)"\(.\+\)"')
if !empty(m)
if m[3] == path
let appendline = -1
let deleteline = line
break
endif
if m[3] < path
let appendline = line
endif
if a:localname != '' && m[2] != ''
let qlocalpath = printf("%s %" . len(m[2])-1 . "s", a:localname, qpath)
endif
let appendstr = 'import' . m[1] . qlocalpath
endif

elseif linestr =~# '^\(var\|const\|type\|func\)\>'
break

endif
let line = line + 1
endwhile

" Append or remove the package import, as requested.
if a:enabled
if deleteline != -1
call s:Error(qpath . ' already being imported')
elseif appendline == -1
call s:Error('No package line found')
endtry

let l:code = split(l:json['code'], "\n")
let l:view = winsaveview()
let l:lastline = line('$')
try
" No imports yet; go to the corect line.
if l:json['end'] is 0
exe 'normal! ' . l:json['start'] . 'go'
" Remove existing imports.
else
if appendline == packageline
call append(appendline + 0, '')
call append(appendline + 1, 'import (')
call append(appendline + 2, ')')
let appendline += 2
let linesdelta += 3
let appendstr = qlocalpath
let indentstr = 1
endif
call append(appendline, appendstr)
execute appendline + 1
if indentstr
execute 'normal! >>'
endif
let linesdelta += 1
silent exe 'normal! ' . l:json['start'] . 'gov' . l:json['end'] . 'gox'
endif
else
if deleteline == -1
call s:Error(qpath . ' not being imported')
else
execute deleteline . 'd'
let linesdelta -= 1

if getline(deleteline-1) =~# '^import\s\+(' && getline(deleteline) =~# '^)'
" Delete empty import block
let deleteline -= 1
execute deleteline . "d"
execute deleteline . "d"
let linesdelta -= 2
endif

if getline(deleteline) == '' && getline(deleteline - 1) == ''
" Delete spacing for removed line too.
execute deleteline . "d"
let linesdelta -= 1
endif
" Add imports.
call setline('.', l:code[0])
call append('.', l:code[1:])
finally
" Adjust view for any changes.
let l:view.lnum += line('$') - l:lastline
let l:view.topline += line('$') - l:lastline
if l:view.topline < 0
let l:view.topline = 0
endif
endif

" Adjust view for any changes.
let view.lnum += linesdelta
let view.topline += linesdelta
if view.topline < 0
let view.topline = 0
endif

" Put buffer back where it was.
call winrestview(view)

call winrestview(l:view)
endtry
endfunction


function! s:Error(s) abort
echohl Error | echo a:s | echohl None
endfunction


" vim: sw=2 ts=2 et
66 changes: 66 additions & 0 deletions autoload/go/import_test.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
func! Test_import() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ 'import "fmt"',
\ '',
\ 'func main() { fmt.Println("foo") }'])

call go#import#SwitchImport(1, '', 'errors', 0)
call gotest#assert_buffer(0, [
\ 'package a',
\ 'import (',
\ ' "fmt"',
\ ' "errors"',
\ ')',
\ '',
\ 'func main() { fmt.Println("foo") }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc

func! Test_import_new() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ 'func main() { fmt.Println("foo") }'])

call go#import#SwitchImport(1, '', 'errors', 0)
call gotest#assert_buffer(0, [
\ 'package a',
\ 'import "errors"',
\ 'func main() { fmt.Println("foo") }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc

func! Test_drop() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ 'import (',
\ ' "fmt"',
\ ' "errors"',
\ ')',
\ '',
\ 'func main() { fmt.Println("foo") }'])

call go#import#SwitchImport(0, '', 'errors', 0)
call gotest#assert_buffer(0, [
\ 'package a',
\ '',
\ 'import "fmt"',
\ '',
\ 'func main() { fmt.Println("foo") }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc


" vim: sw=2 ts=2 et
4 changes: 2 additions & 2 deletions ftplugin/go/commands.vim
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ command! -nargs=0 GoImports call go#fmt#Format(1)
command! -nargs=0 GoAsmFmtAutoSaveToggle call go#asmfmt#ToggleAsmFmtAutoSave()

" -- import
command! -nargs=? -complete=customlist,go#package#Complete GoDrop call go#import#SwitchImport(0, '', <f-args>, '')
command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', <f-args>, '<bang>')
command! -nargs=? -complete=customlist,go#package#Complete GoDrop call go#import#SwitchImport(0, '', <f-args>, '')
command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', <f-args>, '<bang>')
command! -nargs=* -bang -complete=customlist,go#package#Complete GoImportAs call go#import#SwitchImport(1, <f-args>, '<bang>')

" -- linters
Expand Down
1 change: 1 addition & 0 deletions plugin/go.vim
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ let s:packages = {
\ 'gocode': ['github.com/nsf/gocode', {'windows': '-ldflags -H=windowsgui'}],
\ 'godef': ['github.com/rogpeppe/godef'],
\ 'gogetdoc': ['github.com/zmb3/gogetdoc'],
\ 'goimport': ['arp242.net/goimport'],
\ 'goimports': ['golang.org/x/tools/cmd/goimports'],
\ 'golint': ['github.com/golang/lint/golint'],
\ 'gometalinter': ['github.com/alecthomas/gometalinter'],
Expand Down