diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index a5b0b109..31300974 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -157,7 +157,7 @@ class Script(object): if unfinished_dotted: return completion_names else: - return keywords.keyword_names('import') + return set([keywords.keyword('import').name]) if isinstance(user_stmt, tree.Import): module = self._parser.module() @@ -168,7 +168,11 @@ class Script(object): if names is None and not isinstance(user_stmt, tree.Import): if not path and not dot: # add keywords - completion_names += keywords.keyword_names(all=True) + completion_names += keywords.completion_names( + self._evaluator, + user_stmt, + self._pos, + module) # TODO delete? We should search for valid parser # transformations. completion_names += self._simple_complete(path, dot, like) diff --git a/jedi/api/keywords.py b/jedi/api/keywords.py index 2a54ba2d..4feb39fd 100644 --- a/jedi/api/keywords.py +++ b/jedi/api/keywords.py @@ -5,7 +5,7 @@ from jedi._compatibility import is_py3 from jedi import common from jedi.evaluate import compiled from jedi.evaluate.helpers import FakeName - +from jedi.parser.tree import Leaf try: from pydoc_data import topics as pydoc_topics except ImportError: @@ -18,22 +18,49 @@ else: keys = keyword.kwlist + ['None', 'False', 'True'] -def keywords(string='', pos=(0, 0), all=False): - if all: - return set([Keyword(k, pos) for k in keys]) +def has_inappropriate_leaf_keyword(pos, module): + relevant_errors = filter( + lambda error: error.first_pos[0] == pos[0], + module.error_statement_stacks) + + for error in relevant_errors: + if error.next_token in keys: + return True + + return False + +def completion_names(evaluator, stmt, pos, module): + keyword_list = all_keywords() + + if not isinstance(stmt, Leaf) or has_inappropriate_leaf_keyword(pos, module): + keyword_list = filter( + lambda keyword: not keyword.only_valid_as_leaf, + keyword_list + ) + return [keyword.name for keyword in keyword_list] + + +def all_keywords(pos=(0,0)): + return set([Keyword(k, pos) for k in keys]) + + +def keyword(string, pos=(0,0)): if string in keys: - return set([Keyword(string, pos)]) - return set() - - -def keyword_names(*args, **kwargs): - return [k.name for k in keywords(*args, **kwargs)] + return Keyword(string, pos) + else: + return None def get_operator(string, pos): return Keyword(string, pos) +keywords_only_valid_as_leaf = ( + 'continue', + 'break', +) + + class Keyword(object): def __init__(self, name, pos): self.name = FakeName(name, self, pos) @@ -43,6 +70,10 @@ class Keyword(object): def get_parent_until(self): return self.parent + @property + def only_valid_as_leaf(self): + return self.name.value in keywords_only_valid_as_leaf + @property def names(self): """ For a `parsing.Name` like comparision """ diff --git a/test/completion/keywords.py b/test/completion/keywords.py index 851140b1..ba56c210 100644 --- a/test/completion/keywords.py +++ b/test/completion/keywords.py @@ -4,3 +4,24 @@ raise #? ['except', 'Exception'] except + +#? [] +b + continu + +#? [] +b + continue + +#? ['continue'] +b; continue + +#? ['continue'] +b; continu + +#? [] +c + brea + +#? [] +a + break + +#? ['break'] +b; break diff --git a/test/run.py b/test/run.py index a48e1fb2..d7309143 100755 --- a/test/run.py +++ b/test/run.py @@ -290,9 +290,12 @@ def collect_dir_tests(base_dir, test_files, check_thirdparty=False): skip = 'Thirdparty-Library %s not found.' % lib path = os.path.join(base_dir, f_name) - source = open(path).read() - if not is_py3: - source = unicode(source, 'UTF-8') + + if is_py3: + source = open(path, encoding='utf-8').read() + else: + source = unicode(open(path).read(), 'UTF-8') + for case in collect_file_tests(StringIO(source), lines_to_execute): case.path = path