Skip to content
This repository has been archived by the owner on Jan 5, 2019. It is now read-only.

Commit

Permalink
add aligned , moved formatter to pkg, work on formatter
Browse files Browse the repository at this point in the history
Change-Id: I6cbf18dcf887ec61d9eda2f334a94eff6dea9360
  • Loading branch information
emicklei committed Feb 1, 2017
1 parent 1e552bc commit 6eac31d
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 62 deletions.
25 changes: 25 additions & 0 deletions aligned.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package proto

import "strings"

type aligned struct {
source string
left bool
}

var alignedEquals = leftAligned(" = ")

func leftAligned(src string) aligned { return aligned{src, true} }
func rightAligned(src string) aligned { return aligned{src, false} }

func (a aligned) preferredWidth() int { return len(a.source) }

func (a aligned) formatted(width int) string {
if len(a.source) > width {
return a.source[:width]
}
if a.left {
return a.source + strings.Repeat(" ", width-len(a.source))
}
return strings.Repeat(" ", width-len(a.source)) + a.source
}
5 changes: 1 addition & 4 deletions cmd/protofmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,5 @@ func main() {
if err != nil {
log.Fatalln("protofmt failed", err)
}
f := &formatter{w: os.Stdout, indentSeparator: " "}
for _, each := range def.Elements {
each.Accept(f)
}
proto.NewFormatter(os.Stdout, " ").Format(def)
}
5 changes: 4 additions & 1 deletion cmd/protofmt/unformatted.proto
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@ message Message
BILL_BAILEY = 3;
}

reserved 2, 15, 9 to 11;
reserved "foo", "bar";

map<string, Nested> terrain = 4;

enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 2 [(custom_option) = "hello world"];
RUN = 2 [(custom_option) = "hello world"];
}
}

Expand Down
14 changes: 13 additions & 1 deletion enum.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package proto

import "fmt"
import (
"fmt"
"strconv"
)

// Enum definition consists of a name and an enum body.
type Enum struct {
Expand All @@ -26,6 +29,15 @@ func (f *EnumField) Accept(v Visitor) {
v.VisitEnumField(f)
}

// columns returns printable source tokens
func (f EnumField) columns() (cols []aligned) {
cols = append(cols, leftAligned(f.Name), alignedEquals, leftAligned(strconv.Itoa(f.Integer)))
if f.ValueOption != nil {
cols = append(cols, f.ValueOption.columns()...)
}
return
}

func (f *EnumField) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
if tok != tIDENT {
Expand Down
103 changes: 47 additions & 56 deletions cmd/protofmt/formatter.go → formatter.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
package main
package proto

import (
"fmt"
"io"

"strings"

"github.com/emicklei/proto"
)

type formatter struct {
// Formatter visits a Proto and writes formatted source.
type Formatter struct {
w io.Writer
indentLevel int
lastStmt string
indentSeparator string
}

func (f *formatter) VisitComment(c *proto.Comment) {
// 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) {
for _, each := range p.Elements {
each.Accept(f)
}
}

// VisitComment formats a Comment.
func (f *Formatter) VisitComment(c *Comment) {
f.begin("comment")
if c.IsMultiline() {
fmt.Fprintln(f.w, "/*")
Expand All @@ -27,18 +39,18 @@ func (f *formatter) VisitComment(c *proto.Comment) {
}
}

func (f *formatter) VisitEnum(e *proto.Enum) {
// VisitEnum formats a Enum.
func (f *Formatter) VisitEnum(e *Enum) {
f.begin("enum")
fmt.Fprintf(f.w, "enum %s {", e.Name)
f.indentLevel++
for _, each := range e.Elements {
each.Accept(f)
}
f.printAsGroups(e.Elements)
f.indent(-1)
io.WriteString(f.w, "}\n")
}

func (f *formatter) VisitEnumField(e *proto.EnumField) {
// VisitEnumField formats a EnumField.
func (f *Formatter) VisitEnumField(e *EnumField) {
f.begin("field")
io.WriteString(f.w, paddedTo(e.Name, 10))
fmt.Fprintf(f.w, " = %d", e.Integer)
Expand All @@ -50,15 +62,17 @@ func (f *formatter) VisitEnumField(e *proto.EnumField) {
}
}

func (f *formatter) VisitImport(i *proto.Import) {
// VisitImport formats a Import.
func (f *Formatter) VisitImport(i *Import) {
f.begin("import")
if len(i.Kind) > 0 {
fmt.Fprintf(f.w, "import %s ", i.Kind)
}
fmt.Fprintf(f.w, "import %q;\n", i.Filename)
}

func (f *formatter) VisitMessage(m *proto.Message) {
// VisitMessage formats a Message.
func (f *Formatter) VisitMessage(m *Message) {
f.begin("message")
fmt.Fprintf(f.w, "message %s {", m.Name)
f.newLineIf(len(m.Elements) > 0)
Expand All @@ -70,7 +84,8 @@ func (f *formatter) VisitMessage(m *proto.Message) {
io.WriteString(f.w, "}\n")
}

func (f *formatter) VisitOption(o *proto.Option) {
// VisitOption formats a Option.
func (f *Formatter) VisitOption(o *Option) {
if o.IsEmbedded {
io.WriteString(f.w, "[(")
} else {
Expand All @@ -92,12 +107,14 @@ func (f *formatter) VisitOption(o *proto.Option) {
}
}

func (f *formatter) VisitPackage(p *proto.Package) {
// VisitPackage formats a Package.
func (f *Formatter) VisitPackage(p *Package) {
f.begin("package")
fmt.Fprintf(f.w, "package %s;\n", p.Name)
}

func (f *formatter) VisitService(s *proto.Service) {
// VisitService formats a Service.
func (f *Formatter) VisitService(s *Service) {
f.begin("service")
fmt.Fprintf(f.w, "service %s {", s.Name)
f.indentLevel++
Expand All @@ -108,11 +125,13 @@ func (f *formatter) VisitService(s *proto.Service) {
io.WriteString(f.w, "}\n")
}

func (f *formatter) VisitSyntax(s *proto.Syntax) {
// VisitSyntax formats a Syntax.
func (f *Formatter) VisitSyntax(s *Syntax) {
fmt.Fprintf(f.w, "syntax = %q;\n\n", s.Value)
}

func (f *formatter) VisitOneof(o *proto.Oneof) {
// VisitOneof formats a Oneof.
func (f *Formatter) VisitOneof(o *Oneof) {
f.begin("oneof")
fmt.Fprintf(f.w, "oneof %s {", o.Name)
f.indentLevel++
Expand All @@ -123,7 +142,8 @@ func (f *formatter) VisitOneof(o *proto.Oneof) {
io.WriteString(f.w, "}\n")
}

func (f *formatter) VisitOneofField(o *proto.OneOfField) {
// VisitOneofField formats a OneofField.
func (f *Formatter) VisitOneofField(o *OneOfField) {
f.begin("oneoffield")
fmt.Fprintf(f.w, "%s %s = %d", o.Type, o.Name, o.Sequence)
for _, each := range o.Options {
Expand All @@ -132,9 +152,10 @@ func (f *formatter) VisitOneofField(o *proto.OneOfField) {
io.WriteString(f.w, ";\n")
}

func (f *formatter) VisitReserved(r *proto.Reserved) {
// VisitReserved formats a Reserved.
func (f *Formatter) VisitReserved(r *Reserved) {
f.begin("reserved")
io.WriteString(f.w, "reserved")
io.WriteString(f.w, "reserved ")
if len(r.Ranges) > 0 {
io.WriteString(f.w, r.Ranges)
} else {
Expand All @@ -148,7 +169,8 @@ func (f *formatter) VisitReserved(r *proto.Reserved) {
io.WriteString(f.w, ";\n")
}

func (f *formatter) VisitRPC(r *proto.RPC) {
// VisitRPC formats a RPC.
func (f *Formatter) VisitRPC(r *RPC) {
f.begin("rpc")
fmt.Fprintf(f.w, "rpc %s (", r.Name)
if r.StreamsRequest {
Expand All @@ -163,12 +185,14 @@ func (f *formatter) VisitRPC(r *proto.RPC) {
io.WriteString(f.w, ");\n")
}

func (f *formatter) VisitMapField(m *proto.MapField) {
// VisitMapField formats a MapField.
func (f *Formatter) VisitMapField(m *MapField) {
f.begin("map")
fmt.Fprintf(f.w, "map<%s,%s> %s = %d;\n", m.KeyType, m.Type, m.Name, m.Sequence)
}

func (f *formatter) VisitNormalField(f1 *proto.NormalField) {
// VisitNormalField formats a NormalField.
func (f *Formatter) VisitNormalField(f1 *NormalField) {
f.begin("field")
if f1.Repeated {
io.WriteString(f.w, "repeated ")
Expand All @@ -178,36 +202,3 @@ func (f *formatter) VisitNormalField(f1 *proto.NormalField) {
}
fmt.Fprintf(f.w, "%s %s = %d;\n", f1.Type, f1.Name, f1.Sequence)
}

// Utils

func (f *formatter) begin(stmt string) {
if f.lastStmt != stmt && len(f.lastStmt) > 0 { // not the first line
// add separator because stmt is changed, unless it nested thingy
if !strings.Contains("comment", f.lastStmt) {
io.WriteString(f.w, "\n")
}
}
f.indent(0)
f.lastStmt = stmt
}

func (f *formatter) indent(diff int) {
f.indentLevel += diff
for i := 0; i < f.indentLevel; i++ {
io.WriteString(f.w, f.indentSeparator)
}
}

func paddedTo(word string, length int) string {
if len(word) >= length {
return word
}
return word + strings.Repeat(" ", length-len(word))
}

func (f *formatter) newLineIf(ok bool) {
if ok {
io.WriteString(f.w, "\n")
}
}
38 changes: 38 additions & 0 deletions formatter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package proto

import (
"bytes"
"testing"
)

func TestPrintListOfColumns(t *testing.T) {
e0 := new(EnumField)
e0.Name = "A"
e0.Integer = 1
op0 := new(Option)
op0.IsEmbedded = true
op0.Name = "a"
op0.Constant = Literal{Source: "1234"}
e0.ValueOption = op0

e1 := new(EnumField)
e1.Name = "ABC"
e1.Integer = 12
op1 := new(Option)
op1.IsEmbedded = true
op1.Name = "ab"
op1.Constant = Literal{Source: "1234"}
e1.ValueOption = op1

list := []columnsPrintable{e0, e1}
b := new(bytes.Buffer)
f := NewFormatter(b, " ")
f.printListOfColumns(list)
formatted := `
A = 1 [a =1234];
ABC = 12 [ab=1234];
`
if got, want := b.String(), formatted; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
Loading

0 comments on commit 6eac31d

Please sign in to comment.