From 8c828e85e7f45feeb4c9edf23bbc92f22b031dc2 Mon Sep 17 00:00:00 2001 From: Isabel Suchanek Date: Thu, 27 May 2021 08:40:34 -0700 Subject: [PATCH] Use go-glint for spinner, printing in place if tty --- command/deployment_status.go | 163 +- go.mod | 6 +- go.sum | 25 +- vendor/github.com/VividCortex/ewma/.gitignore | 2 + vendor/github.com/VividCortex/ewma/LICENSE | 21 + vendor/github.com/VividCortex/ewma/README.md | 140 + vendor/github.com/VividCortex/ewma/ewma.go | 126 + vendor/github.com/cheggaaa/pb/v3/LICENSE | 12 + vendor/github.com/cheggaaa/pb/v3/element.go | 290 ++ vendor/github.com/cheggaaa/pb/v3/go.mod | 11 + vendor/github.com/cheggaaa/pb/v3/go.sum | 21 + vendor/github.com/cheggaaa/pb/v3/io.go | 49 + vendor/github.com/cheggaaa/pb/v3/pb.go | 566 +++ vendor/github.com/cheggaaa/pb/v3/preset.go | 15 + vendor/github.com/cheggaaa/pb/v3/speed.go | 83 + vendor/github.com/cheggaaa/pb/v3/template.go | 88 + .../cheggaaa/pb/v3/termutil/term.go | 56 + .../cheggaaa/pb/v3/termutil/term_appengine.go | 11 + .../cheggaaa/pb/v3/termutil/term_bsd.go | 9 + .../cheggaaa/pb/v3/termutil/term_linux.go | 7 + .../cheggaaa/pb/v3/termutil/term_nix.go | 8 + .../cheggaaa/pb/v3/termutil/term_plan9.go | 50 + .../cheggaaa/pb/v3/termutil/term_solaris.go | 8 + .../cheggaaa/pb/v3/termutil/term_win.go | 155 + .../cheggaaa/pb/v3/termutil/term_x.go | 76 + vendor/github.com/cheggaaa/pb/v3/util.go | 115 + vendor/github.com/gookit/color/.gitignore | 20 + vendor/github.com/gookit/color/.travis.yml | 21 + vendor/github.com/gookit/color/LICENSE | 20 + vendor/github.com/gookit/color/README.md | 422 +++ .../github.com/gookit/color/README.zh-CN.md | 392 +++ vendor/github.com/gookit/color/color.go | 272 ++ vendor/github.com/gookit/color/color_16.go | 300 ++ vendor/github.com/gookit/color/color_256.go | 218 ++ .../github.com/gookit/color/color_nonwin.go | 23 + vendor/github.com/gookit/color/color_rgb.go | 301 ++ .../github.com/gookit/color/color_windows.go | 152 + vendor/github.com/gookit/color/go.mod | 5 + vendor/github.com/gookit/color/go.sum | 22 + vendor/github.com/gookit/color/rgb_to_256.go | 307 ++ vendor/github.com/gookit/color/style.go | 285 ++ vendor/github.com/gookit/color/tag.go | 310 ++ vendor/github.com/gookit/color/utils.go | 351 ++ vendor/github.com/gosuri/uilive/.travis.yml | 28 - vendor/github.com/gosuri/uilive/LICENSE | 10 - vendor/github.com/gosuri/uilive/Makefile | 7 - vendor/github.com/gosuri/uilive/README.md | 31 - vendor/github.com/gosuri/uilive/doc.go | 2 - vendor/github.com/gosuri/uilive/go.mod | 3 - .../github.com/gosuri/uilive/terminal_size.go | 37 - .../gosuri/uilive/terminal_size_windows.go | 24 - vendor/github.com/gosuri/uilive/writer.go | 173 - .../github.com/gosuri/uilive/writer_posix.go | 15 - .../gosuri/uilive/writer_windows.go | 74 - .../github.com/mattn/go-runewidth/.travis.yml | 8 + vendor/github.com/mattn/go-runewidth/LICENSE | 21 + .../github.com/mattn/go-runewidth/README.mkd | 27 + vendor/github.com/mattn/go-runewidth/go.mod | 3 + .../mattn/go-runewidth/runewidth.go | 258 ++ .../mattn/go-runewidth/runewidth_appengine.go | 8 + .../mattn/go-runewidth/runewidth_js.go | 9 + .../mattn/go-runewidth/runewidth_posix.go | 79 + .../mattn/go-runewidth/runewidth_table.go | 427 +++ .../mattn/go-runewidth/runewidth_windows.go | 28 + vendor/github.com/mitchellh/go-glint/LICENSE | 21 + .../github.com/mitchellh/go-glint/README.md | 79 + .../mitchellh/go-glint/component.go | 89 + .../mitchellh/go-glint/components/progress.go | 38 + .../go-glint/components/sparkline.go | 112 + .../mitchellh/go-glint/components/spinner.go | 36 + .../go-glint/components/stopwatch.go | 23 + .../github.com/mitchellh/go-glint/context.go | 18 + .../github.com/mitchellh/go-glint/document.go | 317 ++ .../github.com/mitchellh/go-glint/finalize.go | 19 + .../mitchellh/go-glint/flex/LICENSE | 31 + .../mitchellh/go-glint/flex/PATENTS | 33 + .../mitchellh/go-glint/flex/README.md | 27 + .../mitchellh/go-glint/flex/enums.go | 454 +++ .../mitchellh/go-glint/flex/math.go | 73 + .../mitchellh/go-glint/flex/print.go | 199 ++ .../mitchellh/go-glint/flex/yoga.go | 3080 +++++++++++++++++ .../mitchellh/go-glint/flex/yoga_h.go | 37 + .../mitchellh/go-glint/flex/yoga_props.go | 615 ++++ .../github.com/mitchellh/go-glint/fragment.go | 14 + vendor/github.com/mitchellh/go-glint/go.mod | 15 + vendor/github.com/mitchellh/go-glint/go.sum | 51 + .../go-glint/internal/layout/builder.go | 42 + .../github.com/mitchellh/go-glint/layout.go | 72 + .../github.com/mitchellh/go-glint/measure.go | 199 ++ .../github.com/mitchellh/go-glint/renderer.go | 54 + .../mitchellh/go-glint/renderer_string.go | 103 + .../mitchellh/go-glint/renderer_term.go | 126 + vendor/github.com/mitchellh/go-glint/style.go | 152 + .../github.com/mitchellh/go-glint/testing.go | 19 + vendor/github.com/mitchellh/go-glint/text.go | 37 + vendor/github.com/mitchellh/go-glint/tree.go | 87 + .../go-testing-interface/.travis.yml | 1 - .../mitchellh/go-testing-interface/README.md | 8 + .../mitchellh/go-testing-interface/go.mod | 2 + .../mitchellh/go-testing-interface/testing.go | 35 +- .../go-testing-interface/testing_go19.go | 108 - .../github.com/mitchellh/go-wordwrap/go.mod | 2 + .../mitchellh/go-wordwrap/wordwrap.go | 26 +- vendor/github.com/tj/go-spin/Readme.md | 28 + vendor/github.com/tj/go-spin/go-spin.gif | Bin 0 -> 375105 bytes vendor/github.com/tj/go-spin/spin.go | 71 + vendor/golang.org/x/crypto/pkcs12/pkcs12.go | 12 +- vendor/modules.txt | 26 +- 108 files changed, 12826 insertions(+), 571 deletions(-) create mode 100644 vendor/github.com/VividCortex/ewma/.gitignore create mode 100644 vendor/github.com/VividCortex/ewma/LICENSE create mode 100644 vendor/github.com/VividCortex/ewma/README.md create mode 100644 vendor/github.com/VividCortex/ewma/ewma.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/LICENSE create mode 100644 vendor/github.com/cheggaaa/pb/v3/element.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/go.mod create mode 100644 vendor/github.com/cheggaaa/pb/v3/go.sum create mode 100644 vendor/github.com/cheggaaa/pb/v3/io.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/pb.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/preset.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/speed.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/template.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_appengine.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_bsd.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_linux.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_nix.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_plan9.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_solaris.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_win.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/termutil/term_x.go create mode 100644 vendor/github.com/cheggaaa/pb/v3/util.go create mode 100644 vendor/github.com/gookit/color/.gitignore create mode 100644 vendor/github.com/gookit/color/.travis.yml create mode 100644 vendor/github.com/gookit/color/LICENSE create mode 100644 vendor/github.com/gookit/color/README.md create mode 100644 vendor/github.com/gookit/color/README.zh-CN.md create mode 100644 vendor/github.com/gookit/color/color.go create mode 100644 vendor/github.com/gookit/color/color_16.go create mode 100644 vendor/github.com/gookit/color/color_256.go create mode 100644 vendor/github.com/gookit/color/color_nonwin.go create mode 100644 vendor/github.com/gookit/color/color_rgb.go create mode 100644 vendor/github.com/gookit/color/color_windows.go create mode 100644 vendor/github.com/gookit/color/go.mod create mode 100644 vendor/github.com/gookit/color/go.sum create mode 100644 vendor/github.com/gookit/color/rgb_to_256.go create mode 100644 vendor/github.com/gookit/color/style.go create mode 100644 vendor/github.com/gookit/color/tag.go create mode 100644 vendor/github.com/gookit/color/utils.go delete mode 100644 vendor/github.com/gosuri/uilive/.travis.yml delete mode 100644 vendor/github.com/gosuri/uilive/LICENSE delete mode 100644 vendor/github.com/gosuri/uilive/Makefile delete mode 100644 vendor/github.com/gosuri/uilive/README.md delete mode 100644 vendor/github.com/gosuri/uilive/doc.go delete mode 100644 vendor/github.com/gosuri/uilive/go.mod delete mode 100644 vendor/github.com/gosuri/uilive/terminal_size.go delete mode 100644 vendor/github.com/gosuri/uilive/terminal_size_windows.go delete mode 100644 vendor/github.com/gosuri/uilive/writer.go delete mode 100644 vendor/github.com/gosuri/uilive/writer_posix.go delete mode 100644 vendor/github.com/gosuri/uilive/writer_windows.go create mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml create mode 100644 vendor/github.com/mattn/go-runewidth/LICENSE create mode 100644 vendor/github.com/mattn/go-runewidth/README.mkd create mode 100644 vendor/github.com/mattn/go-runewidth/go.mod create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_appengine.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_js.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_posix.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_table.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_windows.go create mode 100644 vendor/github.com/mitchellh/go-glint/LICENSE create mode 100644 vendor/github.com/mitchellh/go-glint/README.md create mode 100644 vendor/github.com/mitchellh/go-glint/component.go create mode 100644 vendor/github.com/mitchellh/go-glint/components/progress.go create mode 100644 vendor/github.com/mitchellh/go-glint/components/sparkline.go create mode 100644 vendor/github.com/mitchellh/go-glint/components/spinner.go create mode 100644 vendor/github.com/mitchellh/go-glint/components/stopwatch.go create mode 100644 vendor/github.com/mitchellh/go-glint/context.go create mode 100644 vendor/github.com/mitchellh/go-glint/document.go create mode 100644 vendor/github.com/mitchellh/go-glint/finalize.go create mode 100644 vendor/github.com/mitchellh/go-glint/flex/LICENSE create mode 100644 vendor/github.com/mitchellh/go-glint/flex/PATENTS create mode 100644 vendor/github.com/mitchellh/go-glint/flex/README.md create mode 100644 vendor/github.com/mitchellh/go-glint/flex/enums.go create mode 100644 vendor/github.com/mitchellh/go-glint/flex/math.go create mode 100644 vendor/github.com/mitchellh/go-glint/flex/print.go create mode 100644 vendor/github.com/mitchellh/go-glint/flex/yoga.go create mode 100644 vendor/github.com/mitchellh/go-glint/flex/yoga_h.go create mode 100644 vendor/github.com/mitchellh/go-glint/flex/yoga_props.go create mode 100644 vendor/github.com/mitchellh/go-glint/fragment.go create mode 100644 vendor/github.com/mitchellh/go-glint/go.mod create mode 100644 vendor/github.com/mitchellh/go-glint/go.sum create mode 100644 vendor/github.com/mitchellh/go-glint/internal/layout/builder.go create mode 100644 vendor/github.com/mitchellh/go-glint/layout.go create mode 100644 vendor/github.com/mitchellh/go-glint/measure.go create mode 100644 vendor/github.com/mitchellh/go-glint/renderer.go create mode 100644 vendor/github.com/mitchellh/go-glint/renderer_string.go create mode 100644 vendor/github.com/mitchellh/go-glint/renderer_term.go create mode 100644 vendor/github.com/mitchellh/go-glint/style.go create mode 100644 vendor/github.com/mitchellh/go-glint/testing.go create mode 100644 vendor/github.com/mitchellh/go-glint/text.go create mode 100644 vendor/github.com/mitchellh/go-glint/tree.go delete mode 100644 vendor/github.com/mitchellh/go-testing-interface/testing_go19.go create mode 100644 vendor/github.com/tj/go-spin/Readme.md create mode 100644 vendor/github.com/tj/go-spin/go-spin.gif create mode 100644 vendor/github.com/tj/go-spin/spin.go diff --git a/command/deployment_status.go b/command/deployment_status.go index 3548971089f0..b5aa1a483bcb 100644 --- a/command/deployment_status.go +++ b/command/deployment_status.go @@ -1,6 +1,7 @@ package command import ( + "context" "errors" "fmt" "os" @@ -9,10 +10,11 @@ import ( "time" "github.com/docker/docker/pkg/term" - "github.com/gosuri/uilive" "github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/api/contexts" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mitchellh/go-glint" + "github.com/mitchellh/go-glint/components" "github.com/posener/complete" ) @@ -398,9 +400,30 @@ func hasAutoRevert(d *api.Deployment) bool { return false } -func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, index uint64, verbose bool) { - writer := uilive.New() - writer.Start() +func (c *DeploymentStatusCommand) ttyMonitor(client *api.Client, deployID string, index uint64, verbose bool) { + var length int + if verbose { + length = fullId + } else { + length = shortId + } + + d := glint.New() + spinner := glint.Layout( + components.Spinner(), + glint.TextFunc(func(rows, cols uint) string { + return fmt.Sprintf(" Deployment %q in progress...", limit(deployID, length)) + }), + ).Row() + refreshRate := 100 * time.Millisecond + + d.SetRefreshRate(refreshRate) + d.Set(spinner) + + ctx, cancel := context.WithCancel(context.Background()) + + go d.Render(ctx) + defer cancel() q := api.QueryOptions{ AllowStale: true, @@ -408,6 +431,95 @@ func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, i WaitTime: 2 * time.Second, } + for { + deploy, meta, err := client.Deployments().Info(deployID, &q) + if err != nil { + d.Append(glint.Style( // TODO glint color? + glint.TextFunc(func(rows, cols uint) string { + return c.Colorize().Color(fmt.Sprintf("%s: Error fetching deployment", formatTime(time.Now()))) + }), + )) + return + } + + status := deploy.Status + info := formatTime(time.Now()) + info += fmt.Sprintf("\n%s", formatDeployment(client, deploy, length)) + + if verbose { + info += "\n\n[bold]Allocations[reset]\n" + allocs, _, err := client.Deployments().Allocations(deployID, nil) + if err != nil { + info += "Error fetching allocations" + } else { + info += formatAllocListStubs(allocs, verbose, length) + } + } + + // Add newline before output to avoid prefix indentation when called from job run + msg := c.Colorize().Color(fmt.Sprintf("\n%s", info)) + statusComponent := glint.Style( + glint.TextFunc(func(row, cols uint) string { + return msg + }), + ) + d.Set(spinner, statusComponent) + + endSpinner := glint.Layout( + components.Spinner(), + glint.TextFunc(func(rows, cols uint) string { + return fmt.Sprintf(" Deployment %q %s", limit(deployID, length), status) + }), + ).Row() + + switch status { + case structs.DeploymentStatusFailed: + if hasAutoRevert(deploy) { + // Separate rollback monitoring from failed deployment + d.Set( + endSpinner, + statusComponent, + glint.Layout(glint.Text("")).Row(), + ) + + // Wait for rollback to launch + time.Sleep(1 * time.Second) + rollback, _, err := client.Jobs().LatestDeployment(deploy.JobID, nil) + + if err != nil { + d.Append( + glint.TextFunc(func(rows, cols uint) string { + return c.Colorize().Color( + fmt.Sprintf("%s: Error fetching deployment of previous job version", formatTime(time.Now())), + ) + }), + ) + d.RenderFrame() + return + } + + d.Close() + c.ttyMonitor(client, rollback.ID, index, verbose) + } else { + // render one final time with completion message + d.Set(endSpinner, statusComponent) + d.RenderFrame() + } + return + + case structs.DeploymentStatusSuccessful, structs.DeploymentStatusCancelled, structs.DeploymentStatusDescriptionBlocked: + // render one final time with completion message + d.Set(endSpinner, statusComponent) + d.RenderFrame() + return + default: + q.WaitIndex = meta.LastIndex + continue + } + } +} + +func (c *DeploymentStatusCommand) pipedMonitor(client *api.Client, deployID string, index uint64, verbose bool) { var length int if verbose { length = fullId @@ -415,10 +527,17 @@ func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, i length = shortId } + q := api.QueryOptions{ + AllowStale: true, + WaitIndex: index, + WaitTime: 2 * time.Second, + } + for { deploy, meta, err := client.Deployments().Info(deployID, &q) if err != nil { - c.Ui.Error(c.Colorize().Color("Error fetching deployment")) + // TODO error doesn't get piped + c.Ui.Error(c.Colorize().Color(fmt.Sprintf("%s: Error fetching deployment", formatTime(time.Now())))) return } @@ -438,42 +557,44 @@ func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, i // Add newline before output to avoid prefix indentation when called from job run msg := c.Colorize().Color(fmt.Sprintf("\n%s", info)) - - // Print in place if tty - _, isStdoutTerminal := term.GetFdInfo(os.Stdout) - if isStdoutTerminal { - fmt.Fprint(writer, msg) - } else { - c.Ui.Output(msg) - } + c.Ui.Output(msg) switch status { case structs.DeploymentStatusFailed: if hasAutoRevert(deploy) { + // Separate rollback monitoring from failed deployment + c.Ui.Output("") + // Wait for rollback to launch time.Sleep(1 * time.Second) rollback, _, err := client.Jobs().LatestDeployment(deploy.JobID, nil) if err != nil { - c.Ui.Error(c.Colorize().Color("Error fetching deployment of previous job version")) + c.Ui.Error(c.Colorize().Color( + fmt.Sprintf("%s: Error fetching deployment of previous job version", formatTime(time.Now())), + )) return } - c.Ui.Output("") // Separate rollback monitoring from failed deployment - c.monitor(client, rollback.ID, index, verbose) + + c.pipedMonitor(client, rollback.ID, index, verbose) } return - case structs.DeploymentStatusSuccessful: - case structs.DeploymentStatusCancelled: - case structs.DeploymentStatusBlocked: + case structs.DeploymentStatusSuccessful, structs.DeploymentStatusCancelled, structs.DeploymentStatusDescriptionBlocked: return default: q.WaitIndex = meta.LastIndex continue } + } +} - writer.Stop() - return +func (c *DeploymentStatusCommand) monitor(client *api.Client, deployID string, index uint64, verbose bool) { + _, isStdoutTerminal := term.GetFdInfo(os.Stdout) + if isStdoutTerminal { + c.ttyMonitor(client, deployID, index, verbose) + } else { + c.pipedMonitor(client, deployID, index, verbose) } } diff --git a/go.mod b/go.mod index 243d39f860f4..c46c998c2ae7 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,6 @@ require ( github.com/golang/snappy v0.0.1 github.com/google/go-cmp v0.5.2 github.com/gorilla/websocket v1.4.2 - github.com/gosuri/uilive v0.0.4 github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39 github.com/hashicorp/consul v1.7.8 github.com/hashicorp/consul-template v0.25.1 @@ -98,8 +97,9 @@ require ( github.com/mitchellh/cli v1.1.0 github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 github.com/mitchellh/copystructure v1.0.0 + github.com/mitchellh/go-glint v0.0.0-20201119015200-53f6eb3bf4d2 github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b - github.com/mitchellh/go-testing-interface v1.0.3 + github.com/mitchellh/go-testing-interface v1.14.1 github.com/mitchellh/hashstructure v1.0.0 github.com/mitchellh/mapstructure v1.3.3 github.com/mitchellh/reflectwalk v1.0.1 @@ -126,7 +126,7 @@ require ( github.com/zclconf/go-cty v1.8.0 github.com/zclconf/go-cty-yaml v1.0.2 go.opencensus.io v0.22.1-0.20190713072201-b4a14686f0a9 // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect golang.org/x/mod v0.3.0 // indirect diff --git a/go.sum b/go.sum index 75fa51497fd8..c59b39e43fb0 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= @@ -133,7 +135,10 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE= +github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw= github.com/cilium/ebpf v0.2.0 h1:Fv93L3KKckEcEHR3oApXVzyBTDA8WAm6VXhPE00N3f8= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= @@ -314,6 +319,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gookit/color v1.3.1 h1:PPD/C7sf8u2L8XQPdPgsWRoAiLQGZEZOzU3cf5IYYUk= +github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -324,8 +331,6 @@ github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= -github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39 h1:MqvH60+R2JhSdvVgGxmExOndrkRQtGW7w4+gcrymN64= github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= @@ -503,6 +508,7 @@ github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZi github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -514,6 +520,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -529,6 +537,8 @@ github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 h1:KHyL+3mQO github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-glint v0.0.0-20201119015200-53f6eb3bf4d2 h1:p6jvEJYVtY05cbttwUhvxzV1j/e/40musLqs18vkv+E= +github.com/mitchellh/go-glint v0.0.0-20201119015200-53f6eb3bf4d2/go.mod h1:9X3rpO+I3yuihb6p8ktF8qWxROGwij9DBW/czUsMlhk= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -536,11 +546,13 @@ github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b h1:9+ke9YJ9KGWw5AN github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.3 h1:gqwbsGvc0jbhAPW/26WfEoSiPANAVlR49AAVdvaTjI4= github.com/mitchellh/go-testing-interface v1.0.3/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= @@ -707,6 +719,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtse github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= +github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= @@ -770,8 +784,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191106202628-ed6320f186d4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= diff --git a/vendor/github.com/VividCortex/ewma/.gitignore b/vendor/github.com/VividCortex/ewma/.gitignore new file mode 100644 index 000000000000..6c7104aef4a1 --- /dev/null +++ b/vendor/github.com/VividCortex/ewma/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.*.sw? diff --git a/vendor/github.com/VividCortex/ewma/LICENSE b/vendor/github.com/VividCortex/ewma/LICENSE new file mode 100644 index 000000000000..a78d643ed8f7 --- /dev/null +++ b/vendor/github.com/VividCortex/ewma/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2013 VividCortex + +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. diff --git a/vendor/github.com/VividCortex/ewma/README.md b/vendor/github.com/VividCortex/ewma/README.md new file mode 100644 index 000000000000..7aab61b8751c --- /dev/null +++ b/vendor/github.com/VividCortex/ewma/README.md @@ -0,0 +1,140 @@ +# EWMA [![GoDoc](https://godoc.org/github.com/VividCortex/ewma?status.svg)](https://godoc.org/github.com/VividCortex/ewma) ![Build Status](https://circleci.com/gh/VividCortex/moving_average.png?circle-token=1459fa37f9ca0e50cef05d1963146d96d47ea523) + +This repo provides Exponentially Weighted Moving Average algorithms, or EWMAs for short, [based on our +Quantifying Abnormal Behavior talk](https://vividcortex.com/blog/2013/07/23/a-fast-go-library-for-exponential-moving-averages/). + +### Exponentially Weighted Moving Average + +An exponentially weighted moving average is a way to continuously compute a type of +average for a series of numbers, as the numbers arrive. After a value in the series is +added to the average, its weight in the average decreases exponentially over time. This +biases the average towards more recent data. EWMAs are useful for several reasons, chiefly +their inexpensive computational and memory cost, as well as the fact that they represent +the recent central tendency of the series of values. + +The EWMA algorithm requires a decay factor, alpha. The larger the alpha, the more the average +is biased towards recent history. The alpha must be between 0 and 1, and is typically +a fairly small number, such as 0.04. We will discuss the choice of alpha later. + +The algorithm works thus, in pseudocode: + +1. Multiply the next number in the series by alpha. +2. Multiply the current value of the average by 1 minus alpha. +3. Add the result of steps 1 and 2, and store it as the new current value of the average. +4. Repeat for each number in the series. + +There are special-case behaviors for how to initialize the current value, and these vary +between implementations. One approach is to start with the first value in the series; +another is to average the first 10 or so values in the series using an arithmetic average, +and then begin the incremental updating of the average. Each method has pros and cons. + +It may help to look at it pictorially. Suppose the series has five numbers, and we choose +alpha to be 0.50 for simplicity. Here's the series, with numbers in the neighborhood of 300. + +![Data Series](https://user-images.githubusercontent.com/279875/28242350-463289a2-6977-11e7-88ca-fd778ccef1f0.png) + +Now let's take the moving average of those numbers. First we set the average to the value +of the first number. + +![EWMA Step 1](https://user-images.githubusercontent.com/279875/28242353-464c96bc-6977-11e7-9981-dc4e0789c7ba.png) + +Next we multiply the next number by alpha, multiply the current value by 1-alpha, and add +them to generate a new value. + +![EWMA Step 2](https://user-images.githubusercontent.com/279875/28242351-464abefa-6977-11e7-95d0-43900f29bef2.png) + +This continues until we are done. + +![EWMA Step N](https://user-images.githubusercontent.com/279875/28242352-464c58f0-6977-11e7-8cd0-e01e4efaac7f.png) + +Notice how each of the values in the series decays by half each time a new value +is added, and the top of the bars in the lower portion of the image represents the +size of the moving average. It is a smoothed, or low-pass, average of the original +series. + +For further reading, see [Exponentially weighted moving average](http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) on wikipedia. + +### Choosing Alpha + +Consider a fixed-size sliding-window moving average (not an exponentially weighted moving average) +that averages over the previous N samples. What is the average age of each sample? It is N/2. + +Now suppose that you wish to construct a EWMA whose samples have the same average age. The formula +to compute the alpha required for this is: alpha = 2/(N+1). Proof is in the book +"Production and Operations Analysis" by Steven Nahmias. + +So, for example, if you have a time-series with samples once per second, and you want to get the +moving average over the previous minute, you should use an alpha of .032786885. This, by the way, +is the constant alpha used for this repository's SimpleEWMA. + +### Implementations + +This repository contains two implementations of the EWMA algorithm, with different properties. + +The implementations all conform to the MovingAverage interface, and the constructor returns +that type. + +Current implementations assume an implicit time interval of 1.0 between every sample added. +That is, the passage of time is treated as though it's the same as the arrival of samples. +If you need time-based decay when samples are not arriving precisely at set intervals, then +this package will not support your needs at present. + +#### SimpleEWMA + +A SimpleEWMA is designed for low CPU and memory consumption. It **will** have different behavior than the VariableEWMA +for multiple reasons. It has no warm-up period and it uses a constant +decay. These properties let it use less memory. It will also behave +differently when it's equal to zero, which is assumed to mean +uninitialized, so if a value is likely to actually become zero over time, +then any non-zero value will cause a sharp jump instead of a small change. + +#### VariableEWMA + +Unlike SimpleEWMA, this supports a custom age which must be stored, and thus uses more memory. +It also has a "warmup" time when you start adding values to it. It will report a value of 0.0 +until you have added the required number of samples to it. It uses some memory to store the +number of samples added to it. As a result it uses a little over twice the memory of SimpleEWMA. + +## Usage + +### API Documentation + +View the GoDoc generated documentation [here](http://godoc.org/github.com/VividCortex/ewma). + +```go +package main +import "github.com/VividCortex/ewma" + +func main() { + samples := [100]float64{ + 4599, 5711, 4746, 4621, 5037, 4218, 4925, 4281, 5207, 5203, 5594, 5149, + } + + e := ewma.NewMovingAverage() //=> Returns a SimpleEWMA if called without params + a := ewma.NewMovingAverage(5) //=> returns a VariableEWMA with a decay of 2 / (5 + 1) + + for _, f := range samples { + e.Add(f) + a.Add(f) + } + + e.Value() //=> 13.577404704631077 + a.Value() //=> 1.5806140565521463e-12 +} +``` + +## Contributing + +We only accept pull requests for minor fixes or improvements. This includes: + +* Small bug fixes +* Typos +* Documentation or comments + +Please open issues to discuss new features. Pull requests for new features will be rejected, +so we recommend forking the repository and making changes in your fork for your use case. + +## License + +This repository is Copyright (c) 2013 VividCortex, Inc. All rights reserved. +It is licensed under the MIT license. Please see the LICENSE file for applicable license terms. diff --git a/vendor/github.com/VividCortex/ewma/ewma.go b/vendor/github.com/VividCortex/ewma/ewma.go new file mode 100644 index 000000000000..44d5d53e3ca0 --- /dev/null +++ b/vendor/github.com/VividCortex/ewma/ewma.go @@ -0,0 +1,126 @@ +// Package ewma implements exponentially weighted moving averages. +package ewma + +// Copyright (c) 2013 VividCortex, Inc. All rights reserved. +// Please see the LICENSE file for applicable license terms. + +const ( + // By default, we average over a one-minute period, which means the average + // age of the metrics in the period is 30 seconds. + AVG_METRIC_AGE float64 = 30.0 + + // The formula for computing the decay factor from the average age comes + // from "Production and Operations Analysis" by Steven Nahmias. + DECAY float64 = 2 / (float64(AVG_METRIC_AGE) + 1) + + // For best results, the moving average should not be initialized to the + // samples it sees immediately. The book "Production and Operations + // Analysis" by Steven Nahmias suggests initializing the moving average to + // the mean of the first 10 samples. Until the VariableEwma has seen this + // many samples, it is not "ready" to be queried for the value of the + // moving average. This adds some memory cost. + WARMUP_SAMPLES uint8 = 10 +) + +// MovingAverage is the interface that computes a moving average over a time- +// series stream of numbers. The average may be over a window or exponentially +// decaying. +type MovingAverage interface { + Add(float64) + Value() float64 + Set(float64) +} + +// NewMovingAverage constructs a MovingAverage that computes an average with the +// desired characteristics in the moving window or exponential decay. If no +// age is given, it constructs a default exponentially weighted implementation +// that consumes minimal memory. The age is related to the decay factor alpha +// by the formula given for the DECAY constant. It signifies the average age +// of the samples as time goes to infinity. +func NewMovingAverage(age ...float64) MovingAverage { + if len(age) == 0 || age[0] == AVG_METRIC_AGE { + return new(SimpleEWMA) + } + return &VariableEWMA{ + decay: 2 / (age[0] + 1), + } +} + +// A SimpleEWMA represents the exponentially weighted moving average of a +// series of numbers. It WILL have different behavior than the VariableEWMA +// for multiple reasons. It has no warm-up period and it uses a constant +// decay. These properties let it use less memory. It will also behave +// differently when it's equal to zero, which is assumed to mean +// uninitialized, so if a value is likely to actually become zero over time, +// then any non-zero value will cause a sharp jump instead of a small change. +// However, note that this takes a long time, and the value may just +// decays to a stable value that's close to zero, but which won't be mistaken +// for uninitialized. See http://play.golang.org/p/litxBDr_RC for example. +type SimpleEWMA struct { + // The current value of the average. After adding with Add(), this is + // updated to reflect the average of all values seen thus far. + value float64 +} + +// Add adds a value to the series and updates the moving average. +func (e *SimpleEWMA) Add(value float64) { + if e.value == 0 { // this is a proxy for "uninitialized" + e.value = value + } else { + e.value = (value * DECAY) + (e.value * (1 - DECAY)) + } +} + +// Value returns the current value of the moving average. +func (e *SimpleEWMA) Value() float64 { + return e.value +} + +// Set sets the EWMA's value. +func (e *SimpleEWMA) Set(value float64) { + e.value = value +} + +// VariableEWMA represents the exponentially weighted moving average of a series of +// numbers. Unlike SimpleEWMA, it supports a custom age, and thus uses more memory. +type VariableEWMA struct { + // The multiplier factor by which the previous samples decay. + decay float64 + // The current value of the average. + value float64 + // The number of samples added to this instance. + count uint8 +} + +// Add adds a value to the series and updates the moving average. +func (e *VariableEWMA) Add(value float64) { + switch { + case e.count < WARMUP_SAMPLES: + e.count++ + e.value += value + case e.count == WARMUP_SAMPLES: + e.count++ + e.value = e.value / float64(WARMUP_SAMPLES) + e.value = (value * e.decay) + (e.value * (1 - e.decay)) + default: + e.value = (value * e.decay) + (e.value * (1 - e.decay)) + } +} + +// Value returns the current value of the average, or 0.0 if the series hasn't +// warmed up yet. +func (e *VariableEWMA) Value() float64 { + if e.count <= WARMUP_SAMPLES { + return 0.0 + } + + return e.value +} + +// Set sets the EWMA's value. +func (e *VariableEWMA) Set(value float64) { + e.value = value + if e.count <= WARMUP_SAMPLES { + e.count = WARMUP_SAMPLES + 1 + } +} diff --git a/vendor/github.com/cheggaaa/pb/v3/LICENSE b/vendor/github.com/cheggaaa/pb/v3/LICENSE new file mode 100644 index 000000000000..511970333920 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2012-2015, Sergey Cherepanov +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/cheggaaa/pb/v3/element.go b/vendor/github.com/cheggaaa/pb/v3/element.go new file mode 100644 index 000000000000..965183fe79eb --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/element.go @@ -0,0 +1,290 @@ +package pb + +import ( + "bytes" + "fmt" + "math" + "sync" + "time" +) + +const ( + adElPlaceholder = "%_ad_el_%" + adElPlaceholderLen = len(adElPlaceholder) +) + +var ( + defaultBarEls = [5]string{"[", "-", ">", "_", "]"} +) + +// Element is an interface for bar elements +type Element interface { + ProgressElement(state *State, args ...string) string +} + +// ElementFunc type implements Element interface and created for simplify elements +type ElementFunc func(state *State, args ...string) string + +// ProgressElement just call self func +func (e ElementFunc) ProgressElement(state *State, args ...string) string { + return e(state, args...) +} + +var elementsM sync.Mutex + +var elements = map[string]Element{ + "percent": ElementPercent, + "counters": ElementCounters, + "bar": adaptiveWrap(ElementBar), + "speed": ElementSpeed, + "rtime": ElementRemainingTime, + "etime": ElementElapsedTime, + "string": ElementString, + "cycle": ElementCycle, +} + +// RegisterElement give you a chance to use custom elements +func RegisterElement(name string, el Element, adaptive bool) { + if adaptive { + el = adaptiveWrap(el) + } + elementsM.Lock() + elements[name] = el + elementsM.Unlock() +} + +type argsHelper []string + +func (args argsHelper) getOr(n int, value string) string { + if len(args) > n { + return args[n] + } + return value +} + +func (args argsHelper) getNotEmptyOr(n int, value string) (v string) { + if v = args.getOr(n, value); v == "" { + return value + } + return +} + +func adaptiveWrap(el Element) Element { + return ElementFunc(func(state *State, args ...string) string { + state.recalc = append(state.recalc, ElementFunc(func(s *State, _ ...string) (result string) { + s.adaptive = true + result = el.ProgressElement(s, args...) + s.adaptive = false + return + })) + return adElPlaceholder + }) +} + +// ElementPercent shows current percent of progress. +// Optionally can take one or two string arguments. +// First string will be used as value for format float64, default is "%.02f%%". +// Second string will be used when percent can't be calculated, default is "?%" +// In template use as follows: {{percent .}} or {{percent . "%.03f%%"}} or {{percent . "%.03f%%" "?"}} +var ElementPercent ElementFunc = func(state *State, args ...string) string { + argsh := argsHelper(args) + if state.Total() > 0 { + return fmt.Sprintf( + argsh.getNotEmptyOr(0, "%.02f%%"), + float64(state.Value())/(float64(state.Total())/float64(100)), + ) + } + return argsh.getOr(1, "?%") +} + +// ElementCounters shows current and total values. +// Optionally can take one or two string arguments. +// First string will be used as format value when Total is present (>0). Default is "%s / %s" +// Second string will be used when total <= 0. Default is "%[1]s" +// In template use as follows: {{counters .}} or {{counters . "%s/%s"}} or {{counters . "%s/%s" "%s/?"}} +var ElementCounters ElementFunc = func(state *State, args ...string) string { + var f string + if state.Total() > 0 { + f = argsHelper(args).getNotEmptyOr(0, "%s / %s") + } else { + f = argsHelper(args).getNotEmptyOr(1, "%[1]s") + } + return fmt.Sprintf(f, state.Format(state.Value()), state.Format(state.Total())) +} + +type elementKey int + +const ( + barObj elementKey = iota + speedObj + cycleObj +) + +type bar struct { + eb [5][]byte // elements in bytes + cc [5]int // cell counts + buf *bytes.Buffer +} + +func (p *bar) write(state *State, eln, width int) int { + repeat := width / p.cc[eln] + for i := 0; i < repeat; i++ { + p.buf.Write(p.eb[eln]) + } + StripStringToBuffer(string(p.eb[eln]), width%p.cc[eln], p.buf) + return width +} + +func getProgressObj(state *State, args ...string) (p *bar) { + var ok bool + if p, ok = state.Get(barObj).(*bar); !ok { + p = &bar{ + buf: bytes.NewBuffer(nil), + } + state.Set(barObj, p) + } + argsH := argsHelper(args) + for i := range p.eb { + arg := argsH.getNotEmptyOr(i, defaultBarEls[i]) + if string(p.eb[i]) != arg { + p.cc[i] = CellCount(arg) + p.eb[i] = []byte(arg) + if p.cc[i] == 0 { + p.cc[i] = 1 + p.eb[i] = []byte(" ") + } + } + } + return +} + +// ElementBar make progress bar view [-->__] +// Optionally can take up to 5 string arguments. Defaults is "[", "-", ">", "_", "]" +// In template use as follows: {{bar . }} or {{bar . "<" "oOo" "|" "~" ">"}} +// Color args: {{bar . (red "[") (green "-") ... +var ElementBar ElementFunc = func(state *State, args ...string) string { + // init + var p = getProgressObj(state, args...) + + total, value := state.Total(), state.Value() + if total < 0 { + total = -total + } + if value < 0 { + value = -value + } + + // check for overflow + if total != 0 && value > total { + total = value + } + + p.buf.Reset() + + var widthLeft = state.AdaptiveElWidth() + if widthLeft <= 0 || !state.IsAdaptiveWidth() { + widthLeft = 30 + } + + // write left border + if p.cc[0] < widthLeft { + widthLeft -= p.write(state, 0, p.cc[0]) + } else { + p.write(state, 0, widthLeft) + return p.buf.String() + } + + // check right border size + if p.cc[4] < widthLeft { + // write later + widthLeft -= p.cc[4] + } else { + p.write(state, 4, widthLeft) + return p.buf.String() + } + + var curCount int + + if total > 0 { + // calculate count of currenct space + curCount = int(math.Ceil((float64(value) / float64(total)) * float64(widthLeft))) + } + + // write bar + if total == value && state.IsFinished() { + widthLeft -= p.write(state, 1, curCount) + } else if toWrite := curCount - p.cc[2]; toWrite > 0 { + widthLeft -= p.write(state, 1, toWrite) + widthLeft -= p.write(state, 2, p.cc[2]) + } else if curCount > 0 { + widthLeft -= p.write(state, 2, curCount) + } + if widthLeft > 0 { + widthLeft -= p.write(state, 3, widthLeft) + } + // write right border + p.write(state, 4, p.cc[4]) + // cut result and return string + return p.buf.String() +} + +// ElementRemainingTime calculates remaining time based on speed (EWMA) +// Optionally can take one or two string arguments. +// First string will be used as value for format time duration string, default is "%s". +// Second string will be used when bar finished and value indicates elapsed time, default is "%s" +// Third string will be used when value not available, default is "?" +// In template use as follows: {{rtime .}} or {{rtime . "%s remain"}} or {{rtime . "%s remain" "%s total" "???"}} +var ElementRemainingTime ElementFunc = func(state *State, args ...string) string { + var rts string + sp := getSpeedObj(state).value(state) + if !state.IsFinished() { + if sp > 0 { + remain := float64(state.Total() - state.Value()) + remainDur := time.Duration(remain/sp) * time.Second + rts = remainDur.String() + } else { + return argsHelper(args).getOr(2, "?") + } + } else { + rts = state.Time().Truncate(time.Second).Sub(state.StartTime().Truncate(time.Second)).String() + return fmt.Sprintf(argsHelper(args).getOr(1, "%s"), rts) + } + return fmt.Sprintf(argsHelper(args).getOr(0, "%s"), rts) +} + +// ElementElapsedTime shows elapsed time +// Optionally cat take one argument - it's format for time string. +// In template use as follows: {{etime .}} or {{etime . "%s elapsed"}} +var ElementElapsedTime ElementFunc = func(state *State, args ...string) string { + etm := state.Time().Truncate(time.Second).Sub(state.StartTime().Truncate(time.Second)) + return fmt.Sprintf(argsHelper(args).getOr(0, "%s"), etm.String()) +} + +// ElementString get value from bar by given key and print them +// bar.Set("myKey", "string to print") +// In template use as follows: {{string . "myKey"}} +var ElementString ElementFunc = func(state *State, args ...string) string { + if len(args) == 0 { + return "" + } + v := state.Get(args[0]) + if v == nil { + return "" + } + return fmt.Sprint(v) +} + +// ElementCycle return next argument for every call +// In template use as follows: {{cycle . "1" "2" "3"}} +// Or mix width other elements: {{ bar . "" "" (cycle . "↖" "↗" "↘" "↙" )}} +var ElementCycle ElementFunc = func(state *State, args ...string) string { + if len(args) == 0 { + return "" + } + n, _ := state.Get(cycleObj).(int) + if n >= len(args) { + n = 0 + } + state.Set(cycleObj, n+1) + return args[n] +} diff --git a/vendor/github.com/cheggaaa/pb/v3/go.mod b/vendor/github.com/cheggaaa/pb/v3/go.mod new file mode 100644 index 000000000000..666c86bc6f09 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/go.mod @@ -0,0 +1,11 @@ +module github.com/cheggaaa/pb/v3 + +require ( + github.com/VividCortex/ewma v1.1.1 + github.com/fatih/color v1.7.0 + github.com/mattn/go-colorable v0.1.2 + github.com/mattn/go-isatty v0.0.12 + github.com/mattn/go-runewidth v0.0.7 +) + +go 1.12 diff --git a/vendor/github.com/cheggaaa/pb/v3/go.sum b/vendor/github.com/cheggaaa/pb/v3/go.sum new file mode 100644 index 000000000000..71cb18331202 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/go.sum @@ -0,0 +1,21 @@ +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/cheggaaa/pb/v3/io.go b/vendor/github.com/cheggaaa/pb/v3/io.go new file mode 100644 index 000000000000..6ad5abc249a8 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/io.go @@ -0,0 +1,49 @@ +package pb + +import ( + "io" +) + +// Reader it's a wrapper for given reader, but with progress handle +type Reader struct { + io.Reader + bar *ProgressBar +} + +// Read reads bytes from wrapped reader and add amount of bytes to progress bar +func (r *Reader) Read(p []byte) (n int, err error) { + n, err = r.Reader.Read(p) + r.bar.Add(n) + return +} + +// Close the wrapped reader when it implements io.Closer +func (r *Reader) Close() (err error) { + r.bar.Finish() + if closer, ok := r.Reader.(io.Closer); ok { + return closer.Close() + } + return +} + +// Writer it's a wrapper for given writer, but with progress handle +type Writer struct { + io.Writer + bar *ProgressBar +} + +// Write writes bytes to wrapped writer and add amount of bytes to progress bar +func (r *Writer) Write(p []byte) (n int, err error) { + n, err = r.Writer.Write(p) + r.bar.Add(n) + return +} + +// Close the wrapped reader when it implements io.Closer +func (r *Writer) Close() (err error) { + r.bar.Finish() + if closer, ok := r.Writer.(io.Closer); ok { + return closer.Close() + } + return +} diff --git a/vendor/github.com/cheggaaa/pb/v3/pb.go b/vendor/github.com/cheggaaa/pb/v3/pb.go new file mode 100644 index 000000000000..17f3750be8a0 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/pb.go @@ -0,0 +1,566 @@ +package pb + +import ( + "bytes" + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + "sync/atomic" + "text/template" + "time" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" + + "github.com/cheggaaa/pb/v3/termutil" +) + +// Version of ProgressBar library +const Version = "3.0.5" + +type key int + +const ( + // Bytes means we're working with byte sizes. Numbers will print as Kb, Mb, etc + // bar.Set(pb.Bytes, true) + Bytes key = 1 << iota + + // Use SI bytes prefix names (kB, MB, etc) instead of IEC prefix names (KiB, MiB, etc) + SIBytesPrefix + + // Terminal means we're will print to terminal and can use ascii sequences + // Also we're will try to use terminal width + Terminal + + // Static means progress bar will not update automaticly + Static + + // ReturnSymbol - by default in terminal mode it's '\r' + ReturnSymbol + + // Color by default is true when output is tty, but you can set to false for disabling colors + Color +) + +const ( + defaultBarWidth = 100 + defaultRefreshRate = time.Millisecond * 200 +) + +// New creates new ProgressBar object +func New(total int) *ProgressBar { + return New64(int64(total)) +} + +// New64 creates new ProgressBar object using int64 as total +func New64(total int64) *ProgressBar { + pb := new(ProgressBar) + return pb.SetTotal(total) +} + +// StartNew starts new ProgressBar with Default template +func StartNew(total int) *ProgressBar { + return New(total).Start() +} + +// Start64 starts new ProgressBar with Default template. Using int64 as total. +func Start64(total int64) *ProgressBar { + return New64(total).Start() +} + +var ( + terminalWidth = termutil.TerminalWidth + isTerminal = isatty.IsTerminal + isCygwinTerminal = isatty.IsCygwinTerminal +) + +// ProgressBar is the main object of bar +type ProgressBar struct { + current, total int64 + width int + maxWidth int + mu sync.RWMutex + rm sync.Mutex + vars map[interface{}]interface{} + elements map[string]Element + output io.Writer + coutput io.Writer + nocoutput io.Writer + startTime time.Time + refreshRate time.Duration + tmpl *template.Template + state *State + buf *bytes.Buffer + ticker *time.Ticker + finish chan struct{} + finished bool + configured bool + err error +} + +func (pb *ProgressBar) configure() { + if pb.configured { + return + } + pb.configured = true + + if pb.vars == nil { + pb.vars = make(map[interface{}]interface{}) + } + if pb.output == nil { + pb.output = os.Stderr + } + + if pb.tmpl == nil { + pb.tmpl, pb.err = getTemplate(string(Default)) + if pb.err != nil { + return + } + } + if pb.vars[Terminal] == nil { + if f, ok := pb.output.(*os.File); ok { + if isTerminal(f.Fd()) || isCygwinTerminal(f.Fd()) { + pb.vars[Terminal] = true + } + } + } + if pb.vars[ReturnSymbol] == nil { + if tm, ok := pb.vars[Terminal].(bool); ok && tm { + pb.vars[ReturnSymbol] = "\r" + } + } + if pb.vars[Color] == nil { + if tm, ok := pb.vars[Terminal].(bool); ok && tm { + pb.vars[Color] = true + } + } + if pb.refreshRate == 0 { + pb.refreshRate = defaultRefreshRate + } + if f, ok := pb.output.(*os.File); ok { + pb.coutput = colorable.NewColorable(f) + } else { + pb.coutput = pb.output + } + pb.nocoutput = colorable.NewNonColorable(pb.output) +} + +// Start starts the bar +func (pb *ProgressBar) Start() *ProgressBar { + pb.mu.Lock() + defer pb.mu.Unlock() + if pb.finish != nil { + return pb + } + pb.configure() + pb.finished = false + pb.state = nil + pb.startTime = time.Now() + if st, ok := pb.vars[Static].(bool); ok && st { + return pb + } + pb.finish = make(chan struct{}) + pb.ticker = time.NewTicker(pb.refreshRate) + go pb.writer(pb.finish) + return pb +} + +func (pb *ProgressBar) writer(finish chan struct{}) { + for { + select { + case <-pb.ticker.C: + pb.write(false) + case <-finish: + pb.ticker.Stop() + pb.write(true) + finish <- struct{}{} + return + } + } +} + +// Write performs write to the output +func (pb *ProgressBar) Write() *ProgressBar { + pb.mu.RLock() + finished := pb.finished + pb.mu.RUnlock() + pb.write(finished) + return pb +} + +func (pb *ProgressBar) write(finish bool) { + result, width := pb.render() + if pb.Err() != nil { + return + } + if pb.GetBool(Terminal) { + if r := (width - CellCount(result)); r > 0 { + result += strings.Repeat(" ", r) + } + } + if ret, ok := pb.Get(ReturnSymbol).(string); ok { + result = ret + result + if finish && ret == "\r" { + result += "\n" + } + } + if pb.GetBool(Color) { + pb.coutput.Write([]byte(result)) + } else { + pb.nocoutput.Write([]byte(result)) + } +} + +// Total return current total bar value +func (pb *ProgressBar) Total() int64 { + return atomic.LoadInt64(&pb.total) +} + +// SetTotal sets the total bar value +func (pb *ProgressBar) SetTotal(value int64) *ProgressBar { + atomic.StoreInt64(&pb.total, value) + return pb +} + +// SetCurrent sets the current bar value +func (pb *ProgressBar) SetCurrent(value int64) *ProgressBar { + atomic.StoreInt64(&pb.current, value) + return pb +} + +// Current return current bar value +func (pb *ProgressBar) Current() int64 { + return atomic.LoadInt64(&pb.current) +} + +// Add adding given int64 value to bar value +func (pb *ProgressBar) Add64(value int64) *ProgressBar { + atomic.AddInt64(&pb.current, value) + return pb +} + +// Add adding given int value to bar value +func (pb *ProgressBar) Add(value int) *ProgressBar { + return pb.Add64(int64(value)) +} + +// Increment atomically increments the progress +func (pb *ProgressBar) Increment() *ProgressBar { + return pb.Add64(1) +} + +// Set sets any value by any key +func (pb *ProgressBar) Set(key, value interface{}) *ProgressBar { + pb.mu.Lock() + defer pb.mu.Unlock() + if pb.vars == nil { + pb.vars = make(map[interface{}]interface{}) + } + pb.vars[key] = value + return pb +} + +// Get return value by key +func (pb *ProgressBar) Get(key interface{}) interface{} { + pb.mu.RLock() + defer pb.mu.RUnlock() + if pb.vars == nil { + return nil + } + return pb.vars[key] +} + +// GetBool return value by key and try to convert there to boolean +// If value doesn't set or not boolean - return false +func (pb *ProgressBar) GetBool(key interface{}) bool { + if v, ok := pb.Get(key).(bool); ok { + return v + } + return false +} + +// SetWidth sets the bar width +// When given value <= 0 would be using the terminal width (if possible) or default value. +func (pb *ProgressBar) SetWidth(width int) *ProgressBar { + pb.mu.Lock() + pb.width = width + pb.mu.Unlock() + return pb +} + +// SetMaxWidth sets the bar maximum width +// When given value <= 0 would be using the terminal width (if possible) or default value. +func (pb *ProgressBar) SetMaxWidth(maxWidth int) *ProgressBar { + pb.mu.Lock() + pb.maxWidth = maxWidth + pb.mu.Unlock() + return pb +} + +// Width return the bar width +// It's current terminal width or settled over 'SetWidth' value. +func (pb *ProgressBar) Width() (width int) { + defer func() { + if r := recover(); r != nil { + width = defaultBarWidth + } + }() + pb.mu.RLock() + width = pb.width + maxWidth := pb.maxWidth + pb.mu.RUnlock() + if width <= 0 { + var err error + if width, err = terminalWidth(); err != nil { + return defaultBarWidth + } + } + if maxWidth > 0 && width > maxWidth { + width = maxWidth + } + return +} + +func (pb *ProgressBar) SetRefreshRate(dur time.Duration) *ProgressBar { + pb.mu.Lock() + if dur > 0 { + pb.refreshRate = dur + } + pb.mu.Unlock() + return pb +} + +// SetWriter sets the io.Writer. Bar will write in this writer +// By default this is os.Stderr +func (pb *ProgressBar) SetWriter(w io.Writer) *ProgressBar { + pb.mu.Lock() + pb.output = w + pb.configured = false + pb.configure() + pb.mu.Unlock() + return pb +} + +// StartTime return the time when bar started +func (pb *ProgressBar) StartTime() time.Time { + pb.mu.RLock() + defer pb.mu.RUnlock() + return pb.startTime +} + +// Format convert int64 to string according to the current settings +func (pb *ProgressBar) Format(v int64) string { + if pb.GetBool(Bytes) { + return formatBytes(v, pb.GetBool(SIBytesPrefix)) + } + return strconv.FormatInt(v, 10) +} + +// Finish stops the bar +func (pb *ProgressBar) Finish() *ProgressBar { + pb.mu.Lock() + if pb.finished { + pb.mu.Unlock() + return pb + } + finishChan := pb.finish + pb.finished = true + pb.mu.Unlock() + if finishChan != nil { + finishChan <- struct{}{} + <-finishChan + pb.mu.Lock() + pb.finish = nil + pb.mu.Unlock() + } + return pb +} + +// IsStarted indicates progress bar state +func (pb *ProgressBar) IsStarted() bool { + pb.mu.RLock() + defer pb.mu.RUnlock() + return pb.finish != nil +} + +// SetTemplateString sets ProgressBar tempate string and parse it +func (pb *ProgressBar) SetTemplateString(tmpl string) *ProgressBar { + pb.mu.Lock() + defer pb.mu.Unlock() + pb.tmpl, pb.err = getTemplate(tmpl) + return pb +} + +// SetTemplateString sets ProgressBarTempate and parse it +func (pb *ProgressBar) SetTemplate(tmpl ProgressBarTemplate) *ProgressBar { + return pb.SetTemplateString(string(tmpl)) +} + +// NewProxyReader creates a wrapper for given reader, but with progress handle +// Takes io.Reader or io.ReadCloser +// Also, it automatically switches progress bar to handle units as bytes +func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader { + pb.Set(Bytes, true) + return &Reader{r, pb} +} + +// NewProxyWriter creates a wrapper for given writer, but with progress handle +// Takes io.Writer or io.WriteCloser +// Also, it automatically switches progress bar to handle units as bytes +func (pb *ProgressBar) NewProxyWriter(r io.Writer) *Writer { + pb.Set(Bytes, true) + return &Writer{r, pb} +} + +func (pb *ProgressBar) render() (result string, width int) { + defer func() { + if r := recover(); r != nil { + pb.SetErr(fmt.Errorf("render panic: %v", r)) + } + }() + pb.rm.Lock() + defer pb.rm.Unlock() + pb.mu.Lock() + pb.configure() + if pb.state == nil { + pb.state = &State{ProgressBar: pb} + pb.buf = bytes.NewBuffer(nil) + } + if pb.startTime.IsZero() { + pb.startTime = time.Now() + } + pb.state.id++ + pb.state.finished = pb.finished + pb.state.time = time.Now() + pb.mu.Unlock() + + pb.state.width = pb.Width() + width = pb.state.width + pb.state.total = pb.Total() + pb.state.current = pb.Current() + pb.buf.Reset() + + if e := pb.tmpl.Execute(pb.buf, pb.state); e != nil { + pb.SetErr(e) + return "", 0 + } + + result = pb.buf.String() + + aec := len(pb.state.recalc) + if aec == 0 { + // no adaptive elements + return + } + + staticWidth := CellCount(result) - (aec * adElPlaceholderLen) + + if pb.state.Width()-staticWidth <= 0 { + result = strings.Replace(result, adElPlaceholder, "", -1) + result = StripString(result, pb.state.Width()) + } else { + pb.state.adaptiveElWidth = (width - staticWidth) / aec + for _, el := range pb.state.recalc { + result = strings.Replace(result, adElPlaceholder, el.ProgressElement(pb.state), 1) + } + } + pb.state.recalc = pb.state.recalc[:0] + return +} + +// SetErr sets error to the ProgressBar +// Error will be available over Err() +func (pb *ProgressBar) SetErr(err error) *ProgressBar { + pb.mu.Lock() + pb.err = err + pb.mu.Unlock() + return pb +} + +// Err return possible error +// When all ok - will be nil +// May contain template.Execute errors +func (pb *ProgressBar) Err() error { + pb.mu.RLock() + defer pb.mu.RUnlock() + return pb.err +} + +// String return currrent string representation of ProgressBar +func (pb *ProgressBar) String() string { + res, _ := pb.render() + return res +} + +// ProgressElement implements Element interface +func (pb *ProgressBar) ProgressElement(s *State, args ...string) string { + if s.IsAdaptiveWidth() { + pb.SetWidth(s.AdaptiveElWidth()) + } + return pb.String() +} + +// State represents the current state of bar +// Need for bar elements +type State struct { + *ProgressBar + + id uint64 + total, current int64 + width, adaptiveElWidth int + finished, adaptive bool + time time.Time + + recalc []Element +} + +// Id it's the current state identifier +// - incremental +// - starts with 1 +// - resets after finish/start +func (s *State) Id() uint64 { + return s.id +} + +// Total it's bar int64 total +func (s *State) Total() int64 { + return s.total +} + +// Value it's current value +func (s *State) Value() int64 { + return s.current +} + +// Width of bar +func (s *State) Width() int { + return s.width +} + +// AdaptiveElWidth - adaptive elements must return string with given cell count (when AdaptiveElWidth > 0) +func (s *State) AdaptiveElWidth() int { + return s.adaptiveElWidth +} + +// IsAdaptiveWidth returns true when element must be shown as adaptive +func (s *State) IsAdaptiveWidth() bool { + return s.adaptive +} + +// IsFinished return true when bar is finished +func (s *State) IsFinished() bool { + return s.finished +} + +// IsFirst return true only in first render +func (s *State) IsFirst() bool { + return s.id == 1 +} + +// Time when state was created +func (s *State) Time() time.Time { + return s.time +} diff --git a/vendor/github.com/cheggaaa/pb/v3/preset.go b/vendor/github.com/cheggaaa/pb/v3/preset.go new file mode 100644 index 000000000000..f5e2fff57e6e --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/preset.go @@ -0,0 +1,15 @@ +package pb + +var ( + // Full - preset with all default available elements + // Example: 'Prefix 20/100 [-->______] 20% 1 p/s ETA 1m Suffix' + Full ProgressBarTemplate = `{{string . "prefix"}}{{counters . }} {{bar . }} {{percent . }} {{speed . }} {{rtime . "ETA %s"}}{{string . "suffix"}}` + + // Default - preset like Full but without elapsed time + // Example: 'Prefix 20/100 [-->______] 20% 1 p/s ETA 1m Suffix' + Default ProgressBarTemplate = `{{string . "prefix"}}{{counters . }} {{bar . }} {{percent . }} {{speed . }}{{string . "suffix"}}` + + // Simple - preset without speed and any timers. Only counters, bar and percents + // Example: 'Prefix 20/100 [-->______] 20% Suffix' + Simple ProgressBarTemplate = `{{string . "prefix"}}{{counters . }} {{bar . }} {{percent . }}{{string . "suffix"}}` +) diff --git a/vendor/github.com/cheggaaa/pb/v3/speed.go b/vendor/github.com/cheggaaa/pb/v3/speed.go new file mode 100644 index 000000000000..17a6b1bfa553 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/speed.go @@ -0,0 +1,83 @@ +package pb + +import ( + "fmt" + "math" + "time" + + "github.com/VividCortex/ewma" +) + +var speedAddLimit = time.Second / 2 + +type speed struct { + ewma ewma.MovingAverage + lastStateId uint64 + prevValue, startValue int64 + prevTime, startTime time.Time +} + +func (s *speed) value(state *State) float64 { + if s.ewma == nil { + s.ewma = ewma.NewMovingAverage() + } + if state.IsFirst() || state.Id() < s.lastStateId { + s.reset(state) + return 0 + } + if state.Id() == s.lastStateId { + return s.ewma.Value() + } + if state.IsFinished() { + return s.absValue(state) + } + dur := state.Time().Sub(s.prevTime) + if dur < speedAddLimit { + return s.ewma.Value() + } + diff := math.Abs(float64(state.Value() - s.prevValue)) + lastSpeed := diff / dur.Seconds() + s.prevTime = state.Time() + s.prevValue = state.Value() + s.lastStateId = state.Id() + s.ewma.Add(lastSpeed) + return s.ewma.Value() +} + +func (s *speed) reset(state *State) { + s.lastStateId = state.Id() + s.startTime = state.Time() + s.prevTime = state.Time() + s.startValue = state.Value() + s.prevValue = state.Value() + s.ewma = ewma.NewMovingAverage() +} + +func (s *speed) absValue(state *State) float64 { + if dur := state.Time().Sub(s.startTime); dur > 0 { + return float64(state.Value()) / dur.Seconds() + } + return 0 +} + +func getSpeedObj(state *State) (s *speed) { + if sObj, ok := state.Get(speedObj).(*speed); ok { + return sObj + } + s = new(speed) + state.Set(speedObj, s) + return +} + +// ElementSpeed calculates current speed by EWMA +// Optionally can take one or two string arguments. +// First string will be used as value for format speed, default is "%s p/s". +// Second string will be used when speed not available, default is "? p/s" +// In template use as follows: {{speed .}} or {{speed . "%s per second"}} or {{speed . "%s ps" "..."} +var ElementSpeed ElementFunc = func(state *State, args ...string) string { + sp := getSpeedObj(state).value(state) + if sp == 0 { + return argsHelper(args).getNotEmptyOr(1, "? p/s") + } + return fmt.Sprintf(argsHelper(args).getNotEmptyOr(0, "%s p/s"), state.Format(int64(round(sp)))) +} diff --git a/vendor/github.com/cheggaaa/pb/v3/template.go b/vendor/github.com/cheggaaa/pb/v3/template.go new file mode 100644 index 000000000000..ecfc271121ec --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/template.go @@ -0,0 +1,88 @@ +package pb + +import ( + "math/rand" + "sync" + "text/template" + + "github.com/fatih/color" +) + +// ProgressBarTemplate that template string +type ProgressBarTemplate string + +// New creates new bar from template +func (pbt ProgressBarTemplate) New(total int) *ProgressBar { + return New(total).SetTemplate(pbt) +} + +// Start64 create and start new bar with given int64 total value +func (pbt ProgressBarTemplate) Start64(total int64) *ProgressBar { + return New64(total).SetTemplate(pbt).Start() +} + +// Start create and start new bar with given int total value +func (pbt ProgressBarTemplate) Start(total int) *ProgressBar { + return pbt.Start64(int64(total)) +} + +var templateCacheMu sync.Mutex +var templateCache = make(map[string]*template.Template) + +var defaultTemplateFuncs = template.FuncMap{ + // colors + "black": color.New(color.FgBlack).SprintFunc(), + "red": color.New(color.FgRed).SprintFunc(), + "green": color.New(color.FgGreen).SprintFunc(), + "yellow": color.New(color.FgYellow).SprintFunc(), + "blue": color.New(color.FgBlue).SprintFunc(), + "magenta": color.New(color.FgMagenta).SprintFunc(), + "cyan": color.New(color.FgCyan).SprintFunc(), + "white": color.New(color.FgWhite).SprintFunc(), + "resetcolor": color.New(color.Reset).SprintFunc(), + "rndcolor": rndcolor, + "rnd": rnd, +} + +func getTemplate(tmpl string) (t *template.Template, err error) { + templateCacheMu.Lock() + defer templateCacheMu.Unlock() + t = templateCache[tmpl] + if t != nil { + // found in cache + return + } + t = template.New("") + fillTemplateFuncs(t) + _, err = t.Parse(tmpl) + if err != nil { + t = nil + return + } + templateCache[tmpl] = t + return +} + +func fillTemplateFuncs(t *template.Template) { + t.Funcs(defaultTemplateFuncs) + emf := make(template.FuncMap) + elementsM.Lock() + for k, v := range elements { + emf[k] = v + } + elementsM.Unlock() + t.Funcs(emf) + return +} + +func rndcolor(s string) string { + c := rand.Intn(int(color.FgWhite-color.FgBlack)) + int(color.FgBlack) + return color.New(color.Attribute(c)).Sprint(s) +} + +func rnd(args ...string) string { + if len(args) == 0 { + return "" + } + return args[rand.Intn(len(args))] +} diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term.go new file mode 100644 index 000000000000..02b52797e468 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term.go @@ -0,0 +1,56 @@ +package termutil + +import ( + "errors" + "os" + "os/signal" + "sync" +) + +var echoLocked bool +var echoLockMutex sync.Mutex +var errLocked = errors.New("terminal locked") + +// RawModeOn switches terminal to raw mode +func RawModeOn() (quit chan struct{}, err error) { + echoLockMutex.Lock() + defer echoLockMutex.Unlock() + if echoLocked { + err = errLocked + return + } + if err = lockEcho(); err != nil { + return + } + echoLocked = true + quit = make(chan struct{}, 1) + go catchTerminate(quit) + return +} + +// RawModeOff restore previous terminal state +func RawModeOff() (err error) { + echoLockMutex.Lock() + defer echoLockMutex.Unlock() + if !echoLocked { + return + } + if err = unlockEcho(); err != nil { + return + } + echoLocked = false + return +} + +// listen exit signals and restore terminal state +func catchTerminate(quit chan struct{}) { + sig := make(chan os.Signal, 1) + signal.Notify(sig, unlockSignals...) + defer signal.Stop(sig) + select { + case <-quit: + RawModeOff() + case <-sig: + RawModeOff() + } +} diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_appengine.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_appengine.go new file mode 100644 index 000000000000..4b7b20e6b6fa --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_appengine.go @@ -0,0 +1,11 @@ +// +build appengine + +package termutil + +import "errors" + +// terminalWidth returns width of the terminal, which is not supported +// and should always failed on appengine classic which is a sandboxed PaaS. +func TerminalWidth() (int, error) { + return 0, errors.New("Not supported") +} diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_bsd.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_bsd.go new file mode 100644 index 000000000000..272659a12568 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_bsd.go @@ -0,0 +1,9 @@ +// +build darwin freebsd netbsd openbsd dragonfly +// +build !appengine + +package termutil + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA +const ioctlWriteTermios = syscall.TIOCSETA diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_linux.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_linux.go new file mode 100644 index 000000000000..2f59e53e162b --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_linux.go @@ -0,0 +1,7 @@ +// +build linux +// +build !appengine + +package termutil + +const ioctlReadTermios = 0x5401 // syscall.TCGETS +const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_nix.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_nix.go new file mode 100644 index 000000000000..14277e71ff96 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_nix.go @@ -0,0 +1,8 @@ +// +build linux darwin freebsd netbsd openbsd dragonfly +// +build !appengine + +package termutil + +import "syscall" + +const sysIoctl = syscall.SYS_IOCTL diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_plan9.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_plan9.go new file mode 100644 index 000000000000..f3934c6ec8fe --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_plan9.go @@ -0,0 +1,50 @@ +package termutil + +import ( + "errors" + "os" + "syscall" +) + +var ( + consctl *os.File + + // Plan 9 doesn't have syscall.SIGQUIT + unlockSignals = []os.Signal{ + os.Interrupt, syscall.SIGTERM, syscall.SIGKILL, + } +) + +// TerminalWidth returns width of the terminal. +func TerminalWidth() (int, error) { + return 0, errors.New("Not supported") +} + +func lockEcho() error { + if consctl != nil { + return errors.New("consctl already open") + } + var err error + consctl, err = os.OpenFile("/dev/consctl", os.O_WRONLY, 0) + if err != nil { + return err + } + _, err = consctl.WriteString("rawon") + if err != nil { + consctl.Close() + consctl = nil + return err + } + return nil +} + +func unlockEcho() error { + if consctl == nil { + return nil + } + if err := consctl.Close(); err != nil { + return err + } + consctl = nil + return nil +} diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_solaris.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_solaris.go new file mode 100644 index 000000000000..fc96c2b7f80d --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_solaris.go @@ -0,0 +1,8 @@ +// +build solaris +// +build !appengine + +package termutil + +const ioctlReadTermios = 0x5401 // syscall.TCGETS +const ioctlWriteTermios = 0x5402 // syscall.TCSETS +const sysIoctl = 54 diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_win.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_win.go new file mode 100644 index 000000000000..c867d27227cd --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_win.go @@ -0,0 +1,155 @@ +// +build windows + +package termutil + +import ( + "fmt" + "os" + "os/exec" + "strconv" + "syscall" + "unsafe" +) + +var ( + tty = os.Stdin + + unlockSignals = []os.Signal{ + os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL, + } +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + + // GetConsoleScreenBufferInfo retrieves information about the + // specified console screen buffer. + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + + // GetConsoleMode retrieves the current input mode of a console's + // input buffer or the current output mode of a console screen buffer. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx + getConsoleMode = kernel32.NewProc("GetConsoleMode") + + // SetConsoleMode sets the input mode of a console's input buffer + // or the output mode of a console screen buffer. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + setConsoleMode = kernel32.NewProc("SetConsoleMode") + + // SetConsoleCursorPosition sets the cursor position in the + // specified console screen buffer. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx + setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + + mingw = isMingw() +) + +type ( + // Defines the coordinates of the upper left and lower right corners + // of a rectangle. + // See + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx + smallRect struct { + Left, Top, Right, Bottom int16 + } + + // Defines the coordinates of a character cell in a console screen + // buffer. The origin of the coordinate system (0,0) is at the top, left cell + // of the buffer. + // See + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx + coordinates struct { + X, Y int16 + } + + word int16 + + // Contains information about a console screen buffer. + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx + consoleScreenBufferInfo struct { + dwSize coordinates + dwCursorPosition coordinates + wAttributes word + srWindow smallRect + dwMaximumWindowSize coordinates + } +) + +// TerminalWidth returns width of the terminal. +func TerminalWidth() (width int, err error) { + if mingw { + return termWidthTPut() + } + return termWidthCmd() +} + +func termWidthCmd() (width int, err error) { + var info consoleScreenBufferInfo + _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) + if e != 0 { + return 0, error(e) + } + return int(info.dwSize.X) - 1, nil +} + +func isMingw() bool { + return os.Getenv("MINGW_PREFIX") != "" || os.Getenv("MSYSTEM") == "MINGW64" +} + +func termWidthTPut() (width int, err error) { + // TODO: maybe anybody knows a better way to get it on mintty... + var res []byte + cmd := exec.Command("tput", "cols") + cmd.Stdin = os.Stdin + if res, err = cmd.CombinedOutput(); err != nil { + return 0, fmt.Errorf("%s: %v", string(res), err) + } + if len(res) > 1 { + res = res[:len(res)-1] + } + return strconv.Atoi(string(res)) +} + +func getCursorPos() (pos coordinates, err error) { + var info consoleScreenBufferInfo + _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) + if e != 0 { + return info.dwCursorPosition, error(e) + } + return info.dwCursorPosition, nil +} + +func setCursorPos(pos coordinates) error { + _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0) + if e != 0 { + return error(e) + } + return nil +} + +var oldState word + +func lockEcho() (err error) { + if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 { + err = fmt.Errorf("Can't get terminal settings: %v", e) + return + } + + newState := oldState + const ENABLE_ECHO_INPUT = 0x0004 + const ENABLE_LINE_INPUT = 0x0002 + newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)) + if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 { + err = fmt.Errorf("Can't set terminal settings: %v", e) + return + } + return +} + +func unlockEcho() (err error) { + if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 { + err = fmt.Errorf("Can't set terminal settings") + } + return +} diff --git a/vendor/github.com/cheggaaa/pb/v3/termutil/term_x.go b/vendor/github.com/cheggaaa/pb/v3/termutil/term_x.go new file mode 100644 index 000000000000..69377554902d --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/termutil/term_x.go @@ -0,0 +1,76 @@ +// +build linux darwin freebsd netbsd openbsd solaris dragonfly +// +build !appengine + +package termutil + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +var ( + tty *os.File + + unlockSignals = []os.Signal{ + os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL, + } +) + +type window struct { + Row uint16 + Col uint16 + Xpixel uint16 + Ypixel uint16 +} + +func init() { + var err error + tty, err = os.Open("/dev/tty") + if err != nil { + tty = os.Stdin + } +} + +// TerminalWidth returns width of the terminal. +func TerminalWidth() (int, error) { + w := new(window) + res, _, err := syscall.Syscall(sysIoctl, + tty.Fd(), + uintptr(syscall.TIOCGWINSZ), + uintptr(unsafe.Pointer(w)), + ) + if int(res) == -1 { + return 0, err + } + return int(w.Col), nil +} + +var oldState syscall.Termios + +func lockEcho() (err error) { + fd := tty.Fd() + if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { + err = fmt.Errorf("Can't get terminal settings: %v", e) + return + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + newState.Lflag |= syscall.ICANON | syscall.ISIG + newState.Iflag |= syscall.ICRNL + if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { + err = fmt.Errorf("Can't set terminal settings: %v", e) + return + } + return +} + +func unlockEcho() (err error) { + fd := tty.Fd() + if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { + err = fmt.Errorf("Can't set terminal settings") + } + return +} diff --git a/vendor/github.com/cheggaaa/pb/v3/util.go b/vendor/github.com/cheggaaa/pb/v3/util.go new file mode 100644 index 000000000000..078123420f65 --- /dev/null +++ b/vendor/github.com/cheggaaa/pb/v3/util.go @@ -0,0 +1,115 @@ +package pb + +import ( + "bytes" + "fmt" + "github.com/mattn/go-runewidth" + "math" + "regexp" + //"unicode/utf8" +) + +const ( + _KiB = 1024 + _MiB = 1048576 + _GiB = 1073741824 + _TiB = 1099511627776 + + _kB = 1e3 + _MB = 1e6 + _GB = 1e9 + _TB = 1e12 +) + +var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") + +func CellCount(s string) int { + n := runewidth.StringWidth(s) + for _, sm := range ctrlFinder.FindAllString(s, -1) { + n -= runewidth.StringWidth(sm) + } + return n +} + +func StripString(s string, w int) string { + l := CellCount(s) + if l <= w { + return s + } + var buf = bytes.NewBuffer(make([]byte, 0, len(s))) + StripStringToBuffer(s, w, buf) + return buf.String() +} + +func StripStringToBuffer(s string, w int, buf *bytes.Buffer) { + var seqs = ctrlFinder.FindAllStringIndex(s, -1) +mainloop: + for i, r := range s { + for _, seq := range seqs { + if i >= seq[0] && i < seq[1] { + buf.WriteRune(r) + continue mainloop + } + } + if rw := CellCount(string(r)); rw <= w { + w -= rw + buf.WriteRune(r) + } else { + break + } + } + for w > 0 { + buf.WriteByte(' ') + w-- + } + return +} + +func round(val float64) (newVal float64) { + roundOn := 0.5 + places := 0 + var round float64 + pow := math.Pow(10, float64(places)) + digit := pow * val + _, div := math.Modf(digit) + if div >= roundOn { + round = math.Ceil(digit) + } else { + round = math.Floor(digit) + } + newVal = round / pow + return +} + +// Convert bytes to human readable string. Like a 2 MiB, 64.2 KiB, or 2 MB, 64.2 kB +// if useSIPrefix is set to true +func formatBytes(i int64, useSIPrefix bool) (result string) { + if !useSIPrefix { + switch { + case i >= _TiB: + result = fmt.Sprintf("%.02f TiB", float64(i)/_TiB) + case i >= _GiB: + result = fmt.Sprintf("%.02f GiB", float64(i)/_GiB) + case i >= _MiB: + result = fmt.Sprintf("%.02f MiB", float64(i)/_MiB) + case i >= _KiB: + result = fmt.Sprintf("%.02f KiB", float64(i)/_KiB) + default: + result = fmt.Sprintf("%d B", i) + } + } else { + switch { + case i >= _TB: + result = fmt.Sprintf("%.02f TB", float64(i)/_TB) + case i >= _GB: + result = fmt.Sprintf("%.02f GB", float64(i)/_GB) + case i >= _MB: + result = fmt.Sprintf("%.02f MB", float64(i)/_MB) + case i >= _kB: + result = fmt.Sprintf("%.02f kB", float64(i)/_kB) + default: + result = fmt.Sprintf("%d B", i) + } + } + return +} diff --git a/vendor/github.com/gookit/color/.gitignore b/vendor/github.com/gookit/color/.gitignore new file mode 100644 index 000000000000..5efa5e3f0f11 --- /dev/null +++ b/vendor/github.com/gookit/color/.gitignore @@ -0,0 +1,20 @@ +*.log +*.swp +.idea +*.patch +### Go template +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +.DS_Store +app +demo diff --git a/vendor/github.com/gookit/color/.travis.yml b/vendor/github.com/gookit/color/.travis.yml new file mode 100644 index 000000000000..386f6dbc5698 --- /dev/null +++ b/vendor/github.com/gookit/color/.travis.yml @@ -0,0 +1,21 @@ +language: go +env: + GO111MODULE: on + +go: +# - '1.9' +# - '1.10' + - '1.11' + - '1.12' + - '1.13' + - '1.14' + - '1.15' + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover + +script: + # gofmt -w ./ + # - go test -v -cover + - $HOME/gopath/bin/goveralls -v -service=travis-ci diff --git a/vendor/github.com/gookit/color/LICENSE b/vendor/github.com/gookit/color/LICENSE new file mode 100644 index 000000000000..d839cdc1ad15 --- /dev/null +++ b/vendor/github.com/gookit/color/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2016 inhere + +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. \ No newline at end of file diff --git a/vendor/github.com/gookit/color/README.md b/vendor/github.com/gookit/color/README.md new file mode 100644 index 000000000000..d68432ebc61d --- /dev/null +++ b/vendor/github.com/gookit/color/README.md @@ -0,0 +1,422 @@ +# CLI Color + +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square) +[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/51b28c5f7ffe4cc2b0f12ecf25ed247f)](https://app.codacy.com/app/inhere/color) +[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview) +[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color) +[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color) +[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color) + +A command-line color library with true color support, universal API methods and Windows support. + +> **[中文说明](README.zh-CN.md)** + +Basic color preview: + +![basic-color](_examples/images/basic-color2.png) + +Now, 256 colors and RGB colors have also been supported to work in Windows CMD and PowerShell: + +![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg) + +## Features + + - Simple to use, zero dependencies + - Supports rich color output: 16-color, 256-color, true color (24-bit, RGB) + - 16-color output is the most commonly used and most widely supported, working on any Windows version + - Since `v1.2.4` **the 256-color, true color (24-bit) support windows CMD and PowerShell** + - See [this gist](https://gist.github.com/XVilka/8346728) for information on true color support + - Generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf` + - Supports HTML tag-style color rendering, such as `message`. Support working on windows `cmd` `powerShell` + - Basic colors: `Bold`, `Black`, `White`, `Gray`, `Red`, `Green`, `Yellow`, `Blue`, `Magenta`, `Cyan` + - Additional styles: `Info`, `Note`, `Light`, `Error`, `Danger`, `Notice`, `Success`, `Comment`, `Primary`, `Warning`, `Question`, `Secondary` + +## GoDoc + + - [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1) + - [godoc for github](https://pkg.go.dev/github.com/gookit/color) + +## Install + +```bash +go get github.com/gookit/color +``` + +## Quick start + +```go +package main + +import ( + "fmt" + + "github.com/gookit/color" +) + +func main() { + // quick use like fmt.Print* + color.Red.Println("Simple to use color") + color.Green.Print("Simple to use color\n") + color.Cyan.Printf("Simple to use %s\n", "color") + color.Yellow.Printf("Simple to use %s\n", "color") + + // use like func + red := color.FgRed.Render + green := color.FgGreen.Render + fmt.Printf("%s line %s library\n", red("Command"), green("color")) + + // custom color + color.New(color.FgWhite, color.BgBlack).Println("custom color style") + + // can also: + color.Style{color.FgCyan, color.OpBold}.Println("custom color style") + + // internal theme/style: + color.Info.Tips("message") + color.Info.Prompt("message") + color.Info.Println("message") + color.Warn.Println("message") + color.Error.Println("message") + + // use style tag + color.Print("hello, welcome\n") + + // apply a style tag + color.Tag("info").Println("info style text") + + // prompt message + color.Info.Prompt("prompt style message") + color.Warn.Prompt("prompt style message") + + // tips message + color.Info.Tips("tips style message") + color.Warn.Tips("tips style message") +} +``` + +Run demo: `go run ./_examples/demo.go` + +![colored-out](_examples/images/color-demo.jpg) + +## Custom Build Color + +```go +// Only use foreground color +color.FgCyan.Printf("Simple to use %s\n", "color") +// Only use background color +color.BgRed.Printf("Simple to use %s\n", "color") + +// Full custom: foreground, background, option +myStyle := color.New(color.FgWhite, color.BgBlack, color.OpBold) +myStyle.Println("custom color style") + +// can also: +color.Style{color.FgCyan, color.OpBold}.Println("custom color style") +``` + +custom set console settings: + +```go +// set console color +color.Set(color.FgCyan) + +// print message +fmt.Print("message") + +// reset console settings +color.Reset() +``` + +## Basic Color + +Supported on any Windows version. + + - `color.Bold` + - `color.Black` + - `color.White` + - `color.Gray` + - `color.Red` + - `color.Green` + - `color.Yellow` + - `color.Blue` + - `color.Magenta` + - `color.Cyan` + +```go +color.Bold.Println("bold message") +color.Yellow.Println("yellow message") +``` + +Run demo: `go run ./_examples/basiccolor.go` + +![basic-color](_examples/images/basic-color.png) + +## Additional styles + +Supported on any Windows version. + + - `color.Info` + - `color.Note` + - `color.Warn` + - `color.Light` + - `color.Error` + - `color.Danger` + - `color.Debug` + - `color.Notice` + - `color.Success` + - `color.Comment` + - `color.Primary` + - `color.Question` + - `color.Secondary` + +### Basic Style + +print message use defined style: + +```go +color.Info.Println("Info message") +color.Note.Println("Note message") +color.Notice.Println("Notice message") +color.Error.Println("Error message") +color.Danger.Println("Danger message") +color.Warn.Println("Warn message") +color.Debug.Println("Debug message") +color.Primary.Println("Primary message") +color.Question.Println("Question message") +color.Secondary.Println("Secondary message") +``` + +Run demo: `go run ./_examples/theme_basic.go` + +![theme-basic](_examples/images/theme-basic.png) + +### Tips Style + +```go +color.Info.Tips("Info tips message") +color.Note.Tips("Note tips message") +color.Notice.Tips("Notice tips message") +color.Error.Tips("Error tips message") +color.Danger.Tips("Danger tips message") +color.Warn.Tips("Warn tips message") +color.Debug.Tips("Debug tips message") +color.Primary.Tips("Primary tips message") +color.Question.Tips("Question tips message") +color.Secondary.Tips("Secondary tips message") +``` + +Run demo: `go run ./_examples/theme_tips.go` + +![theme-tips](_examples/images/theme-tips.png) + +### Prompt Style + +```go +color.Info.Prompt("Info prompt message") +color.Note.Prompt("Note prompt message") +color.Notice.Prompt("Notice prompt message") +color.Error.Prompt("Error prompt message") +color.Danger.Prompt("Danger prompt message") +color.Warn.Prompt("Warn prompt message") +color.Debug.Prompt("Debug prompt message") +color.Primary.Prompt("Primary prompt message") +color.Question.Prompt("Question prompt message") +color.Secondary.Prompt("Secondary prompt message") +``` + +Run demo: `go run ./_examples/theme_prompt.go` + +![theme-prompt](_examples/images/theme-prompt.png) + +### Block Style + +```go +color.Info.Block("Info block message") +color.Note.Block("Note block message") +color.Notice.Block("Notice block message") +color.Error.Block("Error block message") +color.Danger.Block("Danger block message") +color.Warn.Block("Warn block message") +color.Debug.Block("Debug block message") +color.Primary.Block("Primary block message") +color.Question.Block("Question block message") +color.Secondary.Block("Secondary block message") +``` + +Run demo: `go run ./_examples/theme_block.go` + +![theme-block](_examples/images/theme-block.png) + +## HTML-like tag usage + +**Supported** on Windows `cmd.exe` `PowerShell` . + +```go +// use style tag +color.Print("hello, welcome") +color.Println("hello") +color.Println("hello") +color.Println("hello") + +// custom color attributes +color.Print("hello, welcome\n") +``` + +- `color.Tag` + +```go +// set a style tag +color.Tag("info").Print("info style text") +color.Tag("info").Printf("%s style text", "info") +color.Tag("info").Println("info style text") +``` + +Run demo: `go run ./_examples/colortag.go` + +![color-tags](_examples/images/color-tags.png) + +## 256-color usage + +> 256 colors support Windows CMD, PowerShell environment after `v1.2.4` + +### Set the foreground or background color + +- `color.C256(val uint8, isBg ...bool) Color256` + +```go +c := color.C256(132) // fg color +c.Println("message") +c.Printf("format %s", "message") + +c := color.C256(132, true) // bg color +c.Println("message") +c.Printf("format %s", "message") +``` + +### Use a 256-color style + +Can be used to set foreground and background colors at the same time. + +- `color.S256(fgAndBg ...uint8) *Style256` + +```go +s := color.S256(32, 203) +s.Println("message") +s.Printf("format %s", "message") +``` + +Run demo: `go run ./_examples/color256.go` + +![color-tags](_examples/images/color-256.png) + +## Use RGB color + +> RGB colors support Windows `CMD`, `PowerShell` environment after `v1.2.4` + +**Preview:** + +> Run demo: `Run demo: go run ./_examples/color_rgb.go` + +![color-rgb](_examples/images/color-rgb.png) + +example: + +```go +color.RGB(30, 144, 255).Println("message. use RGB number") + +color.HEX("#1976D2").Println("blue-darken") +color.HEX("#D50000", true).Println("red-accent. use HEX style") + +color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number") +color.HEXStyle("eee", "D50000").Println("deep-purple color") +``` + +### Set the foreground or background color + + - `color.RGB(r, g, b uint8, isBg ...bool) RGBColor` + +```go +c := color.RGB(30,144,255) // fg color +c.Println("message") +c.Printf("format %s", "message") + +c := color.RGB(30,144,255, true) // bg color +c.Println("message") +c.Printf("format %s", "message") +``` + + Create a style from an hexadecimal color string: + + - `color.HEX(hex string, isBg ...bool) RGBColor` + +```go +c := color.HEX("ccc") // can also: "cccccc" "#cccccc" +c.Println("message") +c.Printf("format %s", "message") + +c = color.HEX("aabbcc", true) // as bg color +c.Println("message") +c.Printf("format %s", "message") +``` + +### Use an RGB color style + +Can be used to set the foreground and background colors at the same time. + +- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle` + +```go +s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23)) +s.Println("message") +s.Printf("format %s", "message") +``` + + Create a style from an hexadecimal color string: + +- `color.HEXStyle(fg string, bg ...string) *RGBStyle` + +```go +s := color.HEXStyle("11aa23", "eee") +s.Println("message") +s.Printf("format %s", "message") +``` + +## Func refer + +there are some useful functions reference + +- `Disable()` disable color render +- `SetOutput(io.Writer)` custom set the colored text output writer +- `ForceOpenColor()` force open color render +- `ClearCode(str string) string` Use for clear color codes +- `ClearTag(s string) string` clear all color html-tag for a string +- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin +- `HexToRgb(hex string) (rgb []int)` Convert hex color string to RGB numbers +- `RgbToHex(rgb []int) string` Convert RGB to hex code + +## Gookit packages + + - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files + - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP + - [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands + - [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go + - [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached. + - [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags + - [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support + - [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data + - [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data + - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more + - More please see https://github.com/gookit + +## See also + + - [`issue9/term`](https://github.com/issue9/term) + - [`beego/bee`](https://github.com/beego/bee) + - [`inhere/console`](https://github.com/inhere/php-console) + - [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code) + +## License + +[MIT](/LICENSE) diff --git a/vendor/github.com/gookit/color/README.zh-CN.md b/vendor/github.com/gookit/color/README.zh-CN.md new file mode 100644 index 000000000000..9d2b998ce4de --- /dev/null +++ b/vendor/github.com/gookit/color/README.zh-CN.md @@ -0,0 +1,392 @@ +# CLI Color + +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square) +[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/51b28c5f7ffe4cc2b0f12ecf25ed247f)](https://app.codacy.com/app/inhere/color) +[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview) +[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color) +[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color) +[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color) + +Golang下的命令行色彩使用库, 拥有丰富的色彩渲染输出,通用的API方法,兼容Windows系统 + +> **[EN README](README.md)** + +基本颜色预览: + +![basic-color](_examples/images/basic-color2.png) + +现在,256色和RGB色彩也已经支持windows CMD和PowerShell中工作: + +![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg) + +## 功能特色 + + - 使用简单方便,无其他依赖 + - 支持丰富的颜色输出, 16色(4bit),256色(8bit),RGB色彩(24bit, RGB) + - 16色(4bit)是最常用和支持最广的,支持Windows `cmd.exe` + - 自 `v1.2.4` 起 **256色(8bit),RGB色彩(24bit)均支持windows CMD和PowerShell终端** + - 请查看 [this gist](https://gist.github.com/XVilka/8346728) 了解支持RGB色彩的终端 + - 通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf` + - 同时支持html标签式的颜色渲染. eg: `message` + - 基础色彩: `Bold` `Black` `White` `Gray` `Red` `Green` `Yellow` `Blue` `Magenta` `Cyan` + - 扩展风格: `Info` `Note` `Light` `Error` `Danger` `Notice` `Success` `Comment` `Primary` `Warning` `Question` `Secondary` + - 支持Linux、Mac同时兼容Windows系统环境 + +## GoDoc + + - [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1) + - [godoc for github](https://pkg.go.dev/github.com/gookit/color) + +## 安装 + +```bash +go get github.com/gookit/color +``` + +## 快速开始 + +如下,引入当前包就可以快速的使用 + +```go +package main + +import ( + "fmt" + + "github.com/gookit/color" +) + +func main() { + // 简单快速的使用,跟 fmt.Print* 类似 + color.Red.Println("Simple to use color") + color.Green.Print("Simple to use color\n") + color.Cyan.Printf("Simple to use %s\n", "color") + color.Yellow.Printf("Simple to use %s\n", "color") + + // use like func + red := color.FgRed.Render + green := color.FgGreen.Render + fmt.Printf("%s line %s library\n", red("Command"), green("color")) + + // 自定义颜色 + color.New(color.FgWhite, color.BgBlack).Println("custom color style") + + // 也可以: + color.Style{color.FgCyan, color.OpBold}.Println("custom color style") + + // internal style: + color.Info.Println("message") + color.Warn.Println("message") + color.Error.Println("message") + + // 使用颜色标签 + color.Print("hello, welcome\n") + + // apply a style tag + color.Tag("info").Println("info style text") + + // prompt message + color.Info.Prompt("prompt style message") + color.Warn.Prompt("prompt style message") + + // tips message + color.Info.Tips("tips style message") + color.Warn.Tips("tips style message") +} +``` + +> 运行 demo: `go run ./_examples/demo.go` + +![colored-out](_examples/images/color-demo.jpg) + +## 构建风格 + +```go +// 仅设置前景色 +color.FgCyan.Printf("Simple to use %s\n", "color") +// 仅设置背景色 +color.BgRed.Printf("Simple to use %s\n", "color") + +// 完全自定义: 前景色 背景色 选项 +style := color.New(color.FgWhite, color.BgBlack, color.OpBold) +style.Println("custom color style") + +// 也可以: +color.Style{color.FgCyan, color.OpBold}.Println("custom color style") +``` + +直接设置控制台属性: + +```go +// 设置console颜色 +color.Set(color.FgCyan) + +// 输出信息 +fmt.Print("message") + +// 重置console颜色 +color.Reset() +``` + +> 当然,color已经内置丰富的色彩风格支持 + +## 基础颜色方法 + +> 支持在windows `cmd.exe` 使用 + + - `color.Bold` + - `color.Black` + - `color.White` + - `color.Gray` + - `color.Red` + - `color.Green` + - `color.Yellow` + - `color.Blue` + - `color.Magenta` + - `color.Cyan` + +```go +color.Bold.Println("bold message") +color.Yellow.Println("yellow message") +``` + +> 运行demo: `go run ./_examples/basiccolor.go` + +![basic-color](_examples/images/basic-color.png) + +## 扩展风格方法 + +> 支持在windows `cmd.exe` 使用 + + - `color.Info` + - `color.Note` + - `color.Light` + - `color.Error` + - `color.Danger` + - `color.Notice` + - `color.Success` + - `color.Comment` + - `color.Primary` + - `color.Warning` + - `color.Question` + - `color.Secondary` + +### 基础风格 + +```go +// print message +color.Info.Println("Info message") +color.Success.Println("Success message") +``` + +Run demo: `go run ./_examples/theme_basic.go` + +![theme-basic](_examples/images/theme-basic.png) + +### 简约提示风格 + +```go +color.Info.Tips("tips style message") +color.Warn.Tips("tips style message") +``` + +Run demo: `go run ./_examples/theme_tips.go` + +![theme-tips](_examples/images/theme-tips.png) + +### 着重提示风格 + +```go +color.Info.Prompt("prompt style message") +color.Warn.Prompt("prompt style message") +``` + +Run demo: `go run ./_examples/theme_prompt.go` + +![theme-prompt](_examples/images/theme-prompt.png) + +### 强调提示风格 + +```go +color.Info.Block("prompt style message") +color.Warn.Block("prompt style message") +``` + +Run demo: `go run ./_examples/theme_block.go` + +![theme-block](_examples/images/theme-block.png) + +### 使用颜色标签 + +> **支持** 在windows `cmd.exe` `PowerShell` 使用 + +使用内置的颜色标签,可以非常方便简单的构建自己需要的任何格式 + +```go +// 使用内置的 color tag +color.Print("hello, welcome") +color.Println("hello") +color.Println("hello") +color.Println("hello") + +// 自定义颜色属性 +color.Print("hello, welcome\n") +``` + + - 使用 `color.Tag` + +给后面输出的文本信息加上给定的颜色风格标签 + +```go +// set a style tag +color.Tag("info").Print("info style text") +color.Tag("info").Printf("%s style text", "info") +color.Tag("info").Println("info style text") +``` + +> 运行 demo: `go run ./_examples/colortag.go` + +![color-tags](_examples/images/color-tags.png) + +## 256色使用 + +> 256色彩在 `v1.2.4` 后支持Windows CMD,PowerShell 环境 + +### 使用前景或后景色 + + - `color.C256(val uint8, isBg ...bool) Color256` + +```go +c := color.C256(132) // fg color +c.Println("message") +c.Printf("format %s", "message") + +c := color.C256(132, true) // bg color +c.Println("message") +c.Printf("format %s", "message") +``` + +### 使用风格 + +> 可同时设置前景和背景色 + + - `color.S256(fgAndBg ...uint8) *Style256` + +```go +s := color.S256(32, 203) +s.Println("message") +s.Printf("format %s", "message") +``` + +> 运行 demo: `go run ./_examples/color256.go` + +![color-tags](_examples/images/color-256.png) + +## RGB色彩使用 + +> RGB色彩在 `v1.2.4` 后支持 Windows `CMD`, `PowerShell` 环境 + +**效果预览:** + +> 运行 demo: `Run demo: go run ./_examples/color_rgb.go` + +![color-rgb](_examples/images/color-rgb.png) + +代码示例: + +```go +color.RGB(30, 144, 255).Println("message. use RGB number") + +color.HEX("#1976D2").Println("blue-darken") +color.HEX("#D50000", true).Println("red-accent. use HEX style") + +color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number") +color.HEXStyle("eee", "D50000").Println("deep-purple color") +``` + +### 使用前景或后景色 + + - `color.RGB(r, g, b uint8, isBg ...bool) RGBColor` + +```go +c := color.RGB(30,144,255) // fg color +c.Println("message") +c.Printf("format %s", "message") + +c := color.RGB(30,144,255, true) // bg color +c.Println("message") +c.Printf("format %s", "message") +``` + + - `color.HEX(hex string, isBg ...bool) RGBColor` 从16进制颜色创建 + +```go +c := color.HEX("ccc") // 也可以写为: "cccccc" "#cccccc" +c.Println("message") +c.Printf("format %s", "message") + +c = color.HEX("aabbcc", true) // as bg color +c.Println("message") +c.Printf("format %s", "message") +``` + +### 使用风格 + +> 可同时设置前景和背景色 + + - `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle` + +```go +s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23)) +s.Println("message") +s.Printf("format %s", "message") +``` + +- `color.HEXStyle(fg string, bg ...string) *RGBStyle` 从16进制颜色创建 + +```go +s := color.HEXStyle("11aa23", "eee") +s.Println("message") +s.Printf("format %s", "message") +``` + +## 方法参考 + +一些有用的工具方法参考 + +- `Disable()` disable color render +- `SetOutput(io.Writer)` custom set the colored text output writer +- `ForceOpenColor()` force open color render +- `ClearCode(str string) string` Use for clear color codes +- `ClearTag(s string) string` clear all color html-tag for a string +- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin +- `HexToRgb(hex string) (rgb []int)` Convert hex color string to RGB numbers +- `RgbToHex(rgb []int) string` Convert RGB to hex code + +## Gookit 工具包 + + - [gookit/ini](https://github.com/gookit/ini) INI配置读取管理,支持多文件加载,数据覆盖合并, 解析ENV变量, 解析变量引用 + - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP + - [gookit/gcli](https://github.com/gookit/gcli) Go的命令行应用,工具库,运行CLI命令,支持命令行色彩,用户交互,进度显示,数据格式化显示 + - [gookit/event](https://github.com/gookit/event) Go实现的轻量级的事件管理、调度程序库, 支持设置监听器的优先级, 支持对一组事件进行监听 + - [gookit/cache](https://github.com/gookit/cache) 通用的缓存使用包装库,通过包装各种常用的驱动,来提供统一的使用API + - [gookit/config](https://github.com/gookit/config) Go应用配置管理,支持多种格式(JSON, YAML, TOML, INI, HCL, ENV, Flags),多文件加载,远程文件加载,数据合并 + - [gookit/color](https://github.com/gookit/color) CLI 控制台颜色渲染工具库, 拥有简洁的使用API,支持16色,256色,RGB色彩渲染输出 + - [gookit/filter](https://github.com/gookit/filter) 提供对Golang数据的过滤,净化,转换 + - [gookit/validate](https://github.com/gookit/validate) Go通用的数据验证与过滤库,使用简单,内置大部分常用验证、过滤器 + - [gookit/goutil](https://github.com/gookit/goutil) Go 的一些工具函数,格式化,特殊处理,常用信息获取等 + - 更多请查看 https://github.com/gookit + +## 参考项目 + + - `issue9/term` https://github.com/issue9/term + - `beego/bee` https://github.com/beego/bee + - `inhere/console` https://github/inhere/php-console + - [ANSI转义序列](https://zh.wikipedia.org/wiki/ANSI转义序列) + - [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map) + +## License + +MIT diff --git a/vendor/github.com/gookit/color/color.go b/vendor/github.com/gookit/color/color.go new file mode 100644 index 000000000000..0151814362da --- /dev/null +++ b/vendor/github.com/gookit/color/color.go @@ -0,0 +1,272 @@ +/* +Package color is Command line color library. +Support rich color rendering output, universal API method, compatible with Windows system + +Source code and other details for the project are available at GitHub: + + https://github.com/gookit/color + +More usage please see README and tests. +*/ +package color + +import ( + "fmt" + "io" + "os" + "regexp" +) + +// console color mode +// const ( +// ModeNormal = iota +// Mode256 // 8 bite +// ModeRGB // 24 bite +// ModeGrayscale +// ) + +// color render templates +// ESC 操作的表示: +// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制) +const ( + SettingTpl = "\x1b[%sm" + FullColorTpl = "\x1b[%sm%s\x1b[0m" +) + +// ResetSet Close all properties. +const ResetSet = "\x1b[0m" + +// CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m" +const CodeExpr = `\033\[[\d;?]+m` + +var ( + // Enable switch color render and display + Enable = true + // RenderTag render HTML tag on call color.Xprint, color.PrintX + RenderTag = true + // errors on windows render OR print to io.Writer + errors []error + // output the default io.Writer message print + output io.Writer = os.Stdout + // mark current env, It's like in `cmd.exe` + // if not in windows, is's always is False. + isLikeInCmd bool + // match color codes + codeRegex = regexp.MustCompile(CodeExpr) + // mark current env is support color. + // Always: isLikeInCmd != isSupportColor + isSupportColor = IsSupportColor() +) + +/************************************************************* + * global settings + *************************************************************/ + +// Set set console color attributes +func Set(colors ...Color) (int, error) { + if !Enable { // not enable + return 0, nil + } + + // on windows cmd.exe + // if isLikeInCmd { + // return winSet(colors...) + // } + + return fmt.Printf(SettingTpl, colors2code(colors...)) +} + +// Reset reset console color attributes +func Reset() (int, error) { + if !Enable { // not enable + return 0, nil + } + + // on windows cmd.exe + // if isLikeInCmd { + // return winReset() + // } + + return fmt.Print(ResetSet) +} + +// Disable disable color output +func Disable() bool { + oldVal := Enable + Enable = false + return oldVal +} + +// NotRenderTag on call color.Xprint, color.PrintX +func NotRenderTag() { + RenderTag = false +} + +// SetOutput set default colored text output +func SetOutput(w io.Writer) { + output = w +} + +// ResetOutput reset output +func ResetOutput() { + output = os.Stdout +} + +// ResetOptions reset all package option setting +func ResetOptions() { + RenderTag = true + Enable = true + output = os.Stdout +} + +// ForceColor force open color render +func ForceColor() bool { + return ForceOpenColor() +} + +// ForceOpenColor force open color render +func ForceOpenColor() bool { + oldVal := isSupportColor + isSupportColor = true + + return oldVal +} + +// IsLikeInCmd check result +func IsLikeInCmd() bool { + return isLikeInCmd +} + +// GetErrors info +func GetErrors() []error { + return errors +} + +/************************************************************* + * render color code + *************************************************************/ + +// RenderCode render message by color code. +// Usage: +// msg := RenderCode("3;32;45", "some", "message") +func RenderCode(code string, args ...interface{}) string { + var message string + if ln := len(args); ln == 0 { + return "" + } + + message = fmt.Sprint(args...) + if len(code) == 0 { + return message + } + + // disabled OR not support color + if !Enable || !isSupportColor { + return ClearCode(message) + } + + return fmt.Sprintf(FullColorTpl, code, message) +} + +// RenderWithSpaces Render code with spaces. +// If the number of args is > 1, a space will be added between the args +func RenderWithSpaces(code string, args ...interface{}) string { + message := formatArgsForPrintln(args) + if len(code) == 0 { + return message + } + + // disabled OR not support color + if !Enable || !isSupportColor { + return ClearCode(message) + } + + return fmt.Sprintf(FullColorTpl, code, message) +} + +// RenderString render a string with color code. +// Usage: +// msg := RenderString("3;32;45", "a message") +func RenderString(code string, str string) string { + if len(code) == 0 || str == "" { + return str + } + + // disabled OR not support color + if !Enable || !isSupportColor { + return ClearCode(str) + } + + return fmt.Sprintf(FullColorTpl, code, str) +} + +// ClearCode clear color codes. +// eg: +// "\033[36;1mText\x1b[0m" -> "Text" +func ClearCode(str string) string { + return codeRegex.ReplaceAllString(str, "") +} + +/************************************************************* + * colored message Printer + *************************************************************/ + +// PrinterFace interface +type PrinterFace interface { + fmt.Stringer + Sprint(a ...interface{}) string + Sprintf(format string, a ...interface{}) string + Print(a ...interface{}) + Printf(format string, a ...interface{}) + Println(a ...interface{}) +} + +// Printer a generic color message printer. +// Usage: +// p := &Printer{"32;45;3"} +// p.Print("message") +type Printer struct { + // ColorCode color code string. eg "32;45;3" + ColorCode string +} + +// NewPrinter instance +func NewPrinter(colorCode string) *Printer { + return &Printer{colorCode} +} + +// String returns color code string. eg: "32;45;3" +func (p *Printer) String() string { + // panic("implement me") + return p.ColorCode +} + +// Sprint returns rendering colored messages +func (p *Printer) Sprint(a ...interface{}) string { + return RenderCode(p.String(), a...) +} + +// Sprintf returns format and rendering colored messages +func (p *Printer) Sprintf(format string, a ...interface{}) string { + return RenderString(p.String(), fmt.Sprintf(format, a...)) +} + +// Print rendering colored messages +func (p *Printer) Print(a ...interface{}) { + doPrintV2(p.String(), fmt.Sprint(a...)) +} + +// Printf format and rendering colored messages +func (p *Printer) Printf(format string, a ...interface{}) { + doPrintV2(p.String(), fmt.Sprintf(format, a...)) +} + +// Println rendering colored messages with newline +func (p *Printer) Println(a ...interface{}) { + doPrintlnV2(p.ColorCode, a) +} + +// IsEmpty color code +func (p *Printer) IsEmpty() bool { + return p.ColorCode == "" +} diff --git a/vendor/github.com/gookit/color/color_16.go b/vendor/github.com/gookit/color/color_16.go new file mode 100644 index 000000000000..4dcd2aa4deb2 --- /dev/null +++ b/vendor/github.com/gookit/color/color_16.go @@ -0,0 +1,300 @@ +package color + +import ( + "fmt" + "strings" +) + +// Color Color16, 16 color value type +// 3(2^3=8) OR 4(2^4=16) bite color. +type Color uint8 + +/************************************************************* + * Basic 16 color definition + *************************************************************/ + +// Foreground colors. basic foreground colors 30 - 37 +const ( + FgBlack Color = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta // 品红 + FgCyan // 青色 + FgWhite + // FgDefault revert default FG + FgDefault Color = 39 +) + +// Extra foreground color 90 - 97(非标准) +const ( + FgDarkGray Color = iota + 90 // 亮黑(灰) + FgLightRed + FgLightGreen + FgLightYellow + FgLightBlue + FgLightMagenta + FgLightCyan + FgLightWhite + // FgGray is alias of FgDarkGray + FgGray Color = 90 // 亮黑(灰) +) + +// Background colors. basic background colors 40 - 47 +const ( + BgBlack Color = iota + 40 + BgRed + BgGreen + BgYellow // BgBrown like yellow + BgBlue + BgMagenta + BgCyan + BgWhite + // BgDefault revert default BG + BgDefault Color = 49 +) + +// Extra background color 100 - 107(非标准) +const ( + BgDarkGray Color = iota + 100 + BgLightRed + BgLightGreen + BgLightYellow + BgLightBlue + BgLightMagenta + BgLightCyan + BgLightWhite + // BgGray is alias of BgDarkGray + BgGray Color = 100 +) + +// Option settings +const ( + OpReset Color = iota // 0 重置所有设置 + OpBold // 1 加粗 + OpFuzzy // 2 模糊(不是所有的终端仿真器都支持) + OpItalic // 3 斜体(不是所有的终端仿真器都支持) + OpUnderscore // 4 下划线 + OpBlink // 5 闪烁 + OpFastBlink // 5 快速闪烁(未广泛支持) + OpReverse // 7 颠倒的 交换背景色与前景色 + OpConcealed // 8 隐匿的 + OpStrikethrough // 9 删除的,删除线(未广泛支持) +) + +// There are basic and light foreground color aliases +const ( + Red = FgRed + Cyan = FgCyan + Gray = FgDarkGray // is light Black + Blue = FgBlue + Black = FgBlack + Green = FgGreen + White = FgWhite + Yellow = FgYellow + Magenta = FgMagenta + + // special + + Bold = OpBold + Normal = FgDefault + + // extra light + + LightRed = FgLightRed + LightCyan = FgLightCyan + LightBlue = FgLightBlue + LightGreen = FgLightGreen + LightWhite = FgLightWhite + LightYellow = FgLightYellow + LightMagenta = FgLightMagenta +) + +/************************************************************* + * Color render methods + *************************************************************/ + +// Text render a text message +func (c Color) Text(message string) string { + return RenderString(c.String(), message) +} + +// Render messages by color setting +// Usage: +// green := color.FgGreen.Render +// fmt.Println(green("message")) +func (c Color) Render(a ...interface{}) string { + return RenderCode(c.String(), a...) +} + +// Renderln messages by color setting. +// like Println, will add spaces for each argument +// Usage: +// green := color.FgGreen.Renderln +// fmt.Println(green("message")) +func (c Color) Renderln(a ...interface{}) string { + return RenderWithSpaces(c.String(), a...) +} + +// Sprint render messages by color setting. is alias of the Render() +func (c Color) Sprint(a ...interface{}) string { + return RenderCode(c.String(), a...) +} + +// Sprintf format and render message. +// Usage: +// green := color.Green.Sprintf +// colored := green("message") +func (c Color) Sprintf(format string, args ...interface{}) string { + return RenderString(c.String(), fmt.Sprintf(format, args...)) +} + +// Print messages. +// Usage: +// color.Green.Print("message") +// OR: +// green := color.FgGreen.Print +// green("message") +func (c Color) Print(args ...interface{}) { + doPrintV2(c.Code(), fmt.Sprint(args...)) +} + +// Printf format and print messages. +// Usage: +// color.Cyan.Printf("string %s", "arg0") +func (c Color) Printf(format string, a ...interface{}) { + doPrintV2(c.Code(), fmt.Sprintf(format, a...)) +} + +// Println messages with new line +func (c Color) Println(a ...interface{}) { + doPrintlnV2(c.String(), a) +} + +// Light current color. eg: 36(FgCyan) -> 96(FgLightCyan). +// Usage: +// lightCyan := Cyan.Light() +// lightCyan.Print("message") +func (c Color) Light() Color { + val := int(c) + if val >= 30 && val <= 47 { + return Color(uint8(c) + 60) + } + + // don't change + return c +} + +// Darken current color. eg. 96(FgLightCyan) -> 36(FgCyan) +// Usage: +// cyan := LightCyan.Darken() +// cyan.Print("message") +func (c Color) Darken() Color { + val := int(c) + if val >= 90 && val <= 107 { + return Color(uint8(c) - 60) + } + + // don't change + return c +} + +// Code convert to code string. eg "35" +func (c Color) Code() string { + return fmt.Sprintf("%d", c) +} + +// String convert to code string. eg "35" +func (c Color) String() string { + return fmt.Sprintf("%d", c) +} + +// IsValid color value +func (c Color) IsValid() bool { + return c < 107 +} + +/************************************************************* + * basic color maps + *************************************************************/ + +// FgColors foreground colors map +var FgColors = map[string]Color{ + "black": FgBlack, + "red": FgRed, + "green": FgGreen, + "yellow": FgYellow, + "blue": FgBlue, + "magenta": FgMagenta, + "cyan": FgCyan, + "white": FgWhite, + "default": FgDefault, +} + +// BgColors background colors map +var BgColors = map[string]Color{ + "black": BgBlack, + "red": BgRed, + "green": BgGreen, + "yellow": BgYellow, + "blue": BgBlue, + "magenta": BgMagenta, + "cyan": BgCyan, + "white": BgWhite, + "default": BgDefault, +} + +// ExFgColors extra foreground colors map +var ExFgColors = map[string]Color{ + "darkGray": FgDarkGray, + "lightRed": FgLightRed, + "lightGreen": FgLightGreen, + "lightYellow": FgLightYellow, + "lightBlue": FgLightBlue, + "lightMagenta": FgLightMagenta, + "lightCyan": FgLightCyan, + "lightWhite": FgLightWhite, +} + +// ExBgColors extra background colors map +var ExBgColors = map[string]Color{ + "darkGray": BgDarkGray, + "lightRed": BgLightRed, + "lightGreen": BgLightGreen, + "lightYellow": BgLightYellow, + "lightBlue": BgLightBlue, + "lightMagenta": BgLightMagenta, + "lightCyan": BgLightCyan, + "lightWhite": BgLightWhite, +} + +// Options color options map +var Options = map[string]Color{ + "reset": OpReset, + "bold": OpBold, + "fuzzy": OpFuzzy, + "italic": OpItalic, + "underscore": OpUnderscore, + "blink": OpBlink, + "reverse": OpReverse, + "concealed": OpConcealed, +} + +/************************************************************* + * helper methods + *************************************************************/ + +// convert colors to code. return like "32;45;3" +func colors2code(colors ...Color) string { + if len(colors) == 0 { + return "" + } + + var codes []string + for _, color := range colors { + codes = append(codes, color.String()) + } + + return strings.Join(codes, ";") +} diff --git a/vendor/github.com/gookit/color/color_256.go b/vendor/github.com/gookit/color/color_256.go new file mode 100644 index 000000000000..f0ca2097eb8f --- /dev/null +++ b/vendor/github.com/gookit/color/color_256.go @@ -0,0 +1,218 @@ +package color + +import ( + "fmt" + "strings" +) + +/* +from wikipedia, 256 color: + ESC[ … 38;5; … m选择前景色 + ESC[ … 48;5; … m选择背景色 + 0- 7:标准颜色(同 ESC[30–37m) + 8- 15:高强度颜色(同 ESC[90–97m) + 16-231:6 × 6 × 6 立方(216色): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) + 232-255:从黑到白的24阶灰度色 +*/ + +// tpl for 8 bit 256 color(`2^8`) +// +// format: +// ESC[ … 38;5; … m // 选择前景色 +// ESC[ … 48;5; … m // 选择背景色 +// +// example: +// fg "\x1b[38;5;242m" +// bg "\x1b[48;5;208m" +// both "\x1b[38;5;242;48;5;208m" +// +// links: +// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#8位 +const ( + TplFg256 = "38;5;%d" + TplBg256 = "48;5;%d" +) + +/************************************************************* + * 8bit(256) Color: Bit8Color Color256 + *************************************************************/ + +// Color256 256 (8 bit) color, uint8 range at 0 - 255 +// +// 颜色值使用10进制和16进制都可 0x98 = 152 +// +// 颜色有两位uint8组成: +// 0: color value +// 1: color type, Fg=0 Bg=1 +// >1: unset value +// example: +// fg color: [152, 0] +// bg color: [152, 1] +// +// NOTICE: now support 256 color on windows CMD, PowerShell +type Color256 [2]uint8 + +// Bit8 create a color256 +func Bit8(val uint8, isBg ...bool) Color256 { + return C256(val, isBg...) +} + +// C256 create a color256 +func C256(val uint8, isBg ...bool) Color256 { + bc := Color256{val} + + // mark is bg color + if len(isBg) > 0 && isBg[0] { + bc[1] = AsBg + } + + return bc +} + +// Print print message +func (c Color256) Print(a ...interface{}) { + doPrintV2(c.String(), fmt.Sprint(a...)) +} + +// Printf format and print message +func (c Color256) Printf(format string, a ...interface{}) { + doPrintV2(c.String(), fmt.Sprintf(format, a...)) +} + +// Println print message with newline +func (c Color256) Println(a ...interface{}) { + doPrintlnV2(c.String(), a) +} + +// Sprint returns rendered message +func (c Color256) Sprint(a ...interface{}) string { + return RenderCode(c.String(), a...) +} + +// Sprintf returns format and rendered message +func (c Color256) Sprintf(format string, a ...interface{}) string { + return RenderString(c.String(), fmt.Sprintf(format, a...)) +} + +// Value return color value +func (c Color256) Value() uint8 { + return c[0] +} + +// String convert to color code string. +func (c Color256) String() string { + if c[1] == AsFg { // 0 is Fg + return fmt.Sprintf(TplFg256, c[0]) + } + + if c[1] == AsBg { // 1 is Bg + return fmt.Sprintf(TplBg256, c[0]) + } + + // empty + return "" +} + +// IsEmpty value +func (c Color256) IsEmpty() bool { + return c[1] > 1 +} + +/************************************************************* + * 8bit(256) Style + *************************************************************/ + +// Style256 definition +// +// 前/背景色 +// 都是由两位uint8组成, 第一位是色彩值; +// 第二位与Bit8Color不一样的是,在这里表示是否设置了值 0 未设置 ^0 已设置 +type Style256 struct { + p *Printer + // Name of the style + Name string + // fg and bg color + fg, bg Color256 +} + +// S256 create a color256 style +// Usage: +// s := color.S256() +// s := color.S256(132) // fg +// s := color.S256(132, 203) // fg and bg +func S256(fgAndBg ...uint8) *Style256 { + s := &Style256{} + vl := len(fgAndBg) + if vl > 0 { // with fg + s.fg = Color256{fgAndBg[0], 1} + + if vl > 1 { // and with bg + s.bg = Color256{fgAndBg[1], 1} + } + } + + return s +} + +// Set fg and bg color value +func (s *Style256) Set(fgVal, bgVal uint8) *Style256 { + s.fg = Color256{fgVal, 1} + s.bg = Color256{bgVal, 1} + return s +} + +// SetBg set bg color value +func (s *Style256) SetBg(bgVal uint8) *Style256 { + s.bg = Color256{bgVal, 1} + return s +} + +// SetFg set fg color value +func (s *Style256) SetFg(fgVal uint8) *Style256 { + s.fg = Color256{fgVal, 1} + return s +} + +// Print print message +func (s *Style256) Print(a ...interface{}) { + doPrintV2(s.String(), fmt.Sprint(a...)) +} + +// Printf format and print message +func (s *Style256) Printf(format string, a ...interface{}) { + doPrintV2(s.String(), fmt.Sprintf(format, a...)) +} + +// Println print message with newline +func (s *Style256) Println(a ...interface{}) { + doPrintlnV2(s.String(), a) +} + +// Sprint returns rendered message +func (s *Style256) Sprint(a ...interface{}) string { + return RenderCode(s.Code(), a...) +} + +// Sprintf returns format and rendered message +func (s *Style256) Sprintf(format string, a ...interface{}) string { + return RenderString(s.Code(), fmt.Sprintf(format, a...)) +} + +// Code convert to color code string +func (s *Style256) Code() string { + return s.String() +} + +// String convert to color code string +func (s *Style256) String() string { + var ss []string + if s.fg[1] > 0 { + ss = append(ss, fmt.Sprintf(TplFg256, s.fg[0])) + } + + if s.bg[1] > 0 { + ss = append(ss, fmt.Sprintf(TplBg256, s.bg[0])) + } + + return strings.Join(ss, ";") +} diff --git a/vendor/github.com/gookit/color/color_nonwin.go b/vendor/github.com/gookit/color/color_nonwin.go new file mode 100644 index 000000000000..c254393341a5 --- /dev/null +++ b/vendor/github.com/gookit/color/color_nonwin.go @@ -0,0 +1,23 @@ +// +build !windows + +// The method in the file has no effect +// Only for compatibility with non-Windows systems + +package color + +func winSet(_ ...Color) (n int, err error) { + return +} + +func winReset() (n int, err error) { + return +} + +func winPrint(_ string, _ ...Color) {} +func winPrintln(_ string, _ ...Color) {} +func renderColorCodeOnCmd(_ func()) {} + +// IsTerminal check currently is terminal +func IsTerminal(_ int) bool { + return true +} diff --git a/vendor/github.com/gookit/color/color_rgb.go b/vendor/github.com/gookit/color/color_rgb.go new file mode 100644 index 000000000000..2e60c3f52e3b --- /dev/null +++ b/vendor/github.com/gookit/color/color_rgb.go @@ -0,0 +1,301 @@ +package color + +import ( + "fmt" + "strconv" + "strings" +) + +// 24 bit RGB color +// RGB: +// R 0-255 G 0-255 B 0-255 +// R 00-FF G 00-FF B 00-FF (16进制) +// +// Format: +// ESC[ … 38;2;;; … m // Select RGB foreground color +// ESC[ … 48;2;;; … m // Choose RGB background color +// +// links: +// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位 +// +// example: +// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m +// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m +// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m +const ( + TplFgRGB = "38;2;%d;%d;%d" + TplBgRGB = "48;2;%d;%d;%d" +) + +// mark color is fg or bg. +const ( + AsFg uint8 = iota + AsBg +) + +/************************************************************* + * RGB Color(Bit24Color, TrueColor) + *************************************************************/ + +// RGBColor definition. +// +// The first to third digits represent the color value. +// The last digit represents the foreground(0), background(1), >1 is unset value +// +// Usage: +// // 0, 1, 2 is R,G,B. +// // 3rd: Fg=0, Bg=1, >1: unset value +// RGBColor{30,144,255, 0} +// RGBColor{30,144,255, 1} +// +// NOTICE: now support RGB color on windows CMD, PowerShell +type RGBColor [4]uint8 + +// create a empty RGBColor +var emptyRGBColor = RGBColor{3: 99} + +// RGB color create. +// Usage: +// c := RGB(30,144,255) +// c := RGB(30,144,255, true) +// c.Print("message") +func RGB(r, g, b uint8, isBg ...bool) RGBColor { + rgb := RGBColor{r, g, b} + if len(isBg) > 0 && isBg[0] { + rgb[3] = AsBg + } + + return rgb +} + +// HEX create RGB color from a HEX color string. +// Usage: +// c := HEX("ccc") // rgb: [204 204 204] +// c := HEX("aabbcc") // rgb: [170 187 204] +// c := HEX("#aabbcc") +// c := HEX("0xaabbcc") +// c.Print("message") +func HEX(hex string, isBg ...bool) RGBColor { + if rgb := HexToRgb(hex); len(rgb) > 0 { + return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...) + } + + // mark is empty + return emptyRGBColor +} + +// RGBFromString create RGB color from a string. +// Usage: +// c := RGBFromString("170,187,204") +// c.Print("message") +func RGBFromString(rgb string, isBg ...bool) RGBColor { + ss := stringToArr(rgb, ",") + if len(ss) != 3 { + return emptyRGBColor + } + + var ar [3]int + for i, val := range ss { + iv, err := strconv.Atoi(val) + if err != nil { + return emptyRGBColor + } + + ar[i] = iv + } + + return RGB(uint8(ar[0]), uint8(ar[1]), uint8(ar[2]), isBg...) +} + +// Print print message +func (c RGBColor) Print(a ...interface{}) { + doPrintV2(c.String(), fmt.Sprint(a...)) +} + +// Printf format and print message +func (c RGBColor) Printf(format string, a ...interface{}) { + doPrintV2(c.String(), fmt.Sprintf(format, a...)) +} + +// Println print message with newline +func (c RGBColor) Println(a ...interface{}) { + doPrintlnV2(c.String(), a) +} + +// Sprint returns rendered message +func (c RGBColor) Sprint(a ...interface{}) string { + return RenderCode(c.String(), a...) +} + +// Sprintf returns format and rendered message +func (c RGBColor) Sprintf(format string, a ...interface{}) string { + return RenderString(c.Code(), fmt.Sprintf(format, a...)) +} + +// Values to RGB values +func (c RGBColor) Values() []int { + return []int{int(c[0]), int(c[1]), int(c[2])} +} + +// Code to color code string +func (c RGBColor) Code() string { + return c.String() +} + +// String to color code string +func (c RGBColor) String() string { + if c[3] == AsFg { // 0 is Fg + return fmt.Sprintf(TplFgRGB, c[0], c[1], c[2]) + } + + if c[3] == AsBg { // 1 is Bg + return fmt.Sprintf(TplBgRGB, c[0], c[1], c[2]) + + } + + // >1 is empty + return "" +} + +// IsEmpty value +func (c RGBColor) IsEmpty() bool { + return c[3] > 1 +} + +// C256 returns the closest approximate 256 (8 bit) color +func (c RGBColor) C256() Color256 { + var isBg bool + if c[3] == 0 { + isBg = false + } else if c[3] == 1 { + isBg = true + } + + return C256(rgb2short(c[0], c[1], c[2]), isBg) +} + +/************************************************************* + * RGB Style + *************************************************************/ + +// RGBStyle definition. +// +// Foreground/Background color +// All are composed of 4 digits uint8, the first three digits are the color value; +// The last bit is different from RGBColor, here it indicates whether the value is set. +// - 1 Has been set +// - ^1 Not set +type RGBStyle struct { + // Name of the style + Name string + // fg and bg color + fg, bg RGBColor +} + +// NewRGBStyle create a RGBStyle. +func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle { + s := &RGBStyle{} + if len(bg) > 0 { + s.SetBg(bg[0]) + } + + return s.SetFg(fg) +} + +// HEXStyle create a RGBStyle from HEX color string. +// Usage: +// s := HEXStyle("aabbcc", "eee") +// s.Print("message") +func HEXStyle(fg string, bg ...string) *RGBStyle { + s := &RGBStyle{} + if len(bg) > 0 { + s.SetBg(HEX(bg[0])) + } + + if len(fg) > 0 { + s.SetFg(HEX(fg)) + } + + return s +} + +// RGBStyleFromString create a RGBStyle from color value string. +// Usage: +// s := RGBStyleFromString("170,187,204", "70,87,4") +// s.Print("message") +func RGBStyleFromString(fg string, bg ...string) *RGBStyle { + s := &RGBStyle{} + if len(bg) > 0 { + s.SetBg(RGBFromString(bg[0])) + } + + return s.SetFg(RGBFromString(fg)) +} + +// Set fg and bg color +func (s *RGBStyle) Set(fg, bg RGBColor) *RGBStyle { + return s.SetFg(fg).SetBg(bg) +} + +// SetFg set fg color +func (s *RGBStyle) SetFg(fg RGBColor) *RGBStyle { + fg[3] = 1 + s.fg = fg + return s +} + +// SetBg set bg color +func (s *RGBStyle) SetBg(bg RGBColor) *RGBStyle { + bg[3] = 1 + s.bg = bg + return s +} + +// Print print message +func (s *RGBStyle) Print(a ...interface{}) { + doPrintV2(s.String(), fmt.Sprint(a...)) +} + +// Printf format and print message +func (s *RGBStyle) Printf(format string, a ...interface{}) { + doPrintV2(s.String(), fmt.Sprintf(format, a...)) +} + +// Println print message with newline +func (s *RGBStyle) Println(a ...interface{}) { + doPrintlnV2(s.String(), a) +} + +// Sprint returns rendered message +func (s *RGBStyle) Sprint(a ...interface{}) string { + return RenderCode(s.String(), a...) +} + +// Sprintf returns format and rendered message +func (s *RGBStyle) Sprintf(format string, a ...interface{}) string { + return RenderString(s.Code(), fmt.Sprintf(format, a...)) +} + +// Code convert to color code string +func (s *RGBStyle) Code() string { + return s.String() +} + +// String convert to color code string +func (s *RGBStyle) String() string { + var ss []string + if s.fg[3] == 1 { // last value ensure is enable. + ss = append(ss, fmt.Sprintf(TplFgRGB, s.fg[0], s.fg[1], s.fg[2])) + } + + if s.bg[3] == 1 { + ss = append(ss, fmt.Sprintf(TplBgRGB, s.bg[0], s.bg[1], s.bg[2])) + } + + return strings.Join(ss, ";") +} + +// IsEmpty style +func (s *RGBStyle) IsEmpty() bool { + return s.fg[3] != 1 && s.bg[3] != 1 +} diff --git a/vendor/github.com/gookit/color/color_windows.go b/vendor/github.com/gookit/color/color_windows.go new file mode 100644 index 000000000000..b5e0f48075cc --- /dev/null +++ b/vendor/github.com/gookit/color/color_windows.go @@ -0,0 +1,152 @@ +// +build windows + +// Display color on windows +// refer: +// golang.org/x/sys/windows +// golang.org/x/crypto/ssh/terminal +// https://docs.microsoft.com/en-us/windows/console +package color + +import ( + "fmt" + "syscall" + "unsafe" +) + +// related docs +// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences +// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples +var ( + // isMSys bool + kernel32 *syscall.LazyDLL + + procGetConsoleMode *syscall.LazyProc + procSetConsoleMode *syscall.LazyProc +) + +func init() { + // if at linux, mac, or windows's ConEmu, Cmder, putty + if isSupportColor { + return + } + + isLikeInCmd = true + if !Enable { + return + } + + // force open + isSupportColor = true + // init simple color code info + // initWinColorsMap() + + // load related windows dll + // isMSys = utils.IsMSys() + kernel32 = syscall.NewLazyDLL("kernel32.dll") + + // https://docs.microsoft.com/en-us/windows/console/setconsolemode + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + + outHandle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + fmt.Println(53, err) + return + } + err = EnableVirtualTerminalProcessing(outHandle, true) + saveInternalError(err) + if err != nil { + fmt.Println(err) + // isSupportColor = false + } + + // fetch console screen buffer info + // err := getConsoleScreenBufferInfo(uintptr(syscall.Stdout), &defScreenInfo) +} + +/************************************************************* + * render full color code on windows(8,16,24bit color) + *************************************************************/ + +// docs https://docs.microsoft.com/zh-cn/windows/console/getconsolemode#parameters +const ( + // equals to docs page's ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 + EnableVirtualTerminalProcessingMode uint32 = 0x4 +) + +// EnableVirtualTerminalProcessing Enable virtual terminal processing +// +// ref from github.com/konsorten/go-windows-terminal-sequences +// doc https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples +// +// Usage: +// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) +// // support print color text +// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) +func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { + var mode uint32 + // Check if it is currently in the terminal + // err := syscall.GetConsoleMode(syscall.Stdout, &mode) + err := syscall.GetConsoleMode(stream, &mode) + if err != nil { + fmt.Println(92, err) + return err + } + + if enable { + mode |= EnableVirtualTerminalProcessingMode + } else { + mode &^= EnableVirtualTerminalProcessingMode + } + + ret, _, err := procSetConsoleMode.Call(uintptr(stream), uintptr(mode)) + if ret == 0 { + return err + } + + return nil +} + +// renderColorCodeOnCmd enable cmd color render. +func renderColorCodeOnCmd(fn func()) { + err := EnableVirtualTerminalProcessing(syscall.Stdout, true) + // if is not in terminal, will clear color tag. + if err != nil { + // panic(err) + fn() + return + } + + // force open color render + old := ForceOpenColor() + fn() + // revert color setting + isSupportColor = old + + err = EnableVirtualTerminalProcessing(syscall.Stdout, false) + if err != nil { + panic(err) + } +} + +/************************************************************* + * render simple color code on windows + *************************************************************/ + +// IsTty returns true if the given file descriptor is a terminal. +func IsTty(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// IsTerminal returns true if the given file descriptor is a terminal. +// Usage: +// fd := os.Stdout.Fd() +// fd := uintptr(syscall.Stdout) // for windows +// IsTerminal(fd) +func IsTerminal(fd int) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} diff --git a/vendor/github.com/gookit/color/go.mod b/vendor/github.com/gookit/color/go.mod new file mode 100644 index 000000000000..f81dabde9581 --- /dev/null +++ b/vendor/github.com/gookit/color/go.mod @@ -0,0 +1,5 @@ +module github.com/gookit/color + +go 1.12 + +require github.com/stretchr/testify v1.3.0 diff --git a/vendor/github.com/gookit/color/go.sum b/vendor/github.com/gookit/color/go.sum new file mode 100644 index 000000000000..8afa960a3a9e --- /dev/null +++ b/vendor/github.com/gookit/color/go.sum @@ -0,0 +1,22 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-playground/assert v1.2.1 h1:ad06XqC+TOv0nJWnbULSlh3ehp5uLuQEojZY5Tq8RgI= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gookit/color v1.2.9/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= +github.com/gookit/goutil v0.3.5 h1:95hWrCXcz7wuwlvcHw7YyUbFH0fV15YM0WPioYMcZww= +github.com/gookit/goutil v0.3.5/go.mod h1:OHs5W5Xmfj4pCMXHnMxsDPrCc0SRbHLgJ2qs6wr5fxM= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/gookit/color/rgb_to_256.go b/vendor/github.com/gookit/color/rgb_to_256.go new file mode 100644 index 000000000000..f29bf26201e8 --- /dev/null +++ b/vendor/github.com/gookit/color/rgb_to_256.go @@ -0,0 +1,307 @@ +package color + +import ( + "fmt" + "math" +) + +// adapted from https://gist.github.com/MicahElliott/719710 + +var ( + lookupTable = map[string]uint8{ // color look-up table + // 8-bit, RGB hex + + // Primary 3-bit (8 colors). Unique representation! + "000000": 0, + "800000": 1, + "008000": 2, + "808000": 3, + "000080": 4, + "800080": 5, + "008080": 6, + "c0c0c0": 7, + + // Equivalent "bright" versions of original 8 colors. + "808080": 8, + "ff0000": 9, + "00ff00": 10, + "ffff00": 11, + "0000ff": 12, + "ff00ff": 13, + "00ffff": 14, + "ffffff": 15, + + // values commented out below are duplicates from the prior sections + + // Strictly ascending. + // "000000": 16, + "00005f": 17, + "000087": 18, + "0000af": 19, + "0000d7": 20, + // "0000ff": 21, + "005f00": 22, + "005f5f": 23, + "005f87": 24, + "005faf": 25, + "005fd7": 26, + "005fff": 27, + "008700": 28, + "00875f": 29, + "008787": 30, + "0087af": 31, + "0087d7": 32, + "0087ff": 33, + "00af00": 34, + "00af5f": 35, + "00af87": 36, + "00afaf": 37, + "00afd7": 38, + "00afff": 39, + "00d700": 40, + "00d75f": 41, + "00d787": 42, + "00d7af": 43, + "00d7d7": 44, + "00d7ff": 45, + // "00ff00": 46, + "00ff5f": 47, + "00ff87": 48, + "00ffaf": 49, + "00ffd7": 50, + // "00ffff": 51, + "5f0000": 52, + "5f005f": 53, + "5f0087": 54, + "5f00af": 55, + "5f00d7": 56, + "5f00ff": 57, + "5f5f00": 58, + "5f5f5f": 59, + "5f5f87": 60, + "5f5faf": 61, + "5f5fd7": 62, + "5f5fff": 63, + "5f8700": 64, + "5f875f": 65, + "5f8787": 66, + "5f87af": 67, + "5f87d7": 68, + "5f87ff": 69, + "5faf00": 70, + "5faf5f": 71, + "5faf87": 72, + "5fafaf": 73, + "5fafd7": 74, + "5fafff": 75, + "5fd700": 76, + "5fd75f": 77, + "5fd787": 78, + "5fd7af": 79, + "5fd7d7": 80, + "5fd7ff": 81, + "5fff00": 82, + "5fff5f": 83, + "5fff87": 84, + "5fffaf": 85, + "5fffd7": 86, + "5fffff": 87, + "870000": 88, + "87005f": 89, + "870087": 90, + "8700af": 91, + "8700d7": 92, + "8700ff": 93, + "875f00": 94, + "875f5f": 95, + "875f87": 96, + "875faf": 97, + "875fd7": 98, + "875fff": 99, + "878700": 100, + "87875f": 101, + "878787": 102, + "8787af": 103, + "8787d7": 104, + "8787ff": 105, + "87af00": 106, + "87af5f": 107, + "87af87": 108, + "87afaf": 109, + "87afd7": 110, + "87afff": 111, + "87d700": 112, + "87d75f": 113, + "87d787": 114, + "87d7af": 115, + "87d7d7": 116, + "87d7ff": 117, + "87ff00": 118, + "87ff5f": 119, + "87ff87": 120, + "87ffaf": 121, + "87ffd7": 122, + "87ffff": 123, + "af0000": 124, + "af005f": 125, + "af0087": 126, + "af00af": 127, + "af00d7": 128, + "af00ff": 129, + "af5f00": 130, + "af5f5f": 131, + "af5f87": 132, + "af5faf": 133, + "af5fd7": 134, + "af5fff": 135, + "af8700": 136, + "af875f": 137, + "af8787": 138, + "af87af": 139, + "af87d7": 140, + "af87ff": 141, + "afaf00": 142, + "afaf5f": 143, + "afaf87": 144, + "afafaf": 145, + "afafd7": 146, + "afafff": 147, + "afd700": 148, + "afd75f": 149, + "afd787": 150, + "afd7af": 151, + "afd7d7": 152, + "afd7ff": 153, + "afff00": 154, + "afff5f": 155, + "afff87": 156, + "afffaf": 157, + "afffd7": 158, + "afffff": 159, + "d70000": 160, + "d7005f": 161, + "d70087": 162, + "d700af": 163, + "d700d7": 164, + "d700ff": 165, + "d75f00": 166, + "d75f5f": 167, + "d75f87": 168, + "d75faf": 169, + "d75fd7": 170, + "d75fff": 171, + "d78700": 172, + "d7875f": 173, + "d78787": 174, + "d787af": 175, + "d787d7": 176, + "d787ff": 177, + "d7af00": 178, + "d7af5f": 179, + "d7af87": 180, + "d7afaf": 181, + "d7afd7": 182, + "d7afff": 183, + "d7d700": 184, + "d7d75f": 185, + "d7d787": 186, + "d7d7af": 187, + "d7d7d7": 188, + "d7d7ff": 189, + "d7ff00": 190, + "d7ff5f": 191, + "d7ff87": 192, + "d7ffaf": 193, + "d7ffd7": 194, + "d7ffff": 195, + // "ff0000": 196, + "ff005f": 197, + "ff0087": 198, + "ff00af": 199, + "ff00d7": 200, + // "ff00ff": 201, + "ff5f00": 202, + "ff5f5f": 203, + "ff5f87": 204, + "ff5faf": 205, + "ff5fd7": 206, + "ff5fff": 207, + "ff8700": 208, + "ff875f": 209, + "ff8787": 210, + "ff87af": 211, + "ff87d7": 212, + "ff87ff": 213, + "ffaf00": 214, + "ffaf5f": 215, + "ffaf87": 216, + "ffafaf": 217, + "ffafd7": 218, + "ffafff": 219, + "ffd700": 220, + "ffd75f": 221, + "ffd787": 222, + "ffd7af": 223, + "ffd7d7": 224, + "ffd7ff": 225, + // "ffff00": 226, + "ffff5f": 227, + "ffff87": 228, + "ffffaf": 229, + "ffffd7": 230, + // "ffffff": 231, + + // Gray-scale range. + "080808": 232, + "121212": 233, + "1c1c1c": 234, + "262626": 235, + "303030": 236, + "3a3a3a": 237, + "444444": 238, + "4e4e4e": 239, + "585858": 240, + "626262": 241, + "6c6c6c": 242, + "767676": 243, + // "808080": 244, + "8a8a8a": 245, + "949494": 246, + "9e9e9e": 247, + "a8a8a8": 248, + "b2b2b2": 249, + "bcbcbc": 250, + "c6c6c6": 251, + "d0d0d0": 252, + "dadada": 253, + "e4e4e4": 254, + "eeeeee": 255, + } + incs = []uint8{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff} +) + +func rgb2short(r, g, b uint8) uint8 { + res := make([]uint8, 3) + for partI, part := range [3]uint8{r, g, b} { + i := 0 + for i < len(incs)-1 { + s, b := incs[i], incs[i+1] // smaller, bigger + if s <= part && part <= b { + s1 := math.Abs(float64(s) - float64(part)) + b1 := math.Abs(float64(b) - float64(part)) + var closest uint8 + if s1 < b1 { + closest = s + } else { + closest = b + } + res[partI] = closest + break + } + i++ + } + } + hex := fmt.Sprintf("%02x%02x%02x", res[0], res[1], res[2]) + equiv := lookupTable[hex] + return equiv +} diff --git a/vendor/github.com/gookit/color/style.go b/vendor/github.com/gookit/color/style.go new file mode 100644 index 000000000000..84d270598cc8 --- /dev/null +++ b/vendor/github.com/gookit/color/style.go @@ -0,0 +1,285 @@ +package color + +import ( + "fmt" + "strings" +) + +/************************************************************* + * 16 color Style + *************************************************************/ + +// Style a 16 color style. can add: fg color, bg color, color options +// +// Example: +// color.Style{color.FgGreen}.Print("message") +type Style []Color + +// New create a custom style +// +// Usage: +// color.New(color.FgGreen).Print("message") +// equals to: +// color.Style{color.FgGreen}.Print("message") +func New(colors ...Color) Style { + return colors +} + +// Save to styles map +func (s Style) Save(name string) { + AddStyle(name, s) +} + +// Render render text +// Usage: +// color.New(color.FgGreen).Render("text") +// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text") +func (s Style) Render(a ...interface{}) string { + return RenderCode(s.String(), a...) +} + +// Renderln render text line. +// like Println, will add spaces for each argument +// Usage: +// color.New(color.FgGreen).Renderln("text", "more") +// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text", "more") +func (s Style) Renderln(a ...interface{}) string { + return RenderWithSpaces(s.String(), a...) +} + +// Sprint is alias of the 'Render' +func (s Style) Sprint(a ...interface{}) string { + return RenderCode(s.String(), a...) +} + +// Sprintf format and render message. +func (s Style) Sprintf(format string, a ...interface{}) string { + return RenderString(s.String(), fmt.Sprintf(format, a...)) +} + +// Print render and Print text +func (s Style) Print(a ...interface{}) { + doPrintV2(s.String(), fmt.Sprint(a...)) +} + +// Printf render and print text +func (s Style) Printf(format string, a ...interface{}) { + doPrintV2(s.Code(), fmt.Sprintf(format, a...)) +} + +// Println render and print text line +func (s Style) Println(a ...interface{}) { + doPrintlnV2(s.String(), a) +} + +// Code convert to code string. returns like "32;45;3" +func (s Style) Code() string { + return s.String() +} + +// String convert to code string. returns like "32;45;3" +func (s Style) String() string { + return colors2code(s...) +} + +// IsEmpty style +func (s Style) IsEmpty() bool { + return len(s) == 0 +} + +/************************************************************* + * Theme(extended Style) + *************************************************************/ + +// Theme definition. extends from Style +type Theme struct { + // Name theme name + Name string + // Style for the theme + Style +} + +// NewTheme instance +func NewTheme(name string, style Style) *Theme { + return &Theme{name, style} +} + +// Save to themes map +func (t *Theme) Save() { + AddTheme(t.Name, t.Style) +} + +// Tips use name as title, only apply style for name +func (t *Theme) Tips(format string, a ...interface{}) { + // only apply style for name + t.Print(strings.ToUpper(t.Name) + ": ") + Printf(format+"\n", a...) +} + +// Prompt use name as title, and apply style for message +func (t *Theme) Prompt(format string, a ...interface{}) { + title := strings.ToUpper(t.Name) + ":" + t.Println(title, fmt.Sprintf(format, a...)) +} + +// Block like Prompt, but will wrap a empty line +func (t *Theme) Block(format string, a ...interface{}) { + title := strings.ToUpper(t.Name) + ":\n" + + t.Println(title, fmt.Sprintf(format, a...)) +} + +/************************************************************* + * Theme: internal themes + *************************************************************/ + +// internal themes(like bootstrap style) +// Usage: +// color.Info.Print("message") +// color.Info.Printf("a %s message", "test") +// color.Warn.Println("message") +// color.Error.Println("message") +var ( + // Info color style + Info = &Theme{"info", Style{OpReset, FgGreen}} + // Note color style + Note = &Theme{"note", Style{OpBold, FgLightCyan}} + // Warn color style + Warn = &Theme{"warning", Style{OpBold, FgYellow}} + // Light color style + Light = &Theme{"light", Style{FgLightWhite, BgBlack}} + // Error color style + Error = &Theme{"error", Style{FgLightWhite, BgRed}} + // Danger color style + Danger = &Theme{"danger", Style{OpBold, FgRed}} + // Debug color style + Debug = &Theme{"debug", Style{OpReset, FgCyan}} + // Notice color style + Notice = &Theme{"notice", Style{OpBold, FgCyan}} + // Comment color style + Comment = &Theme{"comment", Style{OpReset, FgLightYellow}} + // Success color style + Success = &Theme{"success", Style{OpBold, FgGreen}} + // Primary color style + Primary = &Theme{"primary", Style{OpReset, FgBlue}} + // Question color style + Question = &Theme{"question", Style{OpReset, FgMagenta}} + // Secondary color style + Secondary = &Theme{"secondary", Style{FgDarkGray}} +) + +// Themes internal defined themes. +// Usage: +// color.Themes["info"].Println("message") +var Themes = map[string]*Theme{ + "info": Info, + "note": Note, + "light": Light, + "error": Error, + + "debug": Debug, + "danger": Danger, + "notice": Notice, + "success": Success, + "comment": Comment, + "primary": Primary, + "warning": Warn, + + "question": Question, + "secondary": Secondary, +} + +// AddTheme add a theme and style +func AddTheme(name string, style Style) { + Themes[name] = NewTheme(name, style) + Styles[name] = style +} + +// GetTheme get defined theme by name +func GetTheme(name string) *Theme { + return Themes[name] +} + +/************************************************************* + * internal styles + *************************************************************/ + +// Styles internal defined styles, like bootstrap styles. +// Usage: +// color.Styles["info"].Println("message") +var Styles = map[string]Style{ + "info": {OpReset, FgGreen}, + "note": {OpBold, FgLightCyan}, + "light": {FgLightWhite, BgRed}, + "error": {FgLightWhite, BgRed}, + + "danger": {OpBold, FgRed}, + "notice": {OpBold, FgCyan}, + "success": {OpBold, FgGreen}, + "comment": {OpReset, FgMagenta}, + "primary": {OpReset, FgBlue}, + "warning": {OpBold, FgYellow}, + + "question": {OpReset, FgMagenta}, + "secondary": {FgDarkGray}, +} + +// some style name alias +var styleAliases = map[string]string{ + "err": "error", + "suc": "success", + "warn": "warning", +} + +// AddStyle add a style +func AddStyle(name string, s Style) { + Styles[name] = s +} + +// GetStyle get defined style by name +func GetStyle(name string) Style { + if s, ok := Styles[name]; ok { + return s + } + + if realName, ok := styleAliases[name]; ok { + return Styles[realName] + } + + // empty style + return New() +} + +/************************************************************* + * quick use style print message + *************************************************************/ + +// Infof print message with Info style +func Infof(format string, a ...interface{}) { + Info.Printf(format, a...) +} + +// Infoln print message with Info style +func Infoln(a ...interface{}) { + Info.Println(a...) +} + +// Errorf print message with Error style +func Errorf(format string, a ...interface{}) { + Error.Printf(format, a...) +} + +// Errorln print message with Error style +func Errorln(a ...interface{}) { + Error.Println(a...) +} + +// Warnf print message with Warn style +func Warnf(format string, a ...interface{}) { + Warn.Printf(format, a...) +} + +// Warnln print message with Warn style +func Warnln(a ...interface{}) { + Warn.Println(a...) +} diff --git a/vendor/github.com/gookit/color/tag.go b/vendor/github.com/gookit/color/tag.go new file mode 100644 index 000000000000..5963b3b5edf2 --- /dev/null +++ b/vendor/github.com/gookit/color/tag.go @@ -0,0 +1,310 @@ +package color + +import ( + "fmt" + "regexp" + "strings" +) + +// output colored text like use html tag. (not support windows cmd) +const ( + // MatchExpr regex to match color tags + // Notice: golang 不支持反向引用. 即不支持使用 \1 引用第一个匹配 ([a-z=;]+) + // MatchExpr = `<([a-z=;]+)>(.*?)<\/\1>` + // 所以调整一下 统一使用 `` 来结束标签,例如 "some text" + // 支持自定义颜色属性的tag "content" + // (?s:...) s - 让 "." 匹配换行 + MatchExpr = `<([a-zA-Z_=,;]+)>(?s:(.*?))<\/>` + + // AttrExpr regex to match color attributes + AttrExpr = `(fg|bg|op)[\s]*=[\s]*([a-zA-Z,]+);?` + + // StripExpr regex used for removing color tags + // StripExpr = `<[\/]?[a-zA-Z=;]+>` + // 随着上面的做一些调整 + StripExpr = `<[\/]?[a-zA-Z_=,;]*>` +) + +var ( + attrRegex = regexp.MustCompile(AttrExpr) + matchRegex = regexp.MustCompile(MatchExpr) + stripRegex = regexp.MustCompile(StripExpr) +) + +/************************************************************* + * internal defined color tags + *************************************************************/ + +// Some internal defined color tags +// Usage: content text +// @notice 加 0 在前面是为了防止之前的影响到现在的设置 +var colorTags = map[string]string{ + // basic tags, + "red": "0;31", + "blue": "0;34", + "cyan": "0;36", + "black": "0;30", + "green": "0;32", + "white": "1;37", + "default": "0;39", // no color + "normal": "0;39", // no color + "brown": "0;33", + "yellow": "1;33", + "mga": "0;35", // short name + "magenta": "0;35", + "mgb": "1;35", // short name + "magentaB": "1;35", // add bold + + // alert tags, like bootstrap's alert + "suc": "1;32", // same "green" and "bold" + "success": "1;32", + "info": "0;32", // same "green", + "comment": "0;33", // same "brown" + "note": "36;1", + "notice": "36;4", + "warn": "0;1;33", + "warning": "0;30;43", + "primary": "0;34", + "danger": "1;31", // same "red" but add bold + "err": "97;41", + "error": "97;41", // fg light white; bg red + + // more tags + "lightRed": "1;31", + "light_red": "1;31", + "lightGreen": "1;32", + "light_green": "1;32", + "lightBlue": "1;34", + "light_blue": "1;34", + "lightCyan": "1;36", + "light_cyan": "1;36", + "lightDray": "0;37", + "light_gray": "0;37", + "gray": "0;90", + "darkGray": "0;90", + "dark_gray": "0;90", + "lightYellow": "0;93", + "light_yellow": "0;93", + "lightMagenta": "0;95", + "light_magenta": "0;95", + + // extra + "lightRedEx": "0;91", + "light_red_ex": "0;91", + "lightGreenEx": "0;92", + "light_green_ex": "0;92", + "lightBlueEx": "0;94", + "light_blue_ex": "0;94", + "lightCyanEx": "0;96", + "light_cyan_ex": "0;96", + "whiteEx": "0;97;40", + "white_ex": "0;97;40", + + // option + "bold": "1", + "underscore": "4", + "reverse": "7", +} + +/************************************************************* + * parse color tags + *************************************************************/ + +// ReplaceTag parse string, replace color tag and return rendered string +func ReplaceTag(str string) string { + // disable handler TAG OR not contains color tag + if !RenderTag || !strings.Contains(str, "") { + return str + } + + // disabled OR not support color + if !Enable || !isSupportColor { + return ClearTag(str) + } + + // find color tags by regex + matched := matchRegex.FindAllStringSubmatch(str, -1) + + // item: 0 full text 1 tag name 2 tag content + for _, item := range matched { + full, tag, content := item[0], item[1], item[2] + + // custom color in tag: "content" + if code := ParseCodeFromAttr(tag); len(code) > 0 { + now := RenderString(code, content) + str = strings.Replace(str, full, now, 1) + continue + } + + // use defined tag: "content" + if code := GetTagCode(tag); len(code) > 0 { + now := RenderString(code, content) + // old := WrapTag(content, tag) is equals to var 'full' + str = strings.Replace(str, full, now, 1) + } + } + + return str +} + +// ParseCodeFromAttr parse color attributes. +// attr like: +// "fg=VALUE;bg=VALUE;op=VALUE" // VALUE please see var: FgColors, BgColors, Options +// eg: +// "fg=yellow" +// "bg=red" +// "op=bold,underscore" option is allow multi value +// "fg=white;bg=blue;op=bold" +// "fg=white;op=bold,underscore" +func ParseCodeFromAttr(attr string) (code string) { + if !strings.Contains(attr, "=") { + return + } + + attr = strings.Trim(attr, ";=,") + if len(attr) == 0 { + return + } + + var colors []Color + + matched := attrRegex.FindAllStringSubmatch(attr, -1) + for _, item := range matched { + pos, val := item[1], item[2] + switch pos { + case "fg": + if c, ok := FgColors[val]; ok { // basic fg + colors = append(colors, c) + } else if c, ok := ExFgColors[val]; ok { // extra fg + colors = append(colors, c) + } + case "bg": + if c, ok := BgColors[val]; ok { // basic bg + colors = append(colors, c) + } else if c, ok := ExBgColors[val]; ok { // extra bg + colors = append(colors, c) + } + case "op": // options allow multi value + if strings.Contains(val, ",") { + ns := strings.Split(val, ",") + for _, n := range ns { + if c, ok := Options[n]; ok { + colors = append(colors, c) + } + } + } else if c, ok := Options[val]; ok { + colors = append(colors, c) + } + } + } + + return colors2code(colors...) +} + +// ClearTag clear all tag for a string +func ClearTag(s string) string { + if !strings.Contains(s, "") { + return s + } + + return stripRegex.ReplaceAllString(s, "") +} + +/************************************************************* + * helper methods + *************************************************************/ + +// GetTagCode get color code by tag name +func GetTagCode(name string) string { + if code, ok := colorTags[name]; ok { + return code + } + + return "" +} + +// ApplyTag for messages +func ApplyTag(tag string, a ...interface{}) string { + return RenderCode(GetTagCode(tag), a...) +} + +// WrapTag wrap a tag for a string "content" +func WrapTag(s string, tag string) string { + if s == "" || tag == "" { + return s + } + + return fmt.Sprintf("<%s>%s", tag, s) +} + +// GetColorTags get all internal color tags +func GetColorTags() map[string]string { + return colorTags +} + +// IsDefinedTag is defined tag name +func IsDefinedTag(name string) bool { + _, ok := colorTags[name] + return ok +} + +/************************************************************* + * Tag extra + *************************************************************/ + +// Tag value is a defined style name +// Usage: +// Tag("info").Println("message") +type Tag string + +// Print messages +func (tg Tag) Print(a ...interface{}) { + name := string(tg) + str := fmt.Sprint(a...) + + if stl := GetStyle(name); !stl.IsEmpty() { + stl.Print(str) + } else { + doPrintV2(GetTagCode(name), str) + } +} + +// Printf format and print messages +func (tg Tag) Printf(format string, a ...interface{}) { + name := string(tg) + str := fmt.Sprintf(format, a...) + + if stl := GetStyle(name); !stl.IsEmpty() { + stl.Print(str) + } else { + doPrintV2(GetTagCode(name), str) + } +} + +// Println messages line +func (tg Tag) Println(a ...interface{}) { + name := string(tg) + if stl := GetStyle(name); !stl.IsEmpty() { + stl.Println(a...) + } else { + doPrintlnV2(GetTagCode(name), a) + } +} + +// Sprint render messages +func (tg Tag) Sprint(a ...interface{}) string { + name := string(tg) + // if stl := GetStyle(name); !stl.IsEmpty() { + // return stl.Render(args...) + // } + + return RenderCode(GetTagCode(name), a...) +} + +// Sprintf format and render messages +func (tg Tag) Sprintf(format string, a ...interface{}) string { + tag := string(tg) + str := fmt.Sprintf(format, a...) + + return RenderString(GetTagCode(tag), str) +} diff --git a/vendor/github.com/gookit/color/utils.go b/vendor/github.com/gookit/color/utils.go new file mode 100644 index 000000000000..4b3a5fe655ab --- /dev/null +++ b/vendor/github.com/gookit/color/utils.go @@ -0,0 +1,351 @@ +package color + +import ( + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "syscall" +) + +// Support color: +// "TERM=xterm" +// "TERM=xterm-vt220" +// "TERM=xterm-256color" +// "TERM=screen-256color" +// "TERM=tmux-256color" +// "TERM=rxvt-unicode-256color" +// Don't support color: +// "TERM=cygwin" +var specialColorTerms = map[string]bool{ + "alacritty": true, + "screen-256color": true, + "tmux-256color": true, + "rxvt-unicode-256color": true, +} + +// IsConsole Determine whether w is one of stderr, stdout, stdin +func IsConsole(w io.Writer) bool { + o, ok := w.(*os.File) + if !ok { + return false + } + + fd := o.Fd() + + // fix: cannot use 'o == os.Stdout' to compare + return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr) +} + +// IsMSys msys(MINGW64) environment, does not necessarily support color +func IsMSys() bool { + // like "MSYSTEM=MINGW64" + if len(os.Getenv("MSYSTEM")) > 0 { + return true + } + + return false +} + +// IsSupportColor check current console is support color. +// +// Supported: +// linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe +// Not support: +// windows cmd.exe, powerShell.exe +func IsSupportColor() bool { + envTerm := os.Getenv("TERM") + if strings.Contains(envTerm, "xterm") { + return true + } + + // it's special color term + if _, ok := specialColorTerms[envTerm]; ok { + return true + } + + // like on ConEmu software, e.g "ConEmuANSI=ON" + if os.Getenv("ConEmuANSI") == "ON" { + return true + } + + // like on ConEmu software, e.g "ANSICON=189x2000 (189x43)" + if os.Getenv("ANSICON") != "" { + return true + } + + // up: if support 256-color, can also support basic color. + return IsSupport256Color() +} + +// IsSupport256Color render +func IsSupport256Color() bool { + // "TERM=xterm-256color" + // "TERM=screen-256color" + // "TERM=tmux-256color" + // "TERM=rxvt-unicode-256color" + supported := strings.Contains(os.Getenv("TERM"), "256color") + if !supported { + // up: if support true-color, can also support 256-color. + supported = IsSupportTrueColor() + } + + return supported +} + +// IsSupportTrueColor render. IsSupportRGBColor +func IsSupportTrueColor() bool { + // "COLORTERM=truecolor" + return strings.Contains(os.Getenv("COLORTERM"), "truecolor") +} + +// Hex2rgb alias of the HexToRgb() +func Hex2rgb(hex string) []int { return HexToRgb(hex) } + +// HexToRGB alias of the HexToRgb() +func HexToRGB(hex string) []int { return HexToRgb(hex) } + +// HexToRgb convert hex color string to RGB numbers +// Usage: +// rgb := HexToRgb("ccc") // rgb: [204 204 204] +// rgb := HexToRgb("aabbcc") // rgb: [170 187 204] +// rgb := HexToRgb("#aabbcc") // rgb: [170 187 204] +// rgb := HexToRgb("0xad99c0") // rgb: [170 187 204] +func HexToRgb(hex string) (rgb []int) { + hex = strings.TrimSpace(hex) + if hex == "" { + return + } + + // like from css. eg "#ccc" "#ad99c0" + if hex[0] == '#' { + hex = hex[1:] + } + + hex = strings.ToLower(hex) + switch len(hex) { + case 3: // "ccc" + hex = string([]byte{hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]}) + case 8: // "0xad99c0" + hex = strings.TrimPrefix(hex, "0x") + } + + // recheck + if len(hex) != 6 { + return + } + + // convert string to int64 + if i64, err := strconv.ParseInt(hex, 16, 32); err == nil { + color := int(i64) + // parse int + rgb = make([]int, 3) + rgb[0] = color >> 16 + rgb[1] = (color & 0x00FF00) >> 8 + rgb[2] = color & 0x0000FF + } + + return +} + +// Rgb2hex alias of the RgbToHex() +func Rgb2hex(rgb []int) string { return RgbToHex(rgb) } + +// RgbToHex convert RGB to hex code +// Usage: +// hex := RgbToHex([]int{170, 187, 204}) // hex: "aabbcc" +func RgbToHex(rgb []int) string { + hexNodes := make([]string, len(rgb)) + for _, v := range rgb { + hexNodes = append(hexNodes, strconv.FormatInt(int64(v), 16)) + } + + return strings.Join(hexNodes, "") +} + +/************************************************************* + * print methods(will auto parse color tags) + *************************************************************/ + +// Print render color tag and print messages +func Print(a ...interface{}) { + Fprint(output, a...) +} + +// Printf format and print messages +func Printf(format string, a ...interface{}) { + Fprintf(output, format, a...) +} + +// Println messages with new line +func Println(a ...interface{}) { + Fprintln(output, a...) +} + +// Fprint print rendered messages to writer +// Notice: will ignore print error +func Fprint(w io.Writer, a ...interface{}) { + _, err := fmt.Fprint(w, Render(a...)) + saveInternalError(err) + + // if isLikeInCmd { + // renderColorCodeOnCmd(func() { + // _, _ = fmt.Fprint(w, Render(a...)) + // }) + // } else { + // _, _ = fmt.Fprint(w, Render(a...)) + // } +} + +// Fprintf print format and rendered messages to writer. +// Notice: will ignore print error +func Fprintf(w io.Writer, format string, a ...interface{}) { + str := fmt.Sprintf(format, a...) + _, err := fmt.Fprint(w, ReplaceTag(str)) + saveInternalError(err) + + // if isLikeInCmd { + // renderColorCodeOnCmd(func() { + // _, _ = fmt.Fprint(w, ReplaceTag(str)) + // }) + // } else { + // _, _ = fmt.Fprint(w, ReplaceTag(str)) + // } +} + +// Fprintln print rendered messages line to writer +// Notice: will ignore print error +func Fprintln(w io.Writer, a ...interface{}) { + str := formatArgsForPrintln(a) + _, err := fmt.Fprintln(w, ReplaceTag(str)) + saveInternalError(err) + + // if isLikeInCmd { + // renderColorCodeOnCmd(func() { + // _, _ = fmt.Fprintln(w, ReplaceTag(str)) + // }) + // } else { + // _, _ = fmt.Fprintln(w, ReplaceTag(str)) + // } +} + +// Lprint passes colored messages to a log.Logger for printing. +// Notice: should be goroutine safe +func Lprint(l *log.Logger, a ...interface{}) { + l.Print(Render(a...)) + + // if isLikeInCmd { + // renderColorCodeOnCmd(func() { + // l.Print(Render(a...)) + // }) + // } else { + // l.Print(Render(a...)) + // } +} + +// Render parse color tags, return rendered string. +// Usage: +// text := Render("hello world!") +// fmt.Println(text) +func Render(a ...interface{}) string { + if len(a) == 0 { + return "" + } + + return ReplaceTag(fmt.Sprint(a...)) +} + +// Sprint parse color tags, return rendered string +func Sprint(args ...interface{}) string { + return Render(args...) +} + +// Sprintf format and return rendered string +func Sprintf(format string, a ...interface{}) string { + return ReplaceTag(fmt.Sprintf(format, a...)) +} + +// String alias of the ReplaceTag +func String(s string) string { + return ReplaceTag(s) +} + +// Text alias of the ReplaceTag +func Text(s string) string { + return ReplaceTag(s) +} + +/************************************************************* + * helper methods for print + *************************************************************/ + +// its Win system. linux windows darwin +// func isWindows() bool { +// return runtime.GOOS == "windows" +// } + +func saveInternalError(err error) { + if err != nil { + errors = append(errors, err) + } +} + +// new implementation, support render full color code on pwsh.exe, cmd.exe +func doPrintV2(code, str string) { + _, err := fmt.Fprint(output, RenderString(code, str)) + saveInternalError(err) + + // if isLikeInCmd { + // renderColorCodeOnCmd(func() { + // _, _ = fmt.Fprint(output, RenderString(code, str)) + // }) + // } else { + // _, _ = fmt.Fprint(output, RenderString(code, str)) + // } +} + +// new implementation, support render full color code on pwsh.exe, cmd.exe +func doPrintlnV2(code string, args []interface{}) { + str := formatArgsForPrintln(args) + _, err := fmt.Fprintln(output, RenderString(code, str)) + saveInternalError(err) + + // if isLikeInCmd { + // renderColorCodeOnCmd(func() { + // _, _ = fmt.Fprintln(output, RenderString(code, str)) + // }) + // } else { + // _, _ = fmt.Fprintln(output, RenderString(code, str)) + // } +} + +func stringToArr(str, sep string) (arr []string) { + str = strings.TrimSpace(str) + if str == "" { + return + } + + ss := strings.Split(str, sep) + for _, val := range ss { + if val = strings.TrimSpace(val); val != "" { + arr = append(arr, val) + } + } + return +} + +// if use Println, will add spaces for each arg +func formatArgsForPrintln(args []interface{}) (message string) { + if ln := len(args); ln == 0 { + message = "" + } else if ln == 1 { + message = fmt.Sprint(args[0]) + } else { + message = fmt.Sprintln(args...) + // clear last "\n" + message = message[:len(message)-1] + } + return +} diff --git a/vendor/github.com/gosuri/uilive/.travis.yml b/vendor/github.com/gosuri/uilive/.travis.yml deleted file mode 100644 index 345d80593c2e..000000000000 --- a/vendor/github.com/gosuri/uilive/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: go -sudo: false - -matrix: - include: - - go: 1.11.x - os: linux - - go: 1.12.x - os: linux - - go: 1.11.x - os: linux - env: CROSS_COMPILE=true - - go: 1.12.x - os: linux - env: CROSS_COMPILE=true - - go: 1.11.x - os: osx - - go: 1.12.x - os: osx - -install: - - if [ "$TRAVIS_OS_NAME" = "linux" -a "$CROSS_COMPILE" = "true" ]; then go get github.com/mattn/go-isatty ; fi - - go get -t -v ./... - -script: - - go build - - go test - - if [ "$TRAVIS_OS_NAME" = "linux" -a "$CROSS_COMPILE" = "true" ]; then env GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -v; fi diff --git a/vendor/github.com/gosuri/uilive/LICENSE b/vendor/github.com/gosuri/uilive/LICENSE deleted file mode 100644 index e436d90439dd..000000000000 --- a/vendor/github.com/gosuri/uilive/LICENSE +++ /dev/null @@ -1,10 +0,0 @@ -MIT License -=========== - -Copyright (c) 2015, Greg Osuri - -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. diff --git a/vendor/github.com/gosuri/uilive/Makefile b/vendor/github.com/gosuri/uilive/Makefile deleted file mode 100644 index a39a5d2729dd..000000000000 --- a/vendor/github.com/gosuri/uilive/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -test: - @go test -race . - -examples: - @go run -race ./example - -.PHONY: test examples diff --git a/vendor/github.com/gosuri/uilive/README.md b/vendor/github.com/gosuri/uilive/README.md deleted file mode 100644 index 5de7d820d54b..000000000000 --- a/vendor/github.com/gosuri/uilive/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# uilive [![GoDoc](https://godoc.org/github.com/gosuri/uilive?status.svg)](https://godoc.org/github.com/gosuri/uilive) [![Build Status](https://travis-ci.org/gosuri/uilive.svg?branch=master)](https://travis-ci.org/gosuri/uilive) - -uilive is a go library for updating terminal output in realtime. It provides a buffered [io.Writer](https://golang.org/pkg/io/#Writer) that is flushed at a timed interval. uilive powers [uiprogress](https://github.com/gosuri/uiprogress). - -## Usage Example - -Calling `uilive.New()` will create a new writer. To start rendering, simply call `writer.Start()` and update the ui by writing to the `writer`. Full source for the below example is in [example/main.go](example/main.go). - -```go -writer := uilive.New() -// start listening for updates and render -writer.Start() - -for i := 0; i <= 100; i++ { - fmt.Fprintf(writer, "Downloading.. (%d/%d) GB\n", i, 100) - time.Sleep(time.Millisecond * 5) -} - -fmt.Fprintln(writer, "Finished: Downloaded 100GB") -writer.Stop() // flush and stop rendering -``` - -The above will render - -![example](doc/example.gif) - -## Installation - -```sh -$ go get -v github.com/gosuri/uilive -``` diff --git a/vendor/github.com/gosuri/uilive/doc.go b/vendor/github.com/gosuri/uilive/doc.go deleted file mode 100644 index d4810981256a..000000000000 --- a/vendor/github.com/gosuri/uilive/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package uilive provides a writer that live updates the terminal. It provides a buffered io.Writer that is flushed at a timed interval. -package uilive diff --git a/vendor/github.com/gosuri/uilive/go.mod b/vendor/github.com/gosuri/uilive/go.mod deleted file mode 100644 index 80083d8ed352..000000000000 --- a/vendor/github.com/gosuri/uilive/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gosuri/uilive - -go 1.10 diff --git a/vendor/github.com/gosuri/uilive/terminal_size.go b/vendor/github.com/gosuri/uilive/terminal_size.go deleted file mode 100644 index ebef466649cd..000000000000 --- a/vendor/github.com/gosuri/uilive/terminal_size.go +++ /dev/null @@ -1,37 +0,0 @@ -// +build !windows - -package uilive - -import ( - "os" - "runtime" - "syscall" - "unsafe" -) - -type windowSize struct { - rows uint16 - cols uint16 -} - -var out *os.File -var err error -var sz windowSize - -func getTermSize() (int, int) { - if runtime.GOOS == "openbsd" { - out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0) - if err != nil { - return 0, 0 - } - - } else { - out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0) - if err != nil { - return 0, 0 - } - } - _, _, _ = syscall.Syscall(syscall.SYS_IOCTL, - out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz))) - return int(sz.cols), int(sz.rows) -} diff --git a/vendor/github.com/gosuri/uilive/terminal_size_windows.go b/vendor/github.com/gosuri/uilive/terminal_size_windows.go deleted file mode 100644 index 94b7ed032f8a..000000000000 --- a/vendor/github.com/gosuri/uilive/terminal_size_windows.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build windows - -package uilive - -import ( - "os" - "unsafe" -) - -func getTermSize() (int, int) { - out, err := os.Open("CONOUT$") - if err != nil { - return 0, 0 - } - defer out.Close() - - var csbi consoleScreenBufferInfo - ret, _, _ := procGetConsoleScreenBufferInfo.Call(out.Fd(), uintptr(unsafe.Pointer(&csbi))) - if ret == 0 { - return 0, 0 - } - - return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1) -} diff --git a/vendor/github.com/gosuri/uilive/writer.go b/vendor/github.com/gosuri/uilive/writer.go deleted file mode 100644 index d76542af5a2f..000000000000 --- a/vendor/github.com/gosuri/uilive/writer.go +++ /dev/null @@ -1,173 +0,0 @@ -package uilive - -import ( - "bytes" - "errors" - "io" - "os" - "sync" - "time" -) - -// ESC is the ASCII code for escape character -const ESC = 27 - -// RefreshInterval is the default refresh interval to update the ui -var RefreshInterval = time.Millisecond - -var overFlowHandled bool - -var termWidth int - -// Out is the default output writer for the Writer -var Out = io.Writer(os.Stdout) - -// ErrClosedPipe is the error returned when trying to writer is not listening -var ErrClosedPipe = errors.New("uilive: read/write on closed pipe") - -// FdWriter is a writer with a file descriptor. -type FdWriter interface { - io.Writer - Fd() uintptr -} - -// Writer is a buffered the writer that updates the terminal. The contents of writer will be flushed on a timed interval or when Flush is called. -type Writer struct { - // Out is the writer to write to - Out io.Writer - - // RefreshInterval is the time the UI sould refresh - RefreshInterval time.Duration - - ticker *time.Ticker - tdone chan bool - - buf bytes.Buffer - mtx *sync.Mutex - lineCount int -} - -type bypass struct { - writer *Writer -} - -type newline struct { - writer *Writer -} - -// New returns a new Writer with defaults -func New() *Writer { - termWidth, _ = getTermSize() - if termWidth != 0 { - overFlowHandled = true - } - - return &Writer{ - Out: Out, - RefreshInterval: RefreshInterval, - - mtx: &sync.Mutex{}, - } -} - -// Flush writes to the out and resets the buffer. It should be called after the last call to Write to ensure that any data buffered in the Writer is written to output. -// Any incomplete escape sequence at the end is considered complete for formatting purposes. -// An error is returned if the contents of the buffer cannot be written to the underlying output stream -func (w *Writer) Flush() error { - w.mtx.Lock() - defer w.mtx.Unlock() - - // do nothing if buffer is empty - if len(w.buf.Bytes()) == 0 { - return nil - } - w.clearLines() - - lines := 0 - var currentLine bytes.Buffer - for _, b := range w.buf.Bytes() { - if b == '\n' { - lines++ - currentLine.Reset() - } else { - currentLine.Write([]byte{b}) - if overFlowHandled && currentLine.Len() > termWidth { - lines++ - currentLine.Reset() - } - } - } - w.lineCount = lines - _, err := w.Out.Write(w.buf.Bytes()) - w.buf.Reset() - return err -} - -// Start starts the listener in a non-blocking manner -func (w *Writer) Start() { - if w.ticker == nil { - w.ticker = time.NewTicker(w.RefreshInterval) - w.tdone = make(chan bool) - } - - go w.Listen() -} - -// Stop stops the listener that updates the terminal -func (w *Writer) Stop() { - w.Flush() - w.tdone <- true - <-w.tdone -} - -// Listen listens for updates to the writer's buffer and flushes to the out provided. It blocks the runtime. -func (w *Writer) Listen() { - for { - select { - case <-w.ticker.C: - if w.ticker != nil { - _ = w.Flush() - } - case <-w.tdone: - w.mtx.Lock() - w.ticker.Stop() - w.ticker = nil - w.mtx.Unlock() - close(w.tdone) - return - } - } -} - -// Write save the contents of buf to the writer b. The only errors returned are ones encountered while writing to the underlying buffer. -func (w *Writer) Write(buf []byte) (n int, err error) { - w.mtx.Lock() - defer w.mtx.Unlock() - return w.buf.Write(buf) -} - -// Bypass creates an io.Writer which allows non-buffered output to be written to the underlying output -func (w *Writer) Bypass() io.Writer { - return &bypass{writer: w} -} - -func (b *bypass) Write(p []byte) (int, error) { - b.writer.mtx.Lock() - defer b.writer.mtx.Unlock() - - b.writer.clearLines() - b.writer.lineCount = 0 - return b.writer.Out.Write(p) -} - -// Newline creates an io.Writer which allows buffered output to be written to the underlying output. This enable writing -// to multiple lines at once. -func (w *Writer) Newline() io.Writer { - return &newline{writer: w} -} - -func (n *newline) Write(p []byte) (int, error) { - n.writer.mtx.Lock() - defer n.writer.mtx.Unlock() - return n.writer.buf.Write(p) -} \ No newline at end of file diff --git a/vendor/github.com/gosuri/uilive/writer_posix.go b/vendor/github.com/gosuri/uilive/writer_posix.go deleted file mode 100644 index 50684123002e..000000000000 --- a/vendor/github.com/gosuri/uilive/writer_posix.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build !windows - -package uilive - -import ( - "fmt" - "strings" -) - -// clear the line and move the cursor up -var clear = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC) - -func (w *Writer) clearLines() { - _, _ = fmt.Fprint(w.Out, strings.Repeat(clear, w.lineCount)) -} diff --git a/vendor/github.com/gosuri/uilive/writer_windows.go b/vendor/github.com/gosuri/uilive/writer_windows.go deleted file mode 100644 index 8dafb409ea9b..000000000000 --- a/vendor/github.com/gosuri/uilive/writer_windows.go +++ /dev/null @@ -1,74 +0,0 @@ -// +build windows - -package uilive - -import ( - "fmt" - "strings" - "syscall" - "unsafe" - "github.com/mattn/go-isatty" -) - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") -) - -// clear the line and move the cursor up -var clear = fmt.Sprintf("%c[%dA%c[2K\r", ESC, 0, ESC) - -type short int16 -type dword uint32 -type word uint16 - -type coord struct { - x short - y short -} - -type smallRect struct { - left short - top short - right short - bottom short -} - -type consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord -} - -func (w *Writer) clearLines() { - f, ok := w.Out.(FdWriter) - if ok && !isatty.IsTerminal(f.Fd()) { - ok = false - } - if !ok { - _, _ = fmt.Fprint(w.Out, strings.Repeat(clear, w.lineCount)) - return - } - fd := f.Fd() - var csbi consoleScreenBufferInfo - _, _, _ = procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&csbi))) - - for i := 0; i < w.lineCount; i++ { - // move the cursor up - csbi.cursorPosition.y-- - _, _, _ = procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&csbi.cursorPosition)))) - // clear the line - cursor := coord{ - x: csbi.window.left, - y: csbi.window.top + csbi.cursorPosition.y, - } - var count, w dword - count = dword(csbi.size.x) - _, _, _ = procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) - } -} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 000000000000..5c9c2a30f078 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 000000000000..91b5cef30ebd --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +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. diff --git a/vendor/github.com/mattn/go-runewidth/README.mkd b/vendor/github.com/mattn/go-runewidth/README.mkd new file mode 100644 index 000000000000..66663a94b0b4 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/README.mkd @@ -0,0 +1,27 @@ +go-runewidth +============ + +[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD) +[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) +[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) + +Provides functions to get fixed width of the character or string. + +Usage +----- + +```go +runewidth.StringWidth("つのだ☆HIRO") == 12 +``` + + +Author +------ + +Yasuhiro Matsumoto + +License +------- + +under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/mattn/go-runewidth/go.mod b/vendor/github.com/mattn/go-runewidth/go.mod new file mode 100644 index 000000000000..fa7f4d864e49 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/go.mod @@ -0,0 +1,3 @@ +module github.com/mattn/go-runewidth + +go 1.9 diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go new file mode 100644 index 000000000000..8d64da077840 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -0,0 +1,258 @@ +package runewidth + +import ( + "os" +) + +//go:generate go run script/generate.go + +var ( + // EastAsianWidth will be set true if the current locale is CJK + EastAsianWidth bool + + // ZeroWidthJoiner is flag to set to use UTR#51 ZWJ + ZeroWidthJoiner bool + + // DefaultCondition is a condition in current locale + DefaultCondition = &Condition{} +) + +func init() { + handleEnv() +} + +func handleEnv() { + env := os.Getenv("RUNEWIDTH_EASTASIAN") + if env == "" { + EastAsianWidth = IsEastAsian() + } else { + EastAsianWidth = env == "1" + } + // update DefaultCondition + DefaultCondition.EastAsianWidth = EastAsianWidth + DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner +} + +type interval struct { + first rune + last rune +} + +type table []interval + +func inTables(r rune, ts ...table) bool { + for _, t := range ts { + if inTable(r, t) { + return true + } + } + return false +} + +func inTable(r rune, t table) bool { + // func (t table) IncludesRune(r rune) bool { + if r < t[0].first { + return false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) >> 1 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return true + } + } + + return false +} + +var private = table{ + {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, +} + +var nonprint = table{ + {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, + {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, + {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, +} + +// Condition have flag EastAsianWidth whether the current locale is CJK or not. +type Condition struct { + EastAsianWidth bool + ZeroWidthJoiner bool +} + +// NewCondition return new instance of Condition which is current locale. +func NewCondition() *Condition { + return &Condition{ + EastAsianWidth: EastAsianWidth, + ZeroWidthJoiner: ZeroWidthJoiner, + } +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func (c *Condition) RuneWidth(r rune) int { + switch { + case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining, notassigned): + return 0 + case (c.EastAsianWidth && IsAmbiguousWidth(r)) || inTables(r, doublewidth): + return 2 + default: + return 1 + } +} + +func (c *Condition) stringWidth(s string) (width int) { + for _, r := range []rune(s) { + width += c.RuneWidth(r) + } + return width +} + +func (c *Condition) stringWidthZeroJoiner(s string) (width int) { + r1, r2 := rune(0), rune(0) + for _, r := range []rune(s) { + if r == 0xFE0E || r == 0xFE0F { + continue + } + w := c.RuneWidth(r) + if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) { + if width < w { + width = w + } + } else { + width += w + } + r1, r2 = r2, r + } + return width +} + +// StringWidth return width as you can see +func (c *Condition) StringWidth(s string) (width int) { + if c.ZeroWidthJoiner { + return c.stringWidthZeroJoiner(s) + } + return c.stringWidth(s) +} + +// Truncate return string truncated with w cells +func (c *Condition) Truncate(s string, w int, tail string) string { + if c.StringWidth(s) <= w { + return s + } + r := []rune(s) + tw := c.StringWidth(tail) + w -= tw + width := 0 + i := 0 + for ; i < len(r); i++ { + cw := c.RuneWidth(r[i]) + if width+cw > w { + break + } + width += cw + } + return string(r[0:i]) + tail +} + +// Wrap return string wrapped with w cells +func (c *Condition) Wrap(s string, w int) string { + width := 0 + out := "" + for _, r := range []rune(s) { + cw := RuneWidth(r) + if r == '\n' { + out += string(r) + width = 0 + continue + } else if width+cw > w { + out += "\n" + width = 0 + out += string(r) + width += cw + continue + } + out += string(r) + width += cw + } + return out +} + +// FillLeft return string filled in left by spaces in w cells +func (c *Condition) FillLeft(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return string(b) + s + } + return s +} + +// FillRight return string filled in left by spaces in w cells +func (c *Condition) FillRight(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return s + string(b) + } + return s +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func RuneWidth(r rune) int { + return DefaultCondition.RuneWidth(r) +} + +// IsAmbiguousWidth returns whether is ambiguous width or not. +func IsAmbiguousWidth(r rune) bool { + return inTables(r, private, ambiguous) +} + +// IsNeutralWidth returns whether is neutral width or not. +func IsNeutralWidth(r rune) bool { + return inTable(r, neutral) +} + +// StringWidth return width as you can see +func StringWidth(s string) (width int) { + return DefaultCondition.StringWidth(s) +} + +// Truncate return string truncated with w cells +func Truncate(s string, w int, tail string) string { + return DefaultCondition.Truncate(s, w, tail) +} + +// Wrap return string wrapped with w cells +func Wrap(s string, w int) string { + return DefaultCondition.Wrap(s, w) +} + +// FillLeft return string filled in left by spaces in w cells +func FillLeft(s string, w int) string { + return DefaultCondition.FillLeft(s, w) +} + +// FillRight return string filled in left by spaces in w cells +func FillRight(s string, w int) string { + return DefaultCondition.FillRight(s, w) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go new file mode 100644 index 000000000000..7d99f6e52103 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go @@ -0,0 +1,8 @@ +// +build appengine + +package runewidth + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go new file mode 100644 index 000000000000..c5fdf40baa0e --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -0,0 +1,9 @@ +// +build js +// +build !appengine + +package runewidth + +func IsEastAsian() bool { + // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go new file mode 100644 index 000000000000..66a58b5d8736 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -0,0 +1,79 @@ +// +build !windows +// +build !js +// +build !appengine + +package runewidth + +import ( + "os" + "regexp" + "strings" +) + +var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) + +var mblenTable = map[string]int{ + "utf-8": 6, + "utf8": 6, + "jis": 8, + "eucjp": 3, + "euckr": 2, + "euccn": 2, + "sjis": 2, + "cp932": 2, + "cp51932": 2, + "cp936": 2, + "cp949": 2, + "cp950": 2, + "big5": 2, + "gbk": 2, + "gb2312": 2, +} + +func isEastAsian(locale string) bool { + charset := strings.ToLower(locale) + r := reLoc.FindStringSubmatch(locale) + if len(r) == 2 { + charset = strings.ToLower(r[1]) + } + + if strings.HasSuffix(charset, "@cjk_narrow") { + return false + } + + for pos, b := range []byte(charset) { + if b == '@' { + charset = charset[:pos] + break + } + } + max := 1 + if m, ok := mblenTable[charset]; ok { + max = m + } + if max > 1 && (charset[0] != 'u' || + strings.HasPrefix(locale, "ja") || + strings.HasPrefix(locale, "ko") || + strings.HasPrefix(locale, "zh")) { + return true + } + return false +} + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + locale := os.Getenv("LC_CTYPE") + if locale == "" { + locale = os.Getenv("LANG") + } + + // ignore C locale + if locale == "POSIX" || locale == "C" { + return false + } + if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { + return false + } + + return isEastAsian(locale) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_table.go b/vendor/github.com/mattn/go-runewidth/runewidth_table.go new file mode 100644 index 000000000000..9ca6d0e28b1a --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_table.go @@ -0,0 +1,427 @@ +package runewidth + +var combining = table{ + {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, + {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01}, + {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1ABE}, + {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, + {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, + {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, + {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, + {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, + {0x10F46, 0x10F50}, {0x11300, 0x11301}, {0x1133B, 0x1133C}, + {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4}, + {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, + {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, + {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, + {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, +} + +var doublewidth = table{ + {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, + {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, + {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, + {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, + {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, + {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, + {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, + {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, + {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, + {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, + {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31BA}, + {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, + {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, + {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, + {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, + {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, + {0x16FE0, 0x16FE3}, {0x17000, 0x187F7}, {0x18800, 0x18AF2}, + {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, + {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, + {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, + {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, + {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, + {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, + {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, + {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, + {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, + {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, + {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, + {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D5}, {0x1F6EB, 0x1F6EC}, + {0x1F6F4, 0x1F6FA}, {0x1F7E0, 0x1F7EB}, {0x1F90D, 0x1F971}, + {0x1F973, 0x1F976}, {0x1F97A, 0x1F9A2}, {0x1F9A5, 0x1F9AA}, + {0x1F9AE, 0x1F9CA}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA73}, + {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA82}, {0x1FA90, 0x1FA95}, + {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, +} + +var ambiguous = table{ + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, + {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, + {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, + {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, + {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, + {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, + {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, + {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, + {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, + {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, + {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, + {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, + {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, + {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, + {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, + {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, + {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, + {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, + {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, + {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, + {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, + {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, + {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, + {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, + {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, + {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, + {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, + {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, + {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, + {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, + {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, + {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, + {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, + {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, + {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, + {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, + {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, +} +var notassigned = table{ + {0x27E6, 0x27ED}, {0x2985, 0x2986}, +} + +var neutral = table{ + {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9}, + {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, + {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, + {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, + {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, + {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, + {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, + {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, + {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, + {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, + {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, + {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, + {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, + {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250}, + {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6}, + {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, + {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, + {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F}, + {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390}, + {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400}, + {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F}, + {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F}, + {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, + {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A}, + {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, + {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, + {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08BD}, + {0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990}, + {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, + {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, + {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, + {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, + {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, + {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, + {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, + {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, + {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, + {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, + {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, + {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, + {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, + {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, + {0x0B56, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, + {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, + {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, + {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, + {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, + {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, + {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, + {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, + {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, + {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3}, + {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D03}, + {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, + {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, + {0x0D66, 0x0D7F}, {0x0D82, 0x0D83}, {0x0D85, 0x0D96}, + {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, + {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, + {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, + {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, + {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, + {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, + {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, + {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, + {0x0F49, 0x0F6C}, {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, + {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, + {0x10C7, 0x10C7}, {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, + {0x1160, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, + {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, + {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, + {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, + {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, + {0x1318, 0x135A}, {0x135D, 0x137C}, {0x1380, 0x1399}, + {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x169C}, + {0x16A0, 0x16F8}, {0x1700, 0x170C}, {0x170E, 0x1714}, + {0x1720, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C}, + {0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD}, + {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x180E}, + {0x1810, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA}, + {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B}, + {0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D}, + {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, + {0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, + {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, + {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ABE}, {0x1B00, 0x1B4B}, + {0x1B50, 0x1B7C}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, + {0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, + {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, + {0x1DFB, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, + {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, + {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, + {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, + {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, + {0x1FF6, 0x1FFE}, {0x2000, 0x200F}, {0x2011, 0x2012}, + {0x2017, 0x2017}, {0x201A, 0x201B}, {0x201E, 0x201F}, + {0x2023, 0x2023}, {0x2028, 0x202F}, {0x2031, 0x2031}, + {0x2034, 0x2034}, {0x2036, 0x203A}, {0x203C, 0x203D}, + {0x203F, 0x2064}, {0x2066, 0x2071}, {0x2075, 0x207E}, + {0x2080, 0x2080}, {0x2085, 0x208E}, {0x2090, 0x209C}, + {0x20A0, 0x20A8}, {0x20AA, 0x20AB}, {0x20AD, 0x20BF}, + {0x20D0, 0x20F0}, {0x2100, 0x2102}, {0x2104, 0x2104}, + {0x2106, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2115}, + {0x2117, 0x2120}, {0x2123, 0x2125}, {0x2127, 0x212A}, + {0x212C, 0x2152}, {0x2155, 0x215A}, {0x215F, 0x215F}, + {0x216C, 0x216F}, {0x217A, 0x2188}, {0x218A, 0x218B}, + {0x219A, 0x21B7}, {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, + {0x21D5, 0x21E6}, {0x21E8, 0x21FF}, {0x2201, 0x2201}, + {0x2204, 0x2206}, {0x2209, 0x220A}, {0x220C, 0x220E}, + {0x2210, 0x2210}, {0x2212, 0x2214}, {0x2216, 0x2219}, + {0x221B, 0x221C}, {0x2221, 0x2222}, {0x2224, 0x2224}, + {0x2226, 0x2226}, {0x222D, 0x222D}, {0x222F, 0x2233}, + {0x2238, 0x223B}, {0x223E, 0x2247}, {0x2249, 0x224B}, + {0x224D, 0x2251}, {0x2253, 0x225F}, {0x2262, 0x2263}, + {0x2268, 0x2269}, {0x226C, 0x226D}, {0x2270, 0x2281}, + {0x2284, 0x2285}, {0x2288, 0x2294}, {0x2296, 0x2298}, + {0x229A, 0x22A4}, {0x22A6, 0x22BE}, {0x22C0, 0x2311}, + {0x2313, 0x2319}, {0x231C, 0x2328}, {0x232B, 0x23E8}, + {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x2426}, + {0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F}, + {0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F}, + {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, + {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, + {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, + {0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604}, + {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, + {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, + {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, + {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, + {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, + {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, + {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, + {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, + {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, + {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, + {0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF}, + {0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984}, + {0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, + {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2C2E}, + {0x2C30, 0x2C5E}, {0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, + {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, + {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, + {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, + {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, + {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E4F}, {0x303F, 0x303F}, + {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, + {0xA700, 0xA7BF}, {0xA7C2, 0xA7C6}, {0xA7F7, 0xA82B}, + {0xA830, 0xA839}, {0xA840, 0xA877}, {0xA880, 0xA8C5}, + {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, + {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, + {0xAA00, 0xAA36}, {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, + {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, + {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, + {0xAB28, 0xAB2E}, {0xAB30, 0xAB67}, {0xAB70, 0xABED}, + {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, + {0xD800, 0xDFFF}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, + {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, + {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, + {0xFBD3, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, + {0xFDF0, 0xFDFD}, {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, + {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, + {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, + {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, + {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, + {0x10137, 0x1018E}, {0x10190, 0x1019B}, {0x101A0, 0x101A0}, + {0x101D0, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, + {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A}, + {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, + {0x103C8, 0x103D5}, {0x10400, 0x1049D}, {0x104A0, 0x104A9}, + {0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, + {0x10530, 0x10563}, {0x1056F, 0x1056F}, {0x10600, 0x10736}, + {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10800, 0x10805}, + {0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838}, + {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E}, + {0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, + {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F}, + {0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, + {0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, + {0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, + {0x10A50, 0x10A58}, {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, + {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, + {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, + {0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, + {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, + {0x10E60, 0x10E7E}, {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, + {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F}, + {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, + {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11146}, + {0x11150, 0x11176}, {0x11180, 0x111CD}, {0x111D0, 0x111DF}, + {0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x1123E}, + {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, + {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, + {0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C}, + {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330}, + {0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133B, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, + {0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C}, + {0x11370, 0x11374}, {0x11400, 0x11459}, {0x1145B, 0x1145B}, + {0x1145D, 0x1145F}, {0x11480, 0x114C7}, {0x114D0, 0x114D9}, + {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644}, + {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B8}, + {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, + {0x11730, 0x1173F}, {0x11800, 0x1183B}, {0x118A0, 0x118F2}, + {0x118FF, 0x118FF}, {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, + {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, + {0x11AC0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, + {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, + {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, + {0x11D08, 0x11D09}, {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, + {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, + {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, + {0x11D90, 0x11D91}, {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, + {0x11EE0, 0x11EF8}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, + {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, + {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646}, + {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, + {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, + {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, + {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, + {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, + {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5}, + {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245}, + {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, + {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, + {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, + {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, + {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, + {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, + {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, + {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, + {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, + {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9}, + {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, + {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, + {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, + {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, + {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, + {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, + {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, + {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, + {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, + {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, + {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, + {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, + {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, + {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, + {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, + {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, + {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C}, {0x1F12E, 0x1F12F}, + {0x1F16A, 0x1F16C}, {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C}, + {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F}, + {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3}, + {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, + {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F}, + {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3}, + {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB}, + {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA}, + {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D8}, + {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, + {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0x1F900, 0x1F90B}, + {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, {0xE0001, 0xE0001}, + {0xE0020, 0xE007F}, +} + +var emoji = table{ + {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, + {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, + {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, + {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, + {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, + {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, + {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, + {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, + {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, + {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, + {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, + {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, + {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, + {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, + {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, + {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, + {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, + {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, + {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, + {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, + {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, + {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, + {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, + {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FFFD}, +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go new file mode 100644 index 000000000000..d6a61777d7b1 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -0,0 +1,28 @@ +// +build windows +// +build !appengine + +package runewidth + +import ( + "syscall" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") +) + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + r1, _, _ := procGetConsoleOutputCP.Call() + if r1 == 0 { + return false + } + + switch int(r1) { + case 932, 51932, 936, 949, 950: + return true + } + + return false +} diff --git a/vendor/github.com/mitchellh/go-glint/LICENSE b/vendor/github.com/mitchellh/go-glint/LICENSE new file mode 100644 index 000000000000..5e027844e90a --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Mitchell Hashimoto + +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. diff --git a/vendor/github.com/mitchellh/go-glint/README.md b/vendor/github.com/mitchellh/go-glint/README.md new file mode 100644 index 000000000000..4c5734f0068f --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/README.md @@ -0,0 +1,79 @@ +# go-glint [![Godoc](https://godoc.org/github.com/mitchellh/go-glint?status.svg)](https://godoc.org/github.com/mitchellh/go-glint) + +Glint is a component-based UI framework specifically targeted towards +command-line interfaces. This allows you to create highly dynamic CLI interfaces +using shared, easily testable components. Glint uses a Flexbox implementation +to make it easy to lay out components in the CLI, including paddings, margins, +and more. + +**API Status: Unstable.** We're still actively working on the API and +may change it in backwards incompatible ways. See the roadmap section in +particular for work that may impact the API. In particular, we have +integrated this library into [Waypoint](https://github.com/hashicorp/waypoint), +and the experience of using this library in the real world will likely drive major +changes. + +## Example + +The example below shows a simple dynamic counter: + +```go +func main() { + var counter uint32 + go func() { + for { + time.Sleep(100 * time.Millisecond) + atomic.AddUint32(&counter, 1) + } + }() + + d := glint.New() + d.Append( + glint.Style( + glint.TextFunc(func(rows, cols uint) string { + return fmt.Sprintf("%d tests passed", atomic.LoadUint32(&counter)) + }), + glint.Color("green"), + ), + ) + d.Render(context.Background()) +} +``` + +Output: + +![Example](https://user-images.githubusercontent.com/1299/92431533-9baf8000-f14c-11ea-94ad-8ff97ed26fec.gif) + +## Roadmap + +Glint is still an early stage project and there is a lot that we want to +improve on. This may introduce some backwards incompatibilities but we are +trying to stabilize the API as quickly as possible. + +* **Non-interactive interfaces.** We want to add support for rendering to +non-interactive interfaces and allowing components to provide custom behavior +in these cases. For now, users of Glint should detect non-interactivity and +avoid using Glint. + +* **Windows PowerShell and Cmd.** Glint works fine in ANSI-compatible terminals +on Windows, but doesn't work with PowerShell and Cmd. We want to make this +work. + +* **Dirty tracking.** Glint currently rerenders the entire frame on each +tick. I'd like components to be able to report if there are changes (if they +are "dirty") and need to be rerendered. We could then more efficiently +recalculate layouts and rerender outputs. + +* **User Input.** Glint should be able to query for user input and render +this within its existing set of components. + +* **Expose styling to custom renderers.** Currently the `Style` component +is a special-case for the terminal renderer to render colors. I'd like to expose +the styles in a way that other renderers could use it in some meaningful way. + +## Thanks + +This library is heavily inspired by the [Ink project](https://github.com/vadimdemedes/ink). +I saw this project and thought that having a central render loop along with +a full layout engine was a fantastic idea. Most of my projects are in Go +so I wanted to be able to realize these benefits with Go. Thank you! diff --git a/vendor/github.com/mitchellh/go-glint/component.go b/vendor/github.com/mitchellh/go-glint/component.go new file mode 100644 index 000000000000..dcda26057e82 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/component.go @@ -0,0 +1,89 @@ +package glint + +import ( + "context" + + "github.com/mitchellh/go-glint/internal/layout" +) + +// Components are the individual items that are rendered within a document. +type Component interface { + // Body returns the body of this component. This can be another custom + // component or a standard component such as Text. + // + // The context parameter is used to send parameters across multiple + // components. It should not be used for timeouts; Body should aim to + // not block ever since this can block the render loop. + // + // Components are highly encouraged to support finalization (see + // ComponentFinalizer). Components can finalize early by wrapping + // their body in a Finalize built-in component. Finalization allows + // the renderer to highly optimize output. + Body(context.Context) Component +} + +// ComponentFinalizer allows components to be notified they are going to +// be finalized. A finalized component may never be re-rendered again. The +// next call to Body should be considered the final call. +// +// In a Document, if the component list has a set of finalized components +// at the front, the renderer will draw it once and only re-draw non-finalized +// components. For example, consider a document that is a set of text components +// followed by a progress bar. If the text components are static, then they +// will be written to the output once and only the progress bar will redraw. +// +// Currently, Body may be called multiple times after Finalize. Implementers +// should return the same result after being finalized. +type ComponentFinalizer interface { + Component + + // Finalize notifies the component that it will be finalized. This may + // be called multiple times. + Finalize() +} + +// ComponentMounter allows components to be notified when they are +// mounted and unmounted. A mounted component is one that is added to +// a render tree for the first time. A component is unmounted when it is +// removed from the render tree. +// +// The callbacks here may be called multiple times under certain scenarios: +// (1) a component is used in multiple Document instances, (2) a component +// is unmounted and then remounted in the future. +// +// A component mounted multiple times in the same render tree does NOT +// have the mount callbacks called multiple times. +// +// A good use case for this interface is setting up and cleaning up resources. +type ComponentMounter interface { + Component + + // Mount is called when the component is added to a render tree. The + // context given to this is used to access data set by Glint and the + // renderer in use. + Mount(context.Context) + + // Unmount is called when the component is removed from a render tree. + // This will be called under ANY scenario where the component is + // removed from the render tree, including finalization. + Unmount(context.Context) +} + +// componentLayout can be implemented to set custom layout settings +// for the component. This can only be implemented by internal components +// since we use an internal library. +// +// End users should use the "Layout" component to set layout options. +type componentLayout interface { + Component + + // Layout should return the layout settings for this component. + Layout() *layout.Builder +} + +// terminalComponent is an embeddable struct for internal usage that +// satisfies Component. This is used since terminal components are handled +// as special cases. +type terminalComponent struct{} + +func (terminalComponent) Body(context.Context) Component { return nil } diff --git a/vendor/github.com/mitchellh/go-glint/components/progress.go b/vendor/github.com/mitchellh/go-glint/components/progress.go new file mode 100644 index 000000000000..b737c524cac5 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/components/progress.go @@ -0,0 +1,38 @@ +package components + +import ( + "context" + + "github.com/cheggaaa/pb/v3" + "github.com/mitchellh/go-glint" +) + +// ProgressElement renders a progress bar. This wraps the cheggaaa/pb package +// since that provides important functionality. This uses single call renders +// to render the progress bar as values change. +type ProgressElement struct { + *pb.ProgressBar +} + +// Progress creates a new progress bar element with the given total. +// For more fine-grained control, please construct a ProgressElement +// directly. +func Progress(total int) *ProgressElement { + return &ProgressElement{ + ProgressBar: pb.New(total), + } +} + +func (el *ProgressElement) Body(context.Context) glint.Component { + // If we have no progress bar render nothing. + if el.ProgressBar == nil { + return nil + } + + // Write the current progress + return glint.TextFunc(func(rows, cols uint) string { + el.ProgressBar.SetWidth(int(cols)) + + return el.ProgressBar.String() + }) +} diff --git a/vendor/github.com/mitchellh/go-glint/components/sparkline.go b/vendor/github.com/mitchellh/go-glint/components/sparkline.go new file mode 100644 index 000000000000..4ad464356e18 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/components/sparkline.go @@ -0,0 +1,112 @@ +package components + +import ( + "container/ring" + "context" + "math" + "sync" + + "github.com/mitchellh/go-glint" +) + +// SparklineComponent renders a sparkline graph. +type SparklineComponent struct { + sync.Mutex + + // If set, this will style the peak value. + PeakStyle []glint.StyleOption + + values *ring.Ring +} + +// Sparkline creates a SparklineComponent with the given set of initial values. +// These initial values will also specify the max width for this sparkline +// unless values are replaced with Set. +func Sparkline(values []uint) *SparklineComponent { + var c SparklineComponent + c.Set(values) + return &c +} + +// Set sets the full set of values to the given slice. This will also reset +// the size of the sparkline to this length. +func (c *SparklineComponent) Set(values []uint) { + c.Lock() + defer c.Unlock() + c.values = ring.New(len(values)) + for _, v := range values { + c.values.Value = v + c.values = c.values.Next() + } +} + +// Append adds the given values to the end of the values buffer. The buffer +// size is determined by the values list given in Sparkline or Set. This will +// overwrite the oldest values. +func (c *SparklineComponent) Append(values ...uint) { + c.Lock() + defer c.Unlock() + for _, v := range values { + c.values.Value = v + c.values = c.values.Next() + } +} + +func (c *SparklineComponent) valuesSlice() []uint { + result := make([]uint, c.values.Len()) + for i := range result { + result[i] = c.values.Value.(uint) + c.values = c.values.Next() + } + + return result +} + +func (c *SparklineComponent) Body(context.Context) glint.Component { + c.Lock() + defer c.Unlock() + + values := c.valuesSlice() + + // If we have nothing we render nothing + if len(values) == 0 { + return nil + } + + // Find the max + max := values[0] + if len(values) > 1 { + for _, v := range values[1:] { + if v > max { + max = v + } + } + } + + // Build each symbol + peak := false + parts := make([]glint.Component, len(values)) + for i, v := range values { + symbolIdx := int(math.Ceil(float64(v) / float64(max) * float64(len(sparklineSymbols)-1))) + parts[i] = glint.Text(string(sparklineSymbols[symbolIdx])) + + if len(c.PeakStyle) > 0 && v == max && !peak { + peak = true + parts[i] = glint.Style(parts[i], c.PeakStyle...) + } + } + + // Render them in a row + return glint.Layout(parts...).Row() +} + +var sparklineSymbols = []rune{ + '\u2581', + '\u2582', + '\u2583', + '\u2584', + '\u2585', + '\u2586', + '\u2587', + '\u2588', +} diff --git a/vendor/github.com/mitchellh/go-glint/components/spinner.go b/vendor/github.com/mitchellh/go-glint/components/spinner.go new file mode 100644 index 000000000000..97ec030859e9 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/components/spinner.go @@ -0,0 +1,36 @@ +package components + +import ( + "context" + "time" + + "github.com/mitchellh/go-glint" + "github.com/tj/go-spin" +) + +// Spinner creates a new spinner. The created spinner should NOT be started +// or data races will occur that can result in a panic. +func Spinner() *SpinnerComponent { + // Create our spinner and setup our default frames + s := spin.New() + s.Set(spin.Default) + + return &SpinnerComponent{ + s: s, + } +} + +type SpinnerComponent struct { + s *spin.Spinner + last time.Time +} + +func (c *SpinnerComponent) Body(context.Context) glint.Component { + current := time.Now() + if c.last.IsZero() || current.Sub(c.last) > 150*time.Millisecond { + c.last = current + c.s.Next() + } + + return glint.Text(c.s.Current()) +} diff --git a/vendor/github.com/mitchellh/go-glint/components/stopwatch.go b/vendor/github.com/mitchellh/go-glint/components/stopwatch.go new file mode 100644 index 000000000000..f8c29cf5c712 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/components/stopwatch.go @@ -0,0 +1,23 @@ +package components + +import ( + "context" + "time" + + "github.com/mitchellh/go-glint" +) + +// Stopwatch creates a new stopwatch component that starts at the given time. +func Stopwatch(start time.Time) *StopwatchComponent { + return &StopwatchComponent{ + start: start, + } +} + +type StopwatchComponent struct { + start time.Time +} + +func (c *StopwatchComponent) Body(context.Context) glint.Component { + return glint.Text(time.Now().Sub(c.start).Truncate(100 * time.Millisecond).String()) +} diff --git a/vendor/github.com/mitchellh/go-glint/context.go b/vendor/github.com/mitchellh/go-glint/context.go new file mode 100644 index 000000000000..959c3e49c690 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/context.go @@ -0,0 +1,18 @@ +package glint + +// Context is a component type that can be used to set data on the context +// given to Body calls for components that are children of this component. +func Context(inner Component, kv ...interface{}) Component { + if len(kv)%2 != 0 { + panic("kv must be set in pairs") + } + + return &contextComponent{inner: inner, pairs: kv} +} + +type contextComponent struct { + terminalComponent + + inner Component + pairs []interface{} +} diff --git a/vendor/github.com/mitchellh/go-glint/document.go b/vendor/github.com/mitchellh/go-glint/document.go new file mode 100644 index 000000000000..45a44074c848 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/document.go @@ -0,0 +1,317 @@ +package glint + +import ( + "context" + "io" + "os" + "sync" + "time" + + "github.com/mitchellh/go-glint/flex" +) + +// Document is the primary structure for managing and drawing components. +// +// A document represents a terminal window or session. The output can be set and +// components can be added, rendered, and drawn. All the methods on a Document +// are thread-safe unless otherwise documented. This allows you to draw, +// add components, replace components, etc. all while the render loop is active. +// +// Currently, this can only render directly to an io.Writer that expects to +// be a terminal session. In the future, we'll further abstract the concept +// of a "renderer" so that rendering can be done to other mediums as well. +type Document struct { + mu sync.Mutex + r Renderer + els []Component + refreshRate time.Duration + prevRoot *flex.Node + mounted map[ComponentMounter]struct{} + paused bool + closed bool +} + +// New returns a Document that will output to stdout. +func New() *Document { + var d Document + d.SetRenderer(&TerminalRenderer{ + Output: os.Stdout, + }) + + return &d +} + +// SetRenderer sets the renderer to use. If this isn't set then Render +// will do nothing and return immediately. Changes to this will have no +// impact on active render loops. +func (d *Document) SetRenderer(r Renderer) { + d.mu.Lock() + defer d.mu.Unlock() + d.r = r +} + +// SetRefreshRate sets the rate at which output is rendered. +func (d *Document) SetRefreshRate(dur time.Duration) { + d.mu.Lock() + defer d.mu.Unlock() + d.refreshRate = dur +} + +// Append appends components to the document. +func (d *Document) Append(el ...Component) { + d.mu.Lock() + defer d.mu.Unlock() + d.els = append(d.els, el...) +} + +// Set sets the components for the document. This will replace all +// previous components. +func (d *Document) Set(els ...Component) { + d.mu.Lock() + defer d.mu.Unlock() + d.els = els +} + +// Close ensures that all elements are unmounted by finalizing all the +// output and then calling RenderFrame. Users of Document should ensure +// that Close is always called. +func (d *Document) Close() error { + d.mu.Lock() + if d.closed { + d.mu.Unlock() + return nil + } + + for i, el := range d.els { + d.els[i] = Finalize(el) + } + + d.closed = true + r := d.r + d.mu.Unlock() + + // We call RenderFrame twice to ensure we remove the elements AND + // call Unmount on them. + d.RenderFrame() + d.RenderFrame() + + // If our renderer implements closer then call close + if c, ok := r.(io.Closer); ok { + c.Close() + } + + return nil +} + +// Pause will pause the renderer. This will case RenderFrame to do nothing +// until Resume is called. The use case for this is if you want to wait for +// input (stdin) or any other reason. +func (d *Document) Pause() { + d.mu.Lock() + defer d.mu.Unlock() + d.paused = true +} + +// Resume undoes a Pause call. If not paused, this does nothing. +func (d *Document) Resume() { + d.mu.Lock() + defer d.mu.Unlock() + d.paused = false +} + +// Render starts a render loop that continues to render until the +// context is cancelled. This will render at the configured refresh rate. +// If the refresh rate is changed, it will not affect an active render loop. +// You must cancel and restart the render loop. +func (d *Document) Render(ctx context.Context) { + d.mu.Lock() + dur := d.refreshRate + d.mu.Unlock() + if dur == 0 { + dur = time.Second / 24 + } + + for { + // Render. We time the render so that we can adapt the framerate + // if the render is taking too long. + start := time.Now() + d.RenderFrame() + renderDur := time.Now().Sub(start) + + // If our context is canceled, end. + if ctx.Err() != nil { + return + } + + // If the duration is greater than our goal duration, then we + // adapt our duration. Otherwise, we sleep the duration we want + // and continue + sleepDur := dur + if renderDur > dur { + sleepDur = renderDur + + // We slow our attempted framerate down at most to 1 fps + if sleepDur > 1*time.Second { + sleepDur = 1 * time.Second + } + } + + time.Sleep(sleepDur) + } +} + +// RenderFrame will render a single frame and return. +// +// If a manual size is not configured, this will recalcualte the window +// size on each call. This typically requires a syscall. This is a bit +// expensive but something we can optimize in the future if it ends up being +// a real source of FPS issues. +func (d *Document) RenderFrame() { + d.mu.Lock() + defer d.mu.Unlock() + + // If we're paused do nothing. + if d.paused { + return + } + + // If we don't have a renderer set, then don't render anything. + if d.r == nil { + return + } + + // Our context + ctx := WithRenderer(context.Background(), d.r) + + // Setup our root node + root := d.r.LayoutRoot() + + // Build our render tree + tree(ctx, root, Fragment(d.els...), false) + + // Calculate the layout + flex.CalculateLayout(root, flex.Undefined, flex.Undefined, flex.DirectionLTR) + + // Fix any text nodes that need to be fixed. + d.handleNodes(ctx, root, nil) + + // If the height of the root is zero then we do nothing. + if uint(root.LayoutGetHeight()) == 0 { + return + } + + // Render the tree + d.r.RenderRoot(root, d.prevRoot) + + // Store how much we drew + height := uint(root.LayoutGetHeight()) + + // If our component list is prefixed with finalized components, we + // prune these out and do not re-render them. + finalIdx := -1 + for i, el := range d.els { + child := root.GetChild(i) + if child == nil { + break + } + + // If the component is not finalized then we exit. If the + // component doesn't match our expectations it means we hit + // something weird and we exit too. + ctx, ok := child.Context.(*parentContext) + if !ok || ctx == nil || ctx.C != el || !ctx.Finalized { + break + } + + // If this is finalized, then we have to subtract from the + // height the height of this child since we're not going to redraw. + // Then continue until we find one that isn't finalized. + height -= uint(child.LayoutGetHeight()) + finalIdx = i + } + if finalIdx >= 0 { + // Change our elements + els := d.els[finalIdx+1:] + d.els = make([]Component, len(els)) + copy(d.els, els) + + // Reset the height on the root so that it reflects this change + root.Layout.Dimensions[flex.DimensionHeight] = float32(height) + } + + // Store our previous root + d.prevRoot = root +} + +func (d *Document) handleNodes( + ctx context.Context, + parent *flex.Node, + seen map[ComponentMounter]struct{}, +) { + // For our first call, we detect the root since we use it later + // to do some final calls. + root := seen == nil + if root { + seen = map[ComponentMounter]struct{}{} + } + + for _, child := range parent.Children { + if tctx, ok := child.Context.(treeContext); ok { + c := tctx.Component() + + // Mount callbacks + if mc, ok := c.(ComponentMounter); ok { + // Only if we haven't seen this already... + if _, ok := seen[mc]; !ok { + seen[mc] = struct{}{} + + if d.mounted == nil { + d.mounted = map[ComponentMounter]struct{}{} + } + + // And we haven't notified this already... + if _, ok := d.mounted[mc]; !ok { + d.mounted[mc] = struct{}{} + + // Notify + mc.Mount(ctx) + } + } + } + + continue + } + + // If the height/width that the layout engine calculated is less than + // the height that we originally measured, then we need to give the + // element a chance to rerender into that dimension. + if tctx, ok := child.Context.(*TextNodeContext); ok { + height := child.LayoutGetHeight() + width := child.LayoutGetWidth() + if height < tctx.Size.Height || width < tctx.Size.Width { + child.Measure(child, + width, flex.MeasureModeAtMost, + height, flex.MeasureModeAtMost, + ) + } + } + + d.handleNodes(ctx, child, seen) + } + + // If we're the root call, then we preform some final calls. Otherwise + // we just return, we're done. + if !root { + return + } + + // Go through our previously mounted set and if we didn't see it, + // then call unmount on it. After we're done, what we saw is our new + // map of mounted elements. + for mc := range d.mounted { + if _, ok := seen[mc]; !ok { + mc.Unmount(ctx) + } + } + d.mounted = seen +} diff --git a/vendor/github.com/mitchellh/go-glint/finalize.go b/vendor/github.com/mitchellh/go-glint/finalize.go new file mode 100644 index 000000000000..58b5720524ed --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/finalize.go @@ -0,0 +1,19 @@ +package glint + +import "context" + +// Finalize reutrns a component that will finalize the input component. +// See ComponentFinalizer for documentation on what finalization means. +func Finalize(c Component) Component { + return &finalizedComponent{ + Component: c, + } +} + +type finalizedComponent struct { + Component +} + +func (c *finalizedComponent) Body(context.Context) Component { + return c.Component +} diff --git a/vendor/github.com/mitchellh/go-glint/flex/LICENSE b/vendor/github.com/mitchellh/go-glint/flex/LICENSE new file mode 100644 index 000000000000..ab7f3627d3c2 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/LICENSE @@ -0,0 +1,31 @@ +BSD License + +For yoga software + +Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +Copyright (c) 2017-present, Krzysztof Kowalczyk. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/mitchellh/go-glint/flex/PATENTS b/vendor/github.com/mitchellh/go-glint/flex/PATENTS new file mode 100644 index 000000000000..dc05f9db056f --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/PATENTS @@ -0,0 +1,33 @@ +Additional Grant of Patent Rights Version 2 + +"Software" means the yoga software distributed by Facebook, Inc. + +Facebook, Inc. (“Facebook”) hereby grants to each recipient of the Software +(“you”) a perpetual, worldwide, royalty-free, non-exclusive, irrevocable +(subject to the termination provision below) license under any Necessary +Claims, to make, have made, use, sell, offer to sell, import, and otherwise +transfer the Software. For avoidance of doubt, no license is granted under +Facebook's rights in any patent claims that are infringed by (i) modifications +to the Software made by you or any third party or (ii) the Software in +combination with any software or other technology. + +The license granted hereunder will terminate, automatically and without notice, +if you (or any of your subsidiaries, corporate affiliates or agents) initiate +directly or indirectly, or take a direct financial interest in, any Patent +Assertion: (i) against Facebook or any of its subsidiaries or corporate +affiliates, (ii) against any party if such Patent Assertion arises in whole or +in part from any software, technology, product or service of Facebook or any of +its subsidiaries or corporate affiliates, or (iii) against any party relating +to the Software. Notwithstanding the foregoing, if Facebook or any of its +subsidiaries or corporate affiliates files a lawsuit alleging patent +infringement against you in the first instance, and you respond by filing a +patent infringement counterclaim in that lawsuit against that party that is +unrelated to the Software, the license granted hereunder will not terminate +under section (i) of this paragraph due to such counterclaim. + +A "Necessary Claim" is a claim of a patent owned by Facebook that is +necessarily infringed by the Software standing alone. + +A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, +or contributory infringement or inducement to infringe any patent, including a +cross-claim or counterclaim. \ No newline at end of file diff --git a/vendor/github.com/mitchellh/go-glint/flex/README.md b/vendor/github.com/mitchellh/go-glint/flex/README.md new file mode 100644 index 000000000000..992f7b55f5fc --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/README.md @@ -0,0 +1,27 @@ +# flex - CSS flexbox layout implementation in Go + +Go implementation of [flexbox CSS](https://www.w3.org/TR/css-flexbox-1/) layout algorithm. + +A pure Go port of [Facebook's Yoga](https://github.com/facebook/yoga). + +## How to use + +Read [tutorial](https://blog.kowalczyk.info/article/9/tutorial-on-using-github.comkjkflex-go-package.html) or look at `_test.go` files. + +## Status + +The port is finished. The code works and passess all Yoga tests. + +The API is awkward by Go standards but it's the best I could do given that I want to stay close to C version. + +Logic is currently synced up to https://github.com/facebook/yoga/commit/f45059e1e696727c1282742b89d2c8bf06345254 + +## How the port was made + +You can read a [detailed story](https://blog.kowalczyk.info/article/wN9R/experience-porting-4.5k-loc-of-c-to-go-facebooks-css-flexbox-implementation-yoga.html). + +In short: + +* manually ported [C code](https://github.com/facebook/yoga/tree/master/yoga) to Go, line-by-line +* manually ported [tests](https://github.com/facebook/yoga/tree/master/tests) to Go +* tweak the API from C style to be more Go like. The structure and logic still is very close to C code (this makes porting future C changes easy) diff --git a/vendor/github.com/mitchellh/go-glint/flex/enums.go b/vendor/github.com/mitchellh/go-glint/flex/enums.go new file mode 100644 index 000000000000..6c6952176feb --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/enums.go @@ -0,0 +1,454 @@ +package flex + +// port of YGEnums.h + +// Align describes align flex attribute +type Align int + +const ( + // AlignAuto is "auto" + AlignAuto Align = iota + // AlignFlexStart is "flex-start" + AlignFlexStart + // AlignCenter if "center" + AlignCenter + // AlignFlexEnd is "flex-end" + AlignFlexEnd + // AlignStretch is "strech" + AlignStretch + // AlignBaseline is "baseline" + AlignBaseline + // AlignSpaceBetween is "space-between" + AlignSpaceBetween + // AlignSpaceAround is "space-around" + AlignSpaceAround +) + +// Dimension represents dimention +type Dimension int + +const ( + // DimensionWidth is width + DimensionWidth Dimension = iota + // DimensionHeight is height + DimensionHeight +) + +// Direction represents right-to-left or left-to-right direction +type Direction int + +const ( + // DirectionInherit is "inherit" + DirectionInherit Direction = iota + // DirectionLTR is "ltr" + DirectionLTR + // DirectionRTL is "rtl" + DirectionRTL +) + +// Display is "display" property +type Display int + +const ( + // DisplayFlex is "flex" + DisplayFlex Display = iota + // DisplayNone is "none" + DisplayNone +) + +// Edge represents an edge +type Edge int + +const ( + // EdgeLeft is left edge + EdgeLeft Edge = iota + // EdgeTop is top edge + EdgeTop + // EdgeRight is right edge + EdgeRight + // EdgeBottom is bottom edge + EdgeBottom + // EdgeStart is start edge + EdgeStart + // EdgeEnd is end edge + EdgeEnd + // EdgeHorizontal is horizontal edge + EdgeHorizontal + // EdgeVertical is vertical edge + EdgeVertical + // EdgeAll is all edge + EdgeAll +) + +const ( + // EdgeCount is count of edges + EdgeCount = 9 +) + +// ExperimentalFeature defines experimental features +type ExperimentalFeature int + +const ( + // ExperimentalFeatureWebFlexBasis is web flex basis + ExperimentalFeatureWebFlexBasis ExperimentalFeature = iota +) + +const ( + experimentalFeatureCount = 1 +) + +// FlexDirection describes "flex-direction" property +type FlexDirection int + +const ( + // FlexDirectionColumn is "column" + FlexDirectionColumn FlexDirection = iota + // FlexDirectionColumnReverse is "column-reverse" + FlexDirectionColumnReverse + // FlexDirectionRow is "row" + FlexDirectionRow + // FlexDirectionRowReverse is "row-reverse" + FlexDirectionRowReverse +) + +// Justify is "justify" property +type Justify int + +const ( + // JustifyFlexStart is "flex-start" + JustifyFlexStart Justify = iota + // JustifyCenter is "center" + JustifyCenter + // JustifyFlexEnd is "flex-end" + JustifyFlexEnd + // JustifySpaceBetween is "space-between" + JustifySpaceBetween + // JustifySpaceAround is "space-around" + JustifySpaceAround +) + +// LogLevel represents log level +type LogLevel int + +const ( + LogLevelError LogLevel = iota + LogLevelWarn + LogLevelInfo + LogLevelDebug + LogLevelVerbose + LogLevelFatal +) + +// MeasureMode defines measurement mode +type MeasureMode int + +const ( + // MeasureModeUndefined is undefined + MeasureModeUndefined MeasureMode = iota + // MeasureModeExactly is exactly + MeasureModeExactly + // MeasureModeAtMost is at-most + MeasureModeAtMost +) + +const ( + measureModeCount = 3 +) + +// NodeType defines node type +type NodeType int + +const ( + // NodeTypeDefault is default node + NodeTypeDefault NodeType = iota + // NodeTypeText is text node + NodeTypeText +) + +// Overflow describes "overflow" property +type Overflow int + +const ( + // OverflowVisible is "visible" + OverflowVisible Overflow = iota + // OverflowHidden is "hidden" + OverflowHidden + // OverflowScroll is "scroll" + OverflowScroll +) + +// PositionType is "position" property +type PositionType int + +const ( + // PositionTypeRelative is "relative" + PositionTypeRelative PositionType = iota + // PositionTypeAbsolute is "absolute" + PositionTypeAbsolute +) + +type PrintOptions int + +const ( + PrintOptionsLayout PrintOptions = 1 << iota + PrintOptionsStyle + PrintOptionsChildren +) + +// Unit is "unit" property +type Unit int + +const ( + // UnitUndefined is "undefined" + UnitUndefined Unit = iota + // UnitPoint is "point" + UnitPoint + // UnitPercent is "percent" + UnitPercent + // UnitAuto is "auto" + UnitAuto +) + +// Wrap is "wrap" property +type Wrap int + +const ( + // WrapNoWrap is "no-wrap" + WrapNoWrap Wrap = iota + // WrapWrap is "wrap" + WrapWrap + // WrapWrapReverse is "reverse" + WrapWrapReverse +) + +// AlignToString returns string version of Align enum +func AlignToString(value Align) string { + switch value { + case AlignAuto: + return "auto" + case AlignFlexStart: + return "flex-start" + case AlignCenter: + return "center" + case AlignFlexEnd: + return "flex-end" + case AlignStretch: + return "stretch" + case AlignBaseline: + return "baseline" + case AlignSpaceBetween: + return "space-between" + case AlignSpaceAround: + return "space-around" + } + return "unknown" +} + +// DimensionToString returns string version of Dimension enum +func DimensionToString(value Dimension) string { + switch value { + case DimensionWidth: + return "width" + case DimensionHeight: + return "height" + } + return "unknown" +} + +// DirectionToString returns string version of Direction enum +func DirectionToString(value Direction) string { + switch value { + case DirectionInherit: + return "inherit" + case DirectionLTR: + return "ltr" + case DirectionRTL: + return "rtl" + } + return "unknown" +} + +// DisplayToString returns string version of Display enum +func DisplayToString(value Display) string { + switch value { + case DisplayFlex: + return "flex" + case DisplayNone: + return "none" + } + return "unknown" +} + +// EdgeToString returns string version of Edge enum +func EdgeToString(value Edge) string { + switch value { + case EdgeLeft: + return "left" + case EdgeTop: + return "top" + case EdgeRight: + return "right" + case EdgeBottom: + return "bottom" + case EdgeStart: + return "start" + case EdgeEnd: + return "end" + case EdgeHorizontal: + return "horizontal" + case EdgeVertical: + return "vertical" + case EdgeAll: + return "all" + } + return "unknown" +} + +// ExperimentalFeatureToString returns string version of ExperimentalFeature enum +func ExperimentalFeatureToString(value ExperimentalFeature) string { + switch value { + case ExperimentalFeatureWebFlexBasis: + return "web-flex-basis" + } + return "unknown" +} + +// FlexDirectionToString returns string version of FlexDirection enum +func FlexDirectionToString(value FlexDirection) string { + switch value { + case FlexDirectionColumn: + return "column" + case FlexDirectionColumnReverse: + return "column-reverse" + case FlexDirectionRow: + return "row" + case FlexDirectionRowReverse: + return "row-reverse" + } + return "unknown" +} + +// JustifyToString returns string version of Justify enum +func JustifyToString(value Justify) string { + switch value { + case JustifyFlexStart: + return "flex-start" + case JustifyCenter: + return "center" + case JustifyFlexEnd: + return "flex-end" + case JustifySpaceBetween: + return "space-between" + case JustifySpaceAround: + return "space-around" + } + return "unknown" +} + +// LogLevelToString returns string version of LogLevel enum +func LogLevelToString(value LogLevel) string { + switch value { + case LogLevelError: + return "error" + case LogLevelWarn: + return "warn" + case LogLevelInfo: + return "info" + case LogLevelDebug: + return "debug" + case LogLevelVerbose: + return "verbose" + case LogLevelFatal: + return "fatal" + } + return "unknown" +} + +// MeasureModeToString returns string version of MeasureMode enum +func MeasureModeToString(value MeasureMode) string { + switch value { + case MeasureModeUndefined: + return "undefined" + case MeasureModeExactly: + return "exactly" + case MeasureModeAtMost: + return "at-most" + } + return "unknown" +} + +// NodeTypeToString returns string version of NodeType enum +func NodeTypeToString(value NodeType) string { + switch value { + case NodeTypeDefault: + return "default" + case NodeTypeText: + return "text" + } + return "unknown" +} + +// OverflowToString returns string version of Overflow enum +func OverflowToString(value Overflow) string { + switch value { + case OverflowVisible: + return "visible" + case OverflowHidden: + return "hidden" + case OverflowScroll: + return "scroll" + } + return "unknown" +} + +// PositionTypeToString returns string version of PositionType enum +func PositionTypeToString(value PositionType) string { + switch value { + case PositionTypeRelative: + return "relative" + case PositionTypeAbsolute: + return "absolute" + } + return "unknown" +} + +// PrintOptionsToString returns string version of PrintOptions enum +func PrintOptionsToString(value PrintOptions) string { + switch value { + case PrintOptionsLayout: + return "layout" + case PrintOptionsStyle: + return "style" + case PrintOptionsChildren: + return "children" + } + return "unknown" +} + +// UnitToString returns string version of Unit enum +func UnitToString(value Unit) string { + switch value { + case UnitUndefined: + return "undefined" + case UnitPoint: + return "point" + case UnitPercent: + return "percent" + case UnitAuto: + return "auto" + } + return "unknown" +} + +// WrapToString returns string version of Wrap enum +func WrapToString(value Wrap) string { + switch value { + case WrapNoWrap: + return "no-wrap" + case WrapWrap: + return "wrap" + case WrapWrapReverse: + return "wrap-reverse" + } + return "unknown" +} diff --git a/vendor/github.com/mitchellh/go-glint/flex/math.go b/vendor/github.com/mitchellh/go-glint/flex/math.go new file mode 100644 index 000000000000..d32e64b2839c --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/math.go @@ -0,0 +1,73 @@ +package flex + +import "math" + +// from https://github.com/rkusa/gm/blob/master/math32/bits.go + +const ( + uvnan = 0x7FC00001 +) + +var ( + NAN = math.Float32frombits(uvnan) +) + +// NaN returns an IEEE 754 ``not-a-number'' value. +func NaN() float32 { return math.Float32frombits(uvnan) } + +// IsNaN reports whether f is an IEEE 754 ``not-a-number'' value. +func IsNaN(f float32) (is bool) { + return f != f +} + +func feq(a, b float32) bool { + if IsNaN(a) && IsNaN(b) { + return true + } + return a == b +} + +// https://github.com/evanphx/ulysses-libc/blob/master/src/math/fmaxf.c +func fmaxf(a float32, b float32) float32 { + if IsNaN(a) { + return b + } + if IsNaN(b) { + return a + } + // TODO: signed zeros + if a > b { + return a + } + return b +} + +// https://github.com/evanphx/ulysses-libc/blob/master/src/math/fminf.c +func fminf(a float32, b float32) float32 { + if IsNaN(a) { + return b + } + if IsNaN(b) { + return a + } + // TODO: signed zeros + if a < b { + return a + } + return b +} + +func fabs(x float32) float32 { + switch { + case x < 0: + return -x + case x == 0: + return 0 // return correctly abs(-0) + } + return x +} + +func fmodf(x, y float32) float32 { + res := math.Mod(float64(x), float64(y)) + return float32(res) +} diff --git a/vendor/github.com/mitchellh/go-glint/flex/print.go b/vendor/github.com/mitchellh/go-glint/flex/print.go new file mode 100644 index 000000000000..d1b9c2673516 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/print.go @@ -0,0 +1,199 @@ +package flex + +import ( + "fmt" + "io" + "os" + "strings" +) + +// NodePrinter node printer. +type NodePrinter struct { + writer io.Writer + options PrintOptions +} + +// NodePrint prints node to standard output. +func NodePrint(node *Node, options PrintOptions) { + printer := NewNodePrinter(os.Stdout, options) + printer.Print(node) +} + +// NewNodePrinter creates new node printer. +func NewNodePrinter(writer io.Writer, options PrintOptions) *NodePrinter { + return &NodePrinter{ + writer: writer, + options: options, + } +} + +// Print prints node. +func (printer *NodePrinter) Print(node *Node) { + printer.printNode(node, 0) +} + +func (printer *NodePrinter) printNode(node *Node, level int) { + printer.printIndent(level) + printer.printf("
") + + childCount := len(node.Children) + if printer.options&PrintOptionsChildren != 0 && childCount > 0 { + for i := 0; i < childCount; i++ { + printer.printf("\n") + printer.printNode(node.Children[i], level+1) + } + printer.printIndent(level) + printer.printf("\n") + } + if childCount != 0 { + printer.printIndent(level) + } + printer.printf("
") +} + +func (printer *NodePrinter) printEdges(node *Node, str string, edges []Value) { + if fourValuesEqual(edges) { + printer.printNumberIfNotZero(node, str, &edges[EdgeLeft]) + // bugfix for issue #5 + // if we set EdgeAll, the values are + // [{NaN 0} {NaN 0} {NaN 0} {NaN 0} {NaN 0} {NaN 0} {NaN 0} {NaN 0} {20 1}] + // so EdgeLeft is not printed and we won't print padding + // for simplicity, I assume that EdgeAll is exclusive with setting specific edges + // so we can print both and only one should show up + // C code has this bug: https://github.com/facebook/yoga/blob/26481a6553a33d9c005f2b8d24a7952fc58df32c/yoga/Yoga.c#L1036 + printer.printNumberIfNotZero(node, str, &edges[EdgeAll]) + } else { + for edge := EdgeLeft; edge < EdgeCount; edge++ { + buf := fmt.Sprintf("%s-%s", str, EdgeToString(edge)) + printer.printNumberIfNotZero(node, buf, &edges[edge]) + } + } +} + +func (printer *NodePrinter) printEdgeIfNotUndefined(node *Node, str string, edges []Value, edge Edge) { + printer.printNumberIfNotUndefined(node, str, computedEdgeValue(edges, edge, &ValueUndefined)) +} + +func (printer *NodePrinter) printFloatIfNotUndefined(node *Node, str string, number float32) { + if !FloatIsUndefined(number) { + printer.printf("%s: %g; ", str, number) + } +} + +func (printer *NodePrinter) printNumberIfNotUndefined(node *Node, str string, number *Value) { + if number.Unit != UnitUndefined { + if number.Unit == UnitAuto { + printer.printf("%s: auto; ", str) + } else { + unit := "%" + + if number.Unit == UnitPoint { + unit = "px" + } + printer.printf("%s: %g%s; ", str, number.Value, unit) + } + } +} + +func (printer *NodePrinter) printNumberIfNotAuto(node *Node, str string, number *Value) { + if number.Unit != UnitAuto { + printer.printNumberIfNotUndefined(node, str, number) + } +} + +func (printer *NodePrinter) printNumberIfNotZero(node *Node, str string, number *Value) { + if !FloatsEqual(number.Value, 0) { + printer.printNumberIfNotUndefined(node, str, number) + } +} + +func (printer *NodePrinter) printf(format string, args ...interface{}) { + fmt.Fprintf(printer.writer, format, args...) +} + +func (printer *NodePrinter) printIndent(n int) { + printer.writer.Write([]byte(strings.Repeat(" ", n))) +} + +func fourValuesEqual(four []Value) bool { + return ValueEqual(four[0], four[1]) && ValueEqual(four[0], four[2]) && + ValueEqual(four[0], four[3]) +} diff --git a/vendor/github.com/mitchellh/go-glint/flex/yoga.go b/vendor/github.com/mitchellh/go-glint/flex/yoga.go new file mode 100644 index 000000000000..7ef3f3cdaacf --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/yoga.go @@ -0,0 +1,3080 @@ +package flex + +import ( + "fmt" + "os" +) + +// CachedMeasurement describes measurements +type CachedMeasurement struct { + availableWidth float32 + availableHeight float32 + widthMeasureMode MeasureMode + heightMeasureMode MeasureMode + + computedWidth float32 + computedHeight float32 +} + +// This value was chosen based on empiracle data. Even the most complicated +// layouts should not require more than 16 entries to fit within the cache. +const maxCachedResultCount = 16 + +// Layout describes position information after layout is finished +type Layout struct { + Position [4]float32 + Dimensions [2]float32 + Margin [6]float32 + Border [6]float32 + Padding [6]float32 + Direction Direction + + computedFlexBasisGeneration int + computedFlexBasis float32 + HadOverflow bool + + // Instead of recomputing the entire layout every single time, we + // cache some information to break early when nothing changed + generationCount int + lastParentDirection Direction + + nextCachedMeasurementsIndex int + cachedMeasurements [maxCachedResultCount]CachedMeasurement + + measuredDimensions [2]float32 + + cachedLayout CachedMeasurement +} + +// Style describes CSS flexbox style of the node +type Style struct { + Direction Direction + FlexDirection FlexDirection + JustifyContent Justify + AlignContent Align + AlignItems Align + AlignSelf Align + PositionType PositionType + FlexWrap Wrap + Overflow Overflow + Display Display + Flex float32 + FlexGrow float32 + FlexShrink float32 + FlexBasis Value + Margin [EdgeCount]Value + Position [EdgeCount]Value + Padding [EdgeCount]Value + Border [EdgeCount]Value + Dimensions [2]Value + MinDimensions [2]Value + MaxDimensions [2]Value + + // Yoga specific properties, not compatible with flexbox specification + AspectRatio float32 +} + +// Config describes a configuration +type Config struct { + experimentalFeatures [experimentalFeatureCount + 1]bool + UseWebDefaults bool + UseLegacyStretchBehaviour bool + PointScaleFactor float32 + Logger Logger + Context interface{} +} + +// Node describes a an element +type Node struct { + Style Style + Layout Layout + lineIndex int + + Parent *Node + Children []*Node + + NextChild *Node + + Measure MeasureFunc + Baseline BaselineFunc + Print PrintFunc + Config *Config + Context interface{} + + IsDirty bool + hasNewLayout bool + NodeType NodeType + + resolvedDimensions [2]*Value +} + +var ( + undefinedValue = Value{ + Value: Undefined, + Unit: UnitUndefined, + } + + autoValue = Value{ + Value: Undefined, + Unit: UnitAuto, + } + + defaultEdgeValuesUnit = [EdgeCount]Value{ + undefinedValue, + undefinedValue, + undefinedValue, + undefinedValue, + undefinedValue, + undefinedValue, + undefinedValue, + undefinedValue, + undefinedValue, + } + + defaultDimensionValues = [2]float32{ + Undefined, + Undefined, + } + + defaultDimensionValuesUnit = [2]Value{ + undefinedValue, + undefinedValue, + } + + defaultDimensionValuesAutoUnit = [2]Value{ + autoValue, + autoValue, + } +) + +const ( + defaultFlexGrow float32 = 0 + defaultFlexShrink float32 = 0 + webDefaultFlexShrink float32 = 1 +) + +var ( + nodeDefaults = Node{ + Parent: nil, + Children: nil, + hasNewLayout: true, + IsDirty: false, + NodeType: NodeTypeDefault, + resolvedDimensions: [2]*Value{&ValueUndefined, &ValueUndefined}, + Style: Style{ + Flex: Undefined, + FlexGrow: Undefined, + FlexShrink: Undefined, + FlexBasis: autoValue, + JustifyContent: JustifyFlexStart, + AlignItems: AlignStretch, + AlignContent: AlignFlexStart, + Direction: DirectionInherit, + FlexDirection: FlexDirectionColumn, + Overflow: OverflowVisible, + Display: DisplayFlex, + Dimensions: defaultDimensionValuesAutoUnit, + MinDimensions: defaultDimensionValuesUnit, + MaxDimensions: defaultDimensionValuesUnit, + Position: defaultEdgeValuesUnit, + Margin: defaultEdgeValuesUnit, + Padding: defaultEdgeValuesUnit, + Border: defaultEdgeValuesUnit, + AspectRatio: Undefined, + }, + Layout: Layout{ + Dimensions: defaultDimensionValues, + lastParentDirection: Direction(-1), + nextCachedMeasurementsIndex: 0, + computedFlexBasis: Undefined, + HadOverflow: false, + measuredDimensions: defaultDimensionValues, + cachedLayout: CachedMeasurement{ + widthMeasureMode: MeasureMode(-1), + heightMeasureMode: MeasureMode(-1), + computedWidth: -1, + computedHeight: -1, + }, + }, + } + + configDefaults = Config{ + experimentalFeatures: [experimentalFeatureCount + 1]bool{ + false, + false, + }, + UseWebDefaults: false, + PointScaleFactor: 1, + Logger: DefaultLog, + Context: nil, + } + + // ValueZero defines a zero value + ValueZero = Value{Value: 0, Unit: UnitPoint} +) + +func valueEq(v1, v2 Value) bool { + if v1.Unit != v2.Unit { + return false + } + return feq(v1.Value, v2.Value) +} + +// DefaultLog is default logging function +func DefaultLog(config *Config, node *Node, level LogLevel, format string, + args ...interface{}) int { + switch level { + case LogLevelError, LogLevelFatal: + n, _ := fmt.Fprintf(os.Stderr, format, args...) + return n + case LogLevelWarn, LogLevelInfo, LogLevelDebug, LogLevelVerbose: + fallthrough + default: + n, _ := fmt.Printf(format, args...) + return n + } +} + +func computedEdgeValue(edges []Value, edge Edge, defaultValue *Value) *Value { + if edges[edge].Unit != UnitUndefined { + return &edges[edge] + } + + isVertEdge := edge == EdgeTop || edge == EdgeBottom + if isVertEdge && edges[EdgeVertical].Unit != UnitUndefined { + return &edges[EdgeVertical] + } + + isHorizEdge := (edge == EdgeLeft || edge == EdgeRight || edge == EdgeStart || edge == EdgeEnd) + if isHorizEdge && edges[EdgeHorizontal].Unit != UnitUndefined { + return &edges[EdgeHorizontal] + } + + if edges[EdgeAll].Unit != UnitUndefined { + return &edges[EdgeAll] + } + + if edge == EdgeStart || edge == EdgeEnd { + return &ValueUndefined + } + + return defaultValue +} + +func resolveValue(value *Value, parentSize float32) float32 { + switch value.Unit { + case UnitUndefined, UnitAuto: + return Undefined + case UnitPoint: + return value.Value + case UnitPercent: + return value.Value * parentSize / 100 + } + return Undefined +} + +func resolveValueMargin(value *Value, parentSize float32) float32 { + if value.Unit == UnitAuto { + return 0 + } + return resolveValue(value, parentSize) +} + +// NewNodeWithConfig creates new node with config +func NewNodeWithConfig(config *Config) *Node { + node := nodeDefaults + + if config.UseWebDefaults { + node.Style.FlexDirection = FlexDirectionRow + node.Style.AlignContent = AlignStretch + } + node.Config = config + return &node +} + +// NewNode creates a new node +func NewNode() *Node { + return NewNodeWithConfig(&configDefaults) +} + +// Reset resets a node +func (node *Node) Reset() { + assertWithNode(node, len(node.Children) == 0, "Cannot reset a node which still has children attached") + assertWithNode(node, node.Parent == nil, "Cannot reset a node still attached to a parent") + + node.Children = nil + + config := node.Config + *node = nodeDefaults + if config.UseWebDefaults { + node.Style.FlexDirection = FlexDirectionRow + node.Style.AlignContent = AlignStretch + } + node.Config = config +} + +// ConfigGetDefault returns default config, only for C# +func ConfigGetDefault() *Config { + return &configDefaults +} + +// NewConfig creates new config +func NewConfig() *Config { + config := &Config{} + assertCond(config != nil, "Could not allocate memory for config") + + *config = configDefaults + return config +} + +// ConfigCopy copies a config +func ConfigCopy(dest *Config, src *Config) { + *dest = *src +} + +func nodeMarkDirtyInternal(node *Node) { + if !node.IsDirty { + node.IsDirty = true + node.Layout.computedFlexBasis = Undefined + if node.Parent != nil { + nodeMarkDirtyInternal(node.Parent) + } + } +} + +// SetMeasureFunc sets measure function +func (node *Node) SetMeasureFunc(measureFunc MeasureFunc) { + if measureFunc == nil { + node.Measure = nil + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate places in Litho + node.NodeType = NodeTypeDefault + } else { + assertWithNode( + node, + len(node.Children) == 0, + "Cannot set measure function: Nodes with measure functions cannot have children.") + node.Measure = measureFunc + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate places in Litho + node.NodeType = NodeTypeText + } +} + +// InsertChild inserts a child +func (node *Node) InsertChild(child *Node, idx int) { + assertWithNode(node, child.Parent == nil, "Child already has a parent, it must be removed first.") + assertWithNode(node, node.Measure == nil, "Cannot add child: Nodes with measure functions cannot have children.") + + a := node.Children + // https://github.com/golang/go/wiki/SliceTricks + a = append(a[:idx], append([]*Node{child}, a[idx:]...)...) + + node.Children = a + + child.Parent = node + nodeMarkDirtyInternal(node) +} + +func (node *Node) deleteChild(child *Node) *Node { + a := node.Children + n := len(a) + for i := 0; i < n; i++ { + if a[i] == child { + removed := a[i] + copy(a[i:], a[i+1:]) + a[len(a)-1] = nil // or the zero value of T + a = a[:len(a)-1] + node.Children = a + return removed + } + } + return nil +} + +// RemoveChild removes child node +func (node *Node) RemoveChild(child *Node) { + if node.deleteChild(child) != nil { + child.Layout = nodeDefaults.Layout // layout is no longer valid + child.Parent = nil + nodeMarkDirtyInternal(node) + } +} + +// GetChild returns a child at a given index +func (node *Node) GetChild(idx int) *Node { + if idx < len(node.Children) { + return node.Children[idx] + } + return nil +} + +// MarkDirty marks node as dirty +func (node *Node) MarkDirty() { + assertWithNode(node, node.Measure != nil, + "Only leaf nodes with custom measure functions should manually mark themselves as dirty") + nodeMarkDirtyInternal(node) +} + +func styleEq(s1, s2 *Style) bool { + if s1.Direction != s2.Direction || + s1.FlexDirection != s2.FlexDirection || + s1.JustifyContent != s2.JustifyContent || + s1.AlignContent != s2.AlignContent || + s1.AlignItems != s2.AlignItems || + s1.AlignSelf != s2.AlignSelf || + s1.PositionType != s2.PositionType || + s1.FlexWrap != s2.FlexWrap || + s1.Overflow != s2.Overflow || + s1.Display != s2.Display || + !feq(s1.Flex, s2.Flex) || + !feq(s1.FlexGrow, s2.FlexGrow) || + !feq(s1.FlexShrink, s2.FlexShrink) || + !valueEq(s1.FlexBasis, s2.FlexBasis) { + return false + } + for i := 0; i < EdgeCount; i++ { + if !valueEq(s1.Margin[i], s2.Margin[i]) || + !valueEq(s1.Position[i], s2.Position[i]) || + !valueEq(s1.Padding[i], s2.Padding[i]) || + !valueEq(s1.Border[i], s2.Border[i]) { + return false + } + } + for i := 0; i < 2; i++ { + if !valueEq(s1.Dimensions[i], s2.Dimensions[i]) || + !valueEq(s1.MinDimensions[i], s2.MinDimensions[i]) || + !valueEq(s1.MaxDimensions[i], s2.MaxDimensions[i]) { + return false + } + } + return true +} + +// NodeCopyStyle copies style +func NodeCopyStyle(dstNode *Node, srcNode *Node) { + if !styleEq(&dstNode.Style, &srcNode.Style) { + dstNode.Style = srcNode.Style + nodeMarkDirtyInternal(dstNode) + } +} + +func resolveFlexGrow(node *Node) float32 { + // Root nodes flexGrow should always be 0 + if node.Parent == nil { + return 0 + } + if !FloatIsUndefined(node.Style.FlexGrow) { + return node.Style.FlexGrow + } + if !FloatIsUndefined(node.Style.Flex) && node.Style.Flex > 0 { + return node.Style.Flex + } + return defaultFlexGrow +} + +// StyleGetFlexGrow gets flex grow +func (node *Node) StyleGetFlexGrow() float32 { + if FloatIsUndefined(node.Style.FlexGrow) { + return defaultFlexGrow + } + return node.Style.FlexGrow +} + +// StyleGetFlexShrink gets flex shrink +func (node *Node) StyleGetFlexShrink() float32 { + if FloatIsUndefined(node.Style.FlexShrink) { + if node.Config.UseWebDefaults { + return webDefaultFlexShrink + } + return defaultFlexShrink + } + return node.Style.FlexShrink +} + +func nodeResolveFlexShrink(node *Node) float32 { + // Root nodes flexShrink should always be 0 + if node.Parent == nil { + return 0 + } + if !FloatIsUndefined(node.Style.FlexShrink) { + return node.Style.FlexShrink + } + if !node.Config.UseWebDefaults && !FloatIsUndefined(node.Style.Flex) && + node.Style.Flex < 0 { + return -node.Style.Flex + } + if node.Config.UseWebDefaults { + return webDefaultFlexShrink + } + return defaultFlexShrink +} + +func nodeResolveFlexBasisPtr(node *Node) *Value { + style := &node.Style + if style.FlexBasis.Unit != UnitAuto && style.FlexBasis.Unit != UnitUndefined { + return &style.FlexBasis + } + if !FloatIsUndefined(style.Flex) && style.Flex > 0 { + if node.Config.UseWebDefaults { + return &ValueAuto + } + return &ValueZero + } + return &ValueAuto +} + +// see yoga_props.go + +var ( + currentGenerationCount = 0 +) + +// FloatIsUndefined returns true if value is undefined +func FloatIsUndefined(value float32) bool { + return IsNaN(value) +} + +// ValueEqual returns true if values are equal +func ValueEqual(a Value, b Value) bool { + if a.Unit != b.Unit { + return false + } + + if a.Unit == UnitUndefined { + return true + } + + return fabs(a.Value-b.Value) < 0.0001 +} + +func resolveDimensions(node *Node) { + for dim := DimensionWidth; dim <= DimensionHeight; dim++ { + if node.Style.MaxDimensions[dim].Unit != UnitUndefined && + ValueEqual(node.Style.MaxDimensions[dim], node.Style.MinDimensions[dim]) { + node.resolvedDimensions[dim] = &node.Style.MaxDimensions[dim] + } else { + node.resolvedDimensions[dim] = &node.Style.Dimensions[dim] + } + } +} + +// FloatsEqual returns true if floats are approx. equal +func FloatsEqual(a float32, b float32) bool { + if FloatIsUndefined(a) { + return FloatIsUndefined(b) + } + return fabs(a-b) < 0.0001 +} + +// see print.go + +var ( + leading = [4]Edge{EdgeTop, EdgeBottom, EdgeLeft, EdgeRight} + trailing = [4]Edge{EdgeBottom, EdgeTop, EdgeRight, EdgeLeft} + pos = [4]Edge{EdgeTop, EdgeBottom, EdgeLeft, EdgeRight} + dim = [4]Dimension{DimensionHeight, DimensionHeight, DimensionWidth, DimensionWidth} +) + +func init() { + leading[FlexDirectionColumn] = EdgeTop + leading[FlexDirectionColumnReverse] = EdgeBottom + leading[FlexDirectionRow] = EdgeLeft + leading[FlexDirectionRowReverse] = EdgeRight + + trailing[FlexDirectionColumn] = EdgeBottom + trailing[FlexDirectionColumnReverse] = EdgeTop + trailing[FlexDirectionRow] = EdgeRight + trailing[FlexDirectionRowReverse] = EdgeLeft + + pos[FlexDirectionColumn] = EdgeTop + pos[FlexDirectionColumnReverse] = EdgeBottom + pos[FlexDirectionRow] = EdgeLeft + pos[FlexDirectionRowReverse] = EdgeRight + + dim[FlexDirectionColumn] = DimensionHeight + dim[FlexDirectionColumnReverse] = DimensionHeight + dim[FlexDirectionRow] = DimensionWidth + dim[FlexDirectionRowReverse] = DimensionWidth +} + +func flexDirectionIsRow(flexDirection FlexDirection) bool { + return flexDirection == FlexDirectionRow || flexDirection == FlexDirectionRowReverse +} + +func flexDirectionIsColumn(flexDirection FlexDirection) bool { + return flexDirection == FlexDirectionColumn || flexDirection == FlexDirectionColumnReverse +} + +func nodeLeadingMargin(node *Node, axis FlexDirection, widthSize float32) float32 { + if flexDirectionIsRow(axis) && node.Style.Margin[EdgeStart].Unit != UnitUndefined { + return resolveValueMargin(&node.Style.Margin[EdgeStart], widthSize) + } + + v := computedEdgeValue(node.Style.Margin[:], leading[axis], &ValueZero) + return resolveValueMargin(v, widthSize) +} + +func nodeTrailingMargin(node *Node, axis FlexDirection, widthSize float32) float32 { + if flexDirectionIsRow(axis) && node.Style.Margin[EdgeEnd].Unit != UnitUndefined { + return resolveValueMargin(&node.Style.Margin[EdgeEnd], widthSize) + } + + return resolveValueMargin(computedEdgeValue(node.Style.Margin[:], trailing[axis], &ValueZero), + widthSize) +} + +func nodeLeadingPadding(node *Node, axis FlexDirection, widthSize float32) float32 { + if flexDirectionIsRow(axis) && node.Style.Padding[EdgeStart].Unit != UnitUndefined && + resolveValue(&node.Style.Padding[EdgeStart], widthSize) >= 0 { + return resolveValue(&node.Style.Padding[EdgeStart], widthSize) + } + + return fmaxf(resolveValue(computedEdgeValue(node.Style.Padding[:], leading[axis], &ValueZero), widthSize), 0) +} + +func nodeTrailingPadding(node *Node, axis FlexDirection, widthSize float32) float32 { + if flexDirectionIsRow(axis) && node.Style.Padding[EdgeEnd].Unit != UnitUndefined && + resolveValue(&node.Style.Padding[EdgeEnd], widthSize) >= 0 { + return resolveValue(&node.Style.Padding[EdgeEnd], widthSize) + } + + return fmaxf(resolveValue(computedEdgeValue(node.Style.Padding[:], trailing[axis], &ValueZero), widthSize), 0) +} + +func nodeLeadingBorder(node *Node, axis FlexDirection) float32 { + if flexDirectionIsRow(axis) && node.Style.Border[EdgeStart].Unit != UnitUndefined && + node.Style.Border[EdgeStart].Value >= 0 { + return node.Style.Border[EdgeStart].Value + } + + return fmaxf(computedEdgeValue(node.Style.Border[:], leading[axis], &ValueZero).Value, 0) +} + +func nodeTrailingBorder(node *Node, axis FlexDirection) float32 { + if flexDirectionIsRow(axis) && node.Style.Border[EdgeEnd].Unit != UnitUndefined && + node.Style.Border[EdgeEnd].Value >= 0 { + return node.Style.Border[EdgeEnd].Value + } + + return fmaxf(computedEdgeValue(node.Style.Border[:], trailing[axis], &ValueZero).Value, 0) +} + +func nodeLeadingPaddingAndBorder(node *Node, axis FlexDirection, widthSize float32) float32 { + return nodeLeadingPadding(node, axis, widthSize) + nodeLeadingBorder(node, axis) +} + +func nodeTrailingPaddingAndBorder(node *Node, axis FlexDirection, widthSize float32) float32 { + return nodeTrailingPadding(node, axis, widthSize) + nodeTrailingBorder(node, axis) +} + +func nodeMarginForAxis(node *Node, axis FlexDirection, widthSize float32) float32 { + leading := nodeLeadingMargin(node, axis, widthSize) + trailing := nodeTrailingMargin(node, axis, widthSize) + return leading + trailing +} + +func nodePaddingAndBorderForAxis(node *Node, axis FlexDirection, widthSize float32) float32 { + return nodeLeadingPaddingAndBorder(node, axis, widthSize) + + nodeTrailingPaddingAndBorder(node, axis, widthSize) +} + +func nodeAlignItem(node *Node, child *Node) Align { + align := child.Style.AlignSelf + if child.Style.AlignSelf == AlignAuto { + align = node.Style.AlignItems + } + if align == AlignBaseline && flexDirectionIsColumn(node.Style.FlexDirection) { + return AlignFlexStart + } + return align +} + +func nodeResolveDirection(node *Node, parentDirection Direction) Direction { + if node.Style.Direction == DirectionInherit { + if parentDirection > DirectionInherit { + return parentDirection + } + return DirectionLTR + } + return node.Style.Direction +} + +// Baseline retuns baseline +func Baseline(node *Node) float32 { + if node.Baseline != nil { + baseline := node.Baseline(node, node.Layout.measuredDimensions[DimensionWidth], node.Layout.measuredDimensions[DimensionHeight]) + assertWithNode(node, !FloatIsUndefined(baseline), "Expect custom baseline function to not return NaN") + return baseline + } + + var baselineChild *Node + childCount := len(node.Children) + for i := 0; i < childCount; i++ { + child := node.GetChild(i) + if child.lineIndex > 0 { + break + } + if child.Style.PositionType == PositionTypeAbsolute { + continue + } + if nodeAlignItem(node, child) == AlignBaseline { + baselineChild = child + break + } + + if baselineChild == nil { + baselineChild = child + } + } + + if baselineChild == nil { + return node.Layout.measuredDimensions[DimensionHeight] + } + + baseline := Baseline(baselineChild) + return baseline + baselineChild.Layout.Position[EdgeTop] +} + +func resolveFlexDirection(flexDirection FlexDirection, direction Direction) FlexDirection { + if direction == DirectionRTL { + if flexDirection == FlexDirectionRow { + return FlexDirectionRowReverse + } else if flexDirection == FlexDirectionRowReverse { + return FlexDirectionRow + } + } + return flexDirection +} + +func flexDirectionCross(flexDirection FlexDirection, direction Direction) FlexDirection { + if flexDirectionIsColumn(flexDirection) { + return resolveFlexDirection(FlexDirectionRow, direction) + } + return FlexDirectionColumn +} + +func nodeIsFlex(node *Node) bool { + return (node.Style.PositionType == PositionTypeRelative && + (resolveFlexGrow(node) != 0 || nodeResolveFlexShrink(node) != 0)) +} + +func isBaselineLayout(node *Node) bool { + if flexDirectionIsColumn(node.Style.FlexDirection) { + return false + } + if node.Style.AlignItems == AlignBaseline { + return true + } + childCount := len(node.Children) + for i := 0; i < childCount; i++ { + child := node.GetChild(i) + if child.Style.PositionType == PositionTypeRelative && + child.Style.AlignSelf == AlignBaseline { + return true + } + } + + return false +} + +func nodeDimWithMargin(node *Node, axis FlexDirection, widthSize float32) float32 { + return node.Layout.measuredDimensions[dim[axis]] + nodeLeadingMargin(node, axis, widthSize) + + nodeTrailingMargin(node, axis, widthSize) +} + +func nodeIsStyleDimDefined(node *Node, axis FlexDirection, parentSize float32) bool { + v := node.resolvedDimensions[dim[axis]] + isNotDefined := (v.Unit == UnitAuto || + v.Unit == UnitUndefined || + (v.Unit == UnitPoint && v.Value < 0) || + (v.Unit == UnitPercent && (v.Value < 0 || FloatIsUndefined(parentSize)))) + return !isNotDefined +} + +func nodeIsLayoutDimDefined(node *Node, axis FlexDirection) bool { + value := node.Layout.measuredDimensions[dim[axis]] + return !FloatIsUndefined(value) && value >= 0 +} + +func nodeIsLeadingPosDefined(node *Node, axis FlexDirection) bool { + return (flexDirectionIsRow(axis) && + computedEdgeValue(node.Style.Position[:], EdgeStart, &ValueUndefined).Unit != + UnitUndefined) || + computedEdgeValue(node.Style.Position[:], leading[axis], &ValueUndefined).Unit != + UnitUndefined +} + +func nodeIsTrailingPosDefined(node *Node, axis FlexDirection) bool { + return (flexDirectionIsRow(axis) && + computedEdgeValue(node.Style.Position[:], EdgeEnd, &ValueUndefined).Unit != + UnitUndefined) || + computedEdgeValue(node.Style.Position[:], trailing[axis], &ValueUndefined).Unit != + UnitUndefined +} + +func nodeLeadingPosition(node *Node, axis FlexDirection, axisSize float32) float32 { + if flexDirectionIsRow(axis) { + leadingPosition := computedEdgeValue(node.Style.Position[:], EdgeStart, &ValueUndefined) + if leadingPosition.Unit != UnitUndefined { + return resolveValue(leadingPosition, axisSize) + } + } + + leadingPosition := computedEdgeValue(node.Style.Position[:], leading[axis], &ValueUndefined) + + if leadingPosition.Unit == UnitUndefined { + return 0 + } + return resolveValue(leadingPosition, axisSize) +} + +func nodeTrailingPosition(node *Node, axis FlexDirection, axisSize float32) float32 { + if flexDirectionIsRow(axis) { + trailingPosition := computedEdgeValue(node.Style.Position[:], EdgeEnd, &ValueUndefined) + if trailingPosition.Unit != UnitUndefined { + return resolveValue(trailingPosition, axisSize) + } + } + + trailingPosition := computedEdgeValue(node.Style.Position[:], trailing[axis], &ValueUndefined) + + if trailingPosition.Unit == UnitUndefined { + return 0 + } + return resolveValue(trailingPosition, axisSize) +} + +func nodeBoundAxisWithinMinAndMax(node *Node, axis FlexDirection, value float32, axisSize float32) float32 { + min := Undefined + max := Undefined + + if flexDirectionIsColumn(axis) { + min = resolveValue(&node.Style.MinDimensions[DimensionHeight], axisSize) + max = resolveValue(&node.Style.MaxDimensions[DimensionHeight], axisSize) + } else if flexDirectionIsRow(axis) { + min = resolveValue(&node.Style.MinDimensions[DimensionWidth], axisSize) + max = resolveValue(&node.Style.MaxDimensions[DimensionWidth], axisSize) + } + + boundValue := value + + if !FloatIsUndefined(max) && max >= 0 && boundValue > max { + boundValue = max + } + + if !FloatIsUndefined(min) && min >= 0 && boundValue < min { + boundValue = min + } + + return boundValue +} + +func marginLeadingValue(node *Node, axis FlexDirection) *Value { + if flexDirectionIsRow(axis) && node.Style.Margin[EdgeStart].Unit != UnitUndefined { + return &node.Style.Margin[EdgeStart] + } + return &node.Style.Margin[leading[axis]] +} + +func marginTrailingValue(node *Node, axis FlexDirection) *Value { + if flexDirectionIsRow(axis) && node.Style.Margin[EdgeEnd].Unit != UnitUndefined { + return &node.Style.Margin[EdgeEnd] + } + return &node.Style.Margin[trailing[axis]] + +} + +// nodeBoundAxis is like nodeBoundAxisWithinMinAndMax but also ensures that +// the value doesn't go below the padding and border amount. +func nodeBoundAxis(node *Node, axis FlexDirection, value float32, axisSize float32, widthSize float32) float32 { + return fmaxf(nodeBoundAxisWithinMinAndMax(node, axis, value, axisSize), + nodePaddingAndBorderForAxis(node, axis, widthSize)) +} + +func nodeSetChildTrailingPosition(node *Node, child *Node, axis FlexDirection) { + size := child.Layout.measuredDimensions[dim[axis]] + child.Layout.Position[trailing[axis]] = + node.Layout.measuredDimensions[dim[axis]] - size - child.Layout.Position[pos[axis]] +} + +// If both left and right are defined, then use left. Otherwise return +// +left or -right depending on which is defined. +func nodeRelativePosition(node *Node, axis FlexDirection, axisSize float32) float32 { + if nodeIsLeadingPosDefined(node, axis) { + return nodeLeadingPosition(node, axis, axisSize) + } + return -nodeTrailingPosition(node, axis, axisSize) +} + +func constrainMaxSizeForMode(node *Node, axis FlexDirection, parentAxisSize float32, parentWidth float32, mode *MeasureMode, size *float32) { + maxSize := resolveValue(&node.Style.MaxDimensions[dim[axis]], parentAxisSize) + + nodeMarginForAxis(node, axis, parentWidth) + switch *mode { + case MeasureModeExactly, MeasureModeAtMost: + if FloatIsUndefined(maxSize) || *size < maxSize { + // TODO: this is redundant, but what is in original code + //*size = *size + } else { + *size = maxSize + } + case MeasureModeUndefined: + if !FloatIsUndefined(maxSize) { + *mode = MeasureModeAtMost + *size = maxSize + } + } +} + +func nodeSetPosition(node *Node, direction Direction, mainSize float32, crossSize float32, parentWidth float32) { + /* Root nodes should be always layouted as LTR, so we don't return negative values. */ + directionRespectingRoot := DirectionLTR + if node.Parent != nil { + directionRespectingRoot = direction + } + + mainAxis := resolveFlexDirection(node.Style.FlexDirection, directionRespectingRoot) + crossAxis := flexDirectionCross(mainAxis, directionRespectingRoot) + + relativePositionMain := nodeRelativePosition(node, mainAxis, mainSize) + relativePositionCross := nodeRelativePosition(node, crossAxis, crossSize) + + pos := &node.Layout.Position + pos[leading[mainAxis]] = nodeLeadingMargin(node, mainAxis, parentWidth) + relativePositionMain + pos[trailing[mainAxis]] = nodeTrailingMargin(node, mainAxis, parentWidth) + relativePositionMain + pos[leading[crossAxis]] = nodeLeadingMargin(node, crossAxis, parentWidth) + relativePositionCross + pos[trailing[crossAxis]] = nodeTrailingMargin(node, crossAxis, parentWidth) + relativePositionCross +} + +func nodeComputeFlexBasisForChild(node *Node, + child *Node, + width float32, + widthMode MeasureMode, + height float32, + parentWidth float32, + parentHeight float32, + heightMode MeasureMode, + direction Direction, + config *Config) { + mainAxis := resolveFlexDirection(node.Style.FlexDirection, direction) + isMainAxisRow := flexDirectionIsRow(mainAxis) + mainAxisSize := height + mainAxisParentSize := parentHeight + if isMainAxisRow { + mainAxisSize = width + mainAxisParentSize = parentWidth + } + + var childWidth float32 + var childHeight float32 + var childWidthMeasureMode MeasureMode + var childHeightMeasureMode MeasureMode + + resolvedFlexBasis := resolveValue(nodeResolveFlexBasisPtr(child), mainAxisParentSize) + isRowStyleDimDefined := nodeIsStyleDimDefined(child, FlexDirectionRow, parentWidth) + isColumnStyleDimDefined := nodeIsStyleDimDefined(child, FlexDirectionColumn, parentHeight) + + if !FloatIsUndefined(resolvedFlexBasis) && !FloatIsUndefined(mainAxisSize) { + if FloatIsUndefined(child.Layout.computedFlexBasis) || + (child.Config.IsExperimentalFeatureEnabled(ExperimentalFeatureWebFlexBasis) && + child.Layout.computedFlexBasisGeneration != currentGenerationCount) { + child.Layout.computedFlexBasis = + fmaxf(resolvedFlexBasis, nodePaddingAndBorderForAxis(child, mainAxis, parentWidth)) + } + } else if isMainAxisRow && isRowStyleDimDefined { + // The width is definite, so use that as the flex basis. + child.Layout.computedFlexBasis = + fmaxf(resolveValue(child.resolvedDimensions[DimensionWidth], parentWidth), + nodePaddingAndBorderForAxis(child, FlexDirectionRow, parentWidth)) + } else if !isMainAxisRow && isColumnStyleDimDefined { + // The height is definite, so use that as the flex basis. + child.Layout.computedFlexBasis = + fmaxf(resolveValue(child.resolvedDimensions[DimensionHeight], parentHeight), + nodePaddingAndBorderForAxis(child, FlexDirectionColumn, parentWidth)) + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped + // flex basis). + childWidth = Undefined + childHeight = Undefined + childWidthMeasureMode = MeasureModeUndefined + childHeightMeasureMode = MeasureModeUndefined + + marginRow := nodeMarginForAxis(child, FlexDirectionRow, parentWidth) + marginColumn := nodeMarginForAxis(child, FlexDirectionColumn, parentWidth) + + if isRowStyleDimDefined { + childWidth = + resolveValue(child.resolvedDimensions[DimensionWidth], parentWidth) + marginRow + childWidthMeasureMode = MeasureModeExactly + } + if isColumnStyleDimDefined { + childHeight = + resolveValue(child.resolvedDimensions[DimensionHeight], parentHeight) + marginColumn + childHeightMeasureMode = MeasureModeExactly + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (!isMainAxisRow && node.Style.Overflow == OverflowScroll) || + node.Style.Overflow != OverflowScroll { + if FloatIsUndefined(childWidth) && !FloatIsUndefined(width) { + childWidth = width + childWidthMeasureMode = MeasureModeAtMost + } + } + + if (isMainAxisRow && node.Style.Overflow == OverflowScroll) || + node.Style.Overflow != OverflowScroll { + if FloatIsUndefined(childHeight) && !FloatIsUndefined(height) { + childHeight = height + childHeightMeasureMode = MeasureModeAtMost + } + } + + // If child has no defined size in the cross axis and is set to stretch, + // set the cross + // axis to be measured exactly with the available inner width + if !isMainAxisRow && !FloatIsUndefined(width) && !isRowStyleDimDefined && + widthMode == MeasureModeExactly && nodeAlignItem(node, child) == AlignStretch { + childWidth = width + childWidthMeasureMode = MeasureModeExactly + } + if isMainAxisRow && !FloatIsUndefined(height) && !isColumnStyleDimDefined && + heightMode == MeasureModeExactly && nodeAlignItem(node, child) == AlignStretch { + childHeight = height + childHeightMeasureMode = MeasureModeExactly + } + + if !FloatIsUndefined(child.Style.AspectRatio) { + if !isMainAxisRow && childWidthMeasureMode == MeasureModeExactly { + child.Layout.computedFlexBasis = + fmaxf((childWidth-marginRow)/child.Style.AspectRatio, + nodePaddingAndBorderForAxis(child, FlexDirectionColumn, parentWidth)) + return + } else if isMainAxisRow && childHeightMeasureMode == MeasureModeExactly { + child.Layout.computedFlexBasis = + fmaxf((childHeight-marginColumn)*child.Style.AspectRatio, + nodePaddingAndBorderForAxis(child, FlexDirectionRow, parentWidth)) + return + } + } + + constrainMaxSizeForMode( + child, FlexDirectionRow, parentWidth, parentWidth, &childWidthMeasureMode, &childWidth) + constrainMaxSizeForMode(child, + FlexDirectionColumn, + parentHeight, + parentWidth, + &childHeightMeasureMode, + &childHeight) + + // Measure the child + layoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + parentWidth, + parentHeight, + false, + "measure", + config) + + child.Layout.computedFlexBasis = + fmaxf(child.Layout.measuredDimensions[dim[mainAxis]], + nodePaddingAndBorderForAxis(child, mainAxis, parentWidth)) + } + + child.Layout.computedFlexBasisGeneration = currentGenerationCount +} + +func nodeAbsoluteLayoutChild(node *Node, child *Node, width float32, widthMode MeasureMode, height float32, direction Direction, config *Config) { + mainAxis := resolveFlexDirection(node.Style.FlexDirection, direction) + crossAxis := flexDirectionCross(mainAxis, direction) + isMainAxisRow := flexDirectionIsRow(mainAxis) + + childWidth := Undefined + childHeight := Undefined + childWidthMeasureMode := MeasureModeUndefined + childHeightMeasureMode := MeasureModeUndefined + + marginRow := nodeMarginForAxis(child, FlexDirectionRow, width) + marginColumn := nodeMarginForAxis(child, FlexDirectionColumn, width) + + if nodeIsStyleDimDefined(child, FlexDirectionRow, width) { + childWidth = resolveValue(child.resolvedDimensions[DimensionWidth], width) + marginRow + } else { + // If the child doesn't have a specified width, compute the width based + // on the left/right + // offsets if they're defined. + if nodeIsLeadingPosDefined(child, FlexDirectionRow) && + nodeIsTrailingPosDefined(child, FlexDirectionRow) { + childWidth = node.Layout.measuredDimensions[DimensionWidth] - + (nodeLeadingBorder(node, FlexDirectionRow) + + nodeTrailingBorder(node, FlexDirectionRow)) - + (nodeLeadingPosition(child, FlexDirectionRow, width) + + nodeTrailingPosition(child, FlexDirectionRow, width)) + childWidth = nodeBoundAxis(child, FlexDirectionRow, childWidth, width, width) + } + } + + if nodeIsStyleDimDefined(child, FlexDirectionColumn, height) { + childHeight = + resolveValue(child.resolvedDimensions[DimensionHeight], height) + marginColumn + } else { + // If the child doesn't have a specified height, compute the height + // based on the top/bottom + // offsets if they're defined. + if nodeIsLeadingPosDefined(child, FlexDirectionColumn) && + nodeIsTrailingPosDefined(child, FlexDirectionColumn) { + childHeight = node.Layout.measuredDimensions[DimensionHeight] - + (nodeLeadingBorder(node, FlexDirectionColumn) + + nodeTrailingBorder(node, FlexDirectionColumn)) - + (nodeLeadingPosition(child, FlexDirectionColumn, height) + + nodeTrailingPosition(child, FlexDirectionColumn, height)) + childHeight = nodeBoundAxis(child, FlexDirectionColumn, childHeight, height, width) + } + } + + // Exactly one dimension needs to be defined for us to be able to do aspect ratio + // calculation. One dimension being the anchor and the other being flexible. + if FloatIsUndefined(childWidth) != FloatIsUndefined(childHeight) { + if !FloatIsUndefined(child.Style.AspectRatio) { + if FloatIsUndefined(childWidth) { + childWidth = + marginRow + fmaxf((childHeight-marginColumn)*child.Style.AspectRatio, + nodePaddingAndBorderForAxis(child, FlexDirectionColumn, width)) + } else if FloatIsUndefined(childHeight) { + childHeight = + marginColumn + fmaxf((childWidth-marginRow)/child.Style.AspectRatio, + nodePaddingAndBorderForAxis(child, FlexDirectionRow, width)) + } + } + } + + // If we're still missing one or the other dimension, measure the content. + if FloatIsUndefined(childWidth) || FloatIsUndefined(childHeight) { + childWidthMeasureMode = MeasureModeExactly + if FloatIsUndefined(childWidth) { + childWidthMeasureMode = MeasureModeUndefined + } + childHeightMeasureMode = MeasureModeExactly + if FloatIsUndefined(childHeight) { + childHeightMeasureMode = MeasureModeUndefined + } + + // If the size of the parent is defined then try to rain the absolute child to that size + // as well. This allows text within the absolute child to wrap to the size of its parent. + // This is the same behavior as many browsers implement. + if !isMainAxisRow && FloatIsUndefined(childWidth) && widthMode != MeasureModeUndefined && + width > 0 { + childWidth = width + childWidthMeasureMode = MeasureModeAtMost + } + + layoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + childWidth, + childHeight, + false, + "abs-measure", + config) + childWidth = child.Layout.measuredDimensions[DimensionWidth] + + nodeMarginForAxis(child, FlexDirectionRow, width) + childHeight = child.Layout.measuredDimensions[DimensionHeight] + + nodeMarginForAxis(child, FlexDirectionColumn, width) + } + + layoutNodeInternal(child, + childWidth, + childHeight, + direction, + MeasureModeExactly, + MeasureModeExactly, + childWidth, + childHeight, + true, + "abs-layout", + config) + + if nodeIsTrailingPosDefined(child, mainAxis) && !nodeIsLeadingPosDefined(child, mainAxis) { + axisSize := height + if isMainAxisRow { + axisSize = width + } + child.Layout.Position[leading[mainAxis]] = node.Layout.measuredDimensions[dim[mainAxis]] - + child.Layout.measuredDimensions[dim[mainAxis]] - + nodeTrailingBorder(node, mainAxis) - + nodeTrailingMargin(child, mainAxis, width) - + nodeTrailingPosition(child, mainAxis, axisSize) + } else if !nodeIsLeadingPosDefined(child, mainAxis) && + node.Style.JustifyContent == JustifyCenter { + child.Layout.Position[leading[mainAxis]] = (node.Layout.measuredDimensions[dim[mainAxis]] - + child.Layout.measuredDimensions[dim[mainAxis]]) / + 2.0 + } else if !nodeIsLeadingPosDefined(child, mainAxis) && + node.Style.JustifyContent == JustifyFlexEnd { + child.Layout.Position[leading[mainAxis]] = (node.Layout.measuredDimensions[dim[mainAxis]] - + child.Layout.measuredDimensions[dim[mainAxis]]) + } + + if nodeIsTrailingPosDefined(child, crossAxis) && + !nodeIsLeadingPosDefined(child, crossAxis) { + axisSize := width + if isMainAxisRow { + axisSize = height + } + + child.Layout.Position[leading[crossAxis]] = node.Layout.measuredDimensions[dim[crossAxis]] - + child.Layout.measuredDimensions[dim[crossAxis]] - + nodeTrailingBorder(node, crossAxis) - + nodeTrailingMargin(child, crossAxis, width) - + nodeTrailingPosition(child, crossAxis, axisSize) + } else if !nodeIsLeadingPosDefined(child, crossAxis) && + nodeAlignItem(node, child) == AlignCenter { + child.Layout.Position[leading[crossAxis]] = + (node.Layout.measuredDimensions[dim[crossAxis]] - + child.Layout.measuredDimensions[dim[crossAxis]]) / + 2.0 + } else if !nodeIsLeadingPosDefined(child, crossAxis) && + ((nodeAlignItem(node, child) == AlignFlexEnd) != (node.Style.FlexWrap == WrapWrapReverse)) { + child.Layout.Position[leading[crossAxis]] = (node.Layout.measuredDimensions[dim[crossAxis]] - + child.Layout.measuredDimensions[dim[crossAxis]]) + } +} + +// nodeWithMeasureFuncSetMeasuredDimensions sets measure dimensions for node with measure func +func nodeWithMeasureFuncSetMeasuredDimensions(node *Node, availableWidth float32, availableHeight float32, widthMeasureMode MeasureMode, heightMeasureMode MeasureMode, parentWidth float32, parentHeight float32) { + assertWithNode(node, node.Measure != nil, "Expected node to have custom measure function") + + paddingAndBorderAxisRow := nodePaddingAndBorderForAxis(node, FlexDirectionRow, availableWidth) + paddingAndBorderAxisColumn := nodePaddingAndBorderForAxis(node, FlexDirectionColumn, availableWidth) + marginAxisRow := nodeMarginForAxis(node, FlexDirectionRow, availableWidth) + marginAxisColumn := nodeMarginForAxis(node, FlexDirectionColumn, availableWidth) + + // We want to make sure we don't call measure with negative size + innerWidth := fmaxf(0, availableWidth-marginAxisRow-paddingAndBorderAxisRow) + if FloatIsUndefined(availableWidth) { + innerWidth = availableWidth + } + innerHeight := fmaxf(0, availableHeight-marginAxisColumn-paddingAndBorderAxisColumn) + if FloatIsUndefined(availableHeight) { + innerHeight = availableHeight + } + + if widthMeasureMode == MeasureModeExactly && heightMeasureMode == MeasureModeExactly { + // Don't bother sizing the text if both dimensions are already defined. + node.Layout.measuredDimensions[DimensionWidth] = nodeBoundAxis( + node, FlexDirectionRow, availableWidth-marginAxisRow, parentWidth, parentWidth) + node.Layout.measuredDimensions[DimensionHeight] = nodeBoundAxis( + node, FlexDirectionColumn, availableHeight-marginAxisColumn, parentHeight, parentWidth) + } else { + // Measure the text under the current raints. + measuredSize := node.Measure(node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode) + + width := availableWidth - marginAxisRow + if widthMeasureMode == MeasureModeUndefined || + widthMeasureMode == MeasureModeAtMost { + width = measuredSize.Width + paddingAndBorderAxisRow + + } + + node.Layout.measuredDimensions[DimensionWidth] = nodeBoundAxis(node, FlexDirectionRow, width, availableWidth, availableWidth) + + height := availableHeight - marginAxisColumn + if heightMeasureMode == MeasureModeUndefined || + heightMeasureMode == MeasureModeAtMost { + height = measuredSize.Height + paddingAndBorderAxisColumn + } + + node.Layout.measuredDimensions[DimensionHeight] = nodeBoundAxis(node, FlexDirectionColumn, height, availableHeight, availableWidth) + } +} + +// nodeEmptyContainerSetMeasuredDimensions sets measure dimensions for empty container +// For nodes with no children, use the available values if they were provided, +// or the minimum size as indicated by the padding and border sizes. +func nodeEmptyContainerSetMeasuredDimensions(node *Node, availableWidth float32, availableHeight float32, widthMeasureMode MeasureMode, heightMeasureMode MeasureMode, parentWidth float32, parentHeight float32) { + paddingAndBorderAxisRow := nodePaddingAndBorderForAxis(node, FlexDirectionRow, parentWidth) + paddingAndBorderAxisColumn := nodePaddingAndBorderForAxis(node, FlexDirectionColumn, parentWidth) + marginAxisRow := nodeMarginForAxis(node, FlexDirectionRow, parentWidth) + marginAxisColumn := nodeMarginForAxis(node, FlexDirectionColumn, parentWidth) + + width := availableWidth - marginAxisRow + if widthMeasureMode == MeasureModeUndefined || widthMeasureMode == MeasureModeAtMost { + width = paddingAndBorderAxisRow + } + node.Layout.measuredDimensions[DimensionWidth] = nodeBoundAxis(node, FlexDirectionRow, width, parentWidth, parentWidth) + + height := availableHeight - marginAxisColumn + if heightMeasureMode == MeasureModeUndefined || heightMeasureMode == MeasureModeAtMost { + height = paddingAndBorderAxisColumn + } + node.Layout.measuredDimensions[DimensionHeight] = nodeBoundAxis(node, FlexDirectionColumn, height, parentHeight, parentWidth) +} + +func nodeFixedSizeSetMeasuredDimensions(node *Node, + availableWidth float32, + availableHeight float32, + widthMeasureMode MeasureMode, + heightMeasureMode MeasureMode, + parentWidth float32, + parentHeight float32) bool { + if (widthMeasureMode == MeasureModeAtMost && availableWidth <= 0) || + (heightMeasureMode == MeasureModeAtMost && availableHeight <= 0) || + (widthMeasureMode == MeasureModeExactly && heightMeasureMode == MeasureModeExactly) { + marginAxisColumn := nodeMarginForAxis(node, FlexDirectionColumn, parentWidth) + marginAxisRow := nodeMarginForAxis(node, FlexDirectionRow, parentWidth) + + width := availableWidth - marginAxisRow + if FloatIsUndefined(availableWidth) || (widthMeasureMode == MeasureModeAtMost && availableWidth < 0) { + width = 0 + } + node.Layout.measuredDimensions[DimensionWidth] = + nodeBoundAxis(node, FlexDirectionRow, width, parentWidth, parentWidth) + + height := availableHeight - marginAxisColumn + if FloatIsUndefined(availableHeight) || (heightMeasureMode == MeasureModeAtMost && availableHeight < 0) { + height = 0 + } + node.Layout.measuredDimensions[DimensionHeight] = + nodeBoundAxis(node, FlexDirectionColumn, height, parentHeight, parentWidth) + + return true + } + + return false +} + +// zeroOutLayoutRecursivly zeros out layout recursively +func zeroOutLayoutRecursivly(node *Node) { + node.Layout.Dimensions[DimensionHeight] = 0 + node.Layout.Dimensions[DimensionWidth] = 0 + node.Layout.Position[EdgeTop] = 0 + node.Layout.Position[EdgeBottom] = 0 + node.Layout.Position[EdgeLeft] = 0 + node.Layout.Position[EdgeRight] = 0 + node.Layout.cachedLayout.availableHeight = 0 + node.Layout.cachedLayout.availableWidth = 0 + node.Layout.cachedLayout.heightMeasureMode = MeasureModeExactly + node.Layout.cachedLayout.widthMeasureMode = MeasureModeExactly + node.Layout.cachedLayout.computedWidth = 0 + node.Layout.cachedLayout.computedHeight = 0 + node.hasNewLayout = true + childCount := len(node.Children) + for i := 0; i < childCount; i++ { + child := node.Children[i] + zeroOutLayoutRecursivly(child) + } +} + +// This is the main routine that implements a subset of the flexbox layout +// algorithm +// described in the W3C YG documentation: https://www.w3.org/TR/YG3-flexbox/. +// +// Limitations of this algorithm, compared to the full standard: +// * Display property is always assumed to be 'flex' except for Text nodes, +// which +// are assumed to be 'inline-flex'. +// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes +// are +// stacked in document order. +// * The 'order' property is not supported. The order of flex items is always +// defined +// by document order. +// * The 'visibility' property is always assumed to be 'visible'. Values of +// 'collapse' +// and 'hidden' are not supported. +// * There is no support for forced breaks. +// * It does not support vertical inline directions (top-to-bottom or +// bottom-to-top text). +// +// Deviations from standard: +// * Section 4.5 of the spec indicates that all flex items have a default +// minimum +// main size. For text blocks, for example, this is the width of the widest +// word. +// Calculating the minimum width is expensive, so we forego it and assume a +// default +// minimum main size of 0. +// * Min/Max sizes in the main axis are not honored when resolving flexible +// lengths. +// * The spec indicates that the default value for 'flexDirection' is 'row', +// but +// the algorithm below assumes a default of 'column'. +// +// Input parameters: +// - node: current node to be sized and layed out +// - availableWidth & availableHeight: available size to be used for sizing +// the node +// or Undefined if the size is not available; interpretation depends on +// layout +// flags +// - parentDirection: the inline (text) direction within the parent +// (left-to-right or +// right-to-left) +// - widthMeasureMode: indicates the sizing rules for the width (see below +// for explanation) +// - heightMeasureMode: indicates the sizing rules for the height (see below +// for explanation) +// - performLayout: specifies whether the caller is interested in just the +// dimensions +// of the node or it requires the entire node and its subtree to be layed +// out +// (with final positions) +// +// Details: +// This routine is called recursively to lay out subtrees of flexbox +// elements. It uses the +// information in node.style, which is treated as a read-only input. It is +// responsible for +// setting the layout.direction and layout.measuredDimensions fields for the +// input node as well +// as the layout.position and layout.lineIndex fields for its child nodes. +// The +// layout.measuredDimensions field includes any border or padding for the +// node but does +// not include margins. +// +// The spec describes four different layout modes: "fill available", "max +// content", "min +// content", +// and "fit content". Of these, we don't use "min content" because we don't +// support default +// minimum main sizes (see above for details). Each of our measure modes maps +// to a layout mode +// from the spec (https://www.w3.org/TR/YG3-sizing/#terms): +// - YGMeasureModeUndefined: max content +// - YGMeasureModeExactly: fill available +// - YGMeasureModeAtMost: fit content +// +// When calling nodelayoutImpl and YGLayoutNodeInternal, if the caller passes +// an available size of +// undefined then it must also pass a measure mode of YGMeasureModeUndefined +// in that dimension. +func nodelayoutImpl(node *Node, availableWidth float32, availableHeight float32, + parentDirection Direction, widthMeasureMode MeasureMode, + heightMeasureMode MeasureMode, parentWidth float32, parentHeight float32, + performLayout bool, config *Config) { + // assertWithNode(node, YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined : true, "availableWidth is indefinite so widthMeasureMode must be YGMeasureModeUndefined"); + //assertWithNode(node, YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined : true, "availableHeight is indefinite so heightMeasureMode must be YGMeasureModeUndefined"); + + // Set the resolved resolution in the node's layout. + direction := nodeResolveDirection(node, parentDirection) + node.Layout.Direction = direction + + flexRowDirection := resolveFlexDirection(FlexDirectionRow, direction) + flexColumnDirection := resolveFlexDirection(FlexDirectionColumn, direction) + + node.Layout.Margin[EdgeStart] = nodeLeadingMargin(node, flexRowDirection, parentWidth) + node.Layout.Margin[EdgeEnd] = nodeTrailingMargin(node, flexRowDirection, parentWidth) + node.Layout.Margin[EdgeTop] = nodeLeadingMargin(node, flexColumnDirection, parentWidth) + node.Layout.Margin[EdgeBottom] = nodeTrailingMargin(node, flexColumnDirection, parentWidth) + + node.Layout.Border[EdgeStart] = nodeLeadingBorder(node, flexRowDirection) + node.Layout.Border[EdgeEnd] = nodeTrailingBorder(node, flexRowDirection) + node.Layout.Border[EdgeTop] = nodeLeadingBorder(node, flexColumnDirection) + node.Layout.Border[EdgeBottom] = nodeTrailingBorder(node, flexColumnDirection) + + node.Layout.Padding[EdgeStart] = nodeLeadingPadding(node, flexRowDirection, parentWidth) + node.Layout.Padding[EdgeEnd] = nodeTrailingPadding(node, flexRowDirection, parentWidth) + node.Layout.Padding[EdgeTop] = nodeLeadingPadding(node, flexColumnDirection, parentWidth) + node.Layout.Padding[EdgeBottom] = nodeTrailingPadding(node, flexColumnDirection, parentWidth) + + if node.Measure != nil { + nodeWithMeasureFuncSetMeasuredDimensions(node, availableWidth, availableHeight, widthMeasureMode, heightMeasureMode, parentWidth, parentHeight) + return + } + + childCount := len(node.Children) + if childCount == 0 { + nodeEmptyContainerSetMeasuredDimensions(node, availableWidth, availableHeight, widthMeasureMode, heightMeasureMode, parentWidth, parentHeight) + return + } + + // If we're not being asked to perform a full layout we can skip the algorithm if we already know + // the size + if !performLayout && nodeFixedSizeSetMeasuredDimensions(node, availableWidth, availableHeight, widthMeasureMode, heightMeasureMode, parentWidth, parentHeight) { + return + } + + // Reset layout flags, as they could have changed. + node.Layout.HadOverflow = false + + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + mainAxis := resolveFlexDirection(node.Style.FlexDirection, direction) + crossAxis := flexDirectionCross(mainAxis, direction) + isMainAxisRow := flexDirectionIsRow(mainAxis) + justifyContent := node.Style.JustifyContent + isNodeFlexWrap := node.Style.FlexWrap != WrapNoWrap + + mainAxisParentSize := parentHeight + crossAxisParentSize := parentWidth + if isMainAxisRow { + mainAxisParentSize = parentWidth + crossAxisParentSize = parentHeight + } + + var firstAbsoluteChild *Node + var currentAbsoluteChild *Node + + leadingPaddingAndBorderMain := nodeLeadingPaddingAndBorder(node, mainAxis, parentWidth) + trailingPaddingAndBorderMain := nodeTrailingPaddingAndBorder(node, mainAxis, parentWidth) + leadingPaddingAndBorderCross := nodeLeadingPaddingAndBorder(node, crossAxis, parentWidth) + paddingAndBorderAxisMain := nodePaddingAndBorderForAxis(node, mainAxis, parentWidth) + paddingAndBorderAxisCross := nodePaddingAndBorderForAxis(node, crossAxis, parentWidth) + + measureModeMainDim := heightMeasureMode + measureModeCrossDim := widthMeasureMode + + if isMainAxisRow { + measureModeMainDim = widthMeasureMode + measureModeCrossDim = heightMeasureMode + } + + paddingAndBorderAxisRow := paddingAndBorderAxisCross + paddingAndBorderAxisColumn := paddingAndBorderAxisMain + if isMainAxisRow { + paddingAndBorderAxisRow = paddingAndBorderAxisMain + paddingAndBorderAxisColumn = paddingAndBorderAxisCross + } + + marginAxisRow := nodeMarginForAxis(node, FlexDirectionRow, parentWidth) + marginAxisColumn := nodeMarginForAxis(node, FlexDirectionColumn, parentWidth) + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + minInnerWidth := resolveValue(&node.Style.MinDimensions[DimensionWidth], parentWidth) - marginAxisRow - + paddingAndBorderAxisRow + maxInnerWidth := resolveValue(&node.Style.MaxDimensions[DimensionWidth], parentWidth) - marginAxisRow - + paddingAndBorderAxisRow + minInnerHeight := resolveValue(&node.Style.MinDimensions[DimensionHeight], parentHeight) - + marginAxisColumn - paddingAndBorderAxisColumn + maxInnerHeight := resolveValue(&node.Style.MaxDimensions[DimensionHeight], parentHeight) - + marginAxisColumn - paddingAndBorderAxisColumn + + minInnerMainDim := minInnerHeight + maxInnerMainDim := maxInnerHeight + if isMainAxisRow { + minInnerMainDim = minInnerWidth + maxInnerMainDim = maxInnerWidth + } + + // Max dimension overrides predefined dimension value; Min dimension in turn overrides both of the + // above + availableInnerWidth := availableWidth - marginAxisRow - paddingAndBorderAxisRow + if !FloatIsUndefined(availableInnerWidth) { + // We want to make sure our available width does not violate min and max raints + availableInnerWidth = fmaxf(fminf(availableInnerWidth, maxInnerWidth), minInnerWidth) + } + + availableInnerHeight := availableHeight - marginAxisColumn - paddingAndBorderAxisColumn + if !FloatIsUndefined(availableInnerHeight) { + // We want to make sure our available height does not violate min and max raints + availableInnerHeight = fmaxf(fminf(availableInnerHeight, maxInnerHeight), minInnerHeight) + } + + availableInnerMainDim := availableInnerHeight + availableInnerCrossDim := availableInnerWidth + if isMainAxisRow { + availableInnerMainDim = availableInnerWidth + availableInnerCrossDim = availableInnerHeight + } + + // If there is only one child with flexGrow + flexShrink it means we can set the + // computedFlexBasis to 0 instead of measuring and shrinking / flexing the child to exactly + // match the remaining space + var singleFlexChild *Node + if measureModeMainDim == MeasureModeExactly { + for i := 0; i < childCount; i++ { + child := node.GetChild(i) + if singleFlexChild != nil { + if nodeIsFlex(child) { + // There is already a flexible child, abort. + singleFlexChild = nil + break + } + } else if resolveFlexGrow(child) > 0 && nodeResolveFlexShrink(child) > 0 { + singleFlexChild = child + } + } + } + + var totalOuterFlexBasis float32 + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + for i := 0; i < childCount; i++ { + child := node.Children[i] + if child.Style.Display == DisplayNone { + zeroOutLayoutRecursivly(child) + child.hasNewLayout = true + child.IsDirty = false + continue + } + resolveDimensions(child) + if performLayout { + // Set the initial position (relative to the parent). + childDirection := nodeResolveDirection(child, direction) + nodeSetPosition(child, + childDirection, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth) + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if child.Style.PositionType == PositionTypeAbsolute { + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if firstAbsoluteChild == nil { + firstAbsoluteChild = child + } + if currentAbsoluteChild != nil { + currentAbsoluteChild.NextChild = child + } + currentAbsoluteChild = child + child.NextChild = nil + } else { + if child == singleFlexChild { + child.Layout.computedFlexBasisGeneration = currentGenerationCount + child.Layout.computedFlexBasis = 0 + } else { + nodeComputeFlexBasisForChild(node, + child, + availableInnerWidth, + widthMeasureMode, + availableInnerHeight, + availableInnerWidth, + availableInnerHeight, + heightMeasureMode, + direction, + config) + } + } + + totalOuterFlexBasis += + child.Layout.computedFlexBasis + nodeMarginForAxis(child, mainAxis, availableInnerWidth) + + } + + flexBasisOverflows := totalOuterFlexBasis > availableInnerMainDim + if measureModeMainDim == MeasureModeUndefined { + flexBasisOverflows = false + } + if isNodeFlexWrap && flexBasisOverflows && measureModeMainDim == MeasureModeAtMost { + measureModeMainDim = MeasureModeExactly + } + + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + startOfLineIndex := 0 + endOfLineIndex := 0 + + // Number of lines. + lineCount := 0 + + // Accumulated cross dimensions of all lines so far. + var totalLineCrossDim float32 + + // Max main dimension of all the lines. + var maxLineMainDim float32 + + for endOfLineIndex < childCount { + // Number of items on the currently line. May be different than the + // difference + // between start and end indicates because we skip over absolute-positioned + // items. + itemsOnLine := 0 + + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + var sizeConsumedOnCurrentLine float32 + var sizeConsumedOnCurrentLineIncludingMinConstraint float32 + + var totalFlexGrowFactors float32 + var totalFlexShrinkScaledFactors float32 + + // Maintain a linked list of the child nodes that can shrink and/or grow. + var firstRelativeChild *Node + var currentRelativeChild *Node + + // Add items to the current line until it's full or we run out of items. + for i := startOfLineIndex; i < childCount; i++ { + child := node.Children[i] + if child.Style.Display == DisplayNone { + endOfLineIndex++ + continue + } + child.lineIndex = lineCount + + if child.Style.PositionType != PositionTypeAbsolute { + childMarginMainAxis := nodeMarginForAxis(child, mainAxis, availableInnerWidth) + flexBasisWithMaxConstraints := fminf(resolveValue(&child.Style.MaxDimensions[dim[mainAxis]], mainAxisParentSize), child.Layout.computedFlexBasis) + flexBasisWithMinAndMaxConstraints := fmaxf(resolveValue(&child.Style.MinDimensions[dim[mainAxis]], mainAxisParentSize), flexBasisWithMaxConstraints) + + // If this is a multi-line flow and this item pushes us over the + // available size, we've + // hit the end of the current line. Break out of the loop and lay out + // the current line. + if sizeConsumedOnCurrentLineIncludingMinConstraint+flexBasisWithMinAndMaxConstraints+ + childMarginMainAxis > + availableInnerMainDim && + isNodeFlexWrap && itemsOnLine > 0 { + break + } + + sizeConsumedOnCurrentLineIncludingMinConstraint += + flexBasisWithMinAndMaxConstraints + childMarginMainAxis + sizeConsumedOnCurrentLine += flexBasisWithMinAndMaxConstraints + childMarginMainAxis + itemsOnLine++ + + if nodeIsFlex(child) { + totalFlexGrowFactors += resolveFlexGrow(child) + + // Unlike the grow factor, the shrink factor is scaled relative to the child dimension. + totalFlexShrinkScaledFactors += + -nodeResolveFlexShrink(child) * child.Layout.computedFlexBasis + } + + // Store a private linked list of children that need to be layed out. + if firstRelativeChild == nil { + firstRelativeChild = child + } + if currentRelativeChild != nil { + currentRelativeChild.NextChild = child + } + currentRelativeChild = child + child.NextChild = nil + } + endOfLineIndex++ + } + + // The total flex factor needs to be floored to 1. + if totalFlexGrowFactors > 0 && totalFlexGrowFactors < 1 { + totalFlexGrowFactors = 1 + } + + // The total flex shrink factor needs to be floored to 1. + if totalFlexShrinkScaledFactors > 0 && totalFlexShrinkScaledFactors < 1 { + totalFlexShrinkScaledFactors = 1 + } + + // If we don't need to measure the cross axis, we can skip the entire flex + // step. + canSkipFlex := !performLayout && measureModeCrossDim == MeasureModeExactly + + // In order to position the elements in the main axis, we have two + // controls. The space between the beginning and the first element + // and the space between each two elements. + var leadingMainDim float32 + var betweenMainDim float32 + + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + + // If we don't measure with exact main dimension we want to ensure we don't violate min and max + if measureModeMainDim != MeasureModeExactly { + if !FloatIsUndefined(minInnerMainDim) && sizeConsumedOnCurrentLine < minInnerMainDim { + availableInnerMainDim = minInnerMainDim + } else if !FloatIsUndefined(maxInnerMainDim) && + sizeConsumedOnCurrentLine > maxInnerMainDim { + availableInnerMainDim = maxInnerMainDim + } else { + if !node.Config.UseLegacyStretchBehaviour && + (totalFlexGrowFactors == 0 || resolveFlexGrow(node) == 0) { + // If we don't have any children to flex or we can't flex the node itself, + // space we've used is all space we need. Root node also should be shrunk to minimum + availableInnerMainDim = sizeConsumedOnCurrentLine + } + } + } + + var remainingFreeSpace float32 + if !FloatIsUndefined(availableInnerMainDim) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine + } else if sizeConsumedOnCurrentLine < 0 { + // availableInnerMainDim is indefinite which means the node is being sized based on its + // content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine + } + + originalRemainingFreeSpace := remainingFreeSpace + var deltaFreeSpace float32 + + if !canSkipFlex { + var childFlexBasis float32 + var flexShrinkScaledFactor float32 + var flexGrowFactor float32 + var baseMainSize float32 + var boundMainSize float32 + + // Do two passes over the flex items to figure out how to distribute the + // remaining space. + // The first pass finds the items whose min/max raints trigger, + // freezes them at those + // sizes, and excludes those sizes from the remaining space. The second + // pass sets the size + // of each flexible item. It distributes the remaining space amongst the + // items whose min/max + // raints didn't trigger in pass 1. For the other items, it sets + // their sizes by forcing + // their min/max raints to trigger again. + // + // This two pass approach for resolving min/max raints deviates from + // the spec. The + // spec (https://www.w3.org/TR/YG-flexbox-1/#resolve-flexible-lengths) + // describes a process + // that needs to be repeated a variable number of times. The algorithm + // implemented here + // won't handle all cases but it was simpler to implement and it mitigates + // performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max raints trigger + var deltaFlexShrinkScaledFactors float32 + var deltaFlexGrowFactors float32 + currentRelativeChild = firstRelativeChild + for currentRelativeChild != nil { + childFlexBasis = + fminf(resolveValue(¤tRelativeChild.Style.MaxDimensions[dim[mainAxis]], + mainAxisParentSize), + fmaxf(resolveValue(¤tRelativeChild.Style.MinDimensions[dim[mainAxis]], + mainAxisParentSize), + currentRelativeChild.Layout.computedFlexBasis)) + + if remainingFreeSpace < 0 { + flexShrinkScaledFactor = -nodeResolveFlexShrink(currentRelativeChild) * childFlexBasis + + // Is this child able to shrink? + if flexShrinkScaledFactor != 0 { + baseMainSize = + childFlexBasis + + remainingFreeSpace/totalFlexShrinkScaledFactors*flexShrinkScaledFactor + boundMainSize = nodeBoundAxis(currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth) + if baseMainSize != boundMainSize { + // By excluding this item's size and flex factor from remaining, + // this item's + // min/max raints should also trigger in the second pass + // resulting in the + // item's size calculation being identical in the first and second + // passes. + deltaFreeSpace -= boundMainSize - childFlexBasis + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor + } + } + } else if remainingFreeSpace > 0 { + flexGrowFactor = resolveFlexGrow(currentRelativeChild) + + // Is this child able to grow? + if flexGrowFactor != 0 { + baseMainSize = + childFlexBasis + remainingFreeSpace/totalFlexGrowFactors*flexGrowFactor + boundMainSize = nodeBoundAxis(currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth) + + if baseMainSize != boundMainSize { + // By excluding this item's size and flex factor from remaining, + // this item's + // min/max raints should also trigger in the second pass + // resulting in the + // item's size calculation being identical in the first and second + // passes. + deltaFreeSpace -= boundMainSize - childFlexBasis + deltaFlexGrowFactors -= flexGrowFactor + } + } + } + + currentRelativeChild = currentRelativeChild.NextChild + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors + totalFlexGrowFactors += deltaFlexGrowFactors + remainingFreeSpace += deltaFreeSpace + + // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0 + currentRelativeChild = firstRelativeChild + for currentRelativeChild != nil { + childFlexBasis = + fminf(resolveValue(¤tRelativeChild.Style.MaxDimensions[dim[mainAxis]], + mainAxisParentSize), + fmaxf(resolveValue(¤tRelativeChild.Style.MinDimensions[dim[mainAxis]], + mainAxisParentSize), + currentRelativeChild.Layout.computedFlexBasis)) + updatedMainSize := childFlexBasis + + if remainingFreeSpace < 0 { + flexShrinkScaledFactor = -nodeResolveFlexShrink(currentRelativeChild) * childFlexBasis + // Is this child able to shrink? + if flexShrinkScaledFactor != 0 { + var childSize float32 + + if totalFlexShrinkScaledFactors == 0 { + childSize = childFlexBasis + flexShrinkScaledFactor + } else { + childSize = + childFlexBasis + + (remainingFreeSpace/totalFlexShrinkScaledFactors)*flexShrinkScaledFactor + } + + updatedMainSize = nodeBoundAxis(currentRelativeChild, + mainAxis, + childSize, + availableInnerMainDim, + availableInnerWidth) + } + } else if remainingFreeSpace > 0 { + flexGrowFactor = resolveFlexGrow(currentRelativeChild) + + // Is this child able to grow? + if flexGrowFactor != 0 { + updatedMainSize = + nodeBoundAxis(currentRelativeChild, + mainAxis, + childFlexBasis+ + remainingFreeSpace/totalFlexGrowFactors*flexGrowFactor, + availableInnerMainDim, + availableInnerWidth) + } + } + + deltaFreeSpace -= updatedMainSize - childFlexBasis + + marginMain := nodeMarginForAxis(currentRelativeChild, mainAxis, availableInnerWidth) + marginCross := nodeMarginForAxis(currentRelativeChild, crossAxis, availableInnerWidth) + + var childCrossSize float32 + childMainSize := updatedMainSize + marginMain + var childCrossMeasureMode MeasureMode + childMainMeasureMode := MeasureModeExactly + + if !FloatIsUndefined(availableInnerCrossDim) && + !nodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) && + measureModeCrossDim == MeasureModeExactly && + !(isNodeFlexWrap && flexBasisOverflows) && + nodeAlignItem(node, currentRelativeChild) == AlignStretch { + childCrossSize = availableInnerCrossDim + childCrossMeasureMode = MeasureModeExactly + } else if !nodeIsStyleDimDefined(currentRelativeChild, + crossAxis, + availableInnerCrossDim) { + childCrossSize = availableInnerCrossDim + childCrossMeasureMode = MeasureModeAtMost + if FloatIsUndefined(childCrossSize) { + childCrossMeasureMode = MeasureModeUndefined + } + } else { + childCrossSize = resolveValue(currentRelativeChild.resolvedDimensions[dim[crossAxis]], + availableInnerCrossDim) + + marginCross + isLoosePercentageMeasurement := currentRelativeChild.resolvedDimensions[dim[crossAxis]].Unit == UnitPercent && + measureModeCrossDim != MeasureModeExactly + childCrossMeasureMode = MeasureModeExactly + if FloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement { + childCrossMeasureMode = MeasureModeUndefined + } + } + + if !FloatIsUndefined(currentRelativeChild.Style.AspectRatio) { + v := (childMainSize - marginMain) * currentRelativeChild.Style.AspectRatio + if isMainAxisRow { + v = (childMainSize - marginMain) / currentRelativeChild.Style.AspectRatio + } + childCrossSize = fmaxf(v, nodePaddingAndBorderForAxis(currentRelativeChild, crossAxis, availableInnerWidth)) + childCrossMeasureMode = MeasureModeExactly + + // Parent size raint should have higher priority than flex + if nodeIsFlex(currentRelativeChild) { + childCrossSize = fminf(childCrossSize-marginCross, availableInnerCrossDim) + childMainSize = marginMain + if isMainAxisRow { + childMainSize += childCrossSize * currentRelativeChild.Style.AspectRatio + } else { + childMainSize += childCrossSize / currentRelativeChild.Style.AspectRatio + } + } + + childCrossSize += marginCross + } + + constrainMaxSizeForMode(currentRelativeChild, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize) + constrainMaxSizeForMode(currentRelativeChild, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize) + + requiresStretchLayout := !nodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) && + nodeAlignItem(node, currentRelativeChild) == AlignStretch + + childWidth := childCrossSize + if isMainAxisRow { + childWidth = childMainSize + } + childHeight := childCrossSize + if !isMainAxisRow { + childHeight = childMainSize + } + + childWidthMeasureMode := childCrossMeasureMode + if isMainAxisRow { + childWidthMeasureMode = childMainMeasureMode + } + childHeightMeasureMode := childCrossMeasureMode + if !isMainAxisRow { + childHeightMeasureMode = childMainMeasureMode + } + + // Recursively call the layout algorithm for this child with the updated + // main size. + layoutNodeInternal(currentRelativeChild, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + performLayout && !requiresStretchLayout, + "flex", + config) + if currentRelativeChild.Layout.HadOverflow { + node.Layout.HadOverflow = true + } + + currentRelativeChild = currentRelativeChild.NextChild + } + } + + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace + if remainingFreeSpace < 0 { + node.Layout.HadOverflow = true + } + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main + // axis. + // Their dimensions are also set in the cross axis with the exception of + // items + // that are aligned "stretch". We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis. Calculate the remaining space when + // raint by the min size defined for the main axis. + + if measureModeMainDim == MeasureModeAtMost && remainingFreeSpace > 0 { + if node.Style.MinDimensions[dim[mainAxis]].Unit != UnitUndefined && + resolveValue(&node.Style.MinDimensions[dim[mainAxis]], mainAxisParentSize) >= 0 { + remainingFreeSpace = + fmaxf(0, + resolveValue(&node.Style.MinDimensions[dim[mainAxis]], mainAxisParentSize)- + (availableInnerMainDim-remainingFreeSpace)) + } else { + remainingFreeSpace = 0 + } + } + + numberOfAutoMarginsOnCurrentLine := 0 + for i := startOfLineIndex; i < endOfLineIndex; i++ { + child := node.Children[i] + if child.Style.PositionType == PositionTypeRelative { + if marginLeadingValue(child, mainAxis).Unit == UnitAuto { + numberOfAutoMarginsOnCurrentLine++ + } + if marginTrailingValue(child, mainAxis).Unit == UnitAuto { + numberOfAutoMarginsOnCurrentLine++ + } + } + } + + if numberOfAutoMarginsOnCurrentLine == 0 { + switch justifyContent { + case JustifyCenter: + leadingMainDim = remainingFreeSpace / 2 + case JustifyFlexEnd: + leadingMainDim = remainingFreeSpace + case JustifySpaceBetween: + if itemsOnLine > 1 { + betweenMainDim = fmaxf(remainingFreeSpace, 0) / float32(itemsOnLine-1) + } else { + betweenMainDim = 0 + } + case JustifySpaceAround: + // Space on the edges is half of the space between elements + betweenMainDim = remainingFreeSpace / float32(itemsOnLine) + leadingMainDim = betweenMainDim / 2 + case JustifyFlexStart: + } + } + + mainDim := leadingPaddingAndBorderMain + leadingMainDim + var crossDim float32 + + for i := startOfLineIndex; i < endOfLineIndex; i++ { + child := node.Children[i] + if child.Style.Display == DisplayNone { + continue + } + if child.Style.PositionType == PositionTypeAbsolute && + nodeIsLeadingPosDefined(child, mainAxis) { + if performLayout { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child.Layout.Position[pos[mainAxis]] = + nodeLeadingPosition(child, mainAxis, availableInnerMainDim) + + nodeLeadingBorder(node, mainAxis) + + nodeLeadingMargin(child, mainAxis, availableInnerWidth) + } + } else { + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements + // do not take part in that phase. + if child.Style.PositionType == PositionTypeRelative { + if marginLeadingValue(child, mainAxis).Unit == UnitAuto { + mainDim += remainingFreeSpace / float32(numberOfAutoMarginsOnCurrentLine) + } + + if performLayout { + child.Layout.Position[pos[mainAxis]] += mainDim + } + + if marginTrailingValue(child, mainAxis).Unit == UnitAuto { + mainDim += remainingFreeSpace / float32(numberOfAutoMarginsOnCurrentLine) + } + + if canSkipFlex { + // If we skipped the flex step, then we can't rely on the + // measuredDims because + // they weren't computed. This means we can't call YGNodeDimWithMargin. + mainDim += betweenMainDim + nodeMarginForAxis(child, mainAxis, availableInnerWidth) + + child.Layout.computedFlexBasis + crossDim = availableInnerCrossDim + } else { + // The main dimension is the sum of all the elements dimension plus the spacing. + mainDim += betweenMainDim + nodeDimWithMargin(child, mainAxis, availableInnerWidth) + + // The cross dimension is the max of the elements dimension since + // there can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, nodeDimWithMargin(child, crossAxis, availableInnerWidth)) + } + } else if performLayout { + child.Layout.Position[pos[mainAxis]] += + nodeLeadingBorder(node, mainAxis) + leadingMainDim + } + } + } + + mainDim += trailingPaddingAndBorderMain + + containerCrossAxis := availableInnerCrossDim + if measureModeCrossDim == MeasureModeUndefined || + measureModeCrossDim == MeasureModeAtMost { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = nodeBoundAxis(node, + crossAxis, + crossDim+paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) - + paddingAndBorderAxisCross + } + + // If there's no flex wrap, the cross dimension is defined by the container. + if !isNodeFlexWrap && measureModeCrossDim == MeasureModeExactly { + crossDim = availableInnerCrossDim + } + + // Clamp to the min/max size specified on the container. + crossDim = nodeBoundAxis(node, + crossAxis, + crossDim+paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) - + paddingAndBorderAxisCross + + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if performLayout { + for i := startOfLineIndex; i < endOfLineIndex; i++ { + child := node.Children[i] + if child.Style.Display == DisplayNone { + continue + } + if child.Style.PositionType == PositionTypeAbsolute { + // If the child is absolutely positioned and has a + // top/left/bottom/right + // set, override all the previously computed positions to set it + // correctly. + if nodeIsLeadingPosDefined(child, crossAxis) { + child.Layout.Position[pos[crossAxis]] = + nodeLeadingPosition(child, crossAxis, availableInnerCrossDim) + + nodeLeadingBorder(node, crossAxis) + + nodeLeadingMargin(child, crossAxis, availableInnerWidth) + } else { + child.Layout.Position[pos[crossAxis]] = + nodeLeadingBorder(node, crossAxis) + + nodeLeadingMargin(child, crossAxis, availableInnerWidth) + } + } else { + leadingCrossDim := leadingPaddingAndBorderCross + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross + // axis + alignItem := nodeAlignItem(node, child) + + // If the child uses align stretch, we need to lay it out one more + // time, this time + // forcing the cross-axis size to be the computed cross size for the + // current line. + if alignItem == AlignStretch && + marginLeadingValue(child, crossAxis).Unit != UnitAuto && + marginTrailingValue(child, crossAxis).Unit != UnitAuto { + // If the child defines a definite size for its cross axis, there's + // no need to stretch. + if !nodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim) { + childMainSize := child.Layout.measuredDimensions[dim[mainAxis]] + childCrossSize := crossDim + if !FloatIsUndefined(child.Style.AspectRatio) { + childCrossSize = nodeMarginForAxis(child, crossAxis, availableInnerWidth) + if isMainAxisRow { + childCrossSize += childMainSize / child.Style.AspectRatio + } else { + childCrossSize += childMainSize * child.Style.AspectRatio + } + } + + childMainSize += nodeMarginForAxis(child, mainAxis, availableInnerWidth) + + childMainMeasureMode := MeasureModeExactly + childCrossMeasureMode := MeasureModeExactly + constrainMaxSizeForMode(child, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize) + constrainMaxSizeForMode(child, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize) + + childWidth := childCrossSize + if isMainAxisRow { + childWidth = childMainSize + } + childHeight := childCrossSize + if !isMainAxisRow { + childHeight = childMainSize + } + + childWidthMeasureMode := MeasureModeExactly + if FloatIsUndefined(childWidth) { + childWidthMeasureMode = MeasureModeUndefined + } + + childHeightMeasureMode := MeasureModeExactly + if FloatIsUndefined(childHeight) { + childHeightMeasureMode = MeasureModeUndefined + } + + layoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + true, + "stretch", + config) + } + } else { + remainingCrossDim := containerCrossAxis - nodeDimWithMargin(child, crossAxis, availableInnerWidth) + + if marginLeadingValue(child, crossAxis).Unit == UnitAuto && + marginTrailingValue(child, crossAxis).Unit == UnitAuto { + leadingCrossDim += fmaxf(0, remainingCrossDim/2) + } else if marginTrailingValue(child, crossAxis).Unit == UnitAuto { + // No-Op + } else if marginLeadingValue(child, crossAxis).Unit == UnitAuto { + leadingCrossDim += fmaxf(0, remainingCrossDim) + } else if alignItem == AlignFlexStart { + // No-Op + } else if alignItem == AlignCenter { + leadingCrossDim += remainingCrossDim / 2 + } else { + leadingCrossDim += remainingCrossDim + } + } + // And we apply the position + child.Layout.Position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim + } + } + } + + totalLineCrossDim += crossDim + maxLineMainDim = fmaxf(maxLineMainDim, mainDim) + + lineCount++ + startOfLineIndex = endOfLineIndex + + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if performLayout && (lineCount > 1 || isBaselineLayout(node)) && + !FloatIsUndefined(availableInnerCrossDim) { + remainingAlignContentDim := availableInnerCrossDim - totalLineCrossDim + + var crossDimLead float32 + currentLead := leadingPaddingAndBorderCross + + switch node.Style.AlignContent { + case AlignFlexEnd: + currentLead += remainingAlignContentDim + case AlignCenter: + currentLead += remainingAlignContentDim / 2 + case AlignStretch: + if availableInnerCrossDim > totalLineCrossDim { + crossDimLead = remainingAlignContentDim / float32(lineCount) + } + case AlignSpaceAround: + if availableInnerCrossDim > totalLineCrossDim { + currentLead += remainingAlignContentDim / float32(2*lineCount) + if lineCount > 1 { + crossDimLead = remainingAlignContentDim / float32(lineCount) + } + } else { + currentLead += remainingAlignContentDim / 2 + } + case AlignSpaceBetween: + if availableInnerCrossDim > totalLineCrossDim && lineCount > 1 { + crossDimLead = remainingAlignContentDim / float32(lineCount-1) + } + case AlignAuto: + case AlignFlexStart: + case AlignBaseline: + } + + endIndex := 0 + for i := 0; i < lineCount; i++ { + startIndex := endIndex + var ii int + + // compute the line's height and find the endIndex + var lineHeight float32 + var maxAscentForCurrentLine float32 + var maxDescentForCurrentLine float32 + for ii = startIndex; ii < childCount; ii++ { + child := node.Children[ii] + if child.Style.Display == DisplayNone { + continue + } + if child.Style.PositionType == PositionTypeRelative { + if child.lineIndex != i { + break + } + if nodeIsLayoutDimDefined(child, crossAxis) { + lineHeight = fmaxf(lineHeight, + child.Layout.measuredDimensions[dim[crossAxis]]+ + nodeMarginForAxis(child, crossAxis, availableInnerWidth)) + } + if nodeAlignItem(node, child) == AlignBaseline { + ascent := Baseline(child) + nodeLeadingMargin(child, FlexDirectionColumn, availableInnerWidth) + descent := child.Layout.measuredDimensions[DimensionHeight] + nodeMarginForAxis(child, FlexDirectionColumn, availableInnerWidth) - ascent + maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent) + maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent) + lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine+maxDescentForCurrentLine) + } + } + } + endIndex = ii + lineHeight += crossDimLead + + if performLayout { + for ii = startIndex; ii < endIndex; ii++ { + child := node.Children[ii] + if child.Style.Display == DisplayNone { + continue + } + if child.Style.PositionType == PositionTypeRelative { + switch nodeAlignItem(node, child) { + case AlignFlexStart: + { + child.Layout.Position[pos[crossAxis]] = + currentLead + nodeLeadingMargin(child, crossAxis, availableInnerWidth) + } + case AlignFlexEnd: + { + child.Layout.Position[pos[crossAxis]] = + currentLead + lineHeight - + nodeTrailingMargin(child, crossAxis, availableInnerWidth) - + child.Layout.measuredDimensions[dim[crossAxis]] + } + case AlignCenter: + { + childHeight := child.Layout.measuredDimensions[dim[crossAxis]] + child.Layout.Position[pos[crossAxis]] = currentLead + (lineHeight-childHeight)/2 + } + case AlignStretch: + { + child.Layout.Position[pos[crossAxis]] = + currentLead + nodeLeadingMargin(child, crossAxis, availableInnerWidth) + + // Remeasure child with the line height as it as been only measured with the + // parents height yet. + if !nodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim) { + childWidth := lineHeight + if isMainAxisRow { + childWidth = child.Layout.measuredDimensions[DimensionWidth] + + nodeMarginForAxis(child, mainAxis, availableInnerWidth) + } + + childHeight := lineHeight + if !isMainAxisRow { + childHeight = child.Layout.measuredDimensions[DimensionHeight] + + nodeMarginForAxis(child, crossAxis, availableInnerWidth) + } + + if !(FloatsEqual(childWidth, + child.Layout.measuredDimensions[DimensionWidth]) && + FloatsEqual(childHeight, + child.Layout.measuredDimensions[DimensionHeight])) { + layoutNodeInternal(child, + childWidth, + childHeight, + direction, + MeasureModeExactly, + MeasureModeExactly, + availableInnerWidth, + availableInnerHeight, + true, + "multiline-stretch", + config) + } + } + } + case AlignBaseline: + { + child.Layout.Position[EdgeTop] = + currentLead + maxAscentForCurrentLine - Baseline(child) + + nodeLeadingPosition(child, FlexDirectionColumn, availableInnerCrossDim) + } + case AlignAuto: + case AlignSpaceBetween: + case AlignSpaceAround: + } + } + } + } + + currentLead += lineHeight + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + node.Layout.measuredDimensions[DimensionWidth] = nodeBoundAxis( + node, FlexDirectionRow, availableWidth-marginAxisRow, parentWidth, parentWidth) + node.Layout.measuredDimensions[DimensionHeight] = nodeBoundAxis( + node, FlexDirectionColumn, availableHeight-marginAxisColumn, parentHeight, parentWidth) + + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if measureModeMainDim == MeasureModeUndefined || + (node.Style.Overflow != OverflowScroll && measureModeMainDim == MeasureModeAtMost) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.Layout.measuredDimensions[dim[mainAxis]] = + nodeBoundAxis(node, mainAxis, maxLineMainDim, mainAxisParentSize, parentWidth) + } else if measureModeMainDim == MeasureModeAtMost && + node.Style.Overflow == OverflowScroll { + node.Layout.measuredDimensions[dim[mainAxis]] = fmaxf( + fminf(availableInnerMainDim+paddingAndBorderAxisMain, + nodeBoundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim, mainAxisParentSize)), + paddingAndBorderAxisMain) + } + + if measureModeCrossDim == MeasureModeUndefined || + (node.Style.Overflow != OverflowScroll && measureModeCrossDim == MeasureModeAtMost) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.Layout.measuredDimensions[dim[crossAxis]] = + nodeBoundAxis(node, + crossAxis, + totalLineCrossDim+paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) + } else if measureModeCrossDim == MeasureModeAtMost && + node.Style.Overflow == OverflowScroll { + node.Layout.measuredDimensions[dim[crossAxis]] = + fmaxf(fminf(availableInnerCrossDim+paddingAndBorderAxisCross, + nodeBoundAxisWithinMinAndMax(node, + crossAxis, + totalLineCrossDim+paddingAndBorderAxisCross, + crossAxisParentSize)), + paddingAndBorderAxisCross) + } + + // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse. + if performLayout && node.Style.FlexWrap == WrapWrapReverse { + for i := 0; i < childCount; i++ { + child := node.GetChild(i) + if child.Style.PositionType == PositionTypeRelative { + child.Layout.Position[pos[crossAxis]] = node.Layout.measuredDimensions[dim[crossAxis]] - + child.Layout.Position[pos[crossAxis]] - + child.Layout.measuredDimensions[dim[crossAxis]] + } + } + } + + if performLayout { + // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN + for currentAbsoluteChild = firstAbsoluteChild; currentAbsoluteChild != nil; currentAbsoluteChild = currentAbsoluteChild.NextChild { + mode := measureModeCrossDim + if isMainAxisRow { + mode = measureModeMainDim + } + + nodeAbsoluteLayoutChild(node, + currentAbsoluteChild, + availableInnerWidth, + mode, + availableInnerHeight, + direction, + config) + } + + // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN + needsMainTrailingPos := mainAxis == FlexDirectionRowReverse || mainAxis == FlexDirectionColumnReverse + needsCrossTrailingPos := crossAxis == FlexDirectionRowReverse || crossAxis == FlexDirectionColumnReverse + + // Set trailing position if necessary. + if needsMainTrailingPos || needsCrossTrailingPos { + for i := 0; i < childCount; i++ { + child := node.Children[i] + if child.Style.Display == DisplayNone { + continue + } + if needsMainTrailingPos { + nodeSetChildTrailingPosition(node, child, mainAxis) + } + + if needsCrossTrailingPos { + nodeSetChildTrailingPosition(node, child, crossAxis) + } + } + } + } +} + +var ( + gDepth = 0 + gPrintTree = false + gPrintChanges = false + gPrintSkips = false +) + +const ( + spacerStr = " " +) + +// spacer returns spacer string +func spacer(level int) string { + n := len(spacerStr) + if level > n { + level = n + } + return spacerStr[:level] +} + +var ( + measureModeNames = [measureModeCount]string{"UNDEFINED", "EXACTLY", "AT_MOST"} + layoutModeNames = [measureModeCount]string{"LAY_UNDEFINED", "LAY_EXACTLY", "LAY_AT_MOST"} +) + +// measureModeName returns name of measure mode +func measureModeName(mode MeasureMode, performLayout bool) string { + + if mode >= measureModeCount { + return "" + } + + if performLayout { + return layoutModeNames[mode] + } + return measureModeNames[mode] +} + +func measureModeSizeIsExactAndMatchesOldMeasuredSize(sizeMode MeasureMode, size float32, lastComputedSize float32) bool { + return sizeMode == MeasureModeExactly && FloatsEqual(size, lastComputedSize) +} + +func measureModeOldSizeIsUnspecifiedAndStillFits(sizeMode MeasureMode, size float32, lastSizeMode MeasureMode, lastComputedSize float32) bool { + return sizeMode == MeasureModeAtMost && lastSizeMode == MeasureModeUndefined && + (size >= lastComputedSize || FloatsEqual(size, lastComputedSize)) +} + +func measureModeNewMeasureSizeIsStricterAndStillValid(sizeMode MeasureMode, size float32, lastSizeMode MeasureMode, lastSize float32, lastComputedSize float32) bool { + return lastSizeMode == MeasureModeAtMost && sizeMode == MeasureModeAtMost && + lastSize > size && (lastComputedSize <= size || FloatsEqual(size, lastComputedSize)) +} + +// roundValueToPixelGrid rounds value to pixel grid +func roundValueToPixelGrid(value float32, pointScaleFactor float32, forceCeil bool, forceFloor bool) float32 { + scaledValue := value * pointScaleFactor + fractial := fmodf(scaledValue, 1.0) + if FloatsEqual(fractial, 0) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial + } else if FloatsEqual(fractial, 1.0) { + scaledValue = scaledValue - fractial + 1.0 + } else if forceCeil { + // Next we check if we need to use forced rounding + scaledValue = scaledValue - fractial + 1.0 + } else if forceFloor { + scaledValue = scaledValue - fractial + } else { + // Finally we just round the value + var f float32 + if fractial >= 0.5 { + f = 1.0 + } + scaledValue = scaledValue - fractial + f + } + return scaledValue / pointScaleFactor +} + +// nodeCanUseCachedMeasurement returns true if can use cached measurement +func nodeCanUseCachedMeasurement(widthMode MeasureMode, width float32, heightMode MeasureMode, height float32, lastWidthMode MeasureMode, lastWidth float32, lastHeightMode MeasureMode, lastHeight float32, lastComputedWidth float32, lastComputedHeight float32, marginRow float32, marginColumn float32, config *Config) bool { + if lastComputedHeight < 0 || lastComputedWidth < 0 { + return false + } + useRoundedComparison := config != nil && config.PointScaleFactor != 0 + effectiveWidth := width + effectiveHeight := height + effectiveLastWidth := lastWidth + effectiveLastHeight := lastHeight + + if useRoundedComparison { + effectiveWidth = roundValueToPixelGrid(width, config.PointScaleFactor, false, false) + effectiveHeight = roundValueToPixelGrid(height, config.PointScaleFactor, false, false) + effectiveLastWidth = roundValueToPixelGrid(lastWidth, config.PointScaleFactor, false, false) + effectiveLastHeight = roundValueToPixelGrid(lastHeight, config.PointScaleFactor, false, false) + } + + hasSameWidthSpec := lastWidthMode == widthMode && FloatsEqual(effectiveLastWidth, effectiveWidth) + hasSameHeightSpec := lastHeightMode == heightMode && FloatsEqual(effectiveLastHeight, effectiveHeight) + + widthIsCompatible := + hasSameWidthSpec || measureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode, + width-marginRow, + lastComputedWidth) || + measureModeOldSizeIsUnspecifiedAndStillFits(widthMode, + width-marginRow, + lastWidthMode, + lastComputedWidth) || + measureModeNewMeasureSizeIsStricterAndStillValid( + widthMode, width-marginRow, lastWidthMode, lastWidth, lastComputedWidth) + + heightIsCompatible := + hasSameHeightSpec || measureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode, + height-marginColumn, + lastComputedHeight) || + measureModeOldSizeIsUnspecifiedAndStillFits(heightMode, + height-marginColumn, + lastHeightMode, + lastComputedHeight) || + measureModeNewMeasureSizeIsStricterAndStillValid( + heightMode, height-marginColumn, lastHeightMode, lastHeight, lastComputedHeight) + + return widthIsCompatible && heightIsCompatible +} + +// layoutNodeInternal is a wrapper around the YGNodelayoutImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as YGNodelayoutImpl (see above) +// Return parameter is true if layout was performed, false if skipped +func layoutNodeInternal(node *Node, availableWidth float32, availableHeight float32, + parentDirection Direction, widthMeasureMode MeasureMode, + heightMeasureMode MeasureMode, parentWidth float32, parentHeight float32, + performLayout bool, reason string, config *Config) bool { + layout := &node.Layout + + gDepth++ + + needToVisitNode := + (node.IsDirty && layout.generationCount != currentGenerationCount) || + layout.lastParentDirection != parentDirection + + if needToVisitNode { + // Invalidate the cached results. + layout.nextCachedMeasurementsIndex = 0 + layout.cachedLayout.widthMeasureMode = MeasureMode(-1) + layout.cachedLayout.heightMeasureMode = MeasureMode(-1) + layout.cachedLayout.computedWidth = -1 + layout.cachedLayout.computedHeight = -1 + } + + var cachedResults *CachedMeasurement + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the + // positions + // and dimensions for nodes in the subtree. The algorithm assumes that each + // node + // gets layed out a maximum of one time per tree layout, but multiple + // measurements + // may be required to resolve all of the flex dimensions. + // We handle nodes with measure functions specially here because they are the + // most + // expensive to measure, so it's worth avoiding redundant measurements if at + // all possible. + if node.Measure != nil { + marginAxisRow := nodeMarginForAxis(node, FlexDirectionRow, parentWidth) + marginAxisColumn := nodeMarginForAxis(node, FlexDirectionColumn, parentWidth) + + // First, try to use the layout cache. + if nodeCanUseCachedMeasurement(widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout.cachedLayout.widthMeasureMode, + layout.cachedLayout.availableWidth, + layout.cachedLayout.heightMeasureMode, + layout.cachedLayout.availableHeight, + layout.cachedLayout.computedWidth, + layout.cachedLayout.computedHeight, + marginAxisRow, + marginAxisColumn, + config) { + cachedResults = &layout.cachedLayout + } else { + // Try to use the measurement cache. + for i := 0; i < layout.nextCachedMeasurementsIndex; i++ { + if nodeCanUseCachedMeasurement(widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout.cachedMeasurements[i].widthMeasureMode, + layout.cachedMeasurements[i].availableWidth, + layout.cachedMeasurements[i].heightMeasureMode, + layout.cachedMeasurements[i].availableHeight, + layout.cachedMeasurements[i].computedWidth, + layout.cachedMeasurements[i].computedHeight, + marginAxisRow, + marginAxisColumn, + config) { + cachedResults = &layout.cachedMeasurements[i] + break + } + } + } + } else if performLayout { + if FloatsEqual(layout.cachedLayout.availableWidth, availableWidth) && + FloatsEqual(layout.cachedLayout.availableHeight, availableHeight) && + layout.cachedLayout.widthMeasureMode == widthMeasureMode && + layout.cachedLayout.heightMeasureMode == heightMeasureMode { + cachedResults = &layout.cachedLayout + } + } else { + for i := 0; i < layout.nextCachedMeasurementsIndex; i++ { + if FloatsEqual(layout.cachedMeasurements[i].availableWidth, availableWidth) && + FloatsEqual(layout.cachedMeasurements[i].availableHeight, availableHeight) && + layout.cachedMeasurements[i].widthMeasureMode == widthMeasureMode && + layout.cachedMeasurements[i].heightMeasureMode == heightMeasureMode { + cachedResults = &layout.cachedMeasurements[i] + break + } + } + } + + if !needToVisitNode && cachedResults != nil { + layout.measuredDimensions[DimensionWidth] = cachedResults.computedWidth + layout.measuredDimensions[DimensionHeight] = cachedResults.computedHeight + + if gPrintChanges && gPrintSkips { + fmt.Printf("%s%d.{[skipped] ", spacer(gDepth), gDepth) + if node.Print != nil { + node.Print(node) + } + fmt.Printf("wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n", + measureModeName(widthMeasureMode, performLayout), + measureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + cachedResults.computedWidth, + cachedResults.computedHeight, + reason) + } + } else { + if gPrintChanges { + s := "" + if needToVisitNode { + s = "*" + } + fmt.Printf("%s%d.{%s", spacer(gDepth), gDepth, s) + if node.Print != nil { + node.Print(node) + } + fmt.Printf("wm: %s, hm: %s, aw: %f ah: %f %s\n", + measureModeName(widthMeasureMode, performLayout), + measureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + reason) + } + + nodelayoutImpl(node, + availableWidth, + availableHeight, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + performLayout, + config) + + if gPrintChanges { + s := "" + if needToVisitNode { + s = "*" + } + fmt.Printf("%s%d.}%s", spacer(gDepth), gDepth, s) + if node.Print != nil { + node.Print(node) + } + fmt.Printf("wm: %s, hm: %s, d: (%f, %f) %s\n", + measureModeName(widthMeasureMode, performLayout), + measureModeName(heightMeasureMode, performLayout), + layout.measuredDimensions[DimensionWidth], + layout.measuredDimensions[DimensionHeight], + reason) + } + + layout.lastParentDirection = parentDirection + + if cachedResults == nil { + if layout.nextCachedMeasurementsIndex == maxCachedResultCount { + if gPrintChanges { + fmt.Printf("Out of cache entries!\n") + } + layout.nextCachedMeasurementsIndex = 0 + } + + var newCacheEntry *CachedMeasurement + if performLayout { + // Use the single layout cache entry. + newCacheEntry = &layout.cachedLayout + } else { + // Allocate a new measurement cache entry. + newCacheEntry = &layout.cachedMeasurements[layout.nextCachedMeasurementsIndex] + layout.nextCachedMeasurementsIndex++ + } + + newCacheEntry.availableWidth = availableWidth + newCacheEntry.availableHeight = availableHeight + newCacheEntry.widthMeasureMode = widthMeasureMode + newCacheEntry.heightMeasureMode = heightMeasureMode + newCacheEntry.computedWidth = layout.measuredDimensions[DimensionWidth] + newCacheEntry.computedHeight = layout.measuredDimensions[DimensionHeight] + } + } + + if performLayout { + node.Layout.Dimensions[DimensionWidth] = node.Layout.measuredDimensions[DimensionWidth] + node.Layout.Dimensions[DimensionHeight] = node.Layout.measuredDimensions[DimensionHeight] + node.hasNewLayout = true + node.IsDirty = false + } + + gDepth-- + layout.generationCount = currentGenerationCount + return needToVisitNode || cachedResults == nil +} + +// SetPointScaleFactor sets scale factor +func (config *Config) SetPointScaleFactor(pixelsInPoint float32) { + assertWithConfig(config, pixelsInPoint >= 0, "Scale factor should not be less than zero") + + // We store points for Pixel as we will use it for rounding + if pixelsInPoint == 0 { + // Zero is used to skip rounding + config.PointScaleFactor = 0 + } else { + config.PointScaleFactor = pixelsInPoint + } +} + +func roundToPixelGrid(node *Node, pointScaleFactor float32, absoluteLeft float32, absoluteTop float32) { + if pointScaleFactor == 0.0 { + return + } + + nodeLeft := node.Layout.Position[EdgeLeft] + nodeTop := node.Layout.Position[EdgeTop] + + nodeWidth := node.Layout.Dimensions[DimensionWidth] + nodeHeight := node.Layout.Dimensions[DimensionHeight] + + absoluteNodeLeft := absoluteLeft + nodeLeft + absoluteNodeTop := absoluteTop + nodeTop + + absoluteNodeRight := absoluteNodeLeft + nodeWidth + absoluteNodeBottom := absoluteNodeTop + nodeHeight + + // If a node has a custom measure function we never want to round down its size as this could + // lead to unwanted text truncation. + textRounding := node.NodeType == NodeTypeText + + node.Layout.Position[EdgeLeft] = roundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding) + node.Layout.Position[EdgeTop] = roundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding) + + // We multiply dimension by scale factor and if the result is close to the whole number, we don't have any fraction + // To verify if the result is close to whole number we want to check both floor and ceil numbers + hasFractionalWidth := !FloatsEqual(fmodf(nodeWidth*pointScaleFactor, 1), 0) && + !FloatsEqual(fmodf(nodeWidth*pointScaleFactor, 1), 1) + hasFractionalHeight := !FloatsEqual(fmodf(nodeHeight*pointScaleFactor, 1), 0) && + !FloatsEqual(fmodf(nodeHeight*pointScaleFactor, 1), 1) + + node.Layout.Dimensions[DimensionWidth] = + roundValueToPixelGrid( + absoluteNodeRight, + pointScaleFactor, + (textRounding && hasFractionalWidth), + (textRounding && !hasFractionalWidth)) - + roundValueToPixelGrid(absoluteNodeLeft, pointScaleFactor, false, textRounding) + node.Layout.Dimensions[DimensionHeight] = + roundValueToPixelGrid( + absoluteNodeBottom, + pointScaleFactor, + (textRounding && hasFractionalHeight), + (textRounding && !hasFractionalHeight)) - + roundValueToPixelGrid(absoluteNodeTop, pointScaleFactor, false, textRounding) + + for _, child := range node.Children { + roundToPixelGrid(child, pointScaleFactor, absoluteNodeLeft, absoluteNodeTop) + } +} + +func calcStartWidth(node *Node, parentWidth float32) (float32, MeasureMode) { + if nodeIsStyleDimDefined(node, FlexDirectionRow, parentWidth) { + width := resolveValue(node.resolvedDimensions[dim[FlexDirectionRow]], parentWidth) + margin := nodeMarginForAxis(node, FlexDirectionRow, parentWidth) + return width + margin, MeasureModeExactly + } + if resolveValue(&node.Style.MaxDimensions[DimensionWidth], parentWidth) >= 0.0 { + width := resolveValue(&node.Style.MaxDimensions[DimensionWidth], parentWidth) + return width, MeasureModeAtMost + } + + width := parentWidth + widthMeasureMode := MeasureModeExactly + if FloatIsUndefined(width) { + widthMeasureMode = MeasureModeUndefined + } + return width, widthMeasureMode +} +func calcStartHeight(node *Node, parentWidth, parentHeight float32) (float32, MeasureMode) { + if nodeIsStyleDimDefined(node, FlexDirectionColumn, parentHeight) { + height := resolveValue(node.resolvedDimensions[dim[FlexDirectionColumn]], parentHeight) + margin := nodeMarginForAxis(node, FlexDirectionColumn, parentWidth) + return height + margin, MeasureModeExactly + } + if resolveValue(&node.Style.MaxDimensions[DimensionHeight], parentHeight) >= 0 { + height := resolveValue(&node.Style.MaxDimensions[DimensionHeight], parentHeight) + return height, MeasureModeAtMost + } + height := parentHeight + heightMeasureMode := MeasureModeExactly + if FloatIsUndefined(height) { + heightMeasureMode = MeasureModeUndefined + } + return height, heightMeasureMode +} + +// CalculateLayout calculates layout +func CalculateLayout(node *Node, parentWidth float32, parentHeight float32, parentDirection Direction) { + // Increment the generation count. This will force the recursive routine to + // visit + // all dirty nodes at least once. Subsequent visits will be skipped if the + // input + // parameters don't change. + currentGenerationCount++ + + resolveDimensions(node) + + width, widthMeasureMode := calcStartWidth(node, parentWidth) + height, heightMeasureMode := calcStartHeight(node, parentWidth, parentHeight) + + if layoutNodeInternal(node, width, height, parentDirection, + widthMeasureMode, heightMeasureMode, parentWidth, parentHeight, + true, "initial", node.Config) { + nodeSetPosition(node, node.Layout.Direction, parentWidth, parentHeight, parentWidth) + roundToPixelGrid(node, node.Config.PointScaleFactor, 0, 0) + + if gPrintTree { + NodePrint(node, PrintOptionsLayout|PrintOptionsChildren|PrintOptionsStyle) + } + } +} + +// SetExperimentalFeatureEnabled enables experimental feature +func (config *Config) SetExperimentalFeatureEnabled(feature ExperimentalFeature, enabled bool) { + config.experimentalFeatures[feature] = enabled +} + +// IsExperimentalFeatureEnabled returns if experimental feature is enabled +func (config *Config) IsExperimentalFeatureEnabled(feature ExperimentalFeature) bool { + return config.experimentalFeatures[feature] +} + +func log(node *Node, level LogLevel, format string, args ...interface{}) { + fmt.Printf(format, args...) +} + +func assertCond(cond bool, format string, args ...interface{}) { + if !cond { + panic(format) + } +} + +func assertWithNode(node *Node, cond bool, format string, args ...interface{}) { + assertCond(cond, format, args...) +} + +func assertWithConfig(config *Config, condition bool, message string) { + if !condition { + panic(message) + } +} diff --git a/vendor/github.com/mitchellh/go-glint/flex/yoga_h.go b/vendor/github.com/mitchellh/go-glint/flex/yoga_h.go new file mode 100644 index 000000000000..53d277dc6229 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/yoga_h.go @@ -0,0 +1,37 @@ +package flex + +var ( + // Undefined defines undefined value + Undefined = NAN +) + +// Size describes size +type Size struct { + Width float32 + Height float32 +} + +// Value describes value +type Value struct { + Value float32 + Unit Unit +} + +var ( + // ValueUndefined defines undefined YGValue + ValueUndefined = Value{Undefined, UnitUndefined} + // ValueAuto defines auto YGValue + ValueAuto = Value{Undefined, UnitAuto} +) + +// MeasureFunc describes function for measuring +type MeasureFunc func(node *Node, width float32, widthMode MeasureMode, height float32, heightMode MeasureMode) Size + +// BaselineFunc describes function for baseline +type BaselineFunc func(node *Node, width float32, height float32) float32 + +// PrintFunc defines function for printing +type PrintFunc func(node *Node) + +// Logger defines logging function +type Logger func(config *Config, node *Node, level LogLevel, format string, args ...interface{}) int diff --git a/vendor/github.com/mitchellh/go-glint/flex/yoga_props.go b/vendor/github.com/mitchellh/go-glint/flex/yoga_props.go new file mode 100644 index 000000000000..45b53c8cecfd --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/flex/yoga_props.go @@ -0,0 +1,615 @@ +package flex + +/* + +Functions that get/set props, in C code generated from those C macros: + +YG_NODE_PROPERTY_IMPL(void *, Context, context, context); +YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print); +YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout); +YG_NODE_PROPERTY_IMPL(YGNodeType, NodeType, nodeType, nodeType); + +YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction); +YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection); +YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf); +YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType); +YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap); +YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow); +YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display); + +YG_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex); +YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexGrow, flexGrow, flexGrow); +YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexShrink, flexShrink, flexShrink); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, FlexBasis, flexBasis, flexBasis); + +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding); +YG_NODE_STYLE_EDGE_PROPERTY_IMPL(float, Border, border, border); + +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]); + +// Yoga specific properties, not compatible with flexbox specification +YG_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio); + +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]); +YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction); +YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow); + +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin); +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border); +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding); +*/ + +// StyleSetWidth sets width +func (node *Node) StyleSetWidth(width float32) { + dim := &node.Style.Dimensions[DimensionWidth] + if dim.Value != width || dim.Unit != UnitPoint { + dim.Value = width + dim.Unit = UnitPoint + if FloatIsUndefined(width) { + dim.Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetWidthPercent sets width percent +func (node *Node) StyleSetWidthPercent(width float32) { + dim := &node.Style.Dimensions[DimensionWidth] + if dim.Value != width || dim.Unit != UnitPercent { + dim.Value = width + dim.Unit = UnitPercent + if FloatIsUndefined(width) { + dim.Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetWidthAuto sets width auto +func (node *Node) StyleSetWidthAuto() { + dim := &node.Style.Dimensions[DimensionWidth] + if dim.Unit != UnitAuto { + dim.Value = Undefined + dim.Unit = UnitAuto + nodeMarkDirtyInternal(node) + } +} + +// StyleGetWidth gets width +func (node *Node) StyleGetWidth() Value { + return node.Style.Dimensions[DimensionWidth] +} + +// StyleSetHeight sets height +func (node *Node) StyleSetHeight(height float32) { + dim := &node.Style.Dimensions[DimensionHeight] + if dim.Value != height || dim.Unit != UnitPoint { + dim.Value = height + dim.Unit = UnitPoint + if FloatIsUndefined(height) { + dim.Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetHeightPercent sets height percent +func (node *Node) StyleSetHeightPercent(height float32) { + dim := &node.Style.Dimensions[DimensionHeight] + if dim.Value != height || dim.Unit != UnitPercent { + dim.Value = height + dim.Unit = UnitPercent + if FloatIsUndefined(height) { + dim.Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetHeightAuto sets height auto +func (node *Node) StyleSetHeightAuto() { + dim := &node.Style.Dimensions[DimensionHeight] + if dim.Unit != UnitAuto { + dim.Value = Undefined + dim.Unit = UnitAuto + nodeMarkDirtyInternal(node) + } +} + +// StyleGetHeight gets height +func (node *Node) StyleGetHeight() Value { + return node.Style.Dimensions[DimensionHeight] +} + +// StyleSetPositionType sets position type +func (node *Node) StyleSetPositionType(positionType PositionType) { + if node.Style.PositionType != positionType { + node.Style.PositionType = positionType + nodeMarkDirtyInternal(node) + } +} + +// StyleSetPosition sets position +func (node *Node) StyleSetPosition(edge Edge, position float32) { + pos := &node.Style.Position[edge] + if pos.Value != position || pos.Unit != UnitPoint { + pos.Value = position + pos.Unit = UnitPoint + if FloatIsUndefined(position) { + pos.Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetPositionPercent sets position percent +func (node *Node) StyleSetPositionPercent(edge Edge, position float32) { + pos := &node.Style.Position[edge] + if pos.Value != position || pos.Unit != UnitPercent { + pos.Value = position + pos.Unit = UnitPercent + if FloatIsUndefined(position) { + pos.Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetPosition gets position +func (node *Node) StyleGetPosition(edge Edge) Value { + return node.Style.Position[edge] +} + +// StyleSetDirection sets direction +func (node *Node) StyleSetDirection(direction Direction) { + if node.Style.Direction != direction { + node.Style.Direction = direction + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlexDirection sets flex directions +func (node *Node) StyleSetFlexDirection(flexDirection FlexDirection) { + if node.Style.FlexDirection != flexDirection { + node.Style.FlexDirection = flexDirection + nodeMarkDirtyInternal(node) + } +} + +// StyleSetJustifyContent sets justify content +func (node *Node) StyleSetJustifyContent(justifyContent Justify) { + if node.Style.JustifyContent != justifyContent { + node.Style.JustifyContent = justifyContent + nodeMarkDirtyInternal(node) + } +} + +// StyleSetAlignContent sets align content +func (node *Node) StyleSetAlignContent(alignContent Align) { + if node.Style.AlignContent != alignContent { + node.Style.AlignContent = alignContent + nodeMarkDirtyInternal(node) + } +} + +// StyleSetAlignItems sets align content +func (node *Node) StyleSetAlignItems(alignItems Align) { + if node.Style.AlignItems != alignItems { + node.Style.AlignItems = alignItems + nodeMarkDirtyInternal(node) + } +} + +// StyleSetAlignSelf sets align self +func (node *Node) StyleSetAlignSelf(alignSelf Align) { + if node.Style.AlignSelf != alignSelf { + node.Style.AlignSelf = alignSelf + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlexWrap sets flex wrap +func (node *Node) StyleSetFlexWrap(flexWrap Wrap) { + if node.Style.FlexWrap != flexWrap { + node.Style.FlexWrap = flexWrap + nodeMarkDirtyInternal(node) + } +} + +// StyleSetOverflow sets overflow +func (node *Node) StyleSetOverflow(overflow Overflow) { + if node.Style.Overflow != overflow { + node.Style.Overflow = overflow + nodeMarkDirtyInternal(node) + } +} + +// StyleSetDisplay sets display +func (node *Node) StyleSetDisplay(display Display) { + if node.Style.Display != display { + node.Style.Display = display + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlex sets flex +func (node *Node) StyleSetFlex(flex float32) { + if node.Style.Flex != flex { + node.Style.Flex = flex + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlexGrow sets flex grow +func (node *Node) StyleSetFlexGrow(flexGrow float32) { + if node.Style.FlexGrow != flexGrow { + node.Style.FlexGrow = flexGrow + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlexShrink sets flex shrink +func (node *Node) StyleSetFlexShrink(flexShrink float32) { + if node.Style.FlexShrink != flexShrink { + node.Style.FlexShrink = flexShrink + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlexBasis sets flex basis +func (node *Node) StyleSetFlexBasis(flexBasis float32) { + if node.Style.FlexBasis.Value != flexBasis || + node.Style.FlexBasis.Unit != UnitPoint { + node.Style.FlexBasis.Value = flexBasis + node.Style.FlexBasis.Unit = UnitPoint + if FloatIsUndefined(flexBasis) { + node.Style.FlexBasis.Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetFlexBasisPercent sets flex basis percent +func (node *Node) StyleSetFlexBasisPercent(flexBasis float32) { + if node.Style.FlexBasis.Value != flexBasis || + node.Style.FlexBasis.Unit != UnitPercent { + node.Style.FlexBasis.Value = flexBasis + node.Style.FlexBasis.Unit = UnitPercent + if FloatIsUndefined(flexBasis) { + node.Style.FlexBasis.Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// NodeStyleSetFlexBasisAuto sets flex basis auto +func NodeStyleSetFlexBasisAuto(node *Node) { + if node.Style.FlexBasis.Unit != UnitAuto { + node.Style.FlexBasis.Value = Undefined + node.Style.FlexBasis.Unit = UnitAuto + nodeMarkDirtyInternal(node) + } +} + +// StyleSetMargin sets margin +func (node *Node) StyleSetMargin(edge Edge, margin float32) { + if node.Style.Margin[edge].Value != margin || + node.Style.Margin[edge].Unit != UnitPoint { + node.Style.Margin[edge].Value = margin + node.Style.Margin[edge].Unit = UnitPoint + if FloatIsUndefined(margin) { + node.Style.Margin[edge].Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetMarginPercent sets margin percent +func (node *Node) StyleSetMarginPercent(edge Edge, margin float32) { + if node.Style.Margin[edge].Value != margin || + node.Style.Margin[edge].Unit != UnitPercent { + node.Style.Margin[edge].Value = margin + node.Style.Margin[edge].Unit = UnitPercent + if FloatIsUndefined(margin) { + node.Style.Margin[edge].Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetMargin gets margin +func (node *Node) StyleGetMargin(edge Edge) Value { + return node.Style.Margin[edge] +} + +// StyleSetMarginAuto sets margin auto +func (node *Node) StyleSetMarginAuto(edge Edge) { + if node.Style.Margin[edge].Unit != UnitAuto { + node.Style.Margin[edge].Value = Undefined + node.Style.Margin[edge].Unit = UnitAuto + nodeMarkDirtyInternal(node) + } +} + +// StyleSetPadding sets padding +func (node *Node) StyleSetPadding(edge Edge, padding float32) { + if node.Style.Padding[edge].Value != padding || + node.Style.Padding[edge].Unit != UnitPoint { + node.Style.Padding[edge].Value = padding + node.Style.Padding[edge].Unit = UnitPoint + if FloatIsUndefined(padding) { + node.Style.Padding[edge].Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetPaddingPercent sets padding percent +func (node *Node) StyleSetPaddingPercent(edge Edge, padding float32) { + if node.Style.Padding[edge].Value != padding || + node.Style.Padding[edge].Unit != UnitPercent { + node.Style.Padding[edge].Value = padding + node.Style.Padding[edge].Unit = UnitPercent + if FloatIsUndefined(padding) { + node.Style.Padding[edge].Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetPadding gets padding +func (node *Node) StyleGetPadding(edge Edge) Value { + return node.Style.Padding[edge] +} + +// StyleSetBorder sets border +func (node *Node) StyleSetBorder(edge Edge, border float32) { + if node.Style.Border[edge].Value != border || + node.Style.Border[edge].Unit != UnitPoint { + node.Style.Border[edge].Value = border + node.Style.Border[edge].Unit = UnitPoint + if FloatIsUndefined(border) { + node.Style.Border[edge].Unit = UnitUndefined + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetBorder gets border +func (node *Node) StyleGetBorder(edge Edge) float32 { + return node.Style.Border[edge].Value +} + +// StyleSetMinWidth sets min width +func (node *Node) StyleSetMinWidth(minWidth float32) { + if node.Style.MinDimensions[DimensionWidth].Value != minWidth || + node.Style.MinDimensions[DimensionWidth].Unit != UnitPoint { + node.Style.MinDimensions[DimensionWidth].Value = minWidth + node.Style.MinDimensions[DimensionWidth].Unit = UnitPoint + if FloatIsUndefined(minWidth) { + node.Style.MinDimensions[DimensionWidth].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetMinWidthPercent sets width percent +func (node *Node) StyleSetMinWidthPercent(minWidth float32) { + if node.Style.MinDimensions[DimensionWidth].Value != minWidth || + node.Style.MinDimensions[DimensionWidth].Unit != UnitPercent { + node.Style.MinDimensions[DimensionWidth].Value = minWidth + node.Style.MinDimensions[DimensionWidth].Unit = UnitPercent + if FloatIsUndefined(minWidth) { + node.Style.MinDimensions[DimensionWidth].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetMinWidth gets min width +func (node *Node) StyleGetMinWidth() Value { + return node.Style.MinDimensions[DimensionWidth] +} + +// StyleSetMinHeight sets min width +func (node *Node) StyleSetMinHeight(minHeight float32) { + if node.Style.MinDimensions[DimensionHeight].Value != minHeight || + node.Style.MinDimensions[DimensionHeight].Unit != UnitPoint { + node.Style.MinDimensions[DimensionHeight].Value = minHeight + node.Style.MinDimensions[DimensionHeight].Unit = UnitPoint + if FloatIsUndefined(minHeight) { + node.Style.MinDimensions[DimensionHeight].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetMinHeightPercent sets min height percent +func (node *Node) StyleSetMinHeightPercent(minHeight float32) { + if node.Style.MinDimensions[DimensionHeight].Value != minHeight || + node.Style.MinDimensions[DimensionHeight].Unit != UnitPercent { + node.Style.MinDimensions[DimensionHeight].Value = minHeight + node.Style.MinDimensions[DimensionHeight].Unit = UnitPercent + if FloatIsUndefined(minHeight) { + node.Style.MinDimensions[DimensionHeight].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetMinHeight gets min height +func (node *Node) StyleGetMinHeight() Value { + return node.Style.MinDimensions[DimensionHeight] +} + +// StyleSetMaxWidth sets max width +func (node *Node) StyleSetMaxWidth(maxWidth float32) { + if node.Style.MaxDimensions[DimensionWidth].Value != maxWidth || + node.Style.MaxDimensions[DimensionWidth].Unit != UnitPoint { + node.Style.MaxDimensions[DimensionWidth].Value = maxWidth + node.Style.MaxDimensions[DimensionWidth].Unit = UnitPoint + if FloatIsUndefined(maxWidth) { + node.Style.MaxDimensions[DimensionWidth].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetMaxWidthPercent sets max width percent +func (node *Node) StyleSetMaxWidthPercent(maxWidth float32) { + if node.Style.MaxDimensions[DimensionWidth].Value != maxWidth || + node.Style.MaxDimensions[DimensionWidth].Unit != UnitPercent { + node.Style.MaxDimensions[DimensionWidth].Value = maxWidth + node.Style.MaxDimensions[DimensionWidth].Unit = UnitPercent + if FloatIsUndefined(maxWidth) { + node.Style.MaxDimensions[DimensionWidth].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetMaxWidth gets max width +func (node *Node) StyleGetMaxWidth() Value { + return node.Style.MaxDimensions[DimensionWidth] +} + +// StyleSetMaxHeight sets max width +func (node *Node) StyleSetMaxHeight(maxHeight float32) { + if node.Style.MaxDimensions[DimensionHeight].Value != maxHeight || + node.Style.MaxDimensions[DimensionHeight].Unit != UnitPoint { + node.Style.MaxDimensions[DimensionHeight].Value = maxHeight + node.Style.MaxDimensions[DimensionHeight].Unit = UnitPoint + if FloatIsUndefined(maxHeight) { + node.Style.MaxDimensions[DimensionHeight].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleSetMaxHeightPercent sets max height percent +func (node *Node) StyleSetMaxHeightPercent(maxHeight float32) { + if node.Style.MaxDimensions[DimensionHeight].Value != maxHeight || + node.Style.MaxDimensions[DimensionHeight].Unit != UnitPercent { + node.Style.MaxDimensions[DimensionHeight].Value = maxHeight + node.Style.MaxDimensions[DimensionHeight].Unit = UnitPercent + if FloatIsUndefined(maxHeight) { + node.Style.MaxDimensions[DimensionHeight].Unit = UnitAuto + } + nodeMarkDirtyInternal(node) + } +} + +// StyleGetMaxHeight gets max height +func (node *Node) StyleGetMaxHeight() Value { + return node.Style.MaxDimensions[DimensionHeight] +} + +// StyleSetAspectRatio sets axpect ratio +func (node *Node) StyleSetAspectRatio(aspectRatio float32) { + if node.Style.AspectRatio != aspectRatio { + node.Style.AspectRatio = aspectRatio + nodeMarkDirtyInternal(node) + } +} + +// LayoutGetLeft gets left +func (node *Node) LayoutGetLeft() float32 { + return node.Layout.Position[EdgeLeft] +} + +// LayoutGetTop gets top +func (node *Node) LayoutGetTop() float32 { + return node.Layout.Position[EdgeTop] +} + +// LayoutGetRight gets right +func (node *Node) LayoutGetRight() float32 { + return node.Layout.Position[EdgeRight] +} + +// LayoutGetBottom gets bottom +func (node *Node) LayoutGetBottom() float32 { + return node.Layout.Position[EdgeBottom] +} + +// LayoutGetWidth gets width +func (node *Node) LayoutGetWidth() float32 { + return node.Layout.Dimensions[DimensionWidth] +} + +// LayoutGetHeight gets height +func (node *Node) LayoutGetHeight() float32 { + return node.Layout.Dimensions[DimensionHeight] +} + +// LayoutGetMargin gets margin +func (node *Node) LayoutGetMargin(edge Edge) float32 { + assertWithNode(node, edge < EdgeEnd, "Cannot get layout properties of multi-edge shorthands") + if edge == EdgeLeft { + if node.Layout.Direction == DirectionRTL { + return node.Layout.Margin[EdgeEnd] + } + return node.Layout.Margin[EdgeStart] + } + if edge == EdgeRight { + if node.Layout.Direction == DirectionRTL { + return node.Layout.Margin[EdgeStart] + } + return node.Layout.Margin[EdgeEnd] + } + return node.Layout.Margin[edge] +} + +// LayoutGetBorder gets border +func (node *Node) LayoutGetBorder(edge Edge) float32 { + assertWithNode(node, edge < EdgeEnd, + "Cannot get layout properties of multi-edge shorthands") + if edge == EdgeLeft { + if node.Layout.Direction == DirectionRTL { + return node.Layout.Border[EdgeEnd] + } + return node.Layout.Border[EdgeStart] + } + if edge == EdgeRight { + if node.Layout.Direction == DirectionRTL { + return node.Layout.Border[EdgeStart] + } + return node.Layout.Border[EdgeEnd] + } + return node.Layout.Border[edge] +} + +// LayoutGetPadding gets padding +func (node *Node) LayoutGetPadding(edge Edge) float32 { + assertWithNode(node, edge < EdgeEnd, + "Cannot get layout properties of multi-edge shorthands") + if edge == EdgeLeft { + if node.Layout.Direction == DirectionRTL { + return node.Layout.Padding[EdgeEnd] + } + return node.Layout.Padding[EdgeStart] + } + if edge == EdgeRight { + if node.Layout.Direction == DirectionRTL { + return node.Layout.Padding[EdgeStart] + } + return node.Layout.Padding[EdgeEnd] + } + return node.Layout.Padding[edge] +} diff --git a/vendor/github.com/mitchellh/go-glint/fragment.go b/vendor/github.com/mitchellh/go-glint/fragment.go new file mode 100644 index 000000000000..dcdd17fb82a8 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/fragment.go @@ -0,0 +1,14 @@ +package glint + +// Fragment appends multiple components together. A fragment has no layout +// implications, it is as if the set of components were appended directly to +// the parent. +func Fragment(c ...Component) Component { + return &fragmentComponent{List: c} +} + +type fragmentComponent struct { + terminalComponent + + List []Component +} diff --git a/vendor/github.com/mitchellh/go-glint/go.mod b/vendor/github.com/mitchellh/go-glint/go.mod new file mode 100644 index 000000000000..595ea8641a89 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/go.mod @@ -0,0 +1,15 @@ +module github.com/mitchellh/go-glint + +go 1.14 + +require ( + github.com/cheggaaa/pb/v3 v3.0.5 + github.com/containerd/console v1.0.1 + github.com/gookit/color v1.3.1 + github.com/mitchellh/go-testing-interface v1.14.1 + github.com/mitchellh/go-wordwrap v1.0.1 + github.com/morikuni/aec v1.0.0 + github.com/stretchr/testify v1.6.1 + github.com/tj/go-spin v1.1.0 + golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a +) diff --git a/vendor/github.com/mitchellh/go-glint/go.sum b/vendor/github.com/mitchellh/go-glint/go.sum new file mode 100644 index 000000000000..13eb3b8ddef1 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/go.sum @@ -0,0 +1,51 @@ +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE= +github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw= +github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/gookit/color v1.3.1 h1:PPD/C7sf8u2L8XQPdPgsWRoAiLQGZEZOzU3cf5IYYUk= +github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f h1:6Sc1XOXTulBN6imkqo6XoAXDEzoQ4/ro6xy7Vn8+rOM= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/mitchellh/go-glint/internal/layout/builder.go b/vendor/github.com/mitchellh/go-glint/internal/layout/builder.go new file mode 100644 index 000000000000..53628b424a9c --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/internal/layout/builder.go @@ -0,0 +1,42 @@ +package layout + +import ( + "github.com/mitchellh/go-glint/flex" +) + +type SetFunc func(n *flex.Node) + +// Builder builds a set of styles to apply to a flex node. +type Builder struct { + f SetFunc +} + +// Raw composes a SetFunc on the builder. This will call the previous +// styles setters first and then call this function. +func (l *Builder) Raw(f SetFunc) *Builder { + return l.add(f) +} + +// Apply sets the styles on the flex node. +func (l *Builder) Apply(node *flex.Node) { + if l == nil || l.f == nil { + return + } + + l.f(node) +} + +// add is a helper to add the function to the call chain for this builder. +// This will return a new builder. +func (l *Builder) add(f func(*flex.Node)) *Builder { + old := l.f + new := func(n *flex.Node) { + if old != nil { + old(n) + } + + f(n) + } + + return &Builder{f: new} +} diff --git a/vendor/github.com/mitchellh/go-glint/layout.go b/vendor/github.com/mitchellh/go-glint/layout.go new file mode 100644 index 000000000000..0c8404e4da34 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/layout.go @@ -0,0 +1,72 @@ +package glint + +import ( + "context" + + "github.com/mitchellh/go-glint/flex" + "github.com/mitchellh/go-glint/internal/layout" +) + +// Layout is used to set layout properties for the child components. +// This can be used similarly to a "div" in HTML with "display: flex" set. +// This component follows the builder pattern for setting layout properties +// such as margins, paddings, etc. +func Layout(inner ...Component) *LayoutComponent { + return &LayoutComponent{inner: inner, builder: &layout.Builder{}} +} + +// LayoutComponent is a component used for layout settings. See Layout. +type LayoutComponent struct { + inner []Component + builder *layout.Builder +} + +// Row sets the `flex-direction: row` property. +func (c *LayoutComponent) Row() *LayoutComponent { + c.builder = c.builder.Raw(func(n *flex.Node) { + n.StyleSetFlexDirection(flex.FlexDirectionRow) + }) + return c +} + +// MarginLeft sets the `margin-left` property. +func (c *LayoutComponent) MarginLeft(x int) *LayoutComponent { + c.builder = c.builder.Raw(func(n *flex.Node) { + n.StyleSetMargin(flex.EdgeLeft, float32(x)) + }) + return c +} + +// MarginRight sets the `margin-left` property. +func (c *LayoutComponent) MarginRight(x int) *LayoutComponent { + c.builder = c.builder.Raw(func(n *flex.Node) { + n.StyleSetMargin(flex.EdgeRight, float32(x)) + }) + return c +} + +// PaddingLeft sets the `margin-left` property. +func (c *LayoutComponent) PaddingLeft(x int) *LayoutComponent { + c.builder = c.builder.Raw(func(n *flex.Node) { + n.StyleSetPadding(flex.EdgeLeft, float32(x)) + }) + return c +} + +// PaddingRight sets the `margin-left` property. +func (c *LayoutComponent) PaddingRight(x int) *LayoutComponent { + c.builder = c.builder.Raw(func(n *flex.Node) { + n.StyleSetPadding(flex.EdgeRight, float32(x)) + }) + return c +} + +// Component implementation +func (c *LayoutComponent) Body(context.Context) Component { + return Fragment(c.inner...) +} + +// componentLayout internal implementation. +func (c *LayoutComponent) Layout() *layout.Builder { + return c.builder +} diff --git a/vendor/github.com/mitchellh/go-glint/measure.go b/vendor/github.com/mitchellh/go-glint/measure.go new file mode 100644 index 000000000000..913a08f0d979 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/measure.go @@ -0,0 +1,199 @@ +package glint + +import ( + "context" + "math" + "strings" + "unicode/utf8" + + "github.com/mitchellh/go-glint/flex" + "github.com/mitchellh/go-wordwrap" +) + +// TextNodeContext is the *flex.Node.Context set for all *TextComponent flex nodes. +type TextNodeContext struct { + // C is the TextComponent represented. + C *TextComponent + + // The context at the time layout was done + Context context.Context + + // Text is the rendered text. This is populated after MeasureTextNode + // is called. Note that this may not fit in the final layout calculations + // since it is populated on measurement. + Text string + + // Size is the measurement size returned. This can be used to determine + // if the text above fits in the final size. Text is guaranteed to fit + // in this size. + Size flex.Size +} + +func (c *TextNodeContext) Component() Component { return c.C } + +// MeasureTextNode implements flex.MeasureFunc and returns the measurements +// for the given node only if the node represents a TextComponent. This is +// the MeasureFunc that is typically used for renderers since all component +// trees terminate in a text node. +// +// The flex.Node must have Context set to TextNodeContext. After calling this, +// fields such as Text and Size will be populated on the node. +func MeasureTextNode( + node *flex.Node, + width float32, + widthMode flex.MeasureMode, + height float32, + heightMode flex.MeasureMode, +) flex.Size { + // If we have no context set then we use the full spacing. + ctx, ok := node.Context.(*TextNodeContext) + if !ok || ctx == nil { + return flex.Size{Width: width, Height: height} + } + + // Otherwise, we have to render this. + ctx.Text = ctx.C.Render(uint(height), uint(width)) + + // Word wrap and truncate if we're beyond the width limit. + if !math.IsNaN(float64(width)) && width > 0 { + ctx.Text = clampTextWidth( + wordwrap.WrapString(ctx.Text, uint(width)), + int(width)) + } + + // Truncate height if we have a limit. This is a no-op if it fits. + if !math.IsNaN(float64(height)) && height > 0 { + ctx.Text = truncateTextHeight(ctx.Text, int(height)) + } + + // Calculate the size + ctx.Size = flex.Size{ + Width: float32(longestLine(ctx.Text)), + Height: float32(countLines(ctx.Text)), + } + + // We special case the empty-text case, since this is a height of + // one and width of zero. If the user wanted no rendering at all they + // should render nil. + if ctx.Text == "" { + ctx.Size.Height = 1 + } + + return ctx.Size +} + +func countLines(s string) int { + count := strings.Count(s, "\n") + + // If the last character isn't a newline, we have to add one since we'll + // always have one more line than newline characters. + if len(s) > 0 && s[len(s)-1] != '\n' { + count++ + } + return count +} + +func longestLine(s string) int { + longest := 0 + for { + idx := strings.IndexByte(s, '\n') + if idx == -1 { + break + } + + current := utf8.RuneCountInString(s[:idx]) + if current > longest { + longest = current + } + + s = s[idx+1:] + } + + if longest == 0 { + return utf8.RuneCountInString(s) + } + + return longest +} + +func truncateTextHeight(s string, height int) string { + // The way this works is that we iterate through HEIGHT newlines + // and return up to that point. If we either don't find a newline + // or we've reached the end of the string, then the string is shorter + // than the height limit and we return the whole thing. + idx := 0 + for i := 0; i < height; i++ { + next := strings.IndexByte(s[idx:], '\n') + if next == -1 || idx >= len(s) { + return s + } + + idx += next + 1 + } + + // This can happen if height == 0 + if idx == 0 { + return "" + } + + // Subtract one here because the idx is the last "\n" char + return s[:idx-1] +} + +// clampTextWidth cuts off any lines in s that are longer than width +// characters (not including the newline). +func clampTextWidth(s string, width int) string { + // If our width is zero just return empty + if width == 0 { + return "" + } + + // NOTE(mitchellh): This loop is really horrible. It is unclear, weirdly + // repetitive, and just aesthetically gross. But the tests pass and we have + // good test cases on this. Let's fix this later. + var b *strings.Builder + total := 0 + original := s + for { + end := false + idx := strings.IndexByte(s, '\n') + if idx == -1 { + idx = len(s) + end = true + } + + runeCount := utf8.RuneCountInString(s[:idx]) + if runeCount > width { + if b == nil { + b = &strings.Builder{} + if total > 0 { + b.WriteString(original[:total]) + b.WriteByte('\n') + } + } + + runes := []rune(s) + b.WriteString(string(runes[:width])) + if !end { + b.WriteByte('\n') + } + } else if idx > 0 { + if b != nil { + b.WriteString(s[:idx]) + } + } + + if end { + break + } + + total += idx + s = s[idx+1:] + } + + if b == nil { + return original + } + + return b.String() +} diff --git a/vendor/github.com/mitchellh/go-glint/renderer.go b/vendor/github.com/mitchellh/go-glint/renderer.go new file mode 100644 index 000000000000..8946af90a246 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/renderer.go @@ -0,0 +1,54 @@ +package glint + +import ( + "context" + + "github.com/mitchellh/go-glint/flex" +) + +// Renderers are responsible for helping configure layout properties and +// ultimately drawing components. +// +// Renderers may also optionally implement io.Closer. If a renderer implements +// io.Closer, the Close method will be called. After this is called. the +// Render methods will no longer be called again. This can be used to perform +// final cleanups. +type Renderer interface { + // LayoutRoot returns the root node for the layout engine. This should + // set any styling to restrict children such as width. If this returns nil + // then rendering will do nothing. + LayoutRoot() *flex.Node + + // RenderRoot is called to render the tree rooted at the given node. + // This will always be called with the root node. In the future we plan + // to support partial re-renders but this will be done via a separate call. + // + // The height of root is always greater than zero. RenderRoot won't be + // called if the root has a zero height since this implies that nothing + // has to be drawn. + // + // prev will be the previous root that was rendered. This can be used to + // determine layout differences. This will be nil if this is the first + // render. If the height of the previous node is zero then that means that + // everything drawn was finalized. + RenderRoot(root, prev *flex.Node) +} + +// WithRenderer inserts the renderer into the context. This is done automatically +// by Document for components. +func WithRenderer(ctx context.Context, r Renderer) context.Context { + return context.WithValue(ctx, rendererCtxKey, r) +} + +// RendererFromContext returns the Renderer in the context or nil if no +// Renderer is found. +func RendererFromContext(ctx context.Context) Renderer { + v, _ := ctx.Value(rendererCtxKey).(Renderer) + return v +} + +type glintCtxKey string + +const ( + rendererCtxKey = glintCtxKey("renderer") +) diff --git a/vendor/github.com/mitchellh/go-glint/renderer_string.go b/vendor/github.com/mitchellh/go-glint/renderer_string.go new file mode 100644 index 000000000000..03f84a78718b --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/renderer_string.go @@ -0,0 +1,103 @@ +package glint + +import ( + "bytes" + "fmt" + "io" + "strings" + + "github.com/mitchellh/go-glint/flex" +) + +// StringRenderer renders output to a string builder. This will clear +// the builder on each frame render. The StringRenderer is primarily meant +// for testing components. +type StringRenderer struct { + // Builder is the strings builder to write to. If this is nil then + // it will be created on first render. + Builder *strings.Builder + + // Width is a fixed width to set for the root node. If this isn't + // set then a width of 80 is arbitrarily used. + Width uint +} + +func (r *StringRenderer) LayoutRoot() *flex.Node { + width := r.Width + if width == 0 { + width = 80 + } + + node := flex.NewNode() + node.StyleSetWidth(float32(width)) + return node +} + +func (r *StringRenderer) RenderRoot(root, prev *flex.Node) { + if r.Builder == nil { + r.Builder = &strings.Builder{} + } + + // Reset our builder + r.Builder.Reset() + + // Draw + r.renderTree(r.Builder, root, -1, false) +} + +func (r *StringRenderer) renderTree(final io.Writer, parent *flex.Node, lastRow int, color bool) { + var buf bytes.Buffer + for _, child := range parent.Children { + // Ignore children with a zero height + if child.LayoutGetHeight() == 0 { + continue + } + + // If we're on a different row than last time then we draw a newline. + thisRow := int(child.LayoutGetTop()) + if lastRow >= 0 && thisRow > lastRow { + buf.WriteByte('\n') + } + lastRow = thisRow + + // Get our node context. If we don't have one then we're a container + // and we render below. + ctx, ok := child.Context.(*TextNodeContext) + if !ok { + r.renderTree(&buf, child, lastRow, color) + } else { + text := ctx.Text + if color { + text = styleRender(ctx.Context, text) + } + + // Draw our text + fmt.Fprint(&buf, text) + } + } + + // We've finished drawing our main content. If we have any paddings/margins + // we have to draw these now into our buffer. + leftMargin := int(parent.LayoutGetMargin(flex.EdgeLeft)) + rightMargin := int(parent.LayoutGetMargin(flex.EdgeRight)) + leftPadding := int(parent.LayoutGetPadding(flex.EdgeLeft)) + rightPadding := int(parent.LayoutGetPadding(flex.EdgeRight)) + + // NOTE(mitchellh): this is not an optimal way to do this. This was a + // get-it-done-fast implementation. We should swing back around at some + // point and rewrite this with less allocations and copying. + lines := bytes.Split(buf.Bytes(), newline) + for i, line := range lines { + final.Write(bytes.Repeat(space, leftMargin+leftPadding)) + final.Write(line) + final.Write(bytes.Repeat(space, rightMargin+rightPadding)) + if i < len(lines)-1 { + final.Write(newline) + } + } +} + +var ( + space = []byte(" ") + newline = []byte("\n") +) diff --git a/vendor/github.com/mitchellh/go-glint/renderer_term.go b/vendor/github.com/mitchellh/go-glint/renderer_term.go new file mode 100644 index 000000000000..a62b3e36a04e --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/renderer_term.go @@ -0,0 +1,126 @@ +package glint + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/containerd/console" + "github.com/gookit/color" + "github.com/morikuni/aec" + sshterm "golang.org/x/crypto/ssh/terminal" + + "github.com/mitchellh/go-glint/flex" +) + +// TerminalRenderer renders output to a terminal. It expects the Output set +// to be a TTY. This will use ANSI escape codes to redraw. +type TerminalRenderer struct { + // Output is where to write to. This should be a TTY. + Output io.Writer + + // Rows, Cols are the dimensions of the terminal. If these are not set + // (zero), then we will auto-detect the size of the output if it is a TTY. + // If the values are still zero, nothing will be rendered. + Rows, Cols uint +} + +func (r *TerminalRenderer) LayoutRoot() *flex.Node { + // If we don't have a writer set, then don't render anything. + if r.Output == nil { + return nil + } + + // Setup our dimensions + cols := r.Cols + rows := r.Rows + if cols == 0 || rows == 0 { + if f, ok := r.Output.(*os.File); ok && sshterm.IsTerminal(int(f.Fd())) { + if c, err := console.ConsoleFromFile(f); err == nil { + if sz, err := c.Size(); err == nil { + rows = uint(sz.Height) + cols = uint(sz.Width) + } + } + } + } + + // Render nothing if we're going to have any zero dimensions + if cols == 0 || rows == 0 { + return nil + } + + // Setup our node + node := flex.NewNode() + node.StyleSetWidth(float32(cols)) + node.Context = &termRootContext{ + Rows: rows, + Cols: cols, + } + + return node +} + +func (r *TerminalRenderer) RenderRoot(root, prev *flex.Node) { + w := r.Output + rootCtx := root.Context.(*termRootContext) + + // Draw into a buffer first. This minimizes the time we spend with + // a blank screen. + var buf bytes.Buffer + var sr StringRenderer + sr.renderTree(&buf, root, -1, color.IsSupportColor()) + rootCtx.Buf = &buf + + if prev != nil { + // If the previous draw was a terminal and the output was identical, + // then we do nothing. + prevCtx, ok := prev.Context.(*termRootContext) + if ok && + prevCtx != nil && + prevCtx.Buf != nil && + prevCtx.Rows == rootCtx.Rows && + prevCtx.Cols == rootCtx.Cols && + bytes.Equal(prevCtx.Buf.Bytes(), buf.Bytes()) { + return + } + + // Remove what we last drew. If what we last drew is greater than the number + // of rows then we need to clear the screen. + height := uint(prev.LayoutGetHeight()) + if height == 0 { + // If our previous render height is zero that means that everything + // was finalized and we need to start on a new line. + fmt.Fprintf(w, "\n") + } else { + if height <= rootCtx.Rows { + // Delete current line + fmt.Fprint(w, b.Column(0).EraseLine(aec.EraseModes.All).ANSI) + + // Delete n lines above + for i := uint(0); i < height-1; i++ { + fmt.Fprint(w, b.Up(1).Column(0).EraseLine(aec.EraseModes.All).ANSI) + } + } else { + fmt.Fprint(w, b.EraseDisplay(aec.EraseModes.All).EraseDisplay(aec.EraseMode(3)).Position(0, 0).ANSI) + } + } + } + + // Draw our current output. We wrap buf in a bytes.Reader so we don't + // consume the bytes since we'll reuse them in a future call. + io.Copy(w, bytes.NewReader(buf.Bytes())) +} + +func (r *TerminalRenderer) Close() error { + fmt.Fprintln(r.Output, "") + return nil +} + +type termRootContext struct { + Rows, Cols uint + Buf *bytes.Buffer +} + +var b = aec.EmptyBuilder diff --git a/vendor/github.com/mitchellh/go-glint/style.go b/vendor/github.com/mitchellh/go-glint/style.go new file mode 100644 index 000000000000..6618716c17d5 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/style.go @@ -0,0 +1,152 @@ +package glint + +import ( + "context" + + "github.com/gookit/color" +) + +// Style applies visual styles to this component and any children. This +// can be used to set a foreground color, for example, to a set of components. +func Style(inner Component, opts ...StyleOption) Component { + c := &styleComponent{inner: inner} + for _, opt := range opts { + opt(c) + } + + return c +} + +// styleRender is used internally to apply styles to the given string. The +// ctx should be the same context given when Body was called on this +// component. +func styleRender(ctx context.Context, v string) string { + value, _ := ctx.Value(styleCtxKey).([]*styleComponent) + for _, s := range value { + v = s.render(v) + } + + return v +} + +type styleComponent struct { + inner Component + fgColor, bgColor colorizer + style []color.Color +} + +func (c *styleComponent) Body(ctx context.Context) Component { + // Add our style to the list of styles. We have to use copy here + // so we don't append to a parent. + old, _ := ctx.Value(styleCtxKey).([]*styleComponent) + value := make([]*styleComponent, len(old), len(old)+1) + copy(value, old) + value = append(value, c) + return Context(c.inner, styleCtxKey, value) +} + +func (c *styleComponent) render(v string) string { + if c.bgColor != nil { + v = c.bgColor.Sprint(v) + } + if c.fgColor != nil { + v = c.fgColor.Sprint(v) + } + v = color.Style(c.style).Sprint(v) + + return v +} + +type styleCtxKeyType struct{} + +var styleCtxKey = styleCtxKeyType{} + +// StyleOption is an option that can be set when creating Text components. +type StyleOption func(t *styleComponent) + +// Color sets the color by name. The supported colors are listed below. +// +// black, red, green, yellow, blue, magenta, cyan, white, darkGray, +// lightRed, lightGreen, lightYellow, lightBlue, lightMagenta, lightCyan, +// lightWhite. +func Color(name string) StyleOption { + return func(t *styleComponent) { + if c, ok := color.FgColors[name]; ok { + t.fgColor = c + } + if c, ok := color.ExFgColors[name]; ok { + t.fgColor = c + } + } +} + +// ColorHex sets the foreground color by hex code. The value can be +// in formats AABBCC, #AABBCC, 0xAABBCC. +func ColorHex(v string) StyleOption { + return func(t *styleComponent) { + t.fgColor = color.HEX(v) + } +} + +// ColorRGB sets the foreground color by RGB values. +func ColorRGB(r, g, b uint8) StyleOption { + return func(t *styleComponent) { + t.fgColor = color.RGB(r, g, b) + } +} + +// BGColor sets the color by name. The supported colors are listed below. +// +// black, red, green, yellow, blue, magenta, cyan, white, darkGray, +// lightRed, lightGreen, lightYellow, lightBlue, lightMagenta, lightCyan, +// lightWhite. +func BGColor(name string) StyleOption { + return func(t *styleComponent) { + if c, ok := color.BgColors[name]; ok { + t.bgColor = c + } + if c, ok := color.ExBgColors[name]; ok { + t.bgColor = c + } + } +} + +// BGColorHex sets the foreground color by hex code. The value can be +// in formats AABBCC, #AABBCC, 0xAABBCC. +func BGColorHex(v string) StyleOption { + return func(t *styleComponent) { + t.bgColor = color.HEX(v, true) + } +} + +// BGColorRGB sets the foreground color by RGB values. +func BGColorRGB(r, g, b uint8) StyleOption { + return func(t *styleComponent) { + t.bgColor = color.RGB(r, g, b, true) + } +} + +// Bold sets the text to bold. +func Bold() StyleOption { + return func(t *styleComponent) { + t.style = append(t.style, color.OpBold) + } +} + +// Italic sets the text to italic. +func Italic() StyleOption { + return func(t *styleComponent) { + t.style = append(t.style, color.OpItalic) + } +} + +// Underline sets the text to be underlined. +func Underline() StyleOption { + return func(t *styleComponent) { + t.style = append(t.style, color.OpUnderscore) + } +} + +type colorizer interface { + Sprint(...interface{}) string +} diff --git a/vendor/github.com/mitchellh/go-glint/testing.go b/vendor/github.com/mitchellh/go-glint/testing.go new file mode 100644 index 000000000000..e8c508c7f05d --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/testing.go @@ -0,0 +1,19 @@ +package glint + +import ( + "github.com/mitchellh/go-testing-interface" +) + +// TestRender renders the component using the string renderer and returns +// the string. This is a test helper function for writing components. +func TestRender(t testing.T, c Component) string { + // Note that nothing here fails at the moment so the t param above is + // unneeded but we're gonna keep it around in case we need it in the future + // so we don't have to break API. + r := &StringRenderer{} + d := New() + d.SetRenderer(r) + d.Append(c) + d.RenderFrame() + return r.Builder.String() +} diff --git a/vendor/github.com/mitchellh/go-glint/text.go b/vendor/github.com/mitchellh/go-glint/text.go new file mode 100644 index 000000000000..52f67672d968 --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/text.go @@ -0,0 +1,37 @@ +package glint + +import ( + "context" +) + +// TextComponent is a Component that renders text. +type TextComponent struct { + terminalComponent + f func(rows, cols uint) string +} + +// Text creates a TextComponent for static text. The text here will be word +// wrapped automatically based on the width of the terminal. +func Text(v string) *TextComponent { + return TextFunc(func(rows, cols uint) string { return v }) +} + +// TextFunc creates a TextComponent for text that is dependent on the +// size of the draw area. +func TextFunc(f func(rows, cols uint) string) *TextComponent { + return &TextComponent{ + f: f, + } +} + +func (el *TextComponent) Body(context.Context) Component { + return nil +} + +func (el *TextComponent) Render(rows, cols uint) string { + if el.f == nil { + return "" + } + + return el.f(rows, cols) +} diff --git a/vendor/github.com/mitchellh/go-glint/tree.go b/vendor/github.com/mitchellh/go-glint/tree.go new file mode 100644 index 000000000000..a67ef7a0cfce --- /dev/null +++ b/vendor/github.com/mitchellh/go-glint/tree.go @@ -0,0 +1,87 @@ +package glint + +import ( + "context" + + "github.com/mitchellh/go-glint/flex" +) + +func tree( + ctx context.Context, + parent *flex.Node, + c Component, + finalize bool, +) { + // Don't do anything with no component + if c == nil { + return + } + + // Fragments don't create a node + switch c := c.(type) { + case *contextComponent: + for i := 0; i < len(c.pairs); i += 2 { + ctx = context.WithValue(ctx, c.pairs[i], c.pairs[i+1]) + } + + tree(ctx, parent, c.inner, finalize) + return + + case *fragmentComponent: + for _, c := range c.List { + tree(ctx, parent, c, finalize) + } + + return + } + + // Setup our node + node := flex.NewNodeWithConfig(parent.Config) + parent.InsertChild(node, len(parent.Children)) + + // Setup our default context + parentCtx := &parentContext{C: c} + node.Context = parentCtx + + // Check if we're finalized and note it + if _, ok := c.(*finalizedComponent); ok { + parentCtx.Finalized = true + } + + // Finalize + if finalize { + if c, ok := c.(ComponentFinalizer); ok { + c.Finalize() + } + } + + // Setup a custom layout + if c, ok := c.(componentLayout); ok { + c.Layout().Apply(node) + } + + switch c := c.(type) { + case *TextComponent: + node.Context = &TextNodeContext{C: c, Context: ctx} + node.StyleSetFlexShrink(1) + node.StyleSetFlexGrow(0) + node.StyleSetFlexDirection(flex.FlexDirectionRow) + node.SetMeasureFunc(MeasureTextNode) + + default: + // If this is not terminal then we nest. + tree(ctx, node, c.Body(ctx), finalize) + } + +} + +type parentContext struct { + C Component + Finalized bool +} + +func (c *parentContext) Component() Component { return c.C } + +type treeContext interface { + Component() Component +} diff --git a/vendor/github.com/mitchellh/go-testing-interface/.travis.yml b/vendor/github.com/mitchellh/go-testing-interface/.travis.yml index 928d000ec49e..cca949103af0 100644 --- a/vendor/github.com/mitchellh/go-testing-interface/.travis.yml +++ b/vendor/github.com/mitchellh/go-testing-interface/.travis.yml @@ -1,7 +1,6 @@ language: go go: - - 1.8 - 1.x - tip diff --git a/vendor/github.com/mitchellh/go-testing-interface/README.md b/vendor/github.com/mitchellh/go-testing-interface/README.md index 26781bbae887..ee435adc54de 100644 --- a/vendor/github.com/mitchellh/go-testing-interface/README.md +++ b/vendor/github.com/mitchellh/go-testing-interface/README.md @@ -38,6 +38,14 @@ You can also call the test helper at runtime if needed: TestHelper(&testing.RuntimeT{}) } +## Versioning + +The tagged version matches the version of Go that the interface is +compatible with. For example, the version "1.14.0" is for Go 1.14 and +introduced the `Cleanup` function. The patch version (the ".0" in the +prior example) is used to fix any bugs found in this library and has no +correlation to the supported Go version. + ## Why?! **Why would I call a test helper that takes a *testing.T at runtime?** diff --git a/vendor/github.com/mitchellh/go-testing-interface/go.mod b/vendor/github.com/mitchellh/go-testing-interface/go.mod index 062796de727a..acc65c4e5a9c 100644 --- a/vendor/github.com/mitchellh/go-testing-interface/go.mod +++ b/vendor/github.com/mitchellh/go-testing-interface/go.mod @@ -1 +1,3 @@ module github.com/mitchellh/go-testing-interface + +go 1.14 diff --git a/vendor/github.com/mitchellh/go-testing-interface/testing.go b/vendor/github.com/mitchellh/go-testing-interface/testing.go index b05a49a69075..86510322abfc 100644 --- a/vendor/github.com/mitchellh/go-testing-interface/testing.go +++ b/vendor/github.com/mitchellh/go-testing-interface/testing.go @@ -1,5 +1,3 @@ -// +build !go1.9 - package testing import ( @@ -12,6 +10,7 @@ import ( // In unit tests you can just pass a *testing.T struct. At runtime, outside // of tests, you can pass in a RuntimeT struct from this package. type T interface { + Cleanup(func()) Error(args ...interface{}) Errorf(format string, args ...interface{}) Fail() @@ -19,6 +18,7 @@ type T interface { Failed() bool Fatal(args ...interface{}) Fatalf(format string, args ...interface{}) + Helper() Log(args ...interface{}) Logf(format string, args ...interface{}) Name() string @@ -34,10 +34,13 @@ type T interface { // for calls to Fatal. For calls to Error, you'll have to check the errors // list to determine whether to exit yourself. // +// Cleanup does NOT work, so if you're using a helper that uses Cleanup, +// there may be dangling resources. +// // Parallel does not do anything. type RuntimeT struct { - failed bool skipped bool + failed bool } func (t *RuntimeT) Error(args ...interface{}) { @@ -46,20 +49,10 @@ func (t *RuntimeT) Error(args ...interface{}) { } func (t *RuntimeT) Errorf(format string, args ...interface{}) { - log.Println(fmt.Sprintf(format, args...)) + log.Printf(format, args...) t.Fail() } -func (t *RuntimeT) Fatal(args ...interface{}) { - log.Println(fmt.Sprintln(args...)) - t.FailNow() -} - -func (t *RuntimeT) Fatalf(format string, args ...interface{}) { - log.Println(fmt.Sprintf(format, args...)) - t.FailNow() -} - func (t *RuntimeT) Fail() { t.failed = true } @@ -72,6 +65,16 @@ func (t *RuntimeT) Failed() bool { return t.failed } +func (t *RuntimeT) Fatal(args ...interface{}) { + log.Print(args...) + t.FailNow() +} + +func (t *RuntimeT) Fatalf(format string, args ...interface{}) { + log.Printf(format, args...) + t.FailNow() +} + func (t *RuntimeT) Log(args ...interface{}) { log.Println(fmt.Sprintln(args...)) } @@ -103,3 +106,7 @@ func (t *RuntimeT) Skipf(format string, args ...interface{}) { func (t *RuntimeT) Skipped() bool { return t.skipped } + +func (t *RuntimeT) Helper() {} + +func (t *RuntimeT) Cleanup(func()) {} diff --git a/vendor/github.com/mitchellh/go-testing-interface/testing_go19.go b/vendor/github.com/mitchellh/go-testing-interface/testing_go19.go deleted file mode 100644 index 31b42cadf8d3..000000000000 --- a/vendor/github.com/mitchellh/go-testing-interface/testing_go19.go +++ /dev/null @@ -1,108 +0,0 @@ -// +build go1.9 - -// NOTE: This is a temporary copy of testing.go for Go 1.9 with the addition -// of "Helper" to the T interface. Go 1.9 at the time of typing is in RC -// and is set for release shortly. We'll support this on master as the default -// as soon as 1.9 is released. - -package testing - -import ( - "fmt" - "log" -) - -// T is the interface that mimics the standard library *testing.T. -// -// In unit tests you can just pass a *testing.T struct. At runtime, outside -// of tests, you can pass in a RuntimeT struct from this package. -type T interface { - Error(args ...interface{}) - Errorf(format string, args ...interface{}) - Fail() - FailNow() - Failed() bool - Fatal(args ...interface{}) - Fatalf(format string, args ...interface{}) - Log(args ...interface{}) - Logf(format string, args ...interface{}) - Name() string - Skip(args ...interface{}) - SkipNow() - Skipf(format string, args ...interface{}) - Skipped() bool - Helper() -} - -// RuntimeT implements T and can be instantiated and run at runtime to -// mimic *testing.T behavior. Unlike *testing.T, this will simply panic -// for calls to Fatal. For calls to Error, you'll have to check the errors -// list to determine whether to exit yourself. -type RuntimeT struct { - skipped bool - failed bool -} - -func (t *RuntimeT) Error(args ...interface{}) { - log.Println(fmt.Sprintln(args...)) - t.Fail() -} - -func (t *RuntimeT) Errorf(format string, args ...interface{}) { - log.Printf(format, args...) - t.Fail() -} - -func (t *RuntimeT) Fail() { - t.failed = true -} - -func (t *RuntimeT) FailNow() { - panic("testing.T failed, see logs for output (if any)") -} - -func (t *RuntimeT) Failed() bool { - return t.failed -} - -func (t *RuntimeT) Fatal(args ...interface{}) { - log.Print(args...) - t.FailNow() -} - -func (t *RuntimeT) Fatalf(format string, args ...interface{}) { - log.Printf(format, args...) - t.FailNow() -} - -func (t *RuntimeT) Log(args ...interface{}) { - log.Println(fmt.Sprintln(args...)) -} - -func (t *RuntimeT) Logf(format string, args ...interface{}) { - log.Println(fmt.Sprintf(format, args...)) -} - -func (t *RuntimeT) Name() string { - return "" -} - -func (t *RuntimeT) Skip(args ...interface{}) { - log.Print(args...) - t.SkipNow() -} - -func (t *RuntimeT) SkipNow() { - t.skipped = true -} - -func (t *RuntimeT) Skipf(format string, args ...interface{}) { - log.Printf(format, args...) - t.SkipNow() -} - -func (t *RuntimeT) Skipped() bool { - return t.skipped -} - -func (t *RuntimeT) Helper() {} diff --git a/vendor/github.com/mitchellh/go-wordwrap/go.mod b/vendor/github.com/mitchellh/go-wordwrap/go.mod index 2ae411b20123..431d615d47ab 100644 --- a/vendor/github.com/mitchellh/go-wordwrap/go.mod +++ b/vendor/github.com/mitchellh/go-wordwrap/go.mod @@ -1 +1,3 @@ module github.com/mitchellh/go-wordwrap + +go 1.14 diff --git a/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go b/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go index ac67205bc2e5..f7bedda38828 100644 --- a/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go +++ b/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go @@ -5,6 +5,8 @@ import ( "unicode" ) +const nbsp = 0xA0 + // WrapString wraps the given string within lim width in characters. // // Wrapping is currently naive and only happens at white-space. A future @@ -18,50 +20,58 @@ func WrapString(s string, lim uint) string { var current uint var wordBuf, spaceBuf bytes.Buffer + var wordBufLen, spaceBufLen uint for _, char := range s { if char == '\n' { if wordBuf.Len() == 0 { - if current+uint(spaceBuf.Len()) > lim { + if current+spaceBufLen > lim { current = 0 } else { - current += uint(spaceBuf.Len()) + current += spaceBufLen spaceBuf.WriteTo(buf) } spaceBuf.Reset() + spaceBufLen = 0 } else { - current += uint(spaceBuf.Len() + wordBuf.Len()) + current += spaceBufLen + wordBufLen spaceBuf.WriteTo(buf) spaceBuf.Reset() + spaceBufLen = 0 wordBuf.WriteTo(buf) wordBuf.Reset() + wordBufLen = 0 } buf.WriteRune(char) current = 0 - } else if unicode.IsSpace(char) { + } else if unicode.IsSpace(char) && char != nbsp { if spaceBuf.Len() == 0 || wordBuf.Len() > 0 { - current += uint(spaceBuf.Len() + wordBuf.Len()) + current += spaceBufLen + wordBufLen spaceBuf.WriteTo(buf) spaceBuf.Reset() + spaceBufLen = 0 wordBuf.WriteTo(buf) wordBuf.Reset() + wordBufLen = 0 } spaceBuf.WriteRune(char) + spaceBufLen++ } else { - wordBuf.WriteRune(char) + wordBufLen++ - if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim { + if current+wordBufLen+spaceBufLen > lim && wordBufLen < lim { buf.WriteRune('\n') current = 0 spaceBuf.Reset() + spaceBufLen = 0 } } } if wordBuf.Len() == 0 { - if current+uint(spaceBuf.Len()) <= lim { + if current+spaceBufLen <= lim { spaceBuf.WriteTo(buf) } } else { diff --git a/vendor/github.com/tj/go-spin/Readme.md b/vendor/github.com/tj/go-spin/Readme.md new file mode 100644 index 000000000000..b31b6d5e8dd7 --- /dev/null +++ b/vendor/github.com/tj/go-spin/Readme.md @@ -0,0 +1,28 @@ + +# go-spin + + Little terminal spinner lib. + + View the [docs](http://godoc.org/github.com/tj/go-spin). + +## Installation + +``` +$ go get github.com/tj/go-spin +``` + +## Example + +```go +s := spin.New() +for i := 0; i < 30; i++ { + fmt.Printf("\r \033[36mcomputing\033[m %s ", s.Next()) + time.Sleep(100 * time.Millisecond) +} +``` +## GIF FTW +![](./go-spin.gif) + +# License + + MIT \ No newline at end of file diff --git a/vendor/github.com/tj/go-spin/go-spin.gif b/vendor/github.com/tj/go-spin/go-spin.gif new file mode 100644 index 0000000000000000000000000000000000000000..10e37e541d5ca15205aedaad42c5aed848ed7215 GIT binary patch literal 375105 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%0A&0R$2pqtF9fkg}iD+`db> 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 y;dev{Wjd*gHXjs=;r=H{LVdx=`xtBwX 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 Q{VUb!WcyVJBVh3V07$3H5C8xG literal 0 HcmV?d00001 diff --git a/vendor/github.com/tj/go-spin/spin.go b/vendor/github.com/tj/go-spin/spin.go new file mode 100644 index 000000000000..bd302b7cd6ee --- /dev/null +++ b/vendor/github.com/tj/go-spin/spin.go @@ -0,0 +1,71 @@ +package spin + +import "sync" + +// Spinner types. +var ( + Box1 = `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏` + Box2 = `⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓` + Box3 = `⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆` + Box4 = `⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋` + Box5 = `⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁` + Box6 = `⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈` + Box7 = `⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈` + Spin1 = `|/-\` + Spin2 = `◴◷◶◵` + Spin3 = `◰◳◲◱` + Spin4 = `◐◓◑◒` + Spin5 = `▉▊▋▌▍▎▏▎▍▌▋▊▉` + Spin6 = `▌▄▐▀` + Spin7 = `╫╪` + Spin8 = `■□▪▫` + Spin9 = `←↑→↓` + Default = Box1 +) + +// Spinner is exactly what you think it is. +type Spinner struct { + mu sync.Mutex + frames []rune + length int + pos int +} + +// New returns a spinner initialized with Default frames. +func New() *Spinner { + s := &Spinner{} + s.Set(Default) + return s +} + +// Set frames to the given string which must not use spaces. +func (s *Spinner) Set(frames string) { + s.mu.Lock() + defer s.mu.Unlock() + s.frames = []rune(frames) + s.length = len(s.frames) +} + +// Current returns the current rune in the sequence. +func (s *Spinner) Current() string { + s.mu.Lock() + defer s.mu.Unlock() + r := s.frames[s.pos%s.length] + return string(r) +} + +// Next returns the next rune in the sequence. +func (s *Spinner) Next() string { + s.mu.Lock() + defer s.mu.Unlock() + r := s.frames[s.pos%s.length] + s.pos++ + return string(r) +} + +// Reset the spinner to its initial frame. +func (s *Spinner) Reset() { + s.mu.Lock() + defer s.mu.Unlock() + s.pos = 0 +} diff --git a/vendor/golang.org/x/crypto/pkcs12/pkcs12.go b/vendor/golang.org/x/crypto/pkcs12/pkcs12.go index 3e2ce6940758..3a89bdb3e393 100644 --- a/vendor/golang.org/x/crypto/pkcs12/pkcs12.go +++ b/vendor/golang.org/x/crypto/pkcs12/pkcs12.go @@ -30,6 +30,8 @@ var ( oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) + + errUnknownAttributeOID = errors.New("pkcs12: unknown attribute OID") ) type pfxPdu struct { @@ -104,6 +106,11 @@ func unmarshal(in []byte, out interface{}) error { } // ToPEM converts all "safe bags" contained in pfxData to PEM blocks. +// Unknown attributes are discarded. +// +// Note that although the returned PEM blocks for private keys have type +// "PRIVATE KEY", the bytes are not encoded according to PKCS #8, but according +// to PKCS #1 for RSA keys and SEC 1 for ECDSA keys. func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { encodedPassword, err := bmpString(password) if err != nil { @@ -135,6 +142,9 @@ func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { for _, attribute := range bag.Attributes { k, v, err := convertAttribute(&attribute) + if err == errUnknownAttributeOID { + continue + } if err != nil { return nil, err } @@ -188,7 +198,7 @@ func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) key = "Microsoft CSP Name" isString = true default: - return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + return "", "", errUnknownAttributeOID } if isString { diff --git a/vendor/modules.txt b/vendor/modules.txt index 7607752ac132..84b6de303286 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -60,6 +60,8 @@ github.com/NVIDIA/gpu-monitoring-tools/bindings/go/nvml github.com/NYTimes/gziphandler # github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d github.com/StackExchange/wmi +# github.com/VividCortex/ewma v1.1.1 +github.com/VividCortex/ewma # github.com/agext/levenshtein v1.2.1 github.com/agext/levenshtein # github.com/apparentlymart/go-cidr v1.0.1 @@ -147,6 +149,9 @@ github.com/cespare/xxhash/v2 # github.com/checkpoint-restore/go-criu/v4 v4.1.0 github.com/checkpoint-restore/go-criu/v4 github.com/checkpoint-restore/go-criu/v4/rpc +# github.com/cheggaaa/pb/v3 v3.0.5 +github.com/cheggaaa/pb/v3 +github.com/cheggaaa/pb/v3/termutil # github.com/cilium/ebpf v0.2.0 github.com/cilium/ebpf github.com/cilium/ebpf/asm @@ -331,6 +336,8 @@ github.com/google/go-querystring/query github.com/google/uuid # github.com/googleapis/gax-go/v2 v2.0.5 github.com/googleapis/gax-go/v2 +# github.com/gookit/color v1.3.1 +github.com/gookit/color # github.com/gophercloud/gophercloud v0.1.0 github.com/gophercloud/gophercloud github.com/gophercloud/gophercloud/openstack @@ -347,9 +354,6 @@ github.com/gorilla/mux # github.com/gorilla/websocket v1.4.2 ## explicit github.com/gorilla/websocket -# github.com/gosuri/uilive v0.0.4 -## explicit -github.com/gosuri/uilive # github.com/grpc-ecosystem/go-grpc-middleware v1.2.1-0.20200228141219-3ce3d519df39 ## explicit github.com/grpc-ecosystem/go-grpc-middleware/retry @@ -574,6 +578,8 @@ github.com/linode/linodego github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-isatty +# github.com/mattn/go-runewidth v0.0.7 +github.com/mattn/go-runewidth # github.com/mattn/go-shellwords v1.0.5 github.com/mattn/go-shellwords # github.com/matttproud/golang_protobuf_extensions v1.0.1 @@ -589,15 +595,21 @@ github.com/mitchellh/colorstring # github.com/mitchellh/copystructure v1.0.0 ## explicit github.com/mitchellh/copystructure +# github.com/mitchellh/go-glint v0.0.0-20201119015200-53f6eb3bf4d2 +## explicit +github.com/mitchellh/go-glint +github.com/mitchellh/go-glint/components +github.com/mitchellh/go-glint/flex +github.com/mitchellh/go-glint/internal/layout # github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir # github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b ## explicit github.com/mitchellh/go-ps -# github.com/mitchellh/go-testing-interface v1.0.3 +# github.com/mitchellh/go-testing-interface v1.14.1 ## explicit github.com/mitchellh/go-testing-interface -# github.com/mitchellh/go-wordwrap v1.0.0 +# github.com/mitchellh/go-wordwrap v1.0.1 github.com/mitchellh/go-wordwrap # github.com/mitchellh/hashstructure v1.0.0 ## explicit @@ -750,6 +762,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312 +# github.com/tj/go-spin v1.1.0 +github.com/tj/go-spin # github.com/tklauser/go-sysconf v0.3.5 ## explicit github.com/tklauser/go-sysconf @@ -824,7 +838,7 @@ go.opencensus.io/trace go.opencensus.io/trace/internal go.opencensus.io/trace/propagation go.opencensus.io/trace/tracestate -# golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 +# golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a ## explicit golang.org/x/crypto/bcrypt golang.org/x/crypto/blake2b