2017-01-27 58 views
1

我正在研究在Go中编写CLI应用程序。为CLI应用程序实现自动自动完成

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

想象一下,我想使用CLI添加一个新条目。每个条目都可以有一个类别。 这些类别可用于切片。我现在想要做的是让用户在输入add时可以选择可用的类别。

我知道类似https://github.com/chzyer/readlinehttps://github.com/spf13/cobra的图书馆,但无法找到他们是否支持此功能。

+1

你是什么意思 “(CLI)” 是什么意思?也就是说,用户将启动你的程序,然后在“interacive shell”中工作,否则用户将在系统shell中输入完整的命令,并且你的程序执行它并退出? – ain

+0

@ain好问题。我没有想过这个。这与用例无关。因此:无论哪个更好实施。 – Chris

+0

您指出的两个示例非常不同,一个是readline实现,另一个使用bash的自动完成功能(使用不同的readline实现)。 – JimB

回答

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 
}