Update gocode in vendor

This commit is contained in:
Liang Ding 2018-03-13 13:05:45 +08:00
parent bfd836c6fc
commit 0a51a03f35
23 changed files with 691 additions and 221 deletions

View File

@ -1,20 +1,13 @@
FROM golang:latest FROM golang:latest
MAINTAINER Liang Ding <d@b3log.org> MAINTAINER Liang Ding <d@b3log.org>
ENV GOROOT /usr/local/go ADD . /go/src/github.com/b3log/wide
ADD vendor/ /go/src/
RUN apt-get update && apt-get install bzip2 zip unzip && cp -r /usr/local/go /usr/local/gobt
ENV GOROOT_BOOTSTRAP=/usr/local/gobt
ADD . /wide/gogogo/src/github.com/b3log/wide
ADD vendor/* /go/src/
RUN go install github.com/visualfc/gotools github.com/nsf/gocode github.com/bradfitz/goimports RUN go install github.com/visualfc/gotools github.com/nsf/gocode github.com/bradfitz/goimports
RUN useradd wide && useradd runner RUN useradd wide && useradd runner
ENV GOPATH /wide/gogogo WORKDIR /go/src/github.com/b3log/wide
WORKDIR /wide/gogogo/src/github.com/b3log/wide
RUN go build -v RUN go build -v
EXPOSE 7070 EXPOSE 7070

View File

@ -8,7 +8,7 @@ set -e
echo "mode: count" > profile.cov echo "mode: count" > profile.cov
# Standard go tooling behavior is to ignore dirs with leading underscors # Standard go tooling behavior is to ignore dirs with leading underscors
for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d); for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -not -path './vendor*' -type d);
do do
if ls $dir/*.go &> /dev/null; then if ls $dir/*.go &> /dev/null; then
go test -covermode=count -coverprofile=$dir/profile.tmp $dir go test -covermode=count -coverprofile=$dir/profile.tmp $dir

View File

@ -151,7 +151,7 @@ You can change all available options using `gocode set` command. The config file
- *propose-builtins* - *propose-builtins*
A boolean option. If **true**, gocode will add built-in types, functions and constants to an autocompletion proposals. Default: **false**. A boolean option. If **true**, gocode will add built-in types, functions and constants to autocompletion proposals. Default: **false**.
- *lib-path* - *lib-path*
@ -173,6 +173,22 @@ You can change all available options using `gocode set` command. The config file
An integer option. If there have been no completion requests after this number of seconds, the gocode process will terminate. Defaults to 1800 (30 minutes). An integer option. If there have been no completion requests after this number of seconds, the gocode process will terminate. Defaults to 1800 (30 minutes).
- *unimported-packages*
A boolean option. If set to true, gocode will try to import certain known packages automatically for identifiers which cannot be resolved otherwise. Currently only a limited set of standard library packages are supported. Default: **false**.
- *partials*
A boolean option. If set to false, gocode will not filter autocompletion results based on entered prefix before the cursor. Instead it will return all available autocompletion results viable for a given context. Whether this option is set to true or false, gocode will return a valid prefix length for output formats which support it. Setting this option to a non-default value may result in editor misbehaviour. Default: **true**.
- *ignore-case*
A boolean option. If set to true, gocode will perform case-insensitive matching when doing prefix-based filtering. Default: **false**.
- *class-filtering*
A boolean option. Enables or disables gocode's feature where it performs class-based filtering if partial input matches corresponding class keyword: const, var, type, func, package. Default: **true**.
### Debugging ### Debugging
If something went wrong, the first thing you may want to do is manually start the gocode daemon with a debug mode enabled and in a separate terminal window. It will show you all the stack traces, panics if any and additional info about autocompletion requests. Shutdown the daemon if it was already started and run a new one explicitly with a debug mode enabled: If something went wrong, the first thing you may want to do is manually start the gocode daemon with a debug mode enabled and in a separate terminal window. It will show you all the stack traces, panics if any and additional info about autocompletion requests. Shutdown the daemon if it was already started and run a new one explicitly with a debug mode enabled:

View File

@ -6,6 +6,7 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"log"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -25,11 +26,13 @@ type candidate struct {
Name string Name string
Type string Type string
Class decl_class Class decl_class
Package string
} }
type out_buffers struct { type out_buffers struct {
tmpbuf *bytes.Buffer tmpbuf *bytes.Buffer
candidates []candidate candidates []candidate
canonical_aliases map[string]string
ctx *auto_complete_context ctx *auto_complete_context
tmpns map[string]bool tmpns map[string]bool
ignorecase bool ignorecase bool
@ -40,6 +43,10 @@ func new_out_buffers(ctx *auto_complete_context) *out_buffers {
b.tmpbuf = bytes.NewBuffer(make([]byte, 0, 1024)) b.tmpbuf = bytes.NewBuffer(make([]byte, 0, 1024))
b.candidates = make([]candidate, 0, 64) b.candidates = make([]candidate, 0, 64)
b.ctx = ctx b.ctx = ctx
b.canonical_aliases = make(map[string]string)
for _, imp := range b.ctx.current.packages {
b.canonical_aliases[imp.abspath] = imp.alias
}
return b return b
} }
@ -60,7 +67,7 @@ func (b *out_buffers) Swap(i, j int) {
b.candidates[i], b.candidates[j] = b.candidates[j], b.candidates[i] b.candidates[i], b.candidates[j] = b.candidates[j], b.candidates[i]
} }
func (b *out_buffers) append_decl(p, name string, decl *decl, class decl_class) { func (b *out_buffers) append_decl(p, name, pkg string, decl *decl, class decl_class) {
c1 := !g_config.ProposeBuiltins && decl.scope == g_universe_scope && decl.name != "Error" c1 := !g_config.ProposeBuiltins && decl.scope == g_universe_scope && decl.name != "Error"
c2 := class != decl_invalid && decl.class != class c2 := class != decl_invalid && decl.class != class
c3 := class == decl_invalid && !has_prefix(name, p, b.ignorecase) c3 := class == decl_invalid && !has_prefix(name, p, b.ignorecase)
@ -71,16 +78,17 @@ func (b *out_buffers) append_decl(p, name string, decl *decl, class decl_class)
return return
} }
decl.pretty_print_type(b.tmpbuf) decl.pretty_print_type(b.tmpbuf, b.canonical_aliases)
b.candidates = append(b.candidates, candidate{ b.candidates = append(b.candidates, candidate{
Name: name, Name: name,
Type: b.tmpbuf.String(), Type: b.tmpbuf.String(),
Class: decl.class, Class: decl.class,
Package: pkg,
}) })
b.tmpbuf.Reset() b.tmpbuf.Reset()
} }
func (b *out_buffers) append_embedded(p string, decl *decl, class decl_class) { func (b *out_buffers) append_embedded(p string, decl *decl, pkg string, class decl_class) {
if decl.embedded == nil { if decl.embedded == nil {
return return
} }
@ -103,21 +111,26 @@ func (b *out_buffers) append_embedded(p string, decl *decl, class decl_class) {
continue continue
} }
// could be type alias
if typedecl.is_alias() {
typedecl = typedecl.type_dealias()
}
// prevent infinite recursion here // prevent infinite recursion here
if typedecl.flags&decl_visited != 0 { if typedecl.is_visited() {
continue continue
} }
typedecl.flags |= decl_visited typedecl.set_visited()
defer typedecl.clear_visited() defer typedecl.clear_visited()
for _, c := range typedecl.children { for _, c := range typedecl.children {
if _, has := b.tmpns[c.name]; has { if _, has := b.tmpns[c.name]; has {
continue continue
} }
b.append_decl(p, c.name, c, class) b.append_decl(p, c.name, pkg, c, class)
b.tmpns[c.name] = true b.tmpns[c.name] = true
} }
b.append_embedded(p, typedecl, class) b.append_embedded(p, typedecl, pkg, class)
} }
if first_level { if first_level {
@ -183,6 +196,12 @@ func (c *auto_complete_context) merge_decls() {
merge_decls(f.filescope, c.pkg, f.decls) merge_decls(f.filescope, c.pkg, f.decls)
merge_decls_from_packages(c.pkg, f.packages, c.pcache) merge_decls_from_packages(c.pkg, f.packages, c.pcache)
} }
// special pass for type aliases which also have methods, while this is
// valid code, it shouldn't happen a lot in practice, so, whatever
// let's move all type alias methods to their first non-alias type down in
// the chain
propagate_type_alias_methods(c.pkg)
} }
func (c *auto_complete_context) make_decl_set(scope *scope) map[string]*decl { func (c *auto_complete_context) make_decl_set(scope *scope) map[string]*decl {
@ -197,11 +216,47 @@ func (c *auto_complete_context) get_candidates_from_set(set map[string]*decl, pa
continue continue
} }
value.infer_type() value.infer_type()
b.append_decl(partial, key, value, class) pkgname := ""
if pkg, ok := c.pcache[value.name]; ok {
pkgname = pkg.import_name
}
b.append_decl(partial, key, pkgname, value, class)
} }
} }
func (c *auto_complete_context) get_candidates_from_decl_alias(cc cursor_context, class decl_class, b *out_buffers) {
if cc.decl.is_visited() {
return
}
cc.decl = cc.decl.type_dealias()
if cc.decl == nil {
return
}
cc.decl.set_visited()
defer cc.decl.clear_visited()
c.get_candidates_from_decl(cc, class, b)
return
}
func (c *auto_complete_context) decl_package_import_path(decl *decl) string {
if decl == nil || decl.scope == nil {
return ""
}
if pkg, ok := c.pcache[decl.scope.pkgname]; ok {
return pkg.import_name
}
return ""
}
func (c *auto_complete_context) get_candidates_from_decl(cc cursor_context, class decl_class, b *out_buffers) { func (c *auto_complete_context) get_candidates_from_decl(cc cursor_context, class decl_class, b *out_buffers) {
if cc.decl.is_alias() {
c.get_candidates_from_decl_alias(cc, class, b)
return
}
// propose all children of a subject declaration and // propose all children of a subject declaration and
for _, decl := range cc.decl.children { for _, decl := range cc.decl.children {
if cc.decl.class == decl_package && !ast.IsExported(decl.name) { if cc.decl.class == decl_package && !ast.IsExported(decl.name) {
@ -213,34 +268,34 @@ func (c *auto_complete_context) get_candidates_from_decl(cc cursor_context, clas
continue continue
} }
} }
b.append_decl(cc.partial, decl.name, decl, class) b.append_decl(cc.partial, decl.name, c.decl_package_import_path(decl), decl, class)
} }
// propose all children of an underlying struct/interface type // propose all children of an underlying struct/interface type
adecl := advance_to_struct_or_interface(cc.decl) adecl := advance_to_struct_or_interface(cc.decl)
if adecl != nil && adecl != cc.decl { if adecl != nil && adecl != cc.decl {
for _, decl := range adecl.children { for _, decl := range adecl.children {
if decl.class == decl_var { if decl.class == decl_var {
b.append_decl(cc.partial, decl.name, decl, class) b.append_decl(cc.partial, decl.name, c.decl_package_import_path(decl), decl, class)
} }
} }
} }
// propose all children of its embedded types // propose all children of its embedded types
b.append_embedded(cc.partial, cc.decl, class) b.append_embedded(cc.partial, cc.decl, c.decl_package_import_path(cc.decl), class)
} }
func (c *auto_complete_context) get_import_candidates(partial string, b *out_buffers) { func (c *auto_complete_context) get_import_candidates(partial string, b *out_buffers) {
pkgdirs := g_daemon.context.pkg_dirs() currentPackagePath, pkgdirs := g_daemon.context.pkg_dirs()
resultSet := map[string]struct{}{} resultSet := map[string]struct{}{}
for _, pkgdir := range pkgdirs { for _, pkgdir := range pkgdirs {
// convert srcpath to pkgpath and get candidates // convert srcpath to pkgpath and get candidates
get_import_candidates_dir(pkgdir, filepath.FromSlash(partial), b.ignorecase, resultSet) get_import_candidates_dir(pkgdir, filepath.FromSlash(partial), b.ignorecase, currentPackagePath, resultSet)
} }
for k := range resultSet { for k := range resultSet {
b.candidates = append(b.candidates, candidate{Name: k, Class: decl_import}) b.candidates = append(b.candidates, candidate{Name: k, Class: decl_import})
} }
} }
func get_import_candidates_dir(root, partial string, ignorecase bool, r map[string]struct{}) { func get_import_candidates_dir(root, partial string, ignorecase bool, currentPackagePath string, r map[string]struct{}) {
var fpath string var fpath string
var match bool var match bool
if strings.HasSuffix(partial, "/") { if strings.HasSuffix(partial, "/") {
@ -259,7 +314,7 @@ func get_import_candidates_dir(root, partial string, ignorecase bool, r map[stri
if match && !has_prefix(rel, partial, ignorecase) { if match && !has_prefix(rel, partial, ignorecase) {
continue continue
} else if fi[i].IsDir() { } else if fi[i].IsDir() {
get_import_candidates_dir(root, rel+string(filepath.Separator), ignorecase, r) get_import_candidates_dir(root, rel+string(filepath.Separator), ignorecase, currentPackagePath, r)
} else { } else {
ext := filepath.Ext(name) ext := filepath.Ext(name)
if ext != ".a" { if ext != ".a" {
@ -267,7 +322,9 @@ func get_import_candidates_dir(root, partial string, ignorecase bool, r map[stri
} else { } else {
rel = rel[0 : len(rel)-2] rel = rel[0 : len(rel)-2]
} }
r[vendorlessImportPath(filepath.ToSlash(rel))] = struct{}{} if ipath, ok := vendorlessImportPath(filepath.ToSlash(rel), currentPackagePath); ok {
r[ipath] = struct{}{}
}
} }
} }
} }
@ -305,13 +362,29 @@ func (c *auto_complete_context) apropos(file []byte, filename string, cursor int
// And we're ready to Go. ;) // And we're ready to Go. ;)
b := new_out_buffers(c) b := new_out_buffers(c)
if g_config.IgnoreCase {
if *g_debug {
log.Printf("ignoring case sensitivity")
}
b.ignorecase = true
}
partial := 0
cc, ok := c.deduce_cursor_context(file, cursor) cc, ok := c.deduce_cursor_context(file, cursor)
partial := len(cc.partial)
if !g_config.Partials {
if *g_debug {
log.Printf("not performing partial prefix matching")
}
cc.partial = ""
}
if !ok { if !ok {
var d *decl var d *decl
if ident, ok := cc.expr.(*ast.Ident); ok && g_config.UnimportedPackages { if ident, ok := cc.expr.(*ast.Ident); ok && g_config.UnimportedPackages {
d = resolveKnownPackageIdent(ident.Name, c.current.name, c.current.context) p := resolveKnownPackageIdent(ident.Name, c.current.name, c.current.context)
if p != nil {
c.pcache[p.name] = p
d = p.main
}
} }
if d == nil { if d == nil {
return nil, 0 return nil, 0
@ -320,6 +393,7 @@ func (c *auto_complete_context) apropos(file []byte, filename string, cursor int
} }
class := decl_invalid class := decl_invalid
if g_config.ClassFiltering {
switch cc.partial { switch cc.partial {
case "const": case "const":
class = decl_const class = decl_const
@ -332,6 +406,7 @@ func (c *auto_complete_context) apropos(file []byte, filename string, cursor int
case "package": case "package":
class = decl_package class = decl_package
} }
}
if cc.decl_import { if cc.decl_import {
c.get_import_candidates(cc.partial, b) c.get_import_candidates(cc.partial, b)
@ -357,7 +432,6 @@ func (c *auto_complete_context) apropos(file []byte, filename string, cursor int
c.get_candidates_from_decl(cc, class, b) c.get_candidates_from_decl(cc, class, b)
} }
} }
partial = len(cc.partial)
if len(b.candidates) == 0 { if len(b.candidates) == 0 {
return nil, 0 return nil, 0
@ -391,6 +465,52 @@ func update_packages(ps map[string]*package_file_cache) {
} }
} }
func collect_type_alias_methods(d *decl) map[string]*decl {
if d == nil || d.is_visited() || !d.is_alias() {
return nil
}
d.set_visited()
defer d.clear_visited()
// add own methods
m := map[string]*decl{}
for k, v := range d.children {
m[k] = v
}
// recurse into more aliases
dd := type_to_decl(d.typ, d.scope)
for k, v := range collect_type_alias_methods(dd) {
m[k] = v
}
return m
}
func propagate_type_alias_methods(s *scope) {
for _, e := range s.entities {
if !e.is_alias() {
continue
}
methods := collect_type_alias_methods(e)
if len(methods) == 0 {
continue
}
dd := e.type_dealias()
if dd == nil {
continue
}
decl := dd.deep_copy()
for _, v := range methods {
decl.add_child(v)
}
s.entities[decl.name] = decl
}
}
func merge_decls(filescope *scope, pkg *scope, decls map[string]*decl) { func merge_decls(filescope *scope, pkg *scope, decls map[string]*decl) {
for _, d := range decls { for _, d := range decls {
pkg.merge_decl(d) pkg.merge_decl(d)
@ -400,7 +520,7 @@ func merge_decls(filescope *scope, pkg *scope, decls map[string]*decl) {
func merge_decls_from_packages(pkgscope *scope, pkgs []package_import, pcache package_cache) { func merge_decls_from_packages(pkgscope *scope, pkgs []package_import, pcache package_cache) {
for _, p := range pkgs { for _, p := range pkgs {
path, alias := p.path, p.alias path, alias := p.abspath, p.alias
if alias != "." { if alias != "." {
continue continue
} }
@ -418,7 +538,7 @@ func merge_decls_from_packages(pkgscope *scope, pkgs []package_import, pcache pa
func fixup_packages(filescope *scope, pkgs []package_import, pcache package_cache) { func fixup_packages(filescope *scope, pkgs []package_import, pcache package_cache) {
for _, p := range pkgs { for _, p := range pkgs {
path, alias := p.path, p.alias path, alias := p.abspath, p.alias
if alias == "" { if alias == "" {
alias = pcache[path].defalias alias = pcache[path].defalias
} }
@ -555,7 +675,6 @@ func check_type_expr(e ast.Expr) bool {
default: default:
return true return true
} }
return true
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------

View File

@ -141,7 +141,7 @@ func (f *auto_complete_file) process_decl(decl ast.Decl) {
for i, name := range data.names { for i, name := range data.names {
typ, v, vi := data.type_value_index(i) typ, v, vi := data.type_value_index(i)
d := new_decl_full(name.Name, class, 0, typ, v, vi, prevscope) d := new_decl_full(name.Name, class, ast_decl_flags(data.decl), typ, v, vi, prevscope)
if d == nil { if d == nil {
return return
} }
@ -243,9 +243,11 @@ func (f *auto_complete_file) process_select_stmt(a *ast.SelectStmt) {
if astmt, ok := last_cursor_after.Comm.(*ast.AssignStmt); ok && astmt.Tok == token.DEFINE { if astmt, ok := last_cursor_after.Comm.(*ast.AssignStmt); ok && astmt.Tok == token.DEFINE {
vname := astmt.Lhs[0].(*ast.Ident).Name vname := astmt.Lhs[0].(*ast.Ident).Name
v := new_decl_var(vname, nil, astmt.Rhs[0], -1, prevscope) v := new_decl_var(vname, nil, astmt.Rhs[0], -1, prevscope)
if v != nil {
f.scope.add_named_decl(v) f.scope.add_named_decl(v)
} }
} }
}
for _, s := range last_cursor_after.Body { for _, s := range last_cursor_after.Body {
f.process_stmt(s) f.process_stmt(s)
} }

View File

@ -50,6 +50,8 @@ func do_client() int {
cmd_drop_cache(client) cmd_drop_cache(client)
case "set": case "set":
cmd_set(client) cmd_set(client)
case "options":
cmd_options(client)
default: default:
fmt.Printf("unknown argument: %q, try running \"gocode -h\"\n", flag.Arg(0)) fmt.Printf("unknown argument: %q, try running \"gocode -h\"\n", flag.Arg(0))
return 1 return 1
@ -180,3 +182,7 @@ func cmd_set(c *rpc.Client) {
fmt.Print(client_set(c, flag.Arg(1), flag.Arg(2))) fmt.Print(client_set(c, flag.Arg(1), flag.Arg(2)))
} }
} }
func cmd_options(c *rpc.Client) {
fmt.Print(client_options(c, 0))
}

View File

@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
) )
@ -28,9 +29,27 @@ type config struct {
PackageLookupMode string `json:"package-lookup-mode"` PackageLookupMode string `json:"package-lookup-mode"`
CloseTimeout int `json:"close-timeout"` CloseTimeout int `json:"close-timeout"`
UnimportedPackages bool `json:"unimported-packages"` UnimportedPackages bool `json:"unimported-packages"`
Partials bool `json:"partials"`
IgnoreCase bool `json:"ignore-case"`
ClassFiltering bool `json:"class-filtering"`
} }
var g_config = config{ var g_config_desc = map[string]string{
"propose-builtins": "If set to {true}, gocode will add built-in types, functions and constants to autocompletion proposals.",
"lib-path": "A string option. Allows you to add search paths for packages. By default, gocode only searches {$GOPATH/pkg/$GOOS_$GOARCH} and {$GOROOT/pkg/$GOOS_$GOARCH} in terms of previously existed environment variables. Also you can specify multiple paths using ':' (colon) as a separator (on Windows use semicolon ';'). The paths specified by {lib-path} are prepended to the default ones.",
"custom-pkg-prefix": "",
"custom-vendor-dir": "",
"autobuild": "If set to {true}, gocode will try to automatically build out-of-date packages when their source files are modified, in order to obtain the freshest autocomplete results for them. This feature is experimental.",
"force-debug-output": "If is not empty, gocode will forcefully redirect the logging into that file. Also forces enabling of the debug mode on the server side.",
"package-lookup-mode": "If set to {go}, use standard Go package lookup rules. If set to {gb}, use gb-specific lookup rules. See {https://github.com/constabulary/gb} for details.",
"close-timeout": "If there have been no completion requests after this number of seconds, the gocode process will terminate. Default is 30 minutes.",
"unimported-packages": "If set to {true}, gocode will try to import certain known packages automatically for identifiers which cannot be resolved otherwise. Currently only a limited set of standard library packages is supported.",
"partials": "If set to {false}, gocode will not filter autocompletion results based on entered prefix before the cursor. Instead it will return all available autocompletion results viable for a given context. Whether this option is set to {true} or {false}, gocode will return a valid prefix length for output formats which support it. Setting this option to a non-default value may result in editor misbehaviour.",
"ignore-case": "If set to {true}, gocode will perform case-insensitive matching when doing prefix-based filtering.",
"class-filtering": "Enables or disables gocode's feature where it performs class-based filtering if partial input matches corresponding class keyword: const, var, type, func, package.",
}
var g_default_config = config{
ProposeBuiltins: false, ProposeBuiltins: false,
LibPath: "", LibPath: "",
CustomPkgPrefix: "", CustomPkgPrefix: "",
@ -39,7 +58,11 @@ var g_config = config{
PackageLookupMode: "go", PackageLookupMode: "go",
CloseTimeout: 1800, CloseTimeout: 1800,
UnimportedPackages: false, UnimportedPackages: false,
Partials: true,
IgnoreCase: false,
ClassFiltering: true,
} }
var g_config = g_default_config
var g_string_to_bool = map[string]bool{ var g_string_to_bool = map[string]bool{
"t": true, "t": true,
@ -175,3 +198,43 @@ func (this *config) read() error {
return nil return nil
} }
func quoted(v interface{}) string {
switch v.(type) {
case string:
return fmt.Sprintf("%q", v)
case int:
return fmt.Sprint(v)
case bool:
return fmt.Sprint(v)
default:
panic("unreachable")
}
}
var descRE = regexp.MustCompile(`{[^}]+}`)
func preprocess_desc(v string) string {
return descRE.ReplaceAllStringFunc(v, func(v string) string {
return color_cyan + v[1:len(v)-1] + color_none
})
}
func (this *config) options() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%sConfig file location%s: %s\n", color_white_bold, color_none, config_file())
dv := reflect.ValueOf(g_default_config)
v, t := this.value_and_type()
for i, n := 0, t.NumField(); i < n; i++ {
f := t.Field(i)
index := f.Index
tag := f.Tag.Get("json")
fmt.Fprintf(&buf, "\n%s%s%s\n", color_yellow_bold, tag, color_none)
fmt.Fprintf(&buf, "%stype%s: %s\n", color_yellow, color_none, f.Type)
fmt.Fprintf(&buf, "%svalue%s: %s\n", color_yellow, color_none, quoted(v.FieldByIndex(index).Interface()))
fmt.Fprintf(&buf, "%sdefault%s: %s\n", color_yellow, color_none, quoted(dv.FieldByIndex(index).Interface()))
fmt.Fprintf(&buf, "%sdescription%s: %s\n", color_yellow, color_none, preprocess_desc(g_config_desc[tag]))
}
return buf.String()
}

View File

@ -35,10 +35,8 @@ type token_item struct {
func (i token_item) literal() string { func (i token_item) literal() string {
if i.tok.IsLiteral() { if i.tok.IsLiteral() {
return i.lit return i.lit
} else {
return i.tok.String()
} }
return "" return i.tok.String()
} }
func new_token_iterator(src []byte, cursor int) token_iterator { func new_token_iterator(src []byte, cursor int) token_iterator {
@ -116,6 +114,28 @@ func (this *token_iterator) skip_to_left_curly() bool {
return this.skip_to_left(token.LBRACE, token.RBRACE) return this.skip_to_left(token.LBRACE, token.RBRACE)
} }
func (ti *token_iterator) extract_type_alike() string {
if ti.token().tok != token.IDENT { // not Foo, return nothing
return ""
}
b := ti.token().literal()
if !ti.go_back() { // just Foo
return b
}
if ti.token().tok != token.PERIOD { // not .Foo, return Foo
return b
}
if !ti.go_back() { // just .Foo, return Foo (best choice recovery)
return b
}
if ti.token().tok != token.IDENT { // not lib.Foo, return Foo
return b
}
out := ti.token().literal() + "." + b // lib.Foo
ti.go_back()
return out
}
// Extract the type expression right before the enclosing curly bracket block. // Extract the type expression right before the enclosing curly bracket block.
// Examples (# - the cursor): // Examples (# - the cursor):
// &lib.Struct{Whatever: 1, Hel#} // returns "lib.Struct" // &lib.Struct{Whatever: 1, Hel#} // returns "lib.Struct"
@ -130,23 +150,21 @@ func (ti *token_iterator) extract_struct_type() string {
if !ti.go_back() { if !ti.go_back() {
return "" return ""
} }
if ti.token().tok != token.IDENT { if ti.token().tok == token.LBRACE { // Foo{#{}}
if !ti.go_back() {
return "" return ""
} }
b := ti.token().literal() } else if ti.token().tok == token.COMMA { // Foo{abc,#{}}
if !ti.go_back() { return ti.extract_struct_type()
return b
} }
if ti.token().tok != token.PERIOD { typ := ti.extract_type_alike()
return b if typ == "" {
return ""
} }
if !ti.go_back() { if ti.token().tok == token.RPAREN || ti.token().tok == token.MUL {
return b return ""
} }
if ti.token().tok != token.IDENT { return typ
return b
}
return ti.token().literal() + "." + b
} }
// Starting from the token under the cursor move back and extract something // Starting from the token under the cursor move back and extract something
@ -266,7 +284,14 @@ func (c *auto_complete_context) deduce_struct_type_decl(iter *token_iterator) *d
if decl == nil { if decl == nil {
return nil return nil
} }
if _, ok := decl.typ.(*ast.StructType); !ok {
// we allow only struct types here, but also support type aliases
if decl.is_alias() {
dd := decl.type_dealias()
if _, ok := dd.typ.(*ast.StructType); !ok {
return nil
}
} else if _, ok := decl.typ.(*ast.StructType); !ok {
return nil return nil
} }
return decl return decl
@ -395,7 +420,7 @@ func (c *auto_complete_context) deduce_cursor_context(file []byte, cursor int) (
// package name has nothing to do with package file name, that's why we need to // package name has nothing to do with package file name, that's why we need to
// scan the packages. And many of them will have conflicts. Can we make a smart // scan the packages. And many of them will have conflicts. Can we make a smart
// prediction algorithm which will prefer certain packages over another ones? // prediction algorithm which will prefer certain packages over another ones?
func resolveKnownPackageIdent(ident string, filename string, context *package_lookup_context) *decl { func resolveKnownPackageIdent(ident string, filename string, context *package_lookup_context) *package_file_cache {
importPath, ok := knownPackageIdents[ident] importPath, ok := knownPackageIdents[ident]
if !ok { if !ok {
return nil return nil
@ -406,9 +431,9 @@ func resolveKnownPackageIdent(ident string, filename string, context *package_lo
return nil return nil
} }
p := new_package_file_cache(path) p := new_package_file_cache(path, importPath)
p.update_cache() p.update_cache()
return p.main return p
} }
var knownPackageIdents = map[string]string{ var knownPackageIdents = map[string]string{

148
vendor/github.com/nsf/gocode/decl.go generated vendored
View File

@ -56,12 +56,15 @@ func (this decl_class) String() string {
type decl_flags int16 type decl_flags int16
const ( const (
decl_foreign = decl_flags(1 << iota) // imported from another package decl_foreign decl_flags = 1 << iota // imported from another package
// means that the decl is a part of the range statement // means that the decl is a part of the range statement
// its type is inferred in a special way // its type is inferred in a special way
decl_rangevar decl_rangevar
// decl of decl_type class is a type alias
decl_alias
// for preventing infinite recursions and loops in type inference code // for preventing infinite recursions and loops in type inference code
decl_visited decl_visited
) )
@ -115,7 +118,19 @@ func ast_decl_type(d ast.Decl) ast.Expr {
return t.Type return t.Type
} }
panic("unreachable") panic("unreachable")
return nil }
func ast_decl_flags(d ast.Decl) decl_flags {
switch t := d.(type) {
case *ast.GenDecl:
switch t.Tok {
case token.TYPE:
if isAliasTypeSpec(t.Specs[0].(*ast.TypeSpec)) {
return decl_alias
}
}
}
return 0
} }
func ast_decl_class(d ast.Decl) decl_class { func ast_decl_class(d ast.Decl) decl_class {
@ -324,7 +339,7 @@ func new_decl_var(name string, typ ast.Expr, value ast.Expr, vindex int, scope *
func method_of(d ast.Decl) string { func method_of(d ast.Decl) string {
if t, ok := d.(*ast.FuncDecl); ok { if t, ok := d.(*ast.FuncDecl); ok {
if t.Recv != nil { if t.Recv != nil && len(t.Recv.List) != 0 {
switch t := t.Recv.List[0].Type.(type) { switch t := t.Recv.List[0].Type.(type) {
case *ast.StarExpr: case *ast.StarExpr:
if se, ok := t.X.(*ast.SelectorExpr); ok { if se, ok := t.X.(*ast.SelectorExpr); ok {
@ -348,6 +363,7 @@ func (other *decl) deep_copy() *decl {
d := new(decl) d := new(decl)
d.name = other.name d.name = other.name
d.class = other.class d.class = other.class
d.flags = other.flags
d.typ = other.typ d.typ = other.typ
d.value = other.value d.value = other.value
d.value_index = other.value_index d.value_index = other.value_index
@ -363,20 +379,36 @@ func (other *decl) deep_copy() *decl {
return d return d
} }
func (d *decl) is_rangevar() bool {
return d.flags&decl_rangevar != 0
}
func (d *decl) is_alias() bool {
return d.flags&decl_alias != 0
}
func (d *decl) is_visited() bool {
return d.flags&decl_visited != 0
}
func (d *decl) set_visited() {
d.flags |= decl_visited
}
func (d *decl) clear_visited() { func (d *decl) clear_visited() {
d.flags &^= decl_visited d.flags &^= decl_visited
} }
func (d *decl) expand_or_replace(other *decl) { func (d *decl) expand_or_replace(other *decl) {
// expand only if it's a methods stub, otherwise simply copy // expand only if it's a methods stub, otherwise simply keep it as is
if d.class != decl_methods_stub && other.class != decl_methods_stub { if d.class != decl_methods_stub && other.class != decl_methods_stub {
d = other
return return
} }
if d.class == decl_methods_stub { if d.class == decl_methods_stub {
d.typ = other.typ d.typ = other.typ
d.class = other.class d.class = other.class
d.flags = other.flags
} }
if other.children != nil { if other.children != nil {
@ -398,7 +430,7 @@ func (d *decl) matches() bool {
return true return true
} }
func (d *decl) pretty_print_type(out io.Writer) { func (d *decl) pretty_print_type(out io.Writer, canonical_aliases map[string]string) {
switch d.class { switch d.class {
case decl_type: case decl_type:
switch d.typ.(type) { switch d.typ.(type) {
@ -410,15 +442,15 @@ func (d *decl) pretty_print_type(out io.Writer) {
fmt.Fprintf(out, "interface") fmt.Fprintf(out, "interface")
default: default:
if d.typ != nil { if d.typ != nil {
pretty_print_type_expr(out, d.typ) pretty_print_type_expr(out, d.typ, canonical_aliases)
} }
} }
case decl_var: case decl_var:
if d.typ != nil { if d.typ != nil {
pretty_print_type_expr(out, d.typ) pretty_print_type_expr(out, d.typ, canonical_aliases)
} }
case decl_func: case decl_func:
pretty_print_type_expr(out, d.typ) pretty_print_type_expr(out, d.typ, canonical_aliases)
} }
} }
@ -481,18 +513,15 @@ func func_return_type(f *ast.FuncType, index int) ast.Expr {
i := 0 i := 0
var field *ast.Field var field *ast.Field
for _, field = range f.Results.List { for _, field = range f.Results.List {
if i >= index { n := 1
return field.Type
}
if field.Names != nil { if field.Names != nil {
i += len(field.Names) n = len(field.Names)
} else {
i++
} }
} if i <= index && index < i+n {
if i >= index {
return field.Type return field.Type
} }
i += n
}
return nil return nil
} }
@ -599,20 +628,20 @@ func advance_to_type(pred type_predicate, v ast.Expr, scope *scope) (ast.Expr, *
return nil, nil return nil, nil
} }
if decl.flags&decl_visited != 0 { if decl.is_visited() {
return nil, nil return nil, nil
} }
decl.flags |= decl_visited decl.set_visited()
defer decl.clear_visited() defer decl.clear_visited()
return advance_to_type(pred, decl.typ, decl.scope) return advance_to_type(pred, decl.typ, decl.scope)
} }
func advance_to_struct_or_interface(decl *decl) *decl { func advance_to_struct_or_interface(decl *decl) *decl {
if decl.flags&decl_visited != 0 { if decl.is_visited() {
return nil return nil
} }
decl.flags |= decl_visited decl.set_visited()
defer decl.clear_visited() defer decl.clear_visited()
if struct_interface_predicate(decl.typ) { if struct_interface_predicate(decl.typ) {
@ -917,7 +946,7 @@ func infer_type(v ast.Expr, scope *scope, index int) (ast.Expr, *scope, bool) {
// makes sense. // makes sense.
func (d *decl) infer_type() (ast.Expr, *scope) { func (d *decl) infer_type() (ast.Expr, *scope) {
// special case for range vars // special case for range vars
if d.flags&decl_rangevar != 0 { if d.is_rangevar() {
var scope *scope var scope *scope
d.typ, scope = infer_range_type(d.value, d.scope, d.value_index) d.typ, scope = infer_range_type(d.value, d.scope, d.value_index)
return d.typ, scope return d.typ, scope
@ -937,10 +966,10 @@ func (d *decl) infer_type() (ast.Expr, *scope) {
} }
// prevent loops // prevent loops
if d.flags&decl_visited != 0 { if d.is_visited() {
return nil, nil return nil, nil
} }
d.flags |= decl_visited d.set_visited()
defer d.clear_visited() defer d.clear_visited()
var scope *scope var scope *scope
@ -948,13 +977,34 @@ func (d *decl) infer_type() (ast.Expr, *scope) {
return d.typ, scope return d.typ, scope
} }
func (d *decl) find_child(name string) *decl { func (d *decl) type_dealias() *decl {
if d.flags&decl_visited != 0 { if d.is_visited() {
return nil return nil
} }
d.flags |= decl_visited d.set_visited()
defer d.clear_visited() defer d.clear_visited()
dd := type_to_decl(d.typ, d.scope)
if dd != nil && dd.is_alias() {
return dd.type_dealias()
}
return dd
}
func (d *decl) find_child(name string) *decl {
// type aliases don't really have any children on their own, but they
// point to a different type, let's try to find one
if d.is_alias() {
dd := d.type_dealias()
if dd != nil {
return dd.find_child(name)
}
// note that type alias can also point to a type literal, something like
// type A = struct { A int }
// in this case we rely on "advance_to_struct_or_interface" below
}
if d.children != nil { if d.children != nil {
if c, ok := d.children[name]; ok { if c, ok := d.children[name]; ok {
return c return c
@ -963,6 +1013,12 @@ func (d *decl) find_child(name string) *decl {
decl := advance_to_struct_or_interface(d) decl := advance_to_struct_or_interface(d)
if decl != nil && decl != d { if decl != nil && decl != d {
if d.is_visited() {
return nil
}
d.set_visited()
defer d.clear_visited()
return decl.find_child(name) return decl.find_child(name)
} }
return nil return nil
@ -1053,11 +1109,11 @@ func get_array_len(e ast.Expr) string {
return "" return ""
} }
func pretty_print_type_expr(out io.Writer, e ast.Expr) { func pretty_print_type_expr(out io.Writer, e ast.Expr, canonical_aliases map[string]string) {
switch t := e.(type) { switch t := e.(type) {
case *ast.StarExpr: case *ast.StarExpr:
fmt.Fprintf(out, "*") fmt.Fprintf(out, "*")
pretty_print_type_expr(out, t.X) pretty_print_type_expr(out, t.X, canonical_aliases)
case *ast.Ident: case *ast.Ident:
if strings.HasPrefix(t.Name, "$") { if strings.HasPrefix(t.Name, "$") {
// beautify anonymous types // beautify anonymous types
@ -1070,15 +1126,19 @@ func pretty_print_type_expr(out io.Writer, e ast.Expr) {
// it's always true // it's always true
fmt.Fprintf(out, "interface{}") fmt.Fprintf(out, "interface{}")
} }
} else if !*g_debug && strings.HasPrefix(t.Name, "#") {
fmt.Fprintf(out, t.Name[1:])
} else if !*g_debug && strings.HasPrefix(t.Name, "!") { } else if !*g_debug && strings.HasPrefix(t.Name, "!") {
// these are full package names for disambiguating and pretty // these are full package names for disambiguating and pretty
// printing packages withing packages, e.g. // printing packages within packages, e.g.
// !go/ast!ast vs. !github.com/nsf/my/ast!ast // !go/ast!ast vs. !github.com/nsf/my/ast!ast
// another ugly hack, if people are punished in hell for ugly hacks // another ugly hack, if people are punished in hell for ugly hacks
// I'm screwed... // I'm screwed...
fmt.Fprintf(out, t.Name[strings.LastIndex(t.Name, "!")+1:]) emarkIdx := strings.LastIndex(t.Name, "!")
path := t.Name[1:emarkIdx]
alias := canonical_aliases[path]
if alias == "" {
alias = t.Name[emarkIdx+1:]
}
fmt.Fprintf(out, alias)
} else { } else {
fmt.Fprintf(out, t.Name) fmt.Fprintf(out, t.Name)
} }
@ -1092,17 +1152,17 @@ func pretty_print_type_expr(out io.Writer, e ast.Expr) {
} else { } else {
fmt.Fprintf(out, "[]") fmt.Fprintf(out, "[]")
} }
pretty_print_type_expr(out, t.Elt) pretty_print_type_expr(out, t.Elt, canonical_aliases)
case *ast.SelectorExpr: case *ast.SelectorExpr:
pretty_print_type_expr(out, t.X) pretty_print_type_expr(out, t.X, canonical_aliases)
fmt.Fprintf(out, ".%s", t.Sel.Name) fmt.Fprintf(out, ".%s", t.Sel.Name)
case *ast.FuncType: case *ast.FuncType:
fmt.Fprintf(out, "func(") fmt.Fprintf(out, "func(")
pretty_print_func_field_list(out, t.Params) pretty_print_func_field_list(out, t.Params, canonical_aliases)
fmt.Fprintf(out, ")") fmt.Fprintf(out, ")")
buf := bytes.NewBuffer(make([]byte, 0, 256)) buf := bytes.NewBuffer(make([]byte, 0, 256))
nresults := pretty_print_func_field_list(buf, t.Results) nresults := pretty_print_func_field_list(buf, t.Results, canonical_aliases)
if nresults > 0 { if nresults > 0 {
results := buf.String() results := buf.String()
if strings.IndexAny(results, ", ") != -1 { if strings.IndexAny(results, ", ") != -1 {
@ -1112,14 +1172,14 @@ func pretty_print_type_expr(out io.Writer, e ast.Expr) {
} }
case *ast.MapType: case *ast.MapType:
fmt.Fprintf(out, "map[") fmt.Fprintf(out, "map[")
pretty_print_type_expr(out, t.Key) pretty_print_type_expr(out, t.Key, canonical_aliases)
fmt.Fprintf(out, "]") fmt.Fprintf(out, "]")
pretty_print_type_expr(out, t.Value) pretty_print_type_expr(out, t.Value, canonical_aliases)
case *ast.InterfaceType: case *ast.InterfaceType:
fmt.Fprintf(out, "interface{}") fmt.Fprintf(out, "interface{}")
case *ast.Ellipsis: case *ast.Ellipsis:
fmt.Fprintf(out, "...") fmt.Fprintf(out, "...")
pretty_print_type_expr(out, t.Elt) pretty_print_type_expr(out, t.Elt, canonical_aliases)
case *ast.StructType: case *ast.StructType:
fmt.Fprintf(out, "struct") fmt.Fprintf(out, "struct")
case *ast.ChanType: case *ast.ChanType:
@ -1131,10 +1191,10 @@ func pretty_print_type_expr(out io.Writer, e ast.Expr) {
case ast.SEND | ast.RECV: case ast.SEND | ast.RECV:
fmt.Fprintf(out, "chan ") fmt.Fprintf(out, "chan ")
} }
pretty_print_type_expr(out, t.Value) pretty_print_type_expr(out, t.Value, canonical_aliases)
case *ast.ParenExpr: case *ast.ParenExpr:
fmt.Fprintf(out, "(") fmt.Fprintf(out, "(")
pretty_print_type_expr(out, t.X) pretty_print_type_expr(out, t.X, canonical_aliases)
fmt.Fprintf(out, ")") fmt.Fprintf(out, ")")
case *ast.BadExpr: case *ast.BadExpr:
// TODO: probably I should check that in a separate function // TODO: probably I should check that in a separate function
@ -1145,7 +1205,7 @@ func pretty_print_type_expr(out io.Writer, e ast.Expr) {
} }
} }
func pretty_print_func_field_list(out io.Writer, f *ast.FieldList) int { func pretty_print_func_field_list(out io.Writer, f *ast.FieldList, canonical_aliases map[string]string) int {
count := 0 count := 0
if f == nil { if f == nil {
return count return count
@ -1172,7 +1232,7 @@ func pretty_print_func_field_list(out io.Writer, f *ast.FieldList) int {
} }
// type // type
pretty_print_type_expr(out, field.Type) pretty_print_type_expr(out, field.Type, canonical_aliases)
// , // ,
if i != len(f.List)-1 { if i != len(f.List)-1 {

View File

@ -20,6 +20,7 @@ import (
type package_import struct { type package_import struct {
alias string alias string
abspath string
path string path string
} }
@ -32,9 +33,9 @@ func collect_package_imports(filename string, decls []ast.Decl, context *package
for _, spec := range gd.Specs { for _, spec := range gd.Specs {
imp := spec.(*ast.ImportSpec) imp := spec.(*ast.ImportSpec)
path, alias := path_and_alias(imp) path, alias := path_and_alias(imp)
path, ok := abs_path_for_package(filename, path, context) abspath, ok := abs_path_for_package(filename, path, context)
if ok && alias != "_" { if ok && alias != "_" {
pi = append(pi, package_import{alias, path}) pi = append(pi, package_import{alias, abspath, path})
} }
} }
} else { } else {
@ -121,7 +122,7 @@ func append_to_top_decls(decls map[string]*decl, decl ast.Decl, scope *scope) {
for i, name := range data.names { for i, name := range data.names {
typ, v, vi := data.type_value_index(i) typ, v, vi := data.type_value_index(i)
d := new_decl_full(name.Name, class, 0, typ, v, vi, scope) d := new_decl_full(name.Name, class, ast_decl_flags(data.decl), typ, v, vi, scope)
if d == nil { if d == nil {
return return
} }
@ -314,7 +315,11 @@ func find_global_file(imp string, context *package_lookup_context) (string, bool
// gb-specific lookup mode, only if the root dir was found // gb-specific lookup mode, only if the root dir was found
if g_config.PackageLookupMode == "gb" && context.GBProjectRoot != "" { if g_config.PackageLookupMode == "gb" && context.GBProjectRoot != "" {
root := context.GBProjectRoot root := context.GBProjectRoot
pkg_path := filepath.Join(root, "pkg", context.GOOS+"-"+context.GOARCH, pkgfile) pkgdir := filepath.Join(root, "pkg", context.GOOS+"-"+context.GOARCH)
if !is_dir(pkgdir) {
pkgdir = filepath.Join(root, "pkg", context.GOOS+"-"+context.GOARCH+"-race")
}
pkg_path := filepath.Join(pkgdir, pkgfile)
if file_exists(pkg_path) { if file_exists(pkg_path) {
log_found_package_maybe(imp, pkg_path) log_found_package_maybe(imp, pkg_path)
return pkg_path, true return pkg_path, true
@ -453,9 +458,10 @@ func (ctxt *package_lookup_context) gopath() []string {
return all return all
} }
func (ctxt *package_lookup_context) pkg_dirs() []string { func (ctxt *package_lookup_context) pkg_dirs() (string, []string) {
pkgdir := fmt.Sprintf("%s_%s", ctxt.GOOS, ctxt.GOARCH) pkgdir := fmt.Sprintf("%s_%s", ctxt.GOOS, ctxt.GOARCH)
var currentPackagePath string
var all []string var all []string
if ctxt.GOROOT != "" { if ctxt.GOROOT != "" {
dir := filepath.Join(ctxt.GOROOT, "pkg", pkgdir) dir := filepath.Join(ctxt.GOROOT, "pkg", pkgdir)
@ -466,15 +472,23 @@ func (ctxt *package_lookup_context) pkg_dirs() []string {
switch g_config.PackageLookupMode { switch g_config.PackageLookupMode {
case "go": case "go":
currentPackagePath = ctxt.CurrentPackagePath
for _, p := range ctxt.gopath() { for _, p := range ctxt.gopath() {
dir := filepath.Join(p, "pkg", pkgdir) dir := filepath.Join(p, "pkg", pkgdir)
if is_dir(dir) { if is_dir(dir) {
all = append(all, dir) all = append(all, dir)
} }
dir = filepath.Join(dir, currentPackagePath, "vendor")
if is_dir(dir) {
all = append(all, dir)
}
} }
case "gb": case "gb":
if ctxt.GBProjectRoot != "" { if ctxt.GBProjectRoot != "" {
pkgdir := fmt.Sprintf("%s-%s", ctxt.GOOS, ctxt.GOARCH) pkgdir := fmt.Sprintf("%s-%s", ctxt.GOOS, ctxt.GOARCH)
if !is_dir(pkgdir) {
pkgdir = fmt.Sprintf("%s-%s-race", ctxt.GOOS, ctxt.GOARCH)
}
dir := filepath.Join(ctxt.GBProjectRoot, "pkg", pkgdir) dir := filepath.Join(ctxt.GBProjectRoot, "pkg", pkgdir)
if is_dir(dir) { if is_dir(dir) {
all = append(all, dir) all = append(all, dir)
@ -483,7 +497,7 @@ func (ctxt *package_lookup_context) pkg_dirs() []string {
case "bzl": case "bzl":
// TODO: Support bazel mode // TODO: Support bazel mode
} }
return all return currentPackagePath, all
} }
type decl_cache struct { type decl_cache struct {

View File

@ -128,6 +128,18 @@ func (*csv_formatter) write_candidates(candidates []candidate, num int) {
} }
} }
//-------------------------------------------------------------------------
// csv_with_package_formatter
//-------------------------------------------------------------------------
type csv_with_package_formatter struct{}
func (*csv_with_package_formatter) write_candidates(candidates []candidate, num int) {
for _, c := range candidates {
fmt.Printf("%s,,%s,,%s,,%s\n", c.Class, c.Name, c.Type, c.Package)
}
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// json_formatter // json_formatter
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -145,8 +157,8 @@ func (*json_formatter) write_candidates(candidates []candidate, num int) {
if i != 0 { if i != 0 {
fmt.Printf(", ") fmt.Printf(", ")
} }
fmt.Printf(`{"class": "%s", "name": "%s", "type": "%s"}`, fmt.Printf(`{"class": "%s", "name": "%s", "type": "%s", "package": "%s"}`,
c.Class, c.Name, c.Type) c.Class, c.Name, c.Type, c.Package)
} }
fmt.Print("]]") fmt.Print("]]")
} }
@ -163,6 +175,8 @@ func get_formatter(name string) formatter {
return new(nice_formatter) return new(nice_formatter)
case "csv": case "csv":
return new(csv_formatter) return new(csv_formatter)
case "csv-with-package":
return new(csv_with_package_formatter)
case "json": case "json":
return new(json_formatter) return new(json_formatter)
case "godit": case "godit":

View File

@ -12,7 +12,7 @@ import (
var ( var (
g_is_server = flag.Bool("s", false, "run a server instead of a client") 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_format = flag.String("f", "nice", "output format (vim | emacs | nice | csv | csv-with-package | json)")
g_input = flag.String("in", "", "use this file instead of stdin input") g_input = flag.String("in", "", "use this file instead of stdin input")
g_sock = create_sock_flag("sock", "socket type (unix | tcp)") g_sock = create_sock_flag("sock", "socket type (unix | tcp)")
g_addr = flag.String("addr", "127.0.0.1:37373", "address for tcp socket") g_addr = flag.String("addr", "127.0.0.1:37373", "address for tcp socket")
@ -40,9 +40,11 @@ func show_usage() {
"\nCommands:\n"+ "\nCommands:\n"+
" autocomplete [<path>] <offset> main autocompletion command\n"+ " autocomplete [<path>] <offset> main autocompletion command\n"+
" close close the gocode daemon\n"+ " close close the gocode daemon\n"+
" status gocode daemon status report\n"+
" drop-cache drop gocode daemon's cache\n"+ " drop-cache drop gocode daemon's cache\n"+
" set [<name> [<value>]] list or set config options\n") " options list config options (extended)\n"+
" set [<name> [<value>]] list or set config options\n"+
" status gocode daemon status report\n"+
"")
} }
func main() { func main() {

View File

@ -21,6 +21,7 @@ type package_parser interface {
type package_file_cache struct { type package_file_cache struct {
name string // file name name string // file name
import_name string
mtime int64 mtime int64
defalias string defalias string
@ -29,9 +30,10 @@ type package_file_cache struct {
others map[string]*decl others map[string]*decl
} }
func new_package_file_cache(name string) *package_file_cache { func new_package_file_cache(absname, name string) *package_file_cache {
m := new(package_file_cache) m := new(package_file_cache)
m.name = name m.name = absname
m.import_name = name
m.mtime = 0 m.mtime = 0
m.defalias = "" m.defalias = ""
return m return m
@ -92,7 +94,7 @@ func (m *package_file_cache) update_cache() {
} }
func (m *package_file_cache) process_package_data(data []byte) { func (m *package_file_cache) process_package_data(data []byte) {
m.scope = new_scope(g_universe_scope) m.scope = new_named_scope(g_universe_scope, m.name)
// find import section // find import section
i := bytes.Index(data, []byte{'\n', '$', '$'}) i := bytes.Index(data, []byte{'\n', '$', '$'})
@ -126,9 +128,10 @@ func (m *package_file_cache) process_package_data(data []byte) {
pp = &p pp = &p
} }
prefix := "!" + m.name + "!"
pp.parse_export(func(pkg string, decl ast.Decl) { pp.parse_export(func(pkg string, decl ast.Decl) {
anonymify_ast(decl, decl_foreign, m.scope) anonymify_ast(decl, decl_foreign, m.scope)
if pkg == "" || strings.HasPrefix(pkg, "#") { if pkg == "" || strings.HasPrefix(pkg, prefix) {
// main package // main package
add_ast_decl_to_package(m.main, decl, m.scope) add_ast_decl_to_package(m.main, decl, m.scope)
} else { } else {
@ -141,12 +144,12 @@ func (m *package_file_cache) process_package_data(data []byte) {
}) })
// hack, add ourselves to the package scope // hack, add ourselves to the package scope
mainName := "#" + m.defalias mainName := "!" + m.name + "!" + m.defalias
m.add_package_to_scope(mainName, m.name) m.add_package_to_scope(mainName, m.name)
// replace dummy package decls in package scope to actual packages // replace dummy package decls in package scope to actual packages
for key := range m.scope.entities { for key := range m.scope.entities {
if !strings.HasPrefix(key, "#") && !strings.HasPrefix(key, "!") { if !strings.HasPrefix(key, "!") {
continue continue
} }
pkg, ok := m.others[key] pkg, ok := m.others[key]
@ -168,7 +171,7 @@ func add_ast_decl_to_package(pkg *decl, decl ast.Decl, scope *scope) {
for i, name := range data.names { for i, name := range data.names {
typ, v, vi := data.type_value_index(i) typ, v, vi := data.type_value_index(i)
d := new_decl_full(name.Name, class, decl_foreign, typ, v, vi, scope) d := new_decl_full(name.Name, class, decl_foreign|ast_decl_flags(data.decl), typ, v, vi, scope)
if d == nil { if d == nil {
return return
} }
@ -218,16 +221,16 @@ func new_package_cache() package_cache {
// In case if package is not in the cache, it creates one and adds one to the cache. // In case if package is not in the cache, it creates one and adds one to the cache.
func (c package_cache) append_packages(ps map[string]*package_file_cache, pkgs []package_import) { func (c package_cache) append_packages(ps map[string]*package_file_cache, pkgs []package_import) {
for _, m := range pkgs { for _, m := range pkgs {
if _, ok := ps[m.path]; ok { if _, ok := ps[m.abspath]; ok {
continue continue
} }
if mod, ok := c[m.path]; ok { if mod, ok := c[m.abspath]; ok {
ps[m.path] = mod ps[m.abspath] = mod
} else { } else {
mod = new_package_file_cache(m.path) mod = new_package_file_cache(m.abspath, m.path)
ps[m.path] = mod ps[m.abspath] = mod
c[m.path] = mod c[m.abspath] = mod
} }
} }
} }
@ -240,11 +243,6 @@ package unsafe
func @"".Offsetof (? any) uintptr func @"".Offsetof (? any) uintptr
func @"".Sizeof (? any) uintptr func @"".Sizeof (? any) uintptr
func @"".Alignof (? any) uintptr func @"".Alignof (? any) uintptr
func @"".Typeof (i interface { }) interface { }
func @"".Reflect (i interface { }) (typ interface { }, addr @"".Pointer)
func @"".Unreflect (typ interface { }, addr @"".Pointer) interface { }
func @"".New (typ interface { }) @"".Pointer
func @"".NewArray (typ interface { }, n int) @"".Pointer
$$ $$
`) `)

View File

@ -49,10 +49,11 @@ import (
type gc_bin_parser struct { type gc_bin_parser struct {
data []byte data []byte
buf []byte // for reading strings buf []byte // for reading strings
version int version int // export format version
// object lists // object lists
strList []string // in order of appearance strList []string // in order of appearance
pathList []string // in order of appearance
pkgList []string // in order of appearance pkgList []string // in order of appearance
typList []ast.Expr // in order of appearance typList []ast.Expr // in order of appearance
callback func(pkg string, decl ast.Decl) callback func(pkg string, decl ast.Decl)
@ -74,6 +75,7 @@ func (p *gc_bin_parser) init(data []byte, pfc *package_file_cache) {
p.data = data p.data = data
p.version = -1 // unknown version p.version = -1 // unknown version
p.strList = []string{""} // empty string is mapped to 0 p.strList = []string{""} // empty string is mapped to 0
p.pathList = []string{""} // empty string is mapped to 0
p.pfc = pfc p.pfc = pfc
} }
@ -111,17 +113,10 @@ func (p *gc_bin_parser) parse_export(callback func(string, ast.Decl)) {
// read version specific flags - extend as necessary // read version specific flags - extend as necessary
switch p.version { switch p.version {
// case 4: // case 6:
// ... // ...
// fallthrough // fallthrough
case 3, 2, 1: case 5, 4, 3, 2, 1:
// Support for Go 1.8 type aliases will be added very
// soon (Oct 2016). In the meantime, we make a
// best-effort attempt to read v3 export data, failing
// if we encounter a type alias. This allows the
// automated builders to make progress since
// type aliases are not yet used in practice.
// TODO(gri): add support for type aliases.
p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.int() != 0 p.trackAllTypes = p.int() != 0
p.posInfoFormat = p.int() != 0 p.posInfoFormat = p.int() != 0
@ -137,7 +132,8 @@ func (p *gc_bin_parser) parse_export(callback func(string, ast.Decl)) {
p.typList = append(p.typList, predeclared...) p.typList = append(p.typList, predeclared...)
// read package data // read package data
p.pfc.defalias = p.pkg()[1:] pkgName := p.pkg()
p.pfc.defalias = pkgName[strings.LastIndex(pkgName, "!")+1:]
// read objects of phase 1 only (see cmd/compiler/internal/gc/bexport.go) // read objects of phase 1 only (see cmd/compiler/internal/gc/bexport.go)
objcount := 0 objcount := 0
@ -165,12 +161,17 @@ func (p *gc_bin_parser) pkg() string {
// otherwise, i is the package tag (< 0) // otherwise, i is the package tag (< 0)
if i != packageTag { if i != packageTag {
panic(fmt.Sprintf("unexpected package tag %d", i)) panic(fmt.Sprintf("unexpected package tag %d version %d", i, p.version))
} }
// read package data // read package data
name := p.string() name := p.string()
path := p.string() var path string
if p.version >= 5 {
path = p.path()
} else {
path = p.string()
}
// we should never see an empty package name // we should never see an empty package name
if name == "" { if name == "" {
@ -188,7 +189,7 @@ func (p *gc_bin_parser) pkg() string {
fullName = "!" + path + "!" + name fullName = "!" + path + "!" + name
p.pfc.add_package_to_scope(fullName, path) p.pfc.add_package_to_scope(fullName, path)
} else { } else {
fullName = "#" + name fullName = "!" + p.pfc.name + "!" + name
} }
// if the package was imported before, use that one; otherwise create a new one // if the package was imported before, use that one; otherwise create a new one
@ -213,6 +214,17 @@ func (p *gc_bin_parser) obj(tag int) {
}, },
}, },
}) })
case aliasTag:
// TODO(gri) verify type alias hookup is correct
p.pos()
pkg, name := p.qualifiedName()
typ := p.typ("")
p.callback(pkg, &ast.GenDecl{
Tok: token.TYPE,
Specs: []ast.Spec{typeAliasSpec(name, typ)},
})
case typeTag: case typeTag:
_ = p.typ("") _ = p.typ("")
@ -229,6 +241,7 @@ func (p *gc_bin_parser) obj(tag int) {
}, },
}, },
}) })
case funcTag: case funcTag:
p.pos() p.pos()
pkg, name := p.qualifiedName() pkg, name := p.qualifiedName()
@ -244,6 +257,8 @@ func (p *gc_bin_parser) obj(tag int) {
} }
} }
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
func (p *gc_bin_parser) pos() { func (p *gc_bin_parser) pos() {
if !p.posInfoFormat { if !p.posInfoFormat {
return return
@ -251,15 +266,26 @@ func (p *gc_bin_parser) pos() {
file := p.prevFile file := p.prevFile
line := p.prevLine line := p.prevLine
if delta := p.int(); delta != 0 { delta := p.int()
// line changed
line += delta line += delta
} else if n := p.int(); n >= 0 { if p.version >= 5 {
if delta == deltaNewFile {
if n := p.int(); n >= 0 {
// file changed
file = p.path()
line = n
}
}
} else {
if delta == 0 {
if n := p.int(); n >= 0 {
// file changed // file changed
file = p.prevFile[:n] + p.string() file = p.prevFile[:n] + p.string()
p.prevFile = file
line = p.int() line = p.int()
} }
}
}
p.prevFile = file
p.prevLine = line p.prevLine = line
// TODO(gri) register new position // TODO(gri) register new position
@ -391,10 +417,17 @@ func (p *gc_bin_parser) typ(parent string) ast.Expr {
case interfaceTag: case interfaceTag:
i := p.reserveMaybe() i := p.reserveMaybe()
if p.int() != 0 { var embeddeds []*ast.SelectorExpr
panic("unexpected embedded interface") for n := p.int(); n > 0; n-- {
p.pos()
if named, ok := p.typ(parent).(*ast.SelectorExpr); ok {
embeddeds = append(embeddeds, named)
}
} }
methods := p.methodList(parent) methods := p.methodList(parent)
for _, field := range embeddeds {
methods = append(methods, &ast.Field{Type: field})
}
return p.recordMaybe(i, &ast.InterfaceType{Methods: &ast.FieldList{List: methods}}) return p.recordMaybe(i, &ast.InterfaceType{Methods: &ast.FieldList{List: methods}})
case mapTag: case mapTag:
@ -429,17 +462,17 @@ func (p *gc_bin_parser) structType(parent string) *ast.StructType {
if n := p.int(); n > 0 { if n := p.int(); n > 0 {
fields = make([]*ast.Field, n) fields = make([]*ast.Field, n)
for i := range fields { for i := range fields {
fields[i] = p.field(parent) fields[i], _ = p.field(parent) // (*ast.Field, tag), not interested in tags
p.string() // tag, not interested in tags
} }
} }
return &ast.StructType{Fields: &ast.FieldList{List: fields}} return &ast.StructType{Fields: &ast.FieldList{List: fields}}
} }
func (p *gc_bin_parser) field(parent string) *ast.Field { func (p *gc_bin_parser) field(parent string) (*ast.Field, string) {
p.pos() p.pos()
_, name := p.fieldName(parent) _, name, _ := p.fieldName(parent)
typ := p.typ(parent) typ := p.typ(parent)
tag := p.string()
var names []*ast.Ident var names []*ast.Ident
if name != "" { if name != "" {
@ -448,7 +481,7 @@ func (p *gc_bin_parser) field(parent string) *ast.Field {
return &ast.Field{ return &ast.Field{
Names: names, Names: names,
Type: typ, Type: typ,
} }, tag
} }
func (p *gc_bin_parser) methodList(parent string) (methods []*ast.Field) { func (p *gc_bin_parser) methodList(parent string) (methods []*ast.Field) {
@ -463,7 +496,7 @@ func (p *gc_bin_parser) methodList(parent string) (methods []*ast.Field) {
func (p *gc_bin_parser) method(parent string) *ast.Field { func (p *gc_bin_parser) method(parent string) *ast.Field {
p.pos() p.pos()
_, name := p.fieldName(parent) _, name, _ := p.fieldName(parent)
params := p.paramList() params := p.paramList()
results := p.paramList() results := p.paramList()
return &ast.Field{ return &ast.Field{
@ -472,22 +505,32 @@ func (p *gc_bin_parser) method(parent string) *ast.Field {
} }
} }
func (p *gc_bin_parser) fieldName(parent string) (string, string) { func (p *gc_bin_parser) fieldName(parent string) (string, string, bool) {
name := p.string() name := p.string()
pkg := parent pkg := parent
if p.version == 0 && name == "_" { if p.version == 0 && name == "_" {
// versions < 1 don't export a package for _ fields // version 0 didn't export a package for _ fields
// TODO: remove once versions are not supported anymore return pkg, name, false
return pkg, name
} }
if name != "" && !exported(name) { var alias bool
// explicitly qualified field switch name {
if name == "?" { case "":
// 1) field name matches base type name and is exported: nothing to do
case "?":
// 2) field name matches base type name and is not exported: need package
name = "" name = ""
} pkg = p.pkg()
case "@":
// 3) field name doesn't match type name (alias)
name = p.string()
alias = true
fallthrough
default:
if !exported(name) {
pkg = p.pkg() pkg = p.pkg()
} }
return pkg, name }
return pkg, name, alias
} }
func (p *gc_bin_parser) paramList() *ast.FieldList { func (p *gc_bin_parser) paramList() *ast.FieldList {
@ -594,6 +637,26 @@ func (p *gc_bin_parser) int64() int64 {
return p.rawInt64() return p.rawInt64()
} }
func (p *gc_bin_parser) path() string {
if p.debugFormat {
p.marker('p')
}
// if the path was seen before, i is its index (>= 0)
// (the empty string is at index 0)
i := p.rawInt64()
if i >= 0 {
return p.pathList[i]
}
// otherwise, i is the negative path length (< 0)
a := make([]string, -i)
for n := range a {
a[n] = p.string()
}
s := strings.Join(a, "/")
p.pathList = append(p.pathList, s)
return s
}
func (p *gc_bin_parser) string() string { func (p *gc_bin_parser) string() string {
if p.debugFormat { if p.debugFormat {
p.marker('s') p.marker('s')
@ -710,7 +773,11 @@ const (
fractionTag // not used by gc fractionTag // not used by gc
complexTag complexTag
stringTag stringTag
nilTag // only used by gc (appears in exported inlined function bodies)
unknownTag // not used by gc (only appears in packages with errors) unknownTag // not used by gc (only appears in packages with errors)
// Type aliases
aliasTag
) )
var predeclared = []ast.Expr{ var predeclared = []ast.Expr{
@ -733,7 +800,7 @@ var predeclared = []ast.Expr{
ast.NewIdent("complex128"), ast.NewIdent("complex128"),
ast.NewIdent("string"), ast.NewIdent("string"),
// aliases // basic type aliases
ast.NewIdent("byte"), ast.NewIdent("byte"),
ast.NewIdent("rune"), ast.NewIdent("rune"),

View File

@ -172,7 +172,7 @@ func (p *gc_parser) parse_exported_name() *ast.SelectorExpr {
p.expect('@') p.expect('@')
pkg := p.parse_package() pkg := p.parse_package()
if pkg.Name == "" { if pkg.Name == "" {
pkg.Name = "#" + p.pfc.defalias pkg.Name = "!" + p.pfc.name + "!" + p.pfc.defalias
} else { } else {
pkg.Name = p.path_to_name[pkg.Name] pkg.Name = p.path_to_name[pkg.Name]
} }

View File

@ -1,4 +1,4 @@
// +build !go1.7 // +build !go1.7,!go1.8
package main package main

24
vendor/github.com/nsf/gocode/rpc.go generated vendored
View File

@ -136,3 +136,27 @@ func client_set(cli *rpc.Client, Arg0, Arg1 string) string {
} }
return reply.Arg0 return reply.Arg0
} }
// wrapper for: server_options
type Args_options struct {
Arg0 int
}
type Reply_options struct {
Arg0 string
}
func (r *RPC) RPC_options(args *Args_options, reply *Reply_options) error {
reply.Arg0 = server_options(args.Arg0)
return nil
}
func client_options(cli *rpc.Client, Arg0 int) string {
var args Args_options
var reply Reply_options
args.Arg0 = Arg0
err := cli.Call("RPC.RPC_options", &args, &reply)
if err != nil {
panic(err)
}
return reply.Arg0
}

View File

@ -5,12 +5,23 @@ package main
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
type scope struct { type scope struct {
// the package name that this scope resides in
pkgname string
parent *scope // nil for universe scope parent *scope // nil for universe scope
entities map[string]*decl entities map[string]*decl
} }
func new_named_scope(outer *scope, name string) *scope {
s := new_scope(outer)
s.pkgname = name
return s
}
func new_scope(outer *scope) *scope { func new_scope(outer *scope) *scope {
s := new(scope) s := new(scope)
if outer != nil {
s.pkgname = outer.pkgname
}
s.parent = outer s.parent = outer
s.entities = make(map[string]*decl) s.entities = make(map[string]*decl)
return s return s

View File

@ -137,7 +137,7 @@ func server_auto_complete(file []byte, filename string, cursor int, context_pack
if err := recover(); err != nil { if err := recover(); err != nil {
print_backtrace(err) print_backtrace(err)
c = []candidate{ c = []candidate{
{"PANIC", "PANIC", decl_invalid}, {"PANIC", "PANIC", decl_invalid, "panic"},
} }
// drop cache // drop cache
@ -186,6 +186,11 @@ func server_auto_complete(file []byte, filename string, cursor int, context_pack
var buf bytes.Buffer var buf bytes.Buffer
log.Printf("Got autocompletion request for '%s'\n", filename) log.Printf("Got autocompletion request for '%s'\n", filename)
log.Printf("Cursor at: %d\n", cursor) log.Printf("Cursor at: %d\n", cursor)
if cursor > len(file) || cursor < 0 {
log.Println("ERROR! Cursor is outside of the boundaries of the buffer, " +
"this is most likely a text editor plugin bug. Text editor is responsible " +
"for passing the correct cursor position to gocode.")
} else {
buf.WriteString("-------------------------------------------------------\n") buf.WriteString("-------------------------------------------------------\n")
buf.Write(file[:cursor]) buf.Write(file[:cursor])
buf.WriteString("#") buf.WriteString("#")
@ -193,6 +198,7 @@ func server_auto_complete(file []byte, filename string, cursor int, context_pack
log.Print(buf.String()) log.Print(buf.String())
log.Println("-------------------------------------------------------") log.Println("-------------------------------------------------------")
} }
}
candidates, d := g_daemon.autocomplete.apropos(file, filename, cursor) candidates, d := g_daemon.autocomplete.apropos(file, filename, cursor)
if *g_debug { if *g_debug {
log.Printf("Offset: %d\n", d) log.Printf("Offset: %d\n", d)
@ -235,3 +241,7 @@ func server_set(key, value string) string {
g_daemon.drop_cache() g_daemon.drop_cache()
return g_config.set_option(key, value) return g_config.set_option(key, value)
} }
func server_options(notused int) string {
return g_config.options()
}

View File

@ -0,0 +1,18 @@
// +build !go1.9,!go1.8.typealias
package main
import (
"go/ast"
)
func typeAliasSpec(name string, typ ast.Expr) *ast.TypeSpec {
return &ast.TypeSpec{
Name: ast.NewIdent(name),
Type: typ,
}
}
func isAliasTypeSpec(t *ast.TypeSpec) bool {
return false
}

View File

@ -0,0 +1,19 @@
// +build go1.9 go1.8.typealias
package main
import (
"go/ast"
)
func typeAliasSpec(name string, typ ast.Expr) *ast.TypeSpec {
return &ast.TypeSpec{
Name: ast.NewIdent(name),
Assign: 1,
Type: typ,
}
}
func isAliasTypeSpec(t *ast.TypeSpec) bool {
return t.Assign != 0
}

View File

@ -153,15 +153,24 @@ func find_gb_project_root(path string) (string, error) {
// vendorlessImportPath returns the devendorized version of the provided import path. // vendorlessImportPath returns the devendorized version of the provided import path.
// e.g. "foo/bar/vendor/a/b" => "a/b" // e.g. "foo/bar/vendor/a/b" => "a/b"
func vendorlessImportPath(ipath string) string { func vendorlessImportPath(ipath string, currentPackagePath string) (string, bool) {
split := strings.Split(ipath, "vendor/")
// no vendor in path
if len(split) == 1 {
return ipath, true
}
// this import path does not belong to the current package
if currentPackagePath != "" && !strings.Contains(currentPackagePath, split[0]) {
return "", false
}
// Devendorize for use in import statement. // Devendorize for use in import statement.
if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 {
return ipath[i+len("/vendor/"):] return ipath[i+len("/vendor/"):], true
} }
if strings.HasPrefix(ipath, "vendor/") { if strings.HasPrefix(ipath, "vendor/") {
return ipath[len("vendor/"):] return ipath[len("vendor/"):], true
} }
return ipath return ipath, true
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------

12
vendor/vendor.json vendored
View File

@ -3,10 +3,10 @@
"ignore": "test", "ignore": "test",
"package": [ "package": [
{ {
"checksumSHA1": "7NpGLW+EOhRLs5cDvi1S+LqUNQ8=", "checksumSHA1": "ZesPb1w0u5/uv/fdVc+G6wOee30=",
"path": "github.com/b3log/wide", "path": "github.com/b3log/wide",
"revision": "f96c8befdf3484ad4d3abdf7a11c7a3d6182d018", "revision": "bfd836c6fc8fbc007ce437db1406455493cd532f",
"revisionTime": "2018-03-13T04:09:30Z" "revisionTime": "2018-03-13T04:57:02Z"
}, },
{ {
"checksumSHA1": "83iEp3SqOoIkZUYyR7BOVP4vaGE=", "checksumSHA1": "83iEp3SqOoIkZUYyR7BOVP4vaGE=",
@ -45,10 +45,10 @@
"revisionTime": "2015-05-30T03:03:52Z" "revisionTime": "2015-05-30T03:03:52Z"
}, },
{ {
"checksumSHA1": "5i+5zScV0FNuG3cmRzRPn6PFsfo=", "checksumSHA1": "gKm38IQHqLfYHwS7B6JDTJzXYYI=",
"path": "github.com/nsf/gocode", "path": "github.com/nsf/gocode",
"revision": "5070dacabf2a80deeaf4ddb0be3761d06fce7be5", "revision": "416643789f088aa5077f667cecde7f966131f6be",
"revisionTime": "2016-11-22T21:38:51Z" "revisionTime": "2018-01-07T08:36:41Z"
}, },
{ {
"checksumSHA1": "4fp/TH5nX7seO5p4qJfIj/BokbI=", "checksumSHA1": "4fp/TH5nX7seO5p4qJfIj/BokbI=",