github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/cli/mode/instant.go (about) 1 package mode 2 3 import ( 4 "errors" 5 6 "src.elv.sh/pkg/cli" 7 "src.elv.sh/pkg/cli/term" 8 "src.elv.sh/pkg/cli/tk" 9 "src.elv.sh/pkg/ui" 10 ) 11 12 // Instant is a mode that executes code whenever it changes and shows the 13 // result. 14 type Instant interface { 15 tk.Widget 16 } 17 18 // InstantSpec specifies the configuration for the instant mode. 19 type InstantSpec struct { 20 // Key bindings. 21 Bindings tk.Bindings 22 // The function to execute code and returns the output. 23 Execute func(code string) ([]string, error) 24 } 25 26 type instant struct { 27 InstantSpec 28 app cli.App 29 textView tk.TextView 30 lastCode string 31 lastErr error 32 } 33 34 func (w *instant) Render(width, height int) *term.Buffer { 35 bb := term.NewBufferBuilder(width). 36 WriteStyled(modeLine(" INSTANT ", false)).SetDotHere() 37 if w.lastErr != nil { 38 bb.Newline().Write(w.lastErr.Error(), ui.FgRed) 39 } 40 buf := bb.Buffer() 41 if len(buf.Lines) >= height { 42 buf.TrimToLines(0, height) 43 return buf 44 } 45 bufTextView := w.textView.Render(width, height-len(buf.Lines)) 46 buf.Extend(bufTextView, false) 47 return buf 48 } 49 50 func (w *instant) Focus() bool { return false } 51 52 func (w *instant) Handle(event term.Event) bool { 53 handled := w.Bindings.Handle(w, event) 54 if !handled { 55 codeArea := w.app.CodeArea() 56 handled = codeArea.Handle(event) 57 } 58 w.update(false) 59 return handled 60 } 61 62 func (w *instant) update(force bool) { 63 code := w.app.CodeArea().CopyState().Buffer.Content 64 if code == w.lastCode && !force { 65 return 66 } 67 w.lastCode = code 68 output, err := w.Execute(code) 69 w.lastErr = err 70 if err == nil { 71 w.textView.MutateState(func(s *tk.TextViewState) { 72 *s = tk.TextViewState{Lines: output, First: 0} 73 }) 74 } 75 } 76 77 var errExecutorIsRequired = errors.New("executor is required") 78 79 // NewInstant creates a new instant mode. 80 func NewInstant(app cli.App, cfg InstantSpec) (Instant, error) { 81 if cfg.Execute == nil { 82 return nil, errExecutorIsRequired 83 } 84 if cfg.Bindings == nil { 85 cfg.Bindings = tk.DummyBindings{} 86 } 87 w := instant{ 88 InstantSpec: cfg, 89 app: app, 90 textView: tk.NewTextView(tk.TextViewSpec{Scrollable: true}), 91 } 92 w.update(true) 93 return &w, nil 94 }