From da97fc47980213114fce487e567b8d4817943c53 Mon Sep 17 00:00:00 2001 From: Martin Povolny Date: Sun, 27 Nov 2022 18:13:39 +0100 Subject: [PATCH] feat: allow setting of fixed columns in the list of issues --- internal/cmd/issue/list/list.go | 12 +++++-- internal/view/helper.go | 19 +++++++++++ internal/view/issues.go | 14 +++++--- pkg/tui/table.go | 60 ++++++++++++++++++++++++--------- 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/internal/cmd/issue/list/list.go b/internal/cmd/issue/list/list.go index c7f291bd..c28b4864 100644 --- a/internal/cmd/issue/list/list.go +++ b/internal/cmd/issue/list/list.go @@ -116,6 +116,9 @@ func loadList(cmd *cobra.Command) { noTruncate, err := cmd.Flags().GetBool("no-truncate") cmdutil.ExitIfError(err) + fixedColumns, err := cmd.Flags().GetUint("fixed-columns") + cmdutil.ExitIfError(err) + columns, err := cmd.Flags().GetString("columns") cmdutil.ExitIfError(err) @@ -128,9 +131,10 @@ func loadList(cmd *cobra.Command) { loadList(cmd) }, Display: view.DisplayFormat{ - Plain: plain, - NoHeaders: noHeaders, - NoTruncate: noTruncate, + Plain: plain, + NoHeaders: noHeaders, + NoTruncate: noTruncate, + FixedColumns: fixedColumns, Columns: func() []string { if columns != "" { return strings.Split(columns, ",") @@ -138,6 +142,7 @@ func loadList(cmd *cobra.Command) { return []string{} }(), TableStyle: cmdutil.GetTUIStyleConfig(), + CustomKeys: viper.GetStringMap("custom_keys.issues"), }, } @@ -178,6 +183,7 @@ func SetFlags(cmd *cobra.Command) { cmd.Flags().Bool("plain", false, "Display output in plain mode") cmd.Flags().Bool("no-headers", false, "Don't display table headers in plain mode. Works only with --plain") cmd.Flags().Bool("no-truncate", false, "Show all available columns in plain mode. Works only with --plain") + cmd.Flags().Uint("fixed-columns", 1, "Number of fixed columns in the interactive mode.") if cmd.HasParent() && cmd.Parent().Name() != "sprint" { cmd.Flags().String("columns", "", "Comma separated list of columns to display in the plain mode.\n"+ diff --git a/internal/view/helper.go b/internal/view/helper.go index 57545b76..3bb53115 100644 --- a/internal/view/helper.go +++ b/internal/view/helper.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "os/exec" "strings" "text/tabwriter" "time" @@ -115,6 +116,24 @@ func jiraURLFromTuiData(server string, r int, d interface{}) string { return fmt.Sprintf("%s/browse/%s", server, issueKeyFromTuiData(r, d)) } +func runInSh(command string) { + cmd := exec.Command("sh", "-c", command) + _ = cmd.Run() +} + +func customKeyFunc(row, column int, data interface{}, command string) { + i := issueKeyFromTuiData(row, data) + expandedCommand := strings.ReplaceAll(command, "%KEY%", i) + if strings.HasPrefix(expandedCommand, "&") { + go func() { + expandedCommand = strings.TrimPrefix(expandedCommand, "&") + runInSh(expandedCommand) + }() + } else { + runInSh(expandedCommand) + } +} + func navigate(server string) tui.SelectedFunc { return func(r, c int, d interface{}) { _ = browser.Browse(jiraURLFromTuiData(server, r, d)) diff --git a/internal/view/issues.go b/internal/view/issues.go index f7531c78..9f9a36e8 100644 --- a/internal/view/issues.go +++ b/internal/view/issues.go @@ -15,11 +15,13 @@ import ( // DisplayFormat is a issue display type. type DisplayFormat struct { - Plain bool - NoHeaders bool - NoTruncate bool - Columns []string - TableStyle tui.TableStyle + Plain bool + NoHeaders bool + NoTruncate bool + Columns []string + FixedColumns uint + TableStyle tui.TableStyle + CustomKeys map[string]interface{} } // IssueList is a list view for issues. @@ -51,6 +53,7 @@ func (l *IssueList) Render() error { } view := tui.NewTable( + tui.WithCustomKeyFunc(customKeyFunc, l.Display.CustomKeys), tui.WithTableStyle(l.Display.TableStyle), tui.WithTableFooterText(l.FooterText), tui.WithSelectedFunc(navigate(l.Server)), @@ -73,6 +76,7 @@ func (l *IssueList) Render() error { tui.WithCopyFunc(copyURL(l.Server)), tui.WithCopyKeyFunc(copyKey()), tui.WithRefreshFunc(l.Refresh), + tui.WithFixedColumns(l.Display.FixedColumns), ) return view.Paint(data) diff --git a/pkg/tui/table.go b/pkg/tui/table.go index 6e0e4348..bb919366 100644 --- a/pkg/tui/table.go +++ b/pkg/tui/table.go @@ -30,6 +30,9 @@ type CopyFunc func(row, column int, data interface{}) // CopyKeyFunc is fired when a user press 'CTRL+K' character in the table cell. type CopyKeyFunc func(row, column int, data interface{}) +// CustomKeyFunc is fired when one of custom keys is pressed in the table cell. +type CustomKeyFunc func(row, column int, data interface{}, command string) + // TableData is the data to be displayed in a table. type TableData [][]string @@ -42,20 +45,23 @@ type TableStyle struct { // Table is a table layout. type Table struct { - screen *Screen - painter *tview.Pages - view *tview.Table - footer *tview.TextView - style TableStyle - data TableData - colPad uint - maxColWidth uint - footerText string - selectedFunc SelectedFunc - viewModeFunc ViewModeFunc - refreshFunc RefreshFunc - copyFunc CopyFunc - copyKeyFunc CopyKeyFunc + screen *Screen + painter *tview.Pages + view *tview.Table + footer *tview.TextView + style TableStyle + data TableData + colPad uint + colFixed uint + maxColWidth uint + footerText string + selectedFunc SelectedFunc + viewModeFunc ViewModeFunc + customKeys map[string]interface{} + customKeyFunc CustomKeyFunc + refreshFunc RefreshFunc + copyFunc CopyFunc + copyKeyFunc CopyKeyFunc } // TableOption is a functional option to wrap table properties. @@ -120,6 +126,14 @@ func WithViewModeFunc(fn ViewModeFunc) TableOption { } } +// WithCustomKeyFunc sets a func that is triggered when one of custom keys is pressed. +func WithCustomKeyFunc(fn CustomKeyFunc, keys map[string]interface{}) TableOption { + return func(t *Table) { + t.customKeyFunc = fn + t.customKeys = keys + } +} + // WithRefreshFunc sets a func that is triggered when a user press 'CTRL+R' or 'F5'. func WithRefreshFunc(fn RefreshFunc) TableOption { return func(t *Table) { @@ -141,6 +155,13 @@ func WithCopyKeyFunc(fn CopyKeyFunc) TableOption { } } +// WithFixedColumns sets the number of columns that are locked (do not scroll right). +func WithFixedColumns(cols uint) TableOption { + return func(t *Table) { + t.colFixed = cols + } +} + // Paint paints the table layout. First row is treated as a table header. func (t *Table) Paint(data TableData) error { if len(data) == 0 { @@ -224,12 +245,21 @@ func (t *Table) initTable() { // Refresh the screen. t.screen.Draw() }() + default: + if t.customKeyFunc != nil && t.customKeys != nil { + for k, command := range t.customKeys { + if string(ev.Rune()) == k { + r, c := t.view.GetSelection() + t.customKeyFunc(r, c, t.data, command.(string)) + } + } + } } } return ev }) - t.view.SetFixed(1, 1) + t.view.SetFixed(1, int(t.colFixed)) } func renderTableHeader(t *Table, data []string) {