diff options
Diffstat (limited to 'vendor/github.com/buger/goterm')
-rw-r--r-- | vendor/github.com/buger/goterm/README.md | 119 | ||||
-rw-r--r-- | vendor/github.com/buger/goterm/box.go | 122 | ||||
-rw-r--r-- | vendor/github.com/buger/goterm/plot.go | 328 | ||||
-rw-r--r-- | vendor/github.com/buger/goterm/table.go | 34 | ||||
-rw-r--r-- | vendor/github.com/buger/goterm/terminal.go | 258 | ||||
-rw-r--r-- | vendor/github.com/buger/goterm/terminal_nosysioctl.go | 12 | ||||
-rw-r--r-- | vendor/github.com/buger/goterm/terminal_sysioctl.go | 36 |
7 files changed, 909 insertions, 0 deletions
diff --git a/vendor/github.com/buger/goterm/README.md b/vendor/github.com/buger/goterm/README.md new file mode 100644 index 000000000..536b7b885 --- /dev/null +++ b/vendor/github.com/buger/goterm/README.md @@ -0,0 +1,119 @@ +## Description + +This library provides basic building blocks for building advanced console UIs. + +Initially created for [Gor](http://github.com/buger/gor). + +Full API documentation: http://godoc.org/github.com/buger/goterm + +## Basic usage + +Full screen console app, printing current time: + +```go +import ( + tm "github.com/buger/goterm" + "time" +) + +func main() { + tm.Clear() // Clear current screen + + for { + // By moving cursor to top-left position we ensure that console output + // will be overwritten each time, instead of adding new. + tm.MoveCursor(1,1) + + tm.Println("Current Time:", time.Now().Format(time.RFC1123)) + + tm.Flush() // Call it every time at the end of rendering + + time.Sleep(time.Second) + } +} +``` + +This can be seen in [examples/time_example.go](examples/time_example.go). To +run it yourself, go into your `$GOPATH/src/github.com/buger/goterm` directory +and run `go run ./examples/time_example.go` + + +Print red bold message on white background: + +```go +tm.Println(tm.Background(tm.Color(tm.Bold("Important header"), tm.RED), tm.WHITE)) +``` + + +Create box and move it to center of the screen: + +```go +tm.Clear() + +// Create Box with 30% width of current screen, and height of 20 lines +box := tm.NewBox(30|tm.PCT, 20, 0) + +// Add some content to the box +// Note that you can add ANY content, even tables +fmt.Fprint(box, "Some box content") + +// Move Box to approx center of the screen +tm.Print(tm.MoveTo(box.String(), 40|tm.PCT, 40|tm.PCT)) + +tm.Flush() +``` + +This can be found in [examples/box_example.go](examples/box_example.go). + +Draw table: + +```go +// Based on http://golang.org/pkg/text/tabwriter +totals := tm.NewTable(0, 10, 5, ' ', 0) +fmt.Fprintf(totals, "Time\tStarted\tActive\tFinished\n") +fmt.Fprintf(totals, "%s\t%d\t%d\t%d\n", "All", started, started-finished, finished) +tm.Println(totals) +tm.Flush() +``` + +This can be found in [examples/table_example.go](examples/table_example.go). + +## Line charts + +Chart example: + +![screen shot 2013-07-09 at 5 05 37 pm](https://f.cloud.github.com/assets/14009/767676/e3dd35aa-e887-11e2-9cd2-f6451eb26adc.png) + + +```go + import ( + tm "github.com/buger/goterm" + ) + + chart := tm.NewLineChart(100, 20) + + data := new(tm.DataTable) + data.addColumn("Time") + data.addColumn("Sin(x)") + data.addColumn("Cos(x+1)") + + for i := 0.1; i < 10; i += 0.1 { + data.addRow(i, math.Sin(i), math.Cos(i+1)) + } + + tm.Println(chart.Draw(data)) +``` + +This can be found in [examples/chart_example.go](examples/chart_example.go). + +Drawing 2 separate graphs in different scales. Each graph have its own Y axe. + +```go +chart.Flags = tm.DRAW_INDEPENDENT +``` + +Drawing graph with relative scale (Grapwh draw starting from min value instead of zero) + +```go +chart.Flags = tm.DRAW_RELATIVE +``` diff --git a/vendor/github.com/buger/goterm/box.go b/vendor/github.com/buger/goterm/box.go new file mode 100644 index 000000000..7df929d7d --- /dev/null +++ b/vendor/github.com/buger/goterm/box.go @@ -0,0 +1,122 @@ +package goterm + +import ( + "bytes" + "strings" +) + +const DEFAULT_BORDER = "- │ ┌ ┐ └ ┘" + +// Box allows you to create independent parts of screen, with its own buffer and borders. +// Can be used for creating modal windows +// +// Generates boxes likes this: +// ┌--------┐ +// │hello │ +// │world │ +// │ │ +// └--------┘ +// +type Box struct { + Buf *bytes.Buffer + + Width int + Height int + + // To get even padding: PaddingX ~= PaddingY*4 + PaddingX int + PaddingY int + + // Should contain 6 border pieces separated by spaces + // + // Example border: + // "- │ ┌ ┐ └ ┘" + Border string + + Flags int // Not used now +} + +// Create new Box. +// Width and height can be relative: +// +// // Create box with 50% with of current screen and 10 lines height +// box := tm.NewBox(50|tm.PCT, 10, 0) +// +func NewBox(width, height int, flags int) *Box { + width, height = GetXY(width, height) + + box := new(Box) + box.Buf = new(bytes.Buffer) + box.Width = width + box.Height = height + box.Border = DEFAULT_BORDER + box.PaddingX = 1 + box.PaddingY = 0 + box.Flags = flags + + return box +} + +func (b *Box) Write(p []byte) (int, error) { + return b.Buf.Write(p) +} + +// Render Box +func (b *Box) String() (out string) { + borders := strings.Split(b.Border, " ") + lines := strings.Split(b.Buf.String(), "\n") + + // Border + padding + prefix := borders[1] + strings.Repeat(" ", b.PaddingX) + suffix := strings.Repeat(" ", b.PaddingX) + borders[1] + + offset := b.PaddingY + 1 // 1 is border width + + // Content width without borders and padding + contentWidth := b.Width - (b.PaddingX+1)*2 + + for y := 0; y < b.Height; y++ { + var line string + + switch { + // Draw borders for first line + case y == 0: + line = borders[2] + strings.Repeat(borders[0], b.Width-2) + borders[3] + + // Draw borders for last line + case y == (b.Height - 1): + line = borders[4] + strings.Repeat(borders[0], b.Width-2) + borders[5] + + // Draw top and bottom padding + case y <= b.PaddingY || y >= (b.Height-b.PaddingY): + line = borders[1] + strings.Repeat(" ", b.Width-2) + borders[1] + + // Render content + default: + if len(lines) > y-offset { + line = lines[y-offset] + } else { + line = "" + } + + if len(line) > contentWidth-1 { + // If line is too large limit it + line = line[0:contentWidth] + } else { + // If line is too small enlarge it by adding spaces + line = line + strings.Repeat(" ", contentWidth-len(line)) + } + + line = prefix + line + suffix + } + + // Don't add newline for last element + if y != b.Height-1 { + line = line + "\n" + } + + out += line + } + + return out +} diff --git a/vendor/github.com/buger/goterm/plot.go b/vendor/github.com/buger/goterm/plot.go new file mode 100644 index 000000000..77b9fb097 --- /dev/null +++ b/vendor/github.com/buger/goterm/plot.go @@ -0,0 +1,328 @@ +package goterm + +import ( + "fmt" + "math" + "strings" +) + +const ( + AXIS_LEFT = iota + AXIS_RIGHT +) + +const ( + DRAW_INDEPENDENT = 1 << iota + DRAW_RELATIVE +) + +type DataTable struct { + columns []string + + rows [][]float64 +} + +func (d *DataTable) AddColumn(name string) { + d.columns = append(d.columns, name) +} + +func (d *DataTable) AddRow(elms ...float64) { + d.rows = append(d.rows, elms) +} + +type Chart interface { + Draw(data DataTable, flags int) string +} + +type LineChart struct { + Buf []string + chartBuf []string + + data *DataTable + + Width int + Height int + + chartHeight int + chartWidth int + + paddingX int + + paddingY int + + Flags int +} + +func genBuf(size int) []string { + buf := make([]string, size) + + for i := 0; i < size; i++ { + buf[i] = " " + } + + return buf +} + +// Format float +func ff(num interface{}) string { + return fmt.Sprintf("%.1f", num) +} + +func NewLineChart(width, height int) *LineChart { + chart := new(LineChart) + chart.Width = width + chart.Height = height + chart.Buf = genBuf(width * height) + + // axis lines + axies text + chart.paddingY = 2 + + return chart +} + +func (c *LineChart) DrawAxes(maxX, minX, maxY, minY float64, index int) { + side := AXIS_LEFT + + if c.Flags&DRAW_INDEPENDENT != 0 { + if index%2 == 0 { + side = AXIS_RIGHT + } + + c.DrawLine(c.paddingX-1, 1, c.Width-c.paddingX, 1, "-") + } else { + c.DrawLine(c.paddingX-1, 1, c.Width-1, 1, "-") + } + + if side == AXIS_LEFT { + c.DrawLine(c.paddingX-1, 1, c.paddingX-1, c.Height-1, "│") + } else { + c.DrawLine(c.Width-c.paddingX, 1, c.Width-c.paddingX, c.Height-1, "│") + } + + left := 0 + if side == AXIS_RIGHT { + left = c.Width - c.paddingX + 1 + } + + if c.Flags&DRAW_RELATIVE != 0 { + c.writeText(ff(minY), left, 1) + } else { + if minY > 0 { + c.writeText("0", left, 1) + } else { + c.writeText(ff(minY), left, 1) + } + } + + c.writeText(ff(maxY), left, c.Height-1) + + c.writeText(ff(minX), c.paddingX, 0) + + x_col := c.data.columns[0] + c.writeText(c.data.columns[0], c.Width/2-len(x_col)/2, 1) + + if c.Flags&DRAW_INDEPENDENT != 0 || len(c.data.columns) < 3 { + col := c.data.columns[index] + + for idx, char := range strings.Split(col, "") { + start_from := c.Height/2 + len(col)/2 - idx + + if side == AXIS_LEFT { + c.writeText(char, c.paddingX-1, start_from) + } else { + c.writeText(char, c.Width-c.paddingX, start_from) + } + } + } + + if c.Flags&DRAW_INDEPENDENT != 0 { + c.writeText(ff(maxX), c.Width-c.paddingX-len(ff(maxX)), 0) + } else { + c.writeText(ff(maxX), c.Width-len(ff(maxX)), 0) + } +} + +func (c *LineChart) writeText(text string, x, y int) { + coord := y*c.Width + x + + for idx, char := range strings.Split(text, "") { + c.Buf[coord+idx] = char + } +} + +func (c *LineChart) Draw(data *DataTable) (out string) { + var scaleY, scaleX float64 + + c.data = data + + if c.Flags&DRAW_INDEPENDENT != 0 && len(data.columns) > 3 { + fmt.Println("Error: Can't use DRAW_INDEPENDENT for more then 2 graphs") + return "" + } + + charts := len(data.columns) - 1 + + prevPoint := [2]int{-1, -1} + + maxX, minX, maxY, minY := getBoundaryValues(data, -1) + + c.paddingX = int(math.Max(float64(len(ff(minY))), float64(len(ff(maxY))))) + 1 + + c.chartHeight = c.Height - c.paddingY + + if c.Flags&DRAW_INDEPENDENT != 0 { + c.chartWidth = c.Width - 2*c.paddingX + } else { + c.chartWidth = c.Width - c.paddingX - 1 + } + + scaleX = float64(c.chartWidth) / (maxX - minX) + + if c.Flags&DRAW_RELATIVE != 0 || minY < 0 { + scaleY = float64(c.chartHeight) / (maxY - minY) + } else { + scaleY = float64(c.chartHeight) / maxY + } + + for i := 1; i < charts+1; i++ { + if c.Flags&DRAW_INDEPENDENT != 0 { + maxX, minX, maxY, minY = getBoundaryValues(data, i) + + scaleX = float64(c.chartWidth-1) / (maxX - minX) + scaleY = float64(c.chartHeight) / maxY + + if c.Flags&DRAW_RELATIVE != 0 || minY < 0 { + scaleY = float64(c.chartHeight) / (maxY - minY) + } + } + + symbol := Color("•", i) + + c_data := getChartData(data, i) + + for _, point := range c_data { + x := int((point[0]-minX)*scaleX) + c.paddingX + y := int((point[1])*scaleY) + c.paddingY + + if c.Flags&DRAW_RELATIVE != 0 || minY < 0 { + y = int((point[1]-minY)*scaleY) + c.paddingY + } + + if prevPoint[0] == -1 { + prevPoint[0] = x + prevPoint[1] = y + } + + if prevPoint[0] <= x { + c.DrawLine(prevPoint[0], prevPoint[1], x, y, symbol) + } + + prevPoint[0] = x + prevPoint[1] = y + } + + c.DrawAxes(maxX, minX, maxY, minY, i) + } + + for row := c.Height - 1; row >= 0; row-- { + out += strings.Join(c.Buf[row*c.Width:(row+1)*c.Width], "") + "\n" + } + + return +} + +func (c *LineChart) DrawLine(x0, y0, x1, y1 int, symbol string) { + drawLine(x0, y0, x1, y1, func(x, y int) { + coord := y*c.Width + x + + if coord > 0 && coord < len(c.Buf) { + c.Buf[coord] = symbol + } + }) +} + +func getBoundaryValues(data *DataTable, index int) (maxX, minX, maxY, minY float64) { + maxX = data.rows[0][0] + minX = data.rows[0][0] + maxY = data.rows[0][1] + minY = data.rows[0][1] + + for _, r := range data.rows { + maxX = math.Max(maxX, r[0]) + minX = math.Min(minX, r[0]) + + for idx, c := range r { + if idx > 0 { + if index == -1 || index == idx { + maxY = math.Max(maxY, c) + minY = math.Min(minY, c) + } + } + } + } + + if maxY > 0 { + maxY = maxY * 1.1 + } else { + maxY = maxY * 0.9 + } + + if minY > 0 { + minY = minY * 0.9 + } else { + minY = minY * 1.1 + } + + return +} + +// DataTable can contain data for multiple graphs, we need to extract only 1 +func getChartData(data *DataTable, index int) (out [][]float64) { + for _, r := range data.rows { + out = append(out, []float64{r[0], r[index]}) + } + + return +} + +// Algorithm for drawing line between two points +// +// http://en.wikipedia.org/wiki/Bresenham's_line_algorithm +func drawLine(x0, y0, x1, y1 int, plot func(int, int)) { + dx := x1 - x0 + if dx < 0 { + dx = -dx + } + dy := y1 - y0 + if dy < 0 { + dy = -dy + } + var sx, sy int + if x0 < x1 { + sx = 1 + } else { + sx = -1 + } + if y0 < y1 { + sy = 1 + } else { + sy = -1 + } + err := dx - dy + + for { + plot(x0, y0) + if x0 == x1 && y0 == y1 { + break + } + e2 := 2 * err + if e2 > -dy { + err -= dy + x0 += sx + } + if e2 < dx { + err += dx + y0 += sy + } + } +} diff --git a/vendor/github.com/buger/goterm/table.go b/vendor/github.com/buger/goterm/table.go new file mode 100644 index 000000000..d8dae55ce --- /dev/null +++ b/vendor/github.com/buger/goterm/table.go @@ -0,0 +1,34 @@ +package goterm + +import ( + "bytes" + "text/tabwriter" +) + +// Tabwriter with own buffer: +// +// totals := tm.NewTable(0, 10, 5, ' ', 0) +// fmt.Fprintf(totals, "Time\tStarted\tActive\tFinished\n") +// fmt.Fprintf(totals, "%s\t%d\t%d\t%d\n", "All", started, started-finished, finished) +// tm.Println(totals) +// +// Based on http://golang.org/pkg/text/tabwriter +type Table struct { + tabwriter.Writer + + Buf *bytes.Buffer +} + +// Same as here http://golang.org/pkg/text/tabwriter/#Writer.Init +func NewTable(minwidth, tabwidth, padding int, padchar byte, flags uint) *Table { + tbl := new(Table) + tbl.Buf = new(bytes.Buffer) + tbl.Init(tbl.Buf, minwidth, tabwidth, padding, padchar, flags) + + return tbl +} + +func (t *Table) String() string { + t.Flush() + return t.Buf.String() +} diff --git a/vendor/github.com/buger/goterm/terminal.go b/vendor/github.com/buger/goterm/terminal.go new file mode 100644 index 000000000..6b45c78bc --- /dev/null +++ b/vendor/github.com/buger/goterm/terminal.go @@ -0,0 +1,258 @@ +// Provides basic bulding blocks for advanced console UI +// +// Coordinate system: +// +// 1/1---X----> +// | +// Y +// | +// v +// +// Documentation for ANSI codes: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors +// +// Inspired by: http://www.darkcoding.net/software/pretty-command-line-console-output-on-unix-in-python-and-go-lang/ +package goterm + +import ( + "bufio" + "bytes" + "fmt" + "os" + "strings" +) + +// Reset all custom styles +const RESET = "\033[0m" + +// Reset to default color +const RESET_COLOR = "\033[32m" + +// Return curor to start of line and clean it +const RESET_LINE = "\r\033[K" + +// List of possible colors +const ( + BLACK = iota + RED + GREEN + YELLOW + BLUE + MAGENTA + CYAN + WHITE +) + +var Output *bufio.Writer = bufio.NewWriter(os.Stdout) + +func getColor(code int) string { + return fmt.Sprintf("\033[3%dm", code) +} + +func getBgColor(code int) string { + return fmt.Sprintf("\033[4%dm", code) +} + +// Set percent flag: num | PCT +// +// Check percent flag: num & PCT +// +// Reset percent flag: num & 0xFF +const shift = uint(^uint(0)>>63) << 4 +const PCT = 0x8000 << shift + +type winsize struct { + Row uint16 + Col uint16 + Xpixel uint16 + Ypixel uint16 +} + +// Global screen buffer +// Its not recommented write to buffer dirrectly, use package Print,Printf,Println fucntions instead. +var Screen *bytes.Buffer = new(bytes.Buffer) + +// Get relative or absolute coorditantes +// To get relative, set PCT flag to number: +// +// // Get 10% of total width to `x` and 20 to y +// x, y = tm.GetXY(10|tm.PCT, 20) +// +func GetXY(x int, y int) (int, int) { + if y == -1 { + y = CurrentHeight() + 1 + } + + if x&PCT != 0 { + x = int((x & 0xFF) * Width() / 100) + } + + if y&PCT != 0 { + y = int((y & 0xFF) * Height() / 100) + } + + return x, y +} + +type sf func(int, string) string + +// Apply given transformation func for each line in string +func applyTransform(str string, transform sf) (out string) { + out = "" + + for idx, line := range strings.Split(str, "\n") { + out += transform(idx, line) + } + + return +} + +// Clear screen +func Clear() { + Output.WriteString("\033[2J") +} + +// Move cursor to given position +func MoveCursor(x int, y int) { + fmt.Fprintf(Screen, "\033[%d;%dH", x, y) +} + +// Move cursor up relative the current position +func MoveCursorUp(bias int) { + fmt.Fprintf(Screen, "\033[%dA", bias) +} + +// Move cursor down relative the current position +func MoveCursorDown(bias int) { + fmt.Fprintf(Screen, "\033[%dB", bias) +} + +// Move cursor forward relative the current position +func MoveCursorForward(bias int) { + fmt.Fprintf(Screen, "\033[%dC", bias) +} + +// Move cursor backward relative the current position +func MoveCursorBackward(bias int) { + fmt.Fprintf(Screen, "\033[%dD", bias) +} + +// Move string to possition +func MoveTo(str string, x int, y int) (out string) { + x, y = GetXY(x, y) + + return applyTransform(str, func(idx int, line string) string { + return fmt.Sprintf("\033[%d;%dH%s", y+idx, x, line) + }) +} + +// Return carrier to start of line +func ResetLine(str string) (out string) { + return applyTransform(str, func(idx int, line string) string { + return fmt.Sprintf(RESET_LINE, line) + }) +} + +// Make bold +func Bold(str string) string { + return applyTransform(str, func(idx int, line string) string { + return fmt.Sprintf("\033[1m%s\033[0m", line) + }) +} + +// Apply given color to string: +// +// tm.Color("RED STRING", tm.RED) +// +func Color(str string, color int) string { + return applyTransform(str, func(idx int, line string) string { + return fmt.Sprintf("%s%s%s", getColor(color), line, RESET) + }) +} + +func Highlight(str, substr string, color int) string { + hiSubstr := Color(substr, color) + return strings.Replace(str, substr, hiSubstr, -1) +} + +func HighlightRegion(str string, from, to, color int) string { + return str[:from] + Color(str[from:to], color) + str[to:] +} + +// Change background color of string: +// +// tm.Background("string", tm.RED) +// +func Background(str string, color int) string { + return applyTransform(str, func(idx int, line string) string { + return fmt.Sprintf("%s%s%s", getBgColor(color), line, RESET) + }) +} + +// Get console width +func Width() int { + ws, err := getWinsize() + + if err != nil { + return -1 + } + + return int(ws.Col) +} + +// Get console height +func Height() int { + ws, err := getWinsize() + if err != nil { + return -1 + } + return int(ws.Row) +} + +// Get current height. Line count in Screen buffer. +func CurrentHeight() int { + return strings.Count(Screen.String(), "\n") +} + +// Flush buffer and ensure that it will not overflow screen +func Flush() { + for idx, str := range strings.Split(Screen.String(), "\n") { + if idx > Height() { + return + } + + Output.WriteString(str + "\n") + } + + Output.Flush() + Screen.Reset() +} + +func Print(a ...interface{}) (n int, err error) { + return fmt.Fprint(Screen, a...) +} + +func Println(a ...interface{}) (n int, err error) { + return fmt.Fprintln(Screen, a...) +} + +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(Screen, format, a...) +} + +func Context(data string, idx, max int) string { + var start, end int + + if len(data[:idx]) < (max / 2) { + start = 0 + } else { + start = idx - max/2 + } + + if len(data)-idx < (max / 2) { + end = len(data) - 1 + } else { + end = idx + max/2 + } + + return data[start:end] +} diff --git a/vendor/github.com/buger/goterm/terminal_nosysioctl.go b/vendor/github.com/buger/goterm/terminal_nosysioctl.go new file mode 100644 index 000000000..690615008 --- /dev/null +++ b/vendor/github.com/buger/goterm/terminal_nosysioctl.go @@ -0,0 +1,12 @@ +// +build windows plan9 solaris + +package goterm + +func getWinsize() (*winsize, error) { + ws := new(winsize) + + ws.Col = 80 + ws.Row = 24 + + return ws, nil +} diff --git a/vendor/github.com/buger/goterm/terminal_sysioctl.go b/vendor/github.com/buger/goterm/terminal_sysioctl.go new file mode 100644 index 000000000..e98430fb9 --- /dev/null +++ b/vendor/github.com/buger/goterm/terminal_sysioctl.go @@ -0,0 +1,36 @@ +// +build !windows,!plan9,!solaris + +package goterm + +import ( + "fmt" + "os" + "runtime" + "syscall" + "unsafe" +) + +func getWinsize() (*winsize, error) { + ws := new(winsize) + + var _TIOCGWINSZ int64 + + switch runtime.GOOS { + case "linux": + _TIOCGWINSZ = 0x5413 + case "darwin": + _TIOCGWINSZ = 1074295912 + } + + r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL, + uintptr(syscall.Stdin), + uintptr(_TIOCGWINSZ), + uintptr(unsafe.Pointer(ws)), + ) + + if int(r1) == -1 { + fmt.Println("Error:", os.NewSyscallError("GetWinsize", errno)) + return nil, os.NewSyscallError("GetWinsize", errno) + } + return ws, nil +} |