From 8b547f2af039c45968b505a7dc947766b305ac9e Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Fri, 25 Nov 2022 16:56:35 -0400 Subject: [PATCH] feat: make tab completion work with spaces and escaped characters --- CHANGELOG.md | 3 +++ complete.go | 62 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f8b0d..32db1e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,9 @@ an error of missing format variable - Fix an error with sh syntax in aliases - Prompt now works with east asian characters (CJK) - 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 This is a pre-release version of Hilbish for testing. To see the changelog, diff --git a/complete.go b/complete.go index 76d65f7..7c5153f 100644 --- a/complete.go +++ b/complete.go @@ -11,15 +11,49 @@ import ( 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{} sb := &strings.Builder{} quoted := false - for _, r := range str { + for i, r := range str { if r == '"' { quoted = !quoted sb.WriteRune(r) + } else if r == ' ' && str[i - 1] == '\\' { + sb.WriteRune(r) } else if !quoted && r == ' ' { split = append(split, sb.String()) sb.Reset() @@ -39,7 +73,7 @@ func splitQuote(str string) []string { } func fileComplete(query, ctx string, fields []string) ([]string, string) { - q := splitQuote(ctx) + q := splitForFile(ctx) 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 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 @@ -102,6 +135,7 @@ func matchPath(query string) ([]string, string) { var entries []string var baseName string + query = escapeInvertReplaer.Replace(query) path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query))) if string(query) == "" { // filepath base below would give us "." @@ -129,25 +163,7 @@ func matchPath(query string) ([]string, string) { } func escapeFilename(fname string) string { - args := []string{ - "\"", "\\\"", - "'", "\\'", - "`", "\\`", - " ", "\\ ", - "(", "\\(", - ")", "\\)", - "[", "\\[", - "]", "\\]", - "$", "\\$", - "&", "\\&", - "*", "\\*", - ">", "\\>", - "<", "\\<", - "|", "\\|", - } - - r := strings.NewReplacer(args...) - return r.Replace(fname) + return escapeReplaer.Replace(fname) } func completionLoader(rtm *rt.Runtime) *rt.Table {