Hilbish/rl.go

243 lines
5.6 KiB
Go

package main
import (
"fmt"
"io"
"strings"
"hilbish/moonlight"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
"github.com/maxlandon/readline"
"github.com/sahilm/fuzzy"
)
type lineReader struct {
rl *readline.Instance
fileHist *fileHistory
}
var hinter *rt.Closure
var highlighter *rt.Closure
func newLineReader(prompt string, noHist bool) *lineReader {
rl := readline.NewInstance()
lr := &lineReader{
rl: rl,
}
regexSearcher := rl.Searcher
rl.Searcher = func(needle string, haystack []string) []string {
fz, _ := l.DoString("return hilbish.opts.fuzzy")
fuzz, ok := fz.TryBool()
if !fuzz || !ok {
return regexSearcher(needle, haystack)
}
matches := fuzzy.Find(needle, haystack)
suggs := make([]string, 0)
for _, match := range matches {
suggs = append(suggs, match.Str)
}
return suggs
}
// we don't mind hilbish.read rl instances having completion,
// but it cant have shared history
if !noHist {
lr.fileHist = newFileHistory(defaultHistPath)
rl.SetHistoryCtrlR("History", &luaHistory{})
rl.HistoryAutoWrite = false
}
rl.ShowVimMode = false
rl.ViModeCallback = func(mode readline.ViMode) {
modeStr := ""
switch mode {
case readline.VimKeys: modeStr = "normal"
case readline.VimInsert: modeStr = "insert"
case readline.VimDelete: modeStr = "delete"
case readline.VimReplaceOnce, readline.VimReplaceMany: modeStr = "replace"
}
setVimMode(modeStr)
}
rl.ViActionCallback = func(action readline.ViAction, args []string) {
actionStr := ""
switch action {
case readline.VimActionPaste: actionStr = "paste"
case readline.VimActionYank: actionStr = "yank"
}
hooks.Emit("hilbish.vimAction", actionStr, args)
}
rl.HintText = func(line []rune, pos int) []rune {
hinter := hshMod.Get(moonlight.StringValue("hinter"))
retVal, err := l.Call1(hinter, moonlight.StringValue(string(line)), moonlight.IntValue(int64(pos)))
if err != nil {
fmt.Println(err)
return []rune{}
}
hintText := ""
if luaStr, ok := retVal.TryString(); ok {
hintText = luaStr
}
return []rune(hintText)
}
rl.SyntaxHighlighter = func(line []rune) string {
highlighter := hshMod.Get(moonlight.StringValue("highlighter"))
retVal, err := l.Call1(highlighter, moonlight.StringValue(string(line)))
if err != nil {
fmt.Println(err)
return string(line)
}
highlighted := ""
if luaStr, ok := retVal.TryString(); ok {
highlighted = luaStr
}
return highlighted
}
setupTabCompleter(rl)
return lr
}
func (lr *lineReader) Read() (string, error) {
hooks.Emit("command.precmd", nil)
s, err := lr.rl.Readline()
// this is so dumb
if err == readline.EOF {
fmt.Println("")
return "", io.EOF
}
return s, err // might get another error
}
func (lr *lineReader) SetPrompt(p string) {
halfPrompt := strings.Split(p, "\n")
if len(halfPrompt) > 1 {
lr.rl.Multiline = true
lr.rl.SetPrompt(strings.Join(halfPrompt[:len(halfPrompt) - 1], "\n"))
lr.rl.MultilinePrompt = halfPrompt[len(halfPrompt) - 1:][0]
} else {
lr.rl.Multiline = false
lr.rl.MultilinePrompt = ""
lr.rl.SetPrompt(p)
}
if initialized && !running {
lr.rl.RefreshPromptInPlace("")
}
}
func (lr *lineReader) SetRightPrompt(p string) {
lr.rl.SetRightPrompt(p)
if initialized && !running {
lr.rl.RefreshPromptInPlace("")
}
}
func (lr *lineReader) AddHistory(cmd string) {
lr.fileHist.Write(cmd)
}
func (lr *lineReader) ClearInput() {
return
}
func (lr *lineReader) Resize() {
return
}
// #interface history
// command history
// The history interface deals with command history.
// This includes the ability to override functions to change the main
// method of saving history.
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
lrLua := map[string]util.LuaExport{
/*
"add": {lr.luaAddHistory, 1, false},
"all": {lr.luaAllHistory, 0, false},
"clear": {lr.luaClearHistory, 0, false},
"get": {lr.luaGetHistory, 1, false},
"size": {lr.luaSize, 0, false},
*/
}
mod := rt.NewTable()
util.SetExports(rtm, mod, lrLua)
return mod
}
// #interface history
// add(cmd)
// Adds a command to the history.
// #param cmd string
func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
lr.AddHistory(cmd)
return c.Next(), nil
}
// #interface history
// size() -> number
// Returns the amount of commands in the history.
// #eturns number
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.IntValue(int64(lr.fileHist.Len()))), nil
}
// #interface history
// get(index)
// Retrieves a command from the history based on the `index`.
// #param index number
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
idx, err := c.IntArg(0)
if err != nil {
return nil, err
}
cmd, _ := lr.fileHist.GetLine(int(idx))
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
}
// #interface history
// all() -> table
// Retrieves all history as a table.
// #returns table
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
tbl := rt.NewTable()
size := lr.fileHist.Len()
for i := 1; i < size; i++ {
cmd, _ := lr.fileHist.GetLine(i)
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
}
return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil
}
// #interface history
// clear()
// Deletes all commands from the history.
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
lr.fileHist.clear()
return c.Next(), nil
}