github.com/elves/elvish@v0.15.0/pkg/edit/editor.go (about)

     1  // Package edit implements the line editor for Elvish.
     2  //
     3  // The line editor is based on the cli package, which implements a general,
     4  // Elvish-agnostic line editor, and multiple "addon" packages. This package
     5  // glues them together and provides Elvish bindings for them.
     6  package edit
     7  
     8  import (
     9  	"fmt"
    10  	"sync"
    11  
    12  	"github.com/elves/elvish/pkg/cli"
    13  	"github.com/elves/elvish/pkg/eval"
    14  	"github.com/elves/elvish/pkg/eval/vals"
    15  	"github.com/elves/elvish/pkg/eval/vars"
    16  	"github.com/elves/elvish/pkg/parse"
    17  	"github.com/elves/elvish/pkg/store"
    18  )
    19  
    20  // Editor is the interface line editor for Elvish.
    21  type Editor struct {
    22  	app cli.App
    23  	ns  *eval.Ns
    24  
    25  	excMutex sync.RWMutex
    26  	excList  vals.List
    27  }
    28  
    29  // An interface that wraps notifyf and notifyError. It is only implemented by
    30  // the *Editor type; functions may take a notifier instead of *Editor argument
    31  // to make it clear that they do not depend on other parts of *Editor.
    32  type notifier interface {
    33  	notifyf(format string, args ...interface{})
    34  	notifyError(ctx string, e error)
    35  }
    36  
    37  // NewEditor creates a new editor from input and output terminal files.
    38  func NewEditor(tty cli.TTY, ev *eval.Evaler, st store.Store) *Editor {
    39  	// Declare the Editor with a nil App first; some initialization functions
    40  	// require a notifier as an argument, but does not use it immediately.
    41  	ed := &Editor{excList: vals.EmptyList}
    42  	nb := eval.NsBuilder{}
    43  	appSpec := cli.AppSpec{TTY: tty}
    44  
    45  	hs, err := newHistStore(st)
    46  	if err != nil {
    47  		// TODO(xiaq): Report the error.
    48  	}
    49  
    50  	initHighlighter(&appSpec, ev)
    51  	initMaxHeight(&appSpec, nb)
    52  	initReadlineHooks(&appSpec, ev, nb)
    53  	initAddCmdFilters(&appSpec, ev, nb, hs)
    54  	initInsertAPI(&appSpec, ed, ev, nb)
    55  	initPrompts(&appSpec, ed, ev, nb)
    56  	ed.app = cli.NewApp(appSpec)
    57  
    58  	initExceptionsAPI(ed, nb)
    59  	initVarsAPI(ed, nb)
    60  	initCommandAPI(ed, ev, nb)
    61  	initListings(ed, ev, st, hs, nb)
    62  	initNavigation(ed, ev, nb)
    63  	initCompletion(ed, ev, nb)
    64  	initHistWalk(ed, ev, hs, nb)
    65  	initInstant(ed, ev, nb)
    66  	initMinibuf(ed, ev, nb)
    67  
    68  	initBufferBuiltins(ed.app, nb)
    69  	initTTYBuiltins(ed.app, tty, nb)
    70  	initMiscBuiltins(ed.app, nb)
    71  	initStateAPI(ed.app, nb)
    72  	initStoreAPI(ed.app, nb, hs)
    73  
    74  	ed.ns = nb.Ns()
    75  	evalDefaultBinding(ev, ed.ns)
    76  
    77  	return ed
    78  }
    79  
    80  //elvdoc:var exceptions
    81  //
    82  // A list of exceptions thrown from callbacks such as prompts. Useful for
    83  // examining tracebacks and other metadata.
    84  
    85  func initExceptionsAPI(ed *Editor, nb eval.NsBuilder) {
    86  	nb.Add("exceptions", vars.FromPtrWithMutex(&ed.excList, &ed.excMutex))
    87  }
    88  
    89  func evalDefaultBinding(ev *eval.Evaler, ns *eval.Ns) {
    90  	src := parse.Source{Name: "[default bindings]", Code: defaultBindingsElv}
    91  	err := ev.Eval(src, eval.EvalCfg{Global: ns})
    92  	if err != nil {
    93  		panic(err)
    94  	}
    95  }
    96  
    97  // ReadCode reads input from the user.
    98  func (ed *Editor) ReadCode() (string, error) {
    99  	return ed.app.ReadCode()
   100  }
   101  
   102  // Ns returns a namespace for manipulating the editor from Elvish code.
   103  func (ed *Editor) Ns() *eval.Ns {
   104  	return ed.ns
   105  }
   106  
   107  func (ed *Editor) notifyf(format string, args ...interface{}) {
   108  	ed.app.Notify(fmt.Sprintf(format, args...))
   109  }
   110  
   111  func (ed *Editor) notifyError(ctx string, e error) {
   112  	if exc, ok := e.(eval.Exception); ok {
   113  		ed.excMutex.Lock()
   114  		defer ed.excMutex.Unlock()
   115  		ed.excList = ed.excList.Cons(exc)
   116  		ed.notifyf("[%v error] %v\n"+
   117  			`see stack trace with "show $edit:exceptions[%d]"`,
   118  			ctx, e, ed.excList.Len()-1)
   119  	} else {
   120  		ed.notifyf("[%v error] %v", ctx, e)
   121  	}
   122  }