Refactor keybindings
This commit is contained in:
parent
6afdbb6a0b
commit
62d47c3acf
10
README.md
10
README.md
|
@ -24,12 +24,7 @@ Easy building a multi-platform binary of the command line tools because built wi
|
|||
|
||||
![Keyboard shortcuts](./_resources/keyboard-shortcuts.gif)
|
||||
|
||||
go-prompt implements the bash compatible keyboard shortcuts.
|
||||
|
||||
* `Ctrl+A`: Go to the beginning of the line.
|
||||
* `Ctrl+E`: Go to the end of the line.
|
||||
* `Ctrl+K`: Cut the part of the line after the cursor.
|
||||
* etc...
|
||||
You can customize keyboard shortcuts. More details are available from 'KeyBoard Shortcuts' section in Developer Guide.
|
||||
|
||||
#### Easy to use
|
||||
|
||||
|
@ -61,7 +56,7 @@ func main() {
|
|||
|
||||
More practical example is avairable from `_example` directory or [kube-prompt](https://github.com/c-bata/kube-prompt).
|
||||
|
||||
#### Flexible customize
|
||||
#### Flexible customization
|
||||
|
||||
![options](./_resources/prompt-options.png)
|
||||
go-prompt has many color options. All options are listed in [Developer Guide](./example/README.md).
|
||||
|
@ -75,6 +70,7 @@ go-prompt has many color options. All options are listed in [Developer Guide](./
|
|||
|
||||
* If you want to create projects using go-prompt, you might want to look at the [Getting Started](./example/README.md).
|
||||
* If you want to contribute go-prompt, you might want to look at the [Developer Guide](./_tools/README.md).
|
||||
* If you want to know internal API, you might want to look at the [GoDoc](http://godoc.org/github.com/c-bata/go-prompt).
|
||||
|
||||
|
||||
## Author
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package prompt
|
||||
|
||||
var emacsKeyBindings = []KeyBind {
|
||||
// Go to the End of the line
|
||||
{
|
||||
Key: ControlE,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
x := []rune(buf.Document().TextAfterCursor())
|
||||
buf.CursorRight(len(x))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Go to the beginning of the line
|
||||
{
|
||||
Key: ControlA,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
x := []rune(buf.Document().TextBeforeCursor())
|
||||
buf.CursorLeft(len(x))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Cut the Line after the cursor
|
||||
{
|
||||
Key: ControlK,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
x := []rune(buf.Document().TextAfterCursor())
|
||||
buf.Delete(len(x))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Cut/delete the Line before the cursor
|
||||
{
|
||||
Key: ControlU,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
x := []rune(buf.Document().TextBeforeCursor())
|
||||
buf.DeleteBeforeCursor(len(x))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Delete character under the cursor
|
||||
{
|
||||
Key: ControlD,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
if buf.Text() == "" {
|
||||
return buf // This means just exit.
|
||||
}
|
||||
buf.Delete(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Backspace
|
||||
{
|
||||
Key: ControlH,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.DeleteBeforeCursor(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Right allow: Forward one character
|
||||
{
|
||||
Key: ControlF,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.CursorRight(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Left allow: Backward one character
|
||||
{
|
||||
Key: ControlB,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.CursorLeft(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Cut the Word before the cursor.
|
||||
{
|
||||
Key: ControlW,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.DeleteBeforeCursor(len([]rune(buf.Document().GetWordBeforeCursor())))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package prompt
|
||||
|
||||
type KeyBindFunc func(*Buffer) *Buffer
|
||||
|
||||
type KeyBind struct {
|
||||
Key Key
|
||||
Fn KeyBindFunc
|
||||
}
|
||||
|
||||
var commonKeyBindings = []KeyBind {
|
||||
// Go to the End of the line
|
||||
{
|
||||
Key: End,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
x := []rune(buf.Document().TextAfterCursor())
|
||||
buf.CursorRight(len(x))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Go to the beginning of the line
|
||||
{
|
||||
Key: Home,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
x := []rune(buf.Document().TextBeforeCursor())
|
||||
buf.CursorLeft(len(x))
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Delete character under the cursor
|
||||
{
|
||||
Key: Delete,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.Delete(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Backspace
|
||||
{
|
||||
Key: Backspace,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.DeleteBeforeCursor(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Right allow: Forward one character
|
||||
{
|
||||
Key: Right,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.CursorRight(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
// Left allow: Backward one character
|
||||
{
|
||||
Key: Left,
|
||||
Fn: func(buf *Buffer) *Buffer {
|
||||
buf.CursorLeft(1)
|
||||
return buf
|
||||
},
|
||||
},
|
||||
|
||||
}
|
|
@ -145,6 +145,13 @@ func OptionHistory(x []string) option {
|
|||
}
|
||||
}
|
||||
|
||||
func OptionAddKeyBind(b ...KeyBind) option {
|
||||
return func(p *Prompt) error {
|
||||
p.keyBindings = append(p.keyBindings, b...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func New(executor Executor, completer Completer, opts ...option) *Prompt {
|
||||
pt := &Prompt{
|
||||
in: &VT100Parser{fd: syscall.Stdin},
|
||||
|
|
71
prompt.go
71
prompt.go
|
@ -24,6 +24,7 @@ type Prompt struct {
|
|||
executor Executor
|
||||
history *History
|
||||
completion *CompletionManager
|
||||
keyBindings []KeyBind
|
||||
}
|
||||
|
||||
type Exec struct {
|
||||
|
@ -97,7 +98,7 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
key := p.in.GetKey(b)
|
||||
|
||||
switch key {
|
||||
case ControlJ, Enter:
|
||||
case Enter, ControlJ:
|
||||
if s, ok := p.completion.GetSelectedSuggestion(); ok {
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
|
@ -114,30 +115,12 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
if exec.input != "" {
|
||||
p.history.Add(exec.input)
|
||||
}
|
||||
case ControlA:
|
||||
x := []rune(p.buf.Document().TextBeforeCursor())
|
||||
p.buf.CursorLeft(len(x))
|
||||
case ControlE:
|
||||
x := []rune(p.buf.Document().TextAfterCursor())
|
||||
p.buf.CursorRight(len(x))
|
||||
case ControlK:
|
||||
x := []rune(p.buf.Document().TextAfterCursor())
|
||||
p.buf.Delete(len(x))
|
||||
case ControlU:
|
||||
x := []rune(p.buf.Document().TextBeforeCursor())
|
||||
p.buf.DeleteBeforeCursor(len(x))
|
||||
case ControlC:
|
||||
p.renderer.BreakLine(p.buf)
|
||||
p.buf = NewBuffer()
|
||||
p.completion.Reset()
|
||||
p.history.Clear()
|
||||
case ControlD:
|
||||
if p.buf.Text() == "" {
|
||||
shouldExit = true
|
||||
} else {
|
||||
p.buf.Delete(1)
|
||||
}
|
||||
case Up:
|
||||
case Up, ControlP:
|
||||
if !p.completion.Completing() {
|
||||
if newBuf, changed := p.history.Older(p.buf); changed {
|
||||
p.buf = newBuf
|
||||
|
@ -147,7 +130,7 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
fallthrough
|
||||
case BackTab:
|
||||
p.completion.Previous()
|
||||
case Down:
|
||||
case Down, ControlN:
|
||||
if !p.completion.Completing() {
|
||||
if newBuf, changed := p.history.Newer(p.buf); changed {
|
||||
p.buf = newBuf
|
||||
|
@ -157,20 +140,12 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
fallthrough
|
||||
case Tab, ControlI:
|
||||
p.completion.Next()
|
||||
case Left:
|
||||
p.buf.CursorLeft(1)
|
||||
case Right:
|
||||
p.buf.CursorRight(1)
|
||||
case Backspace:
|
||||
if s, ok := p.completion.GetSelectedSuggestion(); ok {
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
case ControlD:
|
||||
if p.buf.Text() == "" {
|
||||
shouldExit = true
|
||||
return
|
||||
}
|
||||
p.buf.InsertText(s.Text, false, true)
|
||||
p.completion.Reset()
|
||||
}
|
||||
p.buf.DeleteBeforeCursor(1)
|
||||
case NotDefined:
|
||||
if s, ok := p.completion.GetSelectedSuggestion(); ok {
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
|
@ -182,8 +157,38 @@ func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
|||
p.completion.Reset()
|
||||
p.buf.InsertText(string(b), false, true)
|
||||
default:
|
||||
if s, ok := p.completion.GetSelectedSuggestion(); ok {
|
||||
w := p.buf.Document().GetWordBeforeCursor()
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(s.Text, false, true)
|
||||
}
|
||||
p.completion.Reset()
|
||||
}
|
||||
|
||||
for i := range commonKeyBindings {
|
||||
kb := commonKeyBindings[i]
|
||||
if kb.Key == key {
|
||||
p.buf = kb.Fn(p.buf)
|
||||
}
|
||||
}
|
||||
|
||||
// All the above assume that bash is running in the default Emacs setting
|
||||
for i := range emacsKeyBindings {
|
||||
kb := emacsKeyBindings[i]
|
||||
if kb.Key == key {
|
||||
p.buf = kb.Fn(p.buf)
|
||||
}
|
||||
}
|
||||
|
||||
// Custom keybindings
|
||||
for i := range p.keyBindings {
|
||||
kb := p.keyBindings[i]
|
||||
if kb.Key == key {
|
||||
p.buf = kb.Fn(p.buf)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue