mirror of https://github.com/chzyer/readline.git
[example] Add a IM example
This commit is contained in:
parent
91ba4d48f0
commit
218eb7fff6
|
@ -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-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)
|
* [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
|
# Todo
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -37,6 +37,11 @@ func newOpHistory(cfg *Config) (o *opHistory) {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *opHistory) Reset() {
|
||||||
|
o.history = list.New()
|
||||||
|
o.current = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (o *opHistory) IsHistoryClosed() bool {
|
func (o *opHistory) IsHistoryClosed() bool {
|
||||||
return o.fd.Fd() == ^(uintptr(0))
|
return o.fd.Fd() == ^(uintptr(0))
|
||||||
}
|
}
|
||||||
|
|
17
operation.go
17
operation.go
|
@ -237,11 +237,16 @@ func (o *Operation) ioloop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// treat as EOF
|
// treat as EOF
|
||||||
|
if !o.cfg.UniqueEditLine {
|
||||||
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
|
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
|
||||||
|
}
|
||||||
o.buf.Reset()
|
o.buf.Reset()
|
||||||
isUpdateHistory = false
|
isUpdateHistory = false
|
||||||
o.history.Revert()
|
o.history.Revert()
|
||||||
o.errchan <- io.EOF
|
o.errchan <- io.EOF
|
||||||
|
if o.cfg.UniqueEditLine {
|
||||||
|
o.buf.Clean()
|
||||||
|
}
|
||||||
case CharInterrupt:
|
case CharInterrupt:
|
||||||
if o.IsSearchMode() {
|
if o.IsSearchMode() {
|
||||||
o.t.KickRead()
|
o.t.KickRead()
|
||||||
|
@ -257,9 +262,13 @@ func (o *Operation) ioloop() {
|
||||||
o.buf.MoveToLineEnd()
|
o.buf.MoveToLineEnd()
|
||||||
o.buf.Refresh(nil)
|
o.buf.Refresh(nil)
|
||||||
hint := o.cfg.InterruptPrompt + "\n"
|
hint := o.cfg.InterruptPrompt + "\n"
|
||||||
|
if !o.cfg.UniqueEditLine {
|
||||||
o.buf.WriteString(hint)
|
o.buf.WriteString(hint)
|
||||||
|
}
|
||||||
remain := o.buf.Reset()
|
remain := o.buf.Reset()
|
||||||
|
if !o.cfg.UniqueEditLine {
|
||||||
remain = remain[:len(remain)-len([]rune(hint))]
|
remain = remain[:len(remain)-len([]rune(hint))]
|
||||||
|
}
|
||||||
isUpdateHistory = false
|
isUpdateHistory = false
|
||||||
o.history.Revert()
|
o.history.Revert()
|
||||||
o.errchan <- &InterruptError{remain}
|
o.errchan <- &InterruptError{remain}
|
||||||
|
@ -419,6 +428,10 @@ func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
|
||||||
return old, nil
|
return old, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Operation) ResetHistory() {
|
||||||
|
o.history.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
// if err is not nil, it just mean it fail to write to file
|
// if err is not nil, it just mean it fail to write to file
|
||||||
// other things goes fine.
|
// other things goes fine.
|
||||||
func (o *Operation) SaveHistory(content string) error {
|
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 {
|
func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
|
||||||
return &DumpListener{f: f}
|
return &DumpListener{f: f}
|
||||||
}
|
}
|
||||||
|
|
25
readline.go
25
readline.go
|
@ -140,6 +140,10 @@ func New(prompt string) (*Instance, error) {
|
||||||
return NewEx(&Config{Prompt: prompt})
|
return NewEx(&Config{Prompt: prompt})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) ResetHistory() {
|
||||||
|
i.Operation.ResetHistory()
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) SetPrompt(s string) {
|
func (i *Instance) SetPrompt(s string) {
|
||||||
i.Operation.SetPrompt(s)
|
i.Operation.SetPrompt(s)
|
||||||
}
|
}
|
||||||
|
@ -189,6 +193,24 @@ func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
|
||||||
return i.Operation.Password(prompt)
|
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)
|
// err is one of (nil, io.EOF, readline.ErrInterrupt)
|
||||||
func (i *Instance) Readline() (string, error) {
|
func (i *Instance) Readline() (string, error) {
|
||||||
return i.Operation.String()
|
return i.Operation.String()
|
||||||
|
@ -211,6 +233,9 @@ func (i *Instance) Close() error {
|
||||||
i.Operation.Close()
|
i.Operation.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (i *Instance) Clean() {
|
||||||
|
i.Operation.Clean()
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) Write(b []byte) (int, error) {
|
func (i *Instance) Write(b []byte) (int, error) {
|
||||||
return i.Stdout().Write(b)
|
return i.Stdout().Write(b)
|
||||||
|
|
|
@ -20,7 +20,7 @@ type RuneBuffer struct {
|
||||||
prompt []rune
|
prompt []rune
|
||||||
w io.Writer
|
w io.Writer
|
||||||
|
|
||||||
cleanInScreen bool
|
hadClean bool
|
||||||
interactive bool
|
interactive bool
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ func (r *RuneBuffer) Refresh(f func()) {
|
||||||
|
|
||||||
func (r *RuneBuffer) print() {
|
func (r *RuneBuffer) print() {
|
||||||
r.w.Write(r.output())
|
r.w.Write(r.output())
|
||||||
r.cleanInScreen = false
|
r.hadClean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) output() []byte {
|
func (r *RuneBuffer) output() []byte {
|
||||||
|
@ -472,9 +472,9 @@ func (r *RuneBuffer) Clean() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) clean(idxLine int) {
|
func (r *RuneBuffer) clean(idxLine int) {
|
||||||
if r.cleanInScreen || !r.interactive {
|
if r.hadClean || !r.interactive {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.cleanInScreen = true
|
r.hadClean = true
|
||||||
r.cleanOutput(r.w, idxLine)
|
r.cleanOutput(r.w, idxLine)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue