2017-01-27 3 views
1

Ich bin dabei, eine CLI-Anwendung in Go zu schreiben.Auto-Autovervollständigung für CLI-Anwendung implementieren

Eine der Anforderungen ist die automatische Vervollständigung. Nicht vom Befehl selbst, sondern von möglichen Optionen.

Stellen Sie sich vor, ich möchte einen neuen Eintrag mithilfe der CLI hinzufügen. Jeder Eintrag kann eine Kategorie haben. Die Kategorien sind in einer Schicht verfügbar. Was ich jetzt tun möchte, ist es, dem Benutzer zu ermöglichen, die verfügbaren Kategorien bei der Eingabe in add durchzublättern.

Ich kenne Bibliotheken wie https://github.com/chzyer/readline und https://github.com/spf13/cobra aber konnte nicht finden, ob oder wie sie dies unterstützen.

+1

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

+0

@ain Gute Frage. Daran habe ich nicht gedacht. Es spielt keine Rolle für den Anwendungsfall. Deshalb: Was ist besser zu implementieren? – Chris

+0

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

Antwort

0

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 
} 
Verwandte Themen