python-2.5.2/win32/Lib/idlelib/AutoComplete.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """AutoComplete.py - An IDLE extension for automatically completing names.
       
     2 
       
     3 This extension can complete either attribute names of file names. It can pop
       
     4 a window with all available names, for the user to select from.
       
     5 """
       
     6 import os
       
     7 import sys
       
     8 import string
       
     9 
       
    10 from configHandler import idleConf
       
    11 
       
    12 import AutoCompleteWindow
       
    13 from HyperParser import HyperParser
       
    14 
       
    15 import __main__
       
    16 
       
    17 # This string includes all chars that may be in a file name (without a path
       
    18 # separator)
       
    19 FILENAME_CHARS = string.ascii_letters + string.digits + os.curdir + "._~#$:-"
       
    20 # This string includes all chars that may be in an identifier
       
    21 ID_CHARS = string.ascii_letters + string.digits + "_"
       
    22 
       
    23 # These constants represent the two different types of completions
       
    24 COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1)
       
    25 
       
    26 class AutoComplete:
       
    27 
       
    28     menudefs = [
       
    29         ('edit', [
       
    30             ("Show Completions", "<<force-open-completions>>"),
       
    31         ])
       
    32     ]
       
    33 
       
    34     popupwait = idleConf.GetOption("extensions", "AutoComplete",
       
    35                                    "popupwait", type="int", default=0)
       
    36 
       
    37     def __init__(self, editwin=None):
       
    38         if editwin == None:  # subprocess and test
       
    39             self.editwin = None
       
    40             return
       
    41         self.editwin = editwin
       
    42         self.text = editwin.text
       
    43         self.autocompletewindow = None
       
    44 
       
    45         # id of delayed call, and the index of the text insert when the delayed
       
    46         # call was issued. If _delayed_completion_id is None, there is no
       
    47         # delayed call.
       
    48         self._delayed_completion_id = None
       
    49         self._delayed_completion_index = None
       
    50 
       
    51     def _make_autocomplete_window(self):
       
    52         return AutoCompleteWindow.AutoCompleteWindow(self.text)
       
    53 
       
    54     def _remove_autocomplete_window(self, event=None):
       
    55         if self.autocompletewindow:
       
    56             self.autocompletewindow.hide_window()
       
    57             self.autocompletewindow = None
       
    58 
       
    59     def force_open_completions_event(self, event):
       
    60         """Happens when the user really wants to open a completion list, even
       
    61         if a function call is needed.
       
    62         """
       
    63         self.open_completions(True, False, True)
       
    64 
       
    65     def try_open_completions_event(self, event):
       
    66         """Happens when it would be nice to open a completion list, but not
       
    67         really neccesary, for example after an dot, so function
       
    68         calls won't be made.
       
    69         """
       
    70         lastchar = self.text.get("insert-1c")
       
    71         if lastchar == ".":
       
    72             self._open_completions_later(False, False, False,
       
    73                                          COMPLETE_ATTRIBUTES)
       
    74         elif lastchar == os.sep:
       
    75             self._open_completions_later(False, False, False,
       
    76                                          COMPLETE_FILES)
       
    77 
       
    78     def autocomplete_event(self, event):
       
    79         """Happens when the user wants to complete his word, and if neccesary,
       
    80         open a completion list after that (if there is more than one
       
    81         completion)
       
    82         """
       
    83         if hasattr(event, "mc_state") and event.mc_state:
       
    84             # A modifier was pressed along with the tab, continue as usual.
       
    85             return
       
    86         if self.autocompletewindow and self.autocompletewindow.is_active():
       
    87             self.autocompletewindow.complete()
       
    88             return "break"
       
    89         else:
       
    90             opened = self.open_completions(False, True, True)
       
    91             if opened:
       
    92                 return "break"
       
    93 
       
    94     def _open_completions_later(self, *args):
       
    95         self._delayed_completion_index = self.text.index("insert")
       
    96         if self._delayed_completion_id is not None:
       
    97             self.text.after_cancel(self._delayed_completion_id)
       
    98         self._delayed_completion_id = \
       
    99             self.text.after(self.popupwait, self._delayed_open_completions,
       
   100                             *args)
       
   101 
       
   102     def _delayed_open_completions(self, *args):
       
   103         self._delayed_completion_id = None
       
   104         if self.text.index("insert") != self._delayed_completion_index:
       
   105             return
       
   106         self.open_completions(*args)
       
   107 
       
   108     def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
       
   109         """Find the completions and create the AutoCompleteWindow.
       
   110         Return True if successful (no syntax error or so found).
       
   111         if complete is True, then if there's nothing to complete and no
       
   112         start of completion, won't open completions and return False.
       
   113         If mode is given, will open a completion list only in this mode.
       
   114         """
       
   115         # Cancel another delayed call, if it exists.
       
   116         if self._delayed_completion_id is not None:
       
   117             self.text.after_cancel(self._delayed_completion_id)
       
   118             self._delayed_completion_id = None
       
   119 
       
   120         hp = HyperParser(self.editwin, "insert")
       
   121         curline = self.text.get("insert linestart", "insert")
       
   122         i = j = len(curline)
       
   123         if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
       
   124             self._remove_autocomplete_window()
       
   125             mode = COMPLETE_FILES
       
   126             while i and curline[i-1] in FILENAME_CHARS:
       
   127                 i -= 1
       
   128             comp_start = curline[i:j]
       
   129             j = i
       
   130             while i and curline[i-1] in FILENAME_CHARS+os.sep:
       
   131                 i -= 1
       
   132             comp_what = curline[i:j]
       
   133         elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES):
       
   134             self._remove_autocomplete_window()
       
   135             mode = COMPLETE_ATTRIBUTES
       
   136             while i and curline[i-1] in ID_CHARS:
       
   137                 i -= 1
       
   138             comp_start = curline[i:j]
       
   139             if i and curline[i-1] == '.':
       
   140                 hp.set_index("insert-%dc" % (len(curline)-(i-1)))
       
   141                 comp_what = hp.get_expression()
       
   142                 if not comp_what or \
       
   143                    (not evalfuncs and comp_what.find('(') != -1):
       
   144                     return
       
   145             else:
       
   146                 comp_what = ""
       
   147         else:
       
   148             return
       
   149 
       
   150         if complete and not comp_what and not comp_start:
       
   151             return
       
   152         comp_lists = self.fetch_completions(comp_what, mode)
       
   153         if not comp_lists[0]:
       
   154             return
       
   155         self.autocompletewindow = self._make_autocomplete_window()
       
   156         self.autocompletewindow.show_window(comp_lists,
       
   157                                             "insert-%dc" % len(comp_start),
       
   158                                             complete,
       
   159                                             mode,
       
   160                                             userWantsWin)
       
   161         return True
       
   162 
       
   163     def fetch_completions(self, what, mode):
       
   164         """Return a pair of lists of completions for something. The first list
       
   165         is a sublist of the second. Both are sorted.
       
   166 
       
   167         If there is a Python subprocess, get the comp. list there.  Otherwise,
       
   168         either fetch_completions() is running in the subprocess itself or it
       
   169         was called in an IDLE EditorWindow before any script had been run.
       
   170 
       
   171         The subprocess environment is that of the most recently run script.  If
       
   172         two unrelated modules are being edited some calltips in the current
       
   173         module may be inoperative if the module was not the last to run.
       
   174         """
       
   175         try:
       
   176             rpcclt = self.editwin.flist.pyshell.interp.rpcclt
       
   177         except:
       
   178             rpcclt = None
       
   179         if rpcclt:
       
   180             return rpcclt.remotecall("exec", "get_the_completion_list",
       
   181                                      (what, mode), {})
       
   182         else:
       
   183             if mode == COMPLETE_ATTRIBUTES:
       
   184                 if what == "":
       
   185                     namespace = __main__.__dict__.copy()
       
   186                     namespace.update(__main__.__builtins__.__dict__)
       
   187                     bigl = eval("dir()", namespace)
       
   188                     bigl.sort()
       
   189                     if "__all__" in bigl:
       
   190                         smalll = eval("__all__", namespace)
       
   191                         smalll.sort()
       
   192                     else:
       
   193                         smalll = filter(lambda s: s[:1] != '_', bigl)
       
   194                 else:
       
   195                     try:
       
   196                         entity = self.get_entity(what)
       
   197                         bigl = dir(entity)
       
   198                         bigl.sort()
       
   199                         if "__all__" in bigl:
       
   200                             smalll = entity.__all__
       
   201                             smalll.sort()
       
   202                         else:
       
   203                             smalll = filter(lambda s: s[:1] != '_', bigl)
       
   204                     except:
       
   205                         return [], []
       
   206 
       
   207             elif mode == COMPLETE_FILES:
       
   208                 if what == "":
       
   209                     what = "."
       
   210                 try:
       
   211                     expandedpath = os.path.expanduser(what)
       
   212                     bigl = os.listdir(expandedpath)
       
   213                     bigl.sort()
       
   214                     smalll = filter(lambda s: s[:1] != '.', bigl)
       
   215                 except OSError:
       
   216                     return [], []
       
   217 
       
   218             if not smalll:
       
   219                 smalll = bigl
       
   220             return smalll, bigl
       
   221 
       
   222     def get_entity(self, name):
       
   223         """Lookup name in a namespace spanning sys.modules and __main.dict__"""
       
   224         namespace = sys.modules.copy()
       
   225         namespace.update(__main__.__dict__)
       
   226         return eval(name, namespace)