Example #1
0
// 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)
}
Example #2
0
// FormatCode runs "goimports -w" on the source file.
func (f *SourceFile) FormatCode() error {
	if NoFormat {
		return nil
	}
	// Parse file into AST
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, f.Abs(), nil, parser.ParseComments)
	if err != nil {
		return err
	}
	// 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) {
				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)
}
Example #3
0
// importPaths returns a list of import paths for the given go source file
func importPaths(src string) ([]string, error) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, src, nil, 0)
	if err != nil {
		return nil, err
	}

	imps := astutil.Imports(fset, f)
	paths := make([]string, len(imps[0]))

	for i, imp := range imps[0] {
		if path := imp.Path; path != nil {
			paths[i], _ = strconv.Unquote(path.Value)
		}
	}

	return paths, nil
}
Example #4
0
// Clean writes the clean source to io.Writer. The source can be a io.Reader,
// string or []bytes
func Clean(w io.Writer, src interface{}) error {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "clean.go", src, parser.ParseComments)
	if err != nil {
		return err
	}
	// 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) {
				astutil.DeleteImport(fset, file, path)
			}
		}
	}
	ast.SortImports(fset, file)
	// Write formatted code without unused imports
	return format.Node(w, fset, file)
}
Example #5
0
func sortImports2(fset *token.FileSet, f *ast.File) (fset2 *token.FileSet, f2 *ast.File) {
	sortImports(fset, f)
	imps := astutil.Imports(fset, f)

	var spacesBefore []string // import paths we need spaces before
	for _, impSection := range imps {
		// Within each block of contiguous imports, see if any
		// import lines are in different group numbers. If so,
		// we'll need to put a space between them so it's
		// compatible with gofmt.
		lastGroup := -1
		for _, importSpec := range impSection {
			importPath, _ := strconv.Unquote(importSpec.Path.Value)
			groupNum := importGroup(importPath)
			if groupNum != lastGroup && lastGroup != -1 {
				spacesBefore = append(spacesBefore, importPath)
			}
			lastGroup = groupNum
		}
	}

	// So gross. Print out the entire AST, add a space by modifying the bytes, and reparse the whole thing.
	// Because I don't see an API that'd let me modify (insert a newline) the FileSet directly.
	// We really need a go/ast v2, which is as friendly to parsing/printing/formatting as current, but
	// also friendly towards direct AST modification... To avoid all these horrible hacks.
	out := []byte(gist5639599.SprintAst(fset, f))
	if len(spacesBefore) > 0 {
		out = addImportSpaces(bytes.NewReader(out), spacesBefore)
	}

	fset2 = token.NewFileSet()
	var err error
	f2, err = parser.ParseFile(fset2, "", out, parser.ParseComments)
	if err != nil {
		panic(err)
	}
	return
}
Example #6
0
// ProcessFileAST processes the files using golang's AST parser
func ProcessFileAST(filePath string, from string, to string) {

	//Colors to be used on the console
	red := ansi.ColorCode("red+bh")
	white := ansi.ColorCode("white+bh")
	yellow := ansi.ColorCode("yellow+bh")
	blackOnWhite := ansi.ColorCode("black+b:white+h")
	//Reset the color
	reset := ansi.ColorCode("reset")

	fmt.Println(blackOnWhite+"Processing file", filePath, "in SAFE MODE", reset)

	// New FileSet to parse the go file to
	fSet := token.NewFileSet()

	// Parse the file
	file, err := parser.ParseFile(fSet, filePath, nil, 0)
	if err != nil {
		fmt.Println(err)
	}

	// Get the list of imports from the ast
	imports := astutil.Imports(fSet, file)

	// Keep track of number of changes
	numChanges := 0

	// Iterate through the imports array
	for _, mPackage := range imports {
		for _, mImport := range mPackage {
			// Since astutil returns the path string with quotes, remove those
			importString := strings.TrimSuffix(strings.TrimPrefix(mImport.Path.Value, "\""), "\"")

			// If the path matches the oldpath, replace it with the new one
			if strings.Contains(importString, from) {
				//If it needs to be replaced, increase numChanges so we can write the file later
				numChanges++

				// Join the path of the import package with the remainder from the old one after removing the old import package
				replacePackage := strings.Replace(importString, from, to, -1)

				fmt.Println(red +
					"Updating import " +
					reset + white +
					importString +
					reset + red +
					" to " +
					reset + white +
					replacePackage +
					reset)

				// Remove the old import and replace it with the replacement
				astutil.DeleteImport(fSet, file, importString)
				astutil.AddImport(fSet, file, replacePackage)
			}
		}
	}

	// If the number of changes are more than 0, write file
	if numChanges > 0 {
		// Print the new AST tree to a new output buffer
		var outputBuffer bytes.Buffer
		printer.Fprint(&outputBuffer, fSet, file)

		ioutil.WriteFile(filePath, outputBuffer.Bytes(), os.ModePerm)
		fmt.Println(yellow+
			"File",
			filePath,
			"saved after",
			numChanges,
			"changes",
			reset, "\n\n")
	} else {
		fmt.Println(yellow+
			"No changes to write on this file.",
			reset, "\n\n")
	}
}
Example #7
0
// Process formats and adjusts imports for the provided file.
// If opt is nil the defaults are used.
//
// Note that filename's directory influences which imports can be chosen,
// so it is important that filename be accurate.
// To process data ``as if'' it were in filename, pass the data as a non-nil src.
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
	if opt == nil {
		opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
	}

	fileSet := token.NewFileSet()
	file, adjust, err := parse(fileSet, filename, src, opt)
	if err != nil {
		return nil, err
	}

	_, err = fixImports(fileSet, file, filename)
	if err != nil {
		return nil, err
	}

	sortImports(fileSet, file)
	imps := astutil.Imports(fileSet, file)

	var spacesBefore []string // import paths we need spaces before
	for _, impSection := range imps {
		// Within each block of contiguous imports, see if any
		// import lines are in different group numbers. If so,
		// we'll need to put a space between them so it's
		// compatible with gofmt.
		lastGroup := -1
		for _, importSpec := range impSection {
			importPath, _ := strconv.Unquote(importSpec.Path.Value)
			groupNum := importGroup(importPath)
			if groupNum != lastGroup && lastGroup != -1 {
				spacesBefore = append(spacesBefore, importPath)
			}
			lastGroup = groupNum
		}

	}

	printerMode := printer.UseSpaces
	if opt.TabIndent {
		printerMode |= printer.TabIndent
	}
	printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth}

	var buf bytes.Buffer
	err = printConfig.Fprint(&buf, fileSet, file)
	if err != nil {
		return nil, err
	}
	out := buf.Bytes()
	if adjust != nil {
		out = adjust(src, out)
	}
	if len(spacesBefore) > 0 {
		out = addImportSpaces(bytes.NewReader(out), spacesBefore)
	}

	out, err = format.Source(out)
	if err != nil {
		return nil, err
	}
	return out, nil
}
Example #8
0
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))
}
Example #9
0
func (d *DirectiveList) LoadList() error {
	data, err := ioutil.ReadFile(d.file)
	if err != nil {
		return err
	}

	fset := token.NewFileSet()

	extMatches := regCommentLine.FindAll(data, -1)
	data = regComment.ReplaceAll(data, []byte(""))

	f, err := parser.ParseFile(token.NewFileSet(), "", data, 0)
	impPaths := astutil.Imports(fset, f)
	node, ok := f.Scope.Lookup("directiveOrder").Decl.(ast.Node)
	if !ok {
		return ErrInvalidDirectiveFile
	}

	c := node.(*ast.ValueSpec).Values[0].(*ast.CompositeLit)
	for _, m := range c.Elts {
		var setup *ast.SelectorExpr
		var directive *ast.BasicLit
		token := m.(*ast.CompositeLit).Elts
		active := true

		if v, ok := token[0].(*ast.BasicLit); ok {
			directive = v
		} else {
			return ErrImportInvalidDirectiveFormat
		}

		if v, ok := m.(*ast.CompositeLit).Elts[1].(*ast.SelectorExpr); ok {
			setup = v
		} else if _, ok := m.(*ast.CompositeLit).Elts[1].(*ast.FuncLit); ok {
			active = false
		} else {
			return ErrImportInvalidDirectiveFormat
		}

		name, err := strconv.Unquote(directive.Value)
		if err != nil {
			return ErrImportInvalidDirectiveFormat
		}
		dir := &Directive{
			Name:    name,
			Active:  active,
			Core:    true,
			Removed: false,
		}

		if active {
			dir.Setup = fmt.Sprintf("%s.%s", setup.X, setup.Sel)
		} else {
			dir.Name = strings.TrimLeft(dir.Name, "!")
		}

		for _, imp := range impPaths[0] {
			if imp.Name.String() == dir.Name {
				dir.Core = false
				dir.ImportPath, _ = strconv.Unquote(imp.Path.Value)
				break
			}
		}

		for _, commented := range extMatches {
			if strings.Contains(string(commented), `{"`+dir.Name) {
				dir.Active = false
				break
			}
		}

		d.list = append(d.list, dir)
	}

	return nil
}