forked from reeflective/readline
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathevents.go
128 lines (107 loc) · 4.51 KB
/
events.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package readline
import (
"bytes"
"regexp"
"strings"
"github.com/reiver/go-caret"
)
// EventCallback is a function that is called with a given key input (as typed by the user in the shell),
// to which is passed the current line and the current cursor position on this line.
// It returns an EventReturn, which specifies a target state (new line/cursor position, exit state, etc).
type EventCallback func(key string, line []rune, cursor int) EventReturn
// EventReturn is a structure returned by the callback event function.
// This is used by readline to determine what state the API should
// return to after the readline event.
type EventReturn struct {
// Widget is the name of a widget to execute when the handler is called.
// This can be used when the user wants an existing widget (eg. 'kill-line',
// 'backward-word', etc) to modify the input line, rather than doing it himself.
// An empty string is not a valid widget.
Widget string
// ForwardKey indicates if the key should be dispatched down to the shell widgets.
// Example:
// A handler modifying the line is bound by a user to the key "^X^Y", in Emacs mode.
// The handler also returns a new line as 'NewLine', and a new cursor pos 'NewPos'.
//
// - If true: The shell first replaces its line and cursor pos with the ones given
// by the event return here. Then, it goes through its local/main keymaps to find
// a widget mapped to '^X^Y', which is also subsequently executed. The latter
// might again update the line and cursor position.
// - If false: The shell replaces its line and cursor pos with the ones given by
// the even return, but will not try to find another widget mapped to '^X^Y'.
ForwardKey bool
// AcceptLine indicates if the shell should return from its read loop,
// eg. close itself. If true, it will return the input line given in NewLine.
AcceptLine bool
// ClearHelpers indicates if completion and hint helpers should be cleared out of display.
ClearHelpers bool
// ToolTip is similar to HintText, except that it is displayed as a prompt
// tooltip (similar to a right-side prompt) rather than below the input line.
ToolTip string
// HintText is a usage string printed below the input line when the callback is executed.
HintText []rune
NewLine []rune // NewLine is the new input line to use after the callback is executed.
NewPos int // NewPos is the new cursor position to use.
}
// AddEventTest registers a bindkey handler for the given keyPress.
// It accepts an optional list of keymap modes for which to register the handler (eg. Vim visual/cmd/insert,
// emacs, completion, history, etc). If no list is passed, the event callback is mapped to all main keymaps
// of the shell, which is either emacs (in Emacs input mode), or viins/vicmd (in Vim input mode).
func (rl *Instance) AddEvent(key string, callback EventCallback, keymaps ...keymapMode) {
if len(keymaps) == 0 {
keymaps = append(keymaps, emacs)
keymaps = append(keymaps, viins)
}
// Prepare the caret decoder to be used.
buf := new(bytes.Buffer)
decoder := caret.Decoder{Writer: buf}
// Add the callback to all keymaps
for _, mode := range keymaps {
if widgets, found := rl.widgets[mode]; found {
rl.bindWidget(key, "", &widgets, decoder, buf)
}
}
}
// DelEventTest deregisters an existing bindkey handler.
// It accepts an optional list of keymaps for which to deregister the handler.
// If this list is empty (or not passed), the bindkey handler is deregistered
// of all keymaps in which it is present. If the list is not empty, the bindkey
// handler is only deregistered from those keymaps, if it is found in them.
func (rl *Instance) DelEvent(key string, keymaps ...keymapMode) {
if len(keymaps) == 0 {
for mode := range rl.config.Keymaps {
keymaps = append(keymaps, mode)
}
}
// Decode the key
buf := new(bytes.Buffer)
decoder := caret.Decoder{Writer: buf}
// Only decode the keys if the keybind is not a regexp expression
if !strings.HasPrefix(key, "[") || !strings.HasSuffix(key, "]") {
if _, err := decoder.Write([]byte(key)); err == nil {
key = buf.String()
buf.Reset()
}
}
reg, err := regexp.Compile(key)
if err != nil || reg == nil {
return
}
// Remove the callback from all keymaps
for _, mode := range keymaps {
if widgets, found := rl.widgets[mode]; found {
delete(widgets, reg)
}
}
}
func (rl *Instance) useEventHelpers(event EventReturn) {
if event.ClearHelpers {
rl.resetHelpers()
}
if len(event.HintText) > 0 {
rl.hint = event.HintText
}
if len(event.ToolTip) > 0 {
rl.Prompt.tooltip = event.ToolTip
}
}