feat: make tab completion work with spaces and escaped characters

pull/217/head
TorchedSammy 2022-11-25 16:56:35 -04:00
parent 1febe66f84
commit 8b547f2af0
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
2 changed files with 42 additions and 23 deletions

View File

@ -151,6 +151,9 @@ an error of missing format variable
- Fix an error with sh syntax in aliases - Fix an error with sh syntax in aliases
- Prompt now works with east asian characters (CJK) - Prompt now works with east asian characters (CJK)
- Set back the prompt to normal after exiting the continue prompt with ctrl-d - Set back the prompt to normal after exiting the continue prompt with ctrl-d
- Users can now tab complete files with spaces while quoted or with escaped spaces.
This means a query of `Files\ to\ ` with file names of `Files to tab complete` and `Files to complete`
will result in the files being completed.
## [2.0.0-rc1] - 2022-09-14 ## [2.0.0-rc1] - 2022-09-14
This is a pre-release version of Hilbish for testing. To see the changelog, This is a pre-release version of Hilbish for testing. To see the changelog,

View File

@ -11,15 +11,49 @@ import (
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
func splitQuote(str string) []string { var charEscapeMap = []string{
"\"", "\\\"",
"'", "\\'",
"`", "\\`",
" ", "\\ ",
"(", "\\(",
")", "\\)",
"[", "\\[",
"]", "\\]",
"$", "\\$",
"&", "\\&",
"*", "\\*",
">", "\\>",
"<", "\\<",
"|", "\\|",
}
var charEscapeMapInvert = invert(charEscapeMap)
var escapeReplaer = strings.NewReplacer(charEscapeMap...)
var escapeInvertReplaer = strings.NewReplacer(charEscapeMapInvert...)
func invert(m []string) []string {
newM := make([]string, len(charEscapeMap))
for i := range m {
if (i + 1) % 2 == 0 {
newM[i] = m[i - 1]
newM[i - 1] = m[i]
}
}
return newM
}
func splitForFile(str string) []string {
split := []string{} split := []string{}
sb := &strings.Builder{} sb := &strings.Builder{}
quoted := false quoted := false
for _, r := range str { for i, r := range str {
if r == '"' { if r == '"' {
quoted = !quoted quoted = !quoted
sb.WriteRune(r) sb.WriteRune(r)
} else if r == ' ' && str[i - 1] == '\\' {
sb.WriteRune(r)
} else if !quoted && r == ' ' { } else if !quoted && r == ' ' {
split = append(split, sb.String()) split = append(split, sb.String())
sb.Reset() sb.Reset()
@ -39,7 +73,7 @@ func splitQuote(str string) []string {
} }
func fileComplete(query, ctx string, fields []string) ([]string, string) { func fileComplete(query, ctx string, fields []string) ([]string, string) {
q := splitQuote(ctx) q := splitForFile(ctx)
return matchPath(q[len(q) - 1]) return matchPath(q[len(q) - 1])
} }
@ -66,7 +100,6 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
// filter out executables, but in path // filter out executables, but in path
for _, dir := range filepath.SplitList(os.Getenv("PATH")) { for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
// print dir to stderr for debugging
// search for an executable which matches our query string // search for an executable which matches our query string
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil { if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
// get basename from matches // get basename from matches
@ -102,6 +135,7 @@ func matchPath(query string) ([]string, string) {
var entries []string var entries []string
var baseName string var baseName string
query = escapeInvertReplaer.Replace(query)
path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query))) path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
if string(query) == "" { if string(query) == "" {
// filepath base below would give us "." // filepath base below would give us "."
@ -129,25 +163,7 @@ func matchPath(query string) ([]string, string) {
} }
func escapeFilename(fname string) string { func escapeFilename(fname string) string {
args := []string{ return escapeReplaer.Replace(fname)
"\"", "\\\"",
"'", "\\'",
"`", "\\`",
" ", "\\ ",
"(", "\\(",
")", "\\)",
"[", "\\[",
"]", "\\]",
"$", "\\$",
"&", "\\&",
"*", "\\*",
">", "\\>",
"<", "\\<",
"|", "\\|",
}
r := strings.NewReplacer(args...)
return r.Replace(fname)
} }
func completionLoader(rtm *rt.Runtime) *rt.Table { func completionLoader(rtm *rt.Runtime) *rt.Table {