From 98878e33fa4863d634c8939daeea1f3f89570714 Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:35:24 +0900 Subject: [PATCH] Implement CLI_PAGER and CLI_AUTOWRAP (#87) --- README.md | 5 ++++- cli.go | 46 +++++++++++++++++++++++++++++++++++++++++---- go.mod | 1 + go.sum | 1 + system_variables.go | 15 +++++++++++++++ 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 79ac15e..f67f888 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,8 @@ You can control your Spanner databases with idiomatic SQL commands. * Allow newlines in prompt using `%n` * System variables expansion * Prompt2 with margin and waiting status - * Autowrap and auto adjust column width to fit within terminal width. + * Autowrap and auto adjust column width to fit within terminal width when `CLI_AUTOWRAP = TRUE`). + * Pager support when `CLI_USE_PAGER = TRUE` * Progress bar of DDL execution. * Utilize other libraries * Dogfooding [`cloudspannerecosystem/memefish`](https://github.com/cloudspannerecosystem/memefish) @@ -521,6 +522,8 @@ They have almost same semantics with [Spanner JDBC properties](https://cloud.goo | CLI_INSECURE | READ_WRITE | `"FALSE"` | | CLI_QUERY_MODE | READ_WRITE | `"PROFILE"` | | CLI_LINT_PLAN | READ_WRITE | `"TRUE"` | +| CLI_USE_PAGER | READ_WRITE | `"TRUE"` | +| CLI_AUTOWRAP | READ_WRITE | `"TRUE"` | ### Embedded Cloud Spanner Emulator diff --git a/cli.go b/cli.go index 05a8470..06ebb45 100644 --- a/cli.go +++ b/cli.go @@ -27,6 +27,7 @@ import ( "log" "math" "os" + "os/exec" "os/signal" "regexp" "slices" @@ -34,6 +35,8 @@ import ( "strings" "time" + "github.com/kballard/go-shellquote" + "github.com/nyaosorg/go-readline-ny" "github.com/hymkor/go-multiline-ny" @@ -348,9 +351,12 @@ func (c *Cli) RunInteractive(ctx context.Context) int { c.updateSystemVariables(result) } - size, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - size = math.MaxInt + size := math.MaxInt + if c.SystemVariables.AutoWrap { + size, _, err = term.GetSize(int(os.Stdout.Fd())) + if err != nil { + size = math.MaxInt + } } c.PrintResult(size, result, c.SystemVariables.CLIFormat, true) @@ -426,7 +432,39 @@ func (c *Cli) PrintBatchError(err error) { } func (c *Cli) PrintResult(screenWidth int, result *Result, mode DisplayMode, interactive bool) { - printResult(c.SystemVariables.Debug, screenWidth, c.OutStream, result, mode, interactive, c.SystemVariables.Verbose) + ostream := c.OutStream + var cmd *exec.Cmd + if c.SystemVariables.UsePager { + pagerpath := cmp.Or(os.Getenv("PAGER"), "less") + + split, err := shellquote.Split(pagerpath) + if err != nil { + return + } + cmd = exec.CommandContext(context.Background(), split[0], split[1:]...) + + pr, pw := io.Pipe() + ostream = pw + cmd.Stdin = pr + cmd.Stdout = c.OutStream + + err = cmd.Start() + if err != nil { + log.Println(err) + return + } + defer func() { + err := pw.Close() + if err != nil { + log.Println(err) + } + err = cmd.Wait() + if err != nil { + log.Println(err) + } + }() + } + printResult(c.SystemVariables.Debug, screenWidth, ostream, result, mode, interactive, c.SystemVariables.Verbose) } func (c *Cli) PrintProgressingMark() func() { diff --git a/go.mod b/go.mod index 6c61f09..bbdd9c2 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/hymkor/go-multiline-ny v0.17.0 github.com/jessevdk/go-flags v1.6.1 github.com/k0kubun/pp/v3 v3.4.1 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-runewidth v0.0.16 github.com/ngicks/go-iterator-helper v0.0.15 github.com/nyaosorg/go-readline-ny v1.6.2 diff --git a/go.sum b/go.sum index f1203d6..33f9b10 100644 --- a/go.sum +++ b/go.sum @@ -923,6 +923,7 @@ github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/k0kubun/pp/v3 v3.4.1 h1:1WdFZDRRqe8UsR61N/2RoOZ3ziTEqgTPVqKrHeb779Y= github.com/k0kubun/pp/v3 v3.4.1/go.mod h1:+SiNiqKnBfw1Nkj82Lh5bIeKQOAkPy6Xw9CAZUZ8npI= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= diff --git a/system_variables.go b/system_variables.go index ad535e7..3501a4e 100644 --- a/system_variables.go +++ b/system_variables.go @@ -47,6 +47,7 @@ type systemVariables struct { LintPlan bool TransactionTag string RequestTag string + UsePager bool // it is internal variable and hidden from system variable statements ProtoDescriptor *descriptorpb.FileDescriptorSet @@ -58,6 +59,7 @@ type systemVariables struct { CurrentSession *Session ReadOnly bool VertexAIProject string + AutoWrap bool } var errIgnored = errors.New("ignored") @@ -453,6 +455,12 @@ var accessorMap = map[string]accessor{ }), Setter: boolSetter(func(sysVars *systemVariables) *bool { return &sysVars.LintPlan }), }, + "CLI_USE_PAGER": boolAccessor(func(variables *systemVariables) *bool { + return &variables.UsePager + }), + "CLI_AUTOWRAP": boolAccessor(func(variables *systemVariables) *bool { + return &variables.AutoWrap + }), "CLI_QUERY_MODE": { Getter: func(this *systemVariables, name string) (map[string]string, error) { if this.QueryMode == nil { @@ -549,6 +557,13 @@ func stringSetter(f func(sysVars *systemVariables) *string) setter { } } +func boolAccessor(f func(variables *systemVariables) *bool) accessor { + return accessor{ + Setter: boolSetter(f), + Getter: boolGetter(f), + } +} + func stringAccessor(f func(variables *systemVariables) *string) accessor { return accessor{ Setter: stringSetter(f),