mirror of
https://github.com/Hilbis/Hilbish
synced 2025-05-11 14:53:22 +00:00
Merge 4e40bc57e0591a236eda628017ab7413721325a7 into 7b3dc951c9ce5e99b911ec4ad136aef45ce54ca6
This commit is contained in:
commit
0518bb44e6
@ -85,6 +85,7 @@ random errors introduced with the new Lua runtime (see [#197])
|
|||||||
- `exec`, `clear` and `cat` builtin commands
|
- `exec`, `clear` and `cat` builtin commands
|
||||||
- `hilbish.cancel` hook
|
- `hilbish.cancel` hook
|
||||||
- 1st item on history is now inserted when history search menu is opened ([#148])
|
- 1st item on history is now inserted when history search menu is opened ([#148])
|
||||||
|
- Case insensitive tab completion! It can be enabled with the `insensitive` opt.
|
||||||
|
|
||||||
[#148]: https://github.com/Rosettea/Hilbish/issues/148
|
[#148]: https://github.com/Rosettea/Hilbish/issues/148
|
||||||
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
||||||
|
53
complete.go
53
complete.go
@ -72,13 +72,13 @@ func splitForFile(str string) []string {
|
|||||||
return split
|
return split
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
func fileComplete(query, ctx string, fields []string, caseInsensitive bool) ([]string, string) {
|
||||||
q := splitForFile(ctx)
|
q := splitForFile(ctx)
|
||||||
|
|
||||||
return matchPath(q[len(q) - 1])
|
return matchPath(q[len(q) - 1], caseInsensitive)
|
||||||
}
|
}
|
||||||
|
|
||||||
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
func binaryComplete(query, ctx string, fields []string, caseInsensitive bool) ([]string, string) {
|
||||||
q := splitForFile(ctx)
|
q := splitForFile(ctx)
|
||||||
query = q[len(q) - 1]
|
query = q[len(q) - 1]
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
|||||||
prefixes := []string{"./", "../", "/", "~/"}
|
prefixes := []string{"./", "../", "/", "~/"}
|
||||||
for _, prefix := range prefixes {
|
for _, prefix := range prefixes {
|
||||||
if strings.HasPrefix(query, prefix) {
|
if strings.HasPrefix(query, prefix) {
|
||||||
fileCompletions, filePref := matchPath(query)
|
fileCompletions, filePref := matchPath(query, caseInsensitive)
|
||||||
if len(fileCompletions) != 0 {
|
if len(fileCompletions) != 0 {
|
||||||
for _, f := range fileCompletions {
|
for _, f := range fileCompletions {
|
||||||
fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
|
fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
|
||||||
@ -132,7 +132,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
|||||||
return completions, query
|
return completions, query
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchPath(query string) ([]string, string) {
|
func matchPath(query string, caseInsensitive bool) ([]string, string) {
|
||||||
oldQuery := query
|
oldQuery := query
|
||||||
query = strings.TrimPrefix(query, "\"")
|
query = strings.TrimPrefix(query, "\"")
|
||||||
var entries []string
|
var entries []string
|
||||||
@ -148,9 +148,17 @@ func matchPath(query string) ([]string, string) {
|
|||||||
baseName = filepath.Base(query)
|
baseName = filepath.Base(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if caseInsensitive {
|
||||||
|
baseName = strings.ToLower(baseName)
|
||||||
|
}
|
||||||
|
|
||||||
files, _ := os.ReadDir(path)
|
files, _ := os.ReadDir(path)
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if strings.HasPrefix(file.Name(), baseName) {
|
fname := file.Name()
|
||||||
|
if caseInsensitive {
|
||||||
|
fname = strings.ToLower(fname)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(fname, baseName) {
|
||||||
entry := file.Name()
|
entry := file.Name()
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
entry = entry + string(os.PathSeparator)
|
entry = entry + string(os.PathSeparator)
|
||||||
@ -174,8 +182,8 @@ func escapeFilename(fname string) string {
|
|||||||
|
|
||||||
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
exports := map[string]util.LuaExport{
|
exports := map[string]util.LuaExport{
|
||||||
"files": {luaFileComplete, 3, false},
|
"files": {luaFileComplete, 3, true},
|
||||||
"bins": {luaBinaryComplete, 3, false},
|
"bins": {luaBinaryComplete, 3, true},
|
||||||
"call": {callLuaCompleter, 4, false},
|
"call": {callLuaCompleter, 4, false},
|
||||||
"handler": {completionHandler, 2, false},
|
"handler": {completionHandler, 2, false},
|
||||||
}
|
}
|
||||||
@ -231,12 +239,12 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
query, ctx, fds, err := getCompleteParams(t, c)
|
query, ctx, fds, insensitive, err := getCompleteParams(t, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
completions, pfx := fileComplete(query, ctx, fds)
|
completions, pfx := fileComplete(query, ctx, fds, insensitive)
|
||||||
luaComps := rt.NewTable()
|
luaComps := rt.NewTable()
|
||||||
|
|
||||||
for i, comp := range completions {
|
for i, comp := range completions {
|
||||||
@ -247,12 +255,12 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
query, ctx, fds, err := getCompleteParams(t, c)
|
query, ctx, fds, insensitive, err := getCompleteParams(t, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
completions, pfx := binaryComplete(query, ctx, fds)
|
completions, pfx := binaryComplete(query, ctx, fds, insensitive)
|
||||||
luaComps := rt.NewTable()
|
luaComps := rt.NewTable()
|
||||||
|
|
||||||
for i, comp := range completions {
|
for i, comp := range completions {
|
||||||
@ -262,21 +270,30 @@ func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||||||
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), 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, bool, error) {
|
||||||
if err := c.CheckNArgs(3); err != nil {
|
if err := c.CheckNArgs(3); err != nil {
|
||||||
return "", "", []string{}, err
|
return "", "", []string{}, false, err
|
||||||
}
|
}
|
||||||
query, err := c.StringArg(0)
|
query, err := c.StringArg(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", []string{}, err
|
return "", "", []string{}, false, err
|
||||||
}
|
}
|
||||||
ctx, err := c.StringArg(1)
|
ctx, err := c.StringArg(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", []string{}, err
|
return "", "", []string{}, false, err
|
||||||
}
|
}
|
||||||
fields, err := c.TableArg(2)
|
fields, err := c.TableArg(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", []string{}, err
|
return "", "", []string{}, false, err
|
||||||
|
}
|
||||||
|
var insensitive bool
|
||||||
|
if len(c.Etc()) != 0 {
|
||||||
|
typ := c.Etc()[0]
|
||||||
|
var ok bool
|
||||||
|
insensitive, ok = typ.TryBool()
|
||||||
|
if !ok {
|
||||||
|
return "", "", []string{}, false, errors.New("bad argument #4 (expected bool, got " + typ.TypeName() + ")")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fds []string
|
var fds []string
|
||||||
@ -286,5 +303,5 @@ func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, er
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return query, ctx, fds, err
|
return query, ctx, fds, insensitive, err
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,9 @@ then there is the `files` function, which is mentioned below.
|
|||||||
|
|
||||||
# Completion Interface
|
# Completion Interface
|
||||||
## Functions
|
## Functions
|
||||||
- `files(query, ctx, fields)` -> table, prefix: get file completions, based
|
- `files(query, ctx, fields, caseInsensitive)` -> table, prefix: get file completions, based
|
||||||
on the user's query.
|
on the user's query.
|
||||||
- `bins(query, ctx, fields)` -> table, prefix: get binary/executable
|
- `bins(query, ctx, fields, caseInsensitive)` -> table, prefix: get binary/executable
|
||||||
completions, based on user query.
|
completions, based on user query.
|
||||||
- `call(scope, query, ctx, fields)` -> table, prefix: call a completion handler
|
- `call(scope, query, ctx, fields)` -> table, prefix: call a completion handler
|
||||||
with `scope`, usually being in the form of `command.<name>`
|
with `scope`, usually being in the form of `command.<name>`
|
||||||
|
@ -15,7 +15,7 @@ function hilbish.completion.handler(line, pos)
|
|||||||
local query = fields[#fields]
|
local query = fields[#fields]
|
||||||
|
|
||||||
if #fields == 1 then
|
if #fields == 1 then
|
||||||
local comps, pfx = hilbish.completion.bins(query, ctx, fields)
|
local comps, pfx = hilbish.completion.bins(query, ctx, fields, hilbish.opts.insensitive)
|
||||||
local compGroup = {
|
local compGroup = {
|
||||||
items = comps,
|
items = comps,
|
||||||
type = 'grid'
|
type = 'grid'
|
||||||
@ -29,7 +29,7 @@ function hilbish.completion.handler(line, pos)
|
|||||||
return compGroups, pfx
|
return compGroups, pfx
|
||||||
end
|
end
|
||||||
|
|
||||||
local comps, pfx = hilbish.completion.files(query, ctx, fields)
|
local comps, pfx = hilbish.completion.files(query, ctx, fields, hilbish.opts.insensitive)
|
||||||
local compGroup = {
|
local compGroup = {
|
||||||
items = comps,
|
items = comps,
|
||||||
type = 'grid'
|
type = 'grid'
|
||||||
|
@ -25,7 +25,8 @@ local defaultOpts = {
|
|||||||
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),
|
||||||
motd = true
|
motd = true,
|
||||||
|
insensitive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
for optsName, default in pairs(defaultOpts) do
|
for optsName, default in pairs(defaultOpts) do
|
||||||
|
1
nature/opts/insensitive.lua
Normal file
1
nature/opts/insensitive.lua
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- noop
|
@ -74,7 +74,9 @@ func (rl *Instance) insertCandidate() {
|
|||||||
|
|
||||||
// Ensure no indexing error happens with prefix
|
// Ensure no indexing error happens with prefix
|
||||||
if len(completion) >= prefix {
|
if len(completion) >= prefix {
|
||||||
rl.insert([]rune(completion[prefix:]))
|
rl.viDeleteByAdjust(-prefix)
|
||||||
|
|
||||||
|
rl.insert([]rune(completion))
|
||||||
if !cur.TrimSlash && !cur.NoSpace {
|
if !cur.TrimSlash && !cur.NoSpace {
|
||||||
rl.insert([]rune(" "))
|
rl.insert([]rune(" "))
|
||||||
}
|
}
|
||||||
@ -86,7 +88,6 @@ func (rl *Instance) insertCandidate() {
|
|||||||
func (rl *Instance) updateVirtualComp() {
|
func (rl *Instance) updateVirtualComp() {
|
||||||
cur := rl.getCurrentGroup()
|
cur := rl.getCurrentGroup()
|
||||||
if cur != nil {
|
if cur != nil {
|
||||||
|
|
||||||
completion := cur.getCurrentCell(rl)
|
completion := cur.getCurrentCell(rl)
|
||||||
prefix := len(rl.tcPrefix)
|
prefix := len(rl.tcPrefix)
|
||||||
|
|
||||||
@ -99,6 +100,9 @@ func (rl *Instance) updateVirtualComp() {
|
|||||||
rl.viUndoSkipAppend = true
|
rl.viUndoSkipAppend = true
|
||||||
rl.resetTabCompletion()
|
rl.resetTabCompletion()
|
||||||
} else {
|
} else {
|
||||||
|
if strings.HasSuffix(string(rl.line), rl.tcPrefix) && (!rl.modeAutoFind || rl.searchMode != HistoryFind) {
|
||||||
|
rl.viDeleteByAdjust(-prefix)
|
||||||
|
}
|
||||||
|
|
||||||
// Special case for the only special escape, which
|
// Special case for the only special escape, which
|
||||||
// if not handled, will make us insert the first
|
// if not handled, will make us insert the first
|
||||||
@ -109,7 +113,12 @@ func (rl *Instance) updateVirtualComp() {
|
|||||||
|
|
||||||
// Or insert it virtually.
|
// Or insert it virtually.
|
||||||
if len(completion) >= prefix {
|
if len(completion) >= prefix {
|
||||||
rl.insertCandidateVirtual([]rune(completion[prefix:]))
|
comp := completion
|
||||||
|
if rl.modeAutoFind && rl.searchMode == HistoryFind {
|
||||||
|
comp = completion[prefix:]
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.insertCandidateVirtual([]rune(comp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +182,7 @@ func (rl *Instance) resetVirtualComp(drop bool) {
|
|||||||
completion = completion + " "
|
completion = completion + " "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rl.insertCandidateVirtual([]rune(completion[prefix:]))
|
rl.insertCandidateVirtual([]rune(completion))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset virtual
|
// Reset virtual
|
||||||
|
Loading…
x
Reference in New Issue
Block a user