| // 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" |
| "io" |
| "io/ioutil" |
| "os" |
| "strings" |
| |
| "github.com/visualfc/gotools/command" |
| |
| "golang.org/x/tools/go/types" |
| ) |
| |
| var Command = &command.Command{ |
| Run: runAstView, |
| UsageLine: "astview [-stdin] files...", |
| Short: "print go files astview", |
| Long: `print go files astview`, |
| } |
| |
| var astViewStdin bool |
| |
| func init() { |
| Command.Flag.BoolVar(&astViewStdin, "stdin", false, "input from stdin") |
| } |
| |
| 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" |
| ) |
| |
| 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 |
| } |
| |
| func (p *PackageView) posText(pos token.Position) (s string) { |
| index := p.posFileIndex(pos) |
| 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() |
| pkgs, pkgsfiles, err := ParseFiles(fset, filenames, parser.AllErrors) |
| 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 |
| file, err := parser.ParseFile(p.fset, filename, src, 0) |
| 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()) |
| if p.expr { |
| fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, f.Name, p.posText(pos), types.ExprString(f.Decl.Type)) |
| } else { |
| fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, f.Name, p.posText(pos)) |
| } |
| } |
| } |
| |
| 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()) |
| for i := 0; i < len(m.Names); i++ { |
| if p.expr && m.Type != nil { |
| fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, m.Names[i], p.posText(pos), types.ExprString(m.Type)) |
| } else { |
| fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, m.Names[i], p.posText(pos)) |
| } |
| } |
| } |
| } |
| } |
| } |
| 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()) |
| fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, d.Type.Name, p.posText(pos)) |
| 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()) |
| if list.Type != nil { |
| fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag_type_value, m.Name, p.posText(pos), types.ExprString(list.Type)) |
| } else { |
| fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_value, m.Name, p.posText(pos)) |
| } |
| } |
| } |
| 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()) |
| fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_method, m.Name, p.posText(pos)) |
| } |
| } |
| } |
| } |
| |
| 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++ |
| } |
| 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()) |
| ps = append(ps, p.posText(pos)) |
| } |
| } |
| } |
| 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) |
| } |
| |
| 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) |
| } |
| |
| // level,tag,pos@info |
| func (p *PackageView) PrintTree(w io.Writer) { |
| p.PrintPackage(w, 0) |
| } |