add ascii-code-bind-option-and-find-current-word-end

This commit is contained in:
Chyroc 2018-04-11 20:14:38 +08:00
parent de51277535
commit 6a9503cb3a
5 changed files with 97 additions and 29 deletions

View File

@ -97,7 +97,7 @@ func (b *Buffer) CursorLeft(count int) {
return return
} }
// CursorRight move to right on the current line. // CursorLineEnd move to right on the current line.
func (b *Buffer) CursorRight(count int) { func (b *Buffer) CursorRight(count int) {
l := b.Document().GetCursorRightPosition(count) l := b.Document().GetCursorRightPosition(count)
b.CursorPosition += l b.CursorPosition += l

View File

@ -4,6 +4,8 @@ import (
"sort" "sort"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"io/ioutil"
"fmt"
) )
// Document has text displayed in terminal and cursor position. // Document has text displayed in terminal and cursor position.
@ -55,6 +57,13 @@ func (d *Document) GetWordBeforeCursor() string {
return x[d.FindStartOfPreviousWord():] 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. // GetWordBeforeCursorWithSpace returns the word before the cursor.
// Unlike GetWordBeforeCursor, it returns string containing space // Unlike GetWordBeforeCursor, it returns string containing space
func (d *Document) GetWordBeforeCursorWithSpace() string { func (d *Document) GetWordBeforeCursorWithSpace() string {
@ -62,6 +71,13 @@ func (d *Document) GetWordBeforeCursorWithSpace() string {
return x[d.FindStartOfPreviousWordWithSpace():] 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 // FindStartOfPreviousWord returns an index relative to the cursor position
// pointing to the start of the previous word. Return `None` if nothing was found. // pointing to the start of the previous word. Return `None` if nothing was found.
func (d *Document) FindStartOfPreviousWord() int { func (d *Document) FindStartOfPreviousWord() int {
@ -76,6 +92,20 @@ func (d *Document) FindStartOfPreviousWord() int {
return 0 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
}
}
return 0
}
// FindStartOfPreviousWordWithSpace is almost the same as FindStartOfPreviousWord. // FindStartOfPreviousWordWithSpace is almost the same as FindStartOfPreviousWord.
// The only difference is to ignore contiguous spaces. // The only difference is to ignore contiguous spaces.
func (d *Document) FindStartOfPreviousWordWithSpace() int { func (d *Document) FindStartOfPreviousWordWithSpace() int {
@ -94,6 +124,28 @@ func (d *Document) FindStartOfPreviousWordWithSpace() int {
return 0 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()
ioutil.WriteFile("/tmp/fff", []byte(fmt.Sprintf("[%s]", x)), 0644)
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. // CurrentLineBeforeCursor returns the text from the start of the line until the cursor.
func (d *Document) CurrentLineBeforeCursor() string { func (d *Document) CurrentLineBeforeCursor() string {
s := strings.Split(d.TextBeforeCursor(), "\n") s := strings.Split(d.TextBeforeCursor(), "\n")

View File

@ -9,6 +9,12 @@ type KeyBind struct {
Fn KeyBindFunc Fn KeyBindFunc
} }
// ASCIICodeBind represents which []byte should do what operation
type ASCIICodeBind struct {
ASCIICode []byte
Fn KeyBindFunc
}
// KeyBindMode to switch a key binding flexibly. // KeyBindMode to switch a key binding flexibly.
type KeyBindMode string type KeyBindMode string
@ -23,45 +29,31 @@ var commonKeyBindings = []KeyBind{
// Go to the End of the line // Go to the End of the line
{ {
Key: End, Key: End,
Fn: func(buf *Buffer) { Fn: GoLineEnd,
x := []rune(buf.Document().TextAfterCursor())
buf.CursorRight(len(x))
},
}, },
// Go to the beginning of the line // Go to the beginning of the line
{ {
Key: Home, Key: Home,
Fn: func(buf *Buffer) { Fn: GoLineBeginning,
x := []rune(buf.Document().TextBeforeCursor())
buf.CursorLeft(len(x))
},
}, },
// Delete character under the cursor // Delete character under the cursor
{ {
Key: Delete, Key: Delete,
Fn: func(buf *Buffer) { Fn: DeleteChar,
buf.Delete(1)
},
}, },
// Backspace // Backspace
{ {
Key: Backspace, Key: Backspace,
Fn: func(buf *Buffer) { Fn: DeleteBeforeChar,
buf.DeleteBeforeCursor(1)
},
}, },
// Right allow: Forward one character // Right allow: Forward one character
{ {
Key: Right, Key: Right,
Fn: func(buf *Buffer) { Fn: GoRightChar,
buf.CursorRight(1)
},
}, },
// Left allow: Backward one character // Left allow: Backward one character
{ {
Key: Left, Key: Left,
Fn: func(buf *Buffer) { Fn: GoLeftChar,
buf.CursorLeft(1)
},
}, },
} }

View File

@ -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. // New returns a Prompt with powerful auto-completion.
func New(executor Executor, completer Completer, opts ...Option) *Prompt { func New(executor Executor, completer Completer, opts ...Option) *Prompt {
pt := &Prompt{ pt := &Prompt{

View File

@ -5,6 +5,7 @@ import (
"log" "log"
"os" "os"
"time" "time"
"bytes"
) )
const ( const (
@ -19,14 +20,15 @@ type Completer func(Document) []Suggest
// Prompt is core struct of go-prompt. // Prompt is core struct of go-prompt.
type Prompt struct { type Prompt struct {
in ConsoleParser in ConsoleParser
buf *Buffer buf *Buffer
renderer *Render renderer *Render
executor Executor executor Executor
history *History history *History
completion *CompletionManager completion *CompletionManager
keyBindings []KeyBind keyBindings []KeyBind
keyBindMode KeyBindMode ASCIICodeBindings []ASCIICodeBind
keyBindMode KeyBindMode
} }
// Exec is the struct contains user input context. // Exec is the struct contains user input context.
@ -140,6 +142,9 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
return return
} }
case NotDefined: case NotDefined:
if p.handleASCIICodeBinding(b) {
return
}
p.buf.InsertText(string(b), false, true) p.buf.InsertText(string(b), false, true)
} }
@ -199,6 +204,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. // Input just returns user input text.
func (p *Prompt) Input() string { func (p *Prompt) Input() string {
if l := os.Getenv(envDebugLogPath); l == "" { if l := os.Getenv(envDebugLogPath); l == "" {