github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/events/onKeyPress/keypress.go (about) 1 package onkeypress 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 8 "github.com/lmorg/murex/builtins/events" 9 "github.com/lmorg/murex/builtins/pipes/streams" 10 "github.com/lmorg/murex/lang" 11 "github.com/lmorg/murex/lang/ref" 12 "github.com/lmorg/murex/lang/stdio" 13 "github.com/lmorg/murex/lang/types" 14 "github.com/lmorg/murex/shell" 15 "github.com/lmorg/murex/shell/variables" 16 "github.com/lmorg/murex/utils/readline" 17 ) 18 19 const eventType = "onKeyPress" 20 21 func init() { 22 events.AddEventType(eventType, newKeyPress(), nil) 23 } 24 25 // Interrupt is a JSONable structure passed to the murex function 26 type Interrupt struct { 27 Line string 28 Raw string 29 Pos int 30 KeySequence string 31 } 32 33 type keyPressEvent struct { 34 name string 35 keySeq string 36 block []rune 37 fileRef *ref.File 38 } 39 40 type keyPressEvents struct { 41 events []keyPressEvent 42 mutex sync.Mutex 43 } 44 45 func newKeyPress() *keyPressEvents { 46 return new(keyPressEvents) 47 } 48 49 // Add a key to the event list 50 func (evt *keyPressEvents) Add(name, keySeq string, block []rune, fileRef *ref.File) error { 51 if shell.Prompt == nil { 52 return errors.New("unable to register event with readline API") 53 } 54 55 shell.Prompt.AddEvent(keySeq, evt.callback) 56 evt.events = append(evt.events, keyPressEvent{ 57 name: name, 58 keySeq: keySeq, 59 block: block, 60 fileRef: fileRef, 61 }) 62 return nil 63 } 64 65 func (evt *keyPressEvents) Remove(name string) error { 66 remove := func(s []keyPressEvent, i int) []keyPressEvent { 67 s[len(s)-1], s[i] = s[i], s[len(s)-1] 68 return s[:len(s)-1] 69 } 70 71 if shell.Prompt == nil { 72 return errors.New("unable to de-register event with readline API") 73 } 74 75 evt.mutex.Lock() 76 defer evt.mutex.Unlock() 77 78 for i := range evt.events { 79 if evt.events[i].name == name { 80 shell.Prompt.DelEvent(evt.events[i].keySeq) 81 evt.events = remove(evt.events, i) 82 return nil 83 } 84 } 85 86 return fmt.Errorf("unable to delete event as no event found with the name `%s` for event type `%s`", name, eventType) 87 } 88 89 func (evt *keyPressEvents) callback(keyPress string, line []rune, pos int) *readline.EventReturn { 90 var i int 91 92 evt.mutex.Lock() 93 defer evt.mutex.Unlock() 94 95 for i = range evt.events { 96 if evt.events[i].keySeq == keyPress { 97 goto eventFound 98 } 99 } 100 return &readline.EventReturn{ 101 NewLine: line, 102 NewPos: pos, 103 } 104 105 eventFound: 106 block := evt.events[i].block 107 108 interrupt := Interrupt{ 109 Line: variables.ExpandString(string(line)), 110 Raw: string(line), 111 Pos: pos, 112 KeySequence: keyPress, 113 } 114 115 stdout := streams.NewStdin() 116 events.Callback( 117 evt.events[i].name, interrupt, block, evt.events[i].fileRef, stdout, true) 118 119 ret := make(map[string]string) 120 err := stdout.ReadMap(lang.ShellProcess.Config, func(readmap *stdio.Map) { 121 v, _ := types.ConvertGoType(readmap.Value, types.String) 122 ret[readmap.Key] = v.(string) 123 }) 124 if err != nil { 125 return &readline.EventReturn{ 126 HintText: []rune("callback error: " + err.Error()), 127 NewLine: line, 128 NewPos: pos, 129 } 130 } 131 132 forwardKey, err := types.ConvertGoType(ret["ForwardKey"], types.Boolean) 133 if err != nil { 134 return &readline.EventReturn{ 135 HintText: []rune("callback error: " + err.Error()), 136 NewLine: line, 137 NewPos: pos, 138 } 139 } 140 141 clearHelpers, err := types.ConvertGoType(ret["ClearHelpers"], types.Boolean) 142 if err != nil { 143 return &readline.EventReturn{ 144 HintText: []rune("callback error: " + err.Error()), 145 NewLine: line, 146 NewPos: pos, 147 } 148 } 149 150 closeReadline, err := types.ConvertGoType(ret["CloseReadline"], types.Boolean) 151 if err != nil { 152 return &readline.EventReturn{ 153 HintText: []rune("callback error: " + err.Error()), 154 NewLine: line, 155 NewPos: pos, 156 } 157 } 158 159 var newLine []rune 160 if ret["NewLine"] != "" { 161 newLine = []rune(ret["NewLine"]) 162 } else { 163 newLine = line 164 } 165 166 var newPos int 167 if ret["NewPos"] != "" { 168 i, err := types.ConvertGoType(ret["NewPos"], types.Integer) 169 if err != nil { 170 return &readline.EventReturn{ 171 HintText: []rune("callback error: " + err.Error()), 172 NewLine: line, 173 NewPos: pos, 174 } 175 } 176 newPos = i.(int) 177 } else { 178 newPos = pos 179 } 180 181 return &readline.EventReturn{ 182 ForwardKey: forwardKey.(bool), 183 ClearHelpers: clearHelpers.(bool), 184 CloseReadline: closeReadline.(bool), 185 HintText: []rune(ret["HintText"]), 186 NewLine: newLine, 187 NewPos: newPos, 188 } 189 } 190 191 func (evt *keyPressEvents) Dump() map[string]events.DumpT { 192 dump := make(map[string]events.DumpT) 193 194 evt.mutex.Lock() 195 196 for i := range evt.events { 197 dump[evt.events[i].name] = events.DumpT{ 198 Interrupt: evt.events[i].keySeq, 199 Block: string(evt.events[i].block), 200 FileRef: evt.events[i].fileRef, 201 } 202 } 203 204 evt.mutex.Unlock() 205 return dump 206 }