def _env(prefix): if prefix.startswith('$'): key = prefix[1:] return {'$' + k for k in builtins.__xonsh_env__ if get_filter_function()(k, key)} return ()
def _env(prefix): if prefix.startswith("$"): key = prefix[1:] return { "$" + k for k in builtins.__xonsh__.env if get_filter_function()(k, key) } return ()
def _complete_python(prefix, context: PythonContext): """ Completes based on the contents of the current Python environment, the Python built-ins, and xonsh operators. """ line = context.multiline_code end = context.cursor_index ctx = context.ctx filt = get_filter_function() rtn = set() if ctx is not None: if "." in prefix: rtn |= attr_complete(prefix, ctx, filt) args = python_signature_complete(prefix, line, end, ctx, filt) rtn |= args rtn |= {s for s in ctx if filt(s, prefix)} else: args = () if len(args) == 0: # not in a function call, so we can add non-expression tokens rtn |= {s for s in XONSH_TOKENS if filt(s, prefix)} else: rtn |= {s for s in XONSH_EXPR_TOKENS if filt(s, prefix)} rtn |= {s for s in dir(builtins) if filt(s, prefix)} return rtn
def complete_pip(context: CommandContext): """Completes python's package manager pip""" prefix = context.prefix if context.arg_index == 0 or (not PIP_RE.search(context.args[0].value)): return None filter_func = get_filter_function() if context.arg_index == 2 and context.args[1].value in PIP_LIST_COMMANDS: # `pip show PREFIX` - complete package names try: enc_items = subprocess.check_output( [context.args[0].value, "list"], stderr=subprocess.DEVNULL) except FileNotFoundError: return None packages = (line.split(maxsplit=1)[0] for line in enc_items.decode("utf-8").splitlines()) return { package for package in packages if filter_func(package, prefix) and not PACKAGE_IGNORE_PATTERN.match(package) } if context.arg_index == 1: # `pip PREFIX` - complete pip commands suggestions = { RichCompletion(c, append_space=True) for c in ALL_COMMANDS if filter_func(c, prefix) } return suggestions return None
def complete_pip(context: CommandContext): """Completes python's package manager pip.""" prefix = context.prefix if context.arg_index == 0 or (not PIP_RE.search( context.args[0].value.lower())): return None filter_func = get_filter_function() args = [arg.raw_value for arg in context.args] env = XSH.env.detype() env.update({ "PIP_AUTO_COMPLETE": "1", "COMP_WORDS": " ".join(args), "COMP_CWORD": str(len(context.args)), }) try: proc = subprocess.run( [args[0]], stderr=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, ) except FileNotFoundError: return None if proc.stdout: out = shlex.split(proc.stdout.decode()) for cmp in out: if filter_func(cmp, prefix): yield RichCompletion(cmp, append_space=True) return None
def complete_aliases(command: CommandContext): """Complete any alias that has ``xonsh_complete`` attribute. The said attribute should be a function. The current command context is passed to it. """ if not command.args: return cmd = command.args[0].value if cmd not in XSH.aliases: # only complete aliases return alias = XSH.aliases.get(cmd) # type: ignore completer = getattr(alias, "xonsh_complete", None) if not completer: return if command.suffix: # completing in a middle of a word # (e.g. "completer some<TAB>thing") return possible = completer(command=command, alias=alias) fltr = get_filter_function() for comp in possible: if fltr(comp, command.prefix): yield comp
def complete_from_man(prefix, line, start, end, ctx): """ Completes an option name, based on the contents of the associated man page. """ global OPTIONS, OPTIONS_PATH if OPTIONS is None: datadir = builtins.__xonsh__.env["XONSH_DATA_DIR"] OPTIONS_PATH = os.path.join(datadir, "man_completions_cache") try: with open(OPTIONS_PATH, "rb") as f: OPTIONS = pickle.load(f) except Exception: OPTIONS = {} if not prefix.startswith("-"): return set() cmd = line.split()[0] if cmd not in OPTIONS: try: manpage = subprocess.Popen(["man", cmd], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) # This is a trick to get rid of reverse line feeds text = subprocess.check_output(["col", "-b"], stdin=manpage.stdout) text = text.decode("utf-8") scraped_text = " ".join(SCRAPE_RE.findall(text)) matches = INNER_OPTIONS_RE.findall(scraped_text) OPTIONS[cmd] = matches with open(OPTIONS_PATH, "wb") as f: pickle.dump(OPTIONS, f) except Exception: return set() return {s for s in OPTIONS[cmd] if get_filter_function()(s, prefix)}
def complete_environment_vars(context: CompletionContext): """Completes environment variables.""" if context.command: prefix = context.command.prefix elif context.python: prefix = context.python.prefix else: return None dollar_location = prefix.rfind("$") if dollar_location == -1: return None key = prefix[dollar_location + 1 :] lprefix = len(key) + 1 filter_func = get_filter_function() env = XSH.env return ( RichCompletion( "$" + k, display=f"${k} [{type(v).__name__}]", description=env.get_docs(k).doc, ) for k, v in env.items() if filter_func(k, key) ), lprefix
def _complete_python(prefix, line, start, end, ctx): """ Completes based on the contents of the current Python environment, the Python built-ins, and xonsh operators. """ if line != '': first = line.split()[0] if first in builtins.__xonsh_commands_cache__ and first not in ctx: return set() filt = get_filter_function() rtn = set() if ctx is not None: if '.' in prefix: rtn |= attr_complete(prefix, ctx, filt) args = python_signature_complete(prefix, line, end, ctx, filt) rtn |= args rtn |= {s for s in ctx if filt(s, prefix)} else: args = () if len(args) == 0: # not in a function call, so we can add non-expression tokens rtn |= {s for s in XONSH_TOKENS if filt(s, prefix)} else: rtn |= {s for s in XONSH_EXPR_TOKENS if filt(s, prefix)} rtn |= {s for s in dir(builtins) if filt(s, prefix)} return rtn
def complete_jedi(context: CompletionContext): """Completes python code using Jedi and xonsh operators""" if context.python is None: return None xonsh_execer: XonshSession = builtins.__xonsh__ # type: ignore ctx = context.python.ctx or {} # if this is the first word and it's a known command, don't complete. # taken from xonsh/completers/python.py if context.command and context.command.arg_index != 0: first = context.command.args[0].value if first in xonsh_execer.commands_cache and first not in ctx: # type: ignore return None filter_func = get_filter_function() jedi.settings.case_insensitive_completion = not xonsh_execer.env.get( "CASE_SENSITIVE_COMPLETIONS" ) source = context.python.multiline_code index = context.python.cursor_index row = source.count("\n", 0, index) + 1 column = ( index - source.rfind("\n", 0, index) - 1 ) # will be `index - (-1) - 1` if there's no newline extra_ctx = {"__xonsh__": xonsh_execer} try: extra_ctx["_"] = _ except NameError: pass if JEDI_NEW_API: script = jedi.Interpreter(source, [ctx, extra_ctx]) else: script = jedi.Interpreter(source, [ctx, extra_ctx], line=row, column=column) script_comp = set() try: if JEDI_NEW_API: script_comp = script.complete(row, column) else: script_comp = script.completions() except Exception: pass res = set(create_completion(comp) for comp in script_comp if should_complete(comp)) if index > 0: last_char = source[index - 1] res.update( RichCompletion(t, prefix_len=1) for t in XONSH_SPECIAL_TOKENS if filter_func(t, last_char) ) else: res.update(RichCompletion(t, prefix_len=0) for t in XONSH_SPECIAL_TOKENS) return res
def complete_from_man(prefix, line, start, end, ctx): """ Completes an option name, based on the contents of the associated man page. """ global OPTIONS, OPTIONS_PATH if OPTIONS is None: datadir = builtins.__xonsh__.env["XONSH_DATA_DIR"] OPTIONS_PATH = os.path.join(datadir, "man_completions_cache") try: with open(OPTIONS_PATH, "rb") as f: OPTIONS = pickle.load(f) except Exception: OPTIONS = {} if not prefix.startswith("-"): return set() cmd = line.split()[0] if cmd not in OPTIONS: try: manpage = subprocess.Popen( ["man", cmd], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ) # This is a trick to get rid of reverse line feeds text = subprocess.check_output(["col", "-b"], stdin=manpage.stdout) text = text.decode("utf-8") scraped_text = " ".join(SCRAPE_RE.findall(text)) matches = INNER_OPTIONS_RE.findall(scraped_text) OPTIONS[cmd] = matches with open(OPTIONS_PATH, "wb") as f: pickle.dump(OPTIONS, f) except Exception: return set() return {s for s in OPTIONS[cmd] if get_filter_function()(s, prefix)}
def _add_env(paths, prefix): if prefix.startswith('$'): key = prefix[1:] paths.update({ '$' + k for k in builtins.__xonsh_env__ if get_filter_function()(k, key) })
def complete_jedi(prefix, line, start, end, ctx): """Completes python code using Jedi and xonsh operators""" # if this is the first word and it's a known command, don't complete. # taken from xonsh/completers/python.py if line.lstrip() != "": first = line.split(maxsplit=1)[0] if prefix == first and first in __xonsh__.commands_cache and first not in ctx: return set() filter_func = get_filter_function() jedi.settings.case_insensitive_completion = not __xonsh__.env.get( "CASE_SENSITIVE_COMPLETIONS") if PTK_COMPLETER: # 'is not None' won't work with lazyobject document = PTK_COMPLETER.current_document source = document.text row = document.cursor_position_row + 1 column = document.cursor_position_col else: source = line row = 1 column = end extra_ctx = {"__xonsh__": __xonsh__} try: extra_ctx["_"] = _ except NameError: pass if JEDI_NEW_API: script = jedi.Interpreter(source, [ctx, extra_ctx]) else: script = jedi.Interpreter(source, [ctx, extra_ctx], line=row, column=column) script_comp = set() try: if JEDI_NEW_API: script_comp = script.complete(row, column) else: script_comp = script.completions() except Exception: pass # make sure _* names are completed only when # the user writes the first underscore complete_underscores = prefix.endswith("_") return set( itertools.chain( (create_completion(comp) for comp in script_comp if complete_underscores or not comp.name.startswith("_") or not comp.complete.startswith("_")), (t for t in XONSH_SPECIAL_TOKENS if filter_func(t, prefix)), ))
def complete_command(cmd, line, start, end, ctx): """ Returns a list of valid commands starting with the first argument """ space = " " out = {s + space for s in builtins.__xonsh_commands_cache__ if get_filter_function()(s, cmd)} if xp.ON_WINDOWS: out |= {i for i in xt.executables_in(".") if i.startswith(cmd)} base = os.path.basename(cmd) if os.path.isdir(base): out |= {os.path.join(base, i) for i in xt.executables_in(base) if i.startswith(cmd)} return out
def fish_proc_completer(ctx: CommandContext): """Populate completions using fish shell and remove bash-completer""" args = [arg.value for arg in ctx.args] + [ctx.prefix] line = " ".join(args) args = ["fish", "-c", f"complete -C '{line}'"] env = XSH.env.detype() output = sp.check_output(args, env=env).decode() filter_func = get_filter_function() if output: for line in output.strip().splitlines(keepends=False): comp = create_rich_completion(line) if filter_func(comp, ctx.prefix): yield comp
def complete_python(prefix, line, start, end, ctx): """ Completes based on the contents of the current Python environment, the Python built-ins, and xonsh operators. """ first = line.split()[0] if first in builtins.__xonsh_commands_cache__ and first not in ctx: return set() filt = get_filter_function() rtn = {s for s in XONSH_TOKENS if filt(s, prefix)} if ctx is not None: if '.' in prefix: rtn |= attr_complete(prefix, ctx, filt) rtn |= {s for s in ctx if filt(s, prefix)} rtn |= {s for s in dir(builtins) if filt(s, prefix)} return rtn
def complete_python(prefix, line, start, end, ctx): """ Completes based on the contents of the current Python environment, the Python built-ins, and xonsh operators. """ first = line.split()[0] if first in builtins.__xonsh_commands_cache__ and first not in ctx: return None filt = get_filter_function() rtn = {s for s in XONSH_TOKENS if filt(s, prefix)} if ctx is not None: if '.' in prefix: rtn |= attr_complete(prefix, ctx, filt) rtn |= {s for s in ctx if filt(s, prefix)} rtn |= {s for s in dir(builtins) if filt(s, prefix)} return rtn
def complete_command(cmd, line, start, end, ctx): """ Returns a list of valid commands starting with the first argument """ space = ' ' out = {s + space for s in builtins.__xonsh_commands_cache__ if get_filter_function()(s, cmd)} if ON_WINDOWS: out |= {i for i in executables_in('.') if i.startswith(cmd)} base = os.path.basename(cmd) if os.path.isdir(base): out |= {os.path.join(base, i) for i in executables_in(base) if i.startswith(cmd)} return out
def complete_environment_vars(context: CompletionContext): if context.command: prefix = context.command.prefix elif context.python: prefix = context.python.prefix else: return None dollar_location = prefix.rfind("$") if dollar_location == -1: return None key = prefix[dollar_location + 1:] lprefix = len(key) + 1 filter_func = get_filter_function() env_names = builtins.__xonsh__.env # type: ignore return {"$" + k for k in env_names if filter_func(k, key)}, lprefix
def complete_command(command: CommandContext): """ Returns a list of valid commands starting with the first argument """ cmd = command.prefix out: tp.Set[Completion] = { RichCompletion(s, append_space=True) for s in builtins.__xonsh__.commands_cache # type: ignore if get_filter_function()(s, cmd) } if xp.ON_WINDOWS: out |= {i for i in xt.executables_in(".") if i.startswith(cmd)} base = os.path.basename(cmd) if os.path.isdir(base): out |= { os.path.join(base, i) for i in xt.executables_in(base) if i.startswith(cmd) } return out
def complete_command(command: CommandContext): """ Returns a list of valid commands starting with the first argument """ cmd = command.prefix show_desc = (XSH.env or {}).get("CMD_COMPLETIONS_SHOW_DESC", False) for s, (path, is_alias) in XSH.commands_cache.iter_commands(): if get_filter_function()(s, cmd): kwargs = {} if show_desc: kwargs["description"] = "Alias" if is_alias else path yield RichCompletion(s, append_space=True, **kwargs) if xp.ON_WINDOWS: for i in xt.executables_in("."): if i.startswith(cmd): yield RichCompletion(i, append_space=True) base = os.path.basename(cmd) if os.path.isdir(base): for i in xt.executables_in(base): if i.startswith(cmd): yield RichCompletion(os.path.join(base, i))
def complete_argparser_aliases(command: CommandContext): """Completer for any alias command that has ``argparser`` in ``parser`` attribute""" if not command.args: return cmd = command.args[0].value alias = XSH.aliases.get(cmd) # type: ignore # todo: checking isinstance(alias, ArgParserAlias) fails when amalgamated. # see https://github.com/xonsh/xonsh/pull/4267#discussion_r676066853 if not hasattr(alias, "parser"): return if command.suffix: # completing in a middle of a word # (e.g. "completer some<TAB>thing") return possible = complete_argparser(alias.parser, command=command, alias=alias) fltr = get_filter_function() for comp in possible: if fltr(comp, command.prefix): yield comp
def complete_from_man(context: CommandContext): """ Completes an option name, based on the contents of the associated man page. """ global OPTIONS, OPTIONS_PATH if OPTIONS is None: datadir: str = XSH.env["XONSH_DATA_DIR"] # type: ignore OPTIONS_PATH = os.path.join(datadir, "man_completions_cache") try: with open(OPTIONS_PATH, "rb") as f: OPTIONS = pickle.load(f) except Exception: OPTIONS = {} if context.arg_index == 0 or not context.prefix.startswith("-"): return set() cmd = context.args[0].value if cmd not in OPTIONS: try: manpage = subprocess.Popen(["man", cmd], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) # This is a trick to get rid of reverse line feeds enc_text = subprocess.check_output(["col", "-b"], stdin=manpage.stdout) text = enc_text.decode("utf-8") scraped_text = " ".join(SCRAPE_RE.findall(text)) matches = INNER_OPTIONS_RE.findall(scraped_text) OPTIONS[cmd] = matches with open(tp.cast(str, OPTIONS_PATH), "wb") as f: pickle.dump(OPTIONS, f) except Exception: return set() return { s for s in OPTIONS[cmd] if get_filter_function()(s, context.prefix) }
def complete_module(prefix): return {s for s in sys.modules if get_filter_function()(s, prefix)}
def filter_completions(prefix, completions): filt = get_filter_function() for comp in completions: if filt(comp, prefix): yield comp
def complete_jedi(context: CompletionContext): """Completes python code using Jedi and xonsh operators""" if context.python is None: return None ctx = context.python.ctx or {} # if the first word is a known command (and we're not completing it), don't complete. # taken from xonsh/completers/python.py if context.command and context.command.arg_index != 0: first = context.command.args[0].value if first in XSH.commands_cache and first not in ctx: # type: ignore return None # if we're completing a possible command and the prefix contains a valid path, don't complete. if context.command: path_dir = os.path.dirname(context.command.prefix) if path_dir and os.path.isdir(os.path.expanduser(path_dir)): return None filter_func = get_filter_function() jedi.settings.case_insensitive_completion = not XSH.env.get( "CASE_SENSITIVE_COMPLETIONS") source = context.python.multiline_code index = context.python.cursor_index row = source.count("\n", 0, index) + 1 column = (index - source.rfind("\n", 0, index) - 1 ) # will be `index - (-1) - 1` if there's no newline extra_ctx = {"__xonsh__": XSH} try: extra_ctx["_"] = _ except NameError: pass if JEDI_NEW_API: script = jedi.Interpreter(source, [ctx, extra_ctx]) else: script = jedi.Interpreter(source, [ctx, extra_ctx], line=row, column=column) script_comp = set() try: if JEDI_NEW_API: script_comp = script.complete(row, column) else: script_comp = script.completions() except Exception: pass res = { create_completion(comp) for comp in script_comp if should_complete(comp) } if index > 0: last_char = source[index - 1] res.update( RichCompletion(t, prefix_len=1) for t in XONSH_SPECIAL_TOKENS if filter_func(t, last_char)) else: res.update( RichCompletion(t, prefix_len=0) for t in XONSH_SPECIAL_TOKENS) return res
def generate_completions( completion_context, old_completer_args, trace: bool) -> tp.Iterator[tp.Tuple[Completion, int]]: filter_func = get_filter_function() for name, func in XSH.completers.items(): try: if is_contextual_completer(func): if completion_context is None: continue out = func(completion_context) else: if old_completer_args is None: continue out = func(*old_completer_args) except StopIteration: # completer requested to stop collecting completions break except Exception as e: name = func.__name__ if hasattr(func, "__name__") else str(func) print_exception( f"Completer {name} raises exception when gets " f"old_args={old_completer_args[:-1]} / completion_context={completion_context!r}:\n" f"{type(e)} - {e}") continue completing_contextual_command = (is_contextual_completer(func) and completion_context is not None and completion_context.command is not None) # -- set comp-defaults -- # the default is that the completer function filters out as necessary # we can change that once fuzzy/substring matches are added is_filtered = True custom_lprefix = False prefix = "" if completing_contextual_command: prefix = completion_context.command.prefix elif old_completer_args is not None: prefix = old_completer_args[0] lprefix = len(prefix) if isinstance(out, cabc.Sequence): # update comp-defaults from res, lprefix_filtered = out if isinstance(lprefix_filtered, bool): is_filtered = lprefix_filtered else: lprefix = lprefix_filtered custom_lprefix = True else: res = out if res is None: continue items = [] for comp in res: if (not is_filtered) and (not filter_func(comp, prefix)): continue comp = Completer._format_completion( comp, completion_context, completing_contextual_command, lprefix or 0, custom_lprefix, ) items.append(comp) yield comp if not items: # empty completion continue if trace: print( f"TRACE COMPLETIONS: Got {len(items)} results" f" from {'' if is_exclusive_completer(func) else 'non-'}exclusive completer '{name}':" ) sys.displayhook(items) if is_exclusive_completer(func): # we got completions for an exclusive completer break
def _add_env(paths, prefix): if prefix.startswith("$"): key = prefix[1:] paths.update({"$" + k for k in builtins.__xonsh_env__ if get_filter_function()(k, key)})