refactory

This commit is contained in:
Cheney 2016-03-05 10:46:11 +08:00
parent e9e6c35b06
commit 867002449c
9 changed files with 42 additions and 37 deletions

View File

@ -183,7 +183,7 @@ func (o *opCompleter) CompleteRefresh() {
colWidth = w
}
}
colNum := getWidth(o.op.cfg.StdoutFd) / (colWidth + o.candidateOff + 2)
colNum := o.op.cfg.FuncGetWidth() / (colWidth + o.candidateOff + 2)
o.candidateColNum = colNum
buf := bytes.NewBuffer(nil)
buf.Write(bytes.Repeat([]byte("\n"), lineCnt))

View File

@ -2,10 +2,7 @@ package readline
import (
"errors"
"fmt"
"io"
"golang.org/x/crypto/ssh/terminal"
)
var (
@ -58,7 +55,7 @@ func (w *wrapWriter) Write(b []byte) (int, error) {
func NewOperation(t *Terminal, cfg *Config) *Operation {
op := &Operation{
t: t,
buf: NewRuneBuffer(t, cfg.Prompt, cfg.MaskRune, cfg),
buf: NewRuneBuffer(t, cfg.Prompt, cfg),
outchan: make(chan []rune),
errchan: make(chan error),
}
@ -341,16 +338,7 @@ func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
}
func (o *Operation) Password(prompt string) ([]byte, error) {
w := o.Stdout()
if prompt != "" {
fmt.Fprintf(w, prompt)
}
o.t.EnterRawMode()
defer o.t.ExitRawMode()
b, err := terminal.ReadPassword(int(o.cfg.Stdin.Fd()))
fmt.Fprint(w, "\r\n")
return b, err
return o.PasswordEx(prompt, nil)
}
func (o *Operation) SetTitle(t string) {

View File

@ -21,7 +21,7 @@ func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) {
func (o *opPassword) PasswordConfig() *Config {
return &Config{
MaskRune: '*',
EnableMask: true,
InterruptPrompt: "\n",
EOFPrompt: "\n",
HistoryLimit: -1,

View File

@ -11,11 +11,6 @@ type Instance struct {
Operation *Operation
}
type FdReader interface {
io.Reader
Fd() uintptr
}
type Config struct {
// prompt supports ANSI escape sequence, so we can color some characters even in windows
Prompt string
@ -39,12 +34,17 @@ type Config struct {
InterruptPrompt string
EOFPrompt string
Stdin FdReader
FuncGetWidth func() int
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
EnableMask bool
MaskRune rune
// erase the editing line after user submited it
// it often use in IM.
UniqueEditLine bool
// force use interactive even stdout is not a tty
@ -100,6 +100,10 @@ func (c *Config) Init() error {
c.EOFPrompt = ""
}
if c.FuncGetWidth == nil {
c.FuncGetWidth = genGetWidthFunc(c.StdoutFd)
}
return nil
}

View File

@ -18,7 +18,6 @@ type RuneBuffer struct {
idx int
prompt []rune
w io.Writer
mask rune
cleanInScreen bool
interactive bool
@ -41,10 +40,9 @@ func (r *RuneBuffer) Restore() {
})
}
func NewRuneBuffer(w io.Writer, prompt string, mask rune, cfg *Config) *RuneBuffer {
func NewRuneBuffer(w io.Writer, prompt string, cfg *Config) *RuneBuffer {
rb := &RuneBuffer{
w: w,
mask: mask,
interactive: cfg.useInteractive(),
cfg: cfg,
}
@ -58,7 +56,7 @@ func (r *RuneBuffer) SetConfig(cfg *Config) {
}
func (r *RuneBuffer) SetMask(m rune) {
r.mask = m
r.cfg.MaskRune = m
}
func (r *RuneBuffer) CurrentWidth(x int) int {
@ -295,7 +293,7 @@ func (r *RuneBuffer) MoveToLineEnd() {
}
func (r *RuneBuffer) LineCount() int {
return LineCount(r.cfg.StdoutFd, runes.WidthAll(r.buf)+r.PromptLen())
return LineCount(r.cfg.FuncGetWidth, runes.WidthAll(r.buf)+r.PromptLen())
}
func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
@ -329,7 +327,7 @@ func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
func (r *RuneBuffer) IdxLine() int {
totalWidth := runes.WidthAll(r.buf[:r.idx]) + r.PromptLen()
w := getWidth(r.cfg.StdoutFd)
w := r.cfg.FuncGetWidth()
if w <= 0 {
return -1
}
@ -368,12 +366,12 @@ func (r *RuneBuffer) Refresh(f func()) {
func (r *RuneBuffer) output() []byte {
buf := bytes.NewBuffer(nil)
buf.WriteString(string(r.prompt))
if r.mask != 0 && len(r.buf) > 0 {
buf.Write([]byte(strings.Repeat(string(r.mask), len(r.buf)-1)))
if r.cfg.EnableMask && len(r.buf) > 0 {
buf.Write([]byte(strings.Repeat(string(r.cfg.MaskRune), len(r.buf)-1)))
if r.buf[len(r.buf)-1] == '\n' {
buf.Write([]byte{'\n'})
} else {
buf.Write([]byte(string(r.mask)))
buf.Write([]byte(string(r.cfg.MaskRune)))
}
} else {
buf.Write([]byte(string(r.buf)))

View File

@ -125,7 +125,7 @@ func (o *opSearch) SearchRefresh(x int) {
}
x = o.buf.CurrentWidth(x)
x += o.buf.PromptLen()
x = x % getWidth(o.cfg.StdoutFd)
x = x % o.cfg.FuncGetWidth()
if o.markStart > 0 {
o.buf.SetStyle(o.markStart, o.markEnd, "4")

9
std.go
View File

@ -17,6 +17,7 @@ var (
stdOnce sync.Once
)
// global instance will not submit history automatic
func getInstance() *Instance {
stdOnce.Do(func() {
std, _ = NewEx(&Config{
@ -26,6 +27,10 @@ func getInstance() *Instance {
return std
}
// let readline load history from filepath
// and try to persist history into disk
// set fp to "" to prevent readline persisting history to disk
// so the `AddHistory` will return nil error forever.
func SetHistoryPath(fp string) {
ins := getInstance()
cfg := ins.Config.Clone()
@ -33,6 +38,7 @@ func SetHistoryPath(fp string) {
ins.SetConfig(cfg)
}
// set auto completer to global instance
func SetAutoComplete(completer AutoCompleter) {
ins := getInstance()
cfg := ins.Config.Clone()
@ -40,11 +46,14 @@ func SetAutoComplete(completer AutoCompleter) {
ins.SetConfig(cfg)
}
// add history to global instance manually
// raise error only if `SetHistoryPath` is set with a non-empty path
func AddHistory(content string) error {
ins := getInstance()
return ins.SaveHistory(content)
}
// readline with global configs
func Line(prompt string) (string, error) {
ins := getInstance()
ins.SetPrompt(prompt)

View File

@ -36,7 +36,7 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
}
func (t *Terminal) EnterRawMode() (err error) {
t.state, err = MakeRaw(int(t.cfg.Stdin.Fd()))
t.state, err = MakeRaw(int(t.cfg.StdinFd))
return err
}
@ -44,7 +44,7 @@ func (t *Terminal) ExitRawMode() (err error) {
if t.state == nil {
return
}
err = Restore(int(t.cfg.Stdin.Fd()), t.state)
err = Restore(int(t.cfg.StdinFd), t.state)
if err == nil {
t.state = nil
}

View File

@ -87,8 +87,8 @@ func escapeKey(r rune) rune {
}
// calculate how many lines for N character
func LineCount(stdoutFd int, w int) int {
screenWidth := getWidth(stdoutFd)
func LineCount(getWidth func() int, w int) int {
screenWidth := getWidth()
r := w / screenWidth
if w%screenWidth != 0 {
r++
@ -116,3 +116,9 @@ func GetInt(s []string, def int) int {
}
return c
}
func genGetWidthFunc(fd int) func() int {
return func() int {
return getWidth(fd)
}
}