Danke @ain und @JimB, dass Sie mich in die richtige Richtung weisen.
Basierend auf dem Beispiel https://github.com/chzyer/readline/tree/master/example/readline-demo konnte ich die gewünschte Funktionalität erreichen. Der folgende Code muss die Hauptbefehle newEntry
und newCategory
sein. Wenn der Benutzer newEntry
eintippt und dann TAB drückt, kann er aus den verfügbaren Kategorien auswählen. Der Befehl newCategory
ermöglicht das Hinzufügen einer neuen benutzerdefinierten Kategorie, die sofort verfügbar ist, wenn das nächste Mal newEntry
ausgeführt wird.
package main
import (
"io"
"log"
"strconv"
"strings"
"github.com/chzyer/readline"
)
// completer defines which commands the user can use
var completer = readline.NewPrefixCompleter()
// categories holding the initial default categories. The user can add categories.
var categories = []string{"Category A", "Category B", "Category C"}
var l *readline.Instance
func main() {
// Initialize config
config := readline.Config{
Prompt: "\033[31m»\033[0m ",
HistoryFile: "/tmp/readline.tmp",
AutoComplete: completer,
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
}
var err error
// Create instance
l, err = readline.NewEx(&config)
if err != nil {
panic(err)
}
defer l.Close()
// Initial initialization of the completer
updateCompleter(categories)
log.SetOutput(l.Stderr())
// This loop watches for user input and process it
for {
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
break
} else {
continue
}
} else if err == io.EOF {
break
}
line = strings.TrimSpace(line)
// Checking which command the user typed
switch {
// Add new category
case strings.HasPrefix(line, "newCategory"):
// Remove the "newCategory " prefix (including space)
if len(line) <= 12 {
log.Println("newCategory <NameOfCategory>")
break
}
// Append everything that comes behind the command as the name of the new category
categories = append(categories, line[12:])
// Update the completer to make the new category available in the cmd
updateCompleter(categories)
// Program is closed when user types "exit"
case line == "exit":
goto exit
// Log all commands we don't know
default:
log.Println("Unknown command:", strconv.Quote(line))
}
}
exit:
}
// updateCompleter is updates the completer allowing to add new command during runtime. The completer is recreated
// and the configuration of the instance update.
func updateCompleter(categories []string) {
var items []readline.PrefixCompleterInterface
for _, category := range categories {
items = append(items, readline.PcItem(category))
}
completer = readline.NewPrefixCompleter(
readline.PcItem("newEntry",
items...,
),
readline.PcItem("newCategory"),
)
l.Config.AutoComplete = completer
}
Was meinen Sie mit "CLI"? Dh wird der Benutzer Ihr Programm starten und dann in "interactive shell" arbeiten oder wird der Benutzer den vollen Befehl in der System-Shell eingeben und Ihr Programm macht es und beendet es? – ain
@ain Gute Frage. Daran habe ich nicht gedacht. Es spielt keine Rolle für den Anwendungsfall. Deshalb: Was ist besser zu implementieren? – Chris
Die beiden Beispiele, auf die Sie verwiesen haben, sind sehr unterschiedlich, eine _ist_ eine readline-Implementierung und eine die Autovervollständigungsfunktionen von bash (die eine andere readline-Implementierung verwendet). – JimB