github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/go/ast/import.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ast 6 7 import ( 8 "go/token" 9 "sort" 10 "strconv" 11 ) 12 13 // SortImports sorts runs of consecutive import lines in import blocks in f. 14 func SortImports(fset *token.FileSet, f *File) { 15 for _, d := range f.Decls { 16 d, ok := d.(*GenDecl) 17 if !ok || d.Tok != token.IMPORT { 18 // Not an import declaration, so we're done. 19 // Imports are always first. 20 break 21 } 22 23 if !d.Lparen.IsValid() { 24 // Not a block: sorted by default. 25 continue 26 } 27 28 // Identify and sort runs of specs on successive lines. 29 i := 0 30 for j, s := range d.Specs { 31 if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line { 32 // j begins a new run. End this one. 33 sortSpecs(fset, f, d.Specs[i:j]) 34 i = j 35 } 36 } 37 sortSpecs(fset, f, d.Specs[i:]) 38 } 39 } 40 41 func importPath(s Spec) string { 42 t, err := strconv.Unquote(s.(*ImportSpec).Path.Value) 43 if err == nil { 44 return t 45 } 46 return "" 47 } 48 49 type posSpan struct { 50 Start token.Pos 51 End token.Pos 52 } 53 54 func sortSpecs(fset *token.FileSet, f *File, specs []Spec) { 55 // Avoid work if already sorted (also catches < 2 entries). 56 sorted := true 57 for i, s := range specs { 58 if i > 0 && importPath(specs[i-1]) > importPath(s) { 59 sorted = false 60 break 61 } 62 } 63 if sorted { 64 return 65 } 66 67 // Record positions for specs. 68 pos := make([]posSpan, len(specs)) 69 for i, s := range specs { 70 pos[i] = posSpan{s.Pos(), s.End()} 71 } 72 73 // Identify comments in this range. 74 // Any comment from pos[0].Start to the final line counts. 75 lastLine := fset.Position(pos[len(pos)-1].End).Line 76 cstart := len(f.Comments) 77 cend := len(f.Comments) 78 for i, g := range f.Comments { 79 if g.Pos() < pos[0].Start { 80 continue 81 } 82 if i < cstart { 83 cstart = i 84 } 85 if fset.Position(g.End()).Line > lastLine { 86 cend = i 87 break 88 } 89 } 90 comments := f.Comments[cstart:cend] 91 92 // Assign each comment to the import spec preceding it. 93 importComment := map[*ImportSpec][]*CommentGroup{} 94 specIndex := 0 95 for _, g := range comments { 96 for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() { 97 specIndex++ 98 } 99 s := specs[specIndex].(*ImportSpec) 100 importComment[s] = append(importComment[s], g) 101 } 102 103 // Sort the import specs by import path. 104 // Reassign the import paths to have the same position sequence. 105 // Reassign each comment to abut the end of its spec. 106 // Sort the comments by new position. 107 sort.Sort(byImportPath(specs)) 108 for i, s := range specs { 109 s := s.(*ImportSpec) 110 if s.Name != nil { 111 s.Name.NamePos = pos[i].Start 112 } 113 s.Path.ValuePos = pos[i].Start 114 s.EndPos = pos[i].End 115 for _, g := range importComment[s] { 116 for _, c := range g.List { 117 c.Slash = pos[i].End 118 } 119 } 120 } 121 sort.Sort(byCommentPos(comments)) 122 } 123 124 type byImportPath []Spec // slice of *ImportSpec 125 126 func (x byImportPath) Len() int { return len(x) } 127 func (x byImportPath) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 128 func (x byImportPath) Less(i, j int) bool { return importPath(x[i]) < importPath(x[j]) } 129 130 type byCommentPos []*CommentGroup 131 132 func (x byCommentPos) Len() int { return len(x) } 133 func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 134 func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }