k9s/internal/view/help.go

244 lines
4.4 KiB
Go

package view
import (
"context"
"fmt"
"runtime"
"sort"
"strconv"
"strings"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const (
helpTitle = "Help"
helpTitleFmt = " [aqua::b]%s "
)
type helpItem struct {
key, description string
}
// Help presents a help viewer.
type Help struct {
*ui.Table
app *App
actions ui.KeyActions
}
// NewHelp returns a new help viewer.
func NewHelp() *Help {
return &Help{
Table: ui.NewTable(helpTitle),
actions: make(ui.KeyActions),
}
}
func (v *Help) Init(ctx context.Context) {
v.app = ctx.Value(ui.KeyApp).(*App)
v.resetTitle()
v.SetBorder(true)
v.SetBorderPadding(0, 0, 1, 1)
v.SetInputCapture(v.keyboard)
v.bindKeys()
v.build(v.app.Hint.Peek())
}
func (v *Help) Name() string { return helpTitle }
func (v *Help) Start() {}
func (v *Help) Stop() {}
func (v *Help) Hints() model.MenuHints { return v.actions.Hints() }
func (v *Help) bindKeys() {
v.actions = ui.KeyActions{
tcell.KeyEsc: ui.NewKeyAction("Back", v.backCmd, true),
tcell.KeyEnter: ui.NewKeyAction("Back", v.backCmd, false),
}
}
func (v *Help) keyboard(evt *tcell.EventKey) *tcell.EventKey {
key := evt.Key()
if key == tcell.KeyRune {
key = tcell.Key(evt.Rune())
}
if a, ok := v.actions[key]; ok {
log.Debug().Msgf(">> TableView handled %s", tcell.KeyNames[key])
return a.Action(evt)
}
return evt
}
func (v *Help) backCmd(evt *tcell.EventKey) *tcell.EventKey {
return v.app.PrevCmd(evt)
}
func (v *Help) showHelp() model.MenuHints {
return model.MenuHints{
{
Mnemonic: "?",
Description: "Help",
},
{
Mnemonic: "Ctrl-a",
Description: "Aliases",
},
}
}
func (v *Help) showNav() model.MenuHints {
return model.MenuHints{
{
Mnemonic: "g",
Description: "Goto Top",
},
{
Mnemonic: "Shift-g",
Description: "Goto Bottom",
},
{
Mnemonic: "Ctrl-b",
Description: "Page Down"},
{
Mnemonic: "Ctrl-f",
Description: "Page Up",
},
{
Mnemonic: "h",
Description: "Left",
},
{
Mnemonic: "l",
Description: "Right",
},
{
Mnemonic: "k",
Description: "Up",
},
{
Mnemonic: "j",
Description: "Down",
},
}
}
func (v *Help) showGeneral() model.MenuHints {
return model.MenuHints{
{
Mnemonic: ":cmd",
Description: "Command mode",
},
{
Mnemonic: "/term",
Description: "Filter mode",
},
{
Mnemonic: "esc",
Description: "Clear filter",
},
{
Mnemonic: "tab",
Description: "Next Field",
},
{
Mnemonic: "backtab",
Description: "Previous Field",
},
{
Mnemonic: "Ctrl-r",
Description: "Refresh",
},
{
Mnemonic: "h",
Description: "Toggle Header",
},
{
Mnemonic: "Shift-i",
Description: "Invert Sort",
},
{
Mnemonic: "p",
Description: "Previous View",
},
{
Mnemonic: ":q",
Description: "Quit",
},
}
}
func (v *Help) getTitle() string {
return helpTitle
}
func (v *Help) resetTitle() {
v.SetTitle(fmt.Sprintf(helpTitleFmt, helpTitle))
}
func (v *Help) build(hh model.MenuHints) {
v.Clear()
sort.Sort(hh)
v.addSection(0, 0, "RESOURCE", hh)
v.addSection(0, 4, "GENERAL", v.showGeneral())
v.addSection(0, 6, "NAVIGATION", v.showNav())
v.addSection(0, 8, "HELP", v.showHelp())
}
func (v *Help) addSection(r, c int, title string, hh model.MenuHints) {
row := r
cell := tview.NewTableCell(title)
cell.SetTextColor(tcell.ColorGreen)
cell.SetAttributes(tcell.AttrBold)
cell.SetExpansion(2)
cell.SetAlign(tview.AlignLeft)
v.SetCell(r, c+1, cell)
row++
for _, h := range hh {
col := c
cell := tview.NewTableCell(toMnemonic(h.Mnemonic))
if _, err := strconv.Atoi(h.Mnemonic); err != nil {
cell.SetTextColor(tcell.ColorDodgerBlue)
} else {
cell.SetTextColor(tcell.ColorFuchsia)
}
cell.SetAttributes(tcell.AttrBold)
cell.SetAlign(tview.AlignRight)
v.SetCell(row, col, cell)
col++
cell = tview.NewTableCell(h.Description)
cell.SetTextColor(tcell.ColorWhite)
v.SetCell(row, col, cell)
row++
}
}
func toMnemonic(s string) string {
if len(s) == 0 {
return s
}
return "<" + keyConv(strings.ToLower(s)) + ">"
}
func keyConv(s string) string {
if !strings.Contains(s, "alt") {
return s
}
if runtime.GOOS != "darwin" {
return s
}
return strings.Replace(s, "alt", "opt", 1)
}