traefik / yaegi

Yaegi is Another Elegant Go Interpreter
https://pkg.go.dev/github.com/traefik/yaegi
Apache License 2.0
6.95k stars 343 forks source link

repl: improved UI #315

Open sbinet opened 5 years ago

sbinet commented 5 years ago

it would be great (beside having regular "readline-like" capabilities like alluded to in #286) to have support for:

in neugram, we used github.com/peterh/liner.

sleeping-barber commented 5 years ago

Tab Completion would be awesome.

gmarik commented 4 years ago

got working prototype in https://github.com/containous/yaegi/pull/389

blasrodri commented 4 years ago

I added a very simple patch for taking the previous command when pressing the right arrow. It's far from being ready for a PR. But I just wanted to know if this makes any sense at all.

diff --git a/interp/interp.go b/interp/interp.go
index 6111a73..2d3361f 100644
--- a/interp/interp.go
+++ b/interp/interp.go
@@ -2,6 +2,7 @@ package interp

 import (
    "bufio"
+   "bytes"
    "context"
    "fmt"
    "go/build"
@@ -138,6 +139,7 @@ type Interpreter struct {
    srcPkg   imports           // source packages used in interpreter, indexed by path
    pkgNames map[string]string // package names, indexed by path
    done     chan struct{}     // for cancellation of channel operations
+   commands []string
 }

 const (
@@ -496,7 +498,19 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
    src := ""
    s := bufio.NewScanner(in)
    for s.Scan() {
-       src += s.Text() + "\n"
+       n := len(interp.commands)
+       if bytes.Equal(s.Bytes(), []byte{27, 91, 65}) {
+           if n > 0 {
+               src = interp.commands[n - 1]
+               // shrink it
+               interp.commands = interp.commands[:n - 1]
+           } else {
+               src = ""
+           }
+       } else {
+           src += s.Text() + "\n"
+           interp.commands = append(interp.commands, src)
+       }
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        handleSignal(ctx, cancel)
mpl commented 4 years ago

Hi @blasrodri

We think that if we're ever going to add some readline type capabilities, we'll probably go with an existing full-blown library, such as liner, because: 1) If we go with a small subset of features, there's always going to be someone who is going to want that we support their favorite one. It's easier to say "you get everything that is in liner, if you want anything else, ask liner to implement it". 2) Reimplementing properly within yaegi such a full-blown library is a huge amount of work, so it wouldn't be wise to do it ourselves when we can reuse an existing one.

That said, we'll only ever depend on liner or anything else once we have figured out how to do that without cluttering with deps the yaegi core itself. Which might require making several go modules or something like that. We'll need to think about that at some point.

blasrodri commented 4 years ago

Hi @mpl ! Thanks a lot! I understand the design trade offs, and it makes sense what you’re saying.

Thanks for all your hard work. This package is really awesome.

sentriz commented 1 year ago

for the meantime i'm just using my shell's "readline" type stuff

user experience is not so bad if you use heredocs "<<<" and are ok with working on one line

yaegi <<<'t := 743 * time.Microsecond; fmt.Printf("%.2f", float64(t) / float64(time.Millisecond))'
wader commented 1 year ago

@sentriz Nice trick, you can also use rlwrap yaegi but i've noticed some weird hangs

sentriz commented 1 year ago

can also use rlwrap yaegi but i've noticed some weird hangs

oh that works really nice. haven't seen any hangs yet. thanks!