diff --git a/README.mdown b/README.mdown index 8772c57b..eca93226 100644 --- a/README.mdown +++ b/README.mdown @@ -39,6 +39,7 @@ nice in Python 2. To keep things simple, some things have been held back: - Classes: Always Python 3, therefore all classes inherit from `object`. - Generators: No `next` method. The `__next__` method is there instead. + - Exceptions are only looked at in the form of `Exception as e`, no comma! Syntax Errors and other strange stuff, that is defined differently in the Python language, may lead to undefined behaviour of the completion. If you diff --git a/evaluate.py b/evaluate.py index a9c27caf..179b23b3 100644 --- a/evaluate.py +++ b/evaluate.py @@ -286,11 +286,24 @@ class Instance(Executable): return names + @property def parent(self): return self.base.parent + @property + def line_nr(self): + return self.base.line_nr + + @property + def indent(self): + return self.base.indent + + @property + def name(self): + return self.base.name + def __repr__(self): - return "" % \ + return "" % \ (self.__class__.__name__, self.base, len(self.var_args or [])) @@ -345,11 +358,15 @@ class Class(object): names.append(i) return names + @property + def name(self): + return self.base.name + def __getattr__(self, name): return getattr(self.base, name) def __repr__(self): - return "" % (self.__class__.__name__, self.base) + return "" % (self.__class__.__name__, self.base) class Execution(Executable): @@ -537,7 +554,7 @@ class Array(object): return self._array def __repr__(self): - return "" % (self.__class__.__name__, self._array) + return "" % (self.__class__.__name__, self._array) class ArrayElement(object): diff --git a/ftest.py b/ftest.py index f92edca5..49766318 100755 --- a/ftest.py +++ b/ftest.py @@ -15,7 +15,7 @@ path = os.path.join(os.getcwd(), f_name) f = open(path) code = f.read() for i in range(1): - completions = functions.complete(code, 180, 200, path) + completions = functions.get_definitions(code, 180, 200, path) #completions = functions.complete(code, 42, 200, path) print '\n', ', '.join(sorted(str(c) for c in completions)) diff --git a/functions.py b/functions.py index 580115cc..e2dff185 100644 --- a/functions.py +++ b/functions.py @@ -1,4 +1,5 @@ import re +import sys import parsing import evaluate @@ -10,7 +11,10 @@ __all__ = ['complete', 'goto', 'get_completion_parts', 'set_debug_function'] class NotFoundError(Exception): """ A custom error to avoid catching the wrong errors """ - pass + def __init__(self, scope, path_tuple, message=None): + super(NotFoundError, self).__init__(message) + self.scope = scope + self.path_tuple = path_tuple class Completion(object): @@ -32,7 +36,7 @@ class Completion(object): def help(self): try: return str(self.name.parent.docstr) - except: + except AttributeError: return '' def get_type(self): @@ -63,6 +67,42 @@ class Completion(object): return self.name.names[-1] +class Definition(object): + def __init__(self, scope): + """ The definition of a function """ + self.scope = scope + + def get_name(self): + return self.scope.name + + def get_module(self): + par = self.scope + while True: + if par.parent is not None: + par = par.parent + else: + break + return par.path + + def get_line(self): + return self.scope.line_nr + + def get_indent(self): + return self.scope.indent + + def __str__(self): + module = self.get_module() + if module[0] == '/': + position = '@%s' % (self.get_line()) + else: + # no path - is a builtin + position = '' + + return "%s.%s%s" % (module, self.get_name(), position) + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, self) + def get_completion_parts(path): """ Returns the parts for the completion @@ -88,18 +128,14 @@ def complete(source, row, column, source_path): :return: list of completion objects :rtype: list """ - f = modules.ModuleWithCursor(source_path, source=source, row=row) - scope = f.parser.user_scope - path = f.get_path_until_cursor(column) - debug.dbg('completion_start: %s in %s' % (path, scope)) - - # just parse one statement, take it and evaluate it - path, dot, like = get_completion_parts(path) - r = parsing.PyFuzzyParser(path, source_path) try: - stmt = r.top.statements[0] - except IndexError: - scope_generator = evaluate.get_names_for_scope(scope) + scopes, path, dot, like = prepare_goto(source, row, column, source_path, True) + except NotFoundError: + # normally this would be used like this: `NotFoundError as exc`, but + # this guarantues backwards compatibility with Python2.5. + exc = sys.exc_info()[1] + path, dot, like = exc.path_tuple + scope_generator = evaluate.get_names_for_scope(exc.scope) completions = [] for dummy, name_list in scope_generator: completions += name_list @@ -107,11 +143,6 @@ def complete(source, row, column, source_path): # if isinstance(, parsing.Function): # print c.parent else: - stmt.line_nr = row - stmt.indent = column - stmt.parent = scope - scopes = evaluate.follow_statement(stmt, scope=scope) - completions = [] debug.dbg('possible scopes', scopes) for s in scopes: @@ -146,20 +177,40 @@ def prepare_goto(source, row, column, source_path, is_like_search): try: stmt = r.top.statements[0] except IndexError: - raise NotFoundError() + path_tuple = path, dot, like + raise NotFoundError(scope, path_tuple) else: stmt.line_nr = row stmt.indent = column stmt.parent = scope scopes = evaluate.follow_statement(stmt, scope=scope) - return scope, scopes + if is_like_search: + return scopes, path, dot, like + else: + return scopes -def goto(source, row, column, source_path): - dummy, scopes = prepare_goto(source, row, column, source_path, False) +def get_definitions(source, row, column, source_path): + """ + Returns the definitions of a the path under the cursor. + This is not a goto function! This follows complicated paths and returns the + end, not the first definition. + :param source: The source code of the current file + :type source: string + :param row: The row to complete in. + :type row: int + :param col: The column to complete in. + :type col: int + :param source_path: The path in the os, the current module is in. + :type source_path: int + + :return: list of Definition objects, which are basically scopes. + :rtype: list + """ + scopes = prepare_goto(source, row, column, source_path, False) _clear_caches() - return scopes + return [Definition(s) for s in set(scopes)] def set_debug_function(func_cb):