2017-01-27 55 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 
}