Skip to content

Commit

Permalink
Adds support for custom commands (#265)
Browse files Browse the repository at this point in the history
* Adds support for custom commands

Supports up to 10 custom commands, defined in the configuration file.
The current or selected URL can be passed to the command by placing
${url} in the command string.

Default hotkeys are Alt-1 through Alt-0 for triggering the command
with the selected link URL as a parameter, or Alt-Shift-1 through
Alt-Shift-0 for triggering the command with the current page URL
as a parameter. This follows the convention set by the copy page
URL (C) and copy selected URL (c) defaults.

Custom commands must be available in the $PATH or use absolute
paths. Relative paths are not supported, neither are pipes or
shell redirections.

* Fix linting issues

* Fix typo

* Further clarify comment

* Use goroutines for Error/Info calls

* Run go fmt to fix issue from merge

---------

Co-authored-by: makeworld <[email protected]>
  • Loading branch information
mntn-xyz and makew0rld authored Dec 19, 2024
1 parent b5c921c commit fd8f122
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 27 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Features in *italics* are in the master branch, but not in the latest release.
- Ability to stream content instead of downloading it first
- Highlighting of preformatted code blocks that list a language in the alt text
- *Search in pages with <kbd>Ctrl-F</kbd>*
- *Run custom commands using the current or selected URL as an argument*


## Usage & Configuration
Expand Down
22 changes: 22 additions & 0 deletions command/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package command

import (
"os/exec"
"strings"
)

// RunCommand runs `command`, replacing the string "${url}" with `url`.
func RunCommand(command string, url string) (string, error) {
cmdWithURL := strings.ReplaceAll(command, "${url}", url)
cmdSplit := strings.SplitN(cmdWithURL, " ", 2)
if len(cmdSplit) > 1 {
if err := exec.Command(cmdSplit[0], cmdSplit[1]).Start(); err != nil {
return "", err
}
return "Ran command " + cmdSplit[0] + " with args " + cmdSplit[1], nil
}
if err := exec.Command(cmdWithURL).Start(); err != nil {
return "", err
}
return "Ran command " + cmdWithURL, nil
}
30 changes: 30 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ func Init() error {
viper.SetDefault("a-general.page_max_time", 10)
viper.SetDefault("a-general.scrollbar", "auto")
viper.SetDefault("a-general.underline", true)
viper.SetDefault("commands.command1", "")
viper.SetDefault("commands.command2", "")
viper.SetDefault("commands.command3", "")
viper.SetDefault("commands.command4", "")
viper.SetDefault("commands.command5", "")
viper.SetDefault("commands.command6", "")
viper.SetDefault("commands.command7", "")
viper.SetDefault("commands.command8", "")
viper.SetDefault("commands.command9", "")
viper.SetDefault("commands.command0", "")
viper.SetDefault("keybindings.bind_reload", []string{"R", "Ctrl-R"})
viper.SetDefault("keybindings.bind_home", "Backspace")
viper.SetDefault("keybindings.bind_bookmarks", "Ctrl-B")
Expand Down Expand Up @@ -250,6 +260,26 @@ func Init() error {
viper.SetDefault("keybindings.bind_tab8", "*")
viper.SetDefault("keybindings.bind_tab9", "(")
viper.SetDefault("keybindings.bind_tab0", ")")
viper.SetDefault("keybindings.bind_command1", "Alt-!")
viper.SetDefault("keybindings.bind_command2", "Alt-@")
viper.SetDefault("keybindings.bind_command3", "Alt-#")
viper.SetDefault("keybindings.bind_command4", "Alt-$")
viper.SetDefault("keybindings.bind_command5", "Alt-%")
viper.SetDefault("keybindings.bind_command6", "Alt-^")
viper.SetDefault("keybindings.bind_command7", "Alt-&")
viper.SetDefault("keybindings.bind_command8", "Alt-*")
viper.SetDefault("keybindings.bind_command9", "Alt-(")
viper.SetDefault("keybindings.bind_command0", "Alt-)")
viper.SetDefault("keybindings.bind_command_target1", "Alt-1")
viper.SetDefault("keybindings.bind_command_target2", "Alt-2")
viper.SetDefault("keybindings.bind_command_target3", "Alt-3")
viper.SetDefault("keybindings.bind_command_target4", "Alt-4")
viper.SetDefault("keybindings.bind_command_target5", "Alt-5")
viper.SetDefault("keybindings.bind_command_target6", "Alt-6")
viper.SetDefault("keybindings.bind_command_target7", "Alt-7")
viper.SetDefault("keybindings.bind_command_target8", "Alt-8")
viper.SetDefault("keybindings.bind_command_target9", "Alt-9")
viper.SetDefault("keybindings.bind_command_target0", "Alt-0")
viper.SetDefault("keybindings.bind_copy_page_url", "C")
viper.SetDefault("keybindings.bind_copy_target_url", "c")
viper.SetDefault("keybindings.bind_beginning", []string{"Home", "g"})
Expand Down
46 changes: 46 additions & 0 deletions config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ underline = true
# Same as [auth.certs] but the path is to the client key file.
[commands]
# Define up to 10 custom commands to execute on the corresponding hotkey press.
# Commands are run in a new process and will not terminate when Amfora is closed.
# If you need your command to accept additional input, it is recommended to open
# a GUI or use a terminal multiplexer like screen or tmux. The string ${url} will
# be replaced with the current or selected URL. Note that pipes and redirections
# are not allowed, if these are needed then you should set up a script. Use only
# absolute paths and/or reference executables in your $PATH.
# command1 = "my-script -a -b -c ${url}"
# command2 = ""
# command3 = ""
# command4 = ""
# command5 = ""
# command6 = ""
# command7 = ""
# command8 = ""
# command9 = ""
# command0 = ""
[keybindings]
# 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):
Expand Down Expand Up @@ -159,6 +179,32 @@ underline = true
# bind_link9 = "9"
# bind_link0 = "0"
# The bind_command[0-9] options are for the command hotkeys. This will pass the URL of the
# current tab as an argument.
# bind_command1 = "Alt-!"
# bind_command2 = "Alt-@"
# bind_command3 = "Alt-#"
# bind_command4 = "Alt-$"
# bind_command5 = "Alt-%"
# bind_command6 = "Alt-^"
# bind_command7 = "Alt-&"
# bind_command8 = "Alt-*"
# bind_command9 = "Alt-("
# bind_command0 = "Alt-)"
# The bind_commandtarget[0-9] options are for command hotkeys that operate on the currently
# highlighted link. This will pass the URL of the highlighted link as an argument.
# bind_command_target1 = "Alt-1"
# bind_command_target2 = "Alt-2"
# bind_command_target3 = "Alt-3"
# bind_command_target4 = "Alt-4"
# bind_command_target5 = "Alt-5"
# bind_command_target6 = "Alt-6"
# bind_command_target7 = "Alt-7"
# bind_command_target8 = "Alt-8"
# bind_command_target9 = "Alt-9"
# bind_command_target0 = "Alt-0"
# All keybindings:
#
# bind_bottom
Expand Down
96 changes: 69 additions & 27 deletions config/keybindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,55 @@ import (
"github.com/spf13/viper"
)

// 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
// NOTE: CmdLink[1-90], CmdTab[1-90], CmdCommand[1-90], and
// CmdCommandTarget[1-90] need to be in-order and consecutive. This
// property is used to simplify key handling in display/display.go
// and display/tab.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
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
CmdCommand1 = 21
CmdCommand2 = 22
CmdCommand3 = 23
CmdCommand4 = 24
CmdCommand5 = 25
CmdCommand6 = 26
CmdCommand7 = 27
CmdCommand8 = 28
CmdCommand9 = 29
CmdCommand0 = 30
CmdCommandTarget1 = 31
CmdCommandTarget2 = 32
CmdCommandTarget3 = 33
CmdCommandTarget4 = 34
CmdCommandTarget5 = 35
CmdCommandTarget6 = 36
CmdCommandTarget7 = 37
CmdCommandTarget8 = 38
CmdCommandTarget9 = 39
CmdCommandTarget0 = 40
CmdBottom = iota
CmdEdit
CmdHome
CmdBookmarks
Expand Down Expand Up @@ -203,9 +225,29 @@ func KeyInit() {
CmdBeginning: "keybindings.bind_beginning",
CmdEnd: "keybindings.bind_end",
CmdURLHandlerOpen: "keybindings.bind_url_handler_open",
CmdSearch: "keybindings.bind_search",
CmdNextMatch: "keybindings.bind_next_match",
CmdPrevMatch: "keybindings.bind_prev_match",
CmdCommand1: "keybindings.bind_command1",
CmdCommand2: "keybindings.bind_command2",
CmdCommand3: "keybindings.bind_command3",
CmdCommand4: "keybindings.bind_command4",
CmdCommand5: "keybindings.bind_command5",
CmdCommand6: "keybindings.bind_command6",
CmdCommand7: "keybindings.bind_command7",
CmdCommand8: "keybindings.bind_command8",
CmdCommand9: "keybindings.bind_command9",
CmdCommand0: "keybindings.bind_command0",
CmdCommandTarget1: "keybindings.bind_command_target1",
CmdCommandTarget2: "keybindings.bind_command_target2",
CmdCommandTarget3: "keybindings.bind_command_target3",
CmdCommandTarget4: "keybindings.bind_command_target4",
CmdCommandTarget5: "keybindings.bind_command_target5",
CmdCommandTarget6: "keybindings.bind_command_target6",
CmdCommandTarget7: "keybindings.bind_command_target7",
CmdCommandTarget8: "keybindings.bind_command_target8",
CmdCommandTarget9: "keybindings.bind_command_target9",
CmdCommandTarget0: "keybindings.bind_command_target0",
CmdSearch: "keybindings.bind_search",
CmdNextMatch: "keybindings.bind_next_match",
CmdPrevMatch: "keybindings.bind_prev_match",
}
// 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
Expand Down
46 changes: 46 additions & 0 deletions default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,26 @@ underline = true
# Same as [auth.certs] but the path is to the client key file.


[commands]
# Define up to 10 custom commands to execute on the corresponding hotkey press.
# Commands are run in a new process and will not terminate when Amfora is closed.
# If you need your command to accept additional input, it is recommended to open
# a GUI or use a terminal multiplexer like screen or tmux. The string ${url} will
# be replaced with the current or selected URL. Note that pipes and redirections
# are not allowed, if these are needed then you should set up a script. Use only
# absolute paths and/or reference executables in your $PATH.
# command1 = "my-script -a -b -c ${url}"
# command2 = ""
# command3 = ""
# command4 = ""
# command5 = ""
# command6 = ""
# command7 = ""
# command8 = ""
# command9 = ""
# command0 = ""


[keybindings]
# 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):
Expand Down Expand Up @@ -156,6 +176,32 @@ underline = true
# bind_link9 = "9"
# bind_link0 = "0"

# The bind_command[0-9] options are for the command hotkeys. This will pass the URL of the
# current tab as an argument.
# bind_command1 = "Alt-!"
# bind_command2 = "Alt-@"
# bind_command3 = "Alt-#"
# bind_command4 = "Alt-$"
# bind_command5 = "Alt-%"
# bind_command6 = "Alt-^"
# bind_command7 = "Alt-&"
# bind_command8 = "Alt-*"
# bind_command9 = "Alt-("
# bind_command0 = "Alt-)"

# The bind_commandtarget[0-9] options are for command hotkeys that operate on the currently
# highlighted link. This will pass the URL of the highlighted link as an argument.
# bind_command_target1 = "Alt-1"
# bind_command_target2 = "Alt-2"
# bind_command_target3 = "Alt-3"
# bind_command_target4 = "Alt-4"
# bind_command_target5 = "Alt-5"
# bind_command_target6 = "Alt-6"
# bind_command_target7 = "Alt-7"
# bind_command_target8 = "Alt-8"
# bind_command_target9 = "Alt-9"
# bind_command_target0 = "Alt-0"

# All keybindings:
#
# bind_bottom
Expand Down
35 changes: 35 additions & 0 deletions display/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package display

import (
"strconv"

"github.com/makeworld-the-better-one/amfora/command"
"github.com/spf13/viper"
)

// CustomCommand runs custom commands as defined in the app configuration.
// Commands are zero-indexed, so 0 is command1 and 9 is command0 (10).
func CustomCommand(num int, url string) {
if num < 0 {
num = 0
}
num++
if num > 9 {
num = 0
}

cmd := viper.GetString("commands.command" + strconv.Itoa(num))
if len(cmd) > 0 {
msg, err := command.RunCommand(cmd, url)
if err != nil {
go Error("Command Error", err.Error())
return
}
go Info(msg)
} else {
go Error("Command Error", "Command "+strconv.Itoa(num)+" not defined")
return
}

App.Draw()
}
10 changes: 10 additions & 0 deletions display/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ var helpCells = strings.TrimSpace(
"%s\tSave the current page to your downloads.\n" +
"%s\tView subscriptions\n" +
"%s\tAdd or update a subscription\n" +
"%s\tExecute a custom command using the current page URL as an argument.\n" +
"\t(Default: Alt-Shift-NUMBER)\n" +
"%s\tExecute a custom command using the selected URL as an argument.\n" +
"\t(Default: Alt-NUMBER)\n" +
"%s\tSearch the page content for a string\n" +
"%s\tFind next search match\n" +
"%s\tFind previous search match\n" +
Expand Down Expand Up @@ -82,6 +86,10 @@ func helpInit() {
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])
commandKeys := fmt.Sprintf("%s to %s", strings.Split(config.GetKeyBinding(config.CmdCommand1), ",")[0],
strings.Split(config.GetKeyBinding(config.CmdCommand0), ",")[0])
commandTargetKeys := fmt.Sprintf("%s to %s", strings.Split(config.GetKeyBinding(config.CmdCommandTarget1), ",")[0],
strings.Split(config.GetKeyBinding(config.CmdCommandTarget0), ",")[0])

helpCells = fmt.Sprintf(helpCells,
config.GetKeyBinding(config.CmdMoveLeft),
Expand Down Expand Up @@ -113,6 +121,8 @@ func helpInit() {
config.GetKeyBinding(config.CmdSave),
config.GetKeyBinding(config.CmdSub),
config.GetKeyBinding(config.CmdAddSub),
commandKeys,
commandTargetKeys,
config.GetKeyBinding(config.CmdSearch),
config.GetKeyBinding(config.CmdNextMatch),
config.GetKeyBinding(config.CmdPrevMatch),
Expand Down
Loading

0 comments on commit fd8f122

Please sign in to comment.