wide/vendor/github.com/visualfc/gotools/astview/astview.go

409 lines
10 KiB
Go
Raw Normal View History

2018-03-13 07:32:44 +03:00
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package astview
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
2018-03-13 08:19:03 +03:00
"go/types"
2018-03-13 07:32:44 +03:00
"io"
"io/ioutil"
"os"
2018-03-13 08:19:03 +03:00
"path/filepath"
2018-03-13 07:32:44 +03:00
"strings"
"github.com/visualfc/gotools/command"
2018-03-13 08:19:03 +03:00
"github.com/visualfc/gotools/pkgutil"
2018-03-13 07:32:44 +03:00
)
var Command = &command.Command{
Run: runAstView,
UsageLine: "astview [-stdin] files...",
Short: "print go files astview",
Long: `print go files astview`,
}
var astViewStdin bool
2018-03-13 08:19:03 +03:00
var astViewShowEndPos bool
var astViewShowTodo bool
2018-03-13 07:32:44 +03:00
func init() {
Command.Flag.BoolVar(&astViewStdin, "stdin", false, "input from stdin")
2018-03-13 08:19:03 +03:00
Command.Flag.BoolVar(&astViewShowEndPos, "end", false, "show decl end pos")
Command.Flag.BoolVar(&astViewShowTodo, "todo", false, "show todo list")
2018-03-13 07:32:44 +03:00
}
func runAstView(cmd *command.Command, args []string) error {
if len(args) == 0 {
cmd.Usage()
return os.ErrInvalid
}
if astViewStdin {
view, err := NewFilePackageSource(args[0], os.Stdin, true)
if err != nil {
fmt.Fprintf(os.Stderr, "astview: %s", err)
command.SetExitStatus(3)
command.Exit()
}
view.PrintTree(os.Stdout)
} else {
err := PrintFilesTree(args, os.Stdout, true)
if err != nil {
fmt.Fprintf(os.Stderr, "astview:%s", err)
command.SetExitStatus(3)
command.Exit()
}
}
return nil
}
const (
tag_package = "p"
tag_imports_folder = "+m"
tag_import = "mm"
tag_type = "t"
tag_struct = "s"
tag_interface = "i"
tag_value = "v"
tag_const = "c"
tag_func = "f"
tag_value_folder = "+v"
tag_const_folder = "+c"
tag_func_folder = "+f"
tag_factor_folder = "+tf"
tag_type_method = "tm"
tag_type_factor = "tf"
tag_type_value = "tv"
2018-03-13 08:19:03 +03:00
tag_todo = "b"
tag_todo_folder = "+b"
2018-03-13 07:32:44 +03:00
)
type PackageView struct {
fset *token.FileSet
pdoc *PackageDoc
pkg *ast.Package
expr bool
}
var AllFiles []string
func (p *PackageView) posFileIndex(pos token.Position) int {
var index = -1
for i := 0; i < len(AllFiles); i++ {
if AllFiles[i] == pos.Filename {
index = i
break
}
}
if index == -1 {
AllFiles = append(AllFiles, pos.Filename)
index = len(AllFiles) - 1
}
return index
}
2018-03-13 08:19:03 +03:00
func (p *PackageView) posText(pos token.Position, end token.Position) (s string) {
2018-03-13 07:32:44 +03:00
index := p.posFileIndex(pos)
2018-03-13 08:19:03 +03:00
if astViewShowEndPos {
return fmt.Sprintf("%d:%d:%d:%d:%d", index, pos.Line, pos.Column, end.Line, end.Column)
}
2018-03-13 07:32:44 +03:00
return fmt.Sprintf("%d:%d:%d", index, pos.Line, pos.Column)
}
func NewFilePackage(filename string) (*PackageView, error) {
p := new(PackageView)
p.fset = token.NewFileSet()
file, err := parser.ParseFile(p.fset, filename, nil, parser.AllErrors)
if file == nil {
return nil, err
}
m := make(map[string]*ast.File)
m[filename] = file
pkg, err := ast.NewPackage(p.fset, m, nil, nil)
if err != nil {
return nil, err
}
p.pkg = pkg
p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
return p, nil
}
func NewPackageView(pkg *ast.Package, fset *token.FileSet, expr bool) (*PackageView, error) {
p := new(PackageView)
p.fset = fset
p.pkg = pkg
p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
p.expr = expr
return p, nil
}
func ParseFiles(fset *token.FileSet, filenames []string, mode parser.Mode) (pkgs map[string]*ast.Package, pkgsfiles []string, first error) {
pkgs = make(map[string]*ast.Package)
for _, filename := range filenames {
if src, err := parser.ParseFile(fset, filename, nil, mode); src != nil {
name := src.Name.Name
pkg, found := pkgs[name]
if !found {
pkg = &ast.Package{
Name: name,
Files: make(map[string]*ast.File),
}
pkgs[name] = pkg
}
pkg.Files[filename] = src
pkgsfiles = append(pkgsfiles, filename)
} else {
first = err
return
}
}
return
}
func PrintFilesTree(filenames []string, w io.Writer, expr bool) error {
fset := token.NewFileSet()
2018-03-13 08:19:03 +03:00
mode := parser.AllErrors
if astViewShowTodo {
mode |= parser.ParseComments
}
pkgs, pkgsfiles, err := ParseFiles(fset, filenames, mode)
2018-03-13 07:32:44 +03:00
if err != nil {
return err
}
AllFiles = pkgsfiles
for i := 0; i < len(AllFiles); i++ {
fmt.Fprintf(w, "@%s\n", AllFiles[i])
}
for _, pkg := range pkgs {
view, err := NewPackageView(pkg, fset, expr)
if err != nil {
return err
}
view.PrintTree(w)
}
return nil
}
func NewFilePackageSource(filename string, f *os.File, expr bool) (*PackageView, error) {
src, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
p := new(PackageView)
p.fset = token.NewFileSet()
p.expr = expr
2018-03-13 08:19:03 +03:00
mode := parser.AllErrors
if astViewShowTodo {
mode |= parser.ParseComments
}
file, err := parser.ParseFile(p.fset, filename, src, mode)
2018-03-13 07:32:44 +03:00
if err != nil {
return nil, err
}
m := make(map[string]*ast.File)
m[filename] = file
pkg, err := ast.NewPackage(p.fset, m, nil, nil)
if err != nil {
return nil, err
}
p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
return p, nil
}
func (p *PackageView) printFuncsHelper(w io.Writer, funcs []*FuncDoc, level int, tag string, tag_folder string) {
for _, f := range funcs {
pos := p.fset.Position(f.Decl.Pos())
2018-03-13 08:19:03 +03:00
end := p.fset.Position(f.Decl.End())
2018-03-13 07:32:44 +03:00
if p.expr {
2018-03-13 08:19:03 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, f.Name, p.posText(pos, end), types.ExprString(f.Decl.Type))
2018-03-13 07:32:44 +03:00
} else {
2018-03-13 08:19:03 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, f.Name, p.posText(pos, end))
2018-03-13 07:32:44 +03:00
}
}
}
func (p *PackageView) PrintVars(w io.Writer, vars []*ValueDoc, level int, tag string, tag_folder string) {
if len(tag_folder) > 0 && len(vars) > 0 {
if tag_folder == tag_value_folder {
fmt.Fprintf(w, "%d,%s,Variables\n", level, tag_folder)
} else if tag_folder == tag_const_folder {
fmt.Fprintf(w, "%d,%s,Constants\n", level, tag_folder)
}
level++
}
for _, v := range vars {
if v.Decl == nil {
continue
}
for _, s := range v.Decl.Specs {
if m, ok := s.(*ast.ValueSpec); ok {
pos := p.fset.Position(m.Pos())
2018-03-13 08:19:03 +03:00
end := p.fset.Position(m.End())
2018-03-13 07:32:44 +03:00
for i := 0; i < len(m.Names); i++ {
if p.expr && m.Type != nil {
2018-03-13 08:19:03 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, m.Names[i], p.posText(pos, end), types.ExprString(m.Type))
2018-03-13 07:32:44 +03:00
} else {
2018-03-13 08:19:03 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, m.Names[i], p.posText(pos, end))
2018-03-13 07:32:44 +03:00
}
}
}
}
}
}
func (p *PackageView) PrintTypes(w io.Writer, types []*TypeDoc, level int) {
for _, d := range types {
if d.Decl == nil {
continue
}
typespec := d.Decl.Specs[0].(*ast.TypeSpec)
var tag = tag_type
if _, ok := typespec.Type.(*ast.InterfaceType); ok {
tag = tag_interface
} else if _, ok := typespec.Type.(*ast.StructType); ok {
tag = tag_struct
}
pos := p.fset.Position(d.Decl.Pos())
2018-03-13 08:19:03 +03:00
end := p.fset.Position(d.Decl.End())
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, d.Type.Name, p.posText(pos, end))
2018-03-13 07:32:44 +03:00
p.printFuncsHelper(w, d.Funcs, level+1, tag_type_factor, "")
p.printFuncsHelper(w, d.Methods, level+1, tag_type_method, "")
p.PrintTypeFields(w, d.Decl, level+1)
//p.PrintVars(w, d.Consts, level+1, tag_const, "")
//p.PrintVars(w, d.Vars, level+1, tag_value, "")
}
}
func (p *PackageView) PrintTypeFields(w io.Writer, decl *ast.GenDecl, level int) {
spec, ok := decl.Specs[0].(*ast.TypeSpec)
if ok == false {
return
}
switch d := spec.Type.(type) {
case *ast.StructType:
for _, list := range d.Fields.List {
if list.Names == nil {
continue
}
for _, m := range list.Names {
pos := p.fset.Position(m.Pos())
2018-03-13 08:19:03 +03:00
end := p.fset.Position(m.End())
2018-03-13 07:32:44 +03:00
if list.Type != nil {
2018-03-13 08:19:03 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag_type_value, m.Name, p.posText(pos, end), types.ExprString(list.Type))
2018-03-13 07:32:44 +03:00
} else {
2018-03-13 08:19:03 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_value, m.Name, p.posText(pos, end))
2018-03-13 07:32:44 +03:00
}
}
}
case *ast.InterfaceType:
for _, list := range d.Methods.List {
if list.Names == nil {
continue
}
for _, m := range list.Names {
pos := p.fset.Position(m.Pos())
2018-03-13 08:19:03 +03:00
end := p.fset.Position(m.End())
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_method, m.Name, p.posText(pos, end))
2018-03-13 07:32:44 +03:00
}
}
}
}
func (p *PackageView) PrintHeader(w io.Writer, level int) {
fmt.Fprintf(w, "%d,%s,%s\n", level, tag_package, p.pdoc.PackageName)
}
func (p *PackageView) PrintImports(w io.Writer, level int, tag, tag_folder string) {
if tag_folder != "" && len(p.pdoc.Imports) > 0 {
fmt.Fprintf(w, "%d,%s,%s\n", level, tag_folder, "Imports")
level++
}
2018-03-13 08:19:03 +03:00
var parentPkg *pkgutil.Package
if pkgutil.IsVendorExperiment() {
for filename, _ := range p.pkg.Files {
if !filepath.IsAbs(filename) {
name, err := filepath.Abs(filename)
if err == nil {
filename = name
}
}
parentPkg = pkgutil.ImportFile(filename)
break
}
}
2018-03-13 07:32:44 +03:00
for _, name := range p.pdoc.Imports {
vname := "\"" + name + "\""
var ps []string
for _, file := range p.pkg.Files {
for _, v := range file.Imports {
if v.Path.Value == vname {
pos := p.fset.Position(v.Pos())
2018-03-13 08:19:03 +03:00
end := p.fset.Position(v.End())
ps = append(ps, p.posText(pos, end))
2018-03-13 07:32:44 +03:00
}
}
}
2018-03-13 08:19:03 +03:00
if parentPkg != nil {
name = pkgutil.VendoredImportPath(parentPkg, name)
}
2018-03-13 07:32:44 +03:00
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, name, strings.Join(ps, ";"))
}
}
func (p *PackageView) PrintFuncs(w io.Writer, level int, tag_folder string) {
hasFolder := false
if len(p.pdoc.Funcs) > 0 || len(p.pdoc.Factorys) > 0 {
hasFolder = true
}
if !hasFolder {
return
}
if len(tag_folder) > 0 {
fmt.Fprintf(w, "%d,%s,Functions\n", level, tag_folder)
level++
}
p.printFuncsHelper(w, p.pdoc.Factorys, level, tag_type_factor, tag_func_folder)
p.printFuncsHelper(w, p.pdoc.Funcs, level, tag_func, tag_func_folder)
}
2018-03-13 08:19:03 +03:00
func (p *PackageView) PrintTodos(w io.Writer, level int, tag, tag_folder string) {
hasFolder := false
if len(p.pdoc.Todos) > 0 {
hasFolder = true
}
if !hasFolder {
return
}
if len(tag_folder) > 0 {
fmt.Fprintf(w, "%d,%s,TodoList\n", level, tag_folder)
level++
}
for _, todo := range p.pdoc.Todos {
c := todo.Comments.List[0]
pos := p.fset.Position(c.Pos())
end := p.fset.Position(c.End())
ps := p.posText(pos, end)
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, todo.Tag, ps, todo.Text)
}
}
2018-03-13 07:32:44 +03:00
func (p *PackageView) PrintPackage(w io.Writer, level int) {
p.PrintHeader(w, level)
level++
p.PrintImports(w, level, tag_import, tag_imports_folder)
p.PrintVars(w, p.pdoc.Vars, level, tag_value, tag_value_folder)
p.PrintVars(w, p.pdoc.Consts, level, tag_const, tag_const_folder)
p.PrintFuncs(w, level, tag_func_folder)
p.PrintTypes(w, p.pdoc.Types, level)
2018-03-13 08:19:03 +03:00
p.PrintTodos(w, level, tag_todo, tag_todo_folder)
2018-03-13 07:32:44 +03:00
}
// level,tag,pos@info
func (p *PackageView) PrintTree(w io.Writer) {
p.PrintPackage(w, 0)
}