Библиотека для создания интерактивных и доступных подсказок на терминалах, поддерживающих последовательности Escape ANSI.
Привет всем! Я наконец смирился с тем фактом, что больше не могу посвятить достаточно времени, чтобы сохранить эту библиотеку. Этот проект превзошел мои самые смелые ожидания и был таким замечательным опытом. Если кто -то еще хочет взять на себя управление, пожалуйста, протяните руку
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var qs = [] * survey. Question {
{
Name : "name" ,
Prompt : & survey. Input { Message : "What is your name?" },
Validate : survey . Required ,
Transform : survey . Title ,
},
{
Name : "color" ,
Prompt : & survey. Select {
Message : "Choose a color:" ,
Options : [] string { "red" , "blue" , "green" },
Default : "red" ,
},
},
{
Name : "age" ,
Prompt : & survey. Input { Message : "How old are you?" },
},
}
func main () {
// the answers will be written to this struct
answers := struct {
Name string // survey will match the question and field names
FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
Age int // if the types don't match, survey will convert it
}{}
// perform the questions
err := survey . Ask ( qs , & answers )
if err != nil {
fmt . Println ( err . Error ())
return
}
fmt . Printf ( "%s chose %s." , answers . Name , answers . FavoriteColor )
} Примеры можно найти в examples/ каталоге. Запустите их, чтобы увидеть базовое поведение:
go run examples/simple.go
go run examples/validation.go Существует два основных способа выполнения подсказок и начать собирать информацию у ваших пользователей: Ask and AskOne . Основное отличие состоит в том, заинтересованы ли вы в сборе одной части информации или есть ли у вас список вопросов, которые нужно задать, чьи ответы должны быть собраны в одной структуре. Для большинства основных использования, Ask должно быть достаточно. Тем не менее, для опросов со сложной логикой ветвления мы рекомендуем разбить ваши вопросы на несколько вызовов обеих этих функций в соответствии с вашими потребностями.
Большинство подсказок принимают мелкозернистую конфигурацию через поля на структурах, которые вы создаете. Также возможно изменить поведение по умолчанию, передавая AskOpts , чтобы Ask или AskOne . Примеры в этом документе будут делать оба взаимозаменяемо:
prompt := & Select {
Message : "Choose a color:" ,
Options : [] string { "red" , "blue" , "green" },
// can pass a validator directly
Validate : survey . Required ,
}
// or define a default for the single call to `AskOne`
// the answer will get written to the color variable
survey . AskOne ( prompt , & color , survey . WithValidator ( survey . Required ))
// or define a default for every entry in a list of questions
// the answer will get copied into the matching field of the struct as shown above
survey . Ask ( questions , & answers , survey . WithValidator ( survey . Required )) name := ""
prompt := & survey. Input {
Message : "ping" ,
}
survey . AskOne ( prompt , & name ) file := ""
prompt := & survey. Input {
Message : "inform a file to save:" ,
Suggest : func ( toComplete string ) [] string {
files , _ := filepath . Glob ( toComplete + "*" )
return files
},
}
}
survey . AskOne ( prompt , & file ) text := ""
prompt := & survey. Multiline {
Message : "ping" ,
}
survey . AskOne ( prompt , & text ) password := ""
prompt := & survey. Password {
Message : "Please type your password" ,
}
survey . AskOne ( prompt , & password ) name := false
prompt := & survey. Confirm {
Message : "Do you like pie?" ,
}
survey . AskOne ( prompt , & name ) color := ""
prompt := & survey. Select {
Message : "Choose a color:" ,
Options : [] string { "red" , "blue" , "green" },
}
survey . AskOne ( prompt , & color ) Поля и значения, которые поступают из Select подсказки, могут быть одной из двух разных вещей. Если вы пройдете в int поле будет иметь значение выбранного индекса. Если вы вместо этого передаете строку, выбранное значение строки будет записано в поле.
Пользователь также может нажать esc , чтобы переключить цикл способностей через параметры с клавишами J и K, чтобы сделать вниз и подняться соответственно.
По умолчанию подсказка SELECT ограничена отображением 7 вариантов за раз и будет проведено плюсрочные списки опций дольше, чем это. Это можно изменить несколько способов:
// as a field on a single select
prompt := & survey. MultiSelect { ... , PageSize : 10 }
// or as an option to Ask or AskOne
survey . AskOne ( prompt , & days , survey . WithPageSize ( 10 ))Необязательный текст описания можно использовать для добавления дополнительной информации в каждую опцию, указанную в приглашении SELECT:
color := ""
prompt := & survey. Select {
Message : "Choose a color:" ,
Options : [] string { "red" , "blue" , "green" },
Description : func ( value string , index int ) string {
if value == "red" {
return "My favorite color"
}
return ""
},
}
survey . AskOne ( prompt , & color )
// Assuming that the user chose "red - My favorite color":
fmt . Println ( color ) //=> "red"
days := [] string {}
prompt := & survey. MultiSelect {
Message : "What days do you prefer:" ,
Options : [] string { "Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" },
}
survey . AskOne ( prompt , & days ) Поля и значения, которые поступают из MultiSelect подсказки, могут быть одной из двух разных вещей. Если вы пройдете int , у поля будет кусок выбранных индексов. Если вы вместо этого пропустите строку, в поле будет записан ломтик выбранных значений строки.
Пользователь также может нажать esc , чтобы переключить цикл способностей через параметры с клавишами J и K, чтобы сделать вниз и подняться соответственно.
По умолчанию подсказка Multiselect ограничена отображением 7 вариантов одновременно и будет проведено на страже списка вариантов дольше, чем это. Это можно изменить несколько способов:
// as a field on a single select
prompt := & survey. MultiSelect { ... , PageSize : 10 }
// or as an option to Ask or AskOne
survey . AskOne ( prompt , & days , survey . WithPageSize ( 10 ))Запускает предпочтительный редактор пользователя (определяемый $ Visual Visual или $ Editor Environment Varials) во временном файле. Как только пользователь выходит из своего редактора, содержимое временного файла читается в результате. Если ни один из них не присутствует, используется блокнот (в Windows) или Vim (Linux или Mac).
Вы также можете указать шаблон для имени временного файла. Это может быть полезно для обеспечения синтаксиса, выделяющего совпадения с вашим использованием.
prompt := & survey. Editor {
Message : "Shell code snippet" ,
FileName : "*.sh" ,
}
survey . AskOne ( prompt , & content )По умолчанию пользователь может отфильтровать для параметров в Select и MultiSelects, вводя введите, в то время как подсказка активна. Это будет отфильтровать все параметры, которые не содержат напечатанную строку в любом месте их имени, игнорируя дело.
Индивидуальная функция фильтра также может быть предоставлена для изменения этого поведения:
func myFilter ( filterValue string , optValue string , optIndex int ) bool {
// only include the option if it includes the filter and has length greater than 5
return strings . Contains ( optValue , filterValue ) && len ( optValue ) >= 5
}
// configure it for a specific prompt
& Select {
Message : "Choose a color:" ,
Options : [] string { "red" , "blue" , "green" },
Filter : myFilter ,
}
// or define a default for all of the questions
survey . AskOne ( prompt , & color , survey . WithFilter ( myFilter ))По умолчанию фильтр исчезнет, если пользователь выберет один из фильтрованных элементов. Как только пользователь выбирает один элемент, настройка фильтра исчезла.
Однако пользователь может предотвратить это и сохранить фильтр активным для нескольких выборов в Multielect EG:
// configure it for a specific prompt
& Select {
Message : "Choose a color:" ,
Options : [] string { "light-green" , "green" , "dark-green" , "red" },
KeepFilter : true ,
}
// or define a default for all of the questions
survey . AskOne ( prompt , & color , survey . WithKeepFilter ( true )) Проверка отдельных ответов для конкретного вопроса может быть сделана путем определения Validate поля в survey.Question . Эта функция принимает interface{} тип и возвращает ошибку, чтобы показать пользователю, предлагая их для другого ответа. Как обычно, валидаторы могут быть предоставлены непосредственно для подсказки или с помощью survey.WithValidator :
q := & survey. Question {
Prompt : & survey. Input { Message : "Hello world validation" },
Validate : func ( val interface {}) error {
// since we are validating an Input, the assertion will always succeed
if str , ok := val .( string ) ; ! ok || len ( str ) > 10 {
return errors . New ( "This response cannot be longer than 10 characters." )
}
return nil
},
}
color := ""
prompt := & survey. Input { Message : "Whats your name?" }
// you can pass multiple validators here and survey will make sure each one passes
survey . AskOne ( prompt , & color , survey . WithValidator ( survey . Required )) survey поступает с несколькими валидаторами, чтобы соответствовать общим ситуациям. В настоящее время эти валидаторы включают в себя:
| имя | Допустимые типы | описание | примечания |
|---|---|---|---|
| Необходимый | любой | Отклоняет нулевые значения типа ответа | Логические значения проходят прямо, поскольку нулевое значение (false) является допустимым ответом |
| Minlength (n) | нить | Обеспечивает соблюдение того, что ответ, по крайней мере, данная длина | |
| Maxlength (n) | нить | Обеспечивает соблюдение того, что ответ не дольше данной длины | |
| Maxitems (n) | [] Опцион | Обеспечивает соблюдение того, что ответ больше не имеет выбора указанного | |
| MiniTems (n) | [] Опцион | Обеспечивает соблюдение того, что ответ имеет не меньше выборов указанных |
Все подсказки имеют поле Help , которое можно определить, чтобы предоставить больше информации вашим пользователям:
& survey. Input {
Message : "What is your phone number:" ,
Help : "Phone number should include the area code" ,
} По умолчанию пользователи могут выбрать все параметры с несколькими выборами, используя клавишу со стрелками правой. Чтобы пользователи не могли сделать это (и удалить <right> to all сообщение из приглашения), используйте опцию WithRemoveSelectAll :
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := & survey. Input {
Message : "This question has the select all option removed" ,
}
survey . AskOne ( prompt , & number , survey . WithRemoveSelectAll ()) Также по умолчанию пользователи могут использовать клавишу со стрелками левого, чтобы не определить все параметры. Чтобы пользователи не смогли сделать это (и удалить сообщение <left> to none из приглашения), используйте опцию WithRemoveSelectNone :
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := & survey. Input {
Message : "This question has the select all option removed" ,
}
survey . AskOne ( prompt , & number , survey . WithRemoveSelectNone ()) В некоторых ситуациях ? это совершенно достоверный ответ. Чтобы справиться с этим, вы можете изменить руну, которую ищет опрос с WithHelpInput :
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := & survey. Input {
Message : "If you have this need, please give me a reasonable message." ,
Help : "I couldn't come up with one." ,
}
survey . AskOne ( prompt , & number , survey . WithHelpInput ( '^' )) Изменение значков и их цвет/формат может быть сделано путем прохождения опции WithIcons . Формат следует за шаблонами, изложенными здесь. Например:
import (
"github.com/AlecAivazis/survey/v2"
)
number := ""
prompt := & survey. Input {
Message : "If you have this need, please give me a reasonable message." ,
Help : "I couldn't come up with one." ,
}
survey . AskOne ( prompt , & number , survey . WithIcons ( func ( icons * survey. IconSet ) {
// you can set any icons
icons . Question . Text = "⁇"
// for more information on formatting the icons, see here: https://github.com/mgutz/ansi#style-format
icons . Question . Format = "yellow+hb"
}))Значки и их текст и формат по умолчанию приведены ниже:
| имя | текст | формат | описание |
|---|---|---|---|
| Ошибка | Х | красный | Перед ошибкой |
| Помощь | я | голубой | Перед помощи текст |
| Вопрос | ? | зеленый+Hb | Перед сообщением о подсказывании |
| SELECTFOCUS | > | зеленый | Оценка текущего фокуса в Select и MultiSelect подсказках |
| Unmarkedoption | [] | по умолчанию+hb | Отмечает невыбранную опцию в MultiSelect Ript |
| Markedoption | [x] | Голуб+б | Оценка выбранного выбора в MultiSelect |
Опрос будет назначать оперативные ответы на ваши пользовательские типы, если они реализуют этот интерфейс:
type Settable interface {
WriteAnswer ( field string , value interface {}) error
}Вот пример, как их использовать:
type MyValue struct {
value string
}
func ( my * MyValue ) WriteAnswer ( name string , value interface {}) error {
my . value = value .( string )
}
myval := MyValue {}
survey . AskOne (
& survey. Input {
Message : "Enter something:" ,
},
& myval
) Вы можете проверить интерактивные подсказки вашей программы, используя GO-Expect. Библиотека может быть использована, чтобы ожидать совпадения на stdout и ответить на Stdin. Поскольку os.Stdout в процессе go test не является TTY, если вы манипулируете курсором или используете survey , вам понадобится способ интерпретировать последовательности Escape Terminal / ANSI для таких вещей, как CursorLocation . vt10x.NewVT10XConsole создаст консоль go-expect , которая также мультиплексирует stdio в виртуальную терминал в памяти.
Для некоторых примеров вы можете увидеть любой из тестов в этом репо.
survey ?Обзор направлен на поддержку большинства терминальных эмуляторов; Он ожидает поддержки последовательностей ANSI Escape. Это означает, что чтение из STDIN или написания в STDOUT не поддерживается и, вероятно, нарушит ваше заявление в этих ситуациях. Смотрите #337
Обычно, когда вы вводите Ctrl-C, терминал распознает это как кнопку «Отказаться» и доставляет сигнал Sigint в процесс, который завершает его. Тем не менее, опрос временно настраивает терминал для доставки кодов управления в качестве обычных байтов ввода. Когда обследование читает байт ^c (ASCII x03, «Конец текста»), он прерывает текущий опрос и возвращает github.com/AlecAivazis/survey/v2/terminal.InterruptErr из Ask или AskOne . Если вы хотите остановить процесс, обработайте возвращенную ошибку в вашем коде:
err := survey . AskOne ( prompt , & myVar )
if err != nil {
if err == terminal. InterruptErr {
log . Fatal ( "interrupted" )
}
...
}