cmd/ethereum: fix JS REPL exit and add support for dumb terminals

It is now possible to exit the REPL using Ctrl-C, Ctrl-D or by typing "exit".
This commit is contained in:
Felix Lange 2015-03-06 12:18:44 +01:00
parent 2393de5d6b
commit de86403f33
2 changed files with 60 additions and 31 deletions

View File

@ -18,9 +18,11 @@
package main package main
import ( import (
"bufio"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/signal"
"path" "path"
"strings" "strings"
@ -55,44 +57,38 @@ type repl struct {
ethereum *eth.Ethereum ethereum *eth.Ethereum
xeth *xeth.XEth xeth *xeth.XEth
prompt string prompt string
histfile *os.File
lr *liner.State lr *liner.State
running bool
} }
func newREPL(ethereum *eth.Ethereum) *repl { func runREPL(ethereum *eth.Ethereum) {
hist, err := os.OpenFile(path.Join(ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
panic(err)
}
xeth := xeth.New(ethereum) xeth := xeth.New(ethereum)
repl := &repl{ repl := &repl{
re: javascript.NewJSRE(xeth), re: javascript.NewJSRE(xeth),
xeth: xeth, xeth: xeth,
ethereum: ethereum, ethereum: ethereum,
prompt: "> ", prompt: "> ",
histfile: hist,
lr: liner.NewLiner(),
} }
repl.initStdFuncs() repl.initStdFuncs()
return repl if !liner.TerminalSupported() {
} repl.dumbRead()
} else {
func (self *repl) Start() { lr := liner.NewLiner()
if !self.running { defer lr.Close()
self.running = true lr.SetCtrlCAborts(true)
self.lr.ReadHistory(self.histfile) repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
go self.read() repl.read(lr)
repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
} }
} }
func (self *repl) Stop() { func (self *repl) withHistory(op func(*os.File)) {
if self.running { hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
self.running = false if err != nil {
self.histfile.Truncate(0) fmt.Printf("unable to open history file: %v\n", err)
self.lr.WriteHistory(self.histfile) return
self.histfile.Close()
} }
op(hist)
hist.Close()
} }
func (self *repl) parseInput(code string) { func (self *repl) parseInput(code string) {
@ -126,9 +122,9 @@ func (self *repl) setIndent() {
} }
} }
func (self *repl) read() { func (self *repl) read(lr *liner.State) {
for { for {
input, err := self.lr.Prompt(self.prompt) input, err := lr.Prompt(self.prompt)
if err != nil { if err != nil {
return return
} }
@ -139,17 +135,51 @@ func (self *repl) read() {
self.setIndent() self.setIndent()
if indentCount <= 0 { if indentCount <= 0 {
if input == "exit" { if input == "exit" {
self.Stop()
return return
} }
hist := str[:len(str)-1] hist := str[:len(str)-1]
self.lr.AppendHistory(hist) lr.AppendHistory(hist)
self.parseInput(str) self.parseInput(str)
str = "" str = ""
} }
} }
} }
func (self *repl) dumbRead() {
fmt.Println("Unsupported terminal, line editing will not work.")
// process lines
readDone := make(chan struct{})
go func() {
r := bufio.NewReader(os.Stdin)
loop:
for {
fmt.Print(self.prompt)
line, err := r.ReadString('\n')
switch {
case err != nil || line == "exit":
break loop
case line == "":
continue
default:
self.parseInput(line + "\n")
}
}
close(readDone)
}()
// wait for Ctrl-C
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, os.Kill)
defer signal.Stop(sigc)
select {
case <-readDone:
case <-sigc:
os.Stdin.Close() // terminate read
}
}
func (self *repl) printValue(v interface{}) { func (self *repl) printValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint") method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v) v, err := self.re.Vm.ToValue(v)

View File

@ -125,7 +125,6 @@ runtime will execute the file and exit.
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
defer logger.Flush() defer logger.Flush()
utils.HandleInterrupt()
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
@ -134,6 +133,7 @@ func main() {
func run(ctx *cli.Context) { func run(ctx *cli.Context) {
fmt.Printf("Welcome to the FRONTIER\n") fmt.Printf("Welcome to the FRONTIER\n")
utils.HandleInterrupt()
eth := utils.GetEthereum(ClientIdentifier, Version, ctx) eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
startEth(ctx, eth) startEth(ctx, eth)
// this blocks the thread // this blocks the thread
@ -144,9 +144,8 @@ func runjs(ctx *cli.Context) {
eth := utils.GetEthereum(ClientIdentifier, Version, ctx) eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
startEth(ctx, eth) startEth(ctx, eth)
if len(ctx.Args()) == 0 { if len(ctx.Args()) == 0 {
repl := newREPL(eth) runREPL(eth)
utils.RegisterInterrupt(func(os.Signal) { repl.Stop() }) eth.Stop()
repl.Start()
eth.WaitForShutdown() eth.WaitForShutdown()
} else if len(ctx.Args()) == 1 { } else if len(ctx.Args()) == 1 {
execJsFile(eth, ctx.Args()[0]) execJsFile(eth, ctx.Args()[0])