// FormatCode runs "goimports -w" on the source file. func (f *SourceFile) FormatCode() error { // Parse file into AST fset := token.NewFileSet() file, err := parser.ParseFile(fset, f.Abs(), nil, parser.ParseComments) if err != nil { content, _ := ioutil.ReadFile(f.Abs()) var buf bytes.Buffer scanner.PrintError(&buf, err) return fmt.Errorf("%s\n========\nContent:\n%s", buf.String(), content) } // Clean unused imports imports := astutil.Imports(fset, file) for _, group := range imports { for _, imp := range group { path := strings.Trim(imp.Path.Value, `"`) if !astutil.UsesImport(file, path) { if imp.Name != nil { astutil.DeleteNamedImport(fset, file, imp.Name.Name, path) } else { astutil.DeleteImport(fset, file, path) } } } } ast.SortImports(fset, file) // Open file to be written w, err := os.OpenFile(f.Abs(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { return err } defer w.Close() // Write formatted code without unused imports return format.Node(w, fset, file) }
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 }
func (d *DirectiveList) Save() error { fset := token.NewFileSet() f, err := parser.ParseFile(fset, d.file, nil, 0) if err != nil { return ErrInvalidDirectiveFile } imps := astutil.Imports(fset, f) for _, imp := range imps[0] { name := "" if imp.Name != nil && imp.Name.String() != "." { name = imp.Name.String() } path, _ := strconv.Unquote(imp.Path.Value) if name != "" { astutil.DeleteNamedImport(fset, f, name, path) } else { astutil.DeleteImport(fset, f, path) } } astutil.AddImport(fset, f, "github.com/mholt/caddy/caddy/https") astutil.AddImport(fset, f, "github.com/mholt/caddy/caddy/parse") astutil.AddImport(fset, f, "github.com/mholt/caddy/caddy/setup") astutil.AddImport(fset, f, "github.com/mholt/caddy/middleware") importName := "" for _, dir := range d.list { if dir.Removed { continue } if dir.Core == false && len(dir.ImportPath) != 0 { importName = strings.Replace(dir.Name, "-", "_", -1) astutil.AddImport(fset, f, fmt.Sprintf("{{import-%s}}", importName)) } } var buf bytes.Buffer err = printer.Fprint(&buf, fset, f) if err != nil { return err } out := buf.String() f, err = parser.ParseFile(token.NewFileSet(), "", out, 0) node, ok := f.Scope.Lookup("directiveOrder").Decl.(ast.Node) if !ok { return ErrInvalidDirectiveFile } begin := out[0 : node.Pos()-1] end := out[node.End():len(out)] dirOrder := "" for _, dir := range d.list { if dir.Removed { continue } comment := "" if dir.Active == false { comment = "//@caddyext " } importName = strings.Replace(dir.Name, "-", "_", -1) begin = strings.Replace(begin, fmt.Sprintf(`"{{import-%s}}"`, importName), fmt.Sprintf(`%s%s "%s"`, comment, importName, dir.ImportPath), -1) if dir.Core == true { dirOrder = dirOrder + fmt.Sprintf(` %s{"%s", %s},`+"\n", comment, dir.Name, dir.Setup) } else { dirOrder = dirOrder + fmt.Sprintf(` %s{"%s", %s.Setup},`+"\n", comment, dir.Name, importName) } } out = begin + dirOrderStart + dirOrder + dirOrderEnd + end return ioutil.WriteFile(d.file, []byte(out), os.FileMode(0660)) }