Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Commit

Permalink
export: support j2d and find refs for imports (#18)
Browse files Browse the repository at this point in the history
* *: add -verbose and -debug flags

* export: print statistics after export

* Finish up

* log: send log to stdout

* export: support imports

* export/exporter: add note
  • Loading branch information
unknwon committed Aug 6, 2019
1 parent 2959006 commit ca18143
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 43 deletions.
81 changes: 38 additions & 43 deletions export/exporter.go
Expand Up @@ -2,7 +2,6 @@
package export

import (
"bytes"
"encoding/json"
"fmt"
"go/ast"
Expand All @@ -12,8 +11,8 @@ import (
"io/ioutil"
"path/filepath"
"strconv"
"strings"

doc "github.com/slimsag/godocmd"
"github.com/sourcegraph/lsif-go/log"
"github.com/sourcegraph/lsif-go/protocol"
"golang.org/x/tools/go/packages"
Expand Down Expand Up @@ -193,6 +192,10 @@ func (e *exporter) exportPkg(p *packages.Package, proID string) (err error) {
e.files[fpos.Filename] = fi
}

if err = e.addImports(p, f, fi); err != nil {
return fmt.Errorf("error exporting imports of %q: %v", p.PkgPath, err)
}

if err = e.exportDefs(p, f, fi, proID, fpos.Filename); err != nil {
return fmt.Errorf("error exporting definitions of %q: %v", p.PkgPath, err)
}
Expand All @@ -211,16 +214,44 @@ func (e *exporter) exportPkg(p *packages.Package, proID string) (err error) {
return nil
}

func (e *exporter) exportDefs(p *packages.Package, f *ast.File, fi *fileInfo, proID, filename string) (err error) {
// addImports constructs *ast.Ident and types.Object out of *ImportSpec and inserts them into
// packages defs map to be exported within a unified process.
func (e *exporter) addImports(p *packages.Package, f *ast.File, fi *fileInfo) error {
for _, ispec := range f.Imports {
// The path value comes from *ImportSpec has surrounding double quotes.
// We should preserve its original format in constructing related AST objects
// for any possible consumers. We use trimmed version here only when we need to
// (trimmed version as a map key or an argument).
ipath := strings.Trim(ispec.Path.Value, `"`)
var name string
if ispec.Name == nil {
name = ispec.Path.Value
} else {
name = ispec.Name.String()
}
p.TypesInfo.Defs[&ast.Ident{
NamePos: ispec.Pos(),
Name: name,
Obj: ast.NewObj(ast.Pkg, name),
}] = types.NewPkgName(ispec.Pos(), p.Types, name, p.Imports[ipath].Types)
log.Debugln("[import] Path:", ipath)
log.Debugln("[import] Name:", ispec.Name)
log.Debugln("[import] iPos:", p.Fset.Position(ispec.Pos()))
}
return nil
}

func (e *exporter) exportDefs(p *packages.Package, f *ast.File, fi *fileInfo, proID, filename string) error {
var rangeIDs []string
for ident, obj := range p.TypesInfo.Defs {
// Object is nil when not denote an object
if obj == nil {
continue
}

// TODO(jchen): emit other documents on the fly, thus we do not need to iterate
// over this map once for every file.
// Only emit if the object belongs to current file
// TODO(jchen): maybe emit other documents on the fly
ipos := p.Fset.Position(ident.Pos())
if ipos.Filename != filename {
continue
Expand Down Expand Up @@ -251,44 +282,9 @@ func (e *exporter) exportDefs(p *packages.Package, f *ast.File, fi *fileInfo, pr
return fmt.Errorf(`emit "next": %v`, err)
}

// TODO(jchen): make the following block of code to function "findContents".
qf := func(*types.Package) string { return "" }
var s string
var extra string
if f, ok := obj.(*types.Var); ok && f.IsField() {
// TODO(jchen): make this be like (T).F not "struct field F string".
s = "struct " + obj.String()
} else {
if obj, ok := obj.(*types.TypeName); ok {
typ := obj.Type().Underlying()
if _, ok := typ.(*types.Struct); ok {
s = "type " + obj.Name() + " struct"
extra = prettyPrintTypesString(types.TypeString(typ, qf))
}
if _, ok := typ.(*types.Interface); ok {
s = "type " + obj.Name() + " interface"
extra = prettyPrintTypesString(types.TypeString(typ, qf))
}
}
if s == "" {
s = types.ObjectString(obj, qf)
}
}

contents := []protocol.MarkedString{
protocol.NewMarkedString(s),
}
comments, err := findComments(f, obj)
contents, err := findContents(f, obj)
if err != nil {
return fmt.Errorf("find comments: %v", err)
}
if comments != "" {
var b bytes.Buffer
doc.ToMarkdown(&b, comments, nil)
contents = append(contents, protocol.RawMarkedString(b.String()))
}
if extra != "" {
contents = append(contents, protocol.NewMarkedString(extra))
return fmt.Errorf("find contents: %v", err)
}

switch v := obj.(type) {
Expand Down Expand Up @@ -340,8 +336,7 @@ func (e *exporter) exportDefs(p *packages.Package, f *ast.File, fi *fileInfo, pr
}

case *types.PkgName:
// TODO: support import paths are not renamed
log.Debugln("[pkgname] Use:", ident)
log.Debugln("[pkgname] Def:", ident)
log.Debugln("[pkgname] iPos:", ipos)
e.imports[ident.Pos()] = &defInfo{
rangeID: rangeID,
Expand Down
45 changes: 45 additions & 0 deletions export/helper.go
Expand Up @@ -2,11 +2,13 @@ package export

import (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"strings"

doc "github.com/slimsag/godocmd"
"github.com/sourcegraph/lsif-go/protocol"
"golang.org/x/tools/go/ast/astutil"
)
Expand All @@ -23,6 +25,49 @@ func lspRange(pos token.Position, name string) (start protocol.Pos, end protocol
}
}

// findContents returns contents used as hover info for given object.
func findContents(f *ast.File, obj types.Object) ([]protocol.MarkedString, error) {
qf := func(*types.Package) string { return "" }
var s string
var extra string
if f, ok := obj.(*types.Var); ok && f.IsField() {
// TODO(jchen): make this be like (T).F not "struct field F string".
s = "struct " + obj.String()
} else {
if obj, ok := obj.(*types.TypeName); ok {
typ := obj.Type().Underlying()
if _, ok := typ.(*types.Struct); ok {
s = "type " + obj.Name() + " struct"
extra = prettyPrintTypesString(types.TypeString(typ, qf))
}
if _, ok := typ.(*types.Interface); ok {
s = "type " + obj.Name() + " interface"
extra = prettyPrintTypesString(types.TypeString(typ, qf))
}
}
if s == "" {
s = types.ObjectString(obj, qf)
}
}

contents := []protocol.MarkedString{
protocol.NewMarkedString(s),
}
comments, err := findComments(f, obj)
if err != nil {
return nil, fmt.Errorf("find comments: %v", err)
}
if comments != "" {
var b bytes.Buffer
doc.ToMarkdown(&b, comments, nil)
contents = append(contents, protocol.RawMarkedString(b.String()))
}
if extra != "" {
contents = append(contents, protocol.NewMarkedString(extra))
}
return contents, nil
}

// prettyPrintTypesString is pretty printing specific to the output of
// types.*String. Instead of re-implementing the printer, we can just
// transform its output.
Expand Down

0 comments on commit ca18143

Please sign in to comment.