...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package format
16
17 import (
18 "sort"
19 "strconv"
20
21 "cuelang.org/go/cue/ast"
22 "cuelang.org/go/cue/token"
23 )
24
25
26
27
28 func sortImports(d *ast.ImportDecl) {
29 if !d.Lparen.IsValid() || len(d.Specs) == 0 {
30
31 return
32 }
33
34
35 i := 0
36 specs := d.Specs[:0]
37 for j, s := range d.Specs {
38 if j > i && (s.Pos().RelPos() >= token.NewSection || hasDoc(s)) {
39 setRelativePos(s, token.Newline)
40
41 block := sortSpecs(d.Specs[i:j])
42 specs = append(specs, block...)
43 i = j
44 }
45 }
46 specs = append(specs, sortSpecs(d.Specs[i:])...)
47 setRelativePos(specs[0], token.Newline)
48 d.Specs = specs
49 }
50
51 func setRelativePos(s *ast.ImportSpec, r token.RelPos) {
52 if hasDoc(s) {
53 return
54 }
55 pos := s.Pos().WithRel(r)
56 if s.Name != nil {
57 s.Name.NamePos = pos
58 } else {
59 s.Path.ValuePos = pos
60 }
61 }
62
63 func hasDoc(s *ast.ImportSpec) bool {
64 for _, doc := range s.Comments() {
65 if doc.Doc {
66 return true
67 }
68 }
69 return false
70 }
71
72 func importPath(s *ast.ImportSpec) string {
73 t, err := strconv.Unquote(s.Path.Value)
74 if err == nil {
75 return t
76 }
77 return ""
78 }
79
80 func importName(s *ast.ImportSpec) string {
81 n := s.Name
82 if n == nil {
83 return ""
84 }
85 return n.Name
86 }
87
88 func importComment(s *ast.ImportSpec) string {
89 for _, c := range s.Comments() {
90 if c.Line {
91 return c.Text()
92 }
93 }
94 return ""
95 }
96
97
98 func collapse(prev, next *ast.ImportSpec) bool {
99 if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
100 return false
101 }
102 for _, c := range prev.Comments() {
103 if !c.Doc {
104 return false
105 }
106 }
107 return true
108 }
109
110 type posSpan struct {
111 Start token.Pos
112 End token.Pos
113 }
114
115 func sortSpecs(specs []*ast.ImportSpec) []*ast.ImportSpec {
116
117
118
119 if len(specs) <= 1 {
120 setRelativePos(specs[0], token.NewSection)
121 return specs
122 }
123
124
125 pos := make([]posSpan, len(specs))
126 for i, s := range specs {
127 pos[i] = posSpan{s.Pos(), s.End()}
128 }
129
130
131
132
133
134
135 sort.Sort(byImportSpec(specs))
136
137
138
139 deduped := specs[:0]
140 for i, s := range specs {
141 if i == len(specs)-1 || !collapse(s, specs[i+1]) {
142 deduped = append(deduped, s)
143 }
144 }
145 specs = deduped
146
147 setRelativePos(specs[0], token.NewSection)
148 return specs
149 }
150
151 type byImportSpec []*ast.ImportSpec
152
153 func (x byImportSpec) Len() int { return len(x) }
154 func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
155 func (x byImportSpec) Less(i, j int) bool {
156 ipath := importPath(x[i])
157 jpath := importPath(x[j])
158 if ipath != jpath {
159 return ipath < jpath
160 }
161 iname := importName(x[i])
162 jname := importName(x[j])
163 if iname != jname {
164 return iname < jname
165 }
166 return importComment(x[i]) < importComment(x[j])
167 }
168
View as plain text