printer: implement a working version

This commit is contained in:
Fatih Arslan 2015-10-25 16:08:09 +03:00
parent 5918e3592b
commit 958093df59
3 changed files with 71 additions and 57 deletions

View File

@ -3,30 +3,38 @@ package printer
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"github.com/fatih/hcl/ast" "github.com/fatih/hcl/ast"
) )
const (
blank = byte(' ')
newline = byte('\n')
tab = byte('\t')
)
func (p *printer) printNode(n ast.Node) []byte { func (p *printer) printNode(n ast.Node) []byte {
var buf bytes.Buffer var buf bytes.Buffer
switch t := n.(type) { switch t := n.(type) {
case *ast.ObjectList: case *ast.ObjectList:
fmt.Println("printing objectList", t) for i, item := range t.Items {
for _, item := range t.Items {
buf.Write(p.printObjectItem(item)) buf.Write(p.printObjectItem(item))
if i != len(t.Items)-1 {
buf.WriteByte(newline)
}
} }
case *ast.ObjectKey: case *ast.ObjectKey:
fmt.Println("printing objectKey", t) buf.WriteString(t.Token.Text)
case *ast.ObjectItem: case *ast.ObjectItem:
fmt.Println("printing objectItem", t)
buf.Write(p.printObjectItem(t)) buf.Write(p.printObjectItem(t))
case *ast.LiteralType: case *ast.LiteralType:
buf.Write(p.printLiteral(t)) buf.WriteString(t.Token.Text)
case *ast.ListType: case *ast.ListType:
buf.Write(p.printList(t)) buf.Write(p.printList(t))
case *ast.ObjectType: case *ast.ObjectType:
fmt.Println("printing ObjectType", t) buf.Write(p.printObjectType(t))
default: default:
fmt.Printf(" unknown type: %T\n", n) fmt.Printf(" unknown type: %T\n", n)
} }
@ -39,14 +47,12 @@ func (p *printer) printObjectItem(o *ast.ObjectItem) []byte {
for i, k := range o.Keys { for i, k := range o.Keys {
buf.WriteString(k.Token.Text) buf.WriteString(k.Token.Text)
if i != len(o.Keys)-1 || len(o.Keys) == 1 { buf.WriteByte(blank)
buf.WriteString(" ")
}
// reach end of key // reach end of key
if i == len(o.Keys)-1 { if i == len(o.Keys)-1 {
buf.WriteString("=") buf.WriteString("=")
buf.WriteString(" ") buf.WriteByte(blank)
} }
} }
@ -58,6 +64,26 @@ func (p *printer) printLiteral(l *ast.LiteralType) []byte {
return []byte(l.Token.Text) return []byte(l.Token.Text)
} }
func (p *printer) printObjectType(o *ast.ObjectType) []byte {
var buf bytes.Buffer
buf.WriteString("{")
buf.WriteByte(newline)
for _, item := range o.List.Items {
// buf.WriteByte(tab)
// buf.Write(p.printObjectItem(item))
a := p.printObjectItem(item)
a = indent(a)
buf.Write(a)
buf.WriteByte(newline)
}
buf.WriteString("}")
return buf.Bytes()
}
func (p *printer) printList(l *ast.ListType) []byte { func (p *printer) printList(l *ast.ListType) []byte {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("[") buf.WriteString("[")
@ -65,20 +91,44 @@ func (p *printer) printList(l *ast.ListType) []byte {
for i, item := range l.List { for i, item := range l.List {
if item.Pos().Line != l.Lbrack.Line { if item.Pos().Line != l.Lbrack.Line {
// not same line // not same line
buf.WriteString("\n") buf.WriteByte(newline)
} }
buf.WriteByte(tab)
buf.Write(p.printNode(item)) buf.Write(p.printNode(item))
if i != len(l.List)-1 { if i != len(l.List)-1 {
buf.WriteString(",") buf.WriteString(",")
buf.WriteString(" ") buf.WriteByte(blank)
} else if item.Pos().Line != l.Lbrack.Line { } else if item.Pos().Line != l.Lbrack.Line {
buf.WriteString(",") buf.WriteString(",")
buf.WriteString("\n") buf.WriteByte(newline)
} }
} }
buf.WriteString("]") buf.WriteString("]")
return buf.Bytes() return buf.Bytes()
} }
func writeBlank(buf io.ByteWriter, indent int) {
for i := 0; i < indent; i++ {
buf.WriteByte(blank)
}
}
func indent(buf []byte) []byte {
splitted := bytes.Split(buf, []byte{newline})
newBuf := make([]byte, len(splitted))
for i, s := range splitted {
s = append(s, 0)
copy(s[1:], s[0:])
s[0] = tab
newBuf = append(newBuf, s...)
if i != len(splitted)-1 {
newBuf = append(newBuf, newline)
}
}
return newBuf
}

View File

@ -1,7 +1,6 @@
package printer package printer
import ( import (
"fmt"
"io" "io"
"text/tabwriter" "text/tabwriter"
@ -15,24 +14,12 @@ type printer struct {
} }
func (p *printer) output() []byte { func (p *printer) output() []byte {
fmt.Println("STARTING OUTPUT")
return p.printNode(p.node) return p.printNode(p.node)
} }
// A Mode value is a set of flags (or 0). They control printing.
type Mode uint
const (
RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
TabIndent // use tabs for indentation independent of UseSpaces
UseSpaces // use spaces instead of tabs for alignment
)
// A Config node controls the output of Fprint. // A Config node controls the output of Fprint.
type Config struct { type Config struct {
Mode Mode // default: 0 SpaceWidth int // if set, it will use spaces instead of tabs for alignment
Tabwidth int // default: 4
Indent int // default: 0 (all code is indented at least by this much)
} }
func (c *Config) fprint(output io.Writer, node ast.Node) error { func (c *Config) fprint(output io.Writer, node ast.Node) error {
@ -41,32 +28,6 @@ func (c *Config) fprint(output io.Writer, node ast.Node) error {
node: node, node: node,
} }
// TODO(arslan): implement this
// redirect output through a trimmer to eliminate trailing whitespace
// (Input to a tabwriter must be untrimmed since trailing tabs provide
// formatting information. The tabwriter could provide trimming
// functionality but no tabwriter is used when RawFormat is set.)
// output = &trimmer{output: output}
// redirect output through a tabwriter if necessary
if c.Mode&RawFormat == 0 {
minwidth := c.Tabwidth
padchar := byte('\t')
if c.Mode&UseSpaces != 0 {
padchar = ' '
}
twmode := tabwriter.DiscardEmptyColumns
if c.Mode&TabIndent != 0 {
minwidth = 0
twmode |= tabwriter.TabIndent
}
output = tabwriter.NewWriter(output, minwidth, c.Tabwidth, 1, padchar, twmode)
}
// write printer result via tabwriter/trimmer to output
if _, err := output.Write(p.output()); err != nil { if _, err := output.Write(p.output()); err != nil {
return err return err
} }
@ -87,5 +48,5 @@ func (c *Config) Fprint(output io.Writer, node ast.Node) error {
// Fprint "pretty-prints" an HCL node to output // Fprint "pretty-prints" an HCL node to output
// It calls Config.Fprint with default settings. // It calls Config.Fprint with default settings.
func Fprint(output io.Writer, node ast.Node) error { func Fprint(output io.Writer, node ast.Node) error {
return (&Config{Tabwidth: 4}).Fprint(output, node) return (&Config{}).Fprint(output, node)
} }

View File

@ -1,6 +1,7 @@
package printer package printer
import ( import (
"fmt"
"os" "os"
"testing" "testing"
@ -9,7 +10,7 @@ import (
var listHCL = `foo = ["fatih", "arslan"]` var listHCL = `foo = ["fatih", "arslan"]`
var listHCL2 = `foo = [ var listHCL2 = `foo = [
"fatih", "fatih",
"arslan", "arslan",
]` ]`
@ -58,8 +59,8 @@ output "web_ip" {
` `
func TestPrint(t *testing.T) { func TestPrint(t *testing.T) {
// node, err := parser.Parse([]byte(complexHcl)) node, err := parser.Parse([]byte(complexHcl))
node, err := parser.Parse([]byte(listHCL2)) // node, err := parser.Parse([]byte(listHCL2))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -67,4 +68,6 @@ func TestPrint(t *testing.T) {
if err := Fprint(os.Stdout, node); err != nil { if err := Fprint(os.Stdout, node); err != nil {
t.Error(err) t.Error(err)
} }
fmt.Println("")
} }