first commit
This commit is contained in:
commit
993f06cb18
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.idea
|
51
aligned.go
Normal file
51
aligned.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
type aligned struct {
|
||||||
|
source string
|
||||||
|
left bool
|
||||||
|
padding bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
alignedEquals = leftAligned(" = ")
|
||||||
|
alignedShortEquals = leftAligned("=")
|
||||||
|
alignedSpace = leftAligned(" ")
|
||||||
|
alignedComma = leftAligned(", ")
|
||||||
|
alignedEmpty = leftAligned("")
|
||||||
|
alignedSemicolon = notAligned(";")
|
||||||
|
alignedColon = notAligned(":")
|
||||||
|
)
|
||||||
|
|
||||||
|
func leftAligned(src string) aligned { return aligned{src, true, true} }
|
||||||
|
func rightAligned(src string) aligned { return aligned{src, false, true} }
|
||||||
|
func notAligned(src string) aligned { return aligned{src, false, false} }
|
||||||
|
|
||||||
|
func (a aligned) preferredWidth() int {
|
||||||
|
if !a.hasAlignment() {
|
||||||
|
return 0 // means do not force padding because of this source
|
||||||
|
}
|
||||||
|
return len(a.source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a aligned) formatted(indentSeparator string, indentLevel, width int) string {
|
||||||
|
if !a.padding {
|
||||||
|
// if the source has newlines then make sure the correct indent level is applied
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for _, each := range a.source {
|
||||||
|
buf.WriteRune(each)
|
||||||
|
if '\n' == each {
|
||||||
|
buf.WriteString(strings.Repeat(indentSeparator, indentLevel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
if a.left {
|
||||||
|
return a.source + strings.Repeat(" ", width-len(a.source))
|
||||||
|
}
|
||||||
|
return strings.Repeat(" ", width-len(a.source)) + a.source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a aligned) hasAlignment() bool { return a.left || a.padding }
|
206
columns.go
Normal file
206
columns.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/emicklei/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func columns(v proto.Visitee) []aligned {
|
||||||
|
return asColumnsPrintable(v).columns()
|
||||||
|
}
|
||||||
|
|
||||||
|
// columnsPrintable is for elements that can be printed in aligned columns.
|
||||||
|
type columnsPrintable interface {
|
||||||
|
columns() (cols []aligned)
|
||||||
|
}
|
||||||
|
|
||||||
|
type columnsPrinter struct {
|
||||||
|
cols []aligned
|
||||||
|
visitee proto.Visitee
|
||||||
|
}
|
||||||
|
|
||||||
|
func asColumnsPrintable(v proto.Visitee) *columnsPrinter {
|
||||||
|
p := new(columnsPrinter)
|
||||||
|
p.visitee = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// columns is part of columnsPrintable
|
||||||
|
func (p *columnsPrinter) columns() []aligned {
|
||||||
|
p.visitee.Accept(p)
|
||||||
|
return p.cols
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *columnsPrinter) VisitMessage(m *proto.Message) {}
|
||||||
|
func (p *columnsPrinter) VisitService(v *proto.Service) {}
|
||||||
|
func (p *columnsPrinter) VisitSyntax(s *proto.Syntax) {}
|
||||||
|
func (p *columnsPrinter) VisitPackage(pkg *proto.Package) {
|
||||||
|
p.cols = append(p.cols, notAligned("package "), notAligned(pkg.Name), alignedSemicolon)
|
||||||
|
if pkg.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, notAligned(" //"), notAligned(pkg.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *columnsPrinter) VisitOption(o *proto.Option) {
|
||||||
|
if !o.IsEmbedded {
|
||||||
|
p.cols = append(p.cols, leftAligned("option "))
|
||||||
|
} else {
|
||||||
|
p.cols = append(p.cols, leftAligned(" ["))
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, keyValuePair(o, o.IsEmbedded)...)
|
||||||
|
if o.IsEmbedded {
|
||||||
|
p.cols = append(p.cols, leftAligned("]"))
|
||||||
|
}
|
||||||
|
if !o.IsEmbedded {
|
||||||
|
p.cols = append(p.cols, alignedSemicolon)
|
||||||
|
if o.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, notAligned(" //"), notAligned(o.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *columnsPrinter) VisitImport(i *proto.Import) {
|
||||||
|
p.cols = append(p.cols, leftAligned("import"), alignedSpace)
|
||||||
|
if len(i.Kind) > 0 {
|
||||||
|
p.cols = append(p.cols, leftAligned(i.Kind), alignedSpace)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, notAligned(fmt.Sprintf("%q", i.Filename)), alignedSemicolon)
|
||||||
|
if i.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, notAligned(" //"), notAligned(i.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitNormalField
|
||||||
|
// [|repeated][|optional][space][name][equals][sequence][|option]
|
||||||
|
func (p *columnsPrinter) VisitNormalField(f *proto.NormalField) {
|
||||||
|
if f.Repeated {
|
||||||
|
p.cols = append(p.cols, leftAligned("repeated "))
|
||||||
|
} else if f.Optional {
|
||||||
|
p.cols = append(p.cols, leftAligned("optional "))
|
||||||
|
} else if f.Required {
|
||||||
|
p.cols = append(p.cols, leftAligned("required "))
|
||||||
|
} else {
|
||||||
|
p.cols = append(p.cols, alignedEmpty)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, leftAligned(f.Type), alignedSpace, leftAligned(f.Name), alignedEquals, rightAligned(strconv.Itoa(f.Sequence)))
|
||||||
|
if len(f.Options) > 0 {
|
||||||
|
p.cols = append(p.cols, leftAligned(" ["))
|
||||||
|
for i, each := range f.Options {
|
||||||
|
if i > 0 {
|
||||||
|
p.cols = append(p.cols, alignedComma)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, keyValuePair(each, true)...)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, leftAligned("]"))
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, alignedSemicolon)
|
||||||
|
if f.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, alignedInlinePrefix(f.InlineComment), notAligned(f.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *columnsPrinter) VisitEnumField(f *proto.EnumField) {
|
||||||
|
p.cols = append(p.cols, leftAligned(f.Name), alignedEquals, rightAligned(strconv.Itoa(f.Integer)))
|
||||||
|
if f.ValueOption != nil {
|
||||||
|
p.cols = append(p.cols, columns(f.ValueOption)...)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, alignedSemicolon)
|
||||||
|
if f.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, alignedInlinePrefix(f.InlineComment), notAligned(f.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *columnsPrinter) VisitEnum(e *proto.Enum) {}
|
||||||
|
func (p *columnsPrinter) VisitComment(e *proto.Comment) {}
|
||||||
|
func (p *columnsPrinter) VisitOneof(o *proto.Oneof) {}
|
||||||
|
func (p *columnsPrinter) VisitOneofField(o *proto.OneOfField) {
|
||||||
|
p.cols = append(p.cols,
|
||||||
|
leftAligned(o.Type),
|
||||||
|
alignedSpace,
|
||||||
|
leftAligned(o.Name),
|
||||||
|
alignedEquals,
|
||||||
|
rightAligned(strconv.Itoa(o.Sequence)))
|
||||||
|
if len(o.Options) > 0 {
|
||||||
|
p.cols = append(p.cols, leftAligned(" ["))
|
||||||
|
for i, each := range o.Options {
|
||||||
|
if i > 0 {
|
||||||
|
p.cols = append(p.cols, alignedComma)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, keyValuePair(each, true)...)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, leftAligned("]"))
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, alignedSemicolon)
|
||||||
|
if o.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, notAligned(" //"), notAligned(o.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *columnsPrinter) VisitReserved(rs *proto.Reserved) {}
|
||||||
|
func (p *columnsPrinter) VisitRPC(r *proto.RPC) {
|
||||||
|
p.cols = append(p.cols,
|
||||||
|
leftAligned("rpc "),
|
||||||
|
leftAligned(r.Name),
|
||||||
|
leftAligned(" ("))
|
||||||
|
if r.StreamsRequest {
|
||||||
|
p.cols = append(p.cols, leftAligned("stream "))
|
||||||
|
} else {
|
||||||
|
p.cols = append(p.cols, alignedEmpty)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols,
|
||||||
|
leftAligned(r.RequestType),
|
||||||
|
leftAligned(") "),
|
||||||
|
leftAligned("returns"),
|
||||||
|
leftAligned(" ("))
|
||||||
|
if r.StreamsReturns {
|
||||||
|
p.cols = append(p.cols, leftAligned("stream "))
|
||||||
|
} else {
|
||||||
|
p.cols = append(p.cols, alignedEmpty)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols,
|
||||||
|
leftAligned(r.ReturnsType),
|
||||||
|
leftAligned(")"))
|
||||||
|
if len(r.Elements) > 0 {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
io.WriteString(buf, " {\n")
|
||||||
|
f := NewFormatter(buf, " ") // TODO get separator, now 2 spaces
|
||||||
|
f.level(1)
|
||||||
|
for _, each := range r.Elements {
|
||||||
|
each.Accept(f)
|
||||||
|
io.WriteString(buf, "\n")
|
||||||
|
}
|
||||||
|
f.indent(-1)
|
||||||
|
io.WriteString(buf, "}")
|
||||||
|
p.cols = append(p.cols, notAligned(buf.String()))
|
||||||
|
} else {
|
||||||
|
p.cols = append(p.cols, alignedSemicolon)
|
||||||
|
}
|
||||||
|
if r.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, notAligned(" //"), notAligned(r.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *columnsPrinter) VisitMapField(f *proto.MapField) {
|
||||||
|
p.cols = append(p.cols,
|
||||||
|
alignedEmpty, // no repeated no optional
|
||||||
|
leftAligned(fmt.Sprintf("map <%s,%s>", f.KeyType, f.Type)),
|
||||||
|
alignedSpace,
|
||||||
|
leftAligned(f.Name),
|
||||||
|
alignedEquals,
|
||||||
|
rightAligned(strconv.Itoa(f.Sequence)))
|
||||||
|
if len(f.Options) > 0 {
|
||||||
|
p.cols = append(p.cols, leftAligned(" ["))
|
||||||
|
for i, each := range f.Options {
|
||||||
|
if i > 0 {
|
||||||
|
p.cols = append(p.cols, alignedComma)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, keyValuePair(each, true)...)
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, leftAligned("]"))
|
||||||
|
}
|
||||||
|
p.cols = append(p.cols, alignedSemicolon)
|
||||||
|
if f.InlineComment != nil {
|
||||||
|
p.cols = append(p.cols, alignedInlinePrefix(f.InlineComment), notAligned(f.InlineComment.Message()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *columnsPrinter) VisitGroup(g *proto.Group) {}
|
||||||
|
func (p *columnsPrinter) VisitExtensions(e *proto.Extensions) {}
|
47
extensions.go
Normal file
47
extensions.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import "github.com/emicklei/proto"
|
||||||
|
|
||||||
|
// keyValuePair returns key = value or "value"
|
||||||
|
func keyValuePair(o *proto.Option, embedded bool) (cols []aligned) {
|
||||||
|
equals := alignedEquals
|
||||||
|
name := o.Name
|
||||||
|
if len(o.Constant.OrderedMap) > 0 {
|
||||||
|
cols = append(cols, leftAligned(name), equals)
|
||||||
|
cols = append(cols, columnsPrintablesFromMap(o.Constant.OrderedMap)...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if embedded {
|
||||||
|
return append(cols, leftAligned(name), equals, leftAligned(o.Constant.SourceRepresentation())) // numbers right, strings left? TODO
|
||||||
|
}
|
||||||
|
return append(cols, rightAligned(name), equals, rightAligned(o.Constant.SourceRepresentation()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func alignedInlinePrefix(c *proto.Comment) aligned {
|
||||||
|
prefix := " //"
|
||||||
|
if c.ExtraSlash {
|
||||||
|
prefix = " ///"
|
||||||
|
}
|
||||||
|
return notAligned(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func columnsPrintables(c *proto.Comment) (list []columnsPrintable) {
|
||||||
|
for _, each := range c.Lines {
|
||||||
|
list = append(list, inlineComment{each, c.ExtraSlash})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeAssertColumnsPrintable(v proto.Visitee) (columnsPrintable, bool) {
|
||||||
|
return asColumnsPrintable(v), len(asColumnsPrintable(v).columns()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func columnsPrintablesFromMap(m proto.LiteralMap) (cols []aligned) {
|
||||||
|
cols = append(cols, leftAligned("{"), alignedSpace)
|
||||||
|
for _, each := range m {
|
||||||
|
// TODO only works for simple constants
|
||||||
|
cols = append(cols, leftAligned(each.Name), alignedColon, leftAligned(each.SourceRepresentation()))
|
||||||
|
}
|
||||||
|
cols = append(cols, alignedSpace, leftAligned("}"))
|
||||||
|
return
|
||||||
|
}
|
233
formatter.go
Normal file
233
formatter.go
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/emicklei/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Formatter visits a Proto and writes formatted source.
|
||||||
|
type Formatter struct {
|
||||||
|
w io.Writer
|
||||||
|
indentSeparator string
|
||||||
|
indentLevel int
|
||||||
|
lastStmt string
|
||||||
|
lastLevel int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFormatter returns a new Formatter. Only the indentation separator is configurable.
|
||||||
|
func NewFormatter(writer io.Writer, indentSeparator string) *Formatter {
|
||||||
|
return &Formatter{w: writer, indentSeparator: indentSeparator}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format visits all proto elements and writes formatted source.
|
||||||
|
func (f *Formatter) Format(p *proto.Proto) {
|
||||||
|
for _, each := range p.Elements {
|
||||||
|
each.Accept(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitComment formats a Comment and writes enclosing newlines.
|
||||||
|
func (f *Formatter) VisitComment(c *proto.Comment) {
|
||||||
|
f.printComment(c)
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitEnum formats a Enum.
|
||||||
|
func (f *Formatter) VisitEnum(e *proto.Enum) {
|
||||||
|
f.begin("enum", e)
|
||||||
|
if _, ok := e.Parent.(*proto.Message); !ok && (e.Comment == nil || len(e.Comment.Lines) == 0) {
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "enum %s {", e.Name)
|
||||||
|
if len(e.Elements) > 0 {
|
||||||
|
f.nl()
|
||||||
|
f.level(1)
|
||||||
|
f.printAsGroups(e.Elements)
|
||||||
|
f.indent(-1)
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, "}\n")
|
||||||
|
f.end("enum")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitEnumField formats a EnumField.
|
||||||
|
func (f *Formatter) VisitEnumField(e *proto.EnumField) {
|
||||||
|
f.printAsGroups([]proto.Visitee{e})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitImport formats a Import.
|
||||||
|
func (f *Formatter) VisitImport(i *proto.Import) {
|
||||||
|
f.beginNoDoc("import", i)
|
||||||
|
f.printAsGroups([]proto.Visitee{i})
|
||||||
|
f.end("import")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitMessage formats a Message.
|
||||||
|
func (f *Formatter) VisitMessage(m *proto.Message) {
|
||||||
|
f.begin("message", m)
|
||||||
|
if _, ok := m.Parent.(*proto.Message); !ok && (m.Comment == nil || len(m.Comment.Lines) == 0) {
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
if m.IsExtend {
|
||||||
|
fmt.Fprintf(f.w, "extend ")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f.w, "message ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "%s {", m.Name)
|
||||||
|
if len(m.Elements) > 0 {
|
||||||
|
f.nl()
|
||||||
|
f.level(1)
|
||||||
|
f.printAsGroups(m.Elements)
|
||||||
|
f.indent(-1)
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, "}\n")
|
||||||
|
f.end("message")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitOption formats a Option.
|
||||||
|
func (f *Formatter) VisitOption(o *proto.Option) {
|
||||||
|
f.begin("option", o)
|
||||||
|
fmt.Fprintf(f.w, "option %s = ", o.Name)
|
||||||
|
f.formatLiteral(&o.Constant)
|
||||||
|
fmt.Fprintf(f.w, ";\n")
|
||||||
|
if o.InlineComment != nil {
|
||||||
|
fmt.Fprintf(f.w, " //%s", o.InlineComment.Message())
|
||||||
|
}
|
||||||
|
f.end("option")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatLiteral(l *proto.Literal) {
|
||||||
|
if len(l.OrderedMap) == 0 && len(l.Array) == 0 {
|
||||||
|
fmt.Fprintf(f.w, "%s", l.SourceRepresentation())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "{\n")
|
||||||
|
for _, other := range l.OrderedMap {
|
||||||
|
f.indent(1)
|
||||||
|
fmt.Fprintf(f.w, "%s", other.Name)
|
||||||
|
if other.PrintsColon {
|
||||||
|
fmt.Fprintf(f.w, ": ")
|
||||||
|
}
|
||||||
|
f.formatLiteral(other.Literal)
|
||||||
|
f.level(-1)
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
f.indent(0)
|
||||||
|
fmt.Fprintf(f.w, "}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitPackage formats a Package.
|
||||||
|
func (f *Formatter) VisitPackage(p *proto.Package) {
|
||||||
|
f.begin("package", p)
|
||||||
|
f.printAsGroups([]proto.Visitee{p})
|
||||||
|
f.end("package")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitService formats a Service.
|
||||||
|
func (f *Formatter) VisitService(s *proto.Service) {
|
||||||
|
f.begin("service", s)
|
||||||
|
fmt.Fprintf(f.w, "service %s {", s.Name)
|
||||||
|
if len(s.Elements) > 0 {
|
||||||
|
f.nl()
|
||||||
|
f.level(1)
|
||||||
|
f.printAsGroups(s.Elements)
|
||||||
|
f.indent(-1)
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, "}\n\n")
|
||||||
|
f.end("service")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitSyntax formats a Syntax.
|
||||||
|
func (f *Formatter) VisitSyntax(s *proto.Syntax) {
|
||||||
|
f.begin("syntax", s)
|
||||||
|
fmt.Fprintf(f.w, "syntax = %q", s.Value)
|
||||||
|
f.endWithComment(s.InlineComment)
|
||||||
|
f.end("syntax")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitOneof formats a Oneof.
|
||||||
|
func (f *Formatter) VisitOneof(o *proto.Oneof) {
|
||||||
|
f.begin("oneof", o)
|
||||||
|
fmt.Fprintf(f.w, "oneof %s {", o.Name)
|
||||||
|
if len(o.Elements) > 0 {
|
||||||
|
f.nl()
|
||||||
|
f.level(1)
|
||||||
|
f.printAsGroups(o.Elements)
|
||||||
|
f.indent(-1)
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, "}\n")
|
||||||
|
f.end("oneof")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitOneofField formats a OneofField.
|
||||||
|
func (f *Formatter) VisitOneofField(o *proto.OneOfField) {
|
||||||
|
f.printAsGroups([]proto.Visitee{o})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitReserved formats a Reserved.
|
||||||
|
func (f *Formatter) VisitReserved(r *proto.Reserved) {
|
||||||
|
f.begin("reserved", r)
|
||||||
|
io.WriteString(f.w, "reserved ")
|
||||||
|
if len(r.Ranges) > 0 {
|
||||||
|
for i, each := range r.Ranges {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(f.w, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "%s", each.SourceRepresentation())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i, each := range r.FieldNames {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(f.w, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "%q", each)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.endWithComment(r.InlineComment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitRPC formats a RPC.
|
||||||
|
func (f *Formatter) VisitRPC(r *proto.RPC) {
|
||||||
|
f.printAsGroups([]proto.Visitee{r})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitMapField formats a MapField.
|
||||||
|
func (f *Formatter) VisitMapField(m *proto.MapField) {
|
||||||
|
f.printAsGroups([]proto.Visitee{m})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitNormalField formats a NormalField.
|
||||||
|
func (f *Formatter) VisitNormalField(f1 *proto.NormalField) {
|
||||||
|
f.printAsGroups([]proto.Visitee{f1})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitGroup formats a proto2 Group.
|
||||||
|
func (f *Formatter) VisitGroup(g *proto.Group) {
|
||||||
|
f.begin("group", g)
|
||||||
|
if g.Optional {
|
||||||
|
io.WriteString(f.w, "optional ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "group %s = %d {", g.Name, g.Sequence)
|
||||||
|
if len(g.Elements) > 0 {
|
||||||
|
f.nl()
|
||||||
|
f.level(1)
|
||||||
|
f.printAsGroups(g.Elements)
|
||||||
|
f.indent(-1)
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, "}\n")
|
||||||
|
f.end("group")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitExtensions formats a proto2 Extensions.
|
||||||
|
func (f *Formatter) VisitExtensions(e *proto.Extensions) {
|
||||||
|
f.begin("extensions", e)
|
||||||
|
io.WriteString(f.w, "extensions ")
|
||||||
|
for i, each := range e.Ranges {
|
||||||
|
if i > 0 {
|
||||||
|
io.WriteString(f.w, ", ")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "%s", each.SourceRepresentation())
|
||||||
|
}
|
||||||
|
f.endWithComment(e.InlineComment)
|
||||||
|
}
|
22
formatter_test.go
Normal file
22
formatter_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/emicklei/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrint(t *testing.T) {
|
||||||
|
def, err := os.ReadFile("testdata.proto")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
parser := proto.NewParser(bytes.NewReader(def))
|
||||||
|
parse, err := parser.Parse()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var buf = new(bytes.Buffer)
|
||||||
|
NewFormatter(buf, " ").Format(parse)
|
||||||
|
err = os.WriteFile("testdata.proto", buf.Bytes(), os.ModePerm)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module gitter.top/common/protofmt
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/emicklei/proto v1.13.2
|
||||||
|
github.com/stretchr/testify v1.9.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
12
go.sum
Normal file
12
go.sum
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
|
||||||
|
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
17
inline_comment.go
Normal file
17
inline_comment.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
type inlineComment struct {
|
||||||
|
line string
|
||||||
|
extraSlash bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i inlineComment) columns() (list []aligned) {
|
||||||
|
if len(i.line) == 0 {
|
||||||
|
return append(list, notAligned(""))
|
||||||
|
}
|
||||||
|
prefix := "//"
|
||||||
|
if i.extraSlash {
|
||||||
|
prefix = "///"
|
||||||
|
}
|
||||||
|
return append(list, notAligned(prefix+i.line))
|
||||||
|
}
|
35
reflector.go
Normal file
35
reflector.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import "github.com/emicklei/proto"
|
||||||
|
|
||||||
|
// reflector is a Visitor that can tell the short type name of a Visitee.
|
||||||
|
type reflector struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// sole instance of reflector
|
||||||
|
var namer = new(reflector)
|
||||||
|
|
||||||
|
func (r *reflector) VisitMessage(m *proto.Message) { r.name = "Message" }
|
||||||
|
func (r *reflector) VisitService(v *proto.Service) { r.name = "Service" }
|
||||||
|
func (r *reflector) VisitSyntax(s *proto.Syntax) { r.name = "Syntax" }
|
||||||
|
func (r *reflector) VisitPackage(p *proto.Package) { r.name = "Package" }
|
||||||
|
func (r *reflector) VisitOption(o *proto.Option) { r.name = "Option" }
|
||||||
|
func (r *reflector) VisitImport(i *proto.Import) { r.name = "Import" }
|
||||||
|
func (r *reflector) VisitNormalField(i *proto.NormalField) { r.name = "NormalField" }
|
||||||
|
func (r *reflector) VisitEnumField(i *proto.EnumField) { r.name = "EnumField" }
|
||||||
|
func (r *reflector) VisitEnum(e *proto.Enum) { r.name = "Enum" }
|
||||||
|
func (r *reflector) VisitComment(e *proto.Comment) { r.name = "Comment" }
|
||||||
|
func (r *reflector) VisitOneof(o *proto.Oneof) { r.name = "Oneof" }
|
||||||
|
func (r *reflector) VisitOneofField(o *proto.OneOfField) { r.name = "OneOfField" }
|
||||||
|
func (r *reflector) VisitReserved(rs *proto.Reserved) { r.name = "Reserved" }
|
||||||
|
func (r *reflector) VisitRPC(rpc *proto.RPC) { r.name = "RPC" }
|
||||||
|
func (r *reflector) VisitMapField(f *proto.MapField) { r.name = "NormalField" } // handle as
|
||||||
|
func (r *reflector) VisitGroup(g *proto.Group) { r.name = "Group" }
|
||||||
|
func (r *reflector) VisitExtensions(e *proto.Extensions) { r.name = "Extensions" }
|
||||||
|
|
||||||
|
// nameOfVisitee returns the short type name of a Visitee.
|
||||||
|
func nameOfVisitee(e proto.Visitee) string {
|
||||||
|
e.Accept(namer)
|
||||||
|
return namer.name
|
||||||
|
}
|
139
testdata.proto
Normal file
139
testdata.proto
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package api_services.v1;
|
||||||
|
|
||||||
|
option go_package = "filesaver/gen;genv1";
|
||||||
|
|
||||||
|
|
||||||
|
// @route_group: true
|
||||||
|
// @base_url: /v1/file
|
||||||
|
// @gen_to: ./services/controller/v1/file_controller.go
|
||||||
|
service FileService {
|
||||||
|
// @desc: 列表
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: GET
|
||||||
|
// @api: /list
|
||||||
|
rpc List (FileServiceListRequest) returns (FileServiceListResponse);
|
||||||
|
// @desc: 上传
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: POST
|
||||||
|
// @api: /upload
|
||||||
|
rpc Upload (FileServiceUploadRequest) returns (FileServiceUploadResponse);
|
||||||
|
// @desc: 删除
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: DELETE
|
||||||
|
// @api: /delete
|
||||||
|
rpc Delete (FileServiceDeleteRequest) returns (FileServiceDeleteResponse);
|
||||||
|
// @desc: 下载
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: GET
|
||||||
|
// @api: /download
|
||||||
|
rpc Download (FileServiceDownloadRequest) returns (FileServiceDownloadResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message FileServiceListRequest {
|
||||||
|
string dirname = 1; // 目录
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileServiceListResponse {
|
||||||
|
message Metadata {
|
||||||
|
string domain = 1; // 域名
|
||||||
|
}
|
||||||
|
message Item {
|
||||||
|
string file_id = 1; // 文件ID
|
||||||
|
string filename = 2; // 文件名
|
||||||
|
string file_size = 3; // 文件大小
|
||||||
|
string created_at = 4; // 上传时间
|
||||||
|
string dirname = 5; // 文件路径
|
||||||
|
bool is_directory = 6; // 是否是目录
|
||||||
|
}
|
||||||
|
repeated Item items = 1; // 列表
|
||||||
|
Metadata metadata = 2; // 元数据
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileServiceUploadRequest {}
|
||||||
|
|
||||||
|
message FileServiceUploadResponse {}
|
||||||
|
|
||||||
|
message FileServiceDeleteRequest {
|
||||||
|
string file_id = 1; // 文件ID
|
||||||
|
string dirname = 2; // 文件夹
|
||||||
|
string filename = 3; // 文件名
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileServiceDeleteResponse {}
|
||||||
|
|
||||||
|
message FileServiceDownloadRequest {
|
||||||
|
string file_id = 1; // 文件ID
|
||||||
|
string dirname = 2; // 文件夹
|
||||||
|
string filename = 3; // 文件名
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileServiceDownloadResponse {}
|
||||||
|
|
||||||
|
|
||||||
|
// @route_group: true
|
||||||
|
// @base_url: /v1/setting
|
||||||
|
// @gen_to: ./services/controller/v1/setting_controller.go
|
||||||
|
service SettingService {
|
||||||
|
// @desc: 改变存储适配器
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: POST
|
||||||
|
// @api: /adapter
|
||||||
|
rpc SettingServiceChangeAdapter (SettingServiceChangeAdapterReq) returns (SettingServiceChangeAdapterResp);
|
||||||
|
// @desc: 改变存储适配器
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: POST
|
||||||
|
// @api: /cf_r2
|
||||||
|
rpc ConfigCloudFlareR2 (SettingServiceConfigCloudFlareR2Req) returns (SettingServiceConfigCloudFlareR2Resp);
|
||||||
|
// @desc: 获取cloudflare r2配置
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: GET
|
||||||
|
// @api: /cf_r2
|
||||||
|
rpc GetCloudFlareR2 (SettingServiceGetCloudFlareR2Req) returns (SettingServiceGetCloudFlareR2Resp);
|
||||||
|
// @desc: 获取适配器配置
|
||||||
|
// @author: Young Xu
|
||||||
|
// @method: GET
|
||||||
|
// @api: /adapter
|
||||||
|
rpc GetAdapter (SettingServiceGetAdapterReq) returns (SettingServiceGetAdapterResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message SettingServiceChangeAdapterReq {
|
||||||
|
string adapter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SettingServiceChangeAdapterResp {}
|
||||||
|
|
||||||
|
message SettingServiceConfigCloudFlareR2Req {
|
||||||
|
string api_token = 1;
|
||||||
|
string user_email = 2;
|
||||||
|
string user_id = 3;
|
||||||
|
string bucket = 4;
|
||||||
|
string s3_api = 5;
|
||||||
|
string domain = 6;
|
||||||
|
string secret_id = 7;
|
||||||
|
string secret_key = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SettingServiceConfigCloudFlareR2Resp {}
|
||||||
|
|
||||||
|
message SettingServiceGetCloudFlareR2Req {}
|
||||||
|
|
||||||
|
message SettingServiceGetCloudFlareR2Resp {
|
||||||
|
string api_token = 1;
|
||||||
|
string user_email = 2;
|
||||||
|
string user_id = 3;
|
||||||
|
string bucket = 4;
|
||||||
|
string s3_api = 5;
|
||||||
|
string domain = 6;
|
||||||
|
string secret_id = 7;
|
||||||
|
string secret_key = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SettingServiceGetAdapterReq {}
|
||||||
|
|
||||||
|
message SettingServiceGetAdapterResp {
|
||||||
|
string adapter = 1;
|
||||||
|
}
|
190
utils.go
Normal file
190
utils.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package protofmt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/emicklei/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// printDoc writes documentation is available
|
||||||
|
func (f *Formatter) printDoc(v proto.Visitee) {
|
||||||
|
if hasDoc, ok := v.(proto.Documented); ok {
|
||||||
|
if doc := hasDoc.Doc(); doc != nil {
|
||||||
|
f.printComment(doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printComment formats a Comment.
|
||||||
|
func (f *Formatter) printComment(c *proto.Comment) {
|
||||||
|
f.nl()
|
||||||
|
if c.Cstyle {
|
||||||
|
f.indent(0)
|
||||||
|
fmt.Fprintln(f.w, "/*")
|
||||||
|
}
|
||||||
|
for i, each := range c.Lines {
|
||||||
|
each = strings.TrimRight(each, " ")
|
||||||
|
f.indent(0)
|
||||||
|
if c.Cstyle {
|
||||||
|
// only skip first and last empty lines
|
||||||
|
skip := (i == 0 && len(each) == 0) ||
|
||||||
|
(i == len(c.Lines)-1 && len(each) == 0)
|
||||||
|
if !skip {
|
||||||
|
fmt.Fprintf(f.w, "%s\n", each)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if c.ExtraSlash {
|
||||||
|
fmt.Fprint(f.w, "/")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f.w, "//%s\n", each)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.Cstyle {
|
||||||
|
fmt.Fprintf(f.w, "*/\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin writes a newline if the last statement kind is different. always indents.
|
||||||
|
// if the Visitee has comment then print it.
|
||||||
|
func (f *Formatter) begin(stmt string, v proto.Visitee) {
|
||||||
|
// if not the first statement and different from last and on same indent level.
|
||||||
|
if len(f.lastStmt) > 0 && f.lastStmt != stmt && f.lastLevel == f.indentLevel {
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
f.lastStmt = stmt
|
||||||
|
f.printDoc(v)
|
||||||
|
f.indent(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// beginNoDoc writes a newline if the last statement kind is different. always indents.
|
||||||
|
func (f *Formatter) beginNoDoc(stmt string, v proto.Visitee) {
|
||||||
|
// if not the first statement and different from last and on same indent level.
|
||||||
|
if len(f.lastStmt) > 0 && f.lastStmt != stmt && f.lastLevel == f.indentLevel {
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
f.lastStmt = stmt
|
||||||
|
f.indent(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) end(stmt string) {
|
||||||
|
f.lastStmt = stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// indent changes the indent level and writes indentation.
|
||||||
|
func (f *Formatter) indent(diff int) {
|
||||||
|
f.level(diff)
|
||||||
|
for i := 0; i < f.indentLevel; i++ {
|
||||||
|
io.WriteString(f.w, f.indentSeparator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) printListOfColumns(list []columnsPrintable) {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// collect all column values
|
||||||
|
values := [][]aligned{}
|
||||||
|
widths := map[int]int{}
|
||||||
|
for _, each := range list {
|
||||||
|
cols := each.columns()
|
||||||
|
values = append(values, cols)
|
||||||
|
// update max widths per column
|
||||||
|
for i, other := range cols {
|
||||||
|
pw := other.preferredWidth()
|
||||||
|
w, ok := widths[i]
|
||||||
|
if ok {
|
||||||
|
if pw > w {
|
||||||
|
widths[i] = pw
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
widths[i] = pw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now print all values
|
||||||
|
for _, each := range values {
|
||||||
|
f.indent(0)
|
||||||
|
for c := 0; c < len(widths); c++ {
|
||||||
|
pw := widths[c]
|
||||||
|
// only print if there is a value
|
||||||
|
if c < len(each) {
|
||||||
|
// using space padding to match the max width
|
||||||
|
io.WriteString(f.w, each[c].formatted(f.indentSeparator, f.indentLevel, pw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.nl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nl writes a newline.
|
||||||
|
func (f *Formatter) nl() {
|
||||||
|
io.WriteString(f.w, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// level changes the current indentLevel but does not print indentation.
|
||||||
|
func (f *Formatter) level(diff int) {
|
||||||
|
f.lastLevel = f.indentLevel
|
||||||
|
f.indentLevel += diff
|
||||||
|
}
|
||||||
|
|
||||||
|
// printAsGroups prints the list in groups of the same element type.
|
||||||
|
func (f *Formatter) printAsGroups(list []proto.Visitee) {
|
||||||
|
group := []columnsPrintable{}
|
||||||
|
lastGroupName := ""
|
||||||
|
for _, each := range list {
|
||||||
|
groupName := nameOfVisitee(each)
|
||||||
|
printable, isColumnsPrintable := typeAssertColumnsPrintable(each)
|
||||||
|
if isColumnsPrintable {
|
||||||
|
if lastGroupName != groupName {
|
||||||
|
lastGroupName = groupName
|
||||||
|
// print current group
|
||||||
|
if len(group) > 0 {
|
||||||
|
f.printListOfColumns(group)
|
||||||
|
// begin new group
|
||||||
|
group = []columnsPrintable{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// comment as a group entity
|
||||||
|
if hasDoc, ok := each.(proto.Documented); ok {
|
||||||
|
if doc := hasDoc.Doc(); doc != nil {
|
||||||
|
f.printListOfColumns(group)
|
||||||
|
// begin new group
|
||||||
|
//group = []columnsPrintable{}
|
||||||
|
//if len(doc.Lines) > 0 { // if comment then add newline before it
|
||||||
|
// group = append(group, inlineComment{line: "", extraSlash: false})
|
||||||
|
//}
|
||||||
|
group = append([]columnsPrintable{}, columnsPrintables(doc)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group = append(group, printable)
|
||||||
|
} else {
|
||||||
|
// not printable in group
|
||||||
|
lastGroupName = groupName
|
||||||
|
// print current group
|
||||||
|
if len(group) > 0 {
|
||||||
|
f.printListOfColumns(group)
|
||||||
|
// begin new group
|
||||||
|
group = []columnsPrintable{}
|
||||||
|
}
|
||||||
|
each.Accept(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// print last group
|
||||||
|
f.printListOfColumns(group)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endWithComment writes a statement end (;) followed by inline comment if present.
|
||||||
|
func (f *Formatter) endWithComment(commentOrNil *proto.Comment) {
|
||||||
|
io.WriteString(f.w, ";")
|
||||||
|
if commentOrNil != nil {
|
||||||
|
if commentOrNil.ExtraSlash {
|
||||||
|
io.WriteString(f.w, " ///")
|
||||||
|
} else {
|
||||||
|
io.WriteString(f.w, " //")
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, commentOrNil.Message())
|
||||||
|
}
|
||||||
|
io.WriteString(f.w, "\n")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user