From 9e0327ed34e9af817b2e002b3efe7e89fe34bc21 Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Wed, 22 Apr 2020 12:20:39 +0800 Subject: [PATCH] Add basic :GoModReplace --- autoload/gopher/go.vim | 17 ++++++++++++ autoload/gopher/mod.vim | 61 +++++++++++++++++++++++++++++++++++++++++ autoload/gopher/pkg.vim | 29 ++++++++++++++++++++ doc/gopher.txt | 6 ++-- ftplugin/go.vim | 2 ++ ftplugin/gomod.vim | 2 ++ 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 autoload/gopher/mod.vim diff --git a/autoload/gopher/go.vim b/autoload/gopher/go.vim index ad9d6819..44ef1eb8 100644 --- a/autoload/gopher/go.vim +++ b/autoload/gopher/go.vim @@ -31,6 +31,23 @@ fun! gopher#go#module() abort return l:out endfun +" Get the go.mod file location, or -1 if there is none. +fun! gopher#go#gomod() abort + let l:dir = expand('%:p:h') + while 1 + " len() check is for Windows; not sure how that's represented. + if l:dir is# '/' || len(l:dir) <= 4 + return -1 + endif + + if filereadable(l:dir + '/go.mod') + return l:dir + '/go.mod' + endif + + let l:dir = fnamemodify(l:dir, ':h') + endwhile +endfun + " Get the package path for the file in the current buffer. fun! gopher#go#package() abort let [l:out, l:err] = gopher#system#run(['go', 'list', './' . expand('%:h')]) diff --git a/autoload/gopher/mod.vim b/autoload/gopher/mod.vim new file mode 100644 index 00000000..361a4d0b --- /dev/null +++ b/autoload/gopher/mod.vim @@ -0,0 +1,61 @@ +" mod.vim: Utilities for working with go.mod files. + +" Add or remove replace directives. +fun! gopher#mod#replace(...) + " TODO: get arguments. + let l:mod = '' + let l:path = '' + + if l:mod is# '' + " TODO: be a bit smarter in go.mod file, since now 'require' is considered a + " module as well. + let l:mod = expand('') + endif + + if &filetype is# 'go' + let l:gomod = gopher#go#gomod() + if l:gomod is# '' + return gopher#error('No go.mod file found.') + endif + + if l:mod is# '' + " On identifier: fmt.Printf("..") + if l:mod =~# '\.' + let l:mod = split(l:mod, '.')[0] + let l:resolve = gopher#pkg#resolve() + if l:resolve is# '' + return gopher#error('Unknown package: %s', l:mod) + endif + + let l:mod = l:resolve + " On package: "fmt". + else + let l:mod = trim(l:mod, '"') + endif + endif + endif + + if l:mod is# '' + return gopher#error('Not a package: %s', l:mod) + endif + + if l:path is# '' + let l:path = '../' . fnamemodify(l:mod, ':t') + endif + + let l:line = printf('replace %s => %s', l:mod, l:path) + + " TODO: :GoModReplace on something that already exists should remove it. + " TODO: Don't add duplicate replaces. + + " Place before first require or replace. + for l:i in range(1, line('$')) + if getline(l:i) =~# '\v^(require|replace)' + call append(l:i-1, [l:line, '']) + return + endif + endfor + + " Nothing in the file yet. + call append('$', l:line) +endfun diff --git a/autoload/gopher/pkg.vim b/autoload/gopher/pkg.vim index b4e0e750..7c2ee8a7 100644 --- a/autoload/gopher/pkg.vim +++ b/autoload/gopher/pkg.vim @@ -37,3 +37,32 @@ fun! gopher#pkg#list_interfaces(pkg) abort return l:out endfun + +" List all imports for the current buffer. +fun! gopher#pkg#list_imports() abort + let [l:out, l:err] = gopher#system#run(['go', 'list', '-f', '{{.Imports}}', expand('%')]) + if l:err + call gopher#error(l:out) + return [] + endif + return split(trim(l:out, '[]'), ' ') +endfun + +" Resolve a package name to the full import path for the current buffer. +" +" fmt → fmt +" http → net/http +" pq → github.com/lib/pq +" +" Returns empty string if the import is not found. +fun! gopher#pkg#resolve(pkg) abort + let l:slash = '/' + a:pkg + + for l:import in gopher#pkg#list_imports() + if l:import is# a:pkg || gopher#str#has_suffix(l:import, l:slash) + return l:import + endif + endfor + + return '' +endfun diff --git a/doc/gopher.txt b/doc/gopher.txt index 77eff07b..aa3fad4e 100644 --- a/doc/gopher.txt +++ b/doc/gopher.txt @@ -379,10 +379,10 @@ COMMANDS *gopher-commands* *g:gopher_gorename_flags* = `[]` List of flags to add to the `gorename` command. -:GoReplace [module] [path] *:GoReplace* +:GoModReplace [module] [path] *:GoModReplace* - Add a `replace` directive in the project's go.mod. This currently only - works in the `go.mod` buffer. + Add a `replace` directive in the project's go.mod. This works from any + `go` or the `go.mod` buffer. [module] is a module name; if it's omitted the module on the current line is used. diff --git a/ftplugin/go.vim b/ftplugin/go.vim index 375d6267..528b2968 100644 --- a/ftplugin/go.vim +++ b/ftplugin/go.vim @@ -109,3 +109,5 @@ command! -nargs=* -complete=customlist,gopher#import#complete GoImport command! -nargs=* -range -complete=customlist,gopher#tags#complete GoTags call gopher#tags#modify(, , , ) command! -nargs=+ -complete=customlist,gopher#guru#complete GoGuru call gopher#guru#do() command! -nargs=? -complete=customlist,gopher#rename#complete GoRename call gopher#rename#do() + +command! -nargs=* GoModReplace call gopher#mod#replace() diff --git a/ftplugin/gomod.vim b/ftplugin/gomod.vim index 45224dd7..f793b765 100644 --- a/ftplugin/gomod.vim +++ b/ftplugin/gomod.vim @@ -9,3 +9,5 @@ call gopher#init#version() setlocal noexpandtab compiler go + +command! -nargs=* GoModReplace call gopher#mod#replace()