def filter_names(inference_state, completion_names, stack, like_name, fuzzy, cached_name): comp_dct = set() if settings.case_insensitive_completion: like_name = like_name.lower() for name in completion_names: string = name.string_name if settings.case_insensitive_completion: string = string.lower() if helpers.match(string, like_name, fuzzy=fuzzy): new = classes.Completion( inference_state, name, stack, len(like_name), is_fuzzy=fuzzy, cached_name=cached_name, ) k = (new.name, new.complete) # key if k not in comp_dct: comp_dct.add(k) tree_name = name.tree_name if tree_name is not None: definition = tree_name.get_definition() if definition is not None and definition.type == 'del_stmt': continue yield new
def filter_names(inference_state, completion_names, stack, like_name, fuzzy): comp_dct = {} if settings.case_insensitive_completion: like_name = like_name.lower() for name in completion_names: string = name.string_name if settings.case_insensitive_completion: string = string.lower() if fuzzy: match = helpers.fuzzy_match(string, like_name) else: match = helpers.start_match(string, like_name) if match: new = classes.Completion( inference_state, name, stack, len(like_name), is_fuzzy=fuzzy, ) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new yield new
def file_name_completions(inference_state, module_context, start_leaf, string, like_name, call_signatures_callback, code_lines, position): # First we want to find out what can actually be changed as a name. like_name_length = len(os.path.basename(string) + like_name) addition = _get_string_additions(module_context, start_leaf) if addition is None: return string = addition + string # Here we use basename again, because if strings are added like # `'foo' + 'bar`, it should complete to `foobar/`. must_start_with = os.path.basename(string) + like_name string = os.path.dirname(string) sigs = call_signatures_callback() is_in_os_path_join = sigs and all(s.full_name == 'os.path.join' for s in sigs) if is_in_os_path_join: to_be_added = _add_os_path_join(module_context, start_leaf, sigs[0].bracket_start) if to_be_added is None: is_in_os_path_join = False else: string = to_be_added + string base_path = os.path.join(inference_state.project._path, string) try: listed = scandir(base_path) # OSError: [Errno 36] File name too long: '...' except (FileNotFoundError, OSError): return for entry in listed: name = entry.name if name.startswith(must_start_with): if is_in_os_path_join or not entry.is_dir(): if start_leaf.type == 'string': quote = get_string_quote(start_leaf) else: assert start_leaf.type == 'error_leaf' quote = start_leaf.value potential_other_quote = \ code_lines[position[0] - 1][position[1]:position[1] + len(quote)] # Add a quote if it's not already there. if quote != potential_other_quote: name += quote else: name += os.path.sep yield classes.Completion( inference_state, FileName(inference_state, name[len(must_start_with) - like_name_length:]), stack=None, like_name_length=like_name_length)
def complete_file_name(inference_state, module_context, start_leaf, string, like_name, signatures_callback, code_lines, position, fuzzy): # First we want to find out what can actually be changed as a name. like_name_length = len(os.path.basename(string)) addition = _get_string_additions(module_context, start_leaf) if addition is None: return string = addition + string # Here we use basename again, because if strings are added like # `'foo' + 'bar`, it should complete to `foobar/`. must_start_with = os.path.basename(string) string = os.path.dirname(string) sigs = signatures_callback(*position) is_in_os_path_join = sigs and all(s.full_name == 'os.path.join' for s in sigs) if is_in_os_path_join: to_be_added = _add_os_path_join(module_context, start_leaf, sigs[0].bracket_start) if to_be_added is None: is_in_os_path_join = False else: string = to_be_added + string base_path = os.path.join(inference_state.project._path, string) try: listed = sorted(scandir(base_path), key=lambda e: e.name) # OSError: [Errno 36] File name too long: '...' except (FileNotFoundError, OSError): return for entry in listed: name = entry.name if fuzzy: match = fuzzy_match(name, must_start_with) else: match = start_match(name, must_start_with) if match: if is_in_os_path_join or not entry.is_dir(): name += get_quote_ending(start_leaf.value, code_lines, position) else: name += os.path.sep yield classes.Completion( inference_state, PathName(inference_state, name[len(must_start_with) - like_name_length:]), stack=None, like_name_length=like_name_length, is_fuzzy=fuzzy, )
def filter_names(evaluator, completion_names, stack, like_name): comp_dct = {} for name in completion_names: if settings.case_insensitive_completion \ and name.string_name.lower().startswith(like_name.lower()) \ or name.string_name.startswith(like_name): new = classes.Completion(evaluator, name, stack, len(like_name)) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new yield new
def filter_names(inference_state, completion_names, stack, like_name): comp_dct = {} if settings.case_insensitive_completion: like_name = like_name.lower() for name in completion_names: string = name.string_name if settings.case_insensitive_completion: string = string.lower() if string.startswith(like_name): new = classes.Completion(inference_state, name, stack, len(like_name)) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new yield new
def filter_names(evaluator, completion_names, stack, like_name): comp_dct = {} for name in set(completion_names): if settings.case_insensitive_completion \ and str(name).lower().startswith(like_name.lower()) \ or str(name).startswith(like_name): if isinstance(name.parent, (tree.Function, tree.Class)): # TODO I think this is a hack. It should be an # er.Function/er.Class before that. name = evaluator.wrap(name.parent).name new = classes.Completion(evaluator, name, stack, len(like_name)) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new yield new
def search_in_module(inference_state, module_context, names, wanted_names, wanted_type, complete=False, fuzzy=False, ignore_imports=False, convert=False): for s in wanted_names[:-1]: new_names = [] for n in names: if s == n.string_name: if n.tree_name is not None and n.api_type == 'module' \ and ignore_imports: continue new_names += complete_trailer(module_context, n.infer()) debug.dbg('dot lookup on search %s from %s', new_names, names[:10]) names = new_names last_name = wanted_names[-1].lower() for n in names: string = n.string_name.lower() if complete and helpers.match(string, last_name, fuzzy=fuzzy) \ or not complete and string == last_name: if isinstance(n, SubModuleName): names = [v.name for v in n.infer()] else: names = [n] if convert: names = convert_names(names) for n2 in names: if complete: def_ = classes.Completion( inference_state, n2, stack=None, like_name_length=len(last_name), is_fuzzy=fuzzy, ) else: def_ = classes.Name(inference_state, n2) if not wanted_type or wanted_type == def_.type: yield def_
def filter_names(evaluator, completion_names, stack, like_name): comp_dct = {} if settings.case_insensitive_completion: like_name = like_name.lower() for name in completion_names: string = name.string_name if settings.case_insensitive_completion: string = string.lower() if settings.fuzzy_completion: pattern = '.*'.join(like_name) + '.*' match = re.match(pattern, string) else: match = string.startswith(like_name) if match: new = classes.Completion(evaluator, name, stack, len(like_name)) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new yield new
def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ def get_completions(user_stmt, bs): if isinstance(user_stmt, pr.Import): context = self._user_context.get_context() next(context) # skip the path if next(context) == 'from': # completion is just "import" if before stands from .. return ((k, bs) for k in keywords.keyword_names('import')) return self._simple_complete(path, like) def completion_possible(path): """ The completion logic is kind of complicated, because we strip the last word part. To ignore certain strange patterns with dots, just use regex. """ if re.match('\d+\.\.$|\.{4}$', path): return True # check Ellipsis and float literal `1.` return not re.search(r'^\.|^\d\.$|\.\.$', path) debug.speed('completions start') path = self._user_context.get_path_until_cursor() if not completion_possible(path): return [] path, dot, like = helpers.completion_parts(path) user_stmt = self._parser.user_stmt_with_whitespace() b = compiled.builtin completions = get_completions(user_stmt, b) if not dot: # add named params for call_sig in self.call_signatures(): # allow protected access, because it's a public API. module = call_sig._definition.get_parent_until() # Compiled modules typically don't allow keyword arguments. if not isinstance(module, compiled.CompiledObject): for p in call_sig.params: # Allow access on _definition here, because it's a # public API and we don't want to make the internal # Name object public. if p._definition.stars == 0: # no *args/**kwargs completions.append((p._definition.get_name(), p)) if not path and not isinstance(user_stmt, pr.Import): # add keywords completions += ((k, b) for k in keywords.keyword_names(all=True)) needs_dot = not dot and path comps = [] comp_dct = {} for c, s in set(completions): n = str(c.names[-1]) if settings.case_insensitive_completion \ and n.lower().startswith(like.lower()) \ or n.startswith(like): if not filter_private_variable( s, user_stmt or self._parser.user_scope(), n): new = classes.Completion(self._evaluator, c, needs_dot, len(like), s) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new comps.append(new) debug.speed('completions end') return sorted( comps, key=lambda x: (x.name.startswith('__'), x.name.startswith('_'), x.name.lower()))
def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ def get_completions(user_stmt, bs): # TODO this closure is ugly. it also doesn't work with # simple_complete (used for Interpreter), somehow redo. module = self._evaluator.wrap(self._parser.module()) names, level, only_modules, unfinished_dotted = \ helpers.check_error_statements(module, self._pos) completion_names = [] if names is not None: imp_names = tuple( str(n) for n in names if n.end_pos < self._pos) i = imports.Importer(self._evaluator, imp_names, module, level) completion_names = i.completion_names(self._evaluator, only_modules) # TODO this paragraph is necessary, but not sure it works. context = self._user_context.get_context() if not next(context).startswith('.'): # skip the path if next(context) == 'from': # completion is just "import" if before stands from .. if unfinished_dotted: return completion_names else: return set([keywords.keyword('import').name]) if isinstance(user_stmt, tree.Import): module = self._parser.module() completion_names += imports.completion_names( self._evaluator, user_stmt, self._pos) return completion_names if names is None and not isinstance(user_stmt, tree.Import): if not path and not dot: # add keywords 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) return completion_names debug.speed('completions start') path = self._user_context.get_path_until_cursor() # Dots following an int are not the start of a completion but a float # literal. if re.search(r'^\d\.$', path): return [] path, dot, like = helpers.completion_parts(path) user_stmt = self._parser.user_stmt_with_whitespace() b = compiled.builtin completion_names = get_completions(user_stmt, b) if not dot: # add named params for call_sig in self.call_signatures(): # Allow protected access, because it's a public API. module = call_sig._name.get_parent_until() # Compiled modules typically don't allow keyword arguments. if not isinstance(module, compiled.CompiledObject): for p in call_sig.params: # Allow access on _definition here, because it's a # public API and we don't want to make the internal # Name object public. if p._definition.stars == 0: # no *args/**kwargs completion_names.append(p._name) needs_dot = not dot and path comps = [] comp_dct = {} for c in set(completion_names): n = str(c) if settings.case_insensitive_completion \ and n.lower().startswith(like.lower()) \ or n.startswith(like): if isinstance(c.parent, (tree.Function, tree.Class)): # TODO I think this is a hack. It should be an # er.Function/er.Class before that. c = self._evaluator.wrap(c.parent).name new = classes.Completion(self._evaluator, c, needs_dot, len(like)) k = (new.name, new.complete) # key if k in comp_dct and settings.no_completion_duplicates: comp_dct[k]._same_name_completions.append(new) else: comp_dct[k] = new comps.append(new) debug.speed('completions end') return sorted( comps, key=lambda x: (x.name.startswith('__'), x.name.startswith('_'), x.name.lower()))