goto function added

This commit is contained in:
David Halter
2012-05-13 12:41:56 +02:00
parent d6c5363132
commit cb201c3b7b
4 changed files with 96 additions and 27 deletions

View File

@@ -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`. - Classes: Always Python 3, therefore all classes inherit from `object`.
- Generators: No `next` method. The `__next__` method is there instead. - 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 Syntax Errors and other strange stuff, that is defined differently in the
Python language, may lead to undefined behaviour of the completion. If you Python language, may lead to undefined behaviour of the completion. If you

View File

@@ -286,11 +286,24 @@ class Instance(Executable):
return names return names
@property
def parent(self): def parent(self):
return self.base.parent 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): def __repr__(self):
return "<p%s of %s (var_args: %s)>" % \ return "<e%s of %s (var_args: %s)>" % \
(self.__class__.__name__, self.base, len(self.var_args or [])) (self.__class__.__name__, self.base, len(self.var_args or []))
@@ -345,11 +358,15 @@ class Class(object):
names.append(i) names.append(i)
return names return names
@property
def name(self):
return self.base.name
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.base, name) return getattr(self.base, name)
def __repr__(self): def __repr__(self):
return "<p%s of %s>" % (self.__class__.__name__, self.base) return "<e%s of %s>" % (self.__class__.__name__, self.base)
class Execution(Executable): class Execution(Executable):
@@ -537,7 +554,7 @@ class Array(object):
return self._array return self._array
def __repr__(self): def __repr__(self):
return "<p%s of %s>" % (self.__class__.__name__, self._array) return "<e%s of %s>" % (self.__class__.__name__, self._array)
class ArrayElement(object): class ArrayElement(object):

View File

@@ -15,7 +15,7 @@ path = os.path.join(os.getcwd(), f_name)
f = open(path) f = open(path)
code = f.read() code = f.read()
for i in range(1): 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) #completions = functions.complete(code, 42, 200, path)
print '\n', ', '.join(sorted(str(c) for c in completions)) print '\n', ', '.join(sorted(str(c) for c in completions))

View File

@@ -1,4 +1,5 @@
import re import re
import sys
import parsing import parsing
import evaluate import evaluate
@@ -10,7 +11,10 @@ __all__ = ['complete', 'goto', 'get_completion_parts', 'set_debug_function']
class NotFoundError(Exception): class NotFoundError(Exception):
""" A custom error to avoid catching the wrong errors """ """ 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): class Completion(object):
@@ -32,7 +36,7 @@ class Completion(object):
def help(self): def help(self):
try: try:
return str(self.name.parent.docstr) return str(self.name.parent.docstr)
except: except AttributeError:
return '' return ''
def get_type(self): def get_type(self):
@@ -63,6 +67,42 @@ class Completion(object):
return self.name.names[-1] 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): def get_completion_parts(path):
""" """
Returns the parts for the completion Returns the parts for the completion
@@ -88,18 +128,14 @@ def complete(source, row, column, source_path):
:return: list of completion objects :return: list of completion objects
:rtype: list :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: try:
stmt = r.top.statements[0] scopes, path, dot, like = prepare_goto(source, row, column, source_path, True)
except IndexError: except NotFoundError:
scope_generator = evaluate.get_names_for_scope(scope) # 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 = [] completions = []
for dummy, name_list in scope_generator: for dummy, name_list in scope_generator:
completions += name_list completions += name_list
@@ -107,11 +143,6 @@ def complete(source, row, column, source_path):
# if isinstance(, parsing.Function): # if isinstance(, parsing.Function):
# print c.parent # print c.parent
else: else:
stmt.line_nr = row
stmt.indent = column
stmt.parent = scope
scopes = evaluate.follow_statement(stmt, scope=scope)
completions = [] completions = []
debug.dbg('possible scopes', scopes) debug.dbg('possible scopes', scopes)
for s in scopes: for s in scopes:
@@ -146,20 +177,40 @@ def prepare_goto(source, row, column, source_path, is_like_search):
try: try:
stmt = r.top.statements[0] stmt = r.top.statements[0]
except IndexError: except IndexError:
raise NotFoundError() path_tuple = path, dot, like
raise NotFoundError(scope, path_tuple)
else: else:
stmt.line_nr = row stmt.line_nr = row
stmt.indent = column stmt.indent = column
stmt.parent = scope stmt.parent = scope
scopes = evaluate.follow_statement(stmt, scope=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): def get_definitions(source, row, column, source_path):
dummy, scopes = prepare_goto(source, row, column, source_path, False) """
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() _clear_caches()
return scopes return [Definition(s) for s in set(scopes)]
def set_debug_function(func_cb): def set_debug_function(func_cb):