实现CLI应用程序的自动自动完成功能

时间:2017-01-27 13:36:36

标签: go command-line-interface

我正在研究在Go中编写CLI应用程序。

其中一项要求是自动完成。不是命令本身,而是可能的选项。

想象一下,我想使用CLI添加一个新条目。每个条目都可以有一个类别。 这些类别以切片形式提供。我现在要做的是让用户在键入add时标记可用的类别。

我知道像https://github.com/chzyer/readlinehttps://github.com/spf13/cobra这样的图书馆,却无法找到他们是否或如何支持这一点。

1 个答案:

答案 0 :(得分:0)

感谢@ain和@JimB指出我正确的方向。

根据https://github.com/chzyer/readline/tree/master/example/readline-demo提供的示例,我能够实现所需的功能。

以下代码包含主要命令newEntrynewCategory。如果用户键入newEntry而不是按TAB,他可以从可用类别中进行选择。 newCategory命令允许添加新的自定义类别,该类别在下次newEntry执行时立即可用。

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
}