1 package cbind
2
3 import (
4 "errors"
5 "strings"
6 "unicode"
7
8 "github.com/gdamore/tcell/v2"
9 )
10
11
12 const (
13 LabelCtrl = "ctrl"
14 LabelAlt = "alt"
15 LabelMeta = "meta"
16 LabelShift = "shift"
17 )
18
19
20 var ErrInvalidKeyEvent = errors.New("invalid key event")
21
22
23
24
25 var UnifyEnterKeys = true
26
27 var fullKeyNames = map[string]string{
28 "backspace2": "Backspace",
29 "pgup": "PageUp",
30 "pgdn": "PageDown",
31 "esc": "Escape",
32 }
33
34 var ctrlKeys = map[rune]tcell.Key{
35 ' ': tcell.KeyCtrlSpace,
36 'a': tcell.KeyCtrlA,
37 'b': tcell.KeyCtrlB,
38 'c': tcell.KeyCtrlC,
39 'd': tcell.KeyCtrlD,
40 'e': tcell.KeyCtrlE,
41 'f': tcell.KeyCtrlF,
42 'g': tcell.KeyCtrlG,
43 'h': tcell.KeyCtrlH,
44 'i': tcell.KeyCtrlI,
45 'j': tcell.KeyCtrlJ,
46 'k': tcell.KeyCtrlK,
47 'l': tcell.KeyCtrlL,
48 'm': tcell.KeyCtrlM,
49 'n': tcell.KeyCtrlN,
50 'o': tcell.KeyCtrlO,
51 'p': tcell.KeyCtrlP,
52 'q': tcell.KeyCtrlQ,
53 'r': tcell.KeyCtrlR,
54 's': tcell.KeyCtrlS,
55 't': tcell.KeyCtrlT,
56 'u': tcell.KeyCtrlU,
57 'v': tcell.KeyCtrlV,
58 'w': tcell.KeyCtrlW,
59 'x': tcell.KeyCtrlX,
60 'y': tcell.KeyCtrlY,
61 'z': tcell.KeyCtrlZ,
62 '\\': tcell.KeyCtrlBackslash,
63 ']': tcell.KeyCtrlRightSq,
64 '^': tcell.KeyCtrlCarat,
65 '_': tcell.KeyCtrlUnderscore,
66 }
67
68
69 func Decode(s string) (mod tcell.ModMask, key tcell.Key, ch rune, err error) {
70 if len(s) == 0 {
71 return 0, 0, 0, ErrInvalidKeyEvent
72 }
73
74
75 if s[len(s)-1:] == "+" {
76 key = tcell.KeyRune
77 ch = '+'
78
79 if len(s) == 1 {
80 return mod, key, ch, nil
81 } else if len(s) == 2 {
82 return 0, 0, 0, ErrInvalidKeyEvent
83 } else {
84 s = s[:len(s)-2]
85 }
86 }
87
88 split := strings.Split(s, "+")
89 DECODEPIECE:
90 for _, piece := range split {
91
92 pieceLower := strings.ToLower(piece)
93 switch pieceLower {
94 case LabelCtrl:
95 mod |= tcell.ModCtrl
96 continue
97 case LabelAlt:
98 mod |= tcell.ModAlt
99 continue
100 case LabelMeta:
101 mod |= tcell.ModMeta
102 continue
103 case LabelShift:
104 mod |= tcell.ModShift
105 continue
106 }
107
108
109 for shortKey, fullKey := range fullKeyNames {
110 if pieceLower == strings.ToLower(fullKey) {
111 pieceLower = shortKey
112 break
113 }
114 }
115 switch pieceLower {
116 case "backspace":
117 key = tcell.KeyBackspace2
118 continue
119 case "space", "spacebar":
120 key = tcell.KeyRune
121 ch = ' '
122 continue
123 }
124 for k, keyName := range tcell.KeyNames {
125 if pieceLower == strings.ToLower(strings.ReplaceAll(keyName, "-", "+")) {
126 key = k
127 if key < 0x80 {
128 ch = rune(k)
129 }
130 continue DECODEPIECE
131 }
132 }
133
134
135 if len(piece) > 1 {
136 return 0, 0, 0, ErrInvalidKeyEvent
137 }
138
139 key = tcell.KeyRune
140 ch = rune(piece[0])
141 }
142
143 if mod&tcell.ModCtrl != 0 {
144 k, ok := ctrlKeys[unicode.ToLower(ch)]
145 if ok {
146 key = k
147 if UnifyEnterKeys && key == ctrlKeys['j'] {
148 key = tcell.KeyEnter
149 } else if key < 0x80 {
150 ch = rune(key)
151 }
152 }
153 }
154
155 return mod, key, ch, nil
156 }
157
158
159 func Encode(mod tcell.ModMask, key tcell.Key, ch rune) (string, error) {
160 var b strings.Builder
161 var wrote bool
162
163 if mod&tcell.ModCtrl != 0 {
164 if key == tcell.KeyBackspace || key == tcell.KeyTab || key == tcell.KeyEnter {
165 mod ^= tcell.ModCtrl
166 } else {
167 for _, ctrlKey := range ctrlKeys {
168 if key == ctrlKey {
169 mod ^= tcell.ModCtrl
170 break
171 }
172 }
173 }
174 }
175
176 if key != tcell.KeyRune {
177 if UnifyEnterKeys && key == ctrlKeys['j'] {
178 key = tcell.KeyEnter
179 } else if key < 0x80 {
180 ch = rune(key)
181 }
182 }
183
184
185 if mod&tcell.ModCtrl != 0 {
186 b.WriteString(upperFirst(LabelCtrl))
187 wrote = true
188 }
189 if mod&tcell.ModAlt != 0 {
190 if wrote {
191 b.WriteRune('+')
192 }
193 b.WriteString(upperFirst(LabelAlt))
194 wrote = true
195 }
196 if mod&tcell.ModMeta != 0 {
197 if wrote {
198 b.WriteRune('+')
199 }
200 b.WriteString(upperFirst(LabelMeta))
201 wrote = true
202 }
203 if mod&tcell.ModShift != 0 {
204 if wrote {
205 b.WriteRune('+')
206 }
207 b.WriteString(upperFirst(LabelShift))
208 wrote = true
209 }
210
211 if key == tcell.KeyRune && ch == ' ' {
212 if wrote {
213 b.WriteRune('+')
214 }
215 b.WriteString("Space")
216 } else if key != tcell.KeyRune {
217
218 keyName := tcell.KeyNames[key]
219 if keyName == "" {
220 return "", ErrInvalidKeyEvent
221 }
222 keyName = strings.ReplaceAll(keyName, "-", "+")
223 fullKeyName := fullKeyNames[strings.ToLower(keyName)]
224 if fullKeyName != "" {
225 keyName = fullKeyName
226 }
227
228 if wrote {
229 b.WriteRune('+')
230 }
231 b.WriteString(keyName)
232 } else {
233
234 if wrote {
235 b.WriteRune('+')
236 }
237 b.WriteRune(ch)
238 }
239
240 return b.String(), nil
241 }
242
243 func upperFirst(s string) string {
244 if len(s) <= 1 {
245 return strings.ToUpper(s)
246 }
247 return strings.ToUpper(s[:1]) + s[1:]
248 }
249
View as plain text