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  }