func (fac FnAsArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) { in, err := makeClosedStdin() if err != nil { return nil, err } ports := []*eval.Port{in, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}} wordValues := make([]eval.Value, len(words)) for i, word := range words { wordValues[i] = eval.String(word) } // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ed.evaler, "[editor completer]", "", ports) values, err := ec.PCaptureOutput(fac.Fn, wordValues) if err != nil { ed.notify("completer error: %v", err) return nil, err } cands := make([]*candidate, len(values)) for i, v := range values { s := eval.ToString(v) cands[i] = &candidate{text: s} } return cands, nil }
func callFnForCandidates(fn eval.FnValue, ev *eval.Evaler, args []string) ([]*candidate, error) { ports := []*eval.Port{eval.DevNullClosedChan, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}} argValues := make([]eval.Value, len(args)) for i, arg := range args { argValues[i] = eval.String(arg) } // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ev, "[editor completer]", "", ports) values, err := ec.PCaptureOutput(fn, argValues, eval.NoOpts) if err != nil { return nil, errors.New("completer error: " + err.Error()) } cands := make([]*candidate, len(values)) for i, v := range values { switch v := v.(type) { case eval.String: cands[i] = &candidate{text: string(v)} case *candidate: cands[i] = v default: return nil, errors.New("completer must output string or candidate") } } return cands, nil }
func (c EvalCaller) Call(ed *Editor) { // Input devnull, err := os.Open("/dev/null") if err != nil { Logger.Println(err) return } defer devnull.Close() in := make(chan eval.Value) close(in) // Output rout, out, err := os.Pipe() if err != nil { Logger.Println(err) return } chanOut := make(chan eval.Value) // Goroutines to collect output. var wg sync.WaitGroup wg.Add(2) go func() { rd := bufio.NewReader(rout) for { line, err := rd.ReadString('\n') Logger.Println("function writes bytes", line) if err != nil { break } } rout.Close() wg.Done() }() go func() { for v := range chanOut { Logger.Println("function writes Value", v.Repr()) } wg.Done() }() ports := []*eval.Port{ {File: devnull, Chan: in}, {File: out, Chan: chanOut}, {File: out, Chan: chanOut}, } // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ed.evaler, "[editor]", "", ports) ex := ec.PCall(c.Caller, []eval.Value{}) if ex != nil { ed.notify("function error: %s", ex.Error()) } out.Close() close(chanOut) wg.Wait() ed.refresh(true) }
// 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) }
// callFnAsPrompt calls a Fn with closed input, captures its output and convert // the output to a slice of *styled's. func callFnForPrompt(ed *Editor, fn eval.Fn) []*styled { ports := []*eval.Port{eval.DevNullClosedChan, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}} // XXX There is no source to pass to NewTopEvalCtx. ec := eval.NewTopEvalCtx(ed.evaler, "[editor prompt]", "", ports) values, err := ec.PCaptureOutput(fn, nil, eval.NoOpts) if err != nil { ed.Notify("prompt function error: %v", err) return nil } var ss []*styled for _, v := range values { if s, ok := v.(*styled); ok { ss = append(ss, s) } else { ss = append(ss, &styled{eval.ToString(v), ""}) } } return ss }
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) }