PageRenderTime 42ms CodeModel.GetById 17ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/idlelib/AutoComplete.py

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