diff --git a/home/dot_vscode/executable_vsext b/home/dot_vscode/executable_vsext new file mode 100644 index 0000000..2735d3f --- /dev/null +++ b/home/dot_vscode/executable_vsext @@ -0,0 +1,155 @@ +#!/usr/bin/env -S viv run rich 'git+https://github.com/usu-dev/usu-py.git' -s + +import argparse +import shlex +import subprocess +import sys +from pathlib import Path + +import usu +from rich.align import Align +from rich.columns import Columns +from rich.console import Console +from rich.panel import Panel +from rich.prompt import Confirm +from rich.table import Table + +console = Console() + + +def run(cmd, capture=True, returncode=False): + result = subprocess.run( + shlex.split(cmd), + stdout=subprocess.PIPE if capture else None, + text=True, + shell=False, + ) + + if returncode: + return result.returncode + elif capture: + return result.stdout.strip() + + +def parse_exts(usu_file): + exts = usu.loads(Path(usu_file).read_text()) + all = [v for ext_list in exts.values() for v in ext_list] + return exts, sorted(all) + + +class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter): + def _format_action(self, action): + parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action) + if action.nargs == argparse.PARSER: + parts = "\n".join(parts.split("\n")[1:]) + return parts + + +def get_args(): + p = argparse.ArgumentParser(formatter_class=SubcommandHelpFormatter) + sp = p.add_subparsers(title="commands", metavar="", dest="command", required=True) + check = sp.add_parser( + "check", + help="check the extensions which have already been installed", + aliases=["c"], + ) + check.add_argument("--verbose", help="show more information", action="store_true") + install = sp.add_parser( + "install", help="install the extensions from the spec", aliases=["i"] + ) + install.add_argument( + "-f", "--force", help="force the installation of exts", action="store_true" + ) + sp.add_parser("remove", help="remove all extra packages", aliases=["r"]) + + p.add_argument( + "-s", + "--spec", + help="mardown spec list (default ./exts.usu)", + default=Path.cwd() / "exts.usu", + ) + + if len(sys.argv) < 2: + p.print_help() + sys.exit(1) + + return p.parse_args() + + +def get_extra_exts(spec, installed): + # sanitize lists + spec = [ext.lower() for ext in spec] + installed = [ext.lower() for ext in installed] + + return [ext for ext in spec if ext not in installed], [ + ext for ext in installed if ext not in spec + ] + + +def code(flag, exts, force=False): + cmd = "code " + " ".join([f"{flag} {ext}" for ext in exts]) + if force: + cmd += " --force" + run(cmd, capture=False) + + +def header(): + console.print( + Panel( + Align("[yellow]VS Code Extensions Installer", align="center"), + style="magenta", + ) + ) + + +def installed_table(head, exts, not_installed): + table = Table(title=head) + table.add_column("extension") + table.add_column("installed?") + for ext in exts: + installed = ext.lower() not in not_installed + table.add_row( + ext, "Y" if installed else "N", style = 'green' if installed else "red" + ) + return table + + +def main(): + args = get_args() + header() + + spec_sections, spec_list = parse_exts(args.spec) + + not_installed, extra_installed = get_extra_exts( + spec_list, run("code --list-extensions").splitlines() + ) + + if args.command == "check" or args.command == "c": + tables = ( + installed_table(head, exts, not_installed) + for head, exts in spec_sections.items() + ) + console.print(Columns(tables)) + if not_installed: + console.print(f"[yellow]missing {len(not_installed)} extensions") + if extra_installed: + console.print(f"[red]{len(extra_installed)} extra extensions installed") + if not args.verbose: + console.print("[dim]run with --verbose to see extra extensions") + else: + console.print(Columns(extra_installed)) + + + elif args.command == "install" or args.command == "i": + console.print("[bold]Installing extensions from spec list") + code("--install-extension", spec_list, args.force) + + elif args.command == "remove" or args.command == "r": + console.print("[red]Removing unspecified extensions") + console.print(Columns(extra_installed)) + if Confirm.ask(f"Remove the above {len(extra_installed)} packages?"): + code("--uninstall-extension", extra_installed) + + +if __name__ == "__main__": + main() diff --git a/home/dot_vscode/exts.usu b/home/dot_vscode/exts.usu new file mode 100644 index 0000000..bd83f6a --- /dev/null +++ b/home/dot_vscode/exts.usu @@ -0,0 +1,59 @@ +# VS Code Extensions + +:general [ + ms-vscode-remote.remote-ssh + ms-vscode-remote.remote-ssh-edit + ms-azuretools.vscode-docker + ms-vscode-remote.remote-containers + ms-vscode.remote-repositories + vscodevim.vim +] + +:misc-tools [ + janisdd.vscode-edit-csv + naumovs.color-highlight + davidhouchin.whitespace-plus + antfu.slidev + antfu.file-nesting + mhutchie.git-graph +] + +:web-dev [ + ritwickdey.liveserver + esbenp.prettier-vscode + astro-build.astro-vscode +] + +:ui [ + johnpapa.vscode-peacock + PKief.material-icon-theme + catppuccin.catppuccin-vsc + catppuccin.catppuccin-vsc-icons +] + +:editing [ + metaseed.metajump + christian-kohler.path-intellisense + aaron-bond.better-comments + streetsidesoftware.code-spell-checker +] + +:python [ + ms-python.python + ms-toolsai.jupyter + quarto.quarto + charliermarsh.ruff +] + +:languages [ + golang.go + bbenoist.nix + antfu.unocss + budparr.language-hugo-vscode + snakemake.snakemake-lang + eww-yuck.yuck + tamasfe.even-better-toml + dlasagno.rasi + nimsaem.nimvscode +] + diff --git a/todo.md b/todo.md index ed6f5ba..c996712 100644 --- a/todo.md +++ b/todo.md @@ -3,4 +3,4 @@ - [ ] make a command to 'forget' files using fzf as selector -- [ ] deal with the abomination that is the ./vscode directory +- [x] deal with the abomination that is the ./vscode directory diff --git a/vscode/spec.md b/vscode/spec.md deleted file mode 100644 index 216396d..0000000 --- a/vscode/spec.md +++ /dev/null @@ -1,60 +0,0 @@ -# VS Code Extensions - -## general -ms-vscode-remote.remote-ssh -ms-vscode-remote.remote-ssh-edit -ms-vscode-remote.remote-wsl -ms-azuretools.vscode-docker -ms-vscode-remote.remote-containers -ms-vscode.remote-repositories -vscodevim.vim - -## misc-tools -Shan.code-settings-sync -janisdd.vscode-edit-csv -naumovs.color-highlight -davidhouchin.whitespace-plus -dlasagno.rasi -antfu.slidev -antfu.file-nesting - -## web-dev -bradlc.vscode-tailwindcss -ritwickdey.liveserver -esbenp.prettier-vscode -antfu.unocss -astro-build.astro-vscode - -## styling -johnpapa.vscode-peacock -PKief.material-icon-theme -Thomaz.preparing -catppuccin.catppuccin-vsc - -## code-help -metaseed.metajump -christian-kohler.path-intellisense -aaron-bond.better-comments -streetsidesoftware.code-spell-checker -eamodio.gitlens -vsls-contrib.gistfs -mhutchie.git-graph - -## python -ms-python.python -ms-python.flake8 -ms-toolsai.jupyter -ms-python.vscode-pylance -ms-toolsai.jupyter-keymap -ms-toolsai.jupyter-renderers -njpwerner.autodocstring -quarto.quarto - -## language-support -budparr.language-hugo-vscode -skellock.just -snakemake.snakemake-lang -voorjaar.windicss-intellisense -eww-yuck.yuck -bungcip.better-toml - diff --git a/vscode/vsext b/vscode/vsext deleted file mode 100755 index 76eae26..0000000 --- a/vscode/vsext +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import shlex -import subprocess -import sys -from pathlib import Path -from shutil import which - - -def is_tool(name): - """Check whether `name` is on PATH and marked as executable.""" - if not which(name): - print(f"ERROR: {name} is not found in your PATH") - sys.exit(1) - - -def run(cmd, capture=True, returncode=False): - result = subprocess.run( - shlex.split(cmd), - stdout=subprocess.PIPE if capture else None, - text=True, - shell=False, - ) - - if returncode: - return result.returncode - elif capture: - return result.stdout.strip() - - -class Gum: - def __init__(self): - is_tool("gum") - - def confirm(self, question): - return not run( - "gum confirm " - "--selected.bold " - "--selected.background 11 " - "--selected.foreground 8 " - f"'{question}'", - returncode=True, - ) - - def color(self, text, color): - return run(f'gum style --foreground {color} "{text}"') - - def header(self, text, color=11): - run( - "gum style " - "--foreground 212 --border-foreground 212 --border rounded " - '--align center --width 30 --margin "1 4" ' - f"'{self.color(text,color)}'", - capture=False, - ) - - def choose(self, options): - if isinstance(options, str): - options = options.split("") - if isinstance(options, list): - options = "".join([f"'{i}'" for i in options]) - return run(f"gum choose {options}") - - def input(self, placeholder, value): - run( - "gum input" + f"--placeholder {placeholder}" - if placeholder - else "" + f"--value {value}" - if value - else "" - ) - - def spinner(self, placeholder, cmd): - run(f"gum spin -s points --title '{placeholder}'" + cmd) - - -gum = Gum() - - -def pprint(text, color): - print(gum.color(text, color)) - - -def parse_exts(mdfile): - - with Path(mdfile).open("r") as f: - md = f.read() - md = md.split("##")[1:] - exts = {} - all = [] - for section in md: - head = section.splitlines()[0].strip() - exts[head] = [] - for line in section.splitlines()[1:]: - if line == "": - continue - exts[head].append(line.strip()) - all.append(line.strip()) - - return exts, sorted(all) - - -class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter): - def _format_action(self, action): - parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action) - if action.nargs == argparse.PARSER: - parts = "\n".join(parts.split("\n")[1:]) - return parts - - -def get_args(): - - p = argparse.ArgumentParser(formatter_class=SubcommandHelpFormatter) - sp = p.add_subparsers(title="commands", metavar="", dest="command",required=True) - check = sp.add_parser( - "check", - help="check the extensions which have already been installed", - aliases = ['c'] - ) - install = sp.add_parser( - "install", - help="install the extensions from the spec", - aliases =['i'] - ) - install.add_argument( - "-f", "--force", help="force the installation of exts", action="store_true" - ) - remove = sp.add_parser( - "remove", - help="remove all extra packages", - aliases = ['r'] - ) - - p.add_argument( - "-s", - "--spec", - help="mardown spec list (default ~/.dotfiles/vscode/spec.md)", - default=Path.home() / ".dotfiles" / "vscode" / "spec.md", - ) - - if len(sys.argv) < 2: - # p.print_usage() - p.print_help() - sys.exit(1) - - return p.parse_args() - - -def get_extra_exts(spec, installed): - # sanitize lists - spec = [ext.lower() for ext in spec] - installed = [ext.lower() for ext in installed] - - return [ext for ext in spec if ext not in installed], [ - ext for ext in installed if ext not in spec - ] - - -def code(flag, exts, force=False): - cmd = "code " + " ".join([f"{flag} {ext}" for ext in exts]) - if force: - cmd +=" --force" - run(cmd, capture=False) - - -def main(): - args = get_args() - gum.header("VS Code Extensions Installer") - - spec_sections, spec_list = parse_exts(args.spec) - - not_installed, extra_installed = get_extra_exts( - spec_list, run("code --list-extensions").splitlines() - ) - - if args.command == "check" or args.command == "c": - - for head, exts in spec_sections.items(): - pprint(f"## {head}", 11) - for ext in exts: - if ext.lower() in not_installed: - pprint(ext, 9) - else: - print(ext) - if extra_installed: - pprint("\nExtra installed extensions:", 11) - print("\n".join(extra_installed)) - - elif args.command == "install" or args.command =='i': - - pprint("Installing extensions from spec list", 11) - code("--install-extension", spec_list, args.force) - - elif args.command == "remove" or args.command == "r": - pprint("Removing extensions I don't know about", 11) - - print("\n".join(extra_installed) + "\n") - - if gum.confirm(f"Remove the above {len(extra_installed)} packages"): - print("bye bye packages") - code("--uninstall-extension", extra_installed) - - -if __name__ == "__main__": - main()