protofmt/utils.go

191 lines
4.6 KiB
Go

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")
}