package common import ( "fmt" "io" "os" "strings" ) // Style is a specific style type Style int // Styles const ( StyleDoubleLine = iota StyleSingleLine StyleDashedLine StyleNoLine ) // NewPen creates a new pen func NewPen(style Style, color int) *Pen { bgcolor := 49 if os.Getenv("CLICOLOR") == "0" { color = 0 bgcolor = 0 } return &Pen{ style: style, color: color, bgcolor: bgcolor, } } type styleDef struct { cornerTL string cornerTR string cornerBL string cornerBR string lineH string lineV string } var styleDefs = []styleDef{ {"\u2554", "\u2557", "\u255a", "\u255d", "\u2550", "\u2551"}, {"\u256d", "\u256e", "\u2570", "\u256f", "\u2500", "\u2502"}, {"\u250c", "\u2510", "\u2514", "\u2518", "\u254c", "\u254e"}, {" ", " ", " ", " ", " ", " "}, } // Pen struct type Pen struct { style Style color int bgcolor int } // Drawing struct type Drawing struct { buf *strings.Builder width int } func (p *Pen) drawTopBars(buf io.Writer, labels ...string) { style := styleDefs[p.style] for _, label := range labels { bar := strings.Repeat(style.lineH, len(label)+2) fmt.Fprintf(buf, " ") fmt.Fprintf(buf, "\x1b[%d;%dm", p.color, p.bgcolor) fmt.Fprintf(buf, "%s%s%s", style.cornerTL, bar, style.cornerTR) fmt.Fprintf(buf, "\x1b[%dm", 0) } fmt.Fprintf(buf, "\n") } func (p *Pen) drawBottomBars(buf io.Writer, labels ...string) { style := styleDefs[p.style] for _, label := range labels { bar := strings.Repeat(style.lineH, len(label)+2) fmt.Fprintf(buf, " ") fmt.Fprintf(buf, "\x1b[%d;%dm", p.color, p.bgcolor) fmt.Fprintf(buf, "%s%s%s", style.cornerBL, bar, style.cornerBR) fmt.Fprintf(buf, "\x1b[%dm", 0) } fmt.Fprintf(buf, "\n") } func (p *Pen) drawLabels(buf io.Writer, labels ...string) { style := styleDefs[p.style] for _, label := range labels { fmt.Fprintf(buf, " ") fmt.Fprintf(buf, "\x1b[%d;%dm", p.color, p.bgcolor) fmt.Fprintf(buf, "%s %s %s", style.lineV, label, style.lineV) fmt.Fprintf(buf, "\x1b[%dm", 0) } fmt.Fprintf(buf, "\n") } // DrawArrow between boxes func (p *Pen) DrawArrow() *Drawing { drawing := &Drawing{ buf: new(strings.Builder), width: 1, } fmt.Fprintf(drawing.buf, "\x1b[%dm", p.color) fmt.Fprintf(drawing.buf, "\u2b07") fmt.Fprintf(drawing.buf, "\x1b[%dm", 0) return drawing } // DrawBoxes to draw boxes func (p *Pen) DrawBoxes(labels ...string) *Drawing { width := 0 for _, l := range labels { width += len(l) + 2 + 2 + 1 } drawing := &Drawing{ buf: new(strings.Builder), width: width, } p.drawTopBars(drawing.buf, labels...) p.drawLabels(drawing.buf, labels...) p.drawBottomBars(drawing.buf, labels...) return drawing } // Draw to writer func (d *Drawing) Draw(writer io.Writer, centerOnWidth int) { padSize := (centerOnWidth - d.GetWidth()) / 2 if padSize < 0 { padSize = 0 } for _, l := range strings.Split(d.buf.String(), "\n") { if len(l) > 0 { padding := strings.Repeat(" ", padSize) fmt.Fprintf(writer, "%s%s\n", padding, l) } } } // GetWidth of drawing func (d *Drawing) GetWidth() int { return d.width }