From 42e5793438dbf65e077f78736d6a576ea9e7dc4b Mon Sep 17 00:00:00 2001 From: Bruno Meneguello Date: Fri, 11 Oct 2019 14:11:06 -0300 Subject: [PATCH 1/5] allow to mark multiple resources to act upon --- internal/config/style.go | 2 ++ internal/resource/list.go | 33 +++++++++++++++++++++++++++++++++ internal/ui/table.go | 14 ++++++++++++++ internal/views/pod.go | 16 +++++++++------- internal/views/resource.go | 36 ++++++++++++++++++++++++++++-------- skins/black_and_wtf.yml | 1 + skins/in_the_navy.yml | 1 + skins/stock.yml | 1 + 8 files changed, 89 insertions(+), 15 deletions(-) diff --git a/internal/config/style.go b/internal/config/style.go index cb276136..8f5175b1 100644 --- a/internal/config/style.go +++ b/internal/config/style.go @@ -99,6 +99,7 @@ type ( FgColor string `yaml:"fgColor"` BgColor string `yaml:"bgColor"` CursorColor string `yaml:"cursorColor"` + MarkColor string `yaml:"markColor"` Header TableHeader `yaml:"header"` } @@ -215,6 +216,7 @@ func newTable() Table { FgColor: "aqua", BgColor: "black", CursorColor: "aqua", + MarkColor: "darkgoldenrod", Header: newTableHeader(), } } diff --git a/internal/resource/list.go b/internal/resource/list.go index cf8c2620..ba645cf6 100644 --- a/internal/resource/list.go +++ b/internal/resource/list.go @@ -66,6 +66,7 @@ type ( Rows RowEvents NumCols map[string]bool Namespace string + Marks []string } // List protocol to display and update a collection of resources @@ -84,6 +85,7 @@ type ( SetFieldSelector(string) SetLabelSelector(string) HasSelectors() bool + ToggleMark(sk string) } // Columnar tracks resources that can be diplayed in a tabular fashion. @@ -127,9 +129,20 @@ type ( verbs int resource Resource cache RowEvents + marks []string } ) +// IsMarked checks if key is marked. +func (t *TableData) IsMarked(sk string) bool { + for _, mark := range t.Marks { + if mark == sk { + return true + } + } + return false +} + func newRowEvent(a watch.EventType, f, d Row) *RowEvent { return &RowEvent{Action: a, Fields: f, Deltas: d} } @@ -232,6 +245,7 @@ func (l *list) Data() TableData { Rows: l.cache, NumCols: l.resource.NumCols(l.namespace), Namespace: l.namespace, + Marks: l.marks, } } @@ -343,10 +357,29 @@ func (l *list) ensureDeletes(kk []string) { } if !found { delete(l.cache, k) + l.removeMark(k) } } } +func (l *list) removeMark(sk string) { + for index, mark := range l.marks { + if mark == sk { + l.marks = append(l.marks[:index], l.marks[index+1:]...) + } + } +} + +func (l *list) ToggleMark(sk string) { + for index, mark := range l.marks { + if mark == sk { + l.marks = append(l.marks[:index], l.marks[index+1:]...) + return + } + } + l.marks = append(l.marks, sk) +} + // Helpers... func computeDeltas(evt *RowEvent, newRow, deltas Row) watch.EventType { diff --git a/internal/ui/table.go b/internal/ui/table.go index f17e1c2a..c4a349c9 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -169,6 +169,16 @@ func (v *Table) GetSelectedItem() string { return v.selectedItem } +// GetSelectedItems return currently marked or selected items names. +func (v *Table) GetSelectedItems() []string { + if len(v.data.Marks) > 0 { + items := make([]string, len(v.data.Marks)) + copy(items, v.data.Marks) + return items + } + return []string{v.GetSelectedItem()} +} + func (v *Table) keyboard(evt *tcell.EventKey) *tcell.EventKey { key := evt.Key() if key == tcell.KeyRune { @@ -318,6 +328,7 @@ func (v *Table) buildRow(row int, data resource.TableData, sk string, pads MaxyP if v.colorerFn != nil { f = v.colorerFn } + m := data.IsMarked(sk) for col, field := range data.Rows[sk].Fields { header := data.Header[col] field, align := v.formatCell(data.NumCols[header], header, field+Deltas(data.Rows[sk].Deltas[col], field), pads[col]) @@ -326,6 +337,9 @@ func (v *Table) buildRow(row int, data resource.TableData, sk string, pads MaxyP c.SetExpansion(1) c.SetAlign(align) c.SetTextColor(f(data.Namespace, data.Rows[sk])) + if m { + c.SetBackgroundColor(config.AsColor(v.styles.Table().MarkColor)) + } } v.SetCell(row, col, c) } diff --git a/internal/views/pod.go b/internal/views/pod.go index af2431ca..abe9455c 100644 --- a/internal/views/pod.go +++ b/internal/views/pod.go @@ -115,15 +115,17 @@ func (v *podView) killCmd(evt *tcell.EventKey) *tcell.EventKey { return evt } - sel := v.masterPage().GetSelectedItem() + sel := v.masterPage().GetSelectedItems() v.masterPage().ShowDeleted() - v.app.Flash().Infof("Delete resource %s %s", v.list.GetName(), sel) - if err := v.list.Resource().Delete(sel, true, false); err != nil { - v.app.Flash().Errf("Delete failed with %s", err) - } else { - deletePortForward(v.app.forwarders, sel) - v.refresh() + for _, res := range sel { + v.app.Flash().Infof("Delete resource %s %s", v.list.GetName(), res) + if err := v.list.Resource().Delete(res, true, false); err != nil { + v.app.Flash().Errf("Delete failed with %s", err) + } else { + deletePortForward(v.app.forwarders, res) + } } + v.refresh() return nil } diff --git a/internal/views/resource.go b/internal/views/resource.go index c1da42c0..4d62664a 100644 --- a/internal/views/resource.go +++ b/internal/views/resource.go @@ -192,23 +192,42 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { return evt } - sel := v.masterPage().GetSelectedItem() - msg := fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel) + sel := v.masterPage().GetSelectedItems() + var msg string + if len(sel) > 1 { + msg = fmt.Sprintf("Delete %d selected %s?", len(sel), v.list.GetName()) + } else { + msg = fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel) + } dialog.ShowDelete(v.Pages, msg, func(cascade, force bool) { v.masterPage().ShowDeleted() - v.app.Flash().Infof("Delete resource %s %s", v.list.GetName(), sel) - if err := v.list.Resource().Delete(sel, cascade, force); err != nil { - v.app.Flash().Errf("Delete failed with %s", err) - } else { - deletePortForward(v.app.forwarders, sel) - v.refresh() + for _, res := range sel { + v.app.Flash().Infof("Delete resource %s %s", v.list.GetName(), res) + if err := v.list.Resource().Delete(res, cascade, force); err != nil { + v.app.Flash().Errf("Delete failed with %s", err) + } else { + deletePortForward(v.app.forwarders, res) + } } + v.refresh() }, func() { v.switchPage("master") }) return nil } +func (v *resourceView) markCmd(evt *tcell.EventKey) *tcell.EventKey { + if !v.masterPage().RowSelected() { + return evt + } + + sel := v.masterPage().GetSelectedItem() + v.list.ToggleMark(sel) + v.refresh() + v.app.Draw() + return nil +} + func deletePortForward(ff map[string]forwarder, sel string) { for k, v := range ff { tokens := strings.Split(k, ":") @@ -371,6 +390,7 @@ func (v *resourceView) refreshActions() { tcell.KeyEnter: ui.NewKeyAction("Enter", v.enterCmd, false), tcell.KeyCtrlR: ui.NewKeyAction("Refresh", v.refreshCmd, false), } + aa[tcell.KeyCtrlSpace] = ui.NewKeyAction("Mark", v.markCmd, true) v.namespaceActions(aa) v.defaultActions(aa) diff --git a/skins/black_and_wtf.yml b/skins/black_and_wtf.yml index 4fe8393b..e8d91b1a 100644 --- a/skins/black_and_wtf.yml +++ b/skins/black_and_wtf.yml @@ -35,6 +35,7 @@ k9s: fgColor: white bgColor: black cursorColor: white + markColor: darkgoldenrod header: fgColor: darkgray bgColor: black diff --git a/skins/in_the_navy.yml b/skins/in_the_navy.yml index 0a7762d6..9dc0f062 100644 --- a/skins/in_the_navy.yml +++ b/skins/in_the_navy.yml @@ -37,6 +37,7 @@ k9s: fgColor: blue bgColor: darkblue cursorColor: aqua + markColor: mediumslateblue header: fgColor: white bgColor: darkblue diff --git a/skins/stock.yml b/skins/stock.yml index 6f42d942..be02aa01 100644 --- a/skins/stock.yml +++ b/skins/stock.yml @@ -35,6 +35,7 @@ k9s: fgColor: blue bgColor: black cursorColor: aqua + markColor: darkgoldenrod header: fgColor: white bgColor: black From 52aa4a122a1a691c8c241eb691d80141c40aec9d Mon Sep 17 00:00:00 2001 From: Bruno Meneguello <1322552+bkmeneguello@users.noreply.github.com> Date: Thu, 7 Nov 2019 18:14:11 -0300 Subject: [PATCH 2/5] Convert marks from slice to map --- internal/resource/list.go | 26 ++++++-------------------- internal/ui/table.go | 8 ++++++-- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/internal/resource/list.go b/internal/resource/list.go index ba645cf6..0fa03995 100644 --- a/internal/resource/list.go +++ b/internal/resource/list.go @@ -66,7 +66,7 @@ type ( Rows RowEvents NumCols map[string]bool Namespace string - Marks []string + Marks map[string]bool } // List protocol to display and update a collection of resources @@ -129,18 +129,13 @@ type ( verbs int resource Resource cache RowEvents - marks []string + marks map[string]bool } ) // IsMarked checks if key is marked. func (t *TableData) IsMarked(sk string) bool { - for _, mark := range t.Marks { - if mark == sk { - return true - } - } - return false + return t.Marks[sk] } func newRowEvent(a watch.EventType, f, d Row) *RowEvent { @@ -155,6 +150,7 @@ func NewList(ns, name string, res Resource, verbs int) *list { verbs: verbs, resource: res, cache: RowEvents{}, + marks: make(map[string]bool), } } @@ -363,21 +359,11 @@ func (l *list) ensureDeletes(kk []string) { } func (l *list) removeMark(sk string) { - for index, mark := range l.marks { - if mark == sk { - l.marks = append(l.marks[:index], l.marks[index+1:]...) - } - } + delete(l.marks, sk) } func (l *list) ToggleMark(sk string) { - for index, mark := range l.marks { - if mark == sk { - l.marks = append(l.marks[:index], l.marks[index+1:]...) - return - } - } - l.marks = append(l.marks, sk) + l.marks[sk] = !l.marks[sk] } // Helpers... diff --git a/internal/ui/table.go b/internal/ui/table.go index c4a349c9..5815acb0 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -172,8 +172,12 @@ func (v *Table) GetSelectedItem() string { // GetSelectedItems return currently marked or selected items names. func (v *Table) GetSelectedItems() []string { if len(v.data.Marks) > 0 { - items := make([]string, len(v.data.Marks)) - copy(items, v.data.Marks) + var items []string + for item, marked := range v.data.Marks { + if marked { + items = append(items, item) + } + } return items } return []string{v.GetSelectedItem()} From b48aa70c06241999c6450f8d22ccc8e7d79a21e5 Mon Sep 17 00:00:00 2001 From: Bruno Meneguello <1322552+bkmeneguello@users.noreply.github.com> Date: Thu, 7 Nov 2019 18:18:49 -0300 Subject: [PATCH 3/5] Fix delete command prompt and flash --- internal/views/resource.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/views/resource.go b/internal/views/resource.go index 4d62664a..494dd939 100644 --- a/internal/views/resource.go +++ b/internal/views/resource.go @@ -197,12 +197,16 @@ func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { if len(sel) > 1 { msg = fmt.Sprintf("Delete %d selected %s?", len(sel), v.list.GetName()) } else { - msg = fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel) + msg = fmt.Sprintf("Delete %s %s?", v.list.GetName(), sel[0]) } dialog.ShowDelete(v.Pages, msg, func(cascade, force bool) { v.masterPage().ShowDeleted() + if len(sel) > 1 { + v.app.Flash().Infof("Delete %d selected %s", len(sel), v.list.GetName()) + } else { + v.app.Flash().Infof("Delete resource %s %s", v.list.GetName(), sel[0]) + } for _, res := range sel { - v.app.Flash().Infof("Delete resource %s %s", v.list.GetName(), res) if err := v.list.Resource().Delete(res, cascade, force); err != nil { v.app.Flash().Errf("Delete failed with %s", err) } else { From 9307f428426710eb7f6ac2f84f2347f7a6d26274 Mon Sep 17 00:00:00 2001 From: Bruno Meneguello <1322552+bkmeneguello@users.noreply.github.com> Date: Thu, 7 Nov 2019 18:33:41 -0300 Subject: [PATCH 4/5] Replace mark shortcut to space --- internal/ui/menu.go | 2 ++ internal/views/resource.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/ui/menu.go b/internal/ui/menu.go index b09a3c4a..a45eafcc 100644 --- a/internal/ui/menu.go +++ b/internal/ui/menu.go @@ -204,6 +204,7 @@ const ( KeyHelp = 63 KeySlash = 47 KeyColon = 58 + KeySpace = 32 ) // Define Shift Keys @@ -253,6 +254,7 @@ var NumKeys = map[int]int32{ func initKeys() { tcell.KeyNames[tcell.Key(KeyHelp)] = "?" tcell.KeyNames[tcell.Key(KeySlash)] = "/" + tcell.KeyNames[tcell.Key(KeySpace)] = "space" initNumbKeys() initStdKeys() diff --git a/internal/views/resource.go b/internal/views/resource.go index 494dd939..5da2b784 100644 --- a/internal/views/resource.go +++ b/internal/views/resource.go @@ -394,7 +394,7 @@ func (v *resourceView) refreshActions() { tcell.KeyEnter: ui.NewKeyAction("Enter", v.enterCmd, false), tcell.KeyCtrlR: ui.NewKeyAction("Refresh", v.refreshCmd, false), } - aa[tcell.KeyCtrlSpace] = ui.NewKeyAction("Mark", v.markCmd, true) + aa[ui.KeySpace] = ui.NewKeyAction("Mark", v.markCmd, true) v.namespaceActions(aa) v.defaultActions(aa) From b73bd7cecf91a7b5fd3abb643570240c0944475b Mon Sep 17 00:00:00 2001 From: Bruno Meneguello <1322552+bkmeneguello@users.noreply.github.com> Date: Fri, 8 Nov 2019 09:58:30 -0300 Subject: [PATCH 5/5] Move mark logic to Table --- internal/resource/list.go | 19 ------------------- internal/ui/table.go | 17 ++++++++++++++--- internal/views/resource.go | 3 +-- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/internal/resource/list.go b/internal/resource/list.go index 0fa03995..cf8c2620 100644 --- a/internal/resource/list.go +++ b/internal/resource/list.go @@ -66,7 +66,6 @@ type ( Rows RowEvents NumCols map[string]bool Namespace string - Marks map[string]bool } // List protocol to display and update a collection of resources @@ -85,7 +84,6 @@ type ( SetFieldSelector(string) SetLabelSelector(string) HasSelectors() bool - ToggleMark(sk string) } // Columnar tracks resources that can be diplayed in a tabular fashion. @@ -129,15 +127,9 @@ type ( verbs int resource Resource cache RowEvents - marks map[string]bool } ) -// IsMarked checks if key is marked. -func (t *TableData) IsMarked(sk string) bool { - return t.Marks[sk] -} - func newRowEvent(a watch.EventType, f, d Row) *RowEvent { return &RowEvent{Action: a, Fields: f, Deltas: d} } @@ -150,7 +142,6 @@ func NewList(ns, name string, res Resource, verbs int) *list { verbs: verbs, resource: res, cache: RowEvents{}, - marks: make(map[string]bool), } } @@ -241,7 +232,6 @@ func (l *list) Data() TableData { Rows: l.cache, NumCols: l.resource.NumCols(l.namespace), Namespace: l.namespace, - Marks: l.marks, } } @@ -353,19 +343,10 @@ func (l *list) ensureDeletes(kk []string) { } if !found { delete(l.cache, k) - l.removeMark(k) } } } -func (l *list) removeMark(sk string) { - delete(l.marks, sk) -} - -func (l *list) ToggleMark(sk string) { - l.marks[sk] = !l.marks[sk] -} - // Helpers... func computeDeltas(evt *RowEvent, newRow, deltas Row) watch.EventType { diff --git a/internal/ui/table.go b/internal/ui/table.go index 5815acb0..4c2e0489 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -42,6 +42,7 @@ type Table struct { selectedRow int selectedFn func(string) string selListeners []SelectedRowFunc + marks map[string]bool } // NewTable returns a new table view. @@ -53,6 +54,7 @@ func NewTable(title string, styles *config.Styles) *Table { cmdBuff: NewCmdBuff('/', FilterBuff), baseTitle: title, sortCol: SortColumn{0, 0, true}, + marks: make(map[string]bool), } v.SetFixed(1, 0) @@ -171,9 +173,9 @@ func (v *Table) GetSelectedItem() string { // GetSelectedItems return currently marked or selected items names. func (v *Table) GetSelectedItems() []string { - if len(v.data.Marks) > 0 { + if len(v.marks) > 0 { var items []string - for item, marked := range v.data.Marks { + for item, marked := range v.marks { if marked { items = append(items, item) } @@ -332,7 +334,7 @@ func (v *Table) buildRow(row int, data resource.TableData, sk string, pads MaxyP if v.colorerFn != nil { f = v.colorerFn } - m := data.IsMarked(sk) + m := v.isMarked(sk) for col, field := range data.Rows[sk].Fields { header := data.Header[col] field, align := v.formatCell(data.NumCols[header], header, field+Deltas(data.Rows[sk].Deltas[col], field), pads[col]) @@ -540,3 +542,12 @@ func (v *Table) SortInvertCmd(evt *tcell.EventKey) *tcell.EventKey { return nil } + +// ToggleMark toggles marked row +func (v *Table) ToggleMark() { + v.marks[v.GetSelectedItem()] = !v.marks[v.GetSelectedItem()] +} + +func (v *Table) isMarked(item string) bool { + return v.marks[item] +} diff --git a/internal/views/resource.go b/internal/views/resource.go index 5da2b784..929bd69a 100644 --- a/internal/views/resource.go +++ b/internal/views/resource.go @@ -225,8 +225,7 @@ func (v *resourceView) markCmd(evt *tcell.EventKey) *tcell.EventKey { return evt } - sel := v.masterPage().GetSelectedItem() - v.list.ToggleMark(sel) + v.masterPage().ToggleMark() v.refresh() v.app.Draw() return nil