|
23 | 23 |
|
24 | 24 | from __future__ import with_statement |
25 | 25 | import __builtin__ |
| 26 | +import __main__ |
26 | 27 | import rlcompleter |
27 | 28 | import line |
28 | 29 | import re |
|
48 | 49 | FUZZY = 'fuzzy' |
49 | 50 |
|
50 | 51 | class Autocomplete(rlcompleter.Completer): |
51 | | - """ |
52 | | -#TOMHERE TODO This doesn't need to be a class anymore - we're overriding every single method |
53 | | - We're not really using it for the right thing anyway - we're hacking it to produce |
54 | | - self.matches then stealing them instead of following the expected interface of calling |
55 | | - complete each time. |
56 | | - """ |
57 | | - |
58 | | - def __init__(self, namespace = None, config = None): |
59 | | - rlcompleter.Completer.__init__(self, namespace) |
60 | | - self.locals = namespace |
61 | | - if hasattr(config, 'autocomplete_mode'): |
62 | | - self.autocomplete_mode = config.autocomplete_mode |
| 52 | + def __init__(self, namespace, config): |
| 53 | + self.namespace = namespace |
| 54 | + self.config = config |
| 55 | + def complete(self, cw, _): |
| 56 | + self.matches = complete(cw, namespace=self.namespace, config=self.config) |
| 57 | + |
| 58 | +def complete(text, namespace=None, config=None): |
| 59 | + """Return list of matches """ |
| 60 | + if namespace is None: |
| 61 | + namespace = __main__.__dict__ |
| 62 | + |
| 63 | + if hasattr(config, 'autocomplete_mode'): |
| 64 | + autocomplete_mode = config.autocomplete_mode |
| 65 | + else: |
| 66 | + autocomplete_mode = SUBSTRING |
| 67 | + |
| 68 | + dictpattern = re.compile('[^\[\]]+\[$') |
| 69 | + def complete_dict(text): |
| 70 | + lastbracket_index = text.rindex('[') |
| 71 | + dexpr = text[:lastbracket_index].lstrip() |
| 72 | + obj = eval(dexpr, namespace) |
| 73 | + if obj and isinstance(obj, type({})) and obj.keys(): |
| 74 | + return [dexpr + "[{!r}]".format(k) for k in obj.keys()] |
63 | 75 | else: |
64 | | - self.autocomplete_mode = SUBSTRING |
65 | | - |
66 | | - def complete(self, text, state): |
67 | | - """Return the next possible completion for 'text'. |
68 | | -
|
69 | | - This is called successively with state == 0, 1, 2, ... until it |
70 | | - returns None. The completion should begin with 'text'. |
71 | | -
|
72 | | - """ |
73 | | - if self.use_main_ns: |
74 | | - self.namespace = __main__.__dict__ |
75 | | - |
76 | | - dictpattern = re.compile('[^\[\]]+\[$') |
77 | | - def complete_dict(text): |
78 | | - lastbracket_index = text.rindex('[') |
79 | | - dexpr = text[:lastbracket_index].lstrip() |
80 | | - obj = eval(dexpr, self.locals) |
81 | | - if obj and isinstance(obj, type({})) and obj.keys(): |
82 | | - self.matches = [dexpr + "[{!r}]".format(k) for k in obj.keys()] |
83 | | - else: |
84 | | - # empty dictionary |
85 | | - self.matches = [] |
86 | | - |
87 | | - if state == 0: |
88 | | - if "." in text: |
89 | | - if dictpattern.match(text): |
90 | | - complete_dict(text) |
91 | | - else: |
92 | | - # Examples: 'foo.b' or 'foo[bar.' |
93 | | - for i in range(1, len(text) + 1): |
94 | | - if text[-i] == '[': |
95 | | - i -= 1 |
96 | | - break |
97 | | - methodtext = text[-i:] |
98 | | - self.matches = [''.join([text[:-i], m]) for m in |
99 | | - self.attr_matches(methodtext)] |
100 | | - elif dictpattern.match(text): |
101 | | - complete_dict(text) |
102 | | - else: |
103 | | - self.matches = self.global_matches(text) |
104 | | - try: |
105 | | - return self.matches[state] |
106 | | - except IndexError: |
107 | | - return None |
108 | | - |
109 | | - def attr_matches(self, text): |
110 | | - """Taken from rlcompleter.py and bent to my will. |
111 | | - """ |
112 | | - |
113 | | - # Gna, Py 2.6's rlcompleter searches for __call__ inside the |
114 | | - # instance instead of the type, so we monkeypatch to prevent |
115 | | - # side-effects (__getattr__/__getattribute__) |
116 | | - m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) |
117 | | - if not m: |
| 76 | + # empty dictionary |
118 | 77 | return [] |
119 | 78 |
|
120 | | - expr, attr = m.group(1, 3) |
121 | | - if expr.isdigit(): |
122 | | - # Special case: float literal, using attrs here will result in |
123 | | - # a SyntaxError |
124 | | - return [] |
125 | | - obj = eval(expr, self.locals) |
126 | | - with inspection.AttrCleaner(obj): |
127 | | - matches = self.attr_lookup(obj, expr, attr) |
128 | | - return matches |
129 | | - |
130 | | - def attr_lookup(self, obj, expr, attr): |
131 | | - """Second half of original attr_matches method factored out so it can |
132 | | - be wrapped in a safe try/finally block in case anything bad happens to |
133 | | - restore the original __getattribute__ method.""" |
134 | | - words = dir(obj) |
135 | | - if hasattr(obj, '__class__'): |
136 | | - words.append('__class__') |
137 | | - words = words + rlcompleter.get_class_members(obj.__class__) |
138 | | - if has_abc and not isinstance(obj.__class__, abc.ABCMeta): |
139 | | - try: |
140 | | - words.remove('__abstractmethods__') |
141 | | - except ValueError: |
142 | | - pass |
143 | | - |
144 | | - matches = [] |
145 | | - n = len(attr) |
146 | | - for word in words: |
147 | | - if self.method_match(word, n, attr) and word != "__builtins__": |
148 | | - matches.append("%s.%s" % (expr, word)) |
149 | | - return matches |
150 | | - |
151 | | - def _callable_postfix(self, value, word): |
152 | | - """rlcompleter's _callable_postfix done right.""" |
153 | | - with inspection.AttrCleaner(value): |
154 | | - if inspection.is_callable(value): |
155 | | - word += '(' |
156 | | - return word |
157 | | - |
158 | | - def global_matches(self, text): |
159 | | - """Compute matches when text is a simple name. |
160 | | - Return a list of all keywords, built-in functions and names currently |
161 | | - defined in self.namespace that match. |
162 | | - """ |
163 | | - |
164 | | - hash = {} |
165 | | - n = len(text) |
166 | | - import keyword |
167 | | - for word in keyword.kwlist: |
168 | | - if self.method_match(word, n, text): |
169 | | - hash[word] = 1 |
170 | | - for nspace in [__builtin__.__dict__, self.namespace]: |
171 | | - for word, val in nspace.items(): |
172 | | - if self.method_match(word, len(text), text) and word != "__builtins__": |
173 | | - hash[self._callable_postfix(val, word)] = 1 |
174 | | - matches = hash.keys() |
175 | | - matches.sort() |
176 | | - return matches |
177 | | - |
178 | | - def method_match(self, word, size, text): |
179 | | - if self.autocomplete_mode == SIMPLE: |
180 | | - return word[:size] == text |
181 | | - elif self.autocomplete_mode == SUBSTRING: |
182 | | - s = r'.*%s.*' % text |
183 | | - return re.search(s, word) |
| 79 | + if "." in text: |
| 80 | + if dictpattern.match(text): |
| 81 | + return complete_dict(text) |
184 | 82 | else: |
185 | | - s = r'.*%s.*' % '.*'.join(list(text)) |
186 | | - return re.search(s, word) |
| 83 | + # Examples: 'foo.b' or 'foo[bar.' |
| 84 | + for i in range(1, len(text) + 1): |
| 85 | + if text[-i] == '[': |
| 86 | + i -= 1 |
| 87 | + break |
| 88 | + methodtext = text[-i:] |
| 89 | + return [''.join([text[:-i], m]) for m in |
| 90 | + attr_matches(methodtext, namespace, autocomplete_mode)] |
| 91 | + elif dictpattern.match(text): |
| 92 | + return complete_dict(text) |
| 93 | + else: |
| 94 | + return global_matches(text, namespace, autocomplete_mode) |
| 95 | + |
| 96 | +def attr_matches(text, namespace, autocomplete_mode): |
| 97 | + """Taken from rlcompleter.py and bent to my will. |
| 98 | + """ |
| 99 | + |
| 100 | + # Gna, Py 2.6's rlcompleter searches for __call__ inside the |
| 101 | + # instance instead of the type, so we monkeypatch to prevent |
| 102 | + # side-effects (__getattr__/__getattribute__) |
| 103 | + m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) |
| 104 | + if not m: |
| 105 | + return [] |
| 106 | + |
| 107 | + expr, attr = m.group(1, 3) |
| 108 | + if expr.isdigit(): |
| 109 | + # Special case: float literal, using attrs here will result in |
| 110 | + # a SyntaxError |
| 111 | + return [] |
| 112 | + obj = eval(expr, namespace) |
| 113 | + with inspection.AttrCleaner(obj): |
| 114 | + matches = attr_lookup(obj, expr, attr, autocomplete_mode) |
| 115 | + return matches |
| 116 | + |
| 117 | +def attr_lookup(obj, expr, attr, autocomplete_mode): |
| 118 | + """Second half of original attr_matches method factored out so it can |
| 119 | + be wrapped in a safe try/finally block in case anything bad happens to |
| 120 | + restore the original __getattribute__ method.""" |
| 121 | + words = dir(obj) |
| 122 | + if hasattr(obj, '__class__'): |
| 123 | + words.append('__class__') |
| 124 | + words = words + rlcompleter.get_class_members(obj.__class__) |
| 125 | + if has_abc and not isinstance(obj.__class__, abc.ABCMeta): |
| 126 | + try: |
| 127 | + words.remove('__abstractmethods__') |
| 128 | + except ValueError: |
| 129 | + pass |
| 130 | + |
| 131 | + matches = [] |
| 132 | + n = len(attr) |
| 133 | + for word in words: |
| 134 | + if method_match(word, n, attr, autocomplete_mode) and word != "__builtins__": |
| 135 | + matches.append("%s.%s" % (expr, word)) |
| 136 | + return matches |
| 137 | + |
| 138 | +def _callable_postfix(value, word): |
| 139 | + """rlcompleter's _callable_postfix done right.""" |
| 140 | + with inspection.AttrCleaner(value): |
| 141 | + if inspection.is_callable(value): |
| 142 | + word += '(' |
| 143 | + return word |
| 144 | + |
| 145 | +def global_matches(text, namespace, autocomplete_mode): |
| 146 | + """Compute matches when text is a simple name. |
| 147 | + Return a list of all keywords, built-in functions and names currently |
| 148 | + defined in self.namespace that match. |
| 149 | + """ |
| 150 | + |
| 151 | + hash = {} |
| 152 | + n = len(text) |
| 153 | + import keyword |
| 154 | + for word in keyword.kwlist: |
| 155 | + if method_match(word, n, text, autocomplete_mode): |
| 156 | + hash[word] = 1 |
| 157 | + for nspace in [__builtin__.__dict__, namespace]: |
| 158 | + for word, val in nspace.items(): |
| 159 | + if method_match(word, len(text), text, autocomplete_mode) and word != "__builtins__": |
| 160 | + hash[_callable_postfix(val, word)] = 1 |
| 161 | + matches = hash.keys() |
| 162 | + matches.sort() |
| 163 | + return matches |
| 164 | + |
| 165 | +def method_match(word, size, text, autocomplete_mode): |
| 166 | + if autocomplete_mode == SIMPLE: |
| 167 | + return word[:size] == text |
| 168 | + elif autocomplete_mode == SUBSTRING: |
| 169 | + s = r'.*%s.*' % text |
| 170 | + return re.search(s, word) |
| 171 | + else: |
| 172 | + s = r'.*%s.*' % '.*'.join(list(text)) |
| 173 | + return re.search(s, word) |
187 | 174 |
|
188 | 175 | def filename_matches(cs): |
189 | 176 | matches = [] |
|
0 commit comments