Path: blob/main/vendor/github.com/chzyer/readline/readline.go
2875 views
// Readline is a pure go implementation for GNU-Readline kind library.1//2// example:3// rl, err := readline.New("> ")4// if err != nil {5// panic(err)6// }7// defer rl.Close()8//9// for {10// line, err := rl.Readline()11// if err != nil { // io.EOF12// break13// }14// println(line)15// }16//17package readline1819import (20"io"21)2223type Instance struct {24Config *Config25Terminal *Terminal26Operation *Operation27}2829type Config struct {30// prompt supports ANSI escape sequence, so we can color some characters even in windows31Prompt string3233// readline will persist historys to file where HistoryFile specified34HistoryFile string35// specify the max length of historys, it's 500 by default, set it to -1 to disable history36HistoryLimit int37DisableAutoSaveHistory bool38// enable case-insensitive history searching39HistorySearchFold bool4041// AutoCompleter will called once user press TAB42AutoComplete AutoCompleter4344// Any key press will pass to Listener45// NOTE: Listener will be triggered by (nil, 0, 0) immediately46Listener Listener4748Painter Painter4950// If VimMode is true, readline will in vim.insert mode by default51VimMode bool5253InterruptPrompt string54EOFPrompt string5556FuncGetWidth func() int5758Stdin io.ReadCloser59StdinWriter io.Writer60Stdout io.Writer61Stderr io.Writer6263EnableMask bool64MaskRune rune6566// erase the editing line after user submited it67// it use in IM usually.68UniqueEditLine bool6970// filter input runes (may be used to disable CtrlZ or for translating some keys to different actions)71// -> output = new (translated) rune and true/false if continue with processing this one72FuncFilterInputRune func(rune) (rune, bool)7374// force use interactive even stdout is not a tty75FuncIsTerminal func() bool76FuncMakeRaw func() error77FuncExitRaw func() error78FuncOnWidthChanged func(func())79ForceUseInteractive bool8081// private fields82inited bool83opHistory *opHistory84opSearch *opSearch85}8687func (c *Config) useInteractive() bool {88if c.ForceUseInteractive {89return true90}91return c.FuncIsTerminal()92}9394func (c *Config) Init() error {95if c.inited {96return nil97}98c.inited = true99if c.Stdin == nil {100c.Stdin = NewCancelableStdin(Stdin)101}102103c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin)104105if c.Stdout == nil {106c.Stdout = Stdout107}108if c.Stderr == nil {109c.Stderr = Stderr110}111if c.HistoryLimit == 0 {112c.HistoryLimit = 500113}114115if c.InterruptPrompt == "" {116c.InterruptPrompt = "^C"117} else if c.InterruptPrompt == "\n" {118c.InterruptPrompt = ""119}120if c.EOFPrompt == "" {121c.EOFPrompt = "^D"122} else if c.EOFPrompt == "\n" {123c.EOFPrompt = ""124}125126if c.AutoComplete == nil {127c.AutoComplete = &TabCompleter{}128}129if c.FuncGetWidth == nil {130c.FuncGetWidth = GetScreenWidth131}132if c.FuncIsTerminal == nil {133c.FuncIsTerminal = DefaultIsTerminal134}135rm := new(RawMode)136if c.FuncMakeRaw == nil {137c.FuncMakeRaw = rm.Enter138}139if c.FuncExitRaw == nil {140c.FuncExitRaw = rm.Exit141}142if c.FuncOnWidthChanged == nil {143c.FuncOnWidthChanged = DefaultOnWidthChanged144}145146return nil147}148149func (c Config) Clone() *Config {150c.opHistory = nil151c.opSearch = nil152return &c153}154155func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {156c.Listener = FuncListener(f)157}158159func (c *Config) SetPainter(p Painter) {160c.Painter = p161}162163func NewEx(cfg *Config) (*Instance, error) {164t, err := NewTerminal(cfg)165if err != nil {166return nil, err167}168rl := t.Readline()169if cfg.Painter == nil {170cfg.Painter = &defaultPainter{}171}172return &Instance{173Config: cfg,174Terminal: t,175Operation: rl,176}, nil177}178179func New(prompt string) (*Instance, error) {180return NewEx(&Config{Prompt: prompt})181}182183func (i *Instance) ResetHistory() {184i.Operation.ResetHistory()185}186187func (i *Instance) SetPrompt(s string) {188i.Operation.SetPrompt(s)189}190191func (i *Instance) SetMaskRune(r rune) {192i.Operation.SetMaskRune(r)193}194195// change history persistence in runtime196func (i *Instance) SetHistoryPath(p string) {197i.Operation.SetHistoryPath(p)198}199200// readline will refresh automatic when write through Stdout()201func (i *Instance) Stdout() io.Writer {202return i.Operation.Stdout()203}204205// readline will refresh automatic when write through Stdout()206func (i *Instance) Stderr() io.Writer {207return i.Operation.Stderr()208}209210// switch VimMode in runtime211func (i *Instance) SetVimMode(on bool) {212i.Operation.SetVimMode(on)213}214215func (i *Instance) IsVimMode() bool {216return i.Operation.IsEnableVimMode()217}218219func (i *Instance) GenPasswordConfig() *Config {220return i.Operation.GenPasswordConfig()221}222223// we can generate a config by `i.GenPasswordConfig()`224func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {225return i.Operation.PasswordWithConfig(cfg)226}227228func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {229return i.Operation.PasswordEx(prompt, l)230}231232func (i *Instance) ReadPassword(prompt string) ([]byte, error) {233return i.Operation.Password(prompt)234}235236type Result struct {237Line string238Error error239}240241func (l *Result) CanContinue() bool {242return len(l.Line) != 0 && l.Error == ErrInterrupt243}244245func (l *Result) CanBreak() bool {246return !l.CanContinue() && l.Error != nil247}248249func (i *Instance) Line() *Result {250ret, err := i.Readline()251return &Result{ret, err}252}253254// err is one of (nil, io.EOF, readline.ErrInterrupt)255func (i *Instance) Readline() (string, error) {256return i.Operation.String()257}258259func (i *Instance) ReadlineWithDefault(what string) (string, error) {260i.Operation.SetBuffer(what)261return i.Operation.String()262}263264func (i *Instance) SaveHistory(content string) error {265return i.Operation.SaveHistory(content)266}267268// same as readline269func (i *Instance) ReadSlice() ([]byte, error) {270return i.Operation.Slice()271}272273// we must make sure that call Close() before process exit.274// if there has a pending reading operation, that reading will be interrupted.275// so you can capture the signal and call Instance.Close(), it's thread-safe.276func (i *Instance) Close() error {277i.Config.Stdin.Close()278i.Operation.Close()279if err := i.Terminal.Close(); err != nil {280return err281}282return nil283}284285// call CaptureExitSignal when you want readline exit gracefully.286func (i *Instance) CaptureExitSignal() {287CaptureExitSignal(func() {288i.Close()289})290}291292func (i *Instance) Clean() {293i.Operation.Clean()294}295296func (i *Instance) Write(b []byte) (int, error) {297return i.Stdout().Write(b)298}299300// WriteStdin prefill the next Stdin fetch301// Next time you call ReadLine() this value will be writen before the user input302// ie :303// i := readline.New()304// i.WriteStdin([]byte("test"))305// _, _= i.Readline()306//307// gives308//309// > test[cursor]310func (i *Instance) WriteStdin(val []byte) (int, error) {311return i.Terminal.WriteStdin(val)312}313314func (i *Instance) SetConfig(cfg *Config) *Config {315if i.Config == cfg {316return cfg317}318old := i.Config319i.Config = cfg320i.Operation.SetConfig(cfg)321i.Terminal.SetConfig(cfg)322return old323}324325func (i *Instance) Refresh() {326i.Operation.Refresh()327}328329// HistoryDisable the save of the commands into the history330func (i *Instance) HistoryDisable() {331i.Operation.history.Disable()332}333334// HistoryEnable the save of the commands into the history (default on)335func (i *Instance) HistoryEnable() {336i.Operation.history.Enable()337}338339340