2017-01-27 4 views
1

Я изучаю приложение CLI в Go.Выполнение автоматического автозаполнения для приложения CLI

Одно из требований - автоматическое завершение. Не из самой команды, а из возможных вариантов.

Представьте, что я хочу добавить новую запись с помощью CLI. Каждая запись может иметь категорию. Категории доступны в разрезе. Теперь я хочу сделать так, чтобы пользователь мог вводить доступные категории при вводе add.

Мне известны библиотеки, такие как https://github.com/chzyer/readline и https://github.com/spf13/cobra, но не смогли найти, поддерживают ли они или как они это поддерживают.

+1

Что вы подразумеваете под "CLI"? Т.е. пользователь запустит вашу программу, а затем будет работать в «интеративной оболочке» в ней или будет ли пользователь вводить полную команду в системной оболочке, и ваша программа делает это и выходит? – ain

+0

@ хороший Хороший вопрос. Я об этом не думал. Это не имеет значения для прецедента. Поэтому: что лучше реализовать. – Chris

+0

Два примера, на которые вы указали, очень разные: одна реализация _is_ readline и одна использует функции автозаполнения bash (которая использует другую реализацию readline). – JimB

ответ

0

Спасибо @ain и @JimB за указание меня в правильном направлении.

На примере, приведенном в https://github.com/chzyer/readline/tree/master/example/readline-demo, я смог достичь желаемой функциональности.

0: newEntry и newCategory. Если пользователь набирает 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 
}