add a nice readline experience (with history!)

This commit is contained in:
David 2021-02-28 12:30:21 -05:00
parent b7c7e02697
commit 8e33e13ffc
3 changed files with 83 additions and 11 deletions

2
go.mod
View File

@ -1,3 +1,5 @@
module git.yetaga.in/alazyreader/prosper
go 1.15
require github.com/peterh/liner v1.2.1

4
go.sum Normal file
View File

@ -0,0 +1,4 @@
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg=
github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=

88
main.go
View File

@ -1,12 +1,14 @@
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/peterh/liner"
)
type libraryFiles []string
@ -20,10 +22,41 @@ func (l *libraryFiles) Set(value string) error {
return nil
}
func historyFn() string {
dataHome := os.Getenv("XDG_DATA_HOME")
home := os.Getenv("HOME")
if dataHome == "" {
dataHome = filepath.Join(home, ".local/share/")
}
prosperDir := filepath.Join(dataHome, "prosper")
os.MkdirAll(prosperDir, 0755)
return filepath.Join(prosperDir, ".history")
}
func completer(d Dictionary) liner.Completer {
return func(line string) []string {
candidates := []string{}
words := strings.Split(line, " ")
for w := range d {
if strings.HasPrefix(w, words[len(words)-1]) {
// must reconstruct the _full_ line to return for the completer
pref := strings.Join(words[:len(words)-1], " ")
if len(pref) != 0 {
candidates = append(candidates, pref+" "+w)
} else {
candidates = append(candidates, w)
}
}
}
return candidates
}
}
func main() {
// flag setup
var libs libraryFiles
flag.Var(&libs, "l", "propser library file (may be repeated)")
debug := flag.Bool("debug", false, "output debugging information")
flag.Parse()
// create main context
@ -64,27 +97,60 @@ func main() {
}
}
reader := bufio.NewReader(os.Stdin)
// set up liner
line := liner.NewLiner()
line.SetCtrlCAborts(true)
line.SetCompleter(completer(dict))
historyFile := historyFn()
if f, err := os.Open(historyFile); err == nil {
_, err := line.ReadHistory(f)
if err != nil && *debug {
fmt.Printf("error reading line history: %v", err)
}
f.Close()
}
defer func() {
if f, err := os.Create(historyFile); err != nil {
if *debug {
fmt.Printf("Error writing history file: %v", err)
}
} else {
line.WriteHistory(f)
f.Close()
}
line.Close()
}()
// Welcome banner
fmt.Print("prosper\n")
// read loop
for {
if c.Flags["Immediate"] {
fmt.Print("> ")
} else {
fmt.Print(" ")
p := "> "
if !c.Flags["Immediate"] {
p = " "
}
line, err := reader.ReadString('\n')
if err != nil {
// read line
l, err := line.Prompt(p)
if err == liner.ErrPromptAborted {
fmt.Println("ctrl-C caught (try BYE)")
continue
} else if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
line = strings.TrimSpace(line)
err = c.Eval(line + " ") // append a space to make sure we always close out our parse loop
// parse line
l = strings.TrimSpace(l)
line.AppendHistory(l)
err = c.Eval(l + " ") // append a space to make sure we always close out our parse loop
if errors.Is(err, ErrExit) {
fmt.Printf("bye\n")
os.Exit(0)
break
} else if err != nil {
fmt.Printf("error in evaluation: %v\n", err)
} else if c.Flags["Immediate"] {