mirror of https://github.com/Hilbis/Hilbish
feat: implement completions
parent
dce02e5310
commit
ead72f0a5c
17
api.go
17
api.go
|
@ -25,8 +25,8 @@ var exports = map[string]util.LuaExport{
|
||||||
/*
|
/*
|
||||||
"alias": hlalias,
|
"alias": hlalias,
|
||||||
"appendPath": hlappendPath,
|
"appendPath": hlappendPath,
|
||||||
"complete": hlcomplete,
|
|
||||||
*/
|
*/
|
||||||
|
"complete": util.LuaExport{hlcomplete, 2, false},
|
||||||
"cwd": util.LuaExport{hlcwd, 0, false},
|
"cwd": util.LuaExport{hlcwd, 0, false},
|
||||||
/*
|
/*
|
||||||
"exec": hlexec,
|
"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.")
|
util.Document(L, historyModule, "History interface for Hilbish.")
|
||||||
L.SetField(mod, "history", historyModule)
|
L.SetField(mod, "history", historyModule)
|
||||||
|
|
||||||
// hilbish.completions table
|
// hilbish.completion table
|
||||||
hshcomp := L.NewTable()
|
hshcomp := L.NewTable()
|
||||||
|
|
||||||
util.SetField(L, hshcomp, "files", L.NewFunction(luaFileComplete), "Completer for files")
|
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))
|
L.Push(lua.LChannel(stop))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// complete(scope, cb)
|
// complete(scope, cb)
|
||||||
// Registers a completion handler for `scope`.
|
// 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)
|
// `grid` (the normal file completion display) or `list` (with a description)
|
||||||
// --- @param scope string
|
// --- @param scope string
|
||||||
// --- @param cb function
|
// --- @param cb function
|
||||||
func hlcomplete(L *lua.LState) int {
|
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
scope := L.CheckString(1)
|
scope, cb, err := util.HandleStrCallback(t, c)
|
||||||
cb := L.CheckFunction(2)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
luaCompletions[scope] = cb
|
luaCompletions[scope] = cb
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// prependPath(dir)
|
// prependPath(dir)
|
||||||
// Prepends `dir` to $PATH
|
// Prepends `dir` to $PATH
|
||||||
// --- @param dir string
|
// --- @param dir string
|
||||||
|
|
3
main.go
3
main.go
|
@ -14,7 +14,6 @@ import (
|
||||||
|
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"github.com/pborman/getopt"
|
"github.com/pborman/getopt"
|
||||||
"github.com/yuin/gopher-lua"
|
|
||||||
"github.com/maxlandon/readline"
|
"github.com/maxlandon/readline"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +23,7 @@ var (
|
||||||
lr *lineReader
|
lr *lineReader
|
||||||
|
|
||||||
commands = map[string]*rt.Closure{}
|
commands = map[string]*rt.Closure{}
|
||||||
luaCompletions = map[string]*lua.LFunction{}
|
luaCompletions = map[string]*rt.Closure{}
|
||||||
|
|
||||||
confDir string
|
confDir string
|
||||||
userDataDir string
|
userDataDir string
|
||||||
|
|
108
rl.go
108
rl.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/maxlandon/readline"
|
"github.com/maxlandon/readline"
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lineReader struct {
|
type lineReader struct {
|
||||||
|
@ -93,6 +94,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
|
|
||||||
return highlighted
|
return highlighted
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
|
rl.TabCompleter = func(line []rune, pos int, _ readline.DelayedTabContext) (string, []*readline.CompletionGroup) {
|
||||||
ctx := string(line)
|
ctx := string(line)
|
||||||
var completions []string
|
var completions []string
|
||||||
|
@ -124,23 +126,20 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
return prefix, compGroup
|
return prefix, compGroup
|
||||||
} else {
|
} else {
|
||||||
if completecb, ok := luaCompletions["command." + fields[0]]; ok {
|
if completecb, ok := luaCompletions["command." + fields[0]]; ok {
|
||||||
luaFields := l.NewTable()
|
luaFields := rt.NewTable()
|
||||||
for _, f := range fields {
|
for i, f := range fields {
|
||||||
luaFields.Append(lua.LString(f))
|
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
|
||||||
}
|
}
|
||||||
err := l.CallByParam(lua.P{
|
|
||||||
Fn: completecb,
|
// we must keep the holy 80 cols
|
||||||
NRet: 1,
|
luacompleteTable, err := rt.Call1(l.MainThread(),
|
||||||
Protect: true,
|
rt.FunctionValue(completecb), rt.StringValue(query),
|
||||||
}, lua.LString(query), lua.LString(ctx), luaFields)
|
rt.StringValue(ctx), rt.TableValue(luaFields))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", compGroup
|
return "", compGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
luacompleteTable := l.Get(-1)
|
|
||||||
l.Pop(1)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
as an example with git,
|
as an example with git,
|
||||||
completion table should be structured like:
|
completion table should be structured like:
|
||||||
|
@ -165,41 +164,78 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
it is the responsibility of the completer
|
it is the responsibility of the completer
|
||||||
to work on subcommands and subcompletions
|
to work on subcommands and subcompletions
|
||||||
*/
|
*/
|
||||||
/*
|
if cmpTbl, ok := luacompleteTable.TryTable(); ok {
|
||||||
if cmpTbl, ok := luacompleteTable.(*lua.LTable); ok {
|
nextVal := rt.NilValue
|
||||||
cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
for {
|
||||||
if key.Type() == lua.LTNumber {
|
next, val, ok := cmpTbl.Next(nextVal)
|
||||||
// completion group
|
if next == rt.NilValue {
|
||||||
if value.Type() == lua.LTTable {
|
break
|
||||||
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 compItems.Type() != lua.LTTable {
|
nextVal = next
|
||||||
l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String())
|
|
||||||
|
_, 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
|
var items []string
|
||||||
itemDescriptions := make(map[string]string)
|
itemDescriptions := make(map[string]string)
|
||||||
compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
|
nxVal := rt.NilValue
|
||||||
if k.Type() == lua.LTString {
|
for {
|
||||||
// ['--flag'] = {'description', '--flag-alias'}
|
nx, vl, _ := compItems.Next(nxVal)
|
||||||
itm := v.(*lua.LTable)
|
if nx == rt.NilValue {
|
||||||
items = append(items, k.String())
|
break
|
||||||
itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String()
|
}
|
||||||
} else {
|
nxVal = nx
|
||||||
items = append(items, v.String())
|
|
||||||
|
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
|
var dispType readline.TabDisplayType
|
||||||
switch compType.String() {
|
switch compType {
|
||||||
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
|
||||||
//case "map": dispType = readline.TabDisplayMap
|
//case "map": dispType = readline.TabDisplayMap
|
||||||
}
|
}
|
||||||
|
|
||||||
compGroup = append(compGroup, &readline.CompletionGroup{
|
compGroup = append(compGroup, &readline.CompletionGroup{
|
||||||
DisplayType: dispType,
|
DisplayType: dispType,
|
||||||
Descriptions: itemDescriptions,
|
Descriptions: itemDescriptions,
|
||||||
|
@ -209,8 +245,6 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(compGroup) == 0 {
|
if len(compGroup) == 0 {
|
||||||
|
@ -223,7 +257,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", compGroup
|
return "", compGroup
|
||||||
}*/
|
}
|
||||||
|
|
||||||
return &lineReader{
|
return &lineReader{
|
||||||
rl,
|
rl,
|
||||||
|
|
Loading…
Reference in New Issue