diff --git a/api.py b/api.py index 8eb9e487..d4fd5050 100644 --- a/api.py +++ b/api.py @@ -112,9 +112,10 @@ class Definition(dynamic.BaseOutput): class CallDef(object): - def __init__(self, executable, index): + def __init__(self, executable, index, call): self.executable = executable self.index = index + self.call = call @property def params(self): @@ -133,7 +134,7 @@ class CallDef(object): class Script(object): - """ + """ A Script is the base for a completion, goto or whatever call. :param source: The source code of the current file @@ -353,41 +354,63 @@ class Script(object): return sorted(names, key=lambda x: (x.module_path, x.start_pos)) def get_in_function_call(self): + """ + Return the function, that the cursor is in, e.g.: + >>> isinstance(| # | <-- cursor is here + + This would return the `isinstance` function. In contrary: + >>> isinstance()| # | <-- cursor is here + + This would return `None`. + """ def scan_array_for_pos(arr, pos): """ Returns the function Call that match search_name in an Array. """ - index = None + index = 0 call = None + stop = False for index, sub in enumerate(arr): call = None for s in sub: if isinstance(s, parsing.Array): new = scan_array_for_pos(s, pos) if new[0] is not None: - call, index = new + call, index, stop = new + if stop: + return call, index, stop elif isinstance(s, parsing.Call): while s is not None: if s.start_pos >= pos: - return call, index + return call, index, stop if s.execution is not None: if s.execution.start_pos <= pos: call = s - c, index = scan_array_for_pos(s.execution, - pos) + c, index, stop = scan_array_for_pos( + s.execution, pos) + if stop: + return c, index, stop if c is not None: call = c else: - return call, index + return call, index, stop + print('E', pos, s.execution.end_pos) + if s.execution.end_pos is not None: + if pos < s.execution.end_pos: + return call, index, True + else: + return None, 0, True s = s.next - return call, index + # The third return is just necessary for recursion inside, because + # it needs to know when to stop iterating. + return call, index, stop user_stmt = self.parser.user_stmt if user_stmt is None or not isinstance(user_stmt, parsing.Statement): return None ass = user_stmt.get_assignment_calls() - call, index = scan_array_for_pos(ass, self.pos) + call, index, stop = scan_array_for_pos(ass, self.pos) if call is None: return None @@ -399,7 +422,7 @@ class Script(object): return None # just take entry zero, because we need just one. executable = origins[0] - return CallDef(executable, index) + return CallDef(executable, index, call) def _get_completion_parts(self, path): """ diff --git a/parsing.py b/parsing.py index 8c5db7c6..744fb09d 100644 --- a/parsing.py +++ b/parsing.py @@ -790,6 +790,7 @@ class Statement(Simple): # always dictionaries and not sets. result.type = Array.DICT level -= 1 + result.end_pos = start_pos[0], start_pos[1] + 1 close_brackets = True else: while is_call_or_close(): @@ -943,6 +944,7 @@ class Array(Call): self.values = values if values else [] self.keys = [] + self.end_pos = None def add_field(self): """ diff --git a/test/regression.py b/test/regression.py index 7bf6a49c..188beb18 100755 --- a/test/regression.py +++ b/test/regression.py @@ -113,16 +113,21 @@ class TestRegression(unittest.TestCase): def test_get_in_function_call(self): s = "isinstance(a, abs(" s2 = "isinstance(), " + s3 = "isinstance()." check = lambda call_def, index: call_def and call_def.index == index + assert check(self.get_in_function_call(s, (1, 11)), 0) assert check(self.get_in_function_call(s, (1, 14)), 1) assert check(self.get_in_function_call(s, (1, 15)), 1) assert check(self.get_in_function_call(s, (1, 18)), 0) - ##assert check(self.get_in_function_call(s2, (1, 11)), 0) # TODO uncomment + assert check(self.get_in_function_call(s2, (1, 11)), 0) assert self.get_in_function_call(s2, (1, 12)) is None assert self.get_in_function_call(s2) is None + assert self.get_in_function_call(s3, (1, 12)) is None + assert self.get_in_function_call(s3) is None + if __name__ == '__main__': unittest.main()