...
Source file src/go/ast/import.go
1
2
3
4
5 package ast
6
7 import (
8 "go/token"
9 "sort"
10 "strconv"
11 )
12
13
14
15 func SortImports(fset *token.FileSet, f *File) {
16 for _, d := range f.Decls {
17 d, ok := d.(*GenDecl)
18 if !ok || d.Tok != token.IMPORT {
19
20
21 break
22 }
23
24 if !d.Lparen.IsValid() {
25
26 continue
27 }
28
29
30 i := 0
31 specs := d.Specs[:0]
32 for j, s := range d.Specs {
33 if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
34
35 specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
36 i = j
37 }
38 }
39 specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
40 d.Specs = specs
41
42
43 if len(d.Specs) > 0 {
44 lastSpec := d.Specs[len(d.Specs)-1]
45 lastLine := fset.Position(lastSpec.Pos()).Line
46 rParenLine := fset.Position(d.Rparen).Line
47 for rParenLine > lastLine+1 {
48 rParenLine--
49 fset.File(d.Rparen).MergeLine(rParenLine)
50 }
51 }
52 }
53 }
54
55 func importPath(s Spec) string {
56 t, err := strconv.Unquote(s.(*ImportSpec).Path.Value)
57 if err == nil {
58 return t
59 }
60 return ""
61 }
62
63 func importName(s Spec) string {
64 n := s.(*ImportSpec).Name
65 if n == nil {
66 return ""
67 }
68 return n.Name
69 }
70
71 func importComment(s Spec) string {
72 c := s.(*ImportSpec).Comment
73 if c == nil {
74 return ""
75 }
76 return c.Text()
77 }
78
79
80 func collapse(prev, next Spec) bool {
81 if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
82 return false
83 }
84 return prev.(*ImportSpec).Comment == nil
85 }
86
87 type posSpan struct {
88 Start token.Pos
89 End token.Pos
90 }
91
92 func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {
93
94
95
96 if len(specs) <= 1 {
97 return specs
98 }
99
100
101 pos := make([]posSpan, len(specs))
102 for i, s := range specs {
103 pos[i] = posSpan{s.Pos(), s.End()}
104 }
105
106
107
108 lastLine := fset.Position(pos[len(pos)-1].End).Line
109 cstart := len(f.Comments)
110 cend := len(f.Comments)
111 for i, g := range f.Comments {
112 if g.Pos() < pos[0].Start {
113 continue
114 }
115 if i < cstart {
116 cstart = i
117 }
118 if fset.Position(g.End()).Line > lastLine {
119 cend = i
120 break
121 }
122 }
123 comments := f.Comments[cstart:cend]
124
125
126 importComments := map[*ImportSpec][]*CommentGroup{}
127 specIndex := 0
128 for _, g := range comments {
129 for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
130 specIndex++
131 }
132 s := specs[specIndex].(*ImportSpec)
133 importComments[s] = append(importComments[s], g)
134 }
135
136
137
138
139
140
141 sort.Slice(specs, func(i, j int) bool {
142 ipath := importPath(specs[i])
143 jpath := importPath(specs[j])
144 if ipath != jpath {
145 return ipath < jpath
146 }
147 iname := importName(specs[i])
148 jname := importName(specs[j])
149 if iname != jname {
150 return iname < jname
151 }
152 return importComment(specs[i]) < importComment(specs[j])
153 })
154
155
156
157 deduped := specs[:0]
158 for i, s := range specs {
159 if i == len(specs)-1 || !collapse(s, specs[i+1]) {
160 deduped = append(deduped, s)
161 } else {
162 p := s.Pos()
163 fset.File(p).MergeLine(fset.Position(p).Line)
164 }
165 }
166 specs = deduped
167
168
169 for i, s := range specs {
170 s := s.(*ImportSpec)
171 if s.Name != nil {
172 s.Name.NamePos = pos[i].Start
173 }
174 s.Path.ValuePos = pos[i].Start
175 s.EndPos = pos[i].End
176 for _, g := range importComments[s] {
177 for _, c := range g.List {
178 c.Slash = pos[i].End
179 }
180 }
181 }
182
183 sort.Slice(comments, func(i, j int) bool {
184 return comments[i].Pos() < comments[j].Pos()
185 })
186
187 return specs
188 }
189
View as plain text