eric6/DebugClients/Python/FlexCompleter.py

changeset 6942
2602857055c5
parent 6891
93f82da09f22
child 7250
d8bdc55aee1a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 """
4 Word completion for the eric6 shell.
5
6 <h4>NOTE for eric6 variant</h4>
7
8 This version is a re-implementation of rlcompleter
9 as found in the Python3 library. It is modified to work with the eric6
10 debug clients.
11
12 <h4>Original rlcompleter documentation</h4>
13
14 This requires the latest extension to the readline module. The completer
15 completes keywords, built-ins and globals in a selectable namespace (which
16 defaults to __main__); when completing NAME.NAME..., it evaluates (!) the
17 expression up to the last dot and completes its attributes.
18
19 It's very cool to do "import sys" type "sys.", hit the
20 completion key (twice), and see the list of names defined by the
21 sys module!
22
23 Tip: to use the tab key as the completion key, call
24
25 readline.parse_and_bind("tab: complete")
26
27 <b>Notes</b>:
28 <ul>
29 <li>
30 Exceptions raised by the completer function are *ignored* (and
31 generally cause the completion to fail). This is a feature -- since
32 readline sets the tty device in raw (or cbreak) mode, printing a
33 traceback wouldn't work well without some complicated hoopla to save,
34 reset and restore the tty state.
35 </li>
36 <li>
37 The evaluation of the NAME.NAME... form may cause arbitrary
38 application defined code to be executed if an object with a
39 __getattr__ hook is found. Since it is the responsibility of the
40 application (or the user) to enable this feature, I consider this an
41 acceptable risk. More complicated expressions (e.g. function calls or
42 indexing operations) are *not* evaluated.
43 </li>
44 <li>
45 When the original stdin is not a tty device, GNU readline is never
46 used, and this module (and the readline module) are silently inactive.
47 </li>
48 </ul>
49 """
50
51 try:
52 import __builtin__ as builtins
53 except ImportError:
54 import builtins
55
56 import __main__
57
58 __all__ = ["Completer"]
59
60
61 class Completer(object):
62 """
63 Class implementing the command line completer object.
64 """
65 def __init__(self, namespace=None):
66 """
67 Constructor
68
69 Completer([namespace]) -> completer instance.
70
71 If unspecified, the default namespace where completions are performed
72 is __main__ (technically, __main__.__dict__). Namespaces should be
73 given as dictionaries.
74
75 Completer instances should be used as the completion mechanism of
76 readline via the set_completer() call:
77
78 readline.set_completer(Completer(my_namespace).complete)
79
80 @param namespace The namespace for the completer.
81 @exception TypeError raised to indicate a wrong data structure of
82 the namespace object
83 """
84 if namespace and not isinstance(namespace, dict):
85 raise TypeError('namespace must be a dictionary')
86
87 # Don't bind to namespace quite yet, but flag whether the user wants a
88 # specific namespace or to use __main__.__dict__. This will allow us
89 # to bind to __main__.__dict__ at completion time, not now.
90 if namespace is None:
91 self.use_main_ns = True
92 else:
93 self.use_main_ns = False
94 self.namespace = namespace
95
96 def complete(self, text, state):
97 """
98 Public method to return the next possible completion for 'text'.
99
100 This is called successively with state == 0, 1, 2, ... until it
101 returns None. The completion should begin with 'text'.
102
103 @param text The text to be completed. (string)
104 @param state The state of the completion. (integer)
105 @return The possible completions as a list of strings.
106 """
107 if self.use_main_ns:
108 self.namespace = __main__.__dict__
109
110 if state == 0:
111 if "." in text:
112 self.matches = self.attr_matches(text)
113 else:
114 self.matches = self.global_matches(text)
115 try:
116 return self.matches[state]
117 except IndexError:
118 return None
119
120 def _callable_postfix(self, val, word):
121 """
122 Protected method to check for a callable.
123
124 @param val value to check (object)
125 @param word word to ammend (string)
126 @return ammended word (string)
127 """
128 if callable(val):
129 word = word + "("
130 return word
131
132 def global_matches(self, text):
133 """
134 Public method to compute matches when text is a simple name.
135
136 @param text The text to be completed. (string)
137 @return A list of all keywords, built-in functions and names currently
138 defined in self.namespace that match.
139 """
140 import keyword
141 matches = []
142 seen = {"__builtins__"}
143 n = len(text)
144 for word in keyword.kwlist:
145 if word[:n] == text:
146 seen.add(word)
147 if word in {'finally', 'try'}:
148 word = word + ':'
149 elif word not in {'False', 'None', 'True',
150 'break', 'continue', 'pass',
151 'else'}:
152 word = word + ' '
153 matches.append(word)
154 for nspace in [builtins.__dict__, self.namespace]:
155 for word, val in nspace.items():
156 if word[:n] == text and word not in seen:
157 seen.add(word)
158 matches.append(self._callable_postfix(val, word))
159 return matches
160
161 def attr_matches(self, text):
162 """
163 Public method to compute matches when text contains a dot.
164
165 Assuming the text is of the form NAME.NAME....[NAME], and is
166 evaluatable in self.namespace, it will be evaluated and its attributes
167 (as revealed by dir()) are used as possible completions. (For class
168 instances, class members are are also considered.)
169
170 <b>WARNING</b>: this can still invoke arbitrary C code, if an object
171 with a __getattr__ hook is evaluated.
172
173 @param text The text to be completed. (string)
174 @return A list of all matches.
175 """
176 import re
177
178 m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
179 if not m:
180 return []
181 expr, attr = m.group(1, 3)
182 try:
183 thisobject = eval(expr, self.namespace)
184 except Exception:
185 return []
186
187 # get the content of the object, except __builtins__
188 words = set(dir(thisobject))
189 words.discard("__builtins__")
190
191 if hasattr(object, '__class__'):
192 words.add('__class__')
193 words.update(get_class_members(thisobject.__class__))
194 matches = []
195 n = len(attr)
196 if attr == '':
197 noprefix = '_'
198 elif attr == '_':
199 noprefix = '__'
200 else:
201 noprefix = None
202 while True:
203 for word in words:
204 if word[:n] == attr and \
205 not (noprefix and word[:n + 1] == noprefix):
206 match = "{0}.{1}".format(expr, word)
207 try:
208 val = getattr(thisobject, word)
209 except Exception:
210 pass # Include even if attribute not set
211 else:
212 match = self._callable_postfix(val, match)
213 matches.append(match)
214 if matches or not noprefix:
215 break
216 if noprefix == '_':
217 noprefix = '__'
218 else:
219 noprefix = None
220 matches.sort()
221 return matches
222
223
224 def get_class_members(klass):
225 """
226 Module function to retrieve the class members.
227
228 @param klass The class object to be analysed.
229 @return A list of all names defined in the class.
230 """
231 ret = dir(klass)
232 if hasattr(klass, '__bases__'):
233 for base in klass.__bases__:
234 ret = ret + get_class_members(base)
235 return ret
236
237 #
238 # eflag: noqa = M702, M111

eric ide

mercurial