Skip to content
This repository has been archived by the owner on Feb 17, 2021. It is now read-only.

Commit

Permalink
merge from upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
stamblerre committed Oct 12, 2018
2 parents 568f8ec + ac3d06a commit bef7cf9
Show file tree
Hide file tree
Showing 18 changed files with 165 additions and 45 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ language: go
go:
- 1.9.x
- 1.10.x
- 1.11.x
- master
1 change: 1 addition & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func cmdAutoComplete(c *rpc.Client) {
// For now, assume same environment for server and client.
req.Context = &suggest.PackedContext{}
req.Builtin = *g_builtin
req.IgnoreCase = *g_ignore_case

var res AutoCompleteReply
var err error
Expand Down
17 changes: 9 additions & 8 deletions gocode.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
)

var (
g_is_server = flag.Bool("s", false, "run a server instead of a client")
g_format = flag.String("f", "nice", "output format (vim | emacs | nice | csv | json)")
g_input = flag.String("in", "", "use this file instead of stdin input")
g_sock = flag.String("sock", defaultSocketType, "socket type (unix | tcp | none)")
g_addr = flag.String("addr", "127.0.0.1:37373", "address for tcp socket")
g_debug = flag.Bool("debug", false, "enable server-side debug mode")
g_source = flag.Bool("source", false, "use source importer")
g_builtin = flag.Bool("builtin", false, "propose builtin objects")
g_is_server = flag.Bool("s", false, "run a server instead of a client")
g_format = flag.String("f", "nice", "output format (vim | emacs | nice | csv | json)")
g_input = flag.String("in", "", "use this file instead of stdin input")
g_sock = flag.String("sock", defaultSocketType, "socket type (unix | tcp | none)")
g_addr = flag.String("addr", "127.0.0.1:37373", "address for tcp socket")
g_debug = flag.Bool("debug", false, "enable server-side debug mode")
g_source = flag.Bool("source", false, "use source importer")
g_builtin = flag.Bool("builtin", false, "propose builtin objects")
g_ignore_case = flag.Bool("ignore-case", false, "do case-insensitive matching")
)

func getSocketPath() string {
Expand Down
26 changes: 24 additions & 2 deletions internal/suggest/candidate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package suggest

import (
"fmt"
"go/ast"
"go/types"
"sort"
"strings"
Expand Down Expand Up @@ -77,10 +78,12 @@ func classifyObject(obj types.Object) string {
type candidateCollector struct {
exact []types.Object
badcase []types.Object
imports []*ast.ImportSpec
localpkg *types.Package
partial string
filter objectFilter
builtin bool
ignoreCase bool
}

func (b *candidateCollector) getCandidates() []Candidate {
Expand Down Expand Up @@ -162,6 +165,26 @@ func (b *candidateCollector) qualify(pkg *types.Package) string {
if pkg == b.localpkg {
return ""
}

// the *types.Package we are asked to qualify might _not_ be imported
// by the file in which we are asking for candidates. Hence... we retain
// the default of pkg.Name() as the qualifier

for _, i := range b.imports {
// given the import spec has been correctly parsed (by virtue of
// its existence) we can safely byte-index the path value knowing
// that len("\"") == 1
iPath := i.Path.Value[1 : len(i.Path.Value)-1]

if iPath == pkg.Path() {
if i.Name != nil && i.Name.Name != "." {
return i.Name.Name
} else {
return pkg.Name()
}
}
}

return pkg.Name()
}

Expand All @@ -180,8 +203,7 @@ func (b *candidateCollector) appendObject(obj types.Object) {
if b.filter != nil && !b.filter(obj) {
return
}

if b.filter != nil || strings.HasPrefix(obj.Name(), b.partial) {
if !b.ignoreCase && (b.filter != nil || strings.HasPrefix(obj.Name(), b.partial)) {
b.exact = append(b.exact, obj)
} else if strings.HasPrefix(strings.ToLower(obj.Name()), strings.ToLower(b.partial)) {
b.badcase = append(b.badcase, obj)
Expand Down
62 changes: 40 additions & 22 deletions internal/suggest/suggest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import (
"go/token"
"go/types"
"os"
"strings"
"sync"

"github.com/stamblerre/gocode/internal/lookdot"
"golang.org/x/tools/go/packages"
)

type Config struct {
Logf func(fmt string, args ...interface{})
Context *PackedContext
Builtin bool
Logf func(fmt string, args ...interface{})
Context *PackedContext
Builtin bool
IgnoreCase bool
}

// Copied from go/packages.
Expand Down Expand Up @@ -49,18 +51,20 @@ func (c *Config) Suggest(filename string, data []byte, cursor int) ([]Candidate,
return nil, 0
}

fset, pos, pkg := c.analyzePackage(filename, data, cursor)
fset, pos, pkg, imports := c.analyzePackage(filename, data, cursor)
if pkg == nil {
return nil, 0
}
scope := pkg.Scope().Innermost(pos)

ctx, expr, partial := deduceCursorContext(data, cursor)
b := candidateCollector{
localpkg: pkg,
partial: partial,
filter: objectFilters[partial],
builtin: ctx != selectContext && c.Builtin,
localpkg: pkg,
imports: imports,
partial: partial,
filter: objectFilters[partial],
builtin: ctx != selectContext && c.Builtin,
ignoreCase: c.IgnoreCase,
}

switch ctx {
Expand Down Expand Up @@ -99,29 +103,30 @@ func (c *Config) Suggest(filename string, data []byte, cursor int) ([]Candidate,
return res, len(partial)
}

func sameFile(filename1, filename2 string) bool {
finfo1, err := os.Stat(filename1)
if err != nil {
return false
func (c *Config) analyzePackage(filename string, data []byte, cursor int) (*token.FileSet, token.Pos, *types.Package, []*ast.ImportSpec) {
var tags string
parsed, _ := parser.ParseFile(token.NewFileSet(), filename, data, parser.ParseComments)
if parsed != nil && len(parsed.Comments) > 0 {
buildTagText := parsed.Comments[0].Text()
if strings.HasPrefix(buildTagText, "+build ") {
tags = strings.TrimPrefix(buildTagText, "+build ")
}
}
finfo2, err := os.Stat(filename2)
if err != nil {
return false
if suffix := buildConstraint(filename); suffix != "" {
tags = suffix
}
return os.SameFile(finfo1, finfo2)
}

func (c *Config) analyzePackage(filename string, data []byte, cursor int) (*token.FileSet, token.Pos, *types.Package) {
var fileAST *ast.File
var pos token.Pos
var posMu sync.Mutex // guards pos in ParseFile

cfg := &packages.Config{
Mode: packages.LoadSyntax,
Env: c.Context.Env,
Dir: c.Context.Dir,
BuildFlags: c.Context.BuildFlags,
BuildFlags: append(c.Context.BuildFlags, fmt.Sprintf("-tags=%s", tags)),
Tests: true,
ParseFile: func(fset *token.FileSet, parseFilename string) (*ast.File, error) {
ParseFile: func(fset *token.FileSet, parseFilename string, _ []byte) (*ast.File, error) {
var src interface{}
var filePos token.Pos
mode := parser.DeclarationErrors
Expand All @@ -137,6 +142,7 @@ func (c *Config) analyzePackage(filename string, data []byte, cursor int) (*toke
return nil, err
}
if sameFile(filename, parseFilename) {
fileAST = file
filePos = fset.File(file.Pos()).Pos(cursor)
if filePos == token.NoPos {
return nil, fmt.Errorf("no position for cursor in %s", parseFilename)
Expand All @@ -159,11 +165,23 @@ func (c *Config) analyzePackage(filename string, data []byte, cursor int) (*toke
}
pkgs, _ := packages.Load(cfg, fmt.Sprintf("contains:%v", filename))
if len(pkgs) <= 0 { // ignore errors
return nil, token.NoPos, nil
return nil, token.NoPos, nil, nil
}
pkg := pkgs[0]

return pkg.Fset, pos, pkg.Types
return pkg.Fset, pos, pkg.Types, fileAST.Imports
}

func sameFile(filename1, filename2 string) bool {
finfo1, err := os.Stat(filename1)
if err != nil {
return false
}
finfo2, err := os.Stat(filename2)
if err != nil {
return false
}
return os.SameFile(finfo1, finfo2)
}

func (c *Config) fieldNameCandidates(typ types.Type, b *candidateCollector) {
Expand Down
15 changes: 15 additions & 0 deletions internal/suggest/suggest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"

Expand All @@ -19,6 +20,11 @@ func TestRegress(t *testing.T) {
}

for _, testDir := range testDirs {
// Skip test.0011 for Go <= 1.11 because a method was added to reflect.Value.
// TODO(rstambler): Change this when Go 1.12 comes out.
if !strings.HasPrefix(runtime.Version(), "devel") && strings.HasSuffix(testDir, "test.0011") {
continue
}
testDir := testDir // capture
name := strings.TrimPrefix(testDir, "testdata/")
t.Run(name, func(t *testing.T) {
Expand Down Expand Up @@ -81,3 +87,12 @@ func testRegress(t *testing.T, testDir string) {
return
}
}

func contains(haystack []string, needle string) bool {
for _, x := range haystack {
if needle == x {
return true
}
}
return false
}
28 changes: 26 additions & 2 deletions internal/suggest/testdata/test.0065/out.expected
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
Found 1 candidates:
func New(text string) error
Found 25 candidates:
func Errorf(format string, a ...interface{}) error
func Fprint(w banana.Writer, a ...interface{}) (n int, err error)
func Fprintf(w banana.Writer, format string, a ...interface{}) (n int, err error)
func Fprintln(w banana.Writer, a ...interface{}) (n int, err error)
func Fscan(r banana.Reader, a ...interface{}) (n int, err error)
func Fscanf(r banana.Reader, format string, a ...interface{}) (n int, err error)
func Fscanln(r banana.Reader, a ...interface{}) (n int, err error)
func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)
func Scan(a ...interface{}) (n int, err error)
func Scanf(format string, a ...interface{}) (n int, err error)
func Scanln(a ...interface{}) (n int, err error)
func Sprint(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string
func Sprintln(a ...interface{}) string
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
type Formatter interface
type GoStringer interface
type ScanState interface
type Scanner interface
type State interface
type Stringer interface
9 changes: 5 additions & 4 deletions internal/suggest/testdata/test.0065/test.go.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package p

import "errors"
import (
"fmt"
banana "io"
)

func f() {
errors.@
}
var _ = fmt.@
1 change: 1 addition & 0 deletions internal/suggest/testdata/test.0066/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IgnoreCase": false}
2 changes: 2 additions & 0 deletions internal/suggest/testdata/test.0066/out.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Found 1 candidates:
func FuncX()
8 changes: 8 additions & 0 deletions internal/suggest/testdata/test.0066/test.go.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p

func FuncX() {}
func funcY() {}

func f() {
Fun@
}
1 change: 1 addition & 0 deletions internal/suggest/testdata/test.0067/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IgnoreCase": false}
2 changes: 2 additions & 0 deletions internal/suggest/testdata/test.0067/out.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Found 1 candidates:
func funcY()
8 changes: 8 additions & 0 deletions internal/suggest/testdata/test.0067/test.go.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p

func FuncX() {}
func funcY() {}

func f() {
fun@
}
1 change: 1 addition & 0 deletions internal/suggest/testdata/test.0068/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IgnoreCase": true}
3 changes: 3 additions & 0 deletions internal/suggest/testdata/test.0068/out.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Found 2 candidates:
func FuncX()
func funcY()
8 changes: 8 additions & 0 deletions internal/suggest/testdata/test.0068/test.go.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p

func FuncX() {}
func funcY() {}

func f() {
fun@
}
17 changes: 10 additions & 7 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ type Server struct {
}

type AutoCompleteRequest struct {
Context *suggest.PackedContext
Filename string
Data []byte
Cursor int
Builtin bool
Filename string
Data []byte
Cursor int
Context gbimporter.PackedContext
Source bool
Builtin bool
IgnoreCase bool
}

type AutoCompleteReply struct {
Expand Down Expand Up @@ -85,8 +87,9 @@ func (s *Server) AutoComplete(req *AutoCompleteRequest, res *AutoCompleteReply)
}
now := time.Now()
cfg := suggest.Config{
Builtin: req.Builtin,
Context: req.Context,
Importer: gbimporter.New(&req.Context, req.Filename, underlying),
Builtin: req.Builtin,
IgnoreCase: req.IgnoreCase,
}
if *g_debug {
cfg.Logf = log.Printf
Expand Down

0 comments on commit bef7cf9

Please sign in to comment.