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