Source file
src/go/ast/commentmap.go
Documentation: go/ast
1
2
3
4
5 package ast
6
7 import (
8 "bytes"
9 "fmt"
10 "go/token"
11 "sort"
12 )
13
14 type byPos []*CommentGroup
15
16 func (a byPos) Len() int { return len(a) }
17 func (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
18 func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
19
20
21
22 func sortComments(list []*CommentGroup) {
23
24
25
26 if orderedList := byPos(list); !sort.IsSorted(orderedList) {
27 sort.Sort(orderedList)
28 }
29 }
30
31
32
33
34
35 type CommentMap map[Node][]*CommentGroup
36
37 func (cmap CommentMap) addComment(n Node, c *CommentGroup) {
38 list := cmap[n]
39 if len(list) == 0 {
40 list = []*CommentGroup{c}
41 } else {
42 list = append(list, c)
43 }
44 cmap[n] = list
45 }
46
47 type byInterval []Node
48
49 func (a byInterval) Len() int { return len(a) }
50 func (a byInterval) Less(i, j int) bool {
51 pi, pj := a[i].Pos(), a[j].Pos()
52 return pi < pj || pi == pj && a[i].End() > a[j].End()
53 }
54 func (a byInterval) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
55
56
57
58 func nodeList(n Node) []Node {
59 var list []Node
60 Inspect(n, func(n Node) bool {
61
62 switch n.(type) {
63 case nil, *CommentGroup, *Comment:
64 return false
65 }
66 list = append(list, n)
67 return true
68 })
69
70
71
72
73
74 return list
75 }
76
77
78
79 type commentListReader struct {
80 fset *token.FileSet
81 list []*CommentGroup
82 index int
83 comment *CommentGroup
84 pos, end token.Position
85 }
86
87 func (r *commentListReader) eol() bool {
88 return r.index >= len(r.list)
89 }
90
91 func (r *commentListReader) next() {
92 if !r.eol() {
93 r.comment = r.list[r.index]
94 r.pos = r.fset.Position(r.comment.Pos())
95 r.end = r.fset.Position(r.comment.End())
96 r.index++
97 }
98 }
99
100
101
102
103 type nodeStack []Node
104
105
106
107
108 func (s *nodeStack) push(n Node) {
109 s.pop(n.Pos())
110 *s = append((*s), n)
111 }
112
113
114
115
116
117 func (s *nodeStack) pop(pos token.Pos) (top Node) {
118 i := len(*s)
119 for i > 0 && (*s)[i-1].End() <= pos {
120 top = (*s)[i-1]
121 i--
122 }
123 *s = (*s)[0:i]
124 return top
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap {
144 if len(comments) == 0 {
145 return nil
146 }
147
148 cmap := make(CommentMap)
149
150
151 tmp := make([]*CommentGroup, len(comments))
152 copy(tmp, comments)
153 sortComments(tmp)
154 r := commentListReader{fset: fset, list: tmp}
155 r.next()
156
157
158 nodes := nodeList(node)
159 nodes = append(nodes, nil)
160
161
162 var (
163 p Node
164 pend token.Position
165 pg Node
166 pgend token.Position
167 stack nodeStack
168 )
169
170 for _, q := range nodes {
171 var qpos token.Position
172 if q != nil {
173 qpos = fset.Position(q.Pos())
174 } else {
175
176
177 const infinity = 1 << 30
178 qpos.Offset = infinity
179 qpos.Line = infinity
180 }
181
182
183 for r.end.Offset <= qpos.Offset {
184
185 if top := stack.pop(r.comment.Pos()); top != nil {
186 pg = top
187 pgend = fset.Position(pg.End())
188 }
189
190
191
192
193
194 var assoc Node
195 switch {
196 case pg != nil &&
197 (pgend.Line == r.pos.Line ||
198 pgend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line):
199
200
201
202
203
204 assoc = pg
205 case p != nil &&
206 (pend.Line == r.pos.Line ||
207 pend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line ||
208 q == nil):
209
210
211 assoc = p
212 default:
213
214 if q == nil {
215
216
217 panic("internal error: no comments should be associated with sentinel")
218 }
219 assoc = q
220 }
221 cmap.addComment(assoc, r.comment)
222 if r.eol() {
223 return cmap
224 }
225 r.next()
226 }
227
228
229 p = q
230 pend = fset.Position(p.End())
231
232
233 switch q.(type) {
234 case *File, *Field, Decl, Spec, Stmt:
235 stack.push(q)
236 }
237 }
238
239 return cmap
240 }
241
242
243
244
245
246 func (cmap CommentMap) Update(old, new Node) Node {
247 if list := cmap[old]; len(list) > 0 {
248 delete(cmap, old)
249 cmap[new] = append(cmap[new], list...)
250 }
251 return new
252 }
253
254
255
256
257
258 func (cmap CommentMap) Filter(node Node) CommentMap {
259 umap := make(CommentMap)
260 Inspect(node, func(n Node) bool {
261 if g := cmap[n]; len(g) > 0 {
262 umap[n] = g
263 }
264 return true
265 })
266 return umap
267 }
268
269
270
271
272 func (cmap CommentMap) Comments() []*CommentGroup {
273 list := make([]*CommentGroup, 0, len(cmap))
274 for _, e := range cmap {
275 list = append(list, e...)
276 }
277 sortComments(list)
278 return list
279 }
280
281 func summary(list []*CommentGroup) string {
282 const maxLen = 40
283 var buf bytes.Buffer
284
285
286 loop:
287 for _, group := range list {
288
289
290
291 for _, comment := range group.List {
292 if buf.Len() >= maxLen {
293 break loop
294 }
295 buf.WriteString(comment.Text)
296 }
297 }
298
299
300 if buf.Len() > maxLen {
301 buf.Truncate(maxLen - 3)
302 buf.WriteString("...")
303 }
304
305
306 bytes := buf.Bytes()
307 for i, b := range bytes {
308 switch b {
309 case '\t', '\n', '\r':
310 bytes[i] = ' '
311 }
312 }
313
314 return string(bytes)
315 }
316
317 func (cmap CommentMap) String() string {
318
319 var nodes []Node
320 for node := range cmap {
321 nodes = append(nodes, node)
322 }
323 sort.Sort(byInterval(nodes))
324
325 var buf bytes.Buffer
326 fmt.Fprintln(&buf, "CommentMap {")
327 for _, node := range nodes {
328 comment := cmap[node]
329
330 var s string
331 if ident, ok := node.(*Ident); ok {
332 s = ident.Name
333 } else {
334 s = fmt.Sprintf("%T", node)
335 }
336 fmt.Fprintf(&buf, "\t%p %20s: %s\n", node, s, summary(comment))
337 }
338 fmt.Fprintln(&buf, "}")
339 return buf.String()
340 }
341
View as plain text