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
}