Improved shell completer for local variables and behavior of Python versions equaled.

Mon, 22 Feb 2016 22:26:21 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Mon, 22 Feb 2016 22:26:21 +0100
changeset 4833
803bf753032a
parent 4828
b313794f46a2
child 4834
3605138fa29d

Improved shell completer for local variables and behavior of Python versions equaled.

DebugClients/Python/DebugClientBase.py file | annotate | diff | comparison | revisions
DebugClients/Python/FlexCompleter.py file | annotate | diff | comparison | revisions
DebugClients/Python3/DebugClientBase.py file | annotate | diff | comparison | revisions
--- a/DebugClients/Python/DebugClientBase.py	Wed Feb 17 22:11:12 2016 +0100
+++ b/DebugClients/Python/DebugClientBase.py	Mon Feb 22 22:26:21 2016 +0100
@@ -1887,8 +1887,7 @@
         """
         completerDelims = ' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?'
         
-        completions = []
-        state = 0
+        completions = set()
         # find position of last delim character
         pos = -1
         while pos >= -len(text):
@@ -1900,20 +1899,38 @@
                 break
             pos -= 1
         
+        # Get local and global completions
         try:
-            comp = self.complete(text, state)
+            localdict = self.currentThread.getFrameLocals(self.framenr)
+            localCompleter = Completer(localdict).complete
+            self.__getCompletionList(text, localCompleter, completions)
+        except AttributeError:
+            pass
+        self.__getCompletionList(text, self.complete, completions)
+        
+        self.write("%s%s||%s\n" % (DebugProtocol.ResponseCompletion,
+                                   unicode(list(completions)), text))
+
+    def __getCompletionList(self, text, completer, completions):
+        """
+        Private method to create a completions list.
+        
+        @param text text to complete (string)
+        @param completer completer methode
+        @param completions set where to add new completions strings (set)
+        """
+        state = 0
+        try:
+            comp = completer(text, state)
         except Exception:
             comp = None
         while comp is not None:
-            completions.append(comp)
+            completions.add(comp)
             state += 1
             try:
-                comp = self.complete(text, state)
+                comp = completer(text, state)
             except Exception:
                 comp = None
-            
-        self.write("%s%s||%s\n" % (DebugProtocol.ResponseCompletion,
-                                   unicode(completions), text))
 
     def startDebugger(self, filename=None, host=None, port=None,
                       enableTrace=1, exceptions=1, tracePython=0, redirect=1):
--- a/DebugClients/Python/FlexCompleter.py	Wed Feb 17 22:11:12 2016 +0100
+++ b/DebugClients/Python/FlexCompleter.py	Mon Feb 22 22:26:21 2016 +0100
@@ -162,6 +162,18 @@
         except IndexError:
             return None
 
+    def _callable_postfix(self, val, word):
+        """
+        Protected method to check for a callable.
+        
+        @param val value to check (object)
+        @param word word to ammend (string)
+        @return ammended word (string)
+        """
+        if hasattr(val, '__call__'):
+            word = word + "("
+        return word
+
     def global_matches(self, text):
         """
         Public method to compute matches when text is a simple name.
@@ -173,14 +185,13 @@
         import keyword
         matches = []
         n = len(text)
-        for list in [keyword.kwlist,
-                     __builtin__.__dict__.keys(),
-                     self.namespace.keys()]:
-            for word in list:
-                if word[:n] == text and \
-                   word != "__builtins__" and \
-                   word not in matches:
-                    matches.append(word)
+        for word in keyword.kwlist:
+            if word[:n] == text:
+                matches.append(word)
+        for nspace in [__builtin__.__dict__, self.namespace]:
+            for word, val in nspace.items():
+                if word[:n] == text and word != "__builtins__":
+                    matches.append(self._callable_postfix(val, word))
         return matches
 
     def attr_matches(self, text):
@@ -212,8 +223,16 @@
         if not m:
             return
         expr, attr = m.group(1, 3)
-        object = eval(expr, self.namespace)
-        words = dir(object)
+        try:
+            thisobject = eval(expr, self.namespace)
+        except Exception:
+            return []
+
+        # get the content of the object, except __builtins__
+        words = dir(thisobject)
+        if "__builtins__" in words:
+            words.remove("__builtins__")
+
         if hasattr(object, '__class__'):
             words.append('__class__')
             words = words + get_class_members(object.__class__)
@@ -221,10 +240,11 @@
         n = len(attr)
         for word in words:
             try:
-                if word[:n] == attr and word != "__builtins__":
-                    match = "%s.%s" % (expr, word)
-                    if match not in matches:
-                        matches.append(match)
+                if word[:n] == attr and hasattr(thisobject, word):
+                    val = getattr(thisobject, word)
+                    word = self._callable_postfix(
+                        val, "%s.%s" % (expr, word))
+                    matches.append(word)
             except Exception:
                 # some badly behaved objects pollute dir() with non-strings,
                 # which cause the completion to fail.  This way we skip the
--- a/DebugClients/Python3/DebugClientBase.py	Wed Feb 17 22:11:12 2016 +0100
+++ b/DebugClients/Python3/DebugClientBase.py	Mon Feb 22 22:26:21 2016 +0100
@@ -1937,8 +1937,7 @@
         """
         completerDelims = ' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?'
         
-        completions = []
-        state = 0
+        completions = set()
         # find position of last delim character
         pos = -1
         while pos >= -len(text):
@@ -1950,20 +1949,38 @@
                 break
             pos -= 1
         
+        # Get local and global completions
         try:
-            comp = self.complete(text, state)
+            localdict = self.currentThread.getFrameLocals(self.framenr)
+            localCompleter = Completer(localdict).complete
+            self.__getCompletionList(text, localCompleter, completions)
+        except AttributeError:
+            pass
+        self.__getCompletionList(text, self.complete, completions)
+        
+        self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion,
+                                          str(list(completions)), text))
+
+    def __getCompletionList(self, text, completer, completions):
+        """
+        Private method to create a completions list.
+        
+        @param text text to complete (string)
+        @param completer completer methode
+        @param completions set where to add new completions strings (set)
+        """
+        state = 0
+        try:
+            comp = completer(text, state)
         except Exception:
             comp = None
         while comp is not None:
-            completions.append(comp)
+            completions.add(comp)
             state += 1
             try:
-                comp = self.complete(text, state)
+                comp = completer(text, state)
             except Exception:
                 comp = None
-        
-        self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion,
-                                          str(completions), text))
 
     def startDebugger(self, filename=None, host=None, port=None,
                       enableTrace=True, exceptions=True, tracePython=False,

eric ide

mercurial