Source code for latch_cli.services.cp.autocomplete
import os
import re
from functools import lru_cache
from pathlib import Path
from typing import List
import click
import click.shell_completion as sc
from latch_cli.services.cp.utils import (
_get_immediate_children_of_node,
_get_known_domains_for_account,
)
cache = lru_cache(maxsize=None)
completion_type = re.compile(
r"""
^
(latch)?
:/?/?
(?P<domain>[^/]*)
(
(?P<parent>(/[^/]*)*)?
(?P<path>/[^/]*)
)?
$
""",
re.VERBOSE,
)
[docs]def complete(
ctx: click.Context,
param: click.Argument,
incomplete: str,
allow_local: bool = True,
) -> List[sc.CompletionItem]:
match = completion_type.match(incomplete)
if match is None:
if not allow_local:
return []
return _complete_local_path(incomplete)
elif match["path"] is None or len(match["path"]) == 0:
return _complete_domain(match)
else:
return _complete_remote_path(match)
[docs]def remote_complete(
ctx: click.Context,
param: click.Argument,
incomplete: str,
):
return complete(ctx, param, incomplete, allow_local=False)
@cache
def _complete_local_path(incomplete: str) -> List[sc.CompletionItem]:
# todo(maximsmol): bash needs this, zsh probably needs the real thing
# return [sc.CompletionItem("", type="file")]
if incomplete == "":
parent = Path.cwd()
stub = ""
else:
p = Path(incomplete).resolve()
parent = p.parent
stub = p.name
res: List[sc.CompletionItem] = []
for sub_path in parent.iterdir():
if not sub_path.name.startswith(stub):
continue
rel_path = os.path.relpath(sub_path)
typ = "file" if sub_path.is_file() else "dir"
res.append(sc.CompletionItem(rel_path, type=typ))
return res
@cache
def _complete_remote_path(match: re.Match) -> List[sc.CompletionItem]:
domain = match["domain"]
parent = match["parent"]
path = match["path"][1:]
parent = f"://{domain}{parent}"
if match[0].startswith("latch"):
parent = f"latch{parent}"
parent_path = parent
if not parent_path.startswith("latch"):
parent_path = f"latch{parent_path}"
children = _get_immediate_children_of_node(parent_path)
res: List[sc.CompletionItem] = []
for child in children:
if not child.startswith(path):
continue
res.append(sc.CompletionItem(f"{parent}/{child}"))
return res
@cache
def _complete_domain(match: re.Match) -> List[sc.CompletionItem]:
stub = match["domain"]
res: List[sc.CompletionItem] = []
for d in _get_known_domains_for_account():
x = f"://{d}/"
if not d.startswith(stub):
continue
if match[0].startswith("latch"):
x = f"latch{x}"
res.append(sc.CompletionItem(x))
return res