Compare commits

...

4 Commits

Author SHA1 Message Date
TorchedSammy 3d525aa7da
fix: dont allow overrides on hilbish table 2022-04-21 20:39:38 -04:00
TorchedSammy 37e1b12b81
feat: add hilbish.completion.call to call a completer 2022-04-21 20:39:06 -04:00
TorchedSammy abfd4e5196
fix(util): SetField on a table with a metatable causing panic 2022-04-21 20:33:32 -04:00
TorchedSammy bd35e3b871
refactor: use foreach function to loop over lua tables 2022-04-21 14:01:59 -04:00
4 changed files with 182 additions and 131 deletions

118
api.go
View File

@ -52,7 +52,30 @@ var hilbishLoader = packagelib.Loader{
} }
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
fakeMod := rt.NewTable()
modmt := rt.NewTable()
mod := rt.NewTable() mod := rt.NewTable()
modIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
arg := c.Arg(1)
val := mod.Get(arg)
return c.PushingNext1(t.Runtime, val), nil
}
modNewIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
k := c.Arg(1)
v := c.Arg(2)
if modVal := mod.Get(k); modVal != rt.NilValue {
return nil, errors.New("not allowed to override in hilbish table")
}
mod.Set(k, v)
return c.Next(), nil
}
modmt.Set(rt.StringValue("__newindex"), rt.FunctionValue(rt.NewGoFunction(modNewIndex, "__newindex", 3, false)))
modmt.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(modIndex, "__index", 2, false)))
fakeMod.SetMetatable(modmt)
util.SetExports(rtm, mod, exports) util.SetExports(rtm, mod, exports)
hshMod = mod hshMod = mod
@ -76,7 +99,7 @@ Check out the {blue}{bold}guide{reset} command to get started.
util.SetField(rtm, mod, "login", rt.BoolValue(login), "Whether this is a login shell") util.SetField(rtm, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
util.SetField(rtm, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.") util.SetField(rtm, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
util.SetField(rtm, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetField(rtm, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
util.SetField(rtm, hshMod, "exitCode", rt.IntValue(0), "Exit code of last exected command") util.SetField(rtm, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
util.Document(mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.") util.Document(mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
// hilbish.userDir table // hilbish.userDir table
@ -118,6 +141,10 @@ Check out the {blue}{bold}guide{reset} command to get started.
rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)), rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)),
"Completer for executables/binaries") "Completer for executables/binaries")
util.SetField(rtm, hshcomp, "call",
rt.FunctionValue(rt.NewGoFunction(callLuaCompleter, "call", 4, false)),
"Calls a completer and get its entries for completions")
util.Document(hshcomp, "Completions interface for Hilbish.") util.Document(hshcomp, "Completions interface for Hilbish.")
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
@ -137,7 +164,7 @@ Check out the {blue}{bold}guide{reset} command to get started.
util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.") util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.")
mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule)) mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule))
return rt.TableValue(mod), nil return rt.TableValue(fakeMod), nil
} }
func getenv(key, fallback string) string { func getenv(key, fallback string) string {
@ -148,75 +175,6 @@ func getenv(key, fallback string) string {
return value return value
} }
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
return nil, err
}
completions, _ := fileComplete(query, ctx, fds)
luaComps := rt.NewTable()
for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
}
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
return nil, err
}
completions, _ := binaryComplete(query, ctx, fds)
luaComps := rt.NewTable()
for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
}
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
if err := c.CheckNArgs(3); err != nil {
return "", "", []string{}, err
}
query, err := c.StringArg(0)
if err != nil {
return "", "", []string{}, err
}
ctx, err := c.StringArg(1)
if err != nil {
return "", "", []string{}, err
}
fields, err := c.TableArg(2)
if err != nil {
return "", "", []string{}, err
}
var fds []string
nextVal := rt.NilValue
for {
next, val, ok := fields.Next(nextVal)
if next == rt.NilValue {
break
}
nextVal = next
valStr, ok := val.TryString()
if !ok {
continue
}
fds = append(fds, valStr)
}
return query, ctx, fds, err
}
func setVimMode(mode string) { func setVimMode(mode string) {
util.SetField(l, hshMod, "vimMode", rt.StringValue(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetField(l, hshMod, "vimMode", rt.StringValue(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
hooks.Em.Emit("hilbish.vimMode", mode) hooks.Em.Emit("hilbish.vimMode", mode)
@ -395,21 +353,11 @@ func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// check if dir is a table or a string // check if dir is a table or a string
if arg.Type() == rt.TableType { if arg.Type() == rt.TableType {
nextVal := rt.NilValue util.ForEach(arg.AsTable(), func(k rt.Value, v rt.Value) {
for { if v.Type() == rt.StringType {
next, val, ok := arg.AsTable().Next(nextVal) appendPath(v.AsString())
if next == rt.NilValue {
break
}
nextVal = next
valStr, ok := val.TryString()
if !ok {
continue
}
appendPath(valStr)
} }
})
} else if arg.Type() == rt.StringType { } else if arg.Type() == rt.StringType {
appendPath(arg.AsString()) appendPath(arg.AsString())
} else { } else {

View File

@ -1,9 +1,14 @@
package main package main
import ( import (
"errors"
"path/filepath" "path/filepath"
"strings" "strings"
"os" "os"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
) )
func fileComplete(query, ctx string, fields []string) ([]string, string) { func fileComplete(query, ctx string, fields []string) ([]string, string) {
@ -111,3 +116,101 @@ func escapeFilename(fname string) string {
r := strings.NewReplacer(args...) r := strings.NewReplacer(args...)
return r.Replace(fname) return r.Replace(fname)
} }
func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(4); err != nil {
return nil, err
}
completer, err := c.StringArg(0)
if err != nil {
return nil, err
}
query, err := c.StringArg(1)
if err != nil {
return nil, err
}
ctx, err := c.StringArg(2)
if err != nil {
return nil, err
}
fields, err := c.TableArg(3)
if err != nil {
return nil, err
}
var completecb *rt.Closure
var ok bool
if completecb, ok = luaCompletions[completer]; !ok {
return nil, errors.New("completer " + completer + " does not exist")
}
// we must keep the holy 80 cols
completerReturn, err := rt.Call1(l.MainThread(),
rt.FunctionValue(completecb), rt.StringValue(query),
rt.StringValue(ctx), rt.TableValue(fields))
if err != nil {
return nil, err
}
return c.PushingNext1(t.Runtime, completerReturn), nil
}
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
return nil, err
}
completions, _ := fileComplete(query, ctx, fds)
luaComps := rt.NewTable()
for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
}
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
return nil, err
}
completions, _ := binaryComplete(query, ctx, fds)
luaComps := rt.NewTable()
for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
}
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
if err := c.CheckNArgs(3); err != nil {
return "", "", []string{}, err
}
query, err := c.StringArg(0)
if err != nil {
return "", "", []string{}, err
}
ctx, err := c.StringArg(1)
if err != nil {
return "", "", []string{}, err
}
fields, err := c.TableArg(2)
if err != nil {
return "", "", []string{}, err
}
var fds []string
util.ForEach(fields, func(k rt.Value, v rt.Value) {
if v.Type() == rt.StringType {
fds = append(fds, v.AsString())
}
})
return query, ctx, fds, err
}

70
rl.go
View File

@ -34,8 +34,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
case readline.VimKeys: modeStr = "normal" case readline.VimKeys: modeStr = "normal"
case readline.VimInsert: modeStr = "insert" case readline.VimInsert: modeStr = "insert"
case readline.VimDelete: modeStr = "delete" case readline.VimDelete: modeStr = "delete"
case readline.VimReplaceOnce: case readline.VimReplaceOnce, readline.VimReplaceMany: modeStr = "replace"
case readline.VimReplaceMany: modeStr = "replace"
} }
setVimMode(modeStr) setVimMode(modeStr)
} }
@ -153,71 +152,54 @@ func newLineReader(prompt string, noHist bool) *lineReader {
to work on subcommands and subcompletions to work on subcommands and subcompletions
*/ */
if cmpTbl, ok := luacompleteTable.TryTable(); ok { if cmpTbl, ok := luacompleteTable.TryTable(); ok {
nextVal := rt.NilValue util.ForEach(cmpTbl, func(key rt.Value, val rt.Value) {
for { if key.Type() != rt.IntType && val.Type() != rt.TableType {
next, val, ok := cmpTbl.Next(nextVal) return
if next == rt.NilValue {
break
}
nextVal = next
_, ok = next.TryInt()
valTbl, okk := val.TryTable()
if !ok || !okk {
// TODO: error?
break
} }
valTbl := val.AsTable()
luaCompType := valTbl.Get(rt.StringValue("type")) luaCompType := valTbl.Get(rt.StringValue("type"))
luaCompItems := valTbl.Get(rt.StringValue("items")) luaCompItems := valTbl.Get(rt.StringValue("items"))
compType, ok := luaCompType.TryString() if luaCompType.Type() != rt.StringType && luaCompItems.Type() != rt.TableType {
compItems, okk := luaCompItems.TryTable() return
if !ok || !okk {
// TODO: error
break
} }
var items []string items := []string{}
itemDescriptions := make(map[string]string) itemDescriptions := make(map[string]string)
nxVal := rt.NilValue
for {
nx, vl, _ := compItems.Next(nxVal)
if nx == rt.NilValue {
break
}
nxVal = nx
if tstr := nx.Type(); tstr == rt.StringType { util.ForEach(luaCompItems.AsTable(), func(lkey rt.Value, lval rt.Value) {
if keytyp := lkey.Type(); keytyp == rt.StringType {
// ['--flag'] = {'description', '--flag-alias'} // ['--flag'] = {'description', '--flag-alias'}
nxStr, ok := nx.TryString() itemName, ok := lkey.TryString()
vlTbl, okk := vl.TryTable() vlTbl, okk := lval.TryTable()
if !ok || !okk { if !ok && !okk {
// TODO: error // TODO: error
continue return
} }
items = append(items, nxStr)
items = append(items, itemName)
itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString() itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString()
if !ok { if !ok {
// TODO: error // TODO: error
continue return
} }
itemDescriptions[nxStr] = itemDescription itemDescriptions[itemName] = itemDescription
} else if tstr == rt.IntType { } else if keytyp == rt.IntType {
vlStr, okk := vl.TryString() vlStr, ok := lval.TryString()
if !okk { if !ok {
// TODO: error // TODO: error
continue return
} }
items = append(items, vlStr) items = append(items, vlStr)
} else { } else {
// TODO: error // TODO: error
continue return
}
} }
})
var dispType readline.TabDisplayType var dispType readline.TabDisplayType
switch compType { switch luaCompType.AsString() {
case "grid": dispType = readline.TabDisplayGrid case "grid": dispType = readline.TabDisplayGrid
case "list": dispType = readline.TabDisplayList case "list": dispType = readline.TabDisplayList
// need special cases, will implement later // need special cases, will implement later
@ -231,7 +213,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
TrimSlash: false, TrimSlash: false,
NoSpace: true, NoSpace: true,
}) })
} })
} }
} }

View File

@ -29,12 +29,16 @@ func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, d
if mt == nil { if mt == nil {
mt = rt.NewTable() mt = rt.NewTable()
docProp := rt.NewTable()
mt.Set(rt.StringValue("__docProp"), rt.TableValue(docProp))
module.SetMetatable(mt) module.SetMetatable(mt)
} }
docProp := mt.Get(rt.StringValue("__docProp")) docProp := mt.Get(rt.StringValue("__docProp"))
if docProp == rt.NilValue {
docPropTbl := rt.NewTable()
mt.Set(rt.StringValue("__docProp"), rt.TableValue(docPropTbl))
docProp = mt.Get(rt.StringValue("__docProp"))
}
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc)) docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc))
module.Set(rt.StringValue(field), value) module.Set(rt.StringValue(field), value)
@ -118,3 +122,17 @@ func HandleStrCallback(t *rt.Thread, c *rt.GoCont) (string, *rt.Closure, error)
return name, cb, err return name, cb, err
} }
// ForEach loops through a Lua table.
func ForEach(tbl *rt.Table, cb func(key rt.Value, val rt.Value)) {
nextVal := rt.NilValue
for {
key, val, _ := tbl.Next(nextVal)
if key == rt.NilValue {
break
}
nextVal = key
cb(key, val)
}
}