~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Lib/idlelib/AutoComplete.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
ImportĀ upstreamĀ versionĀ 3.4~b1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 idlelib.configHandler import idleConf
 
11
 
 
12
# This string includes all chars that may be in an identifier
 
13
ID_CHARS = string.ascii_letters + string.digits + "_"
 
14
 
 
15
# These constants represent the two different types of completions
 
16
COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1)
 
17
 
 
18
from idlelib import AutoCompleteWindow
 
19
from idlelib.HyperParser import HyperParser
 
20
 
 
21
import __main__
 
22
 
 
23
SEPS = os.sep
 
24
if os.altsep:  # e.g. '/' on Windows...
 
25
    SEPS += os.altsep
 
26
 
 
27
class AutoComplete:
 
28
 
 
29
    menudefs = [
 
30
        ('edit', [
 
31
            ("Show Completions", "<<force-open-completions>>"),
 
32
        ])
 
33
    ]
 
34
 
 
35
    popupwait = idleConf.GetOption("extensions", "AutoComplete",
 
36
                                   "popupwait", type="int", default=0)
 
37
 
 
38
    def __init__(self, editwin=None):
 
39
        self.editwin = editwin
 
40
        if editwin is None:  # subprocess and test
 
41
            return
 
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 necessary, 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 in SEPS:
 
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 necessary,
 
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
            # Find the beginning of the string
 
125
            # fetch_completions will look at the file system to determine whether the
 
126
            # string value constitutes an actual file name
 
127
            # XXX could consider raw strings here and unescape the string value if it's
 
128
            # not raw.
 
129
            self._remove_autocomplete_window()
 
130
            mode = COMPLETE_FILES
 
131
            # Find last separator or string start
 
132
            while i and curline[i-1] not in "'\"" + SEPS:
 
133
                i -= 1
 
134
            comp_start = curline[i:j]
 
135
            j = i
 
136
            # Find string start
 
137
            while i and curline[i-1] not in "'\"":
 
138
                i -= 1
 
139
            comp_what = curline[i:j]
 
140
        elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES):
 
141
            self._remove_autocomplete_window()
 
142
            mode = COMPLETE_ATTRIBUTES
 
143
            while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127):
 
144
                i -= 1
 
145
            comp_start = curline[i:j]
 
146
            if i and curline[i-1] == '.':
 
147
                hp.set_index("insert-%dc" % (len(curline)-(i-1)))
 
148
                comp_what = hp.get_expression()
 
149
                if not comp_what or \
 
150
                   (not evalfuncs and comp_what.find('(') != -1):
 
151
                    return
 
152
            else:
 
153
                comp_what = ""
 
154
        else:
 
155
            return
 
156
 
 
157
        if complete and not comp_what and not comp_start:
 
158
            return
 
159
        comp_lists = self.fetch_completions(comp_what, mode)
 
160
        if not comp_lists[0]:
 
161
            return
 
162
        self.autocompletewindow = self._make_autocomplete_window()
 
163
        return not self.autocompletewindow.show_window(
 
164
                comp_lists, "insert-%dc" % len(comp_start),
 
165
                complete, mode, userWantsWin)
 
166
 
 
167
    def fetch_completions(self, what, mode):
 
168
        """Return a pair of lists of completions for something. The first list
 
169
        is a sublist of the second. Both are sorted.
 
170
 
 
171
        If there is a Python subprocess, get the comp. list there.  Otherwise,
 
172
        either fetch_completions() is running in the subprocess itself or it
 
173
        was called in an IDLE EditorWindow before any script had been run.
 
174
 
 
175
        The subprocess environment is that of the most recently run script.  If
 
176
        two unrelated modules are being edited some calltips in the current
 
177
        module may be inoperative if the module was not the last to run.
 
178
        """
 
179
        try:
 
180
            rpcclt = self.editwin.flist.pyshell.interp.rpcclt
 
181
        except:
 
182
            rpcclt = None
 
183
        if rpcclt:
 
184
            return rpcclt.remotecall("exec", "get_the_completion_list",
 
185
                                     (what, mode), {})
 
186
        else:
 
187
            if mode == COMPLETE_ATTRIBUTES:
 
188
                if what == "":
 
189
                    namespace = __main__.__dict__.copy()
 
190
                    namespace.update(__main__.__builtins__.__dict__)
 
191
                    bigl = eval("dir()", namespace)
 
192
                    bigl.sort()
 
193
                    if "__all__" in bigl:
 
194
                        smalll = sorted(eval("__all__", namespace))
 
195
                    else:
 
196
                        smalll = [s for s in bigl if s[:1] != '_']
 
197
                else:
 
198
                    try:
 
199
                        entity = self.get_entity(what)
 
200
                        bigl = dir(entity)
 
201
                        bigl.sort()
 
202
                        if "__all__" in bigl:
 
203
                            smalll = sorted(entity.__all__)
 
204
                        else:
 
205
                            smalll = [s for s in bigl if s[:1] != '_']
 
206
                    except:
 
207
                        return [], []
 
208
 
 
209
            elif mode == COMPLETE_FILES:
 
210
                if what == "":
 
211
                    what = "."
 
212
                try:
 
213
                    expandedpath = os.path.expanduser(what)
 
214
                    bigl = os.listdir(expandedpath)
 
215
                    bigl.sort()
 
216
                    smalll = [s for s in bigl if s[:1] != '.']
 
217
                except OSError:
 
218
                    return [], []
 
219
 
 
220
            if not smalll:
 
221
                smalll = bigl
 
222
            return smalll, bigl
 
223
 
 
224
    def get_entity(self, name):
 
225
        """Lookup name in a namespace spanning sys.modules and __main.dict__"""
 
226
        namespace = sys.modules.copy()
 
227
        namespace.update(__main__.__dict__)
 
228
        return eval(name, namespace)