func sortDecl()

in kinder/hack/orderimports/orderimports.go [181:298]


func sortDecl(fSet *token.FileSet, f *ast.File, d *ast.GenDecl) []ast.Spec {
	// split all imports into different groups
	var stdlibImports, localImports, k8sImports, externalImports []*ast.ImportSpec
	for _, spec := range d.Specs {
		imp := spec.(*ast.ImportSpec)
		importPath := strings.Replace(imp.Path.Value, "\"", "", -1)
		parts := strings.Split(importPath, "/")
		if !strings.Contains(parts[0], ".") {
			// standard library
			stdlibImports = append(stdlibImports, imp)
		} else if *localPrefix != "" && strings.HasPrefix(importPath, *localPrefix) {
			// local imports
			localImports = append(localImports, imp)
		} else if strings.Contains(parts[0], "k8s.io") {
			// other *.k8s.io imports
			k8sImports = append(k8sImports, imp)
		} else {
			// external repositories
			externalImports = append(externalImports, imp)
		}
	}

	// remove comments within imports declaration, will add it back later
	if f.Comments != nil {
		cgs := make([]*ast.CommentGroup, 0)
		for _, cg := range f.Comments {
			ncg := ast.CommentGroup{}
			for _, c := range cg.List {
				if !(d.Pos() <= c.Pos() && c.Pos() <= d.End()) {
					ncg.List = append(ncg.List, c)
				}
			}
			if len(ncg.List) != 0 {
				cgs = append(cgs, &ncg)
			}
		}
		f.Comments = cgs
	}

	// set each import's position, line offset and comments
	orderedImports := make([]ast.Spec, 0)
	impLines := make([]int, 0) // line offset table of imports
	impFile := fSet.File(d.Pos())
	offset := impFile.Offset(impFile.LineStart(impFile.Line(d.Pos()) + 1)) // offset of the line next import keyword

	for _, gImps := range [][]*ast.ImportSpec{
		stdlibImports,
		externalImports,
		k8sImports,
		localImports,
	} {
		sort.SliceStable(gImps, func(i, j int) bool {
			return gImps[i].Path.Value < gImps[j].Path.Value
		})

		for _, imp := range gImps {
			impLines = append(impLines, offset)
			// calculate and set new startPos,endPos for import spec
			sPos := token.Pos(offset + 1)
			ePos := token.Pos(int(sPos) + (int(imp.End()) - int(imp.Pos())))
			if imp.Name != nil {
				imp.Name.NamePos = sPos
			}
			imp.Path.ValuePos = sPos
			imp.EndPos = ePos
			offset = int(ePos)

			text := "" // comment text
			if imp.Comment != nil {
				for _, c := range imp.Comment.List {
					text += c.Text
				}
			}
			if imp.Doc != nil {
				for _, d := range imp.Doc.List {
					text += d.Text
				}
			}
			if text != "" {
				imp.Doc = nil
				imp.Comment = &ast.CommentGroup{
					List: []*ast.Comment{{
						Slash: ePos + 1,
						Text:  text,
					}},
				}
				f.Comments = append(f.Comments, imp.Comment)
				offset = int(imp.Comment.End())
			}

			orderedImports = append(orderedImports, imp)
		}
		// add a blank line between each group
		if len(gImps) != 0 {
			impLines = append(impLines, offset)
			offset++
		}
	}
	sort.Slice(f.Comments, func(i, j int) bool {
		return f.Comments[i].Pos() < f.Comments[j].Pos()
	})

	// update line offset table, first copy the offsets before import declaration,
	// then insert offsets we updated(may have new lines), finally copy the rest
	impStartLine := fSet.Position(d.Pos()).Line // line of the import keyword
	impEndLine := fSet.Position(d.End()).Line   // line after the import declaration

	lines := make([]int, 0)
	for i := 1; i <= impStartLine; i++ {
		lines = append(lines, impFile.Offset(impFile.LineStart(i)))
	}
	lines = append(lines, impLines...)
	for i := impEndLine + 1; i <= impFile.LineCount(); i++ {
		lines = append(lines, impFile.Offset(impFile.LineStart(i)))
	}
	impFile.SetLines(lines)
	return orderedImports
}