Use github.com/peterh/liner for REPL lines

This gives us history and line conveniences.
This commit is contained in:
kortschak 2014-08-07 15:02:30 +09:30
parent 7cd740aa7b
commit 7265e1d7a1
3 changed files with 112 additions and 59 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ main
*.test
*.peg.go
cayley.cfg
.cayley_history

View file

@ -10,6 +10,7 @@ install:
- go get github.com/barakmich/glog
- go get github.com/julienschmidt/httprouter
- go get github.com/petar/GoLLRB/llrb
- go get github.com/peterh/liner
- go get github.com/robertkrimen/otto
- go get github.com/russross/blackfriday
- go get github.com/syndtr/goleveldb/leveldb

View file

@ -15,14 +15,15 @@
package db
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"os/signal"
"strings"
"time"
"github.com/peterh/liner"
"github.com/google/cayley/config"
"github.com/google/cayley/graph"
"github.com/google/cayley/quad/cquads"
@ -62,6 +63,13 @@ func Run(query string, ses query.Session) {
}
}
const (
ps1 = "cayley> "
ps2 = "... "
history = ".cayley_history"
)
func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error {
var ses query.Session
switch queryLanguage {
@ -74,80 +82,123 @@ func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error
default:
ses = gremlin.NewSession(ts, cfg.Timeout, true)
}
buf := bufio.NewReader(os.Stdin)
var line []byte
term, err := terminal(history)
if os.IsNotExist(err) {
fmt.Printf("creating new history file: %q\n", history)
}
defer persist(term, history)
var (
prompt = ps1
code string
)
for {
if len(line) == 0 {
fmt.Print("cayley> ")
if len(code) == 0 {
prompt = ps1
} else {
fmt.Print("... ")
prompt = ps2
}
l, prefix, err := buf.ReadLine()
if err == io.EOF {
if len(line) != 0 {
line = line[:0]
} else {
line, err := term.Prompt(prompt)
if err != nil {
if err == io.EOF {
return nil
}
return err
}
if err != nil {
line = line[:0]
}
if prefix {
return errors.New("line too long")
}
line = append(line, l...)
if len(line) == 0 {
continue
}
line = bytes.TrimSpace(line)
term.AppendHistory(line)
line = strings.TrimSpace(line)
if len(line) == 0 || line[0] == '#' {
line = line[:0]
continue
}
if bytes.HasPrefix(line, []byte(":debug")) {
ses.ToggleDebug()
fmt.Println("Debug Toggled")
line = line[:0]
continue
}
if bytes.HasPrefix(line, []byte(":a")) {
var tripleStmt = line[3:]
triple, err := cquads.Parse(string(tripleStmt))
if !triple.IsValid() {
if err != nil {
fmt.Printf("not a valid triple: %v\n", err)
if code == "" {
switch {
case strings.HasPrefix(line, ":debug"):
ses.ToggleDebug()
fmt.Println("Debug Toggled")
continue
case strings.HasPrefix(line, ":a"):
triple, err := cquads.Parse(line[3:])
if !triple.IsValid() {
if err != nil {
fmt.Printf("not a valid triple: %v\n", err)
}
continue
}
line = line[:0]
ts.AddTriple(triple)
continue
case strings.HasPrefix(line, ":d"):
triple, err := cquads.Parse(line[3:])
if !triple.IsValid() {
if err != nil {
fmt.Printf("not a valid triple: %v\n", err)
}
continue
}
ts.RemoveTriple(triple)
continue
}
ts.AddTriple(triple)
line = line[:0]
continue
}
if bytes.HasPrefix(line, []byte(":d")) {
var tripleStmt = line[3:]
triple, err := cquads.Parse(string(tripleStmt))
if !triple.IsValid() {
if err != nil {
fmt.Printf("not a valid triple: %v\n", err)
}
line = line[:0]
continue
}
ts.RemoveTriple(triple)
line = line[:0]
continue
}
result, err := ses.InputParses(string(line))
code += line
result, err := ses.InputParses(code)
switch result {
case query.Parsed:
Run(string(line), ses)
line = line[:0]
Run(code, ses)
code = ""
case query.ParseFail:
fmt.Println("Error: ", err)
line = line[:0]
code = ""
case query.ParseMore:
}
}
}
func terminal(path string) (*liner.State, error) {
term := liner.NewLiner()
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
<-c
persist(term, history)
err := term.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to properly clean up terminal: %v\n", err)
os.Exit(1)
}
os.Exit(0)
}()
f, err := os.Open(path)
if err != nil {
return term, err
}
defer f.Close()
_, err = term.ReadHistory(f)
return term, err
}
func persist(term *liner.State, path string) error {
f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
return fmt.Errorf("could not open %q to append history: %v", path, err)
}
defer f.Close()
_, err = term.WriteHistory(f)
if err != nil {
return fmt.Errorf("could not write history to %q: %v", path, err)
}
return nil
}