From 7f0fd2bb6de504541f9c9fa8eaf5126087ae81a3 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Sat, 21 Dec 2024 11:06:19 -0400 Subject: Rewrite mktags.sh to MakeTags.py This makes tags for all third-party dependencies, and uses universal-ctags instead of fast-tags. It's so nice having tags for /everything/. It's also pretty fast, and caches the result for third-party deps anyway. Oh yeah this also added an explicit list of third-party Python packages. I need to filter the entire pkgset down to just the ones I'm using in order to make tags for them. --- Biz/Ide/MakeTags.py | 105 ++++++++++++++++++++++++++++++++++++++++++++ Biz/Ide/hooks/post-checkout | 8 ++-- Biz/Ide/mktags.sh | 41 ----------------- 3 files changed, 110 insertions(+), 44 deletions(-) create mode 100755 Biz/Ide/MakeTags.py delete mode 100755 Biz/Ide/mktags.sh (limited to 'Biz/Ide') diff --git a/Biz/Ide/MakeTags.py b/Biz/Ide/MakeTags.py new file mode 100755 index 0000000..add07c0 --- /dev/null +++ b/Biz/Ide/MakeTags.py @@ -0,0 +1,105 @@ +#!/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() diff --git a/Biz/Ide/hooks/post-checkout b/Biz/Ide/hooks/post-checkout index 72ec5a7..4f58c74 100755 --- a/Biz/Ide/hooks/post-checkout +++ b/Biz/Ide/hooks/post-checkout @@ -1,16 +1,18 @@ #!/usr/bin/env bash set -e -mktags=${CODEROOT:?}/Biz/Ide/mktags.sh +function MakeTags { + ${CODEROOT:?}/Biz/Ide/MakeTags.py +} old=$1 new=$2 # filter out only the changed haskell files mapfile -t changed < <(git diff --diff-filter=d --name-only "$old" "$new" -- '*.hs') if [[ ! -r tags ]] || [[ ! -r TAGS ]] then - $mktags "$CODEROOT"/**/* + MakeTags "$CODEROOT"/**/* elif [[ ${#changed[@]} -gt 0 ]] then - $mktags "${changed[@]}" + MakeTags "${changed[@]}" fi ## START BRANCHLESS CONFIG diff --git a/Biz/Ide/mktags.sh b/Biz/Ide/mktags.sh deleted file mode 100755 index aae1505..0000000 --- a/Biz/Ide/mktags.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# -# script to generate tags automatically if there are none. -# - set -euo pipefail - files="$*" - vimtags=${CODEROOT:?}/tags - emacstags=${CODEROOT:?}/TAGS -# - if [[ ! -r $emacstags ]]; then - echo Generating emacs TAGS from scratch... - fast-tags -e -R "${CODEROOT:?}" - ctags -e \ - --append=yes \ - --recurse=yes \ - --exclude="$CODEROOT/_/*" \ - "${CODEROOT:?}" - else - fast-tags -e "$files" - ctags -e \ - --append=yes \ - --exclude="_/*" \ - "$files" - fi -# - if [[ ! -r $vimtags ]]; then - echo Generating vim tags from scratch... - fast-tags -R "${CODEROOT:?}" - ctags \ - --append=yes \ - --recurse=yes \ - --exclude="${CODEROOT:?}/_/*" \ - "${CODEROOT:?}" - else - fast-tags "$files" - ctags \ - --append=yes \ - --exclude="_/*" \ - "$files" - fi -## -- cgit v1.2.3