99
1010 "github.com/reeflective/readline/inputrc"
1111 "github.com/reeflective/readline/internal/strutil"
12+ "github.com/rivo/uniseg"
1213)
1314
1415const (
@@ -22,7 +23,7 @@ var Stdin io.ReadCloser = os.Stdin
2223
2324var rxRcvCursorPos = regexp .MustCompile (`\x1b\[([0-9]+);([0-9]+)R` )
2425
25- // Keys is used read, manage and use keys input by the shell user.
26+ // Keys is used to read, manage and use keys input by the shell user.
2627type Keys struct {
2728 buf []byte // Keys read and waiting to be used.
2829 matched []rune // Keys that have been successfully matched against a bind.
@@ -97,6 +98,20 @@ func WaitAvailableKeys(keys *Keys, cfg *inputrc.Config) {
9798 }
9899}
99100
101+ // PeekKey returns the first key in the stack, without removing it.
102+ func PeekKey (keys * Keys ) (key byte , empty bool ) {
103+ switch {
104+ case len (keys .buf ) > 0 :
105+ key = keys .buf [0 ]
106+ case len (keys .macroKeys ) > 0 :
107+ key = byte (keys .macroKeys [0 ])
108+ default :
109+ return byte (0 ), true
110+ }
111+
112+ return key , false
113+ }
114+
100115// PopKey is used to pop a key off the key stack without
101116// yet marking this key as having matched a bind command.
102117func PopKey (keys * Keys ) (key byte , empty bool ) {
@@ -114,18 +129,40 @@ func PopKey(keys *Keys) (key byte, empty bool) {
114129 return key , false
115130}
116131
117- // PeekKey returns the first key in the stack, without removing it.
118- func PeekKey (keys * Keys ) (key byte , empty bool ) {
132+ // PeekChar returns the first character in the stack, without
133+ // removing it (in order to provide Unicode support).
134+ func PeekChar (keys * Keys ) (char []byte , empty bool ) {
119135 switch {
120136 case len (keys .buf ) > 0 :
121- key = keys .buf [0 ]
137+ // Use the uniseg library to correctly determine where each characters stop.
138+ char , _ , _ , _ = uniseg .FirstGraphemeCluster (keys .buf , - 1 )
139+ return char , false
140+
122141 case len (keys .macroKeys ) > 0 :
123- key = byte (keys .macroKeys [0 ])
142+ // Macros already store keys as runes, just pick one.
143+ // Maybe long-term we should find a remedy to this: storing
144+ // macro keys as runes is inconsistent with the rest of our code.
145+ return []byte (string (keys .macroKeys [0 ])), false
146+
124147 default :
125- return byte ( 0 ) , true
148+ return nil , true
126149 }
150+ }
127151
128- return key , false
152+ // PopChar is used to pop a character off the key stack without
153+ // yet marking this key as having matched a bind command.
154+ func PopChar (keys * Keys ) (char []byte , empty bool ) {
155+ switch {
156+ case len (keys .buf ) > 0 :
157+ char , keys .buf , _ , _ = uniseg .FirstGraphemeCluster (keys .buf , - 1 )
158+ case len (keys .macroKeys ) > 0 :
159+ char = []byte (string (keys .macroKeys [0 ]))
160+ keys .macroKeys = keys .macroKeys [1 :]
161+ default :
162+ return nil , true
163+ }
164+
165+ return char , false
129166}
130167
131168// MatchedKeys is used to indicate how many keys have been evaluated against the shell
0 commit comments