[example] Add a IM example

This commit is contained in:
chzyer 2016-03-31 10:55:53 +08:00
parent 91ba4d48f0
commit 218eb7fff6
6 changed files with 120 additions and 9 deletions

View File

@ -26,6 +26,10 @@ Also works fine in windows
* [example/readline-demo](https://github.com/chzyer/readline/blob/master/example/readline-demo/readline-demo.go) The source code about the demo above
* [example/readline-im](https://github.com/chzyer/readline/blob/master/example/readline-im/readline-im.go) Example for how to write a IM program.
* [example/readline-multiline](https://github.com/chzyer/readline/blob/master/example/readline-multiline/readline-multiline.go) Example for how to parse command which can submit by multiple time.
* [example/readline-pass-strength](https://github.com/chzyer/readline/blob/master/example/readline-pass-strength/readline-pass-strength.go) A example about checking password strength, written by [@sahib](https://github.com/sahib)
# Todo

View File

@ -0,0 +1,60 @@
package main
import (
"fmt"
"math/rand"
"time"
"github.com/chzyer/readline"
)
import "log"
func main() {
rl, err := readline.NewEx(&readline.Config{
UniqueEditLine: true,
})
if err != nil {
panic(err)
}
defer rl.Close()
rl.SetPrompt("username: ")
username, err := rl.Readline()
if err != nil {
return
}
rl.ResetHistory()
log.SetOutput(rl.Stderr())
fmt.Fprintln(rl, "Hi,", username+"! My name is Dave.")
rl.SetPrompt(username + "> ")
done := make(chan struct{})
go func() {
rand.Seed(time.Now().Unix())
loop:
for {
select {
case <-time.After(time.Duration(rand.Intn(20)) * 100 * time.Millisecond):
case <-done:
break loop
}
log.Println("Dave:", "hello")
}
log.Println("Dave:", "bye")
done <- struct{}{}
}()
for {
ln := rl.Line()
if ln.CanContinue() {
continue
} else if ln.CanBreak() {
break
}
log.Println(username+":", ln.Line)
}
rl.Clean()
done <- struct{}{}
<-done
}

View File

@ -37,6 +37,11 @@ func newOpHistory(cfg *Config) (o *opHistory) {
return o
}
func (o *opHistory) Reset() {
o.history = list.New()
o.current = nil
}
func (o *opHistory) IsHistoryClosed() bool {
return o.fd.Fd() == ^(uintptr(0))
}

View File

@ -237,11 +237,16 @@ func (o *Operation) ioloop() {
}
// treat as EOF
if !o.cfg.UniqueEditLine {
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
}
o.buf.Reset()
isUpdateHistory = false
o.history.Revert()
o.errchan <- io.EOF
if o.cfg.UniqueEditLine {
o.buf.Clean()
}
case CharInterrupt:
if o.IsSearchMode() {
o.t.KickRead()
@ -257,9 +262,13 @@ func (o *Operation) ioloop() {
o.buf.MoveToLineEnd()
o.buf.Refresh(nil)
hint := o.cfg.InterruptPrompt + "\n"
if !o.cfg.UniqueEditLine {
o.buf.WriteString(hint)
}
remain := o.buf.Reset()
if !o.cfg.UniqueEditLine {
remain = remain[:len(remain)-len([]rune(hint))]
}
isUpdateHistory = false
o.history.Revert()
o.errchan <- &InterruptError{remain}
@ -419,6 +428,10 @@ func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
return old, nil
}
func (o *Operation) ResetHistory() {
o.history.Reset()
}
// if err is not nil, it just mean it fail to write to file
// other things goes fine.
func (o *Operation) SaveHistory(content string) error {
@ -431,6 +444,10 @@ func (o *Operation) Refresh() {
}
}
func (o *Operation) Clean() {
o.buf.Clean()
}
func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
return &DumpListener{f: f}
}

View File

@ -140,6 +140,10 @@ func New(prompt string) (*Instance, error) {
return NewEx(&Config{Prompt: prompt})
}
func (i *Instance) ResetHistory() {
i.Operation.ResetHistory()
}
func (i *Instance) SetPrompt(s string) {
i.Operation.SetPrompt(s)
}
@ -189,6 +193,24 @@ func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
return i.Operation.Password(prompt)
}
type Result struct {
Line string
Error error
}
func (l *Result) CanContinue() bool {
return len(l.Line) != 0 && l.Error == ErrInterrupt
}
func (l *Result) CanBreak() bool {
return !l.CanContinue() && l.Error != nil
}
func (i *Instance) Line() *Result {
ret, err := i.Readline()
return &Result{ret, err}
}
// err is one of (nil, io.EOF, readline.ErrInterrupt)
func (i *Instance) Readline() (string, error) {
return i.Operation.String()
@ -211,6 +233,9 @@ func (i *Instance) Close() error {
i.Operation.Close()
return nil
}
func (i *Instance) Clean() {
i.Operation.Clean()
}
func (i *Instance) Write(b []byte) (int, error) {
return i.Stdout().Write(b)

View File

@ -20,7 +20,7 @@ type RuneBuffer struct {
prompt []rune
w io.Writer
cleanInScreen bool
hadClean bool
interactive bool
cfg *Config
@ -374,7 +374,7 @@ func (r *RuneBuffer) Refresh(f func()) {
func (r *RuneBuffer) print() {
r.w.Write(r.output())
r.cleanInScreen = false
r.hadClean = false
}
func (r *RuneBuffer) output() []byte {
@ -472,9 +472,9 @@ func (r *RuneBuffer) Clean() {
}
func (r *RuneBuffer) clean(idxLine int) {
if r.cleanInScreen || !r.interactive {
if r.hadClean || !r.interactive {
return
}
r.cleanInScreen = true
r.hadClean = true
r.cleanOutput(r.w, idxLine)
}