feat: lua backed history (#187)

* refactor: put file history handler in line reader instance instead of global

* feat: add lua history handler in go

* feat: use lua to retrieve readline history

* refactor: handle history in lua

this also introduces a new opt: history
if it is false, command history won't get added

* fix: remove nature.history require

* docs: add changes in changelog

* fix: add comma after history opt
history-searcher
sammy 2022-07-13 13:02:09 -07:00 committed by GitHub
parent f7806f5479
commit 349380ae6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 26 deletions

View File

@ -66,6 +66,8 @@ will be ran on startup
- Message of the day on startup (`hilbish.motd`), mainly intended as quick - Message of the day on startup (`hilbish.motd`), mainly intended as quick
small news pieces for releases. It is printed by default. To disable it, small news pieces for releases. It is printed by default. To disable it,
set `hilbish.opts.motd` to false. set `hilbish.opts.motd` to false.
- `history` opt has been added and is true by default. Setting it to false
disables commands being added to history.
- `hilbish.rawInput` hook for input from the readline library - `hilbish.rawInput` hook for input from the readline library
- Completion of files in quotes - Completion of files in quotes
@ -90,6 +92,8 @@ of a dot. (ie. `job.stop()` -> `job:stop()`)
- All `fs` module functions which take paths now implicitly expand ~ to home. - All `fs` module functions which take paths now implicitly expand ~ to home.
- **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is - **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is
always printed by default. To disable it, set the opt to false. always printed by default. To disable it, set the opt to false.
- History is now fetched from Lua, which means users can override `hilbish.history`
methods to make it act how they want.
### Fixed ### Fixed
- If in Vim replace mode, input at the end of the line inserts instead of - If in Vim replace mode, input at the end of the line inserts instead of

View File

@ -540,13 +540,9 @@ func splitInput(input string) ([]string, string) {
} }
func cmdFinish(code uint8, cmdstr string, private bool) { func cmdFinish(code uint8, cmdstr string, private bool) {
// if input has space at the beginning, dont put in history
if interactive && !private {
handleHistory(cmdstr)
}
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command") util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
// using AsValue (to convert to lua type) on an interface which is an int // using AsValue (to convert to lua type) on an interface which is an int
// results in it being unknown in lua .... ???? // results in it being unknown in lua .... ????
// so we allow the hook handler to take lua runtime Values // so we allow the hook handler to take lua runtime Values
hooks.Em.Emit("command.exit", rt.IntValue(int64(code)), cmdstr) hooks.Em.Emit("command.exit", rt.IntValue(int64(code)), cmdstr, private)
} }

View File

@ -4,21 +4,69 @@ import (
"errors" "errors"
"io/fs" "io/fs"
"os" "os"
"path/filepath"
"strings" "strings"
rt "github.com/arnodel/golua/runtime"
) )
type luaHistory struct {}
func (h *luaHistory) Write(line string) (int, error) {
histWrite := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("add"))
ln, err := rt.Call1(l.MainThread(), histWrite, rt.StringValue(line))
var num int64
if ln.Type() == rt.IntType {
num = ln.AsInt()
}
return int(num), err
}
func (h *luaHistory) GetLine(idx int) (string, error) {
histGet := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("get"))
lcmd, err := rt.Call1(l.MainThread(), histGet, rt.IntValue(int64(idx)))
var cmd string
if lcmd.Type() == rt.StringType {
cmd = lcmd.AsString()
}
return cmd, err
}
func (h *luaHistory) Len() int {
histSize := hshMod.Get(rt.StringValue("history")).AsTable().Get(rt.StringValue("size"))
ln, _ := rt.Call1(l.MainThread(), histSize)
var num int64
if ln.Type() == rt.IntType {
num = ln.AsInt()
}
return int(num)
}
func (h *luaHistory) Dump() interface{} {
// hilbish.history interface already has all function, this isnt used in readline
return nil
}
type fileHistory struct { type fileHistory struct {
items []string items []string
f *os.File f *os.File
} }
func newFileHistory() *fileHistory { func newFileHistory(path string) *fileHistory {
err := os.MkdirAll(defaultHistDir, 0755) dir := filepath.Dir(path)
err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
panic(err) panic(err)
} }
data, err := os.ReadFile(defaultHistPath) data, err := os.ReadFile(path)
if err != nil { if err != nil {
if !errors.Is(err, fs.ErrNotExist) { if !errors.Is(err, fs.ErrNotExist) {
panic(err) panic(err)
@ -33,7 +81,7 @@ func newFileHistory() *fileHistory {
} }
itms = append(itms, l) itms = append(itms, l)
} }
f, err := os.OpenFile(defaultHistPath, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755) f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -269,11 +269,6 @@ func fmtPrompt(prompt string) string {
return nprompt return nprompt
} }
func handleHistory(cmd string) {
lr.AddHistory(cmd)
// TODO: load history again (history shared between sessions like this ye)
}
func removeDupes(slice []string) []string { func removeDupes(slice []string) []string {
all := make(map[string]bool) all := make(map[string]bool)
newSlice := []string{} newSlice := []string{}

View File

@ -0,0 +1,5 @@
local bait = require 'bait'
bait.catch('command.exit', function(_, cmd, priv)
if not priv and hilbish.opts.history then hilbish.history.add(cmd) end
end)

View File

@ -21,6 +21,7 @@ end
local defaultOpts = { local defaultOpts = {
autocd = false, autocd = false,
history = true,
greeting = string.format([[Welcome to {magenta}Hilbish{reset}, {cyan}%s{reset}. greeting = string.format([[Welcome to {magenta}Hilbish{reset}, {cyan}%s{reset}.
The nice lil shell for {blue}Lua{reset} fanatics! The nice lil shell for {blue}Lua{reset} fanatics!
]], hilbish.user), ]], hilbish.user),

26
rl.go
View File

@ -13,18 +13,22 @@ import (
type lineReader struct { type lineReader struct {
rl *readline.Instance rl *readline.Instance
fileHist *fileHistory
} }
var fileHist *fileHistory
var hinter *rt.Closure var hinter *rt.Closure
var highlighter *rt.Closure var highlighter *rt.Closure
func newLineReader(prompt string, noHist bool) *lineReader { func newLineReader(prompt string, noHist bool) *lineReader {
rl := readline.NewInstance() rl := readline.NewInstance()
lr := &lineReader{
rl: rl,
}
// we don't mind hilbish.read rl instances having completion, // we don't mind hilbish.read rl instances having completion,
// but it cant have shared history // but it cant have shared history
if !noHist { if !noHist {
fileHist = newFileHistory() lr.fileHist = newFileHistory(defaultHistPath)
rl.SetHistoryCtrlR("History", fileHist) rl.SetHistoryCtrlR("History", &luaHistory{})
rl.HistoryAutoWrite = false rl.HistoryAutoWrite = false
} }
rl.ShowVimMode = false rl.ShowVimMode = false
@ -171,9 +175,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
return pfx, compGroups return pfx, compGroups
} }
return &lineReader{ return lr
rl,
}
} }
func (lr *lineReader) Read() (string, error) { func (lr *lineReader) Read() (string, error) {
@ -212,7 +214,7 @@ func (lr *lineReader) SetRightPrompt(p string) {
} }
func (lr *lineReader) AddHistory(cmd string) { func (lr *lineReader) AddHistory(cmd string) {
fileHist.Write(cmd) lr.fileHist.Write(cmd)
} }
func (lr *lineReader) ClearInput() { func (lr *lineReader) ClearInput() {
@ -253,7 +255,7 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
} }
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.PushingNext1(t.Runtime, rt.IntValue(int64(fileHist.Len()))), nil return c.PushingNext1(t.Runtime, rt.IntValue(int64(lr.fileHist.Len()))), nil
} }
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -265,17 +267,17 @@ func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
return nil, err return nil, err
} }
cmd, _ := fileHist.GetLine(int(idx)) cmd, _ := lr.fileHist.GetLine(int(idx))
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
} }
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
tbl := rt.NewTable() tbl := rt.NewTable()
size := fileHist.Len() size := lr.fileHist.Len()
for i := 1; i < size; i++ { for i := 1; i < size; i++ {
cmd, _ := fileHist.GetLine(i) cmd, _ := lr.fileHist.GetLine(i)
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd)) tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
} }
@ -283,6 +285,6 @@ func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
} }
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
fileHist.clear() lr.fileHist.clear()
return c.Next(), nil return c.Next(), nil
} }