Ticket #19700: patch-Lib-rlcompleter.py.diff

File patch-Lib-rlcompleter.py.diff, 5.1 KB (added by stromnov (Andrey Stromnov), 15 years ago)
  • Lib/rlcompleter.py

    old new  
    11"""Word completion for GNU readline 2.0.
    22
    33This requires the latest extension to the readline module. The completer
    4 completes keywords, built-ins and globals in a selectable namespace (which
    5 defaults to __main__); when completing NAME.NAME..., it evaluates (!) the
    6 expression up to the last dot and completes its attributes.
     4completes keywords, built-ins, globals, and file names in a selectable
     5namespace (which defaults to __main__); when completing NAME.NAME...,
     6it evaluates (!) the expression up to the last dot and completes its
     7attributes.
    78
    89It's very cool to do "import sys" type "sys.", hit the
    910completion key (twice), and see the list of names defined by the
     
    7273            self.use_main_ns = 0
    7374            self.namespace = namespace
    7475
     76        # The cache of matches for a particular text fragment.
     77        self.matches = []
     78
    7579    def complete(self, text, state):
    7680        """Return the next possible completion for 'text'.
    7781
    7882        This is called successively with state == 0, 1, 2, ... until it
    79         returns None.  The completion should begin with 'text'.
     83        returns None.  The completion should begin with 'text'.  Any text
     84        with a period (.) will match as an attribute.  Any text that begins
     85        with a single or double quote will match using file name expansion.
    8086
    8187        """
    8288        if self.use_main_ns:
    8389            self.namespace = __main__.__dict__
    8490
     91        # For the first call with this set of text, compute all possible
     92        # matches and store them in a member variable.  Subsequent calls
     93        # will then iterate through this set of matches.
    8594        if state == 0:
    86             if "." in text:
     95            if ('"' in text) or ("'" in text):
     96                self.matches = self.file_matches(text)
     97            elif "." in text:
    8798                self.matches = self.attr_matches(text)
    8899            else:
    89100                self.matches = self.global_matches(text)
    90         try:
     101        if state < len(self.matches):
    91102            return self.matches[state]
    92         except IndexError:
     103        else:
    93104            return None
    94105
    95106    def _callable_postfix(self, val, word):
     
    129140
    130141        """
    131142        import re
     143        import types
     144
     145        # Setup the regular expression for attributes
    132146        m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
    133147        if not m:
    134148            return []
     149
     150        # Group 1 is the class name, group 3 is the attribute text
    135151        expr, attr = m.group(1, 3)
    136152        try:
    137153            thisobject = eval(expr, self.namespace)
     
    143159        if "__builtins__" in words:
    144160            words.remove("__builtins__")
    145161
    146         if hasattr(thisobject, '__class__'):
    147             words.append('__class__')
    148             words.extend(get_class_members(thisobject.__class__))
     162        # If this type is a class instance, use the __class__ member to
     163        # get the dictionary of attributes
     164        if type(thisobject) == types.InstanceType:
     165            if hasattr(thisobject, '__class__'):
     166                words.append('__class__')
     167                words.extend(get_class_members(thisobject.__class__))
     168        elif type(thisobject) == types.ClassType:
     169            words.extend(get_class_members(thisobject))
     170        else:
     171            words.extend(dir(thisobject))
     172
     173        # Build the full matching text from class.attribute matches
    149174        matches = []
    150175        n = len(attr)
    151176        for word in words:
     
    155180                matches.append(word)
    156181        return matches
    157182
     183    def file_matches(self, text):
     184        """Compute matches when text is a file name.
     185
     186        Expects a leading single or double quote character in the text.
     187        Will expand a leading ~ or ~user to a valid home directory.
     188        Will expand a leading $VAR to an environment variable name."""
     189        import glob
     190        import os
     191
     192        # save the leading quote character so we can re-add it later
     193        quote = text[0]
     194        # strip the leading quote character
     195        path = text[1:]
     196
     197        # expand a tilde (~) or a leading environment variable in the text
     198        path = os.path.expanduser( path )
     199        path = os.path.expandvars( path )
     200
     201        # append the any match character to send to the glob routine
     202        path = path + "*"
     203
     204        # use the glob module to get all of the matches
     205        rawMatches = glob.glob( path )
     206
     207        # re-prefix the text with the quoting character and append the correct
     208        # terminating character depending on the type of match that was found.
     209        # Directories are terminated with '/' and files with an ending quote.
     210        matches = []
     211        for entry in rawMatches:
     212            if os.path.isdir( entry ):
     213                matches.append( quote + entry + os.sep )
     214            elif os.path.isfile( entry ):
     215                matches.append( quote + entry + quote )
     216            else:
     217                matches.append( quote + entry )
     218        return matches
     219
    158220def get_class_members(klass):
    159221    ret = dir(klass)
    160222    if hasattr(klass,'__bases__'):