Repository: rsteube/carapace (go)

Issue:
xonsh: use completion context
use completion context which improves comand line parsing/splitting
needs https://github.com/xonsh/xonsh/pull/4017

Pull request description:
xonsh: use completion context
fix #244 

Patch:
diff --git a/internal/xonsh/snippet.go b/internal/xonsh/snippet.go
index cab3bee..f149bde 100644
--- a/internal/xonsh/snippet.go
+++ b/internal/xonsh/snippet.go
@@ -11,65 +11,23 @@ import (
 // Snippet creates the xonsh completion script
 func Snippet(cmd *cobra.Command) string {
 	functionName := strings.Replace(cmd.Name(), "-", "__", -1)
-	return fmt.Sprintf(`def _%v_completer(prefix, line, begidx, endidx, ctx):
-    """carapace completer for %v"""
-    if not line.startswith('%v '):
-        return # not the expected command to complete
-
-    from shlex import split
-    from xonsh.completers.tools import RichCompletion
-  
-    words=""
-    wordsNonPosix=""
-    try:
-        words=split(line[0:endidx] + "_") # ensure last word is empty when ends with space
-        wordsNonPosix=split(line[0:endidx], posix=False)
-    except:
-        try:
-            words=split(line[0:endidx] + '"' + "_") # ensure last word is empty when ends with space
-            wordsNonPosix=split(line[0:endidx] + '"', posix=False)
-            wordsNonPosix[-1]= wordsNonPosix[-1][:-1]
-        except:
-            words=split(line[0:endidx] + "'" + "_") # ensure last word is empty when ends with space
-            wordsNonPosix=split(line[0:endidx] + "'", posix=False)
-            wordsNonPosix[-1]= wordsNonPosix[-1][:-1]
-    
-    words[-1]=words[-1][0:-1]
-    if len(words[-1]) != 0:
-        begidx = endidx
-        for word in reversed(wordsNonPosix):
-            begidx = begidx - len(word)
-            if line[begidx-1] == " ":
-                break
-    
-    for index, item in enumerate(wordsNonPosix):
-        if item.startswith("r'"):
-            words[index] = words[index][1:] # fix rawstring not handled by shlex
+	return fmt.Sprintf(`from xonsh.completers.tools import *
 
-    current=words[-1]
-    previous=words[-2]
-
-    result = {}
-
-    def _%v_callback():
+@contextual_command_completer
+def _%v_completer(context):
+    """carapace completer for %v"""
+    if context.completing_command('%v'):
         from json import loads
         from subprocess import Popen, PIPE
         from xonsh.completers.tools import RichCompletion
-        cb, _ = Popen(['%v', '_carapace', 'xonsh', '_', *words], stdout=PIPE, stderr=PIPE).communicate()
+        output, _ = Popen(['%v', '_carapace', 'xonsh', '_', *[a.value for a in context.args], context.prefix], stdout=PIPE, stderr=PIPE).communicate()
         try:
-          return {RichCompletion(c["Value"], display=c["Display"], description=c["Description"], prefix_len=0) for c in loads(cb)}
+            return {RichCompletion(c["Value"], display=c["Display"], description=c["Description"], prefix_len=len(context.raw_prefix)) for c in loads(output)}
         except:
-          return {}
-  
-    result = _%v_callback()
-
-    if len(result) == 0:
-        result = {RichCompletion(current, display=current, description='', prefix_len=0)}
+            return
 
-    result = set(map(lambda x: RichCompletion(x, display=x.display, description=x.description, prefix_len=endidx-begidx), result))
-    return result
 
 from xonsh.completers._aliases import _add_one_completer
 _add_one_completer('%v', _%v_completer, 'start')
-`, functionName, cmd.Name(), cmd.Name(), functionName, uid.Executable(), functionName, cmd.Name(), functionName)
+`, functionName, cmd.Name(), cmd.Name(), uid.Executable(), cmd.Name(), functionName)
 }


Test patch:
diff --git a/example/_test/invoke_xonsh b/example/_test/invoke_xonsh
index a32a225..250e151 100755
--- a/example/_test/invoke_xonsh
+++ b/example/_test/invoke_xonsh
@@ -9,7 +9,7 @@ spawn mkdir /tmp/carapace-fakehome
 spawn touch /tmp/carapace-fakehome/.xonshrc
 
 spawn xonsh -i --shell-type dumb
-send "exec(\$(example _carapace xonsh)); echo EXPECT_START; _example_completer('', '$CMDLINE' , 0, len('$CMDLINE'), ''); echo EXPECT_END"
+send "exec(\$(example _carapace xonsh)); from xonsh.parsers.completion_context import *; echo EXPECT_START;_example_completer(CompletionContextParser().parse('$CMDLINE', len('$CMDLINE'), None)); echo EXPECT_END"
 send "\r"
 expect -re "EXPECT_START\r\n(.*?)EXPECT_END"
 puts "$expect_out(1,string)"
diff --git a/example/cmd/_test/xonsh.py b/example/cmd/_test/xonsh.py
index fc99a1f..a5371b5 100644
--- a/example/cmd/_test/xonsh.py
+++ b/example/cmd/_test/xonsh.py
@@ -1,60 +1,18 @@
-def _example_completer(prefix, line, begidx, endidx, ctx):
-    """carapace completer for example"""
-    if not line.startswith('example '):
-        return # not the expected command to complete
-
-    from shlex import split
-    from xonsh.completers.tools import RichCompletion
-  
-    words=""
-    wordsNonPosix=""
-    try:
-        words=split(line[0:endidx] + "_") # ensure last word is empty when ends with space
-        wordsNonPosix=split(line[0:endidx], posix=False)
-    except:
-        try:
-            words=split(line[0:endidx] + '"' + "_") # ensure last word is empty when ends with space
-            wordsNonPosix=split(line[0:endidx] + '"', posix=False)
-            wordsNonPosix[-1]= wordsNonPosix[-1][:-1]
-        except:
-            words=split(line[0:endidx] + "'" + "_") # ensure last word is empty when ends with space
-            wordsNonPosix=split(line[0:endidx] + "'", posix=False)
-            wordsNonPosix[-1]= wordsNonPosix[-1][:-1]
-    
-    words[-1]=words[-1][0:-1]
-    if len(words[-1]) != 0:
-        begidx = endidx
-        for word in reversed(wordsNonPosix):
-            begidx = begidx - len(word)
-            if line[begidx-1] == " ":
-                break
-    
-    for index, item in enumerate(wordsNonPosix):
-        if item.startswith("r'"):
-            words[index] = words[index][1:] # fix rawstring not handled by shlex
+from xonsh.completers.tools import *
 
-    current=words[-1]
-    previous=words[-2]
-
-    result = {}
-
-    def _example_callback():
+@contextual_command_completer
+def _example_completer(context):
+    """carapace completer for example"""
+    if context.completing_command('example'):
         from json import loads
         from subprocess import Popen, PIPE
         from xonsh.completers.tools import RichCompletion
-        cb, _ = Popen(['example', '_carapace', 'xonsh', '_', *words], stdout=PIPE, stderr=PIPE).communicate()
+        output, _ = Popen(['example', '_carapace', 'xonsh', '_', *[a.value for a in context.args], context.prefix], stdout=PIPE, stderr=PIPE).communicate()
         try:
-          return {RichCompletion(c["Value"], display=c["Display"], description=c["Description"], prefix_len=0) for c in loads(cb)}
+            return {RichCompletion(c["Value"], display=c["Display"], description=c["Description"], prefix_len=len(context.raw_prefix)) for c in loads(output)}
         except:
-          return {}
-  
-    result = _example_callback()
-
-    if len(result) == 0:
-        result = {RichCompletion(current, display=current, description='', prefix_len=0)}
+            return
 
-    result = set(map(lambda x: RichCompletion(x, display=x.display, description=x.description, prefix_len=endidx-begidx), result))
-    return result
 
 from xonsh.completers._aliases import _add_one_completer
 _add_one_completer('example', _example_completer, 'start')
diff --git a/example/main_test.go b/example/main_test.go
index 67244dd..b120a1e 100644
--- a/example/main_test.go
+++ b/example/main_test.go
@@ -34,7 +34,10 @@ func doComplete(t *testing.T, shell string, cmdline string, contained ...string)
 			)
 			cmd = exec.Command("./_test/invoke_powershell", replacer.Replace(cmdline))
 		case "xonsh":
-			cmd = exec.Command("./_test/invoke_xonsh", cmdline)
+			replacer := strings.NewReplacer(
+				`\ `, `" "`, // TODO simple escape fix
+			)
+			cmd = exec.Command("./_test/invoke_xonsh", replacer.Replace(cmdline))
 		case "zsh":
 			cmd = exec.Command("./_test/invoke_zsh", cmdline)
 		}