#!/usr/bin/env python """ Make tags for internal or external code. This should run fast, and be executable with just Python, meaning it does not require a build step. """ # : out maketags # : run universal-ctags import argparse import os import pathlib import subprocess import tarfile import zipfile def main() -> None: """Run ctags on internal or external source code. Raises: ValueError: if CODEROOT is not set ArgumentError: when explicit paths aren't provided """ coderoot = os.environ.get("CODEROOT") if coderoot is None: msg = "CODEROOT not set" raise ValueError(msg) cabsrc = pathlib.Path(coderoot) / "_" / "src" cli = argparse.ArgumentParser() cli.add_argument( "paths", nargs="*", default=".", help="List of paths to run ctags on. Defaults to '.'", ) cli.add_argument( "-x", "--external", action="store_true", help=" ".join([ "Use this when `paths` is a list of external packages,", f"they will be extracted or linked into {cabsrc}", ]), ) args = cli.parse_args() if args.external and args.paths == ".": msg = "requires explicit paths" raise argparse.ArgumentError(argument=args.external, message=msg) if args.external: extract_and_copy(cabsrc, args.paths) ctags(["--recurse=yes"], cwd=cabsrc) else: ctags(["--exclude=*_/*", "--recurse=yes"], cwd=pathlib.Path(coderoot)) def strip_nix_hash(path: str) -> str: """Remove the /nix/store/ and hash prefix from a path.""" hash_len = 33 return path.removeprefix("/nix/store/")[hash_len:] def extract_and_copy(cabsrc: pathlib.Path, paths: list[str]) -> None: """ Extract and copy or link sources. Loop over `paths`, if the path is an archive, extract it into `cabsrc`. If its a directory, just symlink the directory into `cabsrc`. Either way, we end up with a directory full of source trees for running ctags on. """ for path in paths: outpath: pathlib.Path = cabsrc / strip_nix_hash(path) if outpath.exists(): continue if path.endswith(".zip"): out = outpath.with_suffix("") if out.exists(): continue zipfile.ZipFile(path).extractall(out) # noqa: S202 elif path.endswith(".tar.gz"): out = outpath.with_suffix("").with_suffix("") if out.exists(): continue with tarfile.open(path) as tarball: tarball.extractall(out) # noqa: S202 elif pathlib.Path(path).is_dir(): outpath.symlink_to(path) def ctags(args: list[str], cwd: pathlib.Path = pathlib.Path()) -> None: """Call `ctags` with `args` for both emacs and vim.""" os.chdir(cwd) excludes = [ "--exclude=.mypy_cache", "--exclude=.git", "--exclude=.direnv", "--exclude=.ruff_cache", ] subprocess.check_call(["ctags", *excludes, *args]) subprocess.check_call(["ctags", "-e", *excludes, *args]) if __name__ == "__main__": main()