func registerPlugins(src string, plugins features.Plugins) error { fset := token.NewFileSet() file := filepath.Join(src, importFile) f, err := parser.ParseFile(fset, file, nil, 0) if err != nil { return err } for _, m := range plugins.Packages() { astutil.AddNamedImport(fset, f, "_", m) } var buf bytes.Buffer err = printer.Fprint(&buf, fset, f) if err != nil { return err } // Save generated code file err = ioutil.WriteFile(file, buf.Bytes(), os.FileMode(0660)) if err != nil { return err } // run `go generate` for all plugins return goGenerate(plugins) }
func args(filename string) { b, err := ioutil.ReadFile(filename) if err != nil { panic(err) } p := parser.ParseFromString(filename, string(b)+"\n") a := generator.GenerateAST(p) fset := token.NewFileSet() defaultImports := []string{"github.com/gsp-lang/stdlib/prelude", "github.com/gsp-lang/gsp/core"} for _, defaultImport := range defaultImports { split := strings.Split(defaultImport, "/") pkgName := split[len(split)-1] if !(a.Name.Name == "prelude" && pkgName == "prelude") { if pkgName == "prelude" { astutil.AddNamedImport(fset, a, "_", defaultImport) } else { astutil.AddImport(fset, a, defaultImport) } } } var buf bytes.Buffer printer.Fprint(&buf, fset, a) fmt.Printf("%s\n", buf.String()) }
// Generates a Go file with the given name using the provided template and // template data. func (g *goFileGenerator) Generate(filename, tmpl string, data interface{}) ([]byte, error) { funcs := template.FuncMap{ "import": g.Import, "formatType": g.FormatType, } for k, v := range g.templateFuncs { funcs[k] = v } t, err := template.New(filename).Delims("<", ">").Funcs(funcs).Parse(tmpl) if err != nil { return nil, fmt.Errorf("failed to parse template %q: %v", filename, err) } var buff bytes.Buffer if err := t.Execute(&buff, data); err != nil { return nil, err } fset := token.NewFileSet() f, err := parser.ParseFile(fset, filename, buff.Bytes(), parser.ParseComments) if err != nil { return nil, fmt.Errorf("failed to parse generated code: %v:\n%s", err, buff.String()) } if len(f.Imports) > 0 { return nil, fmt.Errorf( "plain imports are not allowed with GoFileFromTemplate: use the import function") } importPaths := make([]string, 0, len(g.imports)) for path := range g.imports { importPaths = append(importPaths, path) } sort.Strings(importPaths) for _, path := range importPaths { astutil.AddNamedImport(fset, f, g.imports[path], path) } cfg := printer.Config{ Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8, } buff = bytes.Buffer{} if err := cfg.Fprint(&buff, fset, f); err != nil { return nil, err // TODO wrap error } return buff.Bytes(), nil }
func addImports(file *ast.File, fset *token.FileSet, dirPath string) (*ast.File, *token.FileSet, error) { imports, err := getImports(dirPath, fset) if err != nil { return nil, nil, err } for _, s := range imports { unquotedPath, err := strconv.Unquote(s.Path.Value) if err != nil { return nil, nil, err } if s.Name != nil { astutil.AddNamedImport(fset, file, s.Name.Name, unquotedPath) continue } astutil.AddImport(fset, file, unquotedPath) } return file, fset, nil }
// AddImport will add an import to source code func AddImport(source, path, alias string) string { header, body := header(source) if header == "" { panic("parse failure") } src := []byte(header) fset := token.NewFileSet() f, err := parser.ParseFile(fset, "", src, 0) if err != nil { panic(err) } astutil.AddNamedImport(fset, f, alias, path) ast.SortImports(fset, f) var buf bytes.Buffer err = printer.Fprint(&buf, fset, f) if err != nil { panic(err) } return buf.String() + "\n" + body }
func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) { // refs are a set of possible package references currently unsatisfied by imports. // first key: either base package (e.g. "fmt") or renamed package // second key: referenced package symbol (e.g. "Println") refs := make(map[string]map[string]bool) // decls are the current package imports. key is base package or renamed package. decls := make(map[string]*ast.ImportSpec) abs, err := filepath.Abs(filename) if err != nil { return nil, err } srcDir := path.Dir(abs) // collect potential uses of packages. var visitor visitFn visitor = visitFn(func(node ast.Node) ast.Visitor { if node == nil { return visitor } switch v := node.(type) { case *ast.ImportSpec: if v.Name != nil { decls[v.Name.Name] = v } else { local := importPathToName(strings.Trim(v.Path.Value, `\"`), srcDir) decls[local] = v } case *ast.SelectorExpr: xident, ok := v.X.(*ast.Ident) if !ok { break } if xident.Obj != nil { // if the parser can resolve it, it's not a package ref break } pkgName := xident.Name if refs[pkgName] == nil { refs[pkgName] = make(map[string]bool) } if decls[pkgName] == nil { refs[pkgName][v.Sel.Name] = true } } return visitor }) ast.Walk(visitor, f) // Nil out any unused ImportSpecs, to be removed in following passes unusedImport := map[string]string{} for pkg, is := range decls { if refs[pkg] == nil && pkg != "_" && pkg != "." { name := "" if is.Name != nil { name = is.Name.Name } unusedImport[strings.Trim(is.Path.Value, `"`)] = name } } for ipath, name := range unusedImport { if ipath == "C" { // Don't remove cgo stuff. continue } astutil.DeleteNamedImport(fset, f, name, ipath) } // Search for imports matching potential package references. searches := 0 type result struct { ipath string name string err error } results := make(chan result) for pkgName, symbols := range refs { if len(symbols) == 0 { continue // skip over packages already imported } go func(pkgName string, symbols map[string]bool) { ipath, rename, err := findImport(pkgName, symbols, filename) r := result{ipath: ipath, err: err} if rename { r.name = pkgName } results <- r }(pkgName, symbols) searches++ } for i := 0; i < searches; i++ { result := <-results if result.err != nil { return nil, result.err } if result.ipath != "" { if result.name != "" { astutil.AddNamedImport(fset, f, result.name, result.ipath) } else { astutil.AddImport(fset, f, result.ipath) } added = append(added, result.ipath) } } return added, nil }