Files
aerc-fork-mirror/commands/help.go
Robin Jarry 8ea6b4386d help: list pages dynamically from mandb
Instead of having a hard-coded list of help topics, change the :help
command completion to list available man pages as listed by:

	man -k aerc

Append each man page short description to the completion choices.

In case mandb isn't up to date, or if running aerc without installing
it, fallback to a hard-coded list. I didn't try to walk the "doc" folder
since the working directory isn't guaranteed.

Also, in order for this to work properly, mandb must be executed after
installing the man pages. This isn't part of the makefile since it would
cause issues in downstream builds which install in staging directories.

Add a mention of mandb in the installation instructions.

Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Antonin Godard <antonin@godard.cc>
2025-12-11 21:37:09 +01:00

115 lines
2.4 KiB
Go

package commands
import (
"fmt"
"os/exec"
"regexp"
"sort"
"strings"
"git.sr.ht/~rjarry/aerc/app"
)
type Help struct {
Topic string `opt:"topic" action:"ParseTopic" default:"aerc" complete:"CompleteTopic" desc:"Help topic."`
}
func init() {
Register(Help{})
}
func (Help) Description() string {
return "Display one of aerc's man pages in the embedded terminal."
}
func (Help) Context() CommandContext {
return GLOBAL
}
func (Help) Aliases() []string {
return []string{"help", "man"}
}
var aproposRe = regexp.MustCompile(`(?m)^aerc(?:-([a-z]+))?\s+\([0-9]\)\s+-\s+(.+)$`)
func getTopics() map[string]string {
topics := map[string]string{"keys": "Display contextual key bindings."}
cmd := exec.Command("man", "-k", "aerc")
out, err := cmd.CombinedOutput()
if err != nil {
// mandb not up to date, return "something"
topics["aerc"] = ""
topics["accounts"] = ""
topics["binds"] = ""
topics["config"] = ""
topics["imap"] = ""
topics["jmap"] = ""
topics["maildir"] = ""
topics["notmuch"] = ""
topics["patch"] = ""
topics["search"] = ""
topics["sendmail"] = ""
topics["smtp"] = ""
topics["stylesets"] = ""
topics["templates"] = ""
topics["tutorial"] = ""
return topics
}
for _, match := range aproposRe.FindAllSubmatch(out, -1) {
name := string(match[1])
if name == "" {
name = "aerc"
}
desc := strings.ReplaceAll(string(match[2]), " for aerc(1)", "")
if !strings.HasSuffix(desc, ".") {
desc += "."
}
desc = strings.ToUpper(desc[0:1]) + desc[1:]
topics[name] = desc
}
return topics
}
func (*Help) CompleteTopic(arg string) []string {
var pages []string
for name, desc := range getTopics() {
if desc != "" {
name += "\n" + desc
}
pages = append(pages, name)
}
sort.Strings(pages)
return FilterList(pages, arg, nil)
}
func (h *Help) ParseTopic(arg string) error {
topics := getTopics()
if _, ok := topics[arg]; ok {
if arg != "aerc" {
arg = "aerc-" + arg
}
h.Topic = arg
return nil
}
return fmt.Errorf("unknown topic %q", arg)
}
func (h Help) Execute(args []string) error {
if h.Topic == "aerc-keys" {
app.AddDialog(app.DefaultDialog(
app.NewListBox(
"Bindings: Press <Esc> or <Enter> to close. "+
"Start typing to filter bindings.",
app.HumanReadableBindings(),
app.SelectedAccountUiConfig(),
func(_ string) {
app.CloseDialog()
},
),
))
return nil
}
term := Term{Cmd: []string{"man", h.Topic}}
return term.Execute(args)
}