2
2
mirror of https://github.com/Hilbis/Hilbish synced 2025-07-11 05:22:02 +00:00
Hilbish/complete.go
TorchedSammy 0ed365170c
refactor!: completion api, add hilbish.completion interface
this is a pretty big commit which mainly contains a refactor
and breaking change to how command completions are done.

before that, a hilbish.completion interface has been added
which for now just has 2 functions (`files` and `bins`)
for completions of normal files and executables.

hilbish.complete is now expected to return a table of
"completions groups," which are as the name suggests a group
for a completion. a completion group is a table which has
the fields `type`, which can be either `list` or `grid`,
and `items`, being an array (or string keyed table) of items

if an item is string keyed the item itself is the key name
and the value is a table with the first value in it being the
description for the item. this description is only applied
with the list type.

this is probably the longest commit message ive written
2022-03-05 15:59:00 -04:00

109 lines
2.6 KiB
Go

package main
import (
"path/filepath"
"strings"
"os"
)
func fileComplete(query, ctx string, fields []string) []string {
var completions []string
prefixes := []string{"./", "../", "/", "~/"}
for _, prefix := range prefixes {
if strings.HasPrefix(query, prefix) {
completions, _ = matchPath(strings.Replace(query, "~", curuser.HomeDir, 1), query)
}
}
if len(completions) == 0 && len(fields) > 1 {
completions, _ = matchPath("./" + query, query)
}
return completions
}
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
var completions []string
prefixes := []string{"./", "../", "/", "~/"}
for _, prefix := range prefixes {
if strings.HasPrefix(query, prefix) {
fileCompletions := fileComplete(query, ctx, fields)
if len(fileCompletions) != 0 {
for _, f := range fileCompletions {
name := strings.Replace(query + f, "~", curuser.HomeDir, 1)
if info, err := os.Stat(name); err == nil && info.Mode().Perm() & 0100 == 0 {
continue
}
completions = append(completions, f)
}
}
return completions, ""
}
}
// filter out executables, but in path
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
// print dir to stderr for debugging
// search for an executable which matches our query string
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
// get basename from matches
for _, match := range matches {
// check if we have execute permissions for our match
if info, err := os.Stat(match); err == nil && info.Mode().Perm() & 0100 == 0 {
continue
}
// get basename from match
name := filepath.Base(match)
// add basename to completions
completions = append(completions, name)
}
}
}
// add lua registered commands to completions
for cmdName := range commands {
if strings.HasPrefix(cmdName, query) {
completions = append(completions, cmdName)
}
}
return completions, query
}
func matchPath(path, pref string) ([]string, error) {
var entries []string
matches, err := filepath.Glob(path + "*")
if err == nil {
args := []string{
"\"", "\\\"",
"'", "\\'",
"`", "\\`",
" ", "\\ ",
"(", "\\(",
")", "\\)",
"[", "\\[",
"]", "\\]",
}
r := strings.NewReplacer(args...)
for _, match := range matches {
name := filepath.Base(match)
p := filepath.Base(pref)
if pref == "" {
p = ""
}
name = strings.TrimPrefix(name, p)
matchFull, _ := filepath.Abs(match)
if info, err := os.Stat(matchFull); err == nil && info.IsDir() {
name = name + string(os.PathSeparator)
}
name = r.Replace(name)
entries = append(entries, name)
}
}
return entries, err
}