github.com/aretext/aretext@v1.3.0/input/helpers.go (about) 1 package input 2 3 import ( 4 "strconv" 5 "strings" 6 "unicode/utf8" 7 8 "github.com/gdamore/tcell/v2" 9 10 "github.com/aretext/aretext/clipboard" 11 "github.com/aretext/aretext/input/engine" 12 ) 13 14 func eventKeyToEngineEvent(eventKey *tcell.EventKey) engine.Event { 15 if eventKey.Key() == tcell.KeyRune { 16 return runeToEngineEvent(eventKey.Rune()) 17 } else { 18 return keyToEngineEvent(eventKey.Key()) 19 } 20 } 21 22 func keyToEngineEvent(key tcell.Key) engine.Event { 23 return engine.Event(int64(key) << 32) 24 } 25 26 func runeToEngineEvent(r rune) engine.Event { 27 return engine.Event((int64(tcell.KeyRune) << 32) | int64(r)) 28 } 29 30 func engineEventToKey(engineEvent engine.Event) tcell.Key { 31 return tcell.Key(engineEvent >> 32) 32 } 33 34 func engineEventToRune(engineEvent engine.Event) rune { 35 return rune(engineEvent & 0xFFFFFFFF) 36 } 37 38 const ( 39 captureIdVerbCount = iota 40 captureIdObjectCount 41 captureIdClipboardPage 42 captureIdMatchChar 43 captureIdReplaceChar 44 captureIdInsertChar 45 ) 46 47 // Pre-compute and share these expressions to reduce number of allocations. 48 var verbCountExpr, objectCountExpr, clipboardPageExpr, matchCharExpr, replaceCharExpr, insertExpr engine.Expr 49 50 func init() { 51 verbCountExpr = engine.OptionExpr{ 52 Child: engine.CaptureExpr{ 53 CaptureId: captureIdVerbCount, 54 Child: engine.ConcatExpr{ 55 Children: []engine.Expr{ 56 engine.EventRangeExpr{ 57 StartEvent: runeToEngineEvent('1'), 58 EndEvent: runeToEngineEvent('9'), 59 }, 60 engine.StarExpr{ 61 Child: engine.EventRangeExpr{ 62 StartEvent: runeToEngineEvent('0'), 63 EndEvent: runeToEngineEvent('9'), 64 }, 65 }, 66 }, 67 }, 68 }, 69 } 70 71 objectCountExpr = engine.OptionExpr{ 72 Child: engine.CaptureExpr{ 73 CaptureId: captureIdObjectCount, 74 Child: engine.ConcatExpr{ 75 Children: []engine.Expr{ 76 engine.EventRangeExpr{ 77 StartEvent: runeToEngineEvent('1'), 78 EndEvent: runeToEngineEvent('9'), 79 }, 80 engine.StarExpr{ 81 Child: engine.EventRangeExpr{ 82 StartEvent: runeToEngineEvent('0'), 83 EndEvent: runeToEngineEvent('9'), 84 }, 85 }, 86 }, 87 }, 88 }, 89 } 90 91 clipboardPageExpr = engine.OptionExpr{ 92 Child: engine.ConcatExpr{ 93 Children: []engine.Expr{ 94 engine.EventExpr{ 95 Event: runeToEngineEvent('"'), 96 }, 97 engine.CaptureExpr{ 98 CaptureId: captureIdClipboardPage, 99 Child: engine.EventRangeExpr{ 100 StartEvent: runeToEngineEvent('a'), 101 EndEvent: runeToEngineEvent('z'), 102 }, 103 }, 104 }, 105 }, 106 } 107 108 matchCharExpr = engine.CaptureExpr{ 109 CaptureId: captureIdMatchChar, 110 Child: engine.EventRangeExpr{ 111 StartEvent: runeToEngineEvent(rune(0)), 112 EndEvent: runeToEngineEvent(rune(255)), 113 }, 114 } 115 116 replaceCharExpr = engine.CaptureExpr{ 117 CaptureId: captureIdReplaceChar, 118 Child: engine.AltExpr{ 119 Children: []engine.Expr{ 120 engine.EventRangeExpr{ 121 StartEvent: runeToEngineEvent(rune(0)), 122 EndEvent: runeToEngineEvent(rune(255)), 123 }, 124 engine.EventExpr{ 125 Event: keyToEngineEvent(tcell.KeyEnter), 126 }, 127 engine.EventExpr{ 128 Event: keyToEngineEvent(tcell.KeyTab), 129 }, 130 }, 131 }, 132 } 133 134 insertExpr = engine.CaptureExpr{ 135 CaptureId: captureIdInsertChar, 136 Child: engine.EventRangeExpr{ 137 StartEvent: runeToEngineEvent(rune(0)), 138 EndEvent: runeToEngineEvent(utf8.MaxRune), 139 }, 140 } 141 } 142 143 type captureOpts struct { 144 count bool 145 clipboardPage bool 146 matchChar bool 147 replaceChar bool 148 } 149 150 func altExpr(children ...engine.Expr) engine.Expr { 151 return engine.AltExpr{Children: children} 152 } 153 154 func verbCountThenExpr(expr engine.Expr) engine.Expr { 155 return engine.ConcatExpr{Children: []engine.Expr{verbCountExpr, expr}} 156 } 157 158 func runeExpr(r rune) engine.Expr { 159 return engine.EventExpr{Event: runeToEngineEvent(r)} 160 } 161 162 func keyExpr(key tcell.Key) engine.Expr { 163 return engine.EventExpr{Event: keyToEngineEvent(key)} 164 } 165 166 func cmdExpr(verb string, object string, opts captureOpts) engine.Expr { 167 expr := engine.ConcatExpr{Children: make([]engine.Expr, 0, len(verb))} 168 for _, r := range verb { 169 expr.Children = append(expr.Children, engine.EventExpr{ 170 Event: runeToEngineEvent(r), 171 }) 172 } 173 174 if object != "" { 175 verbExpr := expr 176 objExpr := engine.ConcatExpr{Children: make([]engine.Expr, 0, len(object))} 177 for _, r := range object { 178 objExpr.Children = append(objExpr.Children, engine.EventExpr{ 179 Event: runeToEngineEvent(r), 180 }) 181 } 182 183 if opts.count { 184 objExpr = engine.ConcatExpr{Children: []engine.Expr{objectCountExpr, objExpr}} 185 } 186 187 expr = engine.ConcatExpr{Children: []engine.Expr{verbExpr, objExpr}} 188 } 189 190 if opts.count { 191 expr = engine.ConcatExpr{Children: []engine.Expr{verbCountExpr, expr}} 192 } 193 194 if opts.clipboardPage { 195 expr = engine.ConcatExpr{Children: []engine.Expr{clipboardPageExpr, expr}} 196 } 197 198 if opts.matchChar { 199 expr = engine.ConcatExpr{Children: []engine.Expr{expr, matchCharExpr}} 200 } 201 202 if opts.replaceChar { 203 expr = engine.ConcatExpr{Children: []engine.Expr{expr, replaceCharExpr}} 204 } 205 206 return expr 207 } 208 209 func capturesToCommandParams(captures map[engine.CaptureId][]engine.Event) CommandParams { 210 p := CommandParams{ 211 Count: 1, 212 ClipboardPage: clipboard.PageDefault, 213 MatchChar: '\x00', 214 ReplaceChar: '\x00', 215 InsertChar: '\x00', 216 } 217 for captureId, captureEvents := range captures { 218 switch captureId { 219 case captureIdVerbCount, captureIdObjectCount: 220 // Multiply here so that if both verb and object count are provided, 221 // the total count is the product of the two counts. 222 // For example, "2d3w" should delete 2*3=6 words. 223 p.Count *= eventsToCount(captureEvents) 224 case captureIdClipboardPage: 225 p.ClipboardPage = eventsToClipboardPage(captureEvents) 226 case captureIdMatchChar: 227 p.MatchChar = eventsToChar(captureEvents) 228 case captureIdReplaceChar: 229 p.ReplaceChar = eventsToReplaceChar(captureEvents) 230 case captureIdInsertChar: 231 p.InsertChar = eventsToChar(captureEvents) 232 } 233 } 234 return p 235 } 236 237 func eventsToCount(events []engine.Event) uint64 { 238 var sb strings.Builder 239 for _, e := range events { 240 sb.WriteRune(engineEventToRune(e)) 241 } 242 i, err := strconv.Atoi(sb.String()) 243 if err != nil || i < 0 { 244 return 0 245 } 246 return uint64(i) 247 } 248 249 func eventsToClipboardPage(events []engine.Event) clipboard.PageId { 250 if len(events) != 1 { 251 return clipboard.PageNull 252 } 253 return clipboard.PageIdForLetter(engineEventToRune(events[0])) 254 } 255 256 func eventsToChar(events []engine.Event) rune { 257 if len(events) != 1 { 258 return '\x00' 259 } 260 return engineEventToRune(events[0]) 261 } 262 263 func eventsToReplaceChar(events []engine.Event) rune { 264 if len(events) != 1 { 265 return '\x00' 266 } 267 268 switch engineEventToKey(events[0]) { 269 case tcell.KeyEnter: 270 return '\n' 271 case tcell.KeyTab: 272 return '\t' 273 case tcell.KeyRune: 274 return engineEventToRune(events[0]) 275 default: 276 return '\x00' 277 } 278 }