review: display actual bindings for commands
Parse the actual user bindings to determine what shortcuts are available in the compose::review stage. Add a predefined list of commands for which we want to display the keyboard shortcuts. Fixes: https://todo.sr.ht/~rjarry/aerc/14 Signed-off-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
parent
923e949c05
commit
77b3a141a4
2 changed files with 99 additions and 8 deletions
|
@ -118,6 +118,53 @@ func (bindings *KeyBindings) GetBinding(
|
||||||
return BINDING_NOT_FOUND, nil
|
return BINDING_NOT_FOUND, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bindings *KeyBindings) GetReverseBindings(output []KeyStroke) [][]KeyStroke {
|
||||||
|
var inputs [][]KeyStroke
|
||||||
|
|
||||||
|
for _, binding := range bindings.bindings {
|
||||||
|
if len(binding.Output) != len(output) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i, stroke := range output {
|
||||||
|
if stroke.Modifiers != binding.Output[i].Modifiers {
|
||||||
|
goto next
|
||||||
|
}
|
||||||
|
if stroke.Key != binding.Output[i].Key {
|
||||||
|
goto next
|
||||||
|
}
|
||||||
|
if stroke.Key == tcell.KeyRune && stroke.Rune != binding.Output[i].Rune {
|
||||||
|
goto next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputs = append(inputs, binding.Input)
|
||||||
|
next:
|
||||||
|
}
|
||||||
|
return inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatKeyStrokes(keystrokes []KeyStroke) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
for _, stroke := range keystrokes {
|
||||||
|
s := ""
|
||||||
|
for name, ks := range keyNames {
|
||||||
|
if ks.Modifiers == stroke.Modifiers && ks.Key == stroke.Key && ks.Rune == stroke.Rune {
|
||||||
|
if name == "cr" {
|
||||||
|
name = "enter"
|
||||||
|
}
|
||||||
|
s = fmt.Sprintf("<%s>", name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s == "" && stroke.Key == tcell.KeyRune {
|
||||||
|
s = string(stroke.Rune)
|
||||||
|
}
|
||||||
|
sb.WriteString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
keyNames map[string]KeyStroke
|
keyNames map[string]KeyStroke
|
||||||
)
|
)
|
||||||
|
|
|
@ -946,11 +946,48 @@ type reviewMessage struct {
|
||||||
grid *ui.Grid
|
grid *ui.Grid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reviewCommands = [][]string{
|
||||||
|
{":send<enter>", "Send"},
|
||||||
|
{":edit<enter>", "Edit"},
|
||||||
|
{":attach<space>", "Add attachment"},
|
||||||
|
{":detach<space>", "Remove attachment"},
|
||||||
|
{":postpone<enter>", "Postpone"},
|
||||||
|
{":abort<enter>", "Abort (discard message, no confirmation)"},
|
||||||
|
{":choose -o d discard abort -o p postpone postpone<enter>", "Abort or postpone"},
|
||||||
|
}
|
||||||
|
|
||||||
func newReviewMessage(composer *Composer, err error) *reviewMessage {
|
func newReviewMessage(composer *Composer, err error) *reviewMessage {
|
||||||
|
bindings := composer.config.MergeContextualBinds(
|
||||||
|
composer.config.Bindings.ComposeReview,
|
||||||
|
config.BIND_CONTEXT_ACCOUNT,
|
||||||
|
composer.acctConfig.Name,
|
||||||
|
"compose::review",
|
||||||
|
)
|
||||||
|
|
||||||
|
var actions []string
|
||||||
|
|
||||||
|
for _, command := range reviewCommands {
|
||||||
|
cmd := command[0]
|
||||||
|
name := command[1]
|
||||||
|
strokes, _ := config.ParseKeyStrokes(cmd)
|
||||||
|
var inputs []string
|
||||||
|
for _, input := range bindings.GetReverseBindings(strokes) {
|
||||||
|
inputs = append(inputs, config.FormatKeyStrokes(input))
|
||||||
|
}
|
||||||
|
if len(inputs) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
actions = append(actions, fmt.Sprintf(" %-6s %s", strings.Join(inputs[:], ", "), name))
|
||||||
|
}
|
||||||
|
|
||||||
spec := []ui.GridSpec{
|
spec := []ui.GridSpec{
|
||||||
{ui.SIZE_EXACT, ui.Const(2)},
|
|
||||||
{ui.SIZE_EXACT, ui.Const(1)},
|
{ui.SIZE_EXACT, ui.Const(1)},
|
||||||
}
|
}
|
||||||
|
for i := 0; i < len(actions)-1; i++ {
|
||||||
|
spec = append(spec, ui.GridSpec{ui.SIZE_EXACT, ui.Const(1)})
|
||||||
|
}
|
||||||
|
spec = append(spec, ui.GridSpec{ui.SIZE_EXACT, ui.Const(2)})
|
||||||
|
spec = append(spec, ui.GridSpec{ui.SIZE_EXACT, ui.Const(1)})
|
||||||
for i := 0; i < len(composer.attachments)-1; i++ {
|
for i := 0; i < len(composer.attachments)-1; i++ {
|
||||||
spec = append(spec, ui.GridSpec{ui.SIZE_EXACT, ui.Const(1)})
|
spec = append(spec, ui.GridSpec{ui.SIZE_EXACT, ui.Const(1)})
|
||||||
}
|
}
|
||||||
|
@ -968,18 +1005,25 @@ func newReviewMessage(composer *Composer, err error) *reviewMessage {
|
||||||
grid.AddChild(ui.NewText("Press [q] to close this tab.",
|
grid.AddChild(ui.NewText("Press [q] to close this tab.",
|
||||||
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(1, 0)
|
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(1, 0)
|
||||||
} else {
|
} else {
|
||||||
// TODO: source this from actual keybindings?
|
grid.AddChild(ui.NewText("Send this email?",
|
||||||
grid.AddChild(ui.NewText("Send this email? [y]es/[n]o/[e]dit/[a]ttach",
|
uiConfig.GetStyle(config.STYLE_TITLE))).At(0, 0)
|
||||||
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(0, 0)
|
i := 1
|
||||||
|
for _, action := range actions {
|
||||||
|
grid.AddChild(ui.NewText(action,
|
||||||
|
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i, 0)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
grid.AddChild(ui.NewText("Attachments:",
|
grid.AddChild(ui.NewText("Attachments:",
|
||||||
uiConfig.GetStyle(config.STYLE_TITLE))).At(1, 0)
|
uiConfig.GetStyle(config.STYLE_TITLE))).At(i, 0)
|
||||||
|
i += 1
|
||||||
if len(composer.attachments) == 0 {
|
if len(composer.attachments) == 0 {
|
||||||
grid.AddChild(ui.NewText("(none)",
|
grid.AddChild(ui.NewText("(none)",
|
||||||
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(2, 0)
|
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i, 0)
|
||||||
} else {
|
} else {
|
||||||
for i, a := range composer.attachments {
|
for _, a := range composer.attachments {
|
||||||
grid.AddChild(ui.NewText(a, uiConfig.GetStyle(config.STYLE_DEFAULT))).
|
grid.AddChild(ui.NewText(a, uiConfig.GetStyle(config.STYLE_DEFAULT))).
|
||||||
At(i+2, 0)
|
At(i, 0)
|
||||||
|
i += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue