Skip to content

Commit

Permalink
cliedit: Integrate the "instant" addon.
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaq committed Dec 8, 2019
1 parent e5c97e2 commit 7806948
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 13 deletions.
16 changes: 3 additions & 13 deletions cliedit/default_bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,9 @@ lastcmd:binding = (binding-table [
&Alt-,= $listing:accept~
])
# &Up= $listing:up~
# &Down= $listing:down~
# &Tab= $listing:down-cycle~
# &Shift-Tab= $listing:up-cycle~
#
# &Ctrl-F= $listing:toggle-filtering~
#
# &Alt-Enter= $listing:accept~
# &Enter= $listing:accept-close~
# &Alt-,= $listing:accept-close~
# &Ctrl-'['= $reset-mode~
#
# &Default= $listing:default~
-instant:binding = (binding-table [
&Ctrl-'['= $listing:close~
])
`

// vi: set et:
1 change: 1 addition & 0 deletions cliedit/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func NewEditor(tty cli.TTY, ev *eval.Evaler, st storedefs.Store) *Editor {
initNavigation(app, ev, ns)
initCompletion(app, ev, ns)
initHistWalk(app, ev, ns, fuser)
initInstant(app, ev, ns)

initBufferBuiltins(app, ns)
initTTYBuiltins(app, tty, ns)
Expand Down
74 changes: 74 additions & 0 deletions cliedit/instant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cliedit

import (
"bufio"
"io"
"os"
"strings"
"sync"

"github.com/elves/elvish/cli"
"github.com/elves/elvish/cli/addons/instant"
"github.com/elves/elvish/cli/el"
"github.com/elves/elvish/eval"
"github.com/elves/elvish/eval/vals"
"github.com/elves/elvish/parse"
)

func initInstant(app cli.App, ev *eval.Evaler, ns eval.Ns) {
bindingVar := newBindingVar(EmptyBindingMap)
binding := newMapBinding(app, ev, bindingVar)
ns.AddNs("-instant",
eval.Ns{
"binding": bindingVar,
}.AddGoFns("<edit:-instant>", map[string]interface{}{
"start": func() { instantStart(app, ev, binding) },
}))
}

func instantStart(app cli.App, ev *eval.Evaler, binding el.Handler) {
execute := func(code string) ([]string, error) {
src := eval.NewInteractiveSource(code)
n, err := parse.AsChunk("[instant]", code)
if err != nil {
return nil, err
}
op, err := ev.Compile(n, src)
if err != nil {
return nil, err
}
fm := eval.NewTopFrame(ev, src, []*eval.Port{
{File: eval.DevNull},
{}, // Will be replaced in CaptureOutput
{File: eval.DevNull},
})
var output []string
var outputMutex sync.Mutex
addLine := func(line string) {
outputMutex.Lock()
defer outputMutex.Unlock()
output = append(output, line)
}
valuesCb := func(ch <-chan interface{}) {
for v := range ch {
addLine("▶ " + vals.ToString(v))
}
}
bytesCb := func(r *os.File) {
bufr := bufio.NewReader(r)
for {
line, err := bufr.ReadString('\n')
if err != nil {
if err != io.EOF {
addLine("i/o error: " + err.Error())
}
break
}
addLine(strings.TrimSuffix(line, "\n"))
}
}
err = fm.ExecWithOutputCallback(op, valuesCb, bytesCb)
return output, err
}
instant.Start(app, instant.Config{Binding: binding, Execute: execute})
}
64 changes: 64 additions & 0 deletions cliedit/instant_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cliedit

import (
"testing"

"github.com/elves/elvish/cli"
"github.com/elves/elvish/cli/el/codearea"
"github.com/elves/elvish/cli/term"
)

func TestInstantAddon_ValueOutput(t *testing.T) {
f := setup()
defer f.Cleanup()

evals(f.Evaler, "edit:-instant:start")
f.TestTTY(t,
"~> ", term.DotHere, "\n",
" INSTANT \n", Styles,
"*********",
)

feedInput(f.TTYCtrl, "+")
f.TestTTY(t,
"~> +", Styles,
" v", term.DotHere, "\n",
" INSTANT \n", Styles,
"*********",
"▶ 0",
)

feedInput(f.TTYCtrl, " 1 2")
f.TestTTY(t,
"~> + 1 2", Styles,
" v ", term.DotHere, "\n",
" INSTANT \n", Styles,
"*********",
"▶ 3",
)
}

func TestInstantAddon_ByteOutput(t *testing.T) {
f := setup()
defer f.Cleanup()

// We don't want to trigger the evaluation of "e", "ec", and "ech", so we
// start with a non-empty code buffer.
cli.SetCodeBuffer(f.Editor.app, codearea.Buffer{Content: "echo ", Dot: 5})
evals(f.Evaler, "edit:-instant:start")
f.TestTTY(t,
"~> echo ", Styles,
" vvvv ", term.DotHere, "\n",
" INSTANT \n", Styles,
"*********",
)

feedInput(f.TTYCtrl, "hello")
f.TestTTY(t,
"~> echo hello", Styles,
" vvvv ", term.DotHere, "\n",
" INSTANT \n", Styles,
"*********",
"hello",
)
}
6 changes: 6 additions & 0 deletions eval/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ func (fm *Frame) CallWithOutputCallback(fn Callable, args []interface{}, opts ma
return pcaptureOutputInner(fm, effectOp{funcOp(opFunc), -1, -1}, valuesCb, bytesCb)
}

// ExecWithOutputCallback executes an Op, feeding the outputs to the given
// callbacks.
func (fm *Frame) ExecWithOutputCallback(op Op, valuesCb func(<-chan interface{}), bytesCb func(*os.File)) error {
return pcaptureOutputInner(fm, op.Inner, valuesCb, bytesCb)
}

func catch(perr *error, fm *Frame) {
// NOTE: We have to duplicate instead of calling util.Catch here, since
// recover can only catch a panic when called directly from a deferred
Expand Down

0 comments on commit 7806948

Please sign in to comment.