Support updating the prefix dynamiacally (#30)

* Make possible to change the prefix dynamically

* Add an example for using live-prefix

* Apply live-prefix if it's enabled

* Fix: rendering issue happened when the prefix is empty string Close #27

* Fix the title of propmt

* Remove an excessive blank line

* Refactor: Call `*Render.getCurrentPrefix()` in renderPrefix()
This commit is contained in:
Allajah 2018-02-12 20:00:48 +09:00 committed by Masashi SHIBATA
parent dda4d96c46
commit e15ebadefe
3 changed files with 84 additions and 13 deletions

View File

@ -0,0 +1,48 @@
package main
import (
"fmt"
"github.com/c-bata/go-prompt"
)
var LivePrefixState struct {
LivePrefix string
IsEnable bool
}
func executor(in string) {
fmt.Println("Your input: " + in)
if in == "" {
LivePrefixState.IsEnable = false
LivePrefixState.LivePrefix = in
return
}
LivePrefixState.LivePrefix = in + "> "
LivePrefixState.IsEnable = true
}
func completer(in prompt.Document) []prompt.Suggest {
s := []prompt.Suggest{
{Text: "users", Description: "Store the username and age"},
{Text: "articles", Description: "Store the article text posted by user"},
{Text: "comments", Description: "Store the text commented to articles"},
{Text: "groups", Description: "Combine users with specific rules"},
}
return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true)
}
func changeLivePrefix() (string, bool) {
return LivePrefixState.LivePrefix , LivePrefixState.IsEnable
}
func main() {
p := prompt.New(
executor,
completer,
prompt.OptionPrefix(">>> "),
prompt.OptionLivePrefix(changeLivePrefix),
prompt.OptionTitle("live-prefix-example"),
)
p.Run()
}

View File

@ -36,6 +36,14 @@ func OptionPrefix(x string) Option {
}
}
// OptionLivePrefix to change the prefix dynamically by callback function
func OptionLivePrefix(f func() (prefix string, useLivePrefix bool)) Option {
return func(p *Prompt) error {
p.renderer.livePrefixCallback = f
return nil
}
}
func OptionPrefixTextColor(x Color) Option {
return func(p *Prompt) error {
p.renderer.prefixTextColor = x
@ -178,6 +186,7 @@ func New(executor Executor, completer Completer, opts ...Option) *Prompt {
renderer: &Render{
prefix: "> ",
out: NewStandardOutputWriter(),
livePrefixCallback: func() (string, bool) { return "", false },
prefixTextColor: Blue,
prefixBGColor: DefaultColor,
inputTextColor: DefaultColor,

View File

@ -6,12 +6,13 @@ import (
// Render to render prompt information from state of Buffer.
type Render struct {
out ConsoleWriter
prefix string
title string
row uint16
col uint16
// colors
out ConsoleWriter
prefix string
livePrefixCallback func() (prefix string, useLivePrefix bool)
title string
row uint16
col uint16
// colors,
prefixTextColor Color
prefixBGColor Color
inputTextColor Color
@ -38,9 +39,18 @@ func (r *Render) Setup() {
}
}
// getCurrentPrefix to get current prefix.
// If live-prefix is enabled, return live-prefix.
func (r *Render) getCurrentPrefix() string {
if prefix, ok := r.livePrefixCallback(); ok {
return prefix
}
return r.prefix
}
func (r *Render) renderPrefix() {
r.out.SetColor(r.prefixTextColor, r.prefixBGColor, false)
r.out.WriteStr(r.prefix)
r.out.WriteStr(r.getCurrentPrefix())
r.out.SetColor(DefaultColor, DefaultColor, false)
}
@ -99,16 +109,17 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
return
}
prefix := r.getCurrentPrefix()
formatted, width := formatSuggestions(
suggestions,
int(r.col)-len(r.prefix)-1, // -1 means a width of scrollbar
int(r.col)-len(prefix)-1, // -1 means a width of scrollbar
)
formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
l := len(formatted)
r.prepareArea(windowHeight)
// +1 means a width of scrollbar.
d := (len(r.prefix) + len(buf.Document().TextBeforeCursor()) + 1) % int(r.col)
d := (len(prefix) + len(buf.Document().TextBeforeCursor()) + 1) % int(r.col)
if d == 0 { // the cursor is on right end.
r.out.CursorBackward(width)
} else if d+width > int(r.col) {
@ -143,7 +154,7 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
// +1 means a width of scrollbar.
r.out.CursorBackward(width + 1)
}
if d == 0 { // the cursor is on right end.
if d == 0 && len(prefix) + len(buf.Text()) != 0 { // the cursor is on right end.
// DON'T CURSOR DOWN HERE. Because the line doesn't erase properly.
r.out.CursorForward(width + 1)
} else if d+width > int(r.col) {
@ -157,13 +168,15 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
// Render renders to the console.
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
prefix := r.getCurrentPrefix()
// Erasing
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(prefix))
r.out.EraseDown()
// prepare area
line := buffer.Text()
h := ((len(r.prefix) + len(line)) / int(r.col)) + 1 + int(completion.max)
h := ((len(prefix) + len(line)) / int(r.col)) + 1 + int(completion.max)
if h > int(r.row) || completionMargin > int(r.col) {
r.renderWindowTooSmall()
return
@ -189,7 +202,8 @@ func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
// BreakLine to break line.
func (r *Render) BreakLine(buffer *Buffer) {
// CR
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
prefix := r.getCurrentPrefix()
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(prefix))
// Erasing and Render
r.out.EraseDown()
r.renderPrefix()