1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package protokit
import (
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"bytes"
"strconv"
"strings"
)
// A Comment describes the leading, trailing, and detached comments for a proto object. See `SourceCodeInfo_Location` in
// descriptor.proto for details on what those terms mean
type Comment struct {
Leading string
Trailing string
Detached []string
}
// String returns the leading and trailing comments joined by 2 line breaks (`\n\n`). If either are empty, the line
// breaks are removed.
func (c *Comment) String() string {
b := new(bytes.Buffer)
if c.GetLeading() != "" {
b.WriteString(c.GetLeading())
b.WriteString("\n\n")
}
b.WriteString(c.GetTrailing())
return strings.TrimSpace(b.String())
}
func newComment(loc *descriptor.SourceCodeInfo_Location) *Comment {
detached := make([]string, len(loc.GetLeadingDetachedComments()))
for i, c := range loc.GetLeadingDetachedComments() {
detached[i] = scrub(c)
}
return &Comment{
Leading: scrub(loc.GetLeadingComments()),
Trailing: scrub(loc.GetTrailingComments()),
Detached: detached,
}
}
// GetLeading returns the leading comments
func (c *Comment) GetLeading() string { return c.Leading }
// GetTrailing returns the leading comments
func (c *Comment) GetTrailing() string { return c.Trailing }
// GetDetached returns the detached leading comments
func (c *Comment) GetDetached() []string { return c.Detached }
// Comments is a map of source location paths to values.
type Comments map[string]*Comment
// ParseComments parses all comments within a proto file. The locations are encoded into the map by joining the paths
// with a "." character. E.g. `4.2.3.0`.
//
// Leading/trailing spaces are trimmed for each comment type (leading, trailing, detached)
func ParseComments(fd *descriptor.FileDescriptorProto) Comments {
comments := make(Comments)
for _, loc := range fd.GetSourceCodeInfo().GetLocation() {
if loc.GetLeadingComments() == "" && loc.GetTrailingComments() == "" && len(loc.GetLeadingDetachedComments()) == 0 {
continue
}
path := loc.GetPath()
key := make([]string, len(path))
for idx, p := range path {
key[idx] = strconv.Itoa(int(p))
}
comments[strings.Join(key, ".")] = newComment(loc)
}
return comments
}
func (c Comments) Get(path string) *Comment {
if val, ok := c[path]; ok {
return val
}
// return an empty comment
return &Comment{Detached: make([]string, 0)}
}
func scrub(str string) string {
return strings.TrimSpace(strings.Replace(str, "\n ", "\n", -1))
}
|