remove github.com/buger/goterm dependency
this is just a few bytes of escape codes, there is no need to depend on a library for it. While it is not a big one it still seems better to just write it ourselves. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
parent
4264bf6876
commit
8ef234aedd
|
|
@ -0,0 +1,19 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClearScreen clears the screen and puts the cursor back to position 1,1
|
||||||
|
// Useful when printing output in an interval like podman stats.
|
||||||
|
// When the stdout is not a terminal this is a NOP.
|
||||||
|
func ClearScreen() {
|
||||||
|
// Only write escape sequences when the output is a terminal.
|
||||||
|
if term.IsTerminal(int(os.Stdout.Fd())) {
|
||||||
|
// terminal escape control sequence to clear screen ([2J)
|
||||||
|
// followed by putting the cursor to position 1,1 ([1;1H)
|
||||||
|
os.Stdout.WriteString("\033[2J\033[1;1H")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
tm "github.com/buger/goterm"
|
|
||||||
"github.com/containers/common/libnetwork/types"
|
"github.com/containers/common/libnetwork/types"
|
||||||
"github.com/containers/common/pkg/completion"
|
"github.com/containers/common/pkg/completion"
|
||||||
"github.com/containers/common/pkg/report"
|
"github.com/containers/common/pkg/report"
|
||||||
|
|
@ -267,9 +266,7 @@ func ps(cmd *cobra.Command, _ []string) error {
|
||||||
responses = append(responses, psReporter{r})
|
responses = append(responses, psReporter{r})
|
||||||
}
|
}
|
||||||
|
|
||||||
tm.Clear()
|
common.ClearScreen()
|
||||||
tm.MoveCursor(1, 1)
|
|
||||||
tm.Flush()
|
|
||||||
|
|
||||||
if err := headers(); err != nil {
|
if err := headers(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
tm "github.com/buger/goterm"
|
|
||||||
"github.com/containers/common/pkg/completion"
|
"github.com/containers/common/pkg/completion"
|
||||||
"github.com/containers/common/pkg/report"
|
"github.com/containers/common/pkg/report"
|
||||||
"github.com/containers/podman/v5/cmd/podman/common"
|
"github.com/containers/podman/v5/cmd/podman/common"
|
||||||
|
|
@ -152,9 +151,7 @@ func outputStats(cmd *cobra.Command, reports []define.ContainerStats) error {
|
||||||
"PIDS": "PIDS",
|
"PIDS": "PIDS",
|
||||||
})
|
})
|
||||||
if !statsOptions.NoReset {
|
if !statsOptions.NoReset {
|
||||||
tm.Clear()
|
common.ClearScreen()
|
||||||
tm.MoveCursor(1, 1)
|
|
||||||
tm.Flush()
|
|
||||||
}
|
}
|
||||||
stats := make([]containerStats, 0, len(reports))
|
stats := make([]containerStats, 0, len(reports))
|
||||||
for _, r := range reports {
|
for _, r := range reports {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/buger/goterm"
|
|
||||||
"github.com/containers/common/pkg/report"
|
"github.com/containers/common/pkg/report"
|
||||||
"github.com/containers/podman/v5/cmd/podman/common"
|
"github.com/containers/podman/v5/cmd/podman/common"
|
||||||
"github.com/containers/podman/v5/cmd/podman/registry"
|
"github.com/containers/podman/v5/cmd/podman/registry"
|
||||||
|
|
@ -93,9 +92,7 @@ func stats(cmd *cobra.Command, args []string) error {
|
||||||
err = printJSONPodStats(reports)
|
err = printJSONPodStats(reports)
|
||||||
} else {
|
} else {
|
||||||
if !statsOptions.NoReset {
|
if !statsOptions.NoReset {
|
||||||
goterm.Clear()
|
common.ClearScreen()
|
||||||
goterm.MoveCursor(1, 1)
|
|
||||||
goterm.Flush()
|
|
||||||
}
|
}
|
||||||
if report.OriginUser == rpt.Origin {
|
if report.OriginUser == rpt.Origin {
|
||||||
err = userTemplate(rpt, reports)
|
err = userTemplate(rpt, reports)
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -9,7 +9,6 @@ require (
|
||||||
github.com/BurntSushi/toml v1.5.0
|
github.com/BurntSushi/toml v1.5.0
|
||||||
github.com/Microsoft/go-winio v0.6.2
|
github.com/Microsoft/go-winio v0.6.2
|
||||||
github.com/blang/semver/v4 v4.0.0
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/buger/goterm v1.0.4
|
|
||||||
github.com/checkpoint-restore/checkpointctl v1.3.0
|
github.com/checkpoint-restore/checkpointctl v1.3.0
|
||||||
github.com/checkpoint-restore/go-criu/v7 v7.2.0
|
github.com/checkpoint-restore/go-criu/v7 v7.2.0
|
||||||
github.com/containernetworking/plugins v1.6.2
|
github.com/containernetworking/plugins v1.6.2
|
||||||
|
|
|
||||||
3
go.sum
3
go.sum
|
|
@ -28,8 +28,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
|
|
||||||
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
|
@ -605,7 +603,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
*.swp
|
|
||||||
.idea
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2016 Leonid Bugaev
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
## 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:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
package goterm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
_ "unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ANSI_RE = regexp.MustCompile(`\\0\d+\[\d+(?:;\d+)?m`)
|
|
||||||
|
|
||||||
// String renders 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 = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
r := []rune(line)
|
|
||||||
|
|
||||||
lastAnsii := ""
|
|
||||||
withoutAnsii := []rune{}
|
|
||||||
withOffset := []rune{}
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
for {
|
|
||||||
if i >= len(r) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if r[i] == 27 {
|
|
||||||
lastAnsii = ""
|
|
||||||
withOffset = append(withOffset, r[i])
|
|
||||||
lastAnsii += string(r[i])
|
|
||||||
i++
|
|
||||||
for {
|
|
||||||
|
|
||||||
i++
|
|
||||||
if i > len(r) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
withOffset = append(withOffset, r[i])
|
|
||||||
lastAnsii += string(r[i])
|
|
||||||
|
|
||||||
if r[i] == 'm' {
|
|
||||||
i++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i >= len(r) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
withoutAnsii = append(withoutAnsii, r[i])
|
|
||||||
|
|
||||||
if len(withoutAnsii) <= contentWidth {
|
|
||||||
withOffset = append(withOffset, r[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(withoutAnsii) > contentWidth {
|
|
||||||
// If line is too large limit it
|
|
||||||
line = string(withOffset)
|
|
||||||
} else {
|
|
||||||
// If line is too small enlarge it by adding spaces
|
|
||||||
line += strings.Repeat(" ", contentWidth-len(withoutAnsii))
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastAnsii != "" {
|
|
||||||
line += RESET
|
|
||||||
}
|
|
||||||
|
|
||||||
line = prefix + line + suffix
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't add newline for last element
|
|
||||||
if y != b.Height-1 {
|
|
||||||
line += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
out += line
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
@ -1,329 +0,0 @@
|
||||||
package goterm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
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-utf8.RuneCountInString(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 = math.Inf(-1)
|
|
||||||
minX = math.Inf(1)
|
|
||||||
maxY = math.Inf(-1)
|
|
||||||
minY = math.Inf(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
// 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 cursor 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 recommended write to buffer dirrectly, use package Print,Printf,Println fucntions instead.
|
|
||||||
var Screen *bytes.Buffer = new(bytes.Buffer)
|
|
||||||
|
|
||||||
// GetXY gets relative or absolute coordinates
|
|
||||||
// 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", y, x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetLine returns carrier to start of line
|
|
||||||
func ResetLine(str string) (out string) {
|
|
||||||
return applyTransform(str, func(idx int, line string) string {
|
|
||||||
return fmt.Sprintf("%s%s", 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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Width gets console width
|
|
||||||
func Width() int {
|
|
||||||
ws, err := getWinsize()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(ws.Col)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentHeight gets 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.SplitAfter(Screen.String(), "\n") {
|
|
||||||
if idx > Height() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Output.WriteString(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
//go:build plan9 || solaris
|
|
||||||
// +build plan9 solaris
|
|
||||||
|
|
||||||
package goterm
|
|
||||||
|
|
||||||
func getWinsize() (*winsize, error) {
|
|
||||||
ws := new(winsize)
|
|
||||||
|
|
||||||
ws.Col = 80
|
|
||||||
ws.Row = 24
|
|
||||||
|
|
||||||
return ws, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Height gets console height
|
|
||||||
func Height() int {
|
|
||||||
ws, err := getWinsize()
|
|
||||||
if err != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return int(ws.Row)
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
//go:build !windows && !plan9 && !solaris
|
|
||||||
// +build !windows,!plan9,!solaris
|
|
||||||
|
|
||||||
package goterm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getWinsize() (*unix.Winsize, error) {
|
|
||||||
|
|
||||||
ws, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ)
|
|
||||||
if err != nil {
|
|
||||||
return nil, os.NewSyscallError("GetWinsize", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ws, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Height gets console height
|
|
||||||
func Height() int {
|
|
||||||
ws, err := getWinsize()
|
|
||||||
if err != nil {
|
|
||||||
// returns math.MinInt32 if we could not retrieve the height of console window,
|
|
||||||
// like VSCode debugging console
|
|
||||||
if errors.Is(err, unix.EOPNOTSUPP) {
|
|
||||||
return math.MinInt32
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return int(ws.Row)
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package goterm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getWinsize() (*winsize, error) {
|
|
||||||
ws := new(winsize)
|
|
||||||
fd := os.Stdout.Fd()
|
|
||||||
var info windows.ConsoleScreenBufferInfo
|
|
||||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.Col = uint16(info.Window.Right - info.Window.Left + 1)
|
|
||||||
ws.Row = uint16(info.Window.Bottom - info.Window.Top + 1)
|
|
||||||
|
|
||||||
return ws, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Height gets console height
|
|
||||||
func Height() int {
|
|
||||||
ws, err := getWinsize()
|
|
||||||
if err != nil {
|
|
||||||
// returns math.MinInt32 if we could not retrieve the height of console window,
|
|
||||||
// like VSCode debugging console
|
|
||||||
if errors.Is(err, windows.WSAEOPNOTSUPP) {
|
|
||||||
return math.MinInt32
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return int(ws.Row)
|
|
||||||
}
|
|
||||||
|
|
@ -60,9 +60,6 @@ github.com/asaskevich/govalidator
|
||||||
# github.com/blang/semver/v4 v4.0.0
|
# github.com/blang/semver/v4 v4.0.0
|
||||||
## explicit; go 1.14
|
## explicit; go 1.14
|
||||||
github.com/blang/semver/v4
|
github.com/blang/semver/v4
|
||||||
# github.com/buger/goterm v1.0.4
|
|
||||||
## explicit; go 1.15
|
|
||||||
github.com/buger/goterm
|
|
||||||
# github.com/checkpoint-restore/checkpointctl v1.3.0
|
# github.com/checkpoint-restore/checkpointctl v1.3.0
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
github.com/checkpoint-restore/checkpointctl/lib
|
github.com/checkpoint-restore/checkpointctl/lib
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue