Skip to content

Commit

Permalink
Add coloring to ASCII formatter.
Browse files Browse the repository at this point in the history
The jd command supports `-c` option that outputs colors on the terminal.
  • Loading branch information
yudai committed Jan 7, 2017
1 parent 4e533b0 commit 7b1b7ad
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 63 deletions.
162 changes: 106 additions & 56 deletions formatter/ascii.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,46 @@
package formatter

import (
"bytes"
"errors"
"fmt"
"sort"

diff "github.com/yudai/gojsondiff"
)

func NewAsciiFormatter(left interface{}) *AsciiFormatter {

This comment has been minimized.

Copy link
@mattes
func NewAsciiFormatter(left interface{}, config AsciiFormatterConfig) *AsciiFormatter {
return &AsciiFormatter{
left: left,
ShowArrayIndex: false,
left: left,
config: config,
}
}

type AsciiFormatter struct {
left interface{}
left interface{}
config AsciiFormatterConfig
buffer *bytes.Buffer
path []string
size []int
inArray []bool
line *AsciiLine
}

type AsciiFormatterConfig struct {
ShowArrayIndex bool
buffer string
path []string
size []int
inArray []bool
Coloring bool
}

var AsciiFormatterDefaultConfig = AsciiFormatterConfig{}

type AsciiLine struct {
marker string
indent int
buffer *bytes.Buffer
}

func (f *AsciiFormatter) Format(diff diff.Diff) (result string, err error) {
f.buffer = ""
f.buffer = bytes.NewBuffer([]byte{})
f.path = []string{}
f.size = []int{}
f.inArray = []bool{}
Expand All @@ -39,27 +54,23 @@ func (f *AsciiFormatter) Format(diff diff.Diff) (result string, err error) {
f.left)
}

return f.buffer, nil
return f.buffer.String(), nil
}

func (f *AsciiFormatter) formatObject(left map[string]interface{}, df diff.Diff) {
f.printIndent(AsciiSame)
f.println("{")
f.addLineWith(AsciiSame, "{")
f.push("ROOT", len(left), false)
f.processObject(left, df.Deltas())
f.pop()
f.printIndent(AsciiSame)
f.println("}")
f.addLineWith(AsciiSame, "}")
}

func (f *AsciiFormatter) formatArray(left []interface{}, df diff.Diff) {
f.printIndent(AsciiSame)
f.println("[")
f.addLineWith(AsciiSame, "[")
f.push("ROOT", len(left), true)
f.processArray(left, df.Deltas())
f.pop()
f.printIndent(AsciiSame)
f.println("]")
f.addLineWith(AsciiSame, "]")
}

func (f *AsciiFormatter) processArray(array []interface{}, deltas []diff.Delta) error {
Expand Down Expand Up @@ -121,14 +132,17 @@ func (f *AsciiFormatter) processItem(value interface{}, deltas []diff.Delta, pos
}
o := value.(map[string]interface{})

f.printKeyWithIndent(positionStr, AsciiSame)
f.println("{")
f.newLine(AsciiSame)
f.printKey(positionStr)
f.print("{")
f.closeLine()
f.push(positionStr, len(o), false)
f.processObject(o, d.Deltas)
f.pop()
f.printIndent(AsciiSame)
f.newLine(AsciiSame)
f.print("}")
f.printComma()
f.closeLine()

case *diff.Array:
d := matchedDelta.(*diff.Array)
Expand All @@ -140,14 +154,17 @@ func (f *AsciiFormatter) processItem(value interface{}, deltas []diff.Delta, pos
}
a := value.([]interface{})

f.printKeyWithIndent(positionStr, AsciiSame)
f.println("[")
f.newLine(AsciiSame)
f.printKey(positionStr)
f.print("[")
f.closeLine()
f.push(positionStr, len(a), true)
f.processArray(a, d.Deltas)
f.pop()
f.printIndent(AsciiSame)
f.newLine(AsciiSame)
f.print("]")
f.printComma()
f.closeLine()

case *diff.Added:
d := matchedDelta.(*diff.Added)
Expand Down Expand Up @@ -209,6 +226,11 @@ const (
AsciiDeleted = "-"
)

var AsciiStyles = map[string]string{
AsciiAdded: "30;42",
AsciiDeleted: "30;41",
}

func (f *AsciiFormatter) push(name string, size int, array bool) {
f.path = append(f.path, name)
f.size = append(f.size, size)
Expand All @@ -221,59 +243,79 @@ func (f *AsciiFormatter) pop() {
f.inArray = f.inArray[0 : len(f.inArray)-1]
}

func (f *AsciiFormatter) printIndent(marker string) {
f.print(marker)
for n := 0; n < len(f.path); n++ {
f.print(" ")
func (f *AsciiFormatter) addLineWith(marker string, value string) {
f.line = &AsciiLine{
marker: marker,
indent: len(f.path),
buffer: bytes.NewBufferString(value),
}
f.closeLine()
}

func (f *AsciiFormatter) printKeyWithIndent(name string, marker string) {
f.printIndent(marker)
func (f *AsciiFormatter) newLine(marker string) {
f.line = &AsciiLine{
marker: marker,
indent: len(f.path),
buffer: bytes.NewBuffer([]byte{}),
}
}

func (f *AsciiFormatter) closeLine() {
style, ok := AsciiStyles[f.line.marker]
if f.config.Coloring && ok {
f.buffer.WriteString("\x1b[" + style + "m")
}

f.buffer.WriteString(f.line.marker)
for n := 0; n < f.line.indent; n++ {
f.buffer.WriteString(" ")
}
f.buffer.Write(f.line.buffer.Bytes())

if f.config.Coloring && ok {
f.buffer.WriteString("\x1b[0m")
}

f.buffer.WriteRune('\n')
}

func (f *AsciiFormatter) printKey(name string) {
if !f.inArray[len(f.inArray)-1] {
f.printf(`"%s": `, name)
} else if f.ShowArrayIndex {
f.printf(`%s: `, name)
fmt.Fprintf(f.line.buffer, `"%s": `, name)
} else if f.config.ShowArrayIndex {
fmt.Fprintf(f.line.buffer, `%s: `, name)
}
}

func (f *AsciiFormatter) printComma() {
f.size[len(f.size)-1]--
if f.size[len(f.size)-1] > 0 {
f.println(",")
} else {
f.println()
f.line.buffer.WriteRune(',')
}
}

func (f *AsciiFormatter) printValue(value interface{}) {
switch value.(type) {
case string:
f.buffer += fmt.Sprintf(`"%s"`, value)
fmt.Fprintf(f.line.buffer, `"%s"`, value)
case nil:
f.buffer += "null"
f.line.buffer.WriteString("null")
default:
f.buffer += fmt.Sprintf(`%#v`, value)
fmt.Fprintf(f.line.buffer, `%#v`, value)
}
}

func (f *AsciiFormatter) print(a ...interface{}) {
f.buffer += fmt.Sprint(a...)
}

func (f *AsciiFormatter) printf(format string, a ...interface{}) {
f.buffer += fmt.Sprintf(format, a...)
}

func (f *AsciiFormatter) println(a ...interface{}) {
f.buffer += fmt.Sprintln(a...)
func (f *AsciiFormatter) print(a string) {
f.line.buffer.WriteString(a)
}

func (f *AsciiFormatter) printRecursive(name string, value interface{}, marker string) {
switch value.(type) {
case map[string]interface{}:
f.printKeyWithIndent(name, marker)
f.println("{")
f.newLine(marker)
f.printKey(name)
f.print("{")
f.closeLine()

m := value.(map[string]interface{})
size := len(m)
Expand All @@ -285,12 +327,16 @@ func (f *AsciiFormatter) printRecursive(name string, value interface{}, marker s
}
f.pop()

f.printIndent(marker)
f.newLine(marker)
f.print("}")
f.printComma()
f.closeLine()

case []interface{}:
f.printKeyWithIndent(name, marker)
f.println("[")
f.newLine(marker)
f.printKey(name)
f.print("[")
f.closeLine()

s := value.([]interface{})
size := len(s)
Expand All @@ -300,13 +346,17 @@ func (f *AsciiFormatter) printRecursive(name string, value interface{}, marker s
}
f.pop()

f.printIndent(marker)
f.newLine(marker)
f.print("]")
f.printComma()
f.closeLine()

default:
f.printKeyWithIndent(name, marker)
f.newLine(marker)
f.printKey(name)
f.printValue(value)
f.printComma()
f.closeLine()
}
}

Expand Down
4 changes: 2 additions & 2 deletions formatter/ascii_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var _ = Describe("Ascii", func() {
diff := diff.New().CompareObjects(a, b)
Expect(diff.Modified()).To(BeTrue())

f := NewAsciiFormatter(a)
f := NewAsciiFormatter(a, AsciiFormatterDefaultConfig)
deltaJson, err := f.Format(diff)
Expect(err).To(BeNil())
Expect(deltaJson).To(Equal(
Expand Down Expand Up @@ -79,7 +79,7 @@ var _ = Describe("Ascii", func() {
diff := diff.New().CompareObjects(a, b)
Expect(diff.Modified()).To(BeTrue())

f := NewAsciiFormatter(a)
f := NewAsciiFormatter(a, AsciiFormatterDefaultConfig)
deltaJson, err := f.Format(diff)
Expect(err).To(BeNil())
Expect(deltaJson).To(Equal(
Expand Down
2 changes: 1 addition & 1 deletion formatter/delta.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (f *DeltaFormatter) Format(diff diff.Diff) (result string, err error) {
return "", err
}

return string(resultBytes), nil
return string(resultBytes) + "\n", nil
}

func (f *DeltaFormatter) FormatAsJson(diff diff.Diff) (json map[string]interface{}, err error) {
Expand Down
17 changes: 13 additions & 4 deletions jd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func main() {
Usage: "Diff Outpu Format (ascii, delta)",
EnvVar: "DIFF_FORMAT",
},
cli.BoolFlag{
Name: "coloring, c",
Usage: "Enable coloring in the ASCII mode (not available in the delta mode)",
EnvVar: "COLORING",
},
}

app.Action = func(c *cli.Context) {
Expand Down Expand Up @@ -65,13 +70,17 @@ func main() {
if format == "ascii" {
var aJson map[string]interface{}
json.Unmarshal(aString, &aJson)
formatter := formatter.NewAsciiFormatter(aJson)
formatter.ShowArrayIndex = true

config := formatter.AsciiFormatterConfig{
ShowArrayIndex: true,
Coloring: c.Bool("coloring"),
}

formatter := formatter.NewAsciiFormatter(aJson, config)
diffString, err = formatter.Format(d)
if err != nil {
// No error can occur
}
fmt.Print(diffString)
} else if format == "delta" {
formatter := formatter.NewDeltaFormatter()
diffString, err = formatter.Format(d)
Expand All @@ -83,7 +92,7 @@ func main() {
os.Exit(4)
}

fmt.Println(diffString)
fmt.Print(diffString)
}

app.Run(os.Args)
Expand Down

0 comments on commit 7b1b7ad

Please sign in to comment.