diff --git a/README.md b/README.md index a730de81..6616026d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,16 @@ It's a great library but it's been abandoned for quite a while. This project aims to continue its development. +The library has been rewritten in many aspects, fixing existing bugs and adding new essential functionality. + +Most notable changes include: +- Support for custom syntax highlighting with a lexer +- Multiline editing +- A scrolling buffer is used for displaying the current content which makes it possible to edit text of arbitrary length (only the visible part of the text is rendered) +- Support for automatic indentation when pressing Enter and the input is incomplete or for executing the input when it is complete. This is determined by a custom callback function. + +I highly encourage you to see the [changelog](CHANGELOG.md) which fully documents the changes that have been made. + --- A library for building powerful interactive prompts inspired by [python-prompt-toolkit](https://github.com/jonathanslenders/python-prompt-toolkit), @@ -49,9 +59,21 @@ func main() { ## Features +### Automatic indentation with a custom callback + +![automatic indentation](readme/automatic-indentation.gif) + +### Multiline editing with scrolling + +![multiline editing](readme/multiline-editing.gif) + +### Custom syntax highlighting + +![syntax highlighting](readme/syntax-highlighting.gif) + ### Powerful auto-completion -[![demo](https://github.com/c-bata/assets/raw/master/go-prompt/kube-prompt.gif)](https://github.com/c-bata/kube-prompt) +[![autocompletion](https://github.com/c-bata/assets/raw/master/go-prompt/kube-prompt.gif)](https://github.com/c-bata/kube-prompt) (This is a GIF animation of kube-prompt.) diff --git a/_example/README.md b/_example/README.md index 02fe6057..f32f422e 100644 --- a/_example/README.md +++ b/_example/README.md @@ -9,6 +9,12 @@ Uses a custom lexer that colours every character with an even index green. Shows you how to hook up a custom lexer for syntax highlighting. +## automatic indenter + +Inserts a newline and indentation when the Enter key is pressed unless the input ends with a curly brace `}` and the amount of opening and ending braces is the same (then it gets printed). + +Shows you how to define a custom callback which determines whether the input is complete and should be executed or a newline with indentation should be inserted (after Enter has been pressed). + ## bang-executor Inserts a newline when the Enter key is pressed unless the input ends with an exclamation point `!` (then it gets printed). diff --git a/_example/automatic-indenter/main.go b/_example/automatic-indenter/main.go new file mode 100644 index 00000000..f14f4646 --- /dev/null +++ b/_example/automatic-indenter/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "strings" + "unicode/utf8" + + "github.com/elk-language/go-prompt" +) + +func main() { + p := prompt.New( + executor, + prompt.WithPrefix(">>> "), + prompt.WithExecuteOnEnterCallback(ExecuteOnEnter), + ) + + p.Run() +} + +func ExecuteOnEnter(input string, indentSize int) (int, bool) { + lines := strings.SplitAfter(input, "\n") + var spaces int + if len(lines) > 0 { + lastLine := lines[len(lines)-1] + for _, char := range lastLine { + if char == '}' { + spaces -= 2 * indentSize + break + } + if char != ' ' { + break + } + spaces++ + } + } + + char, _ := utf8.DecodeLastRuneInString(input) + return 1 + spaces/indentSize, char == '}' && strings.Count(input, "}") == strings.Count(input, "{") +} + +func executor(s string) { + fmt.Println("Your input: " + s) +} diff --git a/_example/bang-executor/main.go b/_example/bang-executor/main.go index b6bbb3c0..df4caf6f 100644 --- a/_example/bang-executor/main.go +++ b/_example/bang-executor/main.go @@ -23,5 +23,5 @@ func ExecuteOnEnter(input string, indentSize int) (int, bool) { } func executor(s string) { - fmt.Println("You printed: " + s) + fmt.Println("Your input: " + s) } diff --git a/_example/build.sh b/_example/build.sh index 8c561684..6472f4e9 100755 --- a/_example/build.sh +++ b/_example/build.sh @@ -12,3 +12,4 @@ 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 go build -o ${BIN_DIR}/bang-executor ${DIR}/bang-executor/main.go +go build -o ${BIN_DIR}/automatic-indenter ${DIR}/automatic-indenter/main.go diff --git a/_example/even-lexer/main.go b/_example/even-lexer/main.go index 0ca21956..3805848f 100644 --- a/_example/even-lexer/main.go +++ b/_example/even-lexer/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "unicode" "unicode/utf8" "github.com/elk-language/go-prompt" @@ -11,13 +12,15 @@ import ( func main() { p := prompt.New( executor, - prompt.WithLexer(prompt.NewEagerLexer(lexer)), + prompt.WithLexer(prompt.NewEagerLexer(wordLexer)), + prompt.WithLexer(prompt.NewEagerLexer(charLexer)), // the last one overrides the other ) p.Run() } -func lexer(line string) []prompt.Token { +// colors every other character green +func charLexer(line string) []prompt.Token { var elements []prompt.Token for i, value := range line { @@ -37,6 +40,47 @@ func lexer(line string) []prompt.Token { return elements } +// colors every other word green +func wordLexer(line string) []prompt.Token { + if len(line) == 0 { + return nil + } + + var elements []prompt.Token + var currentByte strings.ByteNumber + var wordIndex int + var lastChar rune + + var color prompt.Color + for i, char := range line { + currentByte = strings.ByteNumber(i) + if unicode.IsSpace(char) { + if wordIndex%2 == 0 { + color = prompt.Green + } else { + color = prompt.White + } + + element := prompt.NewSimpleToken(color, currentByte) + elements = append(elements, element) + wordIndex++ + continue + } + lastChar = char + } + if !unicode.IsSpace(lastChar) { + if wordIndex%2 == 0 { + color = prompt.Green + } else { + color = prompt.White + } + element := prompt.NewSimpleToken(color, currentByte) + elements = append(elements, element) + } + + return elements +} + func executor(s string) { - fmt.Println("You printed: " + s) + fmt.Println("Your input: " + s) } diff --git a/readme/automatic-indentation.gif b/readme/automatic-indentation.gif new file mode 100644 index 00000000..eb204d33 Binary files /dev/null and b/readme/automatic-indentation.gif differ diff --git a/readme/multiline-editing.gif b/readme/multiline-editing.gif new file mode 100644 index 00000000..96a5071e Binary files /dev/null and b/readme/multiline-editing.gif differ diff --git a/readme/syntax-highlighting.gif b/readme/syntax-highlighting.gif new file mode 100644 index 00000000..d358a62e Binary files /dev/null and b/readme/syntax-highlighting.gif differ