blob: ff3773320148f85c65ac4d06e057802e048dedfa [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001package protoparse
2
3import (
4 "bytes"
Zack Williamse940c7a2019-08-21 14:25:39 -07005 "strings"
6
7 "github.com/golang/protobuf/proto"
8 dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
9
10 "github.com/jhump/protoreflect/desc/internal"
11)
12
13func (r *parseResult) generateSourceCodeInfo() *dpb.SourceCodeInfo {
14 if r.nodes == nil {
15 // skip files that do not have AST info (these will be files
16 // that came from well-known descriptors, instead of from source)
17 return nil
18 }
19
20 sci := sourceCodeInfo{commentsUsed: map[*comment]struct{}{}}
21 path := make([]int32, 0, 10)
22
23 fn := r.getFileNode(r.fd).(*fileNode)
Scott Baker4a35a702019-11-26 08:17:33 -080024 sci.newLocWithoutComments(fn, nil)
25
Zack Williamse940c7a2019-08-21 14:25:39 -070026 if fn.syntax != nil {
27 sci.newLoc(fn.syntax, append(path, internal.File_syntaxTag))
28 }
Zack Williamse940c7a2019-08-21 14:25:39 -070029
Scott Baker4a35a702019-11-26 08:17:33 -080030 var depIndex, optIndex, msgIndex, enumIndex, extendIndex, svcIndex int32
Zack Williamse940c7a2019-08-21 14:25:39 -070031
Scott Baker4a35a702019-11-26 08:17:33 -080032 for _, child := range fn.decls {
Zack Williamse940c7a2019-08-21 14:25:39 -070033 switch {
Scott Baker4a35a702019-11-26 08:17:33 -080034 case child.imp != nil:
35 sci.newLoc(child.imp, append(path, internal.File_dependencyTag, int32(depIndex)))
36 depIndex++
37 case child.pkg != nil:
38 sci.newLoc(child.pkg, append(path, internal.File_packageTag))
39 case child.option != nil:
40 r.generateSourceCodeInfoForOption(&sci, child.option, false, &optIndex, append(path, internal.File_optionsTag))
41 case child.message != nil:
42 r.generateSourceCodeInfoForMessage(&sci, child.message, nil, append(path, internal.File_messagesTag, msgIndex))
43 msgIndex++
44 case child.enum != nil:
45 r.generateSourceCodeInfoForEnum(&sci, child.enum, append(path, internal.File_enumsTag, enumIndex))
46 enumIndex++
47 case child.extend != nil:
48 r.generateSourceCodeInfoForExtensions(&sci, child.extend, &extendIndex, &msgIndex, append(path, internal.File_extensionsTag), append(dup(path), internal.File_messagesTag))
49 case child.service != nil:
50 r.generateSourceCodeInfoForService(&sci, child.service, append(path, internal.File_servicesTag, svcIndex))
51 svcIndex++
Zack Williamse940c7a2019-08-21 14:25:39 -070052 }
Scott Baker4a35a702019-11-26 08:17:33 -080053 }
Zack Williamse940c7a2019-08-21 14:25:39 -070054
Scott Baker4a35a702019-11-26 08:17:33 -080055 return &dpb.SourceCodeInfo{Location: sci.locs}
56}
57
58func (r *parseResult) generateSourceCodeInfoForOption(sci *sourceCodeInfo, n *optionNode, compact bool, uninterpIndex *int32, path []int32) {
59 if !compact {
60 sci.newLocWithoutComments(n, path)
61 }
62 subPath := r.interpretedOptions[n]
63 if len(subPath) > 0 {
64 p := path
65 if subPath[0] == -1 {
66 // used by "default" and "json_name" field pseudo-options
67 // to attribute path to parent element (since those are
68 // stored directly on the descriptor, not its options)
69 p = make([]int32, len(path)-1)
70 copy(p, path)
71 subPath = subPath[1:]
Zack Williamse940c7a2019-08-21 14:25:39 -070072 }
Scott Baker4a35a702019-11-26 08:17:33 -080073 sci.newLoc(n, append(p, subPath...))
74 return
75 }
76
77 // it's an uninterpreted option
78 optPath := append(path, internal.UninterpretedOptionsTag, *uninterpIndex)
79 *uninterpIndex++
80 sci.newLoc(n, optPath)
81 var valTag int32
82 switch n.val.(type) {
83 case *compoundIdentNode:
84 valTag = internal.Uninterpreted_identTag
85 case *intLiteralNode:
86 valTag = internal.Uninterpreted_posIntTag
87 case *compoundIntNode:
88 valTag = internal.Uninterpreted_negIntTag
89 case *compoundFloatNode:
90 valTag = internal.Uninterpreted_doubleTag
91 case *compoundStringNode:
92 valTag = internal.Uninterpreted_stringTag
93 case *aggregateLiteralNode:
94 valTag = internal.Uninterpreted_aggregateTag
95 }
96 if valTag != 0 {
97 sci.newLoc(n.val, append(optPath, valTag))
98 }
99 for j, nn := range n.name.parts {
100 optNmPath := append(optPath, internal.Uninterpreted_nameTag, int32(j))
101 sci.newLoc(nn, optNmPath)
102 sci.newLoc(nn.text, append(optNmPath, internal.UninterpretedName_nameTag))
Zack Williamse940c7a2019-08-21 14:25:39 -0700103 }
104}
105
Scott Baker4a35a702019-11-26 08:17:33 -0800106func (r *parseResult) generateSourceCodeInfoForMessage(sci *sourceCodeInfo, n msgDecl, fieldPath []int32, path []int32) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700107 sci.newLoc(n, path)
108
109 var decls []*messageElement
Zack Williamse940c7a2019-08-21 14:25:39 -0700110 switch n := n.(type) {
111 case *messageNode:
112 decls = n.decls
Zack Williamse940c7a2019-08-21 14:25:39 -0700113 case *groupNode:
114 decls = n.decls
Scott Baker4a35a702019-11-26 08:17:33 -0800115 case *mapFieldNode:
Zack Williamse940c7a2019-08-21 14:25:39 -0700116 // map entry so nothing else to do
117 return
118 }
119
120 sci.newLoc(n.messageName(), append(path, internal.Message_nameTag))
Scott Baker4a35a702019-11-26 08:17:33 -0800121 // matching protoc, which emits the corresponding field type name (for group fields)
122 // right after the source location for the group message name
123 if fieldPath != nil {
124 sci.newLoc(n.messageName(), append(fieldPath, internal.Field_typeNameTag))
Zack Williamse940c7a2019-08-21 14:25:39 -0700125 }
126
Scott Baker4a35a702019-11-26 08:17:33 -0800127 var optIndex, fieldIndex, oneOfIndex, extendIndex, nestedMsgIndex int32
128 var nestedEnumIndex, extRangeIndex, reservedRangeIndex, reservedNameIndex int32
129 for _, child := range decls {
130 switch {
131 case child.option != nil:
132 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Message_optionsTag))
133 case child.field != nil:
134 r.generateSourceCodeInfoForField(sci, child.field, append(path, internal.Message_fieldsTag, fieldIndex))
135 fieldIndex++
136 case child.group != nil:
137 fldPath := append(path, internal.Message_fieldsTag, fieldIndex)
138 r.generateSourceCodeInfoForField(sci, child.group, fldPath)
139 fieldIndex++
140 r.generateSourceCodeInfoForMessage(sci, child.group, fldPath, append(dup(path), internal.Message_nestedMessagesTag, nestedMsgIndex))
141 nestedMsgIndex++
142 case child.mapField != nil:
143 r.generateSourceCodeInfoForField(sci, child.mapField, append(path, internal.Message_fieldsTag, fieldIndex))
144 fieldIndex++
145 case child.oneOf != nil:
146 r.generateSourceCodeInfoForOneOf(sci, child.oneOf, &fieldIndex, &nestedMsgIndex, append(path, internal.Message_fieldsTag), append(dup(path), internal.Message_nestedMessagesTag), append(dup(path), internal.Message_oneOfsTag, oneOfIndex))
147 oneOfIndex++
148 case child.nested != nil:
149 r.generateSourceCodeInfoForMessage(sci, child.nested, nil, append(path, internal.Message_nestedMessagesTag, nestedMsgIndex))
150 nestedMsgIndex++
151 case child.enum != nil:
152 r.generateSourceCodeInfoForEnum(sci, child.enum, append(path, internal.Message_enumsTag, nestedEnumIndex))
153 nestedEnumIndex++
154 case child.extend != nil:
155 r.generateSourceCodeInfoForExtensions(sci, child.extend, &extendIndex, &nestedMsgIndex, append(path, internal.Message_extensionsTag), append(dup(path), internal.Message_nestedMessagesTag))
156 case child.extensionRange != nil:
157 r.generateSourceCodeInfoForExtensionRanges(sci, child.extensionRange, &extRangeIndex, append(path, internal.Message_extensionRangeTag))
158 case child.reserved != nil:
159 if len(child.reserved.names) > 0 {
160 resPath := append(path, internal.Message_reservedNameTag)
161 sci.newLoc(child.reserved, resPath)
162 for _, rn := range child.reserved.names {
163 sci.newLoc(rn, append(resPath, reservedNameIndex))
164 reservedNameIndex++
Zack Williamse940c7a2019-08-21 14:25:39 -0700165 }
166 }
Scott Baker4a35a702019-11-26 08:17:33 -0800167 if len(child.reserved.ranges) > 0 {
168 resPath := append(path, internal.Message_reservedRangeTag)
169 sci.newLoc(child.reserved, resPath)
170 for _, rr := range child.reserved.ranges {
171 r.generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex))
172 reservedRangeIndex++
173 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700174 }
175 }
176 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700177}
178
Scott Baker4a35a702019-11-26 08:17:33 -0800179func (r *parseResult) generateSourceCodeInfoForEnum(sci *sourceCodeInfo, n *enumNode, path []int32) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700180 sci.newLoc(n, path)
181 sci.newLoc(n.name, append(path, internal.Enum_nameTag))
182
Scott Baker4a35a702019-11-26 08:17:33 -0800183 var optIndex, valIndex, reservedNameIndex, reservedRangeIndex int32
184 for _, child := range n.decls {
185 switch {
186 case child.option != nil:
187 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Enum_optionsTag))
188 case child.value != nil:
189 r.generateSourceCodeInfoForEnumValue(sci, child.value, append(path, internal.Enum_valuesTag, valIndex))
190 valIndex++
191 case child.reserved != nil:
192 if len(child.reserved.names) > 0 {
193 resPath := append(path, internal.Enum_reservedNameTag)
194 sci.newLoc(child.reserved, resPath)
195 for _, rn := range child.reserved.names {
196 sci.newLoc(rn, append(resPath, reservedNameIndex))
197 reservedNameIndex++
198 }
199 }
200 if len(child.reserved.ranges) > 0 {
201 resPath := append(path, internal.Enum_reservedRangeTag)
202 sci.newLoc(child.reserved, resPath)
203 for _, rr := range child.reserved.ranges {
204 r.generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex))
205 reservedRangeIndex++
206 }
207 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700208 }
209 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700210}
211
Scott Baker4a35a702019-11-26 08:17:33 -0800212func (r *parseResult) generateSourceCodeInfoForEnumValue(sci *sourceCodeInfo, n *enumValueNode, path []int32) {
213 sci.newLoc(n, path)
214 sci.newLoc(n.name, append(path, internal.EnumVal_nameTag))
215 sci.newLoc(n.getNumber(), append(path, internal.EnumVal_numberTag))
Zack Williamse940c7a2019-08-21 14:25:39 -0700216
Scott Baker4a35a702019-11-26 08:17:33 -0800217 // enum value options
218 if n.options != nil {
219 optsPath := append(path, internal.EnumVal_optionsTag)
220 sci.newLoc(n.options, optsPath)
221 var optIndex int32
222 for _, opt := range n.options.decls {
223 r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
224 }
225 }
226}
227
228func (r *parseResult) generateSourceCodeInfoForReservedRange(sci *sourceCodeInfo, n *rangeNode, path []int32) {
229 sci.newLoc(n, path)
230 sci.newLoc(n.stNode, append(path, internal.ReservedRange_startTag))
231 if n.stNode != n.enNode {
232 sci.newLoc(n.enNode, append(path, internal.ReservedRange_endTag))
233 }
234}
235
236func (r *parseResult) generateSourceCodeInfoForExtensions(sci *sourceCodeInfo, n *extendNode, extendIndex, msgIndex *int32, extendPath, msgPath []int32) {
237 sci.newLoc(n, extendPath)
238 for _, decl := range n.decls {
239 switch {
240 case decl.field != nil:
241 r.generateSourceCodeInfoForField(sci, decl.field, append(extendPath, *extendIndex))
242 *extendIndex++
243 case decl.group != nil:
244 fldPath := append(extendPath, *extendIndex)
245 r.generateSourceCodeInfoForField(sci, decl.group, fldPath)
246 *extendIndex++
247 r.generateSourceCodeInfoForMessage(sci, decl.group, fldPath, append(msgPath, *msgIndex))
248 *msgIndex++
249 }
250 }
251}
252
253func (r *parseResult) generateSourceCodeInfoForOneOf(sci *sourceCodeInfo, n *oneOfNode, fieldIndex, nestedMsgIndex *int32, fieldPath, nestedMsgPath, oneOfPath []int32) {
254 sci.newLoc(n, oneOfPath)
255 sci.newLoc(n.name, append(oneOfPath, internal.OneOf_nameTag))
256
257 var optIndex int32
258 for _, child := range n.decls {
259 switch {
260 case child.option != nil:
261 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(oneOfPath, internal.OneOf_optionsTag))
262 case child.field != nil:
263 r.generateSourceCodeInfoForField(sci, child.field, append(fieldPath, *fieldIndex))
264 *fieldIndex++
265 case child.group != nil:
266 fldPath := append(fieldPath, *fieldIndex)
267 r.generateSourceCodeInfoForField(sci, child.group, fldPath)
268 *fieldIndex++
269 r.generateSourceCodeInfoForMessage(sci, child.group, fldPath, append(nestedMsgPath, *nestedMsgIndex))
270 *nestedMsgIndex++
271 }
272 }
273}
274
275func (r *parseResult) generateSourceCodeInfoForField(sci *sourceCodeInfo, n fieldDecl, path []int32) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700276 isGroup := false
Scott Baker4a35a702019-11-26 08:17:33 -0800277 var opts *compactOptionsNode
Zack Williamse940c7a2019-08-21 14:25:39 -0700278 var extendee *extendNode
Scott Baker4a35a702019-11-26 08:17:33 -0800279 var fieldType string
Zack Williamse940c7a2019-08-21 14:25:39 -0700280 switch n := n.(type) {
281 case *fieldNode:
282 opts = n.options
283 extendee = n.extendee
Scott Baker4a35a702019-11-26 08:17:33 -0800284 fieldType = n.fldType.val
Zack Williamse940c7a2019-08-21 14:25:39 -0700285 case *mapFieldNode:
286 opts = n.options
287 case *groupNode:
288 isGroup = true
289 extendee = n.extendee
290 case *syntheticMapField:
291 // shouldn't get here since we don't recurse into fields from a mapNode
292 // in generateSourceCodeInfoForMessage... but just in case
293 return
294 }
295
Scott Baker4a35a702019-11-26 08:17:33 -0800296 if isGroup {
297 // comments will appear on group message
298 sci.newLocWithoutComments(n, path)
299 if extendee != nil {
300 sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag))
301 }
302 if n.fieldLabel() != nil {
303 // no comments here either (label is first token for group, so we want
304 // to leave the comments to be associated with the group message instead)
305 sci.newLocWithoutComments(n.fieldLabel(), append(path, internal.Field_labelTag))
306 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700307 sci.newLoc(n.fieldType(), append(path, internal.Field_typeTag))
Scott Baker4a35a702019-11-26 08:17:33 -0800308 // let the name comments be attributed to the group name
309 sci.newLocWithoutComments(n.fieldName(), append(path, internal.Field_nameTag))
310 } else {
311 sci.newLoc(n, path)
312 if extendee != nil {
313 sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag))
314 }
315 if n.fieldLabel() != nil {
316 sci.newLoc(n.fieldLabel(), append(path, internal.Field_labelTag))
317 }
318 n.fieldType()
319 var tag int32
320 if _, isScalar := fieldTypes[fieldType]; isScalar {
321 tag = internal.Field_typeTag
322 } else {
323 // this is a message or an enum, so attribute type location
324 // to the type name field
325 tag = internal.Field_typeNameTag
326 }
327 sci.newLoc(n.fieldType(), append(path, tag))
328 sci.newLoc(n.fieldName(), append(path, internal.Field_nameTag))
Zack Williamse940c7a2019-08-21 14:25:39 -0700329 }
330 sci.newLoc(n.fieldTag(), append(path, internal.Field_numberTag))
Zack Williamse940c7a2019-08-21 14:25:39 -0700331
Scott Baker4a35a702019-11-26 08:17:33 -0800332 if opts != nil {
333 optsPath := append(path, internal.Field_optionsTag)
334 sci.newLoc(opts, optsPath)
335 var optIndex int32
336 for _, opt := range opts.decls {
337 r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
338 }
339 }
340}
341
342func (r *parseResult) generateSourceCodeInfoForExtensionRanges(sci *sourceCodeInfo, n *extensionRangeNode, extRangeIndex *int32, path []int32) {
343 sci.newLoc(n, path)
344 for _, child := range n.ranges {
345 path := append(path, *extRangeIndex)
346 *extRangeIndex++
347 sci.newLoc(child, path)
348 sci.newLoc(child.stNode, append(path, internal.ExtensionRange_startTag))
349 if child.stNode != child.enNode {
350 sci.newLoc(child.enNode, append(path, internal.ExtensionRange_endTag))
351 }
352 if n.options != nil {
353 optsPath := append(path, internal.ExtensionRange_optionsTag)
354 sci.newLoc(n.options, optsPath)
355 var optIndex int32
356 for _, opt := range n.options.decls {
357 r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
358 }
359 }
360 }
361}
362
363func (r *parseResult) generateSourceCodeInfoForService(sci *sourceCodeInfo, n *serviceNode, path []int32) {
364 sci.newLoc(n, path)
365 sci.newLoc(n.name, append(path, internal.Service_nameTag))
366 var optIndex, rpcIndex int32
367 for _, child := range n.decls {
368 switch {
369 case child.option != nil:
370 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Service_optionsTag))
371 case child.rpc != nil:
372 r.generateSourceCodeInfoForMethod(sci, child.rpc, append(path, internal.Service_methodsTag, rpcIndex))
373 rpcIndex++
374 }
375 }
376}
377
378func (r *parseResult) generateSourceCodeInfoForMethod(sci *sourceCodeInfo, n *methodNode, path []int32) {
379 sci.newLoc(n, path)
380 sci.newLoc(n.name, append(path, internal.Method_nameTag))
381 if n.input.streamKeyword != nil {
382 sci.newLoc(n.input.streamKeyword, append(path, internal.Method_inputStreamTag))
383 }
384 sci.newLoc(n.input.msgType, append(path, internal.Method_inputTag))
385 if n.output.streamKeyword != nil {
386 sci.newLoc(n.output.streamKeyword, append(path, internal.Method_outputStreamTag))
387 }
388 sci.newLoc(n.output.msgType, append(path, internal.Method_outputTag))
389
390 optsPath := append(path, internal.Method_optionsTag)
391 var optIndex int32
392 for _, opt := range n.options {
393 r.generateSourceCodeInfoForOption(sci, opt, false, &optIndex, optsPath)
394 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700395}
396
397type sourceCodeInfo struct {
398 locs []*dpb.SourceCodeInfo_Location
399 commentsUsed map[*comment]struct{}
400}
401
Scott Baker4a35a702019-11-26 08:17:33 -0800402func (sci *sourceCodeInfo) newLocWithoutComments(n node, path []int32) {
403 dup := make([]int32, len(path))
404 copy(dup, path)
405 sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
406 Path: dup,
407 Span: makeSpan(n.start(), n.end()),
408 })
409}
410
Zack Williamse940c7a2019-08-21 14:25:39 -0700411func (sci *sourceCodeInfo) newLoc(n node, path []int32) {
412 leadingComments := n.leadingComments()
413 trailingComments := n.trailingComments()
414 if sci.commentUsed(leadingComments) {
415 leadingComments = nil
416 }
417 if sci.commentUsed(trailingComments) {
418 trailingComments = nil
419 }
420 detached := groupComments(leadingComments)
Scott Baker4a35a702019-11-26 08:17:33 -0800421 var trail *string
422 if str, ok := combineComments(trailingComments); ok {
423 trail = proto.String(str)
424 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700425 var lead *string
426 if len(leadingComments) > 0 && leadingComments[len(leadingComments)-1].end.Line >= n.start().Line-1 {
427 lead = proto.String(detached[len(detached)-1])
428 detached = detached[:len(detached)-1]
429 }
430 dup := make([]int32, len(path))
431 copy(dup, path)
Zack Williamse940c7a2019-08-21 14:25:39 -0700432 sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
433 LeadingDetachedComments: detached,
434 LeadingComments: lead,
435 TrailingComments: trail,
436 Path: dup,
Scott Baker4a35a702019-11-26 08:17:33 -0800437 Span: makeSpan(n.start(), n.end()),
Zack Williamse940c7a2019-08-21 14:25:39 -0700438 })
439}
440
Scott Baker4a35a702019-11-26 08:17:33 -0800441func makeSpan(start, end *SourcePos) []int32 {
442 if start.Line == end.Line {
443 return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Col) - 1}
444 }
445 return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Line) - 1, int32(end.Col) - 1}
446}
447
448func (sci *sourceCodeInfo) commentUsed(c []comment) bool {
Zack Williamse940c7a2019-08-21 14:25:39 -0700449 if len(c) == 0 {
450 return false
451 }
Scott Baker4a35a702019-11-26 08:17:33 -0800452 if _, ok := sci.commentsUsed[&c[0]]; ok {
Zack Williamse940c7a2019-08-21 14:25:39 -0700453 return true
454 }
455
Scott Baker4a35a702019-11-26 08:17:33 -0800456 sci.commentsUsed[&c[0]] = struct{}{}
Zack Williamse940c7a2019-08-21 14:25:39 -0700457 return false
458}
459
Scott Baker4a35a702019-11-26 08:17:33 -0800460func groupComments(comments []comment) []string {
Zack Williamse940c7a2019-08-21 14:25:39 -0700461 if len(comments) == 0 {
462 return nil
463 }
464
465 var groups []string
466 singleLineStyle := comments[0].text[:2] == "//"
467 line := comments[0].end.Line
468 start := 0
469 for i := 1; i < len(comments); i++ {
470 c := comments[i]
471 prevSingleLine := singleLineStyle
472 singleLineStyle = strings.HasPrefix(comments[i].text, "//")
473 if !singleLineStyle || prevSingleLine != singleLineStyle || c.start.Line > line+1 {
474 // new group!
Scott Baker4a35a702019-11-26 08:17:33 -0800475 if str, ok := combineComments(comments[start:i]); ok {
476 groups = append(groups, str)
477 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700478 start = i
479 }
480 line = c.end.Line
481 }
482 // don't forget last group
Scott Baker4a35a702019-11-26 08:17:33 -0800483 if str, ok := combineComments(comments[start:]); ok {
484 groups = append(groups, str)
485 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700486 return groups
487}
488
Scott Baker4a35a702019-11-26 08:17:33 -0800489func combineComments(comments []comment) (string, bool) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700490 if len(comments) == 0 {
Scott Baker4a35a702019-11-26 08:17:33 -0800491 return "", false
Zack Williamse940c7a2019-08-21 14:25:39 -0700492 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700493 var buf bytes.Buffer
494 for _, c := range comments {
Zack Williamse940c7a2019-08-21 14:25:39 -0700495 if c.text[:2] == "//" {
496 buf.WriteString(c.text[2:])
497 } else {
498 lines := strings.Split(c.text[2:len(c.text)-2], "\n")
499 first := true
500 for _, l := range lines {
501 if first {
502 first = false
503 } else {
504 buf.WriteByte('\n')
505 }
506
507 // strip a prefix of whitespace followed by '*'
508 j := 0
509 for j < len(l) {
510 if l[j] != ' ' && l[j] != '\t' {
511 break
512 }
513 j++
514 }
515 if j == len(l) {
516 l = ""
517 } else if l[j] == '*' {
518 l = l[j+1:]
519 } else if j > 0 {
520 l = " " + l[j:]
521 }
522
523 buf.WriteString(l)
524 }
525 }
526 }
Scott Baker4a35a702019-11-26 08:17:33 -0800527 return buf.String(), true
Zack Williamse940c7a2019-08-21 14:25:39 -0700528}
529
Scott Baker4a35a702019-11-26 08:17:33 -0800530func dup(p []int32) []int32 {
531 return append(([]int32)(nil), p...)
Zack Williamse940c7a2019-08-21 14:25:39 -0700532}