github.com/elves/elvish@v0.15.0/pkg/cli/addons/instant/instant.go (about) 1 // Package instant implements an addon that executes code whenever it changes 2 // and shows the result. 3 package instant 4 5 import ( 6 "github.com/elves/elvish/pkg/cli" 7 "github.com/elves/elvish/pkg/cli/term" 8 "github.com/elves/elvish/pkg/ui" 9 ) 10 11 // Config keeps the configuration for the instant addon. 12 type Config struct { 13 // Keybinding. 14 Binding cli.Handler 15 // The function to execute code and returns the output. 16 Execute func(code string) ([]string, error) 17 } 18 19 type widget struct { 20 Config 21 app cli.App 22 textView cli.TextView 23 lastCode string 24 lastErr error 25 } 26 27 func (w *widget) Render(width, height int) *term.Buffer { 28 bb := term.NewBufferBuilder(width). 29 WriteStyled(cli.ModeLine(" INSTANT ", false)).SetDotHere() 30 if w.lastErr != nil { 31 bb.Newline().Write(w.lastErr.Error(), ui.FgRed) 32 } 33 buf := bb.Buffer() 34 if len(buf.Lines) >= height { 35 buf.TrimToLines(0, height) 36 return buf 37 } 38 bufTextView := w.textView.Render(width, height-len(buf.Lines)) 39 buf.Extend(bufTextView, false) 40 return buf 41 } 42 43 func (w *widget) Focus() bool { return false } 44 45 func (w *widget) Handle(event term.Event) bool { 46 handled := w.Binding.Handle(event) 47 if !handled { 48 codeArea := w.app.CodeArea() 49 handled = codeArea.Handle(event) 50 } 51 w.update(false) 52 return handled 53 } 54 55 func (w *widget) update(force bool) { 56 code := w.app.CodeArea().CopyState().Buffer.Content 57 if code == w.lastCode && !force { 58 return 59 } 60 w.lastCode = code 61 output, err := w.Execute(code) 62 w.lastErr = err 63 if err == nil { 64 w.textView.MutateState(func(s *cli.TextViewState) { 65 *s = cli.TextViewState{Lines: output, First: 0} 66 }) 67 } 68 } 69 70 // Start starts the addon. 71 func Start(app cli.App, cfg Config) { 72 if cfg.Execute == nil { 73 app.Notify("executor is required") 74 return 75 } 76 if cfg.Binding == nil { 77 cfg.Binding = cli.DummyHandler{} 78 } 79 w := widget{ 80 Config: cfg, 81 app: app, 82 textView: cli.NewTextView(cli.TextViewSpec{Scrollable: true}), 83 } 84 w.update(true) 85 app.MutateState(func(s *cli.State) { s.Addon = &w }) 86 app.Redraw() 87 }