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 }