Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement lexer support #246

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions _example/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ go build -o ${BIN_DIR}/http-prompt ${DIR}/http-prompt/main.go
go build -o ${BIN_DIR}/live-prefix ${DIR}/live-prefix/main.go
go build -o ${BIN_DIR}/simple-echo ${DIR}/simple-echo/main.go
go build -o ${BIN_DIR}/simple-echo-cjk-cyrillic ${DIR}/simple-echo/cjk-cyrillic/main.go
go build -o ${BIN_DIR}/even-lexer ${DIR}/even-lexer/main.go
48 changes: 48 additions & 0 deletions _example/even-lexer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"fmt"
"github.com/c-bata/go-prompt"
"strings"
)

func main() {
p := prompt.New(
executor,
completer,
prompt.OptionSetLexer(lexer),
)

p.Run()
}

func lexer(line string) []prompt.LexerElement {
var elements []prompt.LexerElement

strArr := strings.Split(line, "")

for k, v := range strArr {
element := prompt.LexerElement{
Text: v,
}

// every even char must be green.
if k%2 == 0 {
element.Color = prompt.Green
} else {
element.Color = prompt.White
}

elements = append(elements, element)
}

return elements
}

func completer(in prompt.Document) []prompt.Suggest {
return []prompt.Suggest{}
}

func executor(s string) {
fmt.Println("You printed: " + s)
}
34 changes: 34 additions & 0 deletions lexer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package prompt

// LexerFunc is a callback from render.
type LexerFunc = func(line string) []LexerElement

// LexerElement is a element of lexer.
type LexerElement struct {
Color Color
Text string
}

// Lexer is a struct with lexer param and function.
type Lexer struct {
IsEnabled bool
fn LexerFunc
}

// NewLexer returns new Lexer.
func NewLexer() *Lexer {
return &Lexer{
IsEnabled: false,
}
}

// SetLexerFunction in lexer struct.
func (l *Lexer) SetLexerFunction(fn LexerFunc) {
l.IsEnabled = true
l.fn = fn
}

// Process line with a custom function.
func (l *Lexer) Process(line string) []LexerElement {
return l.fn(line)
}
9 changes: 9 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ func OptionSetExitCheckerOnInput(fn ExitChecker) Option {
}
}

// OptionSetLexer set lexer function and enable it.
func OptionSetLexer(fn LexerFunc) Option {
return func(p *Prompt) error {
p.lexer.SetLexerFunction(fn)
return nil
}
}

// New returns a Prompt with powerful auto-completion.
func New(executor Executor, completer Completer, opts ...Option) *Prompt {
defaultWriter := NewStdoutWriter()
Expand Down Expand Up @@ -297,6 +305,7 @@ func New(executor Executor, completer Completer, opts ...Option) *Prompt {
buf: NewBuffer(),
executor: executor,
history: NewHistory(),
lexer: NewLexer(),
completion: NewCompletionManager(completer, 6),
keyBindMode: EmacsKeyBind, // All the above assume that bash is running in the default Emacs setting
}
Expand Down
23 changes: 12 additions & 11 deletions prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Prompt struct {
renderer *Render
executor Executor
history *History
lexer *Lexer
completion *CompletionManager
keyBindings []KeyBind
ASCIICodeBindings []ASCIICodeBind
Expand All @@ -54,7 +55,7 @@ func (p *Prompt) Run() {
p.completion.Update(*p.buf.Document())
}

p.renderer.Render(p.buf, p.completion)
p.renderer.Render(p.buf, p.completion, p.lexer)

bufCh := make(chan []byte, 128)
stopReadBufCh := make(chan struct{})
Expand All @@ -69,7 +70,7 @@ func (p *Prompt) Run() {
select {
case b := <-bufCh:
if shouldExit, e := p.feed(b); shouldExit {
p.renderer.BreakLine(p.buf)
p.renderer.BreakLine(p.buf, p.lexer)
stopReadBufCh <- struct{}{}
stopHandleSignalCh <- struct{}{}
return
Expand All @@ -85,7 +86,7 @@ func (p *Prompt) Run() {

p.completion.Update(*p.buf.Document())

p.renderer.Render(p.buf, p.completion)
p.renderer.Render(p.buf, p.completion, p.lexer)

if p.exitChecker != nil && p.exitChecker(e.input, true) {
p.skipTearDown = true
Expand All @@ -97,13 +98,13 @@ func (p *Prompt) Run() {
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh)
} else {
p.completion.Update(*p.buf.Document())
p.renderer.Render(p.buf, p.completion)
p.renderer.Render(p.buf, p.completion, p.lexer)
}
case w := <-winSizeCh:
p.renderer.UpdateWinSize(w)
p.renderer.Render(p.buf, p.completion)
p.renderer.Render(p.buf, p.completion, p.lexer)
case code := <-exitCh:
p.renderer.BreakLine(p.buf)
p.renderer.BreakLine(p.buf, p.lexer)
p.tearDown()
os.Exit(code)
default:
Expand All @@ -121,15 +122,15 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {

switch key {
case Enter, ControlJ, ControlM:
p.renderer.BreakLine(p.buf)
p.renderer.BreakLine(p.buf, p.lexer)

exec = &Exec{input: p.buf.Text()}
p.buf = NewBuffer()
if exec.input != "" {
p.history.Add(exec.input)
}
case ControlC:
p.renderer.BreakLine(p.buf)
p.renderer.BreakLine(p.buf, p.lexer)
p.buf = NewBuffer()
p.history.Clear()
case Up, ControlP:
Expand Down Expand Up @@ -240,7 +241,7 @@ func (p *Prompt) Input() string {
p.completion.Update(*p.buf.Document())
}

p.renderer.Render(p.buf, p.completion)
p.renderer.Render(p.buf, p.completion, p.lexer)
bufCh := make(chan []byte, 128)
stopReadBufCh := make(chan struct{})
go p.readBuffer(bufCh, stopReadBufCh)
Expand All @@ -249,7 +250,7 @@ func (p *Prompt) Input() string {
select {
case b := <-bufCh:
if shouldExit, e := p.feed(b); shouldExit {
p.renderer.BreakLine(p.buf)
p.renderer.BreakLine(p.buf, p.lexer)
stopReadBufCh <- struct{}{}
return ""
} else if e != nil {
Expand All @@ -258,7 +259,7 @@ func (p *Prompt) Input() string {
return e.input
} else {
p.completion.Update(*p.buf.Document())
p.renderer.Render(p.buf, p.completion)
p.renderer.Render(p.buf, p.completion, p.lexer)
}
default:
time.Sleep(10 * time.Millisecond)
Expand Down
67 changes: 60 additions & 7 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package prompt

import (
"runtime"
"strings"

"github.com/c-bata/go-prompt/internal/debug"
runewidth "github.com/mattn/go-runewidth"
Expand Down Expand Up @@ -167,7 +168,7 @@ func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
}

// Render renders to the console.
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
func (r *Render) Render(buffer *Buffer, completion *CompletionManager, lexer *Lexer) {
// In situations where a pseudo tty is allocated (e.g. within a docker container),
// window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions.
if r.col == 0 {
Expand All @@ -194,9 +195,25 @@ func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
defer r.out.ShowCursor()

r.renderPrefix()
r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
r.out.WriteStr(line)

if lexer.IsEnabled {
processed := lexer.Process(line)
var s = line

for _, v := range processed {
a := strings.SplitAfter(s, v.Text)
s = strings.TrimPrefix(s, a[0])

r.out.SetColor(v.Color, r.inputBGColor, false)
r.out.WriteStr(a[0])
}
} else {
r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
r.out.WriteStr(line)
}

r.out.SetColor(DefaultColor, DefaultColor, false)

r.lineWrap(cursor)

r.out.EraseDown()
Expand All @@ -213,7 +230,25 @@ func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
cursor += runewidth.StringWidth(suggest.Text)

rest := buffer.Document().TextAfterCursor()
r.out.WriteStr(rest)

if lexer.IsEnabled {
processed := lexer.Process(rest)

var s = rest

for _, v := range processed {
a := strings.SplitAfter(s, v.Text)
s = strings.TrimPrefix(s, a[0])

r.out.SetColor(v.Color, r.inputBGColor, false)
r.out.WriteStr(a[0])
}
} else {
r.out.WriteStr(rest)
}

r.out.SetColor(DefaultColor, DefaultColor, false)

cursor += runewidth.StringWidth(rest)
r.lineWrap(cursor)

Expand All @@ -223,14 +258,32 @@ func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
}

// BreakLine to break line.
func (r *Render) BreakLine(buffer *Buffer) {
func (r *Render) BreakLine(buffer *Buffer, lexer *Lexer) {
// Erasing and Render
cursor := runewidth.StringWidth(buffer.Document().TextBeforeCursor()) + runewidth.StringWidth(r.getCurrentPrefix())
r.clear(cursor)

r.renderPrefix()
r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
r.out.WriteStr(buffer.Document().Text + "\n")

if lexer.IsEnabled {
processed := lexer.Process(buffer.Document().Text + "\n")

var s = buffer.Document().Text + "\n"

for _, v := range processed {
a := strings.SplitAfter(s, v.Text)
s = strings.TrimPrefix(s, a[0])

r.out.SetColor(v.Color, r.inputBGColor, false)
r.out.WriteStr(a[0])
}
} else {
r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
r.out.WriteStr(buffer.Document().Text + "\n")
}

r.out.SetColor(DefaultColor, DefaultColor, false)

debug.AssertNoError(r.out.Flush())
if r.breakLineCallback != nil {
r.breakLineCallback(buffer.Document())
Expand Down
9 changes: 5 additions & 4 deletions render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ func TestBreakLineCallback(t *testing.T) {
col: 1,
}
b := NewBuffer()
r.BreakLine(b)
l := NewLexer()
r.BreakLine(b, l)

if i != 0 {
t.Errorf("i should initially be 0, before applying a break line callback")
Expand All @@ -105,9 +106,9 @@ func TestBreakLineCallback(t *testing.T) {
r.breakLineCallback = func(doc *Document) {
i++
}
r.BreakLine(b)
r.BreakLine(b)
r.BreakLine(b)
r.BreakLine(b, l)
r.BreakLine(b, l)
r.BreakLine(b, l)

if i != 3 {
t.Errorf("BreakLine callback not called, i should be 3")
Expand Down