Merge pull request #65 from Chyroc/add-ascii-code-bind-option-and-find-current-word-end
Add ascii code bind option and find current word end
This commit is contained in:
commit
ae619c5ea4
52
document.go
52
document.go
@ -55,6 +55,13 @@ func (d *Document) GetWordBeforeCursor() string {
|
||||
return x[d.FindStartOfPreviousWord():]
|
||||
}
|
||||
|
||||
// GetWordAfterCursor returns the word after the cursor.
|
||||
// If we have whitespace after the cursor this returns an empty string.
|
||||
func (d *Document) GetWordAfterCursor() string {
|
||||
x := d.TextAfterCursor()
|
||||
return x[:d.FindEndOfCurrentWord()]
|
||||
}
|
||||
|
||||
// GetWordBeforeCursorWithSpace returns the word before the cursor.
|
||||
// Unlike GetWordBeforeCursor, it returns string containing space
|
||||
func (d *Document) GetWordBeforeCursorWithSpace() string {
|
||||
@ -62,6 +69,13 @@ func (d *Document) GetWordBeforeCursorWithSpace() string {
|
||||
return x[d.FindStartOfPreviousWordWithSpace():]
|
||||
}
|
||||
|
||||
// GetWordAfterCursorWithSpace returns the word after the cursor.
|
||||
// Unlike GetWordAfterCursor, it returns string containing space
|
||||
func (d *Document) GetWordAfterCursorWithSpace() string {
|
||||
x := d.TextAfterCursor()
|
||||
return x[:d.FindEndOfCurrentWordWithSpace()]
|
||||
}
|
||||
|
||||
// FindStartOfPreviousWord returns an index relative to the cursor position
|
||||
// pointing to the start of the previous word. Return `None` if nothing was found.
|
||||
func (d *Document) FindStartOfPreviousWord() int {
|
||||
@ -76,6 +90,23 @@ func (d *Document) FindStartOfPreviousWord() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// FindEndOfCurrentWord returns an index relative to the cursor position
|
||||
// pointing to the end of the current word. Return `None` if nothing was found.
|
||||
func (d *Document) FindEndOfCurrentWord() int {
|
||||
// Reverse the text before the cursor, in order to do an efficient backwards search.
|
||||
x := d.TextAfterCursor()
|
||||
l := len(x)
|
||||
for i := 0; i < l; i++ {
|
||||
if x[i:i+1] == " " {
|
||||
return i
|
||||
}
|
||||
if i == l-1 {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// FindStartOfPreviousWordWithSpace is almost the same as FindStartOfPreviousWord.
|
||||
// The only difference is to ignore contiguous spaces.
|
||||
func (d *Document) FindStartOfPreviousWordWithSpace() int {
|
||||
@ -94,6 +125,27 @@ func (d *Document) FindStartOfPreviousWordWithSpace() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// FindEndOfCurrentWordWithSpace is almost the same as FindEndOfCurrentWord.
|
||||
// The only difference is to ignore contiguous spaces.
|
||||
func (d *Document) FindEndOfCurrentWordWithSpace() int {
|
||||
// Reverse the text before the cursor, in order to do an efficient backwards search.
|
||||
x := d.TextAfterCursor()
|
||||
l := len(x)
|
||||
appear := false
|
||||
for i := 0; i < l; i++ {
|
||||
if x[i:i+1] != " " {
|
||||
appear = true
|
||||
}
|
||||
if x[i:i+1] == " " && appear {
|
||||
return i
|
||||
}
|
||||
if i == l-1 {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CurrentLineBeforeCursor returns the text from the start of the line until the cursor.
|
||||
func (d *Document) CurrentLineBeforeCursor() string {
|
||||
s := strings.Split(d.TextBeforeCursor(), "\n")
|
||||
|
172
document_test.go
172
document_test.go
@ -88,6 +88,49 @@ func TestDocument_GetWordBeforeCursor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_GetWordAfterCursor(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple bana"),
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple "),
|
||||
},
|
||||
expected: "bana",
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple"),
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("ap"),
|
||||
},
|
||||
expected: "ple",
|
||||
},
|
||||
}
|
||||
|
||||
for k, p := range pattern {
|
||||
ac := p.document.GetWordAfterCursor()
|
||||
if ac != p.expected {
|
||||
t.Errorf("[%d]Should be %#v, got %#v", k, p.expected, ac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_GetWordBeforeCursorWithSpace(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
@ -117,6 +160,49 @@ func TestDocument_GetWordBeforeCursorWithSpace(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_GetWordAfterCursorWithSpace(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple bana"),
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple "),
|
||||
},
|
||||
expected: "bana",
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple"),
|
||||
},
|
||||
expected: " bana",
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("ap"),
|
||||
},
|
||||
expected: "ple",
|
||||
},
|
||||
}
|
||||
|
||||
for k, p := range pattern {
|
||||
ac := p.document.GetWordAfterCursorWithSpace()
|
||||
if ac != p.expected {
|
||||
t.Errorf("[%d]Should be %#v, got %#v", k, p.expected, ac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_FindStartOfPreviousWord(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
@ -146,6 +232,49 @@ func TestDocument_FindStartOfPreviousWord(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_FindEndOfCurrentWord(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple bana"),
|
||||
},
|
||||
expected: len(""),
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple "),
|
||||
},
|
||||
expected: len("bana"),
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple"),
|
||||
},
|
||||
expected: len(""),
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("ap"),
|
||||
},
|
||||
expected: len("ple"),
|
||||
},
|
||||
}
|
||||
|
||||
for k, p := range pattern {
|
||||
ac := p.document.FindEndOfCurrentWord()
|
||||
if ac != p.expected {
|
||||
t.Errorf("[%d]Should be %#v, got %#v", k, p.expected, ac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_FindStartOfPreviousWordWithSpace(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
@ -175,6 +304,49 @@ func TestDocument_FindStartOfPreviousWordWithSpace(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_FindEndOfCurrentWordWithSpace(t *testing.T) {
|
||||
pattern := []struct {
|
||||
document *Document
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple bana"),
|
||||
},
|
||||
expected: len(""),
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple "),
|
||||
},
|
||||
expected: len("bana"),
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("apple"),
|
||||
},
|
||||
expected: len(" bana"),
|
||||
},
|
||||
{
|
||||
document: &Document{
|
||||
Text: "apple bana",
|
||||
CursorPosition: len("ap"),
|
||||
},
|
||||
expected: len("ple"),
|
||||
},
|
||||
}
|
||||
|
||||
for k, p := range pattern {
|
||||
ac := p.document.FindEndOfCurrentWordWithSpace()
|
||||
if ac != p.expected {
|
||||
t.Errorf("[%d]Should be %#v, got %#v", k, p.expected, ac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocument_CurrentLineBeforeCursor(t *testing.T) {
|
||||
d := &Document{
|
||||
Text: "line 1\nline 2\nline 3\nline 4\n",
|
||||
|
32
key_bind.go
32
key_bind.go
@ -9,6 +9,12 @@ type KeyBind struct {
|
||||
Fn KeyBindFunc
|
||||
}
|
||||
|
||||
// ASCIICodeBind represents which []byte should do what operation
|
||||
type ASCIICodeBind struct {
|
||||
ASCIICode []byte
|
||||
Fn KeyBindFunc
|
||||
}
|
||||
|
||||
// KeyBindMode to switch a key binding flexibly.
|
||||
type KeyBindMode string
|
||||
|
||||
@ -23,45 +29,31 @@ var commonKeyBindings = []KeyBind{
|
||||
// Go to the End of the line
|
||||
{
|
||||
Key: End,
|
||||
Fn: func(buf *Buffer) {
|
||||
x := []rune(buf.Document().TextAfterCursor())
|
||||
buf.CursorRight(len(x))
|
||||
},
|
||||
Fn: GoLineEnd,
|
||||
},
|
||||
// Go to the beginning of the line
|
||||
{
|
||||
Key: Home,
|
||||
Fn: func(buf *Buffer) {
|
||||
x := []rune(buf.Document().TextBeforeCursor())
|
||||
buf.CursorLeft(len(x))
|
||||
},
|
||||
Fn: GoLineBeginning,
|
||||
},
|
||||
// Delete character under the cursor
|
||||
{
|
||||
Key: Delete,
|
||||
Fn: func(buf *Buffer) {
|
||||
buf.Delete(1)
|
||||
},
|
||||
Fn: DeleteChar,
|
||||
},
|
||||
// Backspace
|
||||
{
|
||||
Key: Backspace,
|
||||
Fn: func(buf *Buffer) {
|
||||
buf.DeleteBeforeCursor(1)
|
||||
},
|
||||
Fn: DeleteBeforeChar,
|
||||
},
|
||||
// Right allow: Forward one character
|
||||
{
|
||||
Key: Right,
|
||||
Fn: func(buf *Buffer) {
|
||||
buf.CursorRight(1)
|
||||
},
|
||||
Fn: GoRightChar,
|
||||
},
|
||||
// Left allow: Backward one character
|
||||
{
|
||||
Key: Left,
|
||||
Fn: func(buf *Buffer) {
|
||||
buf.CursorLeft(1)
|
||||
},
|
||||
Fn: GoLeftChar,
|
||||
},
|
||||
}
|
||||
|
48
key_bind_func.go
Normal file
48
key_bind_func.go
Normal file
@ -0,0 +1,48 @@
|
||||
package prompt
|
||||
|
||||
// CursorLineEnd Go to the End of the line
|
||||
func GoLineEnd(buf *Buffer) {
|
||||
x := []rune(buf.Document().TextAfterCursor())
|
||||
buf.CursorRight(len(x))
|
||||
}
|
||||
|
||||
// GoLineBeginning Go to the beginning of the line
|
||||
func GoLineBeginning(buf *Buffer) {
|
||||
x := []rune(buf.Document().TextBeforeCursor())
|
||||
buf.CursorLeft(len(x))
|
||||
}
|
||||
|
||||
// DeleteChar Delete character under the cursor
|
||||
func DeleteChar(buf *Buffer) {
|
||||
buf.Delete(1)
|
||||
}
|
||||
|
||||
// DeleteWord Delete word before the cursor
|
||||
func DeleteWord(buf *Buffer) {
|
||||
buf.DeleteBeforeCursor(len([]rune(buf.Document().TextBeforeCursor())) - buf.Document().FindStartOfPreviousWordWithSpace())
|
||||
}
|
||||
|
||||
// DeleteBeforeChar Go to Backspace
|
||||
func DeleteBeforeChar(buf *Buffer) {
|
||||
buf.DeleteBeforeCursor(1)
|
||||
}
|
||||
|
||||
// GoRightChar Forward one character
|
||||
func GoRightChar(buf *Buffer) {
|
||||
buf.CursorRight(1)
|
||||
}
|
||||
|
||||
// GoLeftChar Backward one character
|
||||
func GoLeftChar(buf *Buffer) {
|
||||
buf.CursorLeft(1)
|
||||
}
|
||||
|
||||
// GoRightWord Forward one word
|
||||
func GoRightWord(buf *Buffer) {
|
||||
buf.CursorRight(buf.Document().FindEndOfCurrentWordWithSpace())
|
||||
}
|
||||
|
||||
// GoLeftWord Backward one word
|
||||
func GoLeftWord(buf *Buffer) {
|
||||
buf.CursorLeft(len([]rune(buf.Document().TextBeforeCursor())) - buf.Document().FindStartOfPreviousWordWithSpace())
|
||||
}
|
@ -209,6 +209,14 @@ func OptionAddKeyBind(b ...KeyBind) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionAddKeyBind to set a custom key bind.
|
||||
func OptionAddASCIICodeBind(b ...ASCIICodeBind) Option {
|
||||
return func(p *Prompt) error {
|
||||
p.ASCIICodeBindings = append(p.ASCIICodeBindings, b...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a Prompt with powerful auto-completion.
|
||||
func New(executor Executor, completer Completer, opts ...Option) *Prompt {
|
||||
pt := &Prompt{
|
||||
|
32
prompt.go
32
prompt.go
@ -1,6 +1,7 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
@ -19,14 +20,15 @@ type Completer func(Document) []Suggest
|
||||
|
||||
// Prompt is core struct of go-prompt.
|
||||
type Prompt struct {
|
||||
in ConsoleParser
|
||||
buf *Buffer
|
||||
renderer *Render
|
||||
executor Executor
|
||||
history *History
|
||||
completion *CompletionManager
|
||||
keyBindings []KeyBind
|
||||
keyBindMode KeyBindMode
|
||||
in ConsoleParser
|
||||
buf *Buffer
|
||||
renderer *Render
|
||||
executor Executor
|
||||
history *History
|
||||
completion *CompletionManager
|
||||
keyBindings []KeyBind
|
||||
ASCIICodeBindings []ASCIICodeBind
|
||||
keyBindMode KeyBindMode
|
||||
}
|
||||
|
||||
// Exec is the struct contains user input context.
|
||||
@ -142,6 +144,9 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
||||
return
|
||||
}
|
||||
case NotDefined:
|
||||
if p.handleASCIICodeBinding(b) {
|
||||
return
|
||||
}
|
||||
p.buf.InsertText(string(b), false, true)
|
||||
}
|
||||
|
||||
@ -201,6 +206,17 @@ func (p *Prompt) handleKeyBinding(key Key) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prompt) handleASCIICodeBinding(b []byte) bool {
|
||||
checked := false
|
||||
for _, kb := range p.ASCIICodeBindings {
|
||||
if bytes.Compare(kb.ASCIICode, b) == 0 {
|
||||
kb.Fn(p.buf)
|
||||
checked = true
|
||||
}
|
||||
}
|
||||
return checked
|
||||
}
|
||||
|
||||
// Input just returns user input text.
|
||||
func (p *Prompt) Input() string {
|
||||
if l := os.Getenv(envDebugLogPath); l == "" {
|
||||
|
Loading…
Reference in New Issue
Block a user