# -*- coding: utf-8 -*- """ python-part of PythonCompleter code/ideas taken from IPython, but IPython doesn't work with PythonQt... Authors: "Melven Zoellner" """ # system imports import sys import rlcompleter import imp from __main__ import PYMODULES sys.path.append(PYMODULES+'/pygments/') sys.path.insert(0,PYMODULES) # Third-party imports from pygments.lexers import PythonLexer from pygments.token import Token, is_token_subtype from module_completion import module_completion # undo sys.path modifications #if not dot_in_path: # sys.path.remove('/opt/SocExplorer/python') # ugly fix to make rlcompleter work with PythonQt: # problem: # hasattr raises SystemError for # # this is probably a PythonQt-problem and should be fixed there? def _get_class_members(klass): ret = dir(klass) try: for base in klass.__bases__: ret = ret + _get_class_members(base) except SystemError: pass return ret rlcompleter.get_class_members = _get_class_members def pythonqt_specific_completions(context, parent): """ determines pythonqt-specific completions, currently it adds only named children of QObjects """ try: completions = list() # for completions for instances of PythonQt-Wrapper classes # we need to do some ugly stuff here # 1. get the base name base_name = '.'.join(context[0:-1]) # 2. eval the name in the parent context base_object = eval(base_name, parent.__dict__) # 2. check if it is really an instance of a PythonQt-Wrapper class if repr(type(type(base_object))) == "": # 3. look for the names of it's children if( hasattr(base_object, 'children') and callable(base_object.children) ): for child in base_object.children(): if( hasattr(child, 'objectName') and child.objectName != '' ): completions.append(child.objectName) return completions except: return list() def autocompleteCode(code, parent = sys.modules['__main__']): """ try to find useful completions for code """ # get last line and context all_lines = code.split('\n') line = all_lines[-1] context = get_context(line) word = '.'.join(context) completions = None # module completion if line.startswith('from ') or line.startswith('import '): completions = module_completion(line) # rlcompleter if completions is None: completer = rlcompleter.Completer(parent.__dict__) if '.' in word: completions = completer.attr_matches(word) # also append PythonQt-specific completions completions += pythonqt_specific_completions(context, parent) else: completions = completer.global_matches(word) # we only need the last part of the completion # (e.g. my_function from my_class.my_function) completions = [ c.split('.')[-1] for c in completions ] # sort completions and remove duplicates completions = sorted(set(completions), key=(lambda s: s.lower())) # hide all members that begin with '_', # except the user starts a name with '_' if not (context and context[-1].startswith('_')): completions = [c for c in completions if not c.startswith('_')] # return results return completions def get_context(string): """ Assuming the cursor is at the end of the specified string, get the context (a list of names) for the symbol at cursor position. """ lexer = PythonLexer() context = [] reversed_tokens = list(lexer.get_tokens(string)) reversed_tokens.reverse() # Pygments often tacks on a newline when none is specified in the input. # Remove this newline. if reversed_tokens and reversed_tokens[0][1].endswith('\n') and \ not string.endswith('\n'): reversed_tokens.pop(0) current_op = '' for token, text in reversed_tokens: if is_token_subtype(token, Token.Name): # Handle a trailing separator, e.g 'foo.bar.' if current_op == '.': if not context: context.insert(0, '') # Handle non-separator operators and punction. elif current_op: break context.insert(0, text) current_op = '' # Pygments doesn't understand that, e.g., '->' is a single operator # in C++. This is why we have to build up an operator from # potentially several tokens. elif token is Token.Operator or token is Token.Punctuation: current_op = text + current_op # Break on anything that is not a Operator, Punctuation, or Name. else: break return context