// CallFn calls an Fn, displaying its outputs and possible errors as editor // notifications. It is the preferred way to call a Fn while the editor is // active. func (ed *Editor) CallFn(fn eval.FnValue, args ...eval.Value) { if b, ok := fn.(*BuiltinFn); ok { // Builtin function: quick path. b.impl(ed) return } rout, chanOut, ports, err := makePorts() if err != nil { return } // Goroutines to collect output. var wg sync.WaitGroup wg.Add(2) go func() { rd := bufio.NewReader(rout) for { line, err := rd.ReadString('\n') if err != nil { break } ed.Notify("[bytes output] %s", line[:len(line)-1]) } rout.Close() wg.Done() }() go func() { for v := range chanOut { ed.Notify("[value output] %s", v.Repr(eval.NoPretty)) } wg.Done() }() // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ed.evaler, "[editor]", "", ports) ex := ec.PCall(fn, args, eval.NoOpts) if ex != nil { ed.Notify("function error: %s", ex.Error()) } eval.ClosePorts(ports) wg.Wait() ed.refresh(true, true) }
func (c FnAsBoundFunc) Call(ed *Editor) { rout, chanOut, ports, err := makePorts() if err != nil { return } // Goroutines to collect output. var wg sync.WaitGroup wg.Add(2) go func() { rd := bufio.NewReader(rout) for { line, err := rd.ReadString('\n') if err != nil { break } // XXX notify is not concurrency-safe. ed.notify("[bound fn bytes] %s", line[:len(line)-1]) } rout.Close() wg.Done() }() go func() { for v := range chanOut { ed.notify("[bound fn value] %s", v.Repr(eval.NoPretty)) } wg.Done() }() // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ed.evaler, "[editor]", "", ports) ex := ec.PCall(c.Fn, []eval.Value{}) if ex != nil { ed.notify("function error: %s", ex.Error()) } eval.ClosePorts(ports) wg.Wait() ed.refresh(true, true) }