مكتبة لبناء مطالبات تفاعلية ويمكن الوصول إليها على المحطات التي تدعم تسلسل ANSI Escape.
يا الجميع! لقد توصلت أخيرًا إلى حقيقة أنه لم يعد بإمكاني تكريس الوقت الكافي للحفاظ على هذه المكتبة على قيد الحياة. لقد تفوق هذا المشروع على أعنف توقعاتي وكان تجربة رائعة. إذا أراد شخص آخر تولي الصيانة ، فيرجى التواصل
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 و 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 للقيام بأسفل وأعلى على التوالي.
بشكل افتراضي ، يقتصر موجه تحديد على عرض 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 ))يمكن استخدام نص الوصف الاختياري لإضافة معلومات إضافية إلى كل خيار مدرج في موجه تحديد:
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 للقيام بأسفل وأعلى على التوالي.
بشكل افتراضي ، يقتصر موجه متعدد المراحل على عرض 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 أو $ $) على ملف مؤقت. بمجرد خروج المستخدم من المحرر ، تتم قراءة محتويات الملف المؤقت كنتيجة. في حالة عدم وجود أي من هؤلاء ، يتم استخدام المفكرة (على Windows) أو VIM (Linux أو MAC).
يمكنك أيضًا تحديد نمط لاسم الملف المؤقت. يمكن أن يكون هذا مفيدًا لضمان تمييز بناء الجملة يطابق usecase الخاص بك.
prompt := & survey. Editor {
Message : "Shell code snippet" ,
FileName : "*.sh" ,
}
survey . AskOne ( prompt , & content )بشكل افتراضي ، يمكن للمستخدم تصفية الخيارات في تحديدات التحديد والمتعددة عن طريق الكتابة أثناء نشطة المطالبة. سيؤدي ذلك إلى تصفية جميع الخيارات التي لا تحتوي على السلسلة المكتوبة في أي مكان باسمهم ، متجاهلة الحالة.
يمكن أيضًا توفير وظيفة مرشح مخصصة لتغيير هذا السلوك:
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 ))بشكل افتراضي ، سيختفي المرشح إذا حدد المستخدم أحد العناصر التي تمت تصفيتها. بمجرد قيام المستخدم بتحديد عنصر واحد ، يتم تخفيض إعداد المرشح.
ومع ذلك ، يمكن للمستخدم منع ذلك من الحدوث والحفاظ على مرشح نشط لتحديدات متعددة في EG MultiSelect:
// 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 مع بعض المدققين لتناسب المواقف المشتركة. حاليا هذه المدققات تتضمن:
| اسم | أنواع صالحة | وصف | ملحوظات |
|---|---|---|---|
| مطلوب | أي | يرفض قيم الصفر من نوع الاستجابة | تمر القيم المنطقية مباشرة لأن قيمة الصفر (خطأ) هي استجابة صالحة |
| الطول (ن) | خيط | يفرض أن الاستجابة هي على الأقل الطول المعطى | |
| MaxLength (N) | خيط | يفرض أن الرد لم يعد من الطول المعطى | |
| ماكسيميس (ن) | [] OptionAnswer | يفرض أن الاستجابة ليس لها اختيارات أخرى من المشار إليها | |
| Minitems (N) | [] OptionAnswer | يفرض أن الاستجابة ليس لها اختيارات أقل من المشار إليها |
تحتوي جميع المطالبات على حقل 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"
}))تم تلخيص الرموز والنص الافتراضي وتنسيقها أدناه:
| اسم | نص | شكل | وصف |
|---|---|---|---|
| خطأ | x | أحمر | قبل خطأ |
| يساعد | أنا | سماوي | قبل مساعدة النص |
| سؤال | ؟ | الأخضر+HB | قبل رسالة موجه |
| SelectFocus | > | أخضر | يمثل التركيز الحالي في المطالبات Select MultiSelect |
| unmarkedoption | [] | افتراضي+HB | يمثل خيارًا غير محدد في موجه MultiSelect |
| markedoption | [x] | Cyan+b | يمثل اختيار مختار في مطالبة 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 ، فستحتاج إلى طريقة لتفسير تسلسل الهروب الطرفي / ANSI لأشياء مثل CursorLocation . سيقوم vt10x.NewVT10XConsole بإنشاء وحدة تحكم go-expect التي تعدد أيضًا stdio إلى محطة افتراضية في الذاكرة.
لبعض الأمثلة ، يمكنك رؤية أي من الاختبارات في هذا الريبو.
survey ؟يهدف المسح إلى دعم معظم المحاكيات الطرفية ؛ وتتوقع دعم تسلسل ANSI Escape. هذا يعني أن القراءة من stdin piped أو الكتابة إلى stdout الأنابيب غير مدعومة ، ومن المحتمل أن تحطم تطبيقك في هذه المواقف. انظر #337
عادةً ، عندما تكتب CTRL-C ، تتعرف المحطة على ذلك على أنه زر الإقلاع عن التدخين ويقدم إشارة sigint إلى العملية ، والتي تنهيها. ومع ذلك ، يقوم المسح بتكوين المحطة مؤقتًا لتقديم رموز التحكم كبايتات إدخال عادية. عندما يقرأ المسح A ^C Byte (ASCII X03 ، "End of Text") ، فإنه يقطع المسح الحالي ويعيد github.com/AlecAivazis/survey/v2/terminal.InterruptErr من Ask أو AskOne . إذا كنت ترغب في إيقاف العملية ، تعامل مع الخطأ الذي تم إرجاعه في الكود الخاص بك:
err := survey . AskOne ( prompt , & myVar )
if err != nil {
if err == terminal. InterruptErr {
log . Fatal ( "interrupted" )
}
...
}