forked from mirror/readline
refactory
This commit is contained in:
parent
e9e6c35b06
commit
867002449c
|
@ -183,7 +183,7 @@ func (o *opCompleter) CompleteRefresh() {
|
||||||
colWidth = w
|
colWidth = w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
colNum := getWidth(o.op.cfg.StdoutFd) / (colWidth + o.candidateOff + 2)
|
colNum := o.op.cfg.FuncGetWidth() / (colWidth + o.candidateOff + 2)
|
||||||
o.candidateColNum = colNum
|
o.candidateColNum = colNum
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
|
buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
|
||||||
|
|
16
operation.go
16
operation.go
|
@ -2,10 +2,7 @@ package readline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -58,7 +55,7 @@ func (w *wrapWriter) Write(b []byte) (int, error) {
|
||||||
func NewOperation(t *Terminal, cfg *Config) *Operation {
|
func NewOperation(t *Terminal, cfg *Config) *Operation {
|
||||||
op := &Operation{
|
op := &Operation{
|
||||||
t: t,
|
t: t,
|
||||||
buf: NewRuneBuffer(t, cfg.Prompt, cfg.MaskRune, cfg),
|
buf: NewRuneBuffer(t, cfg.Prompt, cfg),
|
||||||
outchan: make(chan []rune),
|
outchan: make(chan []rune),
|
||||||
errchan: make(chan error),
|
errchan: make(chan error),
|
||||||
}
|
}
|
||||||
|
@ -341,16 +338,7 @@ func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) Password(prompt string) ([]byte, error) {
|
func (o *Operation) Password(prompt string) ([]byte, error) {
|
||||||
w := o.Stdout()
|
return o.PasswordEx(prompt, nil)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) SetTitle(t string) {
|
func (o *Operation) SetTitle(t string) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ func (o *opPassword) EnterPasswordMode(cfg *Config) (err error) {
|
||||||
|
|
||||||
func (o *opPassword) PasswordConfig() *Config {
|
func (o *opPassword) PasswordConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
MaskRune: '*',
|
EnableMask: true,
|
||||||
InterruptPrompt: "\n",
|
InterruptPrompt: "\n",
|
||||||
EOFPrompt: "\n",
|
EOFPrompt: "\n",
|
||||||
HistoryLimit: -1,
|
HistoryLimit: -1,
|
||||||
|
|
18
readline.go
18
readline.go
|
@ -11,11 +11,6 @@ type Instance struct {
|
||||||
Operation *Operation
|
Operation *Operation
|
||||||
}
|
}
|
||||||
|
|
||||||
type FdReader interface {
|
|
||||||
io.Reader
|
|
||||||
Fd() uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// prompt supports ANSI escape sequence, so we can color some characters even in windows
|
// prompt supports ANSI escape sequence, so we can color some characters even in windows
|
||||||
Prompt string
|
Prompt string
|
||||||
|
@ -39,12 +34,17 @@ type Config struct {
|
||||||
InterruptPrompt string
|
InterruptPrompt string
|
||||||
EOFPrompt string
|
EOFPrompt string
|
||||||
|
|
||||||
Stdin FdReader
|
FuncGetWidth func() int
|
||||||
|
|
||||||
|
Stdin io.Reader
|
||||||
Stdout io.Writer
|
Stdout io.Writer
|
||||||
Stderr io.Writer
|
Stderr io.Writer
|
||||||
|
|
||||||
MaskRune rune
|
EnableMask bool
|
||||||
|
MaskRune rune
|
||||||
|
|
||||||
|
// erase the editing line after user submited it
|
||||||
|
// it often use in IM.
|
||||||
UniqueEditLine bool
|
UniqueEditLine bool
|
||||||
|
|
||||||
// force use interactive even stdout is not a tty
|
// force use interactive even stdout is not a tty
|
||||||
|
@ -100,6 +100,10 @@ func (c *Config) Init() error {
|
||||||
c.EOFPrompt = ""
|
c.EOFPrompt = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.FuncGetWidth == nil {
|
||||||
|
c.FuncGetWidth = genGetWidthFunc(c.StdoutFd)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
runebuf.go
16
runebuf.go
|
@ -18,7 +18,6 @@ type RuneBuffer struct {
|
||||||
idx int
|
idx int
|
||||||
prompt []rune
|
prompt []rune
|
||||||
w io.Writer
|
w io.Writer
|
||||||
mask rune
|
|
||||||
|
|
||||||
cleanInScreen bool
|
cleanInScreen bool
|
||||||
interactive 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{
|
rb := &RuneBuffer{
|
||||||
w: w,
|
w: w,
|
||||||
mask: mask,
|
|
||||||
interactive: cfg.useInteractive(),
|
interactive: cfg.useInteractive(),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
|
@ -58,7 +56,7 @@ func (r *RuneBuffer) SetConfig(cfg *Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) SetMask(m rune) {
|
func (r *RuneBuffer) SetMask(m rune) {
|
||||||
r.mask = m
|
r.cfg.MaskRune = m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) CurrentWidth(x int) int {
|
func (r *RuneBuffer) CurrentWidth(x int) int {
|
||||||
|
@ -295,7 +293,7 @@ func (r *RuneBuffer) MoveToLineEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) LineCount() int {
|
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) {
|
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 {
|
func (r *RuneBuffer) IdxLine() int {
|
||||||
totalWidth := runes.WidthAll(r.buf[:r.idx]) + r.PromptLen()
|
totalWidth := runes.WidthAll(r.buf[:r.idx]) + r.PromptLen()
|
||||||
w := getWidth(r.cfg.StdoutFd)
|
w := r.cfg.FuncGetWidth()
|
||||||
if w <= 0 {
|
if w <= 0 {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
@ -368,12 +366,12 @@ func (r *RuneBuffer) Refresh(f func()) {
|
||||||
func (r *RuneBuffer) output() []byte {
|
func (r *RuneBuffer) output() []byte {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
buf.WriteString(string(r.prompt))
|
buf.WriteString(string(r.prompt))
|
||||||
if r.mask != 0 && len(r.buf) > 0 {
|
if r.cfg.EnableMask && len(r.buf) > 0 {
|
||||||
buf.Write([]byte(strings.Repeat(string(r.mask), len(r.buf)-1)))
|
buf.Write([]byte(strings.Repeat(string(r.cfg.MaskRune), len(r.buf)-1)))
|
||||||
if r.buf[len(r.buf)-1] == '\n' {
|
if r.buf[len(r.buf)-1] == '\n' {
|
||||||
buf.Write([]byte{'\n'})
|
buf.Write([]byte{'\n'})
|
||||||
} else {
|
} else {
|
||||||
buf.Write([]byte(string(r.mask)))
|
buf.Write([]byte(string(r.cfg.MaskRune)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf.Write([]byte(string(r.buf)))
|
buf.Write([]byte(string(r.buf)))
|
||||||
|
|
|
@ -125,7 +125,7 @@ func (o *opSearch) SearchRefresh(x int) {
|
||||||
}
|
}
|
||||||
x = o.buf.CurrentWidth(x)
|
x = o.buf.CurrentWidth(x)
|
||||||
x += o.buf.PromptLen()
|
x += o.buf.PromptLen()
|
||||||
x = x % getWidth(o.cfg.StdoutFd)
|
x = x % o.cfg.FuncGetWidth()
|
||||||
|
|
||||||
if o.markStart > 0 {
|
if o.markStart > 0 {
|
||||||
o.buf.SetStyle(o.markStart, o.markEnd, "4")
|
o.buf.SetStyle(o.markStart, o.markEnd, "4")
|
||||||
|
|
9
std.go
9
std.go
|
@ -17,6 +17,7 @@ var (
|
||||||
stdOnce sync.Once
|
stdOnce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// global instance will not submit history automatic
|
||||||
func getInstance() *Instance {
|
func getInstance() *Instance {
|
||||||
stdOnce.Do(func() {
|
stdOnce.Do(func() {
|
||||||
std, _ = NewEx(&Config{
|
std, _ = NewEx(&Config{
|
||||||
|
@ -26,6 +27,10 @@ func getInstance() *Instance {
|
||||||
return std
|
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) {
|
func SetHistoryPath(fp string) {
|
||||||
ins := getInstance()
|
ins := getInstance()
|
||||||
cfg := ins.Config.Clone()
|
cfg := ins.Config.Clone()
|
||||||
|
@ -33,6 +38,7 @@ func SetHistoryPath(fp string) {
|
||||||
ins.SetConfig(cfg)
|
ins.SetConfig(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set auto completer to global instance
|
||||||
func SetAutoComplete(completer AutoCompleter) {
|
func SetAutoComplete(completer AutoCompleter) {
|
||||||
ins := getInstance()
|
ins := getInstance()
|
||||||
cfg := ins.Config.Clone()
|
cfg := ins.Config.Clone()
|
||||||
|
@ -40,11 +46,14 @@ func SetAutoComplete(completer AutoCompleter) {
|
||||||
ins.SetConfig(cfg)
|
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 {
|
func AddHistory(content string) error {
|
||||||
ins := getInstance()
|
ins := getInstance()
|
||||||
return ins.SaveHistory(content)
|
return ins.SaveHistory(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readline with global configs
|
||||||
func Line(prompt string) (string, error) {
|
func Line(prompt string) (string, error) {
|
||||||
ins := getInstance()
|
ins := getInstance()
|
||||||
ins.SetPrompt(prompt)
|
ins.SetPrompt(prompt)
|
||||||
|
|
|
@ -36,7 +36,7 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) EnterRawMode() (err 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ func (t *Terminal) ExitRawMode() (err error) {
|
||||||
if t.state == nil {
|
if t.state == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = Restore(int(t.cfg.Stdin.Fd()), t.state)
|
err = Restore(int(t.cfg.StdinFd), t.state)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.state = nil
|
t.state = nil
|
||||||
}
|
}
|
||||||
|
|
10
utils.go
10
utils.go
|
@ -87,8 +87,8 @@ func escapeKey(r rune) rune {
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate how many lines for N character
|
// calculate how many lines for N character
|
||||||
func LineCount(stdoutFd int, w int) int {
|
func LineCount(getWidth func() int, w int) int {
|
||||||
screenWidth := getWidth(stdoutFd)
|
screenWidth := getWidth()
|
||||||
r := w / screenWidth
|
r := w / screenWidth
|
||||||
if w%screenWidth != 0 {
|
if w%screenWidth != 0 {
|
||||||
r++
|
r++
|
||||||
|
@ -116,3 +116,9 @@ func GetInt(s []string, def int) int {
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func genGetWidthFunc(fd int) func() int {
|
||||||
|
return func() int {
|
||||||
|
return getWidth(fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue