| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | # Copyright (c) 2009 www.stani.be (GPL license) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | # it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | # the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | # (at your option) any later version. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | # along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-13 14:38:30 +00:00
										 |  |  | # <pep8-80 compliant> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | """This module provides intellisense features such as:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * autocompletion | 
					
						
							| 
									
										
										
										
											2009-11-06 08:53:07 +00:00
										 |  |  | * calltips | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | It unifies all completion plugins and only loads them on demand. | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2009-11-06 08:53:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | # TODO: file complete if startswith quotes | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # regular expressions to find out which completer we need | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # line which starts with an import statement | 
					
						
							|  |  |  | RE_MODULE = re.compile('^import|from.+') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  | # The following regular expression means an 'unquoted' word | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | RE_UNQUOTED_WORD = re.compile( | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     # don't start with a quote | 
					
						
							|  |  |  |     '''(?:^|[^"'a-zA-Z0-9_])''' | 
					
						
							|  |  |  |     # start with a \w = [a-zA-Z0-9_] | 
					
						
							|  |  |  |     '''((?:\w+''' | 
					
						
							|  |  |  |     # allow also dots and closed bracket pairs [] | 
					
						
							|  |  |  |     '''(?:\w|[.]|\[.+?\])*''' | 
					
						
							|  |  |  |     # allow empty string | 
					
						
							|  |  |  |     '''|)''' | 
					
						
							|  |  |  |     # allow an unfinished index at the end (including quotes) | 
					
						
							|  |  |  |     '''(?:\[[^\]]*$)?)$''', | 
					
						
							|  |  |  |     # allow unicode as theoretically this is possible | 
					
						
							|  |  |  |     re.UNICODE) | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def complete(line, cursor, namespace, private=True): | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     """Returns a list of possible completions:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     * name completion | 
					
						
							|  |  |  |     * attribute completion (obj.attr) | 
					
						
							|  |  |  |     * index completion for lists and dictionaries | 
					
						
							|  |  |  |     * module completion (from/import) | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     :param line: incomplete text line | 
					
						
							|  |  |  |     :type line: str | 
					
						
							|  |  |  |     :param cursor: current character position | 
					
						
							|  |  |  |     :type cursor: int | 
					
						
							|  |  |  |     :param namespace: namespace | 
					
						
							|  |  |  |     :type namespace: dict | 
					
						
							|  |  |  |     :param private: whether private variables should be listed | 
					
						
							|  |  |  |     :type private: bool | 
					
						
							|  |  |  |     :returns: list of completions, word | 
					
						
							|  |  |  |     :rtype: list, str | 
					
						
							| 
									
										
										
										
											2009-11-06 08:53:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     >>> complete('re.sr', 5, {'re': re}) | 
					
						
							|  |  |  |     (['re.sre_compile', 're.sre_parse'], 're.sr') | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     re_unquoted_word = RE_UNQUOTED_WORD.search(line[:cursor]) | 
					
						
							|  |  |  |     if re_unquoted_word: | 
					
						
							|  |  |  |         # unquoted word -> module or attribute completion | 
					
						
							|  |  |  |         word = re_unquoted_word.group(1) | 
					
						
							|  |  |  |         if RE_MODULE.match(line): | 
					
						
							|  |  |  |             import complete_import | 
					
						
							|  |  |  |             matches = complete_import.complete(line) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             import complete_namespace | 
					
						
							|  |  |  |             matches = complete_namespace.complete(word, namespace, private) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # for now we don't have completers for strings | 
					
						
							|  |  |  |         # TODO: add file auto completer for strings | 
					
						
							|  |  |  |         word = '' | 
					
						
							|  |  |  |         matches = [] | 
					
						
							|  |  |  |     return matches, word | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def expand(line, cursor, namespace, private=True): | 
					
						
							|  |  |  |     """This method is invoked when the user asks autocompletion,
 | 
					
						
							|  |  |  |     e.g. when Ctrl+Space is clicked. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param line: incomplete text line | 
					
						
							|  |  |  |     :type line: str | 
					
						
							|  |  |  |     :param cursor: current character position | 
					
						
							|  |  |  |     :type cursor: int | 
					
						
							|  |  |  |     :param namespace: namespace | 
					
						
							|  |  |  |     :type namespace: dict | 
					
						
							|  |  |  |     :param private: whether private variables should be listed | 
					
						
							|  |  |  |     :type private: bool | 
					
						
							|  |  |  |     :returns: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         current expanded line, updated cursor position and scrollback | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :rtype: str, int, str | 
					
						
							| 
									
										
										
										
											2009-11-06 08:53:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     >>> expand('os.path.isdir(', 14, {'os': os})[-1] | 
					
						
							|  |  |  |     'isdir(s)\\nReturn true if the pathname refers to an existing directory.' | 
					
						
							|  |  |  |     >>> expand('abs(', 4, {})[-1] | 
					
						
							|  |  |  |     'abs(number) -> number\\nReturn the absolute value of the argument.' | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2009-11-06 08:53:07 +00:00
										 |  |  |     if line[:cursor].strip().endswith('('): | 
					
						
							|  |  |  |         import complete_calltip | 
					
						
							|  |  |  |         matches, word, scrollback = complete_calltip.complete(line, | 
					
						
							|  |  |  |             cursor, namespace) | 
					
						
							|  |  |  |         no_calltip = False | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         matches, word = complete(line, cursor, namespace, private) | 
					
						
							|  |  |  |         if len(matches) == 1: | 
					
						
							|  |  |  |             scrollback = '' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             scrollback = '  '.join([m.split('.')[-1] for m in matches]) | 
					
						
							|  |  |  |         no_calltip = True | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |     prefix = os.path.commonprefix(matches)[len(word):] | 
					
						
							|  |  |  |     if prefix: | 
					
						
							|  |  |  |         line = line[:cursor] + prefix + line[cursor:] | 
					
						
							|  |  |  |         cursor += len(prefix) | 
					
						
							| 
									
										
										
										
											2009-11-06 08:53:07 +00:00
										 |  |  |         if no_calltip and prefix.endswith('('): | 
					
						
							|  |  |  |             return expand(line, cursor, namespace, private) | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |     return line, cursor, scrollback |