Skip to content

Commit

Permalink
Merge pull request #165 from motemen/support-gomod
Browse files Browse the repository at this point in the history
Support Go modules
  • Loading branch information
itchyny authored Jan 11, 2020
2 parents a75125f + 5b99175 commit 81ba2d7
Show file tree
Hide file tree
Showing 12 changed files with 498 additions and 36 deletions.
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ To quit the session, type `Ctrl-D` or use `:q` command.
- Evaluates any expressions, statements and function declarations
- No "evaluated but not used" errors
- Code completion (requires [gocode](https://github.com/mdempsky/gocode))
- Pretty printing ([pp](https://github.com/k0kubun/pp) or
[spew](https://github.com/davecgh/go-spew) recommended)
- Showing documents
- Auto-importing (`gore -autoimport`)

Expand Down Expand Up @@ -56,7 +54,6 @@ Also recommended:

```sh
go get -u github.com/mdempsky/gocode # for code completion
go get -u github.com/k0kubun/pp # or github.com/davecgh/go-spew/spew
```

## FAQ/Caveats
Expand All @@ -65,8 +62,6 @@ go get -u github.com/k0kubun/pp # or github.com/davecgh/go-spew/spew
while installing gore, run `go get -u golang.org/x/tools/go/types`.
- gore runs code using `go run` for each input. If you have entered
time-consuming code, gore will run it for each input and take some time.
- To import a local package, first fetch it with `go get my/package`,
then `:import` will work properly.

## License

Expand Down
63 changes: 61 additions & 2 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go/types"

"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/packages"
)

type command struct {
Expand Down Expand Up @@ -104,7 +105,8 @@ func actionImport(s *Session, arg string) error {
path := strings.Trim(arg, `"`)

// check if the package specified by path is importable
_, err := s.types.Importer.Import(path)
_, err := packages.Load(&packages.Config{Dir: s.tempDir}, path)
// the importer cannot check go mod package, skip
if err != nil {
return err
}
Expand All @@ -123,6 +125,59 @@ func completeImport(s *Session, prefix string) []string {
p := strings.LastIndexFunc(prefix, unicode.IsSpace) + 1

d, fn := path.Split(prefix[p:])

// complete candidates from the current module
if modules, err := goListAll(); err == nil {
for _, m := range modules {

matchPath := func(fn string) bool {
if len(fn) < 2 {
return false
}
for _, s := range strings.Split(m.Path, "/") {
if strings.HasPrefix(s, fn) || strings.HasPrefix(strings.TrimPrefix(s, "go-"), fn) {
return true
}
}
return false
}
if strings.HasPrefix(m.Path, prefix[p:]) || d == "" && matchPath(fn) {
result = append(result, prefix[:p]+m.Path)
seen[m.Path] = true
continue
}

if strings.HasPrefix(d, m.Path) {
dir := filepath.Join(m.Dir, strings.Replace(d, m.Path, "", 1))
if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
continue
}
entries, err := ioutil.ReadDir(dir)
if err != nil {
continue
}
for _, fi := range entries {
if !fi.IsDir() {
continue
}
name := fi.Name()
if skipCompleteDir(name) {
continue
}
if strings.HasPrefix(name, fn) {
r := path.Join(d, name)
if !seen[r] {
result = append(result, prefix[:p]+r)
seen[r] = true
}
}
}
}

}
}

// complete candidates from GOPATH/src/
for _, srcDir := range build.Default.SrcDirs() {
dir := filepath.Join(srcDir, d)

Expand All @@ -144,7 +199,7 @@ func completeImport(s *Session, prefix string) []string {
}

name := fi.Name()
if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" {
if skipCompleteDir(name) {
continue
}

Expand Down Expand Up @@ -178,6 +233,10 @@ func completeImport(s *Session, prefix string) []string {
return result
}

func skipCompleteDir(dir string) bool {
return strings.HasPrefix(dir, ".") || strings.HasPrefix(dir, "_") || dir == "testdata"
}

func completeDoc(s *Session, prefix string) []string {
pos, cands, err := s.completeCode(prefix, len(prefix), false)
if err != nil {
Expand Down
40 changes: 40 additions & 0 deletions complete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,46 @@ func TestSession_completeWord(t *testing.T) {
assert.Equal(t, []string{"testing", "text", "time"}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord("::i gor", 7)
assert.Equal(t, "::i ", pre)
assert.Equal(t, []string{"github.com/motemen/gore"}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":i gore", 7)
assert.Equal(t, ":i ", pre)
assert.Equal(t, []string{"github.com/motemen/gore"}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":i to", 5)
assert.Equal(t, ":i ", pre)
assert.Equal(t, []string{"golang.org/x/tools"}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":i qu", 5)
assert.Equal(t, ":i ", pre)
assert.Equal(t, []string{"github.com/motemen/go-quickfix"}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":i go-qu", 8)
assert.Equal(t, ":i ", pre)
assert.Equal(t, []string{"github.com/motemen/go-quickfix"}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":i go-", 6)
assert.Equal(t, ":i ", pre)
assert.Equal(t, []string{
"github.com/davecgh/go-spew", "github.com/mattn/go-runewidth",
"github.com/motemen/go-quickfix", "github.com/pmezard/go-difflib",
}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":i mot", 6)
assert.Equal(t, ":i ", pre)
assert.Equal(t, []string{
"github.com/motemen/gore", "github.com/motemen/go-quickfix",
}, cands)
assert.Equal(t, post, "")

pre, cands, post = s.completeWord(":c", 2)
assert.Equal(t, "", pre)
assert.Equal(t, []string{":clear"}, cands)
Expand Down
3 changes: 3 additions & 0 deletions errfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ func replaceErrMsg(p []byte) []byte {
if bytes.HasPrefix(p, []byte("# command-line-arguments")) {
return nil
}
if cs := "build command-line-arguments: "; bytes.HasPrefix(p, []byte(cs)) {
return p[len(cs):]
}
if bytes.HasPrefix(p, []byte(`warning: pattern "all" matched no module dependencies`)) {
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions errfilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func TestErrFilter(t *testing.T) {
},
{
"command-line-arguments",
"# command-line-arguments foo\nbar\n# command-line-arguments baz\nqux",
"bar\nqux",
"# command-line-arguments foo\nbar\nbuild command-line-arguments: baz\nqux",
"bar\nbaz\nqux",
},
{
"gore_session.go",
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module github.com/motemen/gore

go 1.12
go 1.13

require (
github.com/mattn/go-runewidth v0.0.7 // indirect
github.com/motemen/go-quickfix v0.0.0-20160413151302-5c522febc679
github.com/motemen/go-quickfix v0.0.0-20200103095207-27e35cdee537
github.com/peterh/liner v1.1.0
github.com/stretchr/testify v1.3.0
golang.org/x/text v0.3.2
golang.org/x/tools v0.0.0-20191212051200-825cb0626375
golang.org/x/tools v0.0.0-20191230220329-2aa90c603ae3
)
14 changes: 9 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8Bz
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/motemen/go-quickfix v0.0.0-20160413151302-5c522febc679 h1:UkBp8kp/ChZOCNV1nyiKpfufVkTdYU32saV0Y6iQgKk=
github.com/motemen/go-quickfix v0.0.0-20160413151302-5c522febc679/go.mod h1:MIkbIO1YK5LNJNfBzYUstnQr/eQTEFKzlBuRsOkknUI=
github.com/motemen/go-quickfix v0.0.0-20200103095207-27e35cdee537 h1:/2+Lfs8QNwDN7X8lxgPGi2gwCRpSUEyzRxKYe0lejd4=
github.com/motemen/go-quickfix v0.0.0-20200103095207-27e35cdee537/go.mod h1:gvrjti7wRl8ugSgQE1qdEJFNyZt0S0KvkIt91ZTh/dM=
github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=
github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -14,13 +14,17 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191212051200-825cb0626375 h1:hrBCt+pkb1irxFjg/GeadSn24gLxi5/Z861I53OkQbc=
golang.org/x/tools v0.0.0-20191212051200-825cb0626375/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/tools v0.0.0-20191230220329-2aa90c603ae3 h1:2+KluhQfJ1YhW+TB1KrISS2SfiG1pLEoseB0D4VF/bo=
golang.org/x/tools v0.0.0-20191230220329-2aa90c603ae3/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
124 changes: 124 additions & 0 deletions gomod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package gore

import (
"bytes"
"encoding/json"
"go/build"
"io"
"io/ioutil"
"net"
"net/url"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
)

func (s *Session) initGoMod() error {
tempModule := filepath.Base(s.tempDir)
goModPath := filepath.Join(s.tempDir, "go.mod")
directives := listModuleDirectives()
mod := "module " + tempModule + "\n" + strings.Join(directives, "\n")
return ioutil.WriteFile(goModPath, []byte(mod), 0644)
}

func listModuleDirectives() []string {
var directives []string
for i, pp := range printerPkgs {
if pp.path == "fmt" {
continue
}
// Check local module caches.
found := lookupGoModule(pp.path, pp.version)
if found {
for _, r := range pp.requires {
if !lookupGoModule(r.path, r.version) {
found = false
break
}
}
}
if found || canAccessGoproxy() {
// Specifying the version of the printer package improves startup
// performance by skipping module version fetching. Also allows to
// use gore in offline environment.
directives = append(directives, "require "+pp.path+" "+pp.version)
for _, r := range pp.requires {
directives = append(directives, "require "+r.path+" "+r.version)
}
} else {
// If there is no module cache and no network connection, use fmt package.
printerPkgs = printerPkgs[i+1:]
}
// only the first printer is checked (assuming printerPkgs[1] is fmt)
break
}
modules, err := goListAll()
if err != nil {
return directives
}
for _, m := range modules {
if m.Main || m.Replace != nil {
directives = append(directives, "replace "+m.Path+" => "+strconv.Quote(m.Dir))
}
}
return directives
}

type goModule struct {
Path, Dir, Version string
Main bool
Replace *goModule
}

func goListAll() ([]*goModule, error) {
cmd := exec.Command("go", "list", "-json", "-m", "all")
out, err := cmd.Output()
if err != nil {
return nil, err
}
d := json.NewDecoder(bytes.NewReader(out))
var ms []*goModule
for {
m := new(goModule)
if err := d.Decode(m); err != nil {
if err == io.EOF {
return ms, nil
}
return nil, err
}
ms = append(ms, m)
}
}

func lookupGoModule(pkg, version string) bool {
modDir := filepath.Join(build.Default.GOPATH, "pkg/mod", pkg+"@"+version)
fi, err := os.Stat(modDir)
return err == nil && fi.IsDir()
}

func canAccessGoproxy() bool {
var host string
if url, err := url.Parse(getGoproxy()); err != nil {
host = "proxy.golang.org"
} else {
host = url.Hostname()
}
addr := net.JoinHostPort(host, "80")
dialer := net.Dialer{Timeout: 5 * time.Second}
conn, err := dialer.Dial("tcp", addr)
if err != nil {
return false
}
defer conn.Close()
return true
}

func getGoproxy() string {
if goproxy := os.Getenv("GOPROXY"); goproxy != "" {
return goproxy
}
return "https://proxy.golang.org/"
}
2 changes: 2 additions & 0 deletions quickfix.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ quickFixAttempt:
Fset: s.fset,
Files: files,
TypeInfo: &s.typeInfo,
Dir: s.tempDir,
}
_, err := config.QuickFixOnce()
if err == nil {
Expand Down Expand Up @@ -86,6 +87,7 @@ quickFixAttempt:
}

debugf("quickFix :: give up: %#v", err)
break
}

return nil
Expand Down
Loading

0 comments on commit 81ba2d7

Please sign in to comment.