Put the actual entry function in a new run package.

This allows us to test the entry function.
This commit is contained in:
Qi Xiao 2016-02-21 12:52:07 +01:00
parent de66cbe8d5
commit a880b74a1b
3 changed files with 240 additions and 230 deletions

View File

@ -149,6 +149,7 @@ The adjective for elvish is also "elvish", not "elvishy" and definitely not "elv
|eval|[![eval](https://gocover.io/_badge/github.com/elves/elvish/eval/)](https://gocover.io/github.com/elves/elvish/eval/)|
|glob|[![glob](https://gocover.io/_badge/github.com/elves/elvish/glob/)](https://gocover.io/github.com/elves/elvish/glob/)|
|parse|[![parse](https://gocover.io/_badge/github.com/elves/elvish/parse/)](https://gocover.io/github.com/elves/elvish/parse/)|
|run|[![run](https://gocover.io/_badge/github.com/elves/elvish/run/)](https://gocover.io/github.com/elves/elvish/run/)|
|store|[![store](https://gocover.io/_badge/github.com/elves/elvish/store/)](https://gocover.io/github.com/elves/elvish/store/)|
|sys|[![sys](https://gocover.io/_badge/github.com/elves/elvish/sys/)](https://gocover.io/github.com/elves/elvish/sys/)|
|util|[![util](https://gocover.io/_badge/github.com/elves/elvish/util/)](https://gocover.io/github.com/elves/elvish/util/)|

232
main.go
View File

@ -2,236 +2,8 @@
// programming language with an extensible, friendly user interface.
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"os/signal"
"os/user"
"syscall"
"github.com/elves/elvish/edit"
"github.com/elves/elvish/eval"
"github.com/elves/elvish/parse"
"github.com/elves/elvish/store"
"github.com/elves/elvish/sys"
"github.com/elves/elvish/util"
)
var Logger = util.GetLogger("[main] ")
var (
log = flag.String("log", "", "a file to write debug log to")
dbname = flag.String("db", "", "path to the database")
help = flag.Bool("help", false, "show usage help and quit")
)
func usage() {
fmt.Println("usage: elvish [flags] [script]")
fmt.Println("flags:")
flag.PrintDefaults()
}
import "github.com/elves/elvish/run"
func main() {
defer rescue()
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) > 1 {
usage()
os.Exit(2)
}
if *help {
usage()
os.Exit(0)
}
if *log != "" {
err := util.SetOutputFile(*log)
if err != nil {
fmt.Println(err)
}
}
go handleQuit()
go logSignals()
ev, st := newEvalerAndStore()
defer func() {
err := st.Close()
if err != nil {
fmt.Println("failed to close database:", err)
}
}()
if len(args) == 1 {
script(ev, args[0])
} else {
interact(ev, st)
}
}
func rescue() {
r := recover()
if r != nil {
println()
fmt.Println(r)
print(sys.DumpStack())
println("\nexecing recovery shell /bin/sh")
syscall.Exec("/bin/sh", []string{"/bin/sh"}, os.Environ())
}
}
func script(ev *eval.Evaler, fname string) {
err := ev.Source(fname)
if err != nil {
printError(err)
os.Exit(1)
}
}
func interact(ev *eval.Evaler, st *store.Store) {
// Build Editor.
sigch := make(chan os.Signal)
signal.Notify(sigch)
ed := edit.NewEditor(os.Stdin, sigch, ev, st)
// Source rc.elv.
datadir, err := store.EnsureDataDir()
printError(err)
if err == nil {
// XXX
err := ev.Source(datadir + "/rc.elv")
if err != nil && !os.IsNotExist(err) {
printError(err)
}
}
// Build prompt and rprompt.
username := "???"
user, err := user.Current()
if err == nil {
username = user.Username
}
hostname, err := os.Hostname()
if err != nil {
hostname = "???"
}
rpromptStr := username + "@" + hostname
prompt := func() string {
return util.Getwd() + "> "
}
rprompt := func() string {
return rpromptStr
}
// Build readLine function.
readLine := func() edit.LineRead {
return ed.ReadLine(prompt, rprompt)
}
usingBasic := false
if !sys.IsATTY(0) {
readLine = basicReadLine
usingBasic = true
}
cmdNum := 0
for {
cmdNum++
// name := fmt.Sprintf("<tty %d>", cmdNum)
lr := readLine()
if lr.EOF {
break
} else if lr.Err != nil {
fmt.Println("Editor error:", lr.Err)
if !usingBasic {
fmt.Println("Falling back to basic line editor")
readLine = basicReadLine
usingBasic = true
}
continue
}
n, err := parse.Parse(lr.Line)
printError(err)
if err == nil {
err := ev.EvalInteractive(lr.Line, n)
printError(err)
}
}
}
func basicReadLine() edit.LineRead {
stdin := bufio.NewReaderSize(os.Stdin, 0)
line, err := stdin.ReadString('\n')
if err == nil {
return edit.LineRead{Line: line}
} else if err == io.EOF {
return edit.LineRead{EOF: true}
} else {
return edit.LineRead{Err: err}
}
}
func logSignals() {
sigs := make(chan os.Signal)
signal.Notify(sigs)
for sig := range sigs {
Logger.Println("signal", sig)
}
}
func handleQuit() {
quitSigs := make(chan os.Signal)
signal.Notify(quitSigs, syscall.SIGQUIT)
<-quitSigs
fmt.Print(sys.DumpStack())
os.Exit(3)
}
func newEvalerAndStore() (*eval.Evaler, *store.Store) {
dataDir, err := store.EnsureDataDir()
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish")
}
var st *store.Store
if err == nil {
db := *dbname
if db == "" {
db = dataDir + "/db"
}
st, err = store.NewStore(db)
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err)
}
}
return eval.NewEvaler(st), st
}
func printError(err error) {
if err == nil {
return
}
switch err := err.(type) {
case *util.Errors:
for _, e := range err.Errors {
printError(e)
}
default:
eval.PprintError(err)
fmt.Println()
}
run.Main()
}

237
run/run.go Normal file
View File

@ -0,0 +1,237 @@
// Package run is the entry point of elvish.
package run
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"os/signal"
"os/user"
"syscall"
"github.com/elves/elvish/edit"
"github.com/elves/elvish/eval"
"github.com/elves/elvish/parse"
"github.com/elves/elvish/store"
"github.com/elves/elvish/sys"
"github.com/elves/elvish/util"
)
var Logger = util.GetLogger("[main] ")
var (
log = flag.String("log", "", "a file to write debug log to")
dbname = flag.String("db", "", "path to the database")
help = flag.Bool("help", false, "show usage help and quit")
)
func usage() {
fmt.Println("usage: elvish [flags] [script]")
fmt.Println("flags:")
flag.PrintDefaults()
}
// Main is the entry point of elvish.
func Main() {
defer rescue()
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) > 1 {
usage()
os.Exit(2)
}
if *help {
usage()
os.Exit(0)
}
if *log != "" {
err := util.SetOutputFile(*log)
if err != nil {
fmt.Println(err)
}
}
go handleQuit()
go logSignals()
ev, st := newEvalerAndStore()
defer func() {
err := st.Close()
if err != nil {
fmt.Println("failed to close database:", err)
}
}()
if len(args) == 1 {
script(ev, args[0])
} else {
interact(ev, st)
}
}
func rescue() {
r := recover()
if r != nil {
println()
fmt.Println(r)
print(sys.DumpStack())
println("\nexecing recovery shell /bin/sh")
syscall.Exec("/bin/sh", []string{"/bin/sh"}, os.Environ())
}
}
func script(ev *eval.Evaler, fname string) {
err := ev.Source(fname)
if err != nil {
printError(err)
os.Exit(1)
}
}
func interact(ev *eval.Evaler, st *store.Store) {
// Build Editor.
sigch := make(chan os.Signal)
signal.Notify(sigch)
ed := edit.NewEditor(os.Stdin, sigch, ev, st)
// Source rc.elv.
datadir, err := store.EnsureDataDir()
printError(err)
if err == nil {
// XXX
err := ev.Source(datadir + "/rc.elv")
if err != nil && !os.IsNotExist(err) {
printError(err)
}
}
// Build prompt and rprompt.
username := "???"
user, err := user.Current()
if err == nil {
username = user.Username
}
hostname, err := os.Hostname()
if err != nil {
hostname = "???"
}
rpromptStr := username + "@" + hostname
prompt := func() string {
return util.Getwd() + "> "
}
rprompt := func() string {
return rpromptStr
}
// Build readLine function.
readLine := func() edit.LineRead {
return ed.ReadLine(prompt, rprompt)
}
usingBasic := false
if !sys.IsATTY(0) {
readLine = basicReadLine
usingBasic = true
}
cmdNum := 0
for {
cmdNum++
// name := fmt.Sprintf("<tty %d>", cmdNum)
lr := readLine()
if lr.EOF {
break
} else if lr.Err != nil {
fmt.Println("Editor error:", lr.Err)
if !usingBasic {
fmt.Println("Falling back to basic line editor")
readLine = basicReadLine
usingBasic = true
}
continue
}
n, err := parse.Parse(lr.Line)
printError(err)
if err == nil {
err := ev.EvalInteractive(lr.Line, n)
printError(err)
}
}
}
func basicReadLine() edit.LineRead {
stdin := bufio.NewReaderSize(os.Stdin, 0)
line, err := stdin.ReadString('\n')
if err == nil {
return edit.LineRead{Line: line}
} else if err == io.EOF {
return edit.LineRead{EOF: true}
} else {
return edit.LineRead{Err: err}
}
}
func logSignals() {
sigs := make(chan os.Signal)
signal.Notify(sigs)
for sig := range sigs {
Logger.Println("signal", sig)
}
}
func handleQuit() {
quitSigs := make(chan os.Signal)
signal.Notify(quitSigs, syscall.SIGQUIT)
<-quitSigs
fmt.Print(sys.DumpStack())
os.Exit(3)
}
func newEvalerAndStore() (*eval.Evaler, *store.Store) {
dataDir, err := store.EnsureDataDir()
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish")
}
var st *store.Store
if err == nil {
db := *dbname
if db == "" {
db = dataDir + "/db"
}
st, err = store.NewStore(db)
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err)
}
}
return eval.NewEvaler(st), st
}
func printError(err error) {
if err == nil {
return
}
switch err := err.(type) {
case *util.Errors:
for _, e := range err.Errors {
printError(e)
}
default:
eval.PprintError(err)
fmt.Println()
}
}