feat: implement completions

lua5.4
TorchedSammy 2022-03-29 07:03:58 -04:00
parent dce02e5310
commit ead72f0a5c
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
3 changed files with 99 additions and 63 deletions

17
api.go
View File

@ -25,8 +25,8 @@ var exports = map[string]util.LuaExport{
/*
"alias": hlalias,
"appendPath": hlappendPath,
"complete": hlcomplete,
*/
"complete": util.LuaExport{hlcomplete, 2, false},
"cwd": util.LuaExport{hlcwd, 0, false},
/*
"exec": hlexec,
@ -121,7 +121,7 @@ Check out the {blue}{bold}guide{reset} command to get started.
util.Document(L, historyModule, "History interface for Hilbish.")
L.SetField(mod, "history", historyModule)
// hilbish.completions table
// hilbish.completion table
hshcomp := L.NewTable()
util.SetField(L, hshcomp, "files", L.NewFunction(luaFileComplete), "Completer for files")
@ -446,6 +446,7 @@ func hlinterval(L *lua.LState) int {
L.Push(lua.LChannel(stop))
return 1
}
*/
// complete(scope, cb)
// Registers a completion handler for `scope`.
@ -457,15 +458,17 @@ func hlinterval(L *lua.LState) int {
// `grid` (the normal file completion display) or `list` (with a description)
// --- @param scope string
// --- @param cb function
func hlcomplete(L *lua.LState) int {
scope := L.CheckString(1)
cb := L.CheckFunction(2)
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
scope, cb, err := util.HandleStrCallback(t, c)
if err != nil {
return nil, err
}
luaCompletions[scope] = cb
return 0
return c.Next(), nil
}
/*
// prependPath(dir)
// Prepends `dir` to $PATH
// --- @param dir string

View File

@ -14,7 +14,6 @@ import (
rt "github.com/arnodel/golua/runtime"
"github.com/pborman/getopt"
"github.com/yuin/gopher-lua"
"github.com/maxlandon/readline"
"golang.org/x/term"
)
@ -24,7 +23,7 @@ var (
lr *lineReader
commands = map[string]*rt.Closure{}
luaCompletions = map[string]*lua.LFunction{}
luaCompletions = map[string]*rt.Closure{}
confDir string
userDataDir string

108
rl.go
View File

@ -6,6 +6,7 @@ import (
"strings"
"github.com/maxlandon/readline"
rt "github.com/arnodel/golua/runtime"
)
type lineReader struct {
@ -93,6 +94,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
return highlighted
}
*/
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
ctx := string(line)
var completions []string
@ -124,23 +126,20 @@ func newLineReader(prompt string, noHist bool) *lineReader {
return prefix, compGroup
} else {
if completecb, ok := luaCompletions["command." + fields[0]]; ok {
luaFields := l.NewTable()
for _, f := range fields {
luaFields.Append(lua.LString(f))
luaFields := rt.NewTable()
for i, f := range fields {
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
}
err := l.CallByParam(lua.P{
Fn: completecb,
NRet: 1,
Protect: true,
}, lua.LString(query), lua.LString(ctx), luaFields)
// we must keep the holy 80 cols
luacompleteTable, err := rt.Call1(l.MainThread(),
rt.FunctionValue(completecb), rt.StringValue(query),
rt.StringValue(ctx), rt.TableValue(luaFields))
if err != nil {
return "", compGroup
}
luacompleteTable := l.Get(-1)
l.Pop(1)
/*
as an example with git,
completion table should be structured like:
@ -165,41 +164,78 @@ func newLineReader(prompt string, noHist bool) *lineReader {
it is the responsibility of the completer
to work on subcommands and subcompletions
*/
/*
if cmpTbl, ok := luacompleteTable.(*lua.LTable); ok {
cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
if key.Type() == lua.LTNumber {
// completion group
if value.Type() == lua.LTTable {
luaCmpGroup := value.(*lua.LTable)
compType := luaCmpGroup.RawGet(lua.LString("type"))
compItems := luaCmpGroup.RawGet(lua.LString("items"))
if compType.Type() != lua.LTString {
l.RaiseError("bad type name for completion (expected string, got %v)", compType.Type().String())
if cmpTbl, ok := luacompleteTable.TryTable(); ok {
nextVal := rt.NilValue
for {
next, val, ok := cmpTbl.Next(nextVal)
if next == rt.NilValue {
break
}
if compItems.Type() != lua.LTTable {
l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String())
nextVal = next
_, ok = next.TryInt()
valTbl, okk := val.TryTable()
if !ok || !okk {
// TODO: error?
break
}
luaCompType := valTbl.Get(rt.StringValue("type"))
luaCompItems := valTbl.Get(rt.StringValue("items"))
compType, ok := luaCompType.TryString()
compItems, okk := luaCompItems.TryTable()
if !ok || !okk {
// TODO: error
break
}
var items []string
itemDescriptions := make(map[string]string)
compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
if k.Type() == lua.LTString {
// ['--flag'] = {'description', '--flag-alias'}
itm := v.(*lua.LTable)
items = append(items, k.String())
itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String()
} else {
items = append(items, v.String())
nxVal := rt.NilValue
for {
nx, vl, _ := compItems.Next(nxVal)
if nx == rt.NilValue {
break
}
nxVal = nx
if tstr := nx.Type(); tstr == rt.StringType {
// ['--flag'] = {'description', '--flag-alias'}
nxStr, ok := nx.TryString()
vlTbl, okk := vl.TryTable()
if !ok || !okk {
// TODO: error
continue
}
items = append(items, nxStr)
itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString()
if !ok {
// TODO: error
continue
}
itemDescriptions[nxStr] = itemDescription
} else if tstr == rt.IntType {
vlStr, okk := vl.TryString()
if !okk {
// TODO: error
continue
}
items = append(items, vlStr)
} else {
// TODO: error
continue
}
}
})
var dispType readline.TabDisplayType
switch compType.String() {
switch compType {
case "grid": dispType = readline.TabDisplayGrid
case "list": dispType = readline.TabDisplayList
// need special cases, will implement later
//case "map": dispType = readline.TabDisplayMap
}
compGroup = append(compGroup, &readline.CompletionGroup{
DisplayType: dispType,
Descriptions: itemDescriptions,
@ -209,8 +245,6 @@ func newLineReader(prompt string, noHist bool) *lineReader {
})
}
}
})
}
}
if len(compGroup) == 0 {
@ -223,7 +257,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
}
}
return "", compGroup
}*/
}
return &lineReader{
rl,