Fix history and Refactor completion
This commit is contained in:
parent
3002a7e8af
commit
654143f525
|
@ -0,0 +1,56 @@
|
|||
package prompt
|
||||
|
||||
type Completion struct {
|
||||
Text string
|
||||
Description string
|
||||
}
|
||||
|
||||
type CompletionManager struct {
|
||||
selected int // -1 means nothing one is selected.
|
||||
tmp []*Completion
|
||||
Max uint16
|
||||
}
|
||||
|
||||
func (c *CompletionManager) Reset() {
|
||||
c.selected = -1
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CompletionManager) Update(new []*Completion) {
|
||||
c.selected = -1
|
||||
c.tmp = new
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CompletionManager) Previous() {
|
||||
c.selected--
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CompletionManager) Next() {
|
||||
c.selected++
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CompletionManager) Completing() bool {
|
||||
return c.selected != -1
|
||||
}
|
||||
|
||||
func (c *CompletionManager) update(completions []Completion) {
|
||||
max := int(c.Max)
|
||||
if len(completions) < max {
|
||||
max = len(completions)
|
||||
}
|
||||
if c.selected >= max {
|
||||
c.Reset()
|
||||
} else if c.selected < -1 {
|
||||
c.selected = max - 1
|
||||
}
|
||||
}
|
||||
|
||||
func NewCompletionManager(max uint16) *CompletionManager {
|
||||
return &CompletionManager{
|
||||
selected: -1,
|
||||
Max: max,
|
||||
}
|
||||
}
|
|
@ -10,7 +10,12 @@ type History struct {
|
|||
|
||||
func (h *History) Add(input string) {
|
||||
h.histories = append(h.histories, input)
|
||||
h.tmp = append(h.histories, "")
|
||||
copy(h.tmp, h.histories)
|
||||
h.tmp = append(h.tmp, "")
|
||||
h.selected = len(h.tmp) - 1
|
||||
}
|
||||
|
||||
func (h *History) Clear() {
|
||||
h.selected = len(h.tmp) - 1
|
||||
}
|
||||
|
||||
|
@ -30,7 +35,7 @@ func (h *History) Older(buf *Buffer) (new *Buffer, changed bool) {
|
|||
|
||||
func (h *History) Newer(buf *Buffer) (new *Buffer, changed bool) {
|
||||
log.Printf("[DEBUG] Before %#v\n", h)
|
||||
if h.selected >= len(h.tmp) - 1 {
|
||||
if h.selected >= len(h.tmp)-1 {
|
||||
return buf, false
|
||||
}
|
||||
h.tmp[h.selected] = buf.Text()
|
||||
|
|
|
@ -146,7 +146,7 @@ func OptionSelectedDescriptionBGColor(x Color) option {
|
|||
|
||||
func OptionMaxCompletions(x uint16) option {
|
||||
return func(p *Prompt) error {
|
||||
p.maxCompletions = x
|
||||
p.completion.Max = x
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -177,9 +177,8 @@ func NewPrompt(executor Executor, completer Completer, opts ...option) *Prompt {
|
|||
buf: NewBuffer(),
|
||||
executor: executor,
|
||||
completer: completer,
|
||||
maxCompletions: 6,
|
||||
selected: -1,
|
||||
history: NewHistory(),
|
||||
completion: NewCompletionManager(6),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
|
|
63
prompt.go
63
prompt.go
|
@ -17,10 +17,6 @@ const (
|
|||
|
||||
type Executor func(context.Context, string) string
|
||||
type Completer func(string) []Completion
|
||||
type Completion struct {
|
||||
Text string
|
||||
Description string
|
||||
}
|
||||
|
||||
type Prompt struct {
|
||||
in ConsoleParser
|
||||
|
@ -28,9 +24,8 @@ type Prompt struct {
|
|||
renderer *Render
|
||||
executor Executor
|
||||
completer Completer
|
||||
maxCompletions uint16
|
||||
selected int // -1 means nothing one is selected.
|
||||
history *History
|
||||
completion *CompletionManager
|
||||
}
|
||||
|
||||
type Exec struct {
|
||||
|
@ -59,7 +54,7 @@ func (p *Prompt) Run() {
|
|||
log.Println("[INFO] Logging is enabled.")
|
||||
}
|
||||
|
||||
p.renderer.Render(p.buf, p.completer(p.buf.Text()), p.maxCompletions, p.selected)
|
||||
p.renderer.Render(p.buf, p.completer(p.buf.Text()), p.completion.Max, p.completion.selected)
|
||||
|
||||
bufCh := make(chan []byte, 128)
|
||||
go readBuffer(bufCh)
|
||||
|
@ -77,17 +72,17 @@ func (p *Prompt) Run() {
|
|||
p.runExecutor(exec, bufCh)
|
||||
|
||||
completions := p.completer(p.buf.Text())
|
||||
p.updateSelectedCompletion(completions)
|
||||
p.renderer.Render(p.buf, completions, p.maxCompletions, p.selected)
|
||||
p.completion.update(completions)
|
||||
p.renderer.Render(p.buf, completions, p.completion.Max, p.completion.selected)
|
||||
} else {
|
||||
completions := p.completer(p.buf.Text())
|
||||
p.updateSelectedCompletion(completions)
|
||||
p.renderer.Render(p.buf, completions, p.maxCompletions, p.selected)
|
||||
p.completion.update(completions)
|
||||
p.renderer.Render(p.buf, completions, p.completion.Max, p.completion.selected)
|
||||
}
|
||||
case w := <-winSizeCh:
|
||||
p.renderer.UpdateWinSize(w)
|
||||
completions := p.completer(p.buf.Text())
|
||||
p.renderer.Render(p.buf, completions, p.maxCompletions, p.selected)
|
||||
p.renderer.Render(p.buf, completions, p.completion.Max, p.completion.selected)
|
||||
case code := <-exitCh:
|
||||
p.tearDown()
|
||||
os.Exit(code)
|
||||
|
@ -124,8 +119,8 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
|
||||
switch key {
|
||||
case ControlJ, Enter:
|
||||
if p.selected != -1 {
|
||||
c := p.completer(p.buf.Text())[p.selected]
|
||||
if p.completion.Completing() {
|
||||
c := p.completer(p.buf.Text())[p.completion.selected]
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
|
@ -137,18 +132,19 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
exec = &Exec{input: p.buf.Text()}
|
||||
log.Printf("[History] %s", p.buf.Text())
|
||||
p.buf = NewBuffer()
|
||||
p.selected = -1
|
||||
p.completion.Reset()
|
||||
if exec.input != "" {
|
||||
p.history.Add(exec.input)
|
||||
}
|
||||
case ControlC:
|
||||
p.renderer.BreakLine(p.buf)
|
||||
p.buf = NewBuffer()
|
||||
p.selected = -1
|
||||
p.completion.Reset()
|
||||
p.history.Clear()
|
||||
case ControlD:
|
||||
shouldExit = true
|
||||
case Up:
|
||||
if p.selected == -1 {
|
||||
if !p.completion.Completing() {
|
||||
if newBuf, changed := p.history.Older(p.buf); changed {
|
||||
p.buf = newBuf
|
||||
}
|
||||
|
@ -156,9 +152,9 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
}
|
||||
fallthrough
|
||||
case BackTab:
|
||||
p.selected -= 1
|
||||
p.completion.Previous()
|
||||
case Down:
|
||||
if p.selected == -1 {
|
||||
if !p.completion.Completing() {
|
||||
if newBuf, changed := p.history.Newer(p.buf); changed {
|
||||
p.buf = newBuf
|
||||
}
|
||||
|
@ -166,56 +162,43 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
}
|
||||
fallthrough
|
||||
case Tab, ControlI:
|
||||
p.selected += 1
|
||||
p.completion.Next()
|
||||
case Left:
|
||||
p.buf.CursorLeft(1)
|
||||
case Right:
|
||||
p.buf.CursorRight(1)
|
||||
case Backspace:
|
||||
if p.selected != -1 {
|
||||
c := p.completer(p.buf.Text())[p.selected]
|
||||
if p.completion.Completing() {
|
||||
c := p.completer(p.buf.Text())[p.completion.selected]
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(c.Text, false, true)
|
||||
p.selected = -1
|
||||
p.completion.Reset()
|
||||
}
|
||||
p.buf.DeleteBeforeCursor(1)
|
||||
case NotDefined:
|
||||
if p.selected != -1 {
|
||||
c := p.completer(p.buf.Text())[p.selected]
|
||||
if p.completion.Completing() {
|
||||
c := p.completer(p.buf.Text())[p.completion.selected]
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(c.Text, false, true)
|
||||
}
|
||||
p.selected = -1
|
||||
p.completion.Reset()
|
||||
p.buf.InsertText(string(b), false, true)
|
||||
default:
|
||||
p.selected = -1
|
||||
p.completion.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Prompt) updateSelectedCompletion(completions []Completion) {
|
||||
max := int(p.maxCompletions)
|
||||
if len(completions) < max {
|
||||
max = len(completions)
|
||||
}
|
||||
if p.selected >= max {
|
||||
p.selected = -1
|
||||
} else if p.selected < -1 {
|
||||
p.selected = max - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prompt) setUp() {
|
||||
p.in.Setup()
|
||||
p.renderer.Setup()
|
||||
p.renderer.UpdateWinSize(p.in.GetWinSize())
|
||||
p.selected = -1 // -1 means nothing one is selected.
|
||||
}
|
||||
|
||||
func (p *Prompt) tearDown() {
|
||||
|
|
Loading…
Reference in New Issue