|
|
|
@ -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"] { |
|
|
|
|