Skip to content

Commit

Permalink
Add ExecuteOnEnterCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
Verseth committed Jul 7, 2023
1 parent 2fede7b commit 194a198
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 76 deletions.
26 changes: 26 additions & 0 deletions _example/bang-executor/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"fmt"
"unicode/utf8"

"github.com/elk-language/go-prompt"
)

func main() {
p := prompt.New(
executor,
prompt.WithExecuteOnEnterCallback(ExecuteOnEnter),
)

p.Run()
}

func ExecuteOnEnter(input string) bool {
char, _ := utf8.DecodeLastRuneInString(input)
return char == '!'
}

func executor(s string) {
fmt.Println("You printed: " + s)
}
13 changes: 7 additions & 6 deletions _example/http-prompt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ var suggestions = []prompt.Suggest{
{"X-XSRF-TOKEN", "Prevent cross-site request forgery"},
}

func livePrefix() (string, bool) {
if ctx.url.Path == "/" {
return "", false
func livePrefix(defaultPrefix string) prompt.PrefixCallback {
return func() string {
if ctx.url.Path == "/" {
return defaultPrefix
}
return ctx.url.String() + "> "
}
return ctx.url.String() + "> ", true
}

func executor(in string) {
Expand Down Expand Up @@ -183,8 +185,7 @@ func main() {

p := prompt.New(
executor,
prompt.WithPrefix(u.String()+"> "),
prompt.WithLivePrefix(livePrefix),
prompt.WithPrefixCallback(livePrefix(u.String()+"> ")),
prompt.WithTitle("http-prompt"),
prompt.WithCompleter(completer),
)
Expand Down
18 changes: 6 additions & 12 deletions _example/live-prefix/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@ import (
prompt "github.com/elk-language/go-prompt"
)

var LivePrefixState struct {
LivePrefix string
IsEnable bool
}
var LivePrefix string = ">>> "

func executor(in string) {
fmt.Println("Your input: " + in)
if in == "" {
LivePrefixState.IsEnable = false
LivePrefixState.LivePrefix = in
LivePrefix = ">>> "
return
}
LivePrefixState.LivePrefix = in + "> "
LivePrefixState.IsEnable = true
LivePrefix = in + "> "
}

func completer(in prompt.Document) []prompt.Suggest {
Expand All @@ -32,15 +27,14 @@ func completer(in prompt.Document) []prompt.Suggest {
return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true)
}

func changeLivePrefix() (string, bool) {
return LivePrefixState.LivePrefix, LivePrefixState.IsEnable
func changeLivePrefix() string {
return LivePrefix
}

func main() {
p := prompt.New(
executor,
prompt.WithPrefix(">>> "),
prompt.WithLivePrefix(changeLivePrefix),
prompt.WithPrefixCallback(changeLivePrefix),
prompt.WithTitle("live-prefix-example"),
prompt.WithCompleter(completer),
)
Expand Down
79 changes: 50 additions & 29 deletions constructor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package prompt
// prompt.New accepts any number of options (this is functional option pattern).
type Option func(prompt *Prompt) error

// Callback function that returns a prompt prefix.
type PrefixCallback func() (prefix string)

// WithCompleter is an option that sets a custom Completer object.
func WithCompleter(c Completer) Option {
return func(p *Prompt) error {
Expand All @@ -12,59 +15,59 @@ func WithCompleter(c Completer) Option {
}
}

// WithReader to set a custom Reader object. An argument should implement Reader interface.
func WithReader(x Reader) Option {
// WithReader can be used to set a custom Reader object.
func WithReader(r Reader) Option {
return func(p *Prompt) error {
p.reader = x
p.reader = r
return nil
}
}

// WithWriter to set a custom Writer object. An argument should implement Writer interface.
func WithWriter(x Writer) Option {
// WithWriter can be used to set a custom Writer object.
func WithWriter(w Writer) Option {
return func(p *Prompt) error {
registerWriter(x)
p.renderer.out = x
registerWriter(w)
p.renderer.out = w
return nil
}
}

// WithTitle to set title displayed at the header bar of terminal.
func WithTitle(x string) Option {
// WithTitle can be used to set the title displayed at the header bar of the terminal.
func WithTitle(t string) Option {
return func(p *Prompt) error {
p.renderer.title = x
p.renderer.title = t
return nil
}
}

// WithPrefix to set prefix string.
func WithPrefix(x string) Option {
// WithPrefix can be used to set a prefix string for the prompt.
func WithPrefix(prefix string) Option {
return func(p *Prompt) error {
p.renderer.prefix = x
p.renderer.prefixCallback = func() string { return prefix }
return nil
}
}

// WithInitialBufferText to set the initial buffer text
func WithInitialBufferText(x string) Option {
// WithInitialText can be used to set the initial buffer text.
func WithInitialText(text string) Option {
return func(p *Prompt) error {
p.buf.InsertText(x, false, true)
p.buf.InsertText(text, false, true)
return nil
}
}

// WithCompletionWordSeparator to set word separators. Enable only ' ' if empty.
func WithCompletionWordSeparator(x string) Option {
// WithCompletionWordSeparator can be used to set word separators. Enable only ' ' if empty.
func WithCompletionWordSeparator(sep string) Option {
return func(p *Prompt) error {
p.completion.wordSeparator = x
p.completion.wordSeparator = sep
return nil
}
}

// WithLivePrefix to change the prefix dynamically by callback function
func WithLivePrefix(f func() (prefix string, useLivePrefix bool)) Option {
// WithPrefixCallback can be used to change the prefix dynamically by a callback function.
func WithPrefixCallback(f PrefixCallback) Option {
return func(p *Prompt) error {
p.renderer.livePrefixCallback = f
p.renderer.prefixCallback = f
return nil
}
}
Expand Down Expand Up @@ -282,6 +285,24 @@ func WithLexer(lex Lexer) Option {
}
}

// WithExecuteOnEnterCallback can be used to set
// a custom callback function that determines whether an Enter key
// should trigger the Executor or add a newline to the user input buffer.
func WithExecuteOnEnterCallback(fn ExecuteOnEnterCallback) Option {
return func(p *Prompt) error {
p.executeOnEnterCallback = fn
return nil
}
}

func DefaultExecuteOnEnterCallback(input string) bool {
return true
}

func DefaultPrefixCallback() string {
return "> "
}

// New returns a Prompt with powerful auto-completion.
func New(executor Executor, opts ...Option) *Prompt {
defaultWriter := NewStdoutWriter()
Expand All @@ -290,9 +311,8 @@ func New(executor Executor, opts ...Option) *Prompt {
pt := &Prompt{
reader: NewStdinReader(),
renderer: &Render{
prefix: "> ",
out: defaultWriter,
livePrefixCallback: func() (string, bool) { return "", false },
prefixCallback: DefaultPrefixCallback,
prefixTextColor: Blue,
prefixBGColor: DefaultColor,
inputTextColor: DefaultColor,
Expand All @@ -310,11 +330,12 @@ func New(executor Executor, opts ...Option) *Prompt {
scrollbarThumbColor: DarkGray,
scrollbarBGColor: Cyan,
},
buf: NewBuffer(),
executor: executor,
history: NewHistory(),
completion: NewCompletionManager(6),
keyBindMode: EmacsKeyBind, // All the above assume that bash is running in the default Emacs setting
buf: NewBuffer(),
executor: executor,
history: NewHistory(),
completion: NewCompletionManager(6),
executeOnEnterCallback: DefaultExecuteOnEnterCallback,
keyBindMode: EmacsKeyBind, // All the above assume that bash is running in the default Emacs setting
}

for _, opt := range opts {
Expand Down
40 changes: 26 additions & 14 deletions prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,33 @@ type Executor func(string)
// Exit means exit go-prompt (not the overall Go program)
type ExitChecker func(in string, breakline bool) bool

// ExecuteOnEnterCallback is a function that receives
// user input after Enter has been pressed
// and determines whether the input should be executed.
// If this function returns true, the Executor callback will be called
// otherwise a newline will be added to the buffer containing user input.
type ExecuteOnEnterCallback func(input string) bool

// Completer is a function that returns
// a slice of suggestions for the given Document.
type Completer func(Document) []Suggest

// Prompt is a core struct of go-prompt.
type Prompt struct {
reader Reader
buf *Buffer
renderer *Render
executor Executor
history *History
lexer Lexer
completion *CompletionManager
keyBindings []KeyBind
ASCIICodeBindings []ASCIICodeBind
keyBindMode KeyBindMode
completionOnDown bool
exitChecker ExitChecker
skipClose bool
reader Reader
buf *Buffer
renderer *Render
executor Executor
history *History
lexer Lexer
completion *CompletionManager
keyBindings []KeyBind
ASCIICodeBindings []ASCIICodeBind
keyBindMode KeyBindMode
completionOnDown bool
exitChecker ExitChecker
executeOnEnterCallback ExecuteOnEnterCallback
skipClose bool
}

// UserInput is the struct that contains the user input context.
Expand Down Expand Up @@ -137,8 +145,12 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, userInput *UserInput) {

switch key {
case Enter, ControlJ, ControlM:
p.renderer.BreakLine(p.buf, p.lexer)
if !p.executeOnEnterCallback(p.buf.Text()) {
p.buf.NewLine(false)
break
}

p.renderer.BreakLine(p.buf, p.lexer)
userInput = &UserInput{input: p.buf.Text()}
p.buf = NewBuffer()
if userInput.input != "" {
Expand Down
18 changes: 7 additions & 11 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import (

// Render to render prompt information from state of Buffer.
type Render struct {
out Writer
prefix string
livePrefixCallback func() (prefix string, useLivePrefix bool)
breakLineCallback func(*Document)
title string
row uint16
col uint16
out Writer
prefixCallback PrefixCallback
breakLineCallback func(*Document)
title string
row uint16
col uint16

previousCursor Position

Expand Down Expand Up @@ -50,10 +49,7 @@ 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
return r.prefixCallback()
}

func (r *Render) renderPrefix() {
Expand Down
3 changes: 1 addition & 2 deletions render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,10 @@ func TestFormatCompletion(t *testing.T) {
func TestBreakLineCallback(t *testing.T) {
var i int
r := &Render{
prefix: "> ",
out: &PosixWriter{
fd: syscall.Stdin, // "write" to stdin just so we don't mess with the output of the tests
},
livePrefixCallback: func() (string, bool) { return "", false },
prefixCallback: DefaultPrefixCallback,
prefixTextColor: Blue,
prefixBGColor: DefaultColor,
inputTextColor: DefaultColor,
Expand Down
3 changes: 1 addition & 2 deletions shortcut.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package prompt
func NoopExecutor(in string) {}

// Input get the input data from the user and return it.
func Input(prefix string, opts ...Option) string {
func Input(opts ...Option) string {
pt := New(NoopExecutor)
pt.renderer.prefixTextColor = DefaultColor
pt.renderer.prefix = prefix

for _, opt := range opts {
if err := opt(pt); err != nil {
Expand Down

0 comments on commit 194a198

Please sign in to comment.