From 77b3a141a4459a1e59583f0d9fee46e871466d74 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Wed, 2 Feb 2022 20:47:54 +0100 Subject: [PATCH] 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 --- config/bindings.go | 47 ++++++++++++++++++++++++++++++++++++ widgets/compose.go | 60 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 99 insertions(+), 8 deletions(-) diff --git a/config/bindings.go b/config/bindings.go index b704fa6..7ff0c24 100644 --- a/config/bindings.go +++ b/config/bindings.go @@ -118,6 +118,53 @@ func (bindings *KeyBindings) GetBinding( 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 ( keyNames map[string]KeyStroke ) diff --git a/widgets/compose.go b/widgets/compose.go index 46d4025..1693314 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -946,11 +946,48 @@ type reviewMessage struct { grid *ui.Grid } +var reviewCommands = [][]string{ + {":send", "Send"}, + {":edit", "Edit"}, + {":attach", "Add attachment"}, + {":detach", "Remove attachment"}, + {":postpone", "Postpone"}, + {":abort", "Abort (discard message, no confirmation)"}, + {":choose -o d discard abort -o p postpone postpone", "Abort or postpone"}, +} + 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{ - {ui.SIZE_EXACT, ui.Const(2)}, {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++ { 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.", uiConfig.GetStyle(config.STYLE_DEFAULT))).At(1, 0) } else { - // TODO: source this from actual keybindings? - grid.AddChild(ui.NewText("Send this email? [y]es/[n]o/[e]dit/[a]ttach", - uiConfig.GetStyle(config.STYLE_DEFAULT))).At(0, 0) + grid.AddChild(ui.NewText("Send this email?", + uiConfig.GetStyle(config.STYLE_TITLE))).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:", - uiConfig.GetStyle(config.STYLE_TITLE))).At(1, 0) + uiConfig.GetStyle(config.STYLE_TITLE))).At(i, 0) + i += 1 if len(composer.attachments) == 0 { grid.AddChild(ui.NewText("(none)", - uiConfig.GetStyle(config.STYLE_DEFAULT))).At(2, 0) + uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i, 0) } else { - for i, a := range composer.attachments { + for _, a := range composer.attachments { grid.AddChild(ui.NewText(a, uiConfig.GetStyle(config.STYLE_DEFAULT))). - At(i+2, 0) + At(i, 0) + i += 1 } } }