github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/elvish/edit/api.go (about)

     1  package edit
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"unicode/utf8"
     7  
     8  	"github.com/u-root/u-root/cmds/elvish/edit/eddefs"
     9  	"github.com/u-root/u-root/cmds/elvish/edit/ui"
    10  	"github.com/u-root/u-root/cmds/elvish/eval"
    11  	"github.com/u-root/u-root/cmds/elvish/eval/vars"
    12  )
    13  
    14  // This file implements types and functions for interactions with the
    15  // Elvishscript runtime.
    16  
    17  var (
    18  	errNotNav             = errors.New("not in navigation mode")
    19  	errLineMustBeString   = errors.New("line must be string")
    20  	errDotMustBeString    = errors.New("dot must be string")
    21  	errDotMustBeInt       = errors.New("dot must be integer")
    22  	errDotOutOfRange      = errors.New("dot out of range")
    23  	errDotInsideCodepoint = errors.New("dot cannot be inside a codepoint")
    24  	errEditorInactive     = errors.New("editor inactive")
    25  )
    26  
    27  // makeNs makes the edit: namespace.
    28  func makeNs(ed *editor) eval.Ns {
    29  	ns := eval.NewNs()
    30  
    31  	// TODO(xiaq): Everything here should be registered to some registry instead
    32  	// of centralized here.
    33  
    34  	// Internal states.
    35  	ns["current-command"] = vars.FromSetGet(
    36  		func(v interface{}) error {
    37  			if !ed.active {
    38  				return errEditorInactive
    39  			}
    40  			if s, ok := v.(string); ok {
    41  				ed.buffer = s
    42  				ed.dot = len(ed.buffer)
    43  			} else {
    44  				return errLineMustBeString
    45  			}
    46  			return nil
    47  		},
    48  		func() interface{} { return ed.buffer },
    49  	)
    50  	ns["-dot"] = vars.FromSetGet(
    51  		func(v interface{}) error {
    52  			s, ok := v.(string)
    53  			if !ok {
    54  				return errDotMustBeString
    55  			}
    56  			i, err := strconv.Atoi(s)
    57  			if err != nil {
    58  				if err.(*strconv.NumError).Err == strconv.ErrRange {
    59  					return errDotOutOfRange
    60  				} else {
    61  					return errDotMustBeInt
    62  				}
    63  			}
    64  			if i < 0 || i > len(ed.buffer) {
    65  				return errDotOutOfRange
    66  			}
    67  			if i < len(ed.buffer) {
    68  				r, _ := utf8.DecodeRuneInString(ed.buffer[i:])
    69  				if r == utf8.RuneError {
    70  					return errDotInsideCodepoint
    71  				}
    72  			}
    73  			ed.dot = i
    74  			return nil
    75  		},
    76  		func() interface{} { return strconv.Itoa(ed.dot) },
    77  	)
    78  	ns["selected-file"] = vars.FromGet(
    79  		func() interface{} {
    80  			if !ed.active {
    81  				throw(errEditorInactive)
    82  			}
    83  			nav, ok := ed.mode.(*navigation)
    84  			if !ok {
    85  				throw(errNotNav)
    86  			}
    87  			return nav.current.selectedName()
    88  		},
    89  	)
    90  
    91  	// Functions.
    92  	fns := map[string]interface{}{
    93  		"binding-table": eddefs.MakeBindingMap,
    94  		"insert-at-dot": ed.InsertAtDot,
    95  		"replace-input": ed.replaceInput,
    96  		"styled":        styled,
    97  		"key":           ui.ToKey,
    98  		"wordify":       wordifyBuiltin,
    99  		"-dump-buf":     ed.dumpBuf,
   100  	}
   101  	ns.AddBuiltinFns("edit:", fns)
   102  
   103  	return ns
   104  }
   105  
   106  // CallFn calls an Fn, displaying its outputs and possible errors as editor
   107  // notifications. It is the preferred way to call a Fn while the editor is
   108  // active.
   109  func (ed *editor) CallFn(fn eval.Callable, args ...interface{}) {
   110  	ports := []*eval.Port{
   111  		eval.DevNullClosedChan, ed.notifyPort, ed.notifyPort,
   112  	}
   113  	// XXX There is no source to pass to NewTopEvalCtx.
   114  	ec := eval.NewTopFrame(ed.evaler, eval.NewInternalSource("[editor]"), ports)
   115  	ex := ec.Call(fn, args, eval.NoOpts)
   116  	if ex != nil {
   117  		ed.Notify("function error: %s", ex.Error())
   118  	}
   119  
   120  	// XXX Concurrency-dangerous!
   121  	ed.refresh(true, true)
   122  }