def follow_error_node_imports_if_possible(context, name): error_node = tree.search_ancestor(name, 'error_node') if error_node is not None: # Get the first command start of a started simple_stmt. The error # node is sometimes a small_stmt and sometimes a simple_stmt. Check # for ; leaves that start a new statements. start_index = 0 for index, n in enumerate(error_node.children): if n.start_pos > name.start_pos: break if n == ';': start_index = index + 1 nodes = error_node.children[start_index:] first_name = nodes[0].get_first_leaf().value # Make it possible to infer stuff like `import foo.` or # `from foo.bar`. if first_name in ('from', 'import'): is_import_from = first_name == 'from' level, names = helpers.parse_dotted_names( nodes, is_import_from=is_import_from, until_node=name, ) return Importer( context.inference_state, names, context.get_root_context(), level).follow() return None
def _get_value_completions(self, leaf): """ Analyzes the value that a completion is made in and decides what to return. Technically this works by generating a parser stack and analysing the current stack for possible grammar nodes. Possible enhancements: - global/nonlocal search global - yield from / raise from <- could be only exceptions/generators - In args: */**: no completion - In params (also lambda): no completion before = """ grammar = self._inference_state.grammar self.stack = stack = None try: self.stack = stack = helpers.get_stack_at_position( grammar, self._code_lines, leaf, self._position) except helpers.OnErrorLeaf as e: value = e.error_leaf.value if value == '.': # After ErrorLeaf's that are dots, we will not do any # completions since this probably just confuses the user. return [] # If we don't have a value, just use global completion. return self._global_completions() allowed_transitions = \ list(stack._allowed_transition_names_and_token_types()) if 'if' in allowed_transitions: leaf = self._module_node.get_leaf_for_position( self._position, include_prefixes=True) previous_leaf = leaf.get_previous_leaf() indent = self._position[1] if not (leaf.start_pos <= self._position <= leaf.end_pos): indent = leaf.start_pos[1] if previous_leaf is not None: stmt = previous_leaf while True: stmt = search_ancestor( stmt, 'if_stmt', 'for_stmt', 'while_stmt', 'try_stmt', 'error_node', ) if stmt is None: break type_ = stmt.type if type_ == 'error_node': first = stmt.children[0] if isinstance(first, Leaf): type_ = first.value + '_stmt' # Compare indents if stmt.start_pos[1] == indent: if type_ == 'if_stmt': allowed_transitions += ['elif', 'else'] elif type_ == 'try_stmt': allowed_transitions += [ 'except', 'finally', 'else' ] elif type_ == 'for_stmt': allowed_transitions.append('else') completion_names = [] current_line = self._code_lines[self._position[0] - 1][:self._position[1]] if not current_line or current_line[-1] in ' \t.;': completion_names += self._get_keyword_completion_names( allowed_transitions) if any(t in allowed_transitions for t in (PythonTokenTypes.NAME, PythonTokenTypes.INDENT)): # This means that we actually have to do type inference. nonterminals = [stack_node.nonterminal for stack_node in stack] nodes = [] for stack_node in stack: if stack_node.dfa.from_rule == 'small_stmt': nodes = [] else: nodes += stack_node.nodes if nodes and nodes[-1] in ('as', 'def', 'class'): # No completions for ``with x as foo`` and ``import x as foo``. # Also true for defining names as a class or function. return list( self._get_class_value_completions(is_function=True)) elif "import_stmt" in nonterminals: level, names = parse_dotted_names( nodes, "import_from" in nonterminals) only_modules = not ("import_from" in nonterminals and 'import' in nodes) completion_names += self._get_importer_names( names, level, only_modules=only_modules, ) elif nonterminals[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.': dot = self._module_node.get_leaf_for_position(self._position) completion_names += self._trailer_completions( dot.get_previous_leaf()) else: completion_names += self._global_completions() completion_names += self._get_class_value_completions( is_function=False) if 'trailer' in nonterminals: call_signatures = self._call_signatures_callback() completion_names += get_call_signature_param_names( call_signatures) return completion_names
def _complete_python(self, leaf): """ Analyzes the current context of a completion and decides what to return. Technically this works by generating a parser stack and analysing the current stack for possible grammar nodes. Possible enhancements: - global/nonlocal search global - yield from / raise from <- could be only exceptions/generators - In args: */**: no completion - In params (also lambda): no completion before = """ grammar = self._inference_state.grammar self.stack = stack = None self._position = (self._original_position[0], self._original_position[1] - len(self._like_name)) cached_name = None try: self.stack = stack = helpers.get_stack_at_position( grammar, self._code_lines, leaf, self._position) except helpers.OnErrorLeaf as e: value = e.error_leaf.value if value == '.': # After ErrorLeaf's that are dots, we will not do any # completions since this probably just confuses the user. return cached_name, [] # If we don't have a value, just use global completion. return cached_name, self._complete_global_scope() allowed_transitions = \ list(stack._allowed_transition_names_and_token_types()) if 'if' in allowed_transitions: leaf = self._module_node.get_leaf_for_position( self._position, include_prefixes=True) previous_leaf = leaf.get_previous_leaf() indent = self._position[1] if not (leaf.start_pos <= self._position <= leaf.end_pos): indent = leaf.start_pos[1] if previous_leaf is not None: stmt = previous_leaf while True: stmt = search_ancestor( stmt, 'if_stmt', 'for_stmt', 'while_stmt', 'try_stmt', 'error_node', ) if stmt is None: break type_ = stmt.type if type_ == 'error_node': first = stmt.children[0] if isinstance(first, Leaf): type_ = first.value + '_stmt' # Compare indents if stmt.start_pos[1] == indent: if type_ == 'if_stmt': allowed_transitions += ['elif', 'else'] elif type_ == 'try_stmt': allowed_transitions += [ 'except', 'finally', 'else' ] elif type_ == 'for_stmt': allowed_transitions.append('else') completion_names = [] current_line = self._code_lines[self._position[0] - 1][:self._position[1]] kwargs_only = False if any(t in allowed_transitions for t in (PythonTokenTypes.NAME, PythonTokenTypes.INDENT)): # This means that we actually have to do type inference. nonterminals = [stack_node.nonterminal for stack_node in stack] nodes = _gather_nodes(stack) if nodes and nodes[-1] in ('as', 'def', 'class'): # No completions for ``with x as foo`` and ``import x as foo``. # Also true for defining names as a class or function. return cached_name, list( self._complete_inherited(is_function=True)) elif "import_stmt" in nonterminals: level, names = parse_dotted_names( nodes, "import_from" in nonterminals) only_modules = not ("import_from" in nonterminals and 'import' in nodes) completion_names += self._get_importer_names( names, level, only_modules=only_modules, ) elif nonterminals[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.': dot = self._module_node.get_leaf_for_position(self._position) cached_name, n = self._complete_trailer( dot.get_previous_leaf()) completion_names += n elif self._is_parameter_completion(): completion_names += self._complete_params(leaf) else: # Apparently this looks like it's good enough to filter most cases # so that signature completions don't randomly appear. # To understand why this works, three things are important: # 1. trailer with a `,` in it is either a subscript or an arglist. # 2. If there's no `,`, it's at the start and only signatures start # with `(`. Other trailers could start with `.` or `[`. # 3. Decorators are very primitive and have an optional `(` with # optional arglist in them. if nodes[-1] in ['(', ','] \ and nonterminals[-1] in ('trailer', 'arglist', 'decorator'): signatures = self._signatures_callback(*self._position) if signatures: call_details = signatures[0]._call_details used_kwargs = list( call_details.iter_used_keyword_arguments()) positional_count = call_details.count_positional_arguments( ) completion_names += _get_signature_param_names( signatures, positional_count, used_kwargs, ) kwargs_only = _must_be_kwarg(signatures, positional_count, used_kwargs) if not kwargs_only: completion_names += self._complete_global_scope() completion_names += self._complete_inherited( is_function=False) if not kwargs_only: completion_names += self._complete_keywords( allowed_transitions, only_values=not (not current_line or current_line[-1] in ' \t.;' and current_line[-3:] != '...')) return cached_name, completion_names