Use github.com/peterh/liner for REPL lines
This gives us history and line conveniences.
This commit is contained in:
parent
7cd740aa7b
commit
7265e1d7a1
3 changed files with 112 additions and 59 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,3 +3,4 @@ main
|
||||||
*.test
|
*.test
|
||||||
*.peg.go
|
*.peg.go
|
||||||
cayley.cfg
|
cayley.cfg
|
||||||
|
.cayley_history
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ install:
|
||||||
- go get github.com/barakmich/glog
|
- go get github.com/barakmich/glog
|
||||||
- go get github.com/julienschmidt/httprouter
|
- go get github.com/julienschmidt/httprouter
|
||||||
- go get github.com/petar/GoLLRB/llrb
|
- go get github.com/petar/GoLLRB/llrb
|
||||||
|
- go get github.com/peterh/liner
|
||||||
- go get github.com/robertkrimen/otto
|
- go get github.com/robertkrimen/otto
|
||||||
- go get github.com/russross/blackfriday
|
- go get github.com/russross/blackfriday
|
||||||
- go get github.com/syndtr/goleveldb/leveldb
|
- go get github.com/syndtr/goleveldb/leveldb
|
||||||
|
|
|
||||||
169
db/repl.go
169
db/repl.go
|
|
@ -15,14 +15,15 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/peterh/liner"
|
||||||
|
|
||||||
"github.com/google/cayley/config"
|
"github.com/google/cayley/config"
|
||||||
"github.com/google/cayley/graph"
|
"github.com/google/cayley/graph"
|
||||||
"github.com/google/cayley/quad/cquads"
|
"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 {
|
func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error {
|
||||||
var ses query.Session
|
var ses query.Session
|
||||||
switch queryLanguage {
|
switch queryLanguage {
|
||||||
|
|
@ -74,80 +82,123 @@ func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error
|
||||||
default:
|
default:
|
||||||
ses = gremlin.NewSession(ts, cfg.Timeout, true)
|
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 {
|
for {
|
||||||
if len(line) == 0 {
|
if len(code) == 0 {
|
||||||
fmt.Print("cayley> ")
|
prompt = ps1
|
||||||
} else {
|
} else {
|
||||||
fmt.Print("... ")
|
prompt = ps2
|
||||||
}
|
}
|
||||||
l, prefix, err := buf.ReadLine()
|
line, err := term.Prompt(prompt)
|
||||||
if err == io.EOF {
|
if err != nil {
|
||||||
if len(line) != 0 {
|
if err == io.EOF {
|
||||||
line = line[:0]
|
|
||||||
} else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
line = line[:0]
|
term.AppendHistory(line)
|
||||||
}
|
|
||||||
if prefix {
|
line = strings.TrimSpace(line)
|
||||||
return errors.New("line too long")
|
|
||||||
}
|
|
||||||
line = append(line, l...)
|
|
||||||
if len(line) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line = bytes.TrimSpace(line)
|
|
||||||
if len(line) == 0 || line[0] == '#' {
|
if len(line) == 0 || line[0] == '#' {
|
||||||
line = line[:0]
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if bytes.HasPrefix(line, []byte(":debug")) {
|
|
||||||
ses.ToggleDebug()
|
if code == "" {
|
||||||
fmt.Println("Debug Toggled")
|
switch {
|
||||||
line = line[:0]
|
case strings.HasPrefix(line, ":debug"):
|
||||||
continue
|
ses.ToggleDebug()
|
||||||
}
|
fmt.Println("Debug Toggled")
|
||||||
if bytes.HasPrefix(line, []byte(":a")) {
|
continue
|
||||||
var tripleStmt = line[3:]
|
|
||||||
triple, err := cquads.Parse(string(tripleStmt))
|
case strings.HasPrefix(line, ":a"):
|
||||||
if !triple.IsValid() {
|
triple, err := cquads.Parse(line[3:])
|
||||||
if err != nil {
|
if !triple.IsValid() {
|
||||||
fmt.Printf("not a valid triple: %v\n", err)
|
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
|
continue
|
||||||
}
|
}
|
||||||
ts.AddTriple(triple)
|
|
||||||
line = line[:0]
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if bytes.HasPrefix(line, []byte(":d")) {
|
|
||||||
var tripleStmt = line[3:]
|
code += line
|
||||||
triple, err := cquads.Parse(string(tripleStmt))
|
|
||||||
if !triple.IsValid() {
|
result, err := ses.InputParses(code)
|
||||||
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))
|
|
||||||
switch result {
|
switch result {
|
||||||
case query.Parsed:
|
case query.Parsed:
|
||||||
Run(string(line), ses)
|
Run(code, ses)
|
||||||
line = line[:0]
|
code = ""
|
||||||
case query.ParseFail:
|
case query.ParseFail:
|
||||||
fmt.Println("Error: ", err)
|
fmt.Println("Error: ", err)
|
||||||
line = line[:0]
|
code = ""
|
||||||
case query.ParseMore:
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue