mirror of https://github.com/Hilbis/Hilbish
feat: allow overwrite of completion handler (closes #122)
this also makes the completion functions `bins` and `files` also return the prefix to pass to the completion handler. this is an overhaul to the completion system, which gets the completion handler from lua instead of being made to only have lua provided *command* completions. it does not have any performance deficit, even though it calls in to golua for completions.insensitive-tab^2
parent
3194add3dc
commit
abfbeb5f84
14
api.go
14
api.go
|
@ -149,19 +149,7 @@ Check out the {blue}{bold}guide{reset} command to get started.
|
||||||
util.Document(historyModule, "History interface for Hilbish.")
|
util.Document(historyModule, "History interface for Hilbish.")
|
||||||
|
|
||||||
// hilbish.completion table
|
// hilbish.completion table
|
||||||
hshcomp := rt.NewTable()
|
hshcomp := completionLoader(rtm)
|
||||||
util.SetField(rtm, hshcomp, "files",
|
|
||||||
rt.FunctionValue(rt.NewGoFunction(luaFileComplete, "files", 3, false)),
|
|
||||||
"Completer for files")
|
|
||||||
|
|
||||||
util.SetField(rtm, hshcomp, "bins",
|
|
||||||
rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)),
|
|
||||||
"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))
|
||||||
|
|
||||||
|
|
104
complete.go
104
complete.go
|
@ -11,6 +11,8 @@ import (
|
||||||
rt "github.com/arnodel/golua/runtime"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var completer rt.Value
|
||||||
|
|
||||||
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
||||||
return matchPath(query)
|
return matchPath(query)
|
||||||
}
|
}
|
||||||
|
@ -117,6 +119,100 @@ func escapeFilename(fname string) string {
|
||||||
return r.Replace(fname)
|
return r.Replace(fname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
|
exports := map[string]util.LuaExport{
|
||||||
|
"files": {luaFileComplete, 3, false},
|
||||||
|
"bins": {luaBinaryComplete, 3, false},
|
||||||
|
"call": {callLuaCompleter, 4, false},
|
||||||
|
"handler": {completionHandler, 2, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
mod := rt.NewTable()
|
||||||
|
util.SetExports(rtm, mod, exports)
|
||||||
|
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
|
||||||
|
func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// just for validation
|
||||||
|
_, err = c.IntArg(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := strings.TrimLeft(line, " ")
|
||||||
|
if len(ctx) == 0 {
|
||||||
|
return c.PushingNext(t.Runtime, rt.TableValue(rt.NewTable()), rt.StringValue("")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = aliases.Resolve(ctx)
|
||||||
|
fields := strings.Split(ctx, " ")
|
||||||
|
query := fields[len(fields) - 1]
|
||||||
|
|
||||||
|
luaFields := rt.NewTable()
|
||||||
|
|
||||||
|
for i, f := range fields {
|
||||||
|
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
compMod := hshMod.Get(rt.StringValue("completion")).AsTable()
|
||||||
|
var term *rt.Termination
|
||||||
|
if len(fields) == 1 {
|
||||||
|
term = rt.NewTerminationWith(t.CurrentCont(), 2, false)
|
||||||
|
err := rt.Call(t, compMod.Get(rt.StringValue("bins")), []rt.Value{
|
||||||
|
rt.StringValue(query),
|
||||||
|
rt.StringValue(ctx),
|
||||||
|
rt.TableValue(luaFields),
|
||||||
|
}, term)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gterm := rt.NewTerminationWith(t.CurrentCont(), 2, false)
|
||||||
|
err := rt.Call(t, compMod.Get(rt.StringValue("call")), []rt.Value{
|
||||||
|
rt.StringValue("commands." + fields[0]),
|
||||||
|
rt.StringValue(query),
|
||||||
|
rt.StringValue(ctx),
|
||||||
|
rt.TableValue(luaFields),
|
||||||
|
}, gterm)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
groups := gterm.Get(0)
|
||||||
|
pfx := gterm.Get(1)
|
||||||
|
|
||||||
|
return c.PushingNext(t.Runtime, groups, pfx), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// error means there isnt a command handler - default to files in that case
|
||||||
|
term = rt.NewTerminationWith(t.CurrentCont(), 2, false)
|
||||||
|
err = rt.Call(t, compMod.Get(rt.StringValue("files")), []rt.Value{
|
||||||
|
rt.StringValue(query),
|
||||||
|
rt.StringValue(ctx),
|
||||||
|
rt.TableValue(luaFields),
|
||||||
|
}, term)
|
||||||
|
}
|
||||||
|
|
||||||
|
comps := term.Get(0)
|
||||||
|
pfx := term.Get(1)
|
||||||
|
|
||||||
|
groups := rt.NewTable()
|
||||||
|
|
||||||
|
compGroup := rt.NewTable()
|
||||||
|
compGroup.Set(rt.StringValue("items"), comps)
|
||||||
|
compGroup.Set(rt.StringValue("type"), rt.StringValue("grid"))
|
||||||
|
|
||||||
|
groups.Set(rt.IntValue(1), rt.TableValue(compGroup))
|
||||||
|
return c.PushingNext(t.Runtime, rt.TableValue(groups), pfx), nil
|
||||||
|
}
|
||||||
|
|
||||||
func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if err := c.CheckNArgs(4); err != nil {
|
if err := c.CheckNArgs(4); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -162,14 +258,14 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
completions, _ := fileComplete(query, ctx, fds)
|
completions, pfx := fileComplete(query, ctx, fds)
|
||||||
luaComps := rt.NewTable()
|
luaComps := rt.NewTable()
|
||||||
|
|
||||||
for i, comp := range completions {
|
for i, comp := range completions {
|
||||||
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
|
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
@ -178,14 +274,14 @@ func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
completions, _ := binaryComplete(query, ctx, fds)
|
completions, pfx := binaryComplete(query, ctx, fds)
|
||||||
luaComps := rt.NewTable()
|
luaComps := rt.NewTable()
|
||||||
|
|
||||||
for i, comp := range completions {
|
for i, comp := range completions {
|
||||||
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
|
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
|
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
|
||||||
|
|
102
rl.go
102
rl.go
|
@ -84,76 +84,29 @@ 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)
|
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false)
|
||||||
|
compHandle := hshMod.Get(rt.StringValue("completion")).AsTable().Get(rt.StringValue("handler"))
|
||||||
var compGroup []*readline.CompletionGroup
|
err := rt.Call(l.MainThread(), compHandle, []rt.Value{rt.StringValue(string(line)),
|
||||||
|
rt.IntValue(int64(pos))}, term)
|
||||||
ctx = strings.TrimLeft(ctx, " ")
|
|
||||||
if len(ctx) == 0 {
|
|
||||||
return "", compGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := strings.Split(ctx, " ")
|
|
||||||
if len(fields) == 0 {
|
|
||||||
return "", compGroup
|
|
||||||
}
|
|
||||||
query := fields[len(fields) - 1]
|
|
||||||
|
|
||||||
ctx = aliases.Resolve(ctx)
|
|
||||||
|
|
||||||
if len(fields) == 1 {
|
|
||||||
completions, prefix := binaryComplete(query, ctx, fields)
|
|
||||||
|
|
||||||
compGroup = append(compGroup, &readline.CompletionGroup{
|
|
||||||
TrimSlash: false,
|
|
||||||
NoSpace: true,
|
|
||||||
Suggestions: completions,
|
|
||||||
})
|
|
||||||
|
|
||||||
return prefix, compGroup
|
|
||||||
} else {
|
|
||||||
if completecb, ok := luaCompletions["command." + fields[0]]; ok {
|
|
||||||
luaFields := rt.NewTable()
|
|
||||||
for i, f := range fields {
|
|
||||||
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
|
|
||||||
|
var compGroups []*readline.CompletionGroup
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", compGroup
|
return "", compGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
luaCompGroups := term.Get(0)
|
||||||
as an example with git,
|
luaPrefix := term.Get(1)
|
||||||
completion table should be structured like:
|
|
||||||
{
|
if luaCompGroups.Type() != rt.TableType {
|
||||||
{
|
return "", compGroups
|
||||||
items = {
|
|
||||||
'add',
|
|
||||||
'clone',
|
|
||||||
'init'
|
|
||||||
},
|
|
||||||
type = 'grid'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
items = {
|
|
||||||
'-c',
|
|
||||||
'--git-dir'
|
|
||||||
},
|
|
||||||
type = 'list'
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
^ a table of completion groups.
|
groups := luaCompGroups.AsTable()
|
||||||
it is the responsibility of the completer
|
// prefix is optional
|
||||||
to work on subcommands and subcompletions
|
pfx, _ := luaPrefix.TryString()
|
||||||
*/
|
|
||||||
if cmpTbl, ok := luacompleteTable.TryTable(); ok {
|
util.ForEach(groups, func(key rt.Value, val rt.Value) {
|
||||||
util.ForEach(cmpTbl, func(key rt.Value, val rt.Value) {
|
if key.Type() != rt.IntType || val.Type() != rt.TableType {
|
||||||
if key.Type() != rt.IntType && val.Type() != rt.TableType {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +114,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
luaCompType := valTbl.Get(rt.StringValue("type"))
|
luaCompType := valTbl.Get(rt.StringValue("type"))
|
||||||
luaCompItems := valTbl.Get(rt.StringValue("items"))
|
luaCompItems := valTbl.Get(rt.StringValue("items"))
|
||||||
|
|
||||||
if luaCompType.Type() != rt.StringType && luaCompItems.Type() != rt.TableType {
|
if luaCompType.Type() != rt.StringType || luaCompItems.Type() != rt.TableType {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +159,7 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
//case "map": dispType = readline.TabDisplayMap
|
//case "map": dispType = readline.TabDisplayMap
|
||||||
}
|
}
|
||||||
|
|
||||||
compGroup = append(compGroup, &readline.CompletionGroup{
|
compGroups = append(compGroups, &readline.CompletionGroup{
|
||||||
DisplayType: dispType,
|
DisplayType: dispType,
|
||||||
Descriptions: itemDescriptions,
|
Descriptions: itemDescriptions,
|
||||||
Suggestions: items,
|
Suggestions: items,
|
||||||
|
@ -214,21 +167,8 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
NoSpace: true,
|
NoSpace: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(compGroup) == 0 {
|
return pfx, compGroups
|
||||||
completions, p := fileComplete(query, ctx, fields)
|
|
||||||
fcompGroup := []*readline.CompletionGroup{{
|
|
||||||
TrimSlash: false,
|
|
||||||
NoSpace: true,
|
|
||||||
Suggestions: completions,
|
|
||||||
}}
|
|
||||||
|
|
||||||
return p, fcompGroup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", compGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lineReader{
|
return &lineReader{
|
||||||
|
|
Loading…
Reference in New Issue