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  }