From f10337e429e26c2fb2dfd95dc922dcc9fd4eb928 Mon Sep 17 00:00:00 2001 From: Jeff <46704233+phaedrus-jaf@users.noreply.github.com> Date: Thu, 24 Dec 2020 13:13:38 -0800 Subject: [PATCH] Keybinding system for global keys (#135) Co-authored-by: makeworld <25111343+makeworld-the-better-one@users.noreply.github.com> --- config/config.go | 44 +++++++- config/default.go | 47 ++++++++- config/default.sh | 8 +- config/keybindings.go | 239 +++++++++++++++++++++++++++++++++++++++--- default-config.toml | 47 ++++++++- display/display.go | 155 +++++++++++---------------- display/help.go | 76 ++++++++++---- 7 files changed, 473 insertions(+), 143 deletions(-) diff --git a/config/config.go b/config/config.go index 9184648..b7639de 100644 --- a/config/config.go +++ b/config/config.go @@ -194,7 +194,46 @@ func Init() error { viper.SetDefault("a-general.page_max_size", 2097152) viper.SetDefault("a-general.page_max_time", 10) viper.SetDefault("a-general.emoji_favicons", false) - viper.SetDefault("keybindings.shift_numbers", "!@#$%^&*()") + viper.SetDefault("keybindings.bind_reload", []string{"R", "Ctrl-R"}) + viper.SetDefault("keybindings.bind_home", "Backspace") + viper.SetDefault("keybindings.bind_bookmarks", "Ctrl-B") + viper.SetDefault("keybindings.bind_add_bookmark", "Ctrl-D") + viper.SetDefault("keybindings.bind_sub", "Ctrl-A") + viper.SetDefault("keybindings.bind_add_sub", "Ctrl-X") + viper.SetDefault("keybindings.bind_save", "Ctrl-S") + viper.SetDefault("keybindings.bind_pgup", []string{"PgUp", "u"}) + viper.SetDefault("keybindings.bind_pgdn", []string{"PgDn", "d"}) + viper.SetDefault("keybindings.bind_bottom", "Space") + viper.SetDefault("keybindings.bind_edit", "e") + viper.SetDefault("keybindings.bind_back", []string{"b", "Alt-Left"}) + viper.SetDefault("keybindings.bind_forward", []string{"f", "Alt-Right"}) + viper.SetDefault("keybindings.bind_new_tab", "Ctrl-T") + viper.SetDefault("keybindings.bind_close_tab", "Ctrl-W") + viper.SetDefault("keybindings.bind_next_tab", "F2") + viper.SetDefault("keybindings.bind_prev_tab", "F1") + viper.SetDefault("keybindings.bind_quit", []string{"Ctrl-C", "Ctrl-Q", "q"}) + viper.SetDefault("keybindings.bind_help", "?") + viper.SetDefault("keybindings.bind_link1", "1") + viper.SetDefault("keybindings.bind_link2", "2") + viper.SetDefault("keybindings.bind_link3", "3") + viper.SetDefault("keybindings.bind_link4", "4") + viper.SetDefault("keybindings.bind_link5", "5") + viper.SetDefault("keybindings.bind_link6", "6") + viper.SetDefault("keybindings.bind_link7", "7") + viper.SetDefault("keybindings.bind_link8", "8") + viper.SetDefault("keybindings.bind_link9", "9") + viper.SetDefault("keybindings.bind_link0", "0") + viper.SetDefault("keybindings.bind_tab1", "!") + viper.SetDefault("keybindings.bind_tab2", "@") + viper.SetDefault("keybindings.bind_tab3", "#") + viper.SetDefault("keybindings.bind_tab4", "$") + viper.SetDefault("keybindings.bind_tab5", "%") + viper.SetDefault("keybindings.bind_tab6", "^") + viper.SetDefault("keybindings.bind_tab7", "&") + viper.SetDefault("keybindings.bind_tab8", "*") + viper.SetDefault("keybindings.bind_tab9", "(") + viper.SetDefault("keybindings.bind_tab0", ")") + viper.SetDefault("keybindings.shift_numbers", "") viper.SetDefault("url-handlers.other", "off") viper.SetDefault("cache.max_size", 0) viper.SetDefault("cache.max_pages", 20) @@ -211,6 +250,9 @@ func Init() error { return err } + // Setup the key bindings + KeyInit() + // *** Downloads paths, setup, and creation *** // Setup downloads dir diff --git a/config/default.go b/config/default.go index 162565d..2a87114 100644 --- a/config/default.go +++ b/config/default.go @@ -90,12 +90,51 @@ emoji_favicons = false [keybindings] -# In the future there will be more settings here. +# If you have a non-US keyboard, use bind_tab1 through bind_tab0 to +# setup the shift-number bindings: Eg, for US keyboards (the default): +# bind_tab1 = "!" +# bind_tab2 = "@" +# bind_tab3 = "#" +# bind_tab4 = "$" +# bind_tab5 = "%" +# bind_tab6 = "^" +# bind_tab7 = "&" +# bind_tab8 = "*" +# bind_tab9 = "(" +# bind_tab0 = ")" -# Hold down shift and press the numbers on your keyboard (1,2,3,4,5,6,7,8,9,0) to set this up. -# It is default set to be accurate for US keyboards. -shift_numbers = "!@#$%^&*()" +# Whitespace is not allowed in any of the keybindings! Use 'Space' and 'Tab' to bind to those keys. +# Multiple keys can be bound to one command, just use a TOML array. +# To add the Alt modifier, the binding must start with Alt-, should be reasonably universal +# Ctrl- won't work on all keys, see this for a list: +# https://github.com/gdamore/tcell/blob/cb1e5d6fa606/key.go#L83 +# An example of a TOML array for multiple keys being bound to one command is the default +# binding for reload: +# bind_reload = ["R","Ctrl-R"] +# One thing to note here is that "R" is capitalization sensitive, so it means shift-r. +# "Ctrl-R" means both ctrl-r and ctrl-shift-R (this is a quirk of what ctrl-r means on +# an ANSI terminal) + +# The default binding for opening the bottom bar for entering a URL or link number is: +# bind_bottom = "Space" +# This is how to get the Spacebar as a keybinding, if you try to use " ", it won't work. +# And, finally, an example of a simple, unmodified character is: +# bind_edit = "e" +# This binds the "e" key to the command to edit the current URL. + +# The bind_link[1-90] options are for the commands to go to the first 10 links on a page, +# typically these are bound to the number keys: +# bind_link1 = "1" +# bind_link2 = "2" +# bind_link3 = "3" +# bind_link4 = "4" +# bind_link5 = "5" +# bind_link6 = "6" +# bind_link7 = "7" +# bind_link8 = "8" +# bind_link9 = "9" +# bind_link0 = "0" [url-handlers] # Allows setting the commands to run for various URL schemes. diff --git a/config/default.sh b/config/default.sh index 3de772f..b7f96bb 100755 --- a/config/default.sh +++ b/config/default.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash -head -n 3 default.go | tee default.go > /dev/null +cat > default.go <<-EOF +package config + +//go:generate ./default.sh +EOF echo -n 'var defaultConf = []byte(`' >> default.go cat ../default-config.toml >> default.go -echo '`)' >> default.go \ No newline at end of file +echo '`)' >> default.go diff --git a/config/keybindings.go b/config/keybindings.go index e3cd06e..8afd2e3 100644 --- a/config/keybindings.go +++ b/config/keybindings.go @@ -1,24 +1,237 @@ package config import ( - "errors" + "strings" + "github.com/gdamore/tcell" "github.com/spf13/viper" ) -// KeyToNum returns the number on the user's keyboard they pressed, -// using the rune returned when when they press Shift+Num. -// The error is not nil if the provided key is invalid. -func KeyToNum(key rune) (int, error) { - runes := []rune(viper.GetString("keybindings.shift_numbers")) - for i := range runes { - if key == runes[i] { - if i == len(runes)-1 { - // Last key is 0, not 10 - return 0, nil +// NOTE: CmdLink[1-90] and CmdTab[1-90] need to be in-order and consecutive +// This property is used to simplify key handling in display/display.go +type Command int + +const ( + CmdInvalid Command = 0 + CmdLink1 = 1 + CmdLink2 = 2 + CmdLink3 = 3 + CmdLink4 = 4 + CmdLink5 = 5 + CmdLink6 = 6 + CmdLink7 = 7 + CmdLink8 = 8 + CmdLink9 = 9 + CmdLink0 = 10 + CmdTab1 = 11 + CmdTab2 = 12 + CmdTab3 = 13 + CmdTab4 = 14 + CmdTab5 = 15 + CmdTab6 = 16 + CmdTab7 = 17 + CmdTab8 = 18 + CmdTab9 = 19 + CmdTab0 = 20 + CmdBottom = iota + CmdEdit + CmdHome + CmdBookmarks + CmdAddBookmark + CmdSave + CmdReload + CmdBack + CmdForward + CmdPgup + CmdPgdn + CmdNewTab + CmdCloseTab + CmdNextTab + CmdPrevTab + CmdQuit + CmdHelp + CmdSub + CmdAddSub +) + +type keyBinding struct { + key tcell.Key + mod tcell.ModMask + r rune +} + +// Map of active keybindings to commands. +var bindings map[keyBinding]Command + +// inversion of tcell.KeyNames, used to simplify config parsing. +// used by parseBinding() below. +var tcellKeys map[string]tcell.Key + +// helper function that takes a single keyBinding object and returns +// a string in the format used by the configuration file. Support +// function for GetKeyBinding(), used to make the help panel helpful. +func keyBindingToString(kb keyBinding) (string, bool) { + var prefix string = "" + + if kb.mod&tcell.ModAlt == tcell.ModAlt { + prefix = "Alt-" + } + + if kb.key == tcell.KeyRune { + if kb.r == ' ' { + return prefix + "Space", true + } + return prefix + string(kb.r), true + } + s, ok := tcell.KeyNames[kb.key] + if ok { + return prefix + s, true + } + return "", false +} + +// Get all keybindings for a Command as a string. +// Used by the help panel so bindable keys display with their +// bound values rather than hardcoded defaults. +func GetKeyBinding(cmd Command) string { + var s string = "" + for kb, c := range bindings { + if c == cmd { + t, ok := keyBindingToString(kb) + if ok { + s += t + ", " } - return i + 1, nil } } - return -1, errors.New("provided key is invalid") //nolint:goerr113 + + if len(s) > 0 { + return s[:len(s)-2] + } + return s +} + +// Parse a single keybinding string and add it to the binding map +func parseBinding(cmd Command, binding string) { + var k tcell.Key + var m tcell.ModMask = 0 + var r rune = 0 + + if strings.HasPrefix(binding, "Alt-") { + m = tcell.ModAlt + binding = binding[4:] + } + + if len(binding) == 1 { + k = tcell.KeyRune + r = []rune(binding)[0] + } else if len(binding) == 0 { + return + } else if binding == "Space" { + k = tcell.KeyRune + r = ' ' + } else { + var ok bool + k, ok = tcellKeys[binding] + if !ok { // Bad keybinding! Quietly ignore... + return + } + if strings.HasPrefix(binding, "Ctrl") { + m += tcell.ModCtrl + } + } + + bindings[keyBinding{k, m, r}] = cmd +} + +// Generate the bindings map from the TOML configuration file. +// Called by config.Init() +func KeyInit() { + configBindings := map[Command]string{ + CmdLink1: "keybindings.bind_link1", + CmdLink2: "keybindings.bind_link2", + CmdLink3: "keybindings.bind_link3", + CmdLink4: "keybindings.bind_link4", + CmdLink5: "keybindings.bind_link5", + CmdLink6: "keybindings.bind_link6", + CmdLink7: "keybindings.bind_link7", + CmdLink8: "keybindings.bind_link8", + CmdLink9: "keybindings.bind_link9", + CmdLink0: "keybindings.bind_link0", + CmdBottom: "keybindings.bind_bottom", + CmdEdit: "keybindings.bind_edit", + CmdHome: "keybindings.bind_home", + CmdBookmarks: "keybindings.bind_bookmarks", + CmdAddBookmark: "keybindings.bind_add_bookmark", + CmdSave: "keybindings.bind_save", + CmdReload: "keybindings.bind_reload", + CmdBack: "keybindings.bind_back", + CmdForward: "keybindings.bind_forward", + CmdPgup: "keybindings.bind_pgup", + CmdPgdn: "keybindings.bind_pgdn", + CmdNewTab: "keybindings.bind_new_tab", + CmdCloseTab: "keybindings.bind_close_tab", + CmdNextTab: "keybindings.bind_next_tab", + CmdPrevTab: "keybindings.bind_prev_tab", + CmdQuit: "keybindings.bind_quit", + CmdHelp: "keybindings.bind_help", + CmdSub: "keybindings.bind_sub", + CmdAddSub: "keybindings.bind_add_sub", + } + // This is split off to allow shift_numbers to override bind_tab[1-90] + // (This is needed for older configs so that the default bind_tab values + // aren't used) + configTabNBindings := map[Command]string{ + CmdTab1: "keybindings.bind_tab1", + CmdTab2: "keybindings.bind_tab2", + CmdTab3: "keybindings.bind_tab3", + CmdTab4: "keybindings.bind_tab4", + CmdTab5: "keybindings.bind_tab5", + CmdTab6: "keybindings.bind_tab6", + CmdTab7: "keybindings.bind_tab7", + CmdTab8: "keybindings.bind_tab8", + CmdTab9: "keybindings.bind_tab9", + CmdTab0: "keybindings.bind_tab0", + } + tcellKeys = make(map[string]tcell.Key) + bindings = make(map[keyBinding]Command) + + for k, kname := range tcell.KeyNames { + tcellKeys[kname] = k + } + + for c, allb := range configBindings { + for _, b := range viper.GetStringSlice(allb) { + parseBinding(c, b) + } + } + + // Backwards compatibility with the old shift_numbers config line. + shiftNumbers := []rune(viper.GetString("keybindings.shift_numbers")) + if len(shiftNumbers) > 0 && len(shiftNumbers) <= 10 { + for i, r := range shiftNumbers { + bindings[keyBinding{tcell.KeyRune, 0, r}] = CmdTab1 + Command(i) + } + } else { + for c, allb := range configTabNBindings { + for _, b := range viper.GetStringSlice(allb) { + parseBinding(c, b) + } + } + } +} + +// Used by the display package to turn a tcell.EventKey into a Command +func TranslateKeyEvent(e *tcell.EventKey) Command { + var ok bool + var cmd Command + k := e.Key() + if k == tcell.KeyRune { + cmd, ok = bindings[keyBinding{k, e.Modifiers(), e.Rune()}] + } else { // Sometimes tcell sets e.Rune() on non-KeyRune events. + cmd, ok = bindings[keyBinding{k, e.Modifiers(), 0}] + } + if ok { + return cmd + } + return CmdInvalid } diff --git a/default-config.toml b/default-config.toml index c815c37..07ad69e 100644 --- a/default-config.toml +++ b/default-config.toml @@ -87,12 +87,51 @@ emoji_favicons = false [keybindings] -# In the future there will be more settings here. +# If you have a non-US keyboard, use bind_tab1 through bind_tab0 to +# setup the shift-number bindings: Eg, for US keyboards (the default): +# bind_tab1 = "!" +# bind_tab2 = "@" +# bind_tab3 = "#" +# bind_tab4 = "$" +# bind_tab5 = "%" +# bind_tab6 = "^" +# bind_tab7 = "&" +# bind_tab8 = "*" +# bind_tab9 = "(" +# bind_tab0 = ")" -# Hold down shift and press the numbers on your keyboard (1,2,3,4,5,6,7,8,9,0) to set this up. -# It is default set to be accurate for US keyboards. -shift_numbers = "!@#$%^&*()" +# Whitespace is not allowed in any of the keybindings! Use 'Space' and 'Tab' to bind to those keys. +# Multiple keys can be bound to one command, just use a TOML array. +# To add the Alt modifier, the binding must start with Alt-, should be reasonably universal +# Ctrl- won't work on all keys, see this for a list: +# https://github.com/gdamore/tcell/blob/cb1e5d6fa606/key.go#L83 +# An example of a TOML array for multiple keys being bound to one command is the default +# binding for reload: +# bind_reload = ["R","Ctrl-R"] +# One thing to note here is that "R" is capitalization sensitive, so it means shift-r. +# "Ctrl-R" means both ctrl-r and ctrl-shift-R (this is a quirk of what ctrl-r means on +# an ANSI terminal) + +# The default binding for opening the bottom bar for entering a URL or link number is: +# bind_bottom = "Space" +# This is how to get the Spacebar as a keybinding, if you try to use " ", it won't work. +# And, finally, an example of a simple, unmodified character is: +# bind_edit = "e" +# This binds the "e" key to the command to edit the current URL. + +# The bind_link[1-90] options are for the commands to go to the first 10 links on a page, +# typically these are bound to the number keys: +# bind_link1 = "1" +# bind_link2 = "2" +# bind_link3 = "3" +# bind_link4 = "4" +# bind_link5 = "5" +# bind_link6 = "6" +# bind_link7 = "7" +# bind_link8 = "8" +# bind_link9 = "9" +# bind_link0 = "0" [url-handlers] # Allows setting the commands to run for various URL schemes. diff --git a/display/display.go b/display/display.go index 1b9f41d..ffc8b17 100644 --- a/display/display.go +++ b/display/display.go @@ -272,43 +272,36 @@ func Init(version, commit, builtBy string) { return event } + // To add a configurable global key command, you'll need to update one of + // the two switch statements here. You'll also need to add an enum entry in + // config/keybindings.go, update KeyInit() in config/keybindings.go, add a default + // keybinding in config/config.go and update the help panel in display/help.go + + cmd := config.TranslateKeyEvent(event) if tabs[curTab].mode == tabModeDone { // All the keys and operations that can only work while NOT loading - - // History arrow keys - if event.Modifiers() == tcell.ModAlt { - if event.Key() == tcell.KeyLeft { - histBack(tabs[curTab]) - return nil - } - if event.Key() == tcell.KeyRight { - histForward(tabs[curTab]) - return nil - } - } - //nolint:exhaustive - switch event.Key() { - case tcell.KeyCtrlR: + switch cmd { + case config.CmdReload: Reload() return nil - case tcell.KeyCtrlH: + case config.CmdHome: URL(viper.GetString("a-general.home")) return nil - case tcell.KeyCtrlB: + case config.CmdBookmarks: Bookmarks(tabs[curTab]) tabs[curTab].addToHistory("about:bookmarks") return nil - case tcell.KeyCtrlD: + case config.CmdAddBookmark: go addBookmark() return nil - case tcell.KeyPgUp: + case config.CmdPgup: tabs[curTab].pageUp() return nil - case tcell.KeyPgDn: + case config.CmdPgdn: tabs[curTab].pageDown() return nil - case tcell.KeyCtrlS: + case config.CmdSave: if tabs[curTab].hasContent() { savePath, err := downloadPage(tabs[curTab].page) if err != nil { @@ -320,66 +313,48 @@ func Init(version, commit, builtBy string) { Info("The current page has no content, so it couldn't be downloaded.") } return nil - case tcell.KeyCtrlA: + case config.CmdBottom: + // Space starts typing, like Bombadillo + bottomBar.SetLabel("[::b]URL/Num./Search: [::-]") + bottomBar.SetText("") + // Don't save bottom bar, so that whenever you switch tabs, it's not in that mode + App.SetFocus(bottomBar) + return nil + case config.CmdEdit: + // Letter e allows to edit current URL + bottomBar.SetLabel("[::b]Edit URL: [::-]") + bottomBar.SetText(tabs[curTab].page.URL) + App.SetFocus(bottomBar) + return nil + case config.CmdBack: + histBack(tabs[curTab]) + return nil + case config.CmdForward: + histForward(tabs[curTab]) + return nil + case config.CmdSub: Subscriptions(tabs[curTab], "about:subscriptions") tabs[curTab].addToHistory("about:subscriptions") return nil - case tcell.KeyCtrlX: + case config.CmdAddSub: go addSubscription() return nil - case tcell.KeyRune: - // Regular key was sent - switch string(event.Rune()) { - case " ": - // Space starts typing, like Bombadillo - bottomBar.SetLabel("[::b]URL/Num./Search: [::-]") - bottomBar.SetText("") - // Don't save bottom bar, so that whenever you switch tabs, it's not in that mode - App.SetFocus(bottomBar) - return nil - case "e": - // Letter e allows to edit current URL - bottomBar.SetLabel("[::b]Edit URL: [::-]") - bottomBar.SetText(tabs[curTab].page.URL) - App.SetFocus(bottomBar) - return nil - case "R": - Reload() - return nil - case "b": - histBack(tabs[curTab]) - return nil - case "f": - histForward(tabs[curTab]) - return nil - case "u": - tabs[curTab].pageUp() - return nil - case "d": - tabs[curTab].pageDown() - return nil - } + } - // Number key: 1-9, 0 - i, err := strconv.Atoi(string(event.Rune())) - if err == nil { - if i == 0 { - i = 10 // 0 key is for link 10 - } - if i <= len(tabs[curTab].page.Links) && i > 0 { - // It's a valid link number - followLink(tabs[curTab], tabs[curTab].page.URL, tabs[curTab].page.Links[i-1]) - return nil - } + // Number key: 1-9, 0, LINK1-LINK10 + if cmd >= config.CmdLink1 && cmd <= config.CmdLink0 { + if int(cmd) <= len(tabs[curTab].page.Links) { + // It's a valid link number + followLink(tabs[curTab], tabs[curTab].page.URL, tabs[curTab].page.Links[cmd-1]) + return nil } } } // All the keys and operations that can work while a tab IS loading - //nolint:exhaustive - switch event.Key() { - case tcell.KeyCtrlT: + switch cmd { + case config.CmdNewTab: if tabs[curTab].page.Mode == structs.ModeLinkSelect { next, err := resolveRelLink(tabs[curTab], tabs[curTab].page.URL, tabs[curTab].page.Selected) if err != nil { @@ -392,45 +367,33 @@ func Init(version, commit, builtBy string) { NewTab() } return nil - case tcell.KeyCtrlW: + case config.CmdCloseTab: CloseTab() return nil - case tcell.KeyCtrlQ: + case config.CmdQuit: Stop() return nil - case tcell.KeyCtrlC: - Stop() - return nil - case tcell.KeyF1: + case config.CmdPrevTab: // Wrap around, allow for modulo with negative numbers n := NumTabs() SwitchTab((((curTab - 1) % n) + n) % n) return nil - case tcell.KeyF2: + case config.CmdNextTab: SwitchTab((curTab + 1) % NumTabs()) return nil - case tcell.KeyRune: - // Regular key was sent + case config.CmdHelp: + Help() + return nil + } - if num, err := config.KeyToNum(event.Rune()); err == nil { - // It's a Shift+Num key - if num == 0 { - // Zero key goes to the last tab - SwitchTab(NumTabs() - 1) - } else { - SwitchTab(num - 1) - } - return nil - } - - switch string(event.Rune()) { - case "q": - Stop() - return nil - case "?": - Help() - return nil + if cmd >= config.CmdTab1 && cmd <= config.CmdTab0 { + if cmd == config.CmdTab0 { + // Zero key goes to the last tab + SwitchTab(NumTabs() - 1) + } else { + SwitchTab(int(cmd - config.CmdTab1)) } + return nil } // Let another element handle the event, it's not a special global key diff --git a/display/help.go b/display/help.go index 4128d39..20da364 100644 --- a/display/help.go +++ b/display/help.go @@ -1,10 +1,12 @@ package display import ( + "fmt" "strconv" "strings" "github.com/gdamore/tcell" + "github.com/makeworld-the-better-one/amfora/config" "gitlab.com/tslocum/cview" ) @@ -12,41 +14,39 @@ var helpCells = strings.TrimSpace(` ?|Bring up this help. You can scroll! Esc|Leave the help Arrow keys, h/j/k/l|Scroll and move a page. -PgUp, u|Go up a page in document -PgDn, d|Go down a page in document +%s|Go up a page in document +%s|Go down a page in document g|Go to top of document G|Go to bottom of document Tab|Navigate to the next item in a popup. Shift-Tab|Navigate to the previous item in a popup. -b, Alt-Left|Go back in the history -f, Alt-Right|Go forward in the history -spacebar|Open bar at the bottom - type a URL, link number, search term. +%s|Go back in the history +%s|Go forward in the history +%s|Open bar at the bottom - type a URL, link number, search term. |You can also type two dots (..) to go up a directory in the URL. |Typing new:N will open link number N in a new tab |instead of the current one. -Numbers|Go to links 1-10 respectively. -e|Edit current URL +%s|Go to links 1-10 respectively. +%s|Edit current URL Enter, Tab|On a page this will start link highlighting. |Press Tab and Shift-Tab to pick different links. |Press Enter again to go to one, or Esc to stop. -Shift-NUMBER|Go to a specific tab. -Shift-0, )|Go to the last tab. -F1|Previous tab -F2|Next tab -Ctrl-H|Go home -Ctrl-T|New tab, or if a link is selected, +%s|Go to a specific tab. (Default: Shift-NUMBER) +%s|Go to the last tab. +%s|Previous tab +%s|Next tab +%s|Go home +%s|New tab, or if a link is selected, |this will open the link in a new tab. -Ctrl-W|Close tab. For now, only the right-most tab can be closed. -Ctrl-R, R|Reload a page, discarding the cached version. +%s|Close tab. For now, only the right-most tab can be closed. +%s|Reload a page, discarding the cached version. |This can also be used if you resize your terminal. -Ctrl-B|View bookmarks -Ctrl-D|Add, change, or remove a bookmark for the current page. -Ctrl-S|Save the current page to your downloads. -Ctrl-A|View subscriptions -Ctrl-X|Add or update a subscription -q, Ctrl-Q|Quit -Ctrl-C|Hard quit. This can be used when in the middle of downloading, -|for example. +%s|View bookmarks +%s|Add, change, or remove a bookmark for the current page. +%s|Save the current page to your downloads. +%s|View subscriptions +%s|Add or update a subscription +%s|Quit `) var helpTable = cview.NewTable(). @@ -71,6 +71,36 @@ func helpInit() { App.Draw() } }) + + tabKeys := fmt.Sprintf("%s to %s", strings.Split(config.GetKeyBinding(config.CmdTab1), ",")[0], + strings.Split(config.GetKeyBinding(config.CmdTab9), ",")[0]) + linkKeys := fmt.Sprintf("%s to %s", strings.Split(config.GetKeyBinding(config.CmdLink1), ",")[0], + strings.Split(config.GetKeyBinding(config.CmdLink0), ",")[0]) + + helpCells = fmt.Sprintf(helpCells, + config.GetKeyBinding(config.CmdPgup), + config.GetKeyBinding(config.CmdPgdn), + config.GetKeyBinding(config.CmdBack), + config.GetKeyBinding(config.CmdForward), + config.GetKeyBinding(config.CmdBottom), + linkKeys, + config.GetKeyBinding(config.CmdEdit), + tabKeys, + config.GetKeyBinding(config.CmdTab0), + config.GetKeyBinding(config.CmdPrevTab), + config.GetKeyBinding(config.CmdNextTab), + config.GetKeyBinding(config.CmdHome), + config.GetKeyBinding(config.CmdNewTab), + config.GetKeyBinding(config.CmdCloseTab), + config.GetKeyBinding(config.CmdReload), + config.GetKeyBinding(config.CmdBookmarks), + config.GetKeyBinding(config.CmdAddBookmark), + config.GetKeyBinding(config.CmdSave), + config.GetKeyBinding(config.CmdSub), + config.GetKeyBinding(config.CmdAddSub), + config.GetKeyBinding(config.CmdQuit), + ) + rows := strings.Count(helpCells, "\n") + 1 cells := strings.Split( strings.ReplaceAll(helpCells, "\n", "|"),