diff --git a/maintainers/scripts/pluginupdate.py b/maintainers/scripts/pluginupdate.py new file mode 100644 index 00000000000..79c79c0f093 --- /dev/null +++ b/maintainers/scripts/pluginupdate.py @@ -0,0 +1,526 @@ +# Used by pkgs/misc/vim-plugins/update.py and pkgs/applications/editors/kakoune/plugins/update.py + +# format: +# $ nix run nixpkgs.python3Packages.black -c black update.py +# type-check: +# $ nix run nixpkgs.python3Packages.mypy -c mypy update.py +# linted: +# $ nix run nixpkgs.python3Packages.flake8 -c flake8 --ignore E501,E265 update.py + +import argparse +import functools +import http +import json +import os +import subprocess +import sys +import time +import traceback +import urllib.error +import urllib.parse +import urllib.request +import xml.etree.ElementTree as ET +from datetime import datetime +from functools import wraps +from multiprocessing.dummy import Pool +from pathlib import Path +from typing import Dict, List, Optional, Tuple, Union, Any, Callable +from urllib.parse import urljoin, urlparse +from tempfile import NamedTemporaryFile + +import git + +ATOM_ENTRY = "{http://www.w3.org/2005/Atom}entry" # " vim gets confused here +ATOM_LINK = "{http://www.w3.org/2005/Atom}link" # " +ATOM_UPDATED = "{http://www.w3.org/2005/Atom}updated" # " + + +def retry(ExceptionToCheck: Any, tries: int = 4, delay: float = 3, backoff: float = 2): + """Retry calling the decorated function using an exponential backoff. + http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ + original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry + (BSD licensed) + :param ExceptionToCheck: the exception on which to retry + :param tries: number of times to try (not retry) before giving up + :param delay: initial delay between retries in seconds + :param backoff: backoff multiplier e.g. value of 2 will double the delay + each retry + """ + + def deco_retry(f: Callable) -> Callable: + @wraps(f) + def f_retry(*args: Any, **kwargs: Any) -> Any: + mtries, mdelay = tries, delay + while mtries > 1: + try: + return f(*args, **kwargs) + except ExceptionToCheck as e: + print(f"{str(e)}, Retrying in {mdelay} seconds...") + time.sleep(mdelay) + mtries -= 1 + mdelay *= backoff + return f(*args, **kwargs) + + return f_retry # true decorator + + return deco_retry + + +def make_request(url: str) -> urllib.request.Request: + token = os.getenv("GITHUB_API_TOKEN") + headers = {} + if token is not None: + headers["Authorization"] = f"token {token}" + return urllib.request.Request(url, headers=headers) + + +class Repo: + def __init__( + self, owner: str, name: str, branch: str, alias: Optional[str] + ) -> None: + self.owner = owner + self.name = name + self.branch = branch + self.alias = alias + self.redirect: Dict[str, str] = {} + + def url(self, path: str) -> str: + return urljoin(f"https://github.com/{self.owner}/{self.name}/", path) + + def __repr__(self) -> str: + return f"Repo({self.owner}, {self.name})" + + @retry(urllib.error.URLError, tries=4, delay=3, backoff=2) + def has_submodules(self) -> bool: + try: + req = make_request(self.url(f"blob/{self.branch}/.gitmodules")) + urllib.request.urlopen(req, timeout=10).close() + except urllib.error.HTTPError as e: + if e.code == 404: + return False + else: + raise + return True + + @retry(urllib.error.URLError, tries=4, delay=3, backoff=2) + def latest_commit(self) -> Tuple[str, datetime]: + commit_url = self.url(f"commits/{self.branch}.atom") + commit_req = make_request(commit_url) + with urllib.request.urlopen(commit_req, timeout=10) as req: + self.check_for_redirect(commit_url, req) + xml = req.read() + root = ET.fromstring(xml) + latest_entry = root.find(ATOM_ENTRY) + assert latest_entry is not None, f"No commits found in repository {self}" + commit_link = latest_entry.find(ATOM_LINK) + assert commit_link is not None, f"No link tag found feed entry {xml}" + url = urlparse(commit_link.get("href")) + updated_tag = latest_entry.find(ATOM_UPDATED) + assert ( + updated_tag is not None and updated_tag.text is not None + ), f"No updated tag found feed entry {xml}" + updated = datetime.strptime(updated_tag.text, "%Y-%m-%dT%H:%M:%SZ") + return Path(str(url.path)).name, updated + + def check_for_redirect(self, url: str, req: http.client.HTTPResponse): + response_url = req.geturl() + if url != response_url: + new_owner, new_name = ( + urllib.parse.urlsplit(response_url).path.strip("/").split("/")[:2] + ) + end_line = "\n" if self.alias is None else f" as {self.alias}\n" + plugin_line = "{owner}/{name}" + end_line + + old_plugin = plugin_line.format(owner=self.owner, name=self.name) + new_plugin = plugin_line.format(owner=new_owner, name=new_name) + self.redirect[old_plugin] = new_plugin + + def prefetch_git(self, ref: str) -> str: + data = subprocess.check_output( + ["nix-prefetch-git", "--fetch-submodules", self.url(""), ref] + ) + return json.loads(data)["sha256"] + + def prefetch_github(self, ref: str) -> str: + data = subprocess.check_output( + ["nix-prefetch-url", "--unpack", self.url(f"archive/{ref}.tar.gz")] + ) + return data.strip().decode("utf-8") + + +class Plugin: + def __init__( + self, + name: str, + commit: str, + has_submodules: bool, + sha256: str, + date: Optional[datetime] = None, + ) -> None: + self.name = name + self.commit = commit + self.has_submodules = has_submodules + self.sha256 = sha256 + self.date = date + + @property + def normalized_name(self) -> str: + return self.name.replace(".", "-") + + @property + def version(self) -> str: + assert self.date is not None + return self.date.strftime("%Y-%m-%d") + + def as_json(self) -> Dict[str, str]: + copy = self.__dict__.copy() + del copy["date"] + return copy + + +class Editor: + """The configuration of the update script.""" + + def __init__( + self, + name: str, + root: Path, + get_plugins: str, + generate_nix: Callable[[List[Tuple[str, str, Plugin]], str], None], + default_in: Optional[Path] = None, + default_out: Optional[Path] = None, + deprecated: Optional[Path] = None, + cache_file: Optional[str] = None, + ): + self.name = name + self.root = root + self.get_plugins = get_plugins + self.generate_nix = generate_nix + self.default_in = default_in or root.joinpath(f"{name}-plugin-names") + self.default_out = default_out or root.joinpath("generated.nix") + self.deprecated = deprecated or root.joinpath("deprecated.json") + self.cache_file = cache_file or f"{name}-plugin-cache.json" + + +class CleanEnvironment(object): + def __enter__(self) -> None: + self.old_environ = os.environ.copy() + local_pkgs = str(Path(__file__).parent.parent.parent) + os.environ["NIX_PATH"] = f"localpkgs={local_pkgs}" + self.empty_config = NamedTemporaryFile() + self.empty_config.write(b"{}") + self.empty_config.flush() + os.environ["NIXPKGS_CONFIG"] = self.empty_config.name + + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + os.environ.update(self.old_environ) + self.empty_config.close() + + +def get_current_plugins(editor: Editor) -> List[Plugin]: + with CleanEnvironment(): + out = subprocess.check_output(["nix", "eval", "--json", editor.get_plugins]) + data = json.loads(out) + plugins = [] + for name, attr in data.items(): + p = Plugin(name, attr["rev"], attr["submodules"], attr["sha256"]) + plugins.append(p) + return plugins + + +def prefetch_plugin( + user: str, + repo_name: str, + branch: str, + alias: Optional[str], + cache: "Optional[Cache]" = None, +) -> Tuple[Plugin, Dict[str, str]]: + repo = Repo(user, repo_name, branch, alias) + commit, date = repo.latest_commit() + has_submodules = repo.has_submodules() + cached_plugin = cache[commit] if cache else None + if cached_plugin is not None: + cached_plugin.name = alias or repo_name + cached_plugin.date = date + return cached_plugin, repo.redirect + + print(f"prefetch {user}/{repo_name}") + if has_submodules: + sha256 = repo.prefetch_git(commit) + else: + sha256 = repo.prefetch_github(commit) + + return ( + Plugin(alias or repo_name, commit, has_submodules, sha256, date=date), + repo.redirect, + ) + + +def fetch_plugin_from_pluginline(plugin_line: str) -> Plugin: + plugin, _ = prefetch_plugin(*parse_plugin_line(plugin_line)) + return plugin + + +def print_download_error(plugin: str, ex: Exception): + print(f"{plugin}: {ex}", file=sys.stderr) + ex_traceback = ex.__traceback__ + tb_lines = [ + line.rstrip("\n") + for line in traceback.format_exception(ex.__class__, ex, ex_traceback) + ] + print("\n".join(tb_lines)) + + +def check_results( + results: List[Tuple[str, str, Union[Exception, Plugin], Dict[str, str]]] +) -> Tuple[List[Tuple[str, str, Plugin]], Dict[str, str]]: + failures: List[Tuple[str, Exception]] = [] + plugins = [] + redirects: Dict[str, str] = {} + for (owner, name, result, redirect) in results: + if isinstance(result, Exception): + failures.append((name, result)) + else: + plugins.append((owner, name, result)) + redirects.update(redirect) + + print(f"{len(results) - len(failures)} plugins were checked", end="") + if len(failures) == 0: + print() + return plugins, redirects + else: + print(f", {len(failures)} plugin(s) could not be downloaded:\n") + + for (plugin, exception) in failures: + print_download_error(plugin, exception) + + sys.exit(1) + + +def parse_plugin_line(line: str) -> Tuple[str, str, str, Optional[str]]: + branch = "master" + alias = None + name, repo = line.split("/") + if " as " in repo: + repo, alias = repo.split(" as ") + alias = alias.strip() + if "@" in repo: + repo, branch = repo.split("@") + + return (name.strip(), repo.strip(), branch.strip(), alias) + + +def load_plugin_spec(plugin_file: str) -> List[Tuple[str, str, str, Optional[str]]]: + plugins = [] + with open(plugin_file) as f: + for line in f: + plugin = parse_plugin_line(line) + if not plugin[0]: + msg = f"Invalid repository {line}, must be in the format owner/repo[ as alias]" + print(msg, file=sys.stderr) + sys.exit(1) + plugins.append(plugin) + return plugins + + +def get_cache_path(cache_file_name: str) -> Optional[Path]: + xdg_cache = os.environ.get("XDG_CACHE_HOME", None) + if xdg_cache is None: + home = os.environ.get("HOME", None) + if home is None: + return None + xdg_cache = str(Path(home, ".cache")) + + return Path(xdg_cache, cache_file_name) + + +class Cache: + def __init__(self, initial_plugins: List[Plugin], cache_file_name: str) -> None: + self.cache_file = get_cache_path(cache_file_name) + + downloads = {} + for plugin in initial_plugins: + downloads[plugin.commit] = plugin + downloads.update(self.load()) + self.downloads = downloads + + def load(self) -> Dict[str, Plugin]: + if self.cache_file is None or not self.cache_file.exists(): + return {} + + downloads: Dict[str, Plugin] = {} + with open(self.cache_file) as f: + data = json.load(f) + for attr in data.values(): + p = Plugin( + attr["name"], attr["commit"], attr["has_submodules"], attr["sha256"] + ) + downloads[attr["commit"]] = p + return downloads + + def store(self) -> None: + if self.cache_file is None: + return + + os.makedirs(self.cache_file.parent, exist_ok=True) + with open(self.cache_file, "w+") as f: + data = {} + for name, attr in self.downloads.items(): + data[name] = attr.as_json() + json.dump(data, f, indent=4, sort_keys=True) + + def __getitem__(self, key: str) -> Optional[Plugin]: + return self.downloads.get(key, None) + + def __setitem__(self, key: str, value: Plugin) -> None: + self.downloads[key] = value + + +def prefetch( + args: Tuple[str, str, str, Optional[str]], cache: Cache +) -> Tuple[str, str, Union[Exception, Plugin], dict]: + assert len(args) == 4 + owner, repo, branch, alias = args + try: + plugin, redirect = prefetch_plugin(owner, repo, branch, alias, cache) + cache[plugin.commit] = plugin + return (owner, repo, plugin, redirect) + except Exception as e: + return (owner, repo, e, {}) + + +def rewrite_input( + input_file: Path, + deprecated: Path, + redirects: Dict[str, str] = None, + append: Tuple = (), +): + with open(input_file, "r") as f: + lines = f.readlines() + + lines.extend(append) + + if redirects: + lines = [redirects.get(line, line) for line in lines] + + cur_date_iso = datetime.now().strftime("%Y-%m-%d") + with open(deprecated, "r") as f: + deprecations = json.load(f) + for old, new in redirects.items(): + old_plugin = fetch_plugin_from_pluginline(old) + new_plugin = fetch_plugin_from_pluginline(new) + if old_plugin.normalized_name != new_plugin.normalized_name: + deprecations[old_plugin.normalized_name] = { + "new": new_plugin.normalized_name, + "date": cur_date_iso, + } + with open(deprecated, "w") as f: + json.dump(deprecations, f, indent=4, sort_keys=True) + + lines = sorted(lines, key=str.casefold) + + with open(input_file, "w") as f: + f.writelines(lines) + + +def parse_args(editor: Editor): + parser = argparse.ArgumentParser( + description=( + f"Updates nix derivations for {editor.name} plugins" + f"By default from {editor.default_in} to {editor.default_out}" + ) + ) + parser.add_argument( + "--add", + dest="add_plugins", + default=[], + action="append", + help=f"Plugin to add to {editor.name}Plugins from Github in the form owner/repo", + ) + parser.add_argument( + "--input-names", + "-i", + dest="input_file", + default=editor.default_in, + help="A list of plugins in the form owner/repo", + ) + parser.add_argument( + "--out", + "-o", + dest="outfile", + default=editor.default_out, + help="Filename to save generated nix code", + ) + parser.add_argument( + "--proc", + "-p", + dest="proc", + type=int, + default=30, + help="Number of concurrent processes to spawn.", + ) + return parser.parse_args() + + +def commit(repo: git.Repo, message: str, files: List[Path]) -> None: + repo.index.add([str(f.resolve()) for f in files]) + + if repo.index.diff("HEAD"): + print(f'committing to nixpkgs "{message}"') + repo.index.commit(message) + else: + print("no changes in working tree to commit") + + +def get_update(input_file: str, outfile: str, proc: int, editor: Editor): + cache: Cache = Cache(get_current_plugins(editor), editor.cache_file) + _prefetch = functools.partial(prefetch, cache=cache) + + def update() -> dict: + plugin_names = load_plugin_spec(input_file) + + try: + pool = Pool(processes=proc) + results = pool.map(_prefetch, plugin_names) + finally: + cache.store() + + plugins, redirects = check_results(results) + + editor.generate_nix(plugins, outfile) + + return redirects + + return update + + +def update_plugins(editor: Editor): + """The main entry function of this module. All input arguments are grouped in the `Editor`.""" + + args = parse_args(editor) + nixpkgs_repo = git.Repo(editor.root, search_parent_directories=True) + update = get_update(args.input_file, args.outfile, args.proc, editor) + + redirects = update() + rewrite_input(args.input_file, editor.deprecated, redirects) + commit(nixpkgs_repo, f"{editor.name}Plugins: update", [args.outfile]) + + if redirects: + update() + commit( + nixpkgs_repo, + f"{editor.name}Plugins: resolve github repository redirects", + [args.outfile, args.input_file, editor.deprecated], + ) + + for plugin_line in args.add_plugins: + rewrite_input(args.input_fil, editor.deprecated, append=(plugin_line + "\n",)) + update() + plugin = fetch_plugin_from_pluginline(plugin_line) + commit( + nixpkgs_repo, + "{editor}Plugins.{name}: init at {version}".format( + editor=editor.name, name=plugin.normalized_name, version=plugin.version + ), + [args.outfile, args.input_file], + ) diff --git a/pkgs/applications/editors/kakoune/plugins/aliases.nix b/pkgs/applications/editors/kakoune/plugins/aliases.nix new file mode 100644 index 00000000000..5ff2a03441d --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/aliases.nix @@ -0,0 +1,46 @@ +# Deprecated aliases - for backward compatibility + +lib: overriden: + +with overriden; + +let + # Removing recurseForDerivation prevents derivations of aliased attribute + # set to appear while listing all the packages available. + removeRecurseForDerivations = alias: with lib; + if alias.recurseForDerivations or false then + removeAttrs alias ["recurseForDerivations"] + else alias; + + # Disabling distribution prevents top-level aliases for non-recursed package + # sets from building on Hydra. + removeDistribute = alias: with lib; + if isDerivation alias then + dontDistribute alias + else alias; + + # Make sure that we are not shadowing something from + # all-packages.nix. + checkInPkgs = n: alias: if builtins.hasAttr n overriden + then throw "Alias ${n} is still in kakounePlugins" + else alias; + + mapAliases = aliases: + lib.mapAttrs (n: alias: removeDistribute + (removeRecurseForDerivations + (checkInPkgs n alias))) + aliases; + + deprecations = lib.mapAttrs (old: info: + throw "${old} was renamed to ${info.new} on ${info.date}. Please update to ${info.new}." + ) (builtins.fromJSON (builtins.readFile ./deprecated.json)); + +in +mapAliases ({ + kak-auto-pairs = auto-pairs-kak; # backwards compat, added 2021-01-04 + kak-buffers = kakoune-buffers; # backwards compat, added 2021-01-04 + kak-fzf = fzf-kak; # backwards compat, added 2021-01-04 + kak-powerline = powerline-kak; # backwards compat, added 2021-01-04 + kak-prelude = prelude-kak; # backwards compat, added 2021-01-04 + kak-vertical-selection = kakoune-vertical-selection; # backwards compat, added 2021-01-04 +} // deprecations) diff --git a/pkgs/applications/editors/kakoune/plugins/build-kakoune-plugin.nix b/pkgs/applications/editors/kakoune/plugins/build-kakoune-plugin.nix new file mode 100644 index 00000000000..f35a175312c --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/build-kakoune-plugin.nix @@ -0,0 +1,33 @@ +{ lib, stdenv, rtpPath ? "share/kak/autoload/plugins" }: +rec { + buildKakounePlugin = attrs@{ + name ? "${attrs.pname}-${attrs.version}", + namePrefix ? "kakplugin-", + src, + unpackPhase ? "", + configurePhase ? "", + buildPhase ? "", + preInstall ? "", + postInstall ? "", + path ? lib.getName name, + ... + }: + stdenv.mkDerivation ((builtins.removeAttrs attrs [ "namePrefix" "path" ]) // { + name = namePrefix + name; + + installPhase = '' + runHook preInstall + + target=$out/${rtpPath}/${path} + mkdir -p $out/${rtpPath} + cp -r . $target + + runHook postInstall + ''; + }); + + buildKakounePluginFrom2Nix = attrs: buildKakounePlugin ({ + buildPhase = ":"; + configurePhase = ":"; + } // attrs); +} diff --git a/pkgs/applications/editors/kakoune/plugins/case.kak.nix b/pkgs/applications/editors/kakoune/plugins/case.kak.nix deleted file mode 100644 index 3fa91113bba..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/case.kak.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ lib, stdenv, fetchFromGitLab }: - -stdenv.mkDerivation { - name = "case.kak"; - version = "unstable-2020-04-06"; - - src = fetchFromGitLab { - owner = "FlyingWombat"; - repo = "case.kak"; - rev = "6f1511820aa3abfa118e0f856118adc8113e2185"; - sha256 = "002njrlwgakqgp74wivbppr9qyn57dn4n5bxkr6k6nglk9qndwdp"; - }; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r rc/case.kak $out/share/kak/autoload/plugins - ''; - - meta = with lib; { - description = "Case convention conversion for Kakoune"; - homepage = "https://gitlab.com/FlyingWombat/case.kak"; - license = licenses.unlicense; - maintainers = with maintainers; [ eraserhd ]; - platform = platforms.all; - }; -} - diff --git a/pkgs/applications/editors/kakoune/plugins/default.nix b/pkgs/applications/editors/kakoune/plugins/default.nix index 98b77338b9b..1fe16a4e60c 100644 --- a/pkgs/applications/editors/kakoune/plugins/default.nix +++ b/pkgs/applications/editors/kakoune/plugins/default.nix @@ -1,17 +1,25 @@ -{ pkgs, parinfer-rust, rep, kak-lsp }: +{ callPackage, config, kakouneUtils, lib }: -{ - inherit parinfer-rust rep kak-lsp; +let - case-kak = pkgs.callPackage ./case.kak.nix { }; - kak-ansi = pkgs.callPackage ./kak-ansi.nix { }; - kak-auto-pairs = pkgs.callPackage ./kak-auto-pairs.nix { }; - kak-buffers = pkgs.callPackage ./kak-buffers.nix { }; - kak-fzf = pkgs.callPackage ./kak-fzf.nix { }; - kak-plumb = pkgs.callPackage ./kak-plumb.nix { }; - kak-powerline = pkgs.callPackage ./kak-powerline.nix { }; - kak-prelude = pkgs.callPackage ./kak-prelude.nix { }; - kak-vertical-selection = pkgs.callPackage ./kak-vertical-selection.nix { }; - openscad-kak = pkgs.callPackage ./openscad.kak.nix { }; - quickscope-kak = pkgs.callPackage ./quickscope.kak.nix { }; -} + inherit (kakouneUtils.override {}) buildKakounePluginFrom2Nix; + + plugins = callPackage ./generated.nix { + inherit buildKakounePluginFrom2Nix overrides; + }; + + # TL;DR + # * Add your plugin to ./kakoune-plugin-names + # * run ./update.py + # + # If additional modifications to the build process are required, + # add to ./overrides.nix. + overrides = callPackage ./overrides.nix { + inherit buildKakounePluginFrom2Nix; + }; + + aliases = lib.optionalAttrs (config.allowAliases or true) (import ./aliases.nix lib plugins); + +in + +plugins // aliases diff --git a/pkgs/applications/editors/kakoune/plugins/deprecated.json b/pkgs/applications/editors/kakoune/plugins/deprecated.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/deprecated.json @@ -0,0 +1 @@ +{} diff --git a/pkgs/applications/editors/kakoune/plugins/generated.nix b/pkgs/applications/editors/kakoune/plugins/generated.nix new file mode 100644 index 00000000000..3ea3ea51478 --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/generated.nix @@ -0,0 +1,211 @@ +# This file has been generated by ./pkgs/applications/editors/kakoune/plugins/update.py. Do not edit! +{ lib, buildKakounePluginFrom2Nix, fetchFromGitHub, overrides ? (self: super: {}) }: +let + packages = ( self: +{ + active-window-kak = buildKakounePluginFrom2Nix { + pname = "active-window-kak"; + version = "2020-05-13"; + src = fetchFromGitHub { + owner = "greenfork"; + repo = "active-window.kak"; + rev = "988db69cfbb88bd741d089bb43b0be551693e7c1"; + sha256 = "1fv1cp9q212gamf9z2papl5xcl2w31fpcmbgdzbxcxdl1pvfsqp8"; + }; + meta.homepage = "https://github.com/greenfork/active-window.kak/"; + }; + + auto-pairs-kak = buildKakounePluginFrom2Nix { + pname = "auto-pairs-kak"; + version = "2020-10-04"; + src = fetchFromGitHub { + owner = "alexherbo2"; + repo = "auto-pairs.kak"; + rev = "fd735ec149ef0d9ca5f628a95b1e52858b5afbdc"; + sha256 = "07795kv9njlnp6mckwv141ny2ns6wyf5r0dfjaxh9ngd105zgif1"; + }; + meta.homepage = "https://github.com/alexherbo2/auto-pairs.kak/"; + }; + + connect-kak = buildKakounePluginFrom2Nix { + pname = "connect-kak"; + version = "2021-02-13"; + src = fetchFromGitHub { + owner = "alexherbo2"; + repo = "connect.kak"; + rev = "0858c0e50c6ca6d214fb088f052385a242548e83"; + sha256 = "1w4pwybg3v916hcyc49gz0blygv54ivv81x8fxp44ck0sy98idr3"; + }; + meta.homepage = "https://github.com/alexherbo2/connect.kak/"; + }; + + fzf-kak = buildKakounePluginFrom2Nix { + pname = "fzf-kak"; + version = "2021-01-27"; + src = fetchFromGitHub { + owner = "andreyorst"; + repo = "fzf.kak"; + rev = "e10de405e2a98e910d0808059200d206ba80f865"; + sha256 = "1hbsm1k8k0cgv7gxqicvnl22n2lb1plhkanniggk694gll22lq68"; + }; + meta.homepage = "https://github.com/andreyorst/fzf.kak/"; + }; + + kakoune-buffer-switcher = buildKakounePluginFrom2Nix { + pname = "kakoune-buffer-switcher"; + version = "2020-12-27"; + src = fetchFromGitHub { + owner = "occivink"; + repo = "kakoune-buffer-switcher"; + rev = "6a27c45db87a23070c34fab36d2f8d812cd002a6"; + sha256 = "1rmwy317908v8p54806m721bpzm8sgygb9abri34537ka6r05y5j"; + }; + meta.homepage = "https://github.com/occivink/kakoune-buffer-switcher/"; + }; + + kakoune-buffers = buildKakounePluginFrom2Nix { + pname = "kakoune-buffers"; + version = "2020-06-11"; + src = fetchFromGitHub { + owner = "Delapouite"; + repo = "kakoune-buffers"; + rev = "67959fbad727ba8470fe8cd6361169560f4fb532"; + sha256 = "09prhzz4yzf6ryw0npd1gpcfp77681vgawpp1ilfvbf25xgbbz33"; + }; + meta.homepage = "https://github.com/Delapouite/kakoune-buffers/"; + }; + + kakoune-easymotion = buildKakounePluginFrom2Nix { + pname = "kakoune-easymotion"; + version = "2020-03-09"; + src = fetchFromGitHub { + owner = "danr"; + repo = "kakoune-easymotion"; + rev = "0ca75450023a149efc70e8e383e459b571355c70"; + sha256 = "15czvl0qj2k767pysr6xk2v31mkhvcbmv76xs2a8yrslchms70b5"; + }; + meta.homepage = "https://github.com/danr/kakoune-easymotion/"; + }; + + kakoune-extra-filetypes = buildKakounePluginFrom2Nix { + pname = "kakoune-extra-filetypes"; + version = "2021-01-15"; + src = fetchFromGitHub { + owner = "kakoune-editor"; + repo = "kakoune-extra-filetypes"; + rev = "c6f8aaccd8c9cd6b487964c8943416e21fbe7c18"; + sha256 = "1vkff8xbycfgxv8x09cvc79qcg5fdzn2x77mbmifmkq236khrwrg"; + }; + meta.homepage = "https://github.com/kakoune-editor/kakoune-extra-filetypes/"; + }; + + kakoune-rainbow = buildKakounePluginFrom2Nix { + pname = "kakoune-rainbow"; + version = "2020-09-01"; + src = fetchFromGitHub { + owner = "listentolist"; + repo = "kakoune-rainbow"; + rev = "d09103e8d268cf4621215bf162a0244c9482be3c"; + sha256 = "1i3id7xw0j4z1a14mscr68ckpgvcwsjpl86lr864wy7w7qcmblx6"; + }; + meta.homepage = "https://github.com/listentolist/kakoune-rainbow/"; + }; + + kakoune-registers = buildKakounePluginFrom2Nix { + pname = "kakoune-registers"; + version = "2020-06-19"; + src = fetchFromGitHub { + owner = "Delapouite"; + repo = "kakoune-registers"; + rev = "9531947baecd83c1d4c3bea0adf10f4462f1e120"; + sha256 = "08v9ndghh7wvr8rsrqm05gksk9ai5vnwvw9gwqasbppb48cv4a8c"; + }; + meta.homepage = "https://github.com/Delapouite/kakoune-registers/"; + }; + + kakoune-vertical-selection = buildKakounePluginFrom2Nix { + pname = "kakoune-vertical-selection"; + version = "2019-04-11"; + src = fetchFromGitHub { + owner = "occivink"; + repo = "kakoune-vertical-selection"; + rev = "c420f8b867ce47375fac303886e31623669a42b7"; + sha256 = "13jdyd2j45wvgqvxdzw9zww14ly93bqjb6700zzxj7mkbiff6wsb"; + }; + meta.homepage = "https://github.com/occivink/kakoune-vertical-selection/"; + }; + + openscad-kak = buildKakounePluginFrom2Nix { + pname = "openscad-kak"; + version = "2020-12-10"; + src = fetchFromGitHub { + owner = "mayjs"; + repo = "openscad.kak"; + rev = "ba51bbdcd96ccf94bb9239bef1481b6f37125849"; + sha256 = "15dybd6dnnwla6mj8sw83nwd62para1syxzifznl6rz6kp8vqjjj"; + }; + meta.homepage = "https://github.com/mayjs/openscad.kak/"; + }; + + powerline-kak = buildKakounePluginFrom2Nix { + pname = "powerline-kak"; + version = "2021-02-15"; + src = fetchFromGitHub { + owner = "jdugan6240"; + repo = "powerline.kak"; + rev = "322a760daa099d519ff50d14c29b27f3e2af00d1"; + sha256 = "0mb8f8p6g75p05ifp45i0gbq2mib8c8giz7r1xfd0yrwspp4aksc"; + }; + meta.homepage = "https://github.com/jdugan6240/powerline.kak/"; + }; + + prelude-kak = buildKakounePluginFrom2Nix { + pname = "prelude-kak"; + version = "2020-09-06"; + src = fetchFromGitHub { + owner = "alexherbo2"; + repo = "prelude.kak"; + rev = "f1e0f4d5cb62a36924e3f8ba6824d6aed8c19d23"; + sha256 = "1pncr8azqvl2z9yvzhc68p1s9fld8cvak8yz88zgrp5ypx2cxl8c"; + }; + meta.homepage = "https://github.com/alexherbo2/prelude.kak/"; + }; + + replace-mode-kak = buildKakounePluginFrom2Nix { + pname = "replace-mode-kak"; + version = "2020-10-07"; + src = fetchFromGitHub { + owner = "alexherbo2"; + repo = "replace-mode.kak"; + rev = "5f4c73cdbaf5aeb964ee35ad4b9081b233af90c0"; + sha256 = "1cmylx99bm7jwfb4hclb69sdc4n8f29ssyy2byjiw53ni9rnc8q0"; + }; + meta.homepage = "https://github.com/alexherbo2/replace-mode.kak/"; + }; + + sleuth-kak = buildKakounePluginFrom2Nix { + pname = "sleuth-kak"; + version = "2020-11-06"; + src = fetchFromGitHub { + owner = "alexherbo2"; + repo = "sleuth.kak"; + rev = "911db8bd208ad0d289b8fa15a2ac665ff39bd6bd"; + sha256 = "0g41c0038fpmihqva71xl4vfbmvsp13i47gp6fnmaikajpynzc51"; + }; + meta.homepage = "https://github.com/alexherbo2/sleuth.kak/"; + }; + + tabs-kak = buildKakounePluginFrom2Nix { + pname = "tabs-kak"; + version = "2021-02-16"; + src = fetchFromGitHub { + owner = "enricozb"; + repo = "tabs.kak"; + rev = "1aaa8cd89e404cbbd76d44ff8089de0951612fbf"; + sha256 = "0dfz6j6yxl65jbh4xvpiy2abr2sdjyalynzhl28y7l1gzqv4ni3j"; + }; + meta.homepage = "https://github.com/enricozb/tabs.kak/"; + }; + +}); +in lib.fix' (lib.extends overrides packages) diff --git a/pkgs/applications/editors/kakoune/plugins/kak-ansi.nix b/pkgs/applications/editors/kakoune/plugins/kak-ansi.nix deleted file mode 100644 index f34ee6d2999..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-ansi.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ lib, stdenv, fetchFromGitHub }: - -stdenv.mkDerivation rec { - name = "kak-ansi"; - version = "0.2.1"; - - src = fetchFromGitHub { - owner = "eraserhd"; - repo = "kak-ansi"; - rev = "v${version}"; - sha256 = "0ddjih8hfyf6s4g7y46p1355kklaw1ydzzh61141i0r45wyb2d0d"; - }; - - installPhase = '' - mkdir -p $out/bin $out/share/kak/autoload/plugins/ - cp kak-ansi-filter $out/bin/ - # Hard-code path of filter and don't try to build when Kakoune boots - sed ' - /^declare-option.* ansi_filter /i\ -declare-option -hidden str ansi_filter %{'"$out"'/bin/kak-ansi-filter} - /^declare-option.* ansi_filter /,/^}/d - ' rc/ansi.kak >$out/share/kak/autoload/plugins/ansi.kak - ''; - - meta = with lib; { - description = "Kakoune support for rendering ANSI code"; - homepage = "https://github.com/eraserhd/kak-ansi"; - license = licenses.unlicense; - maintainers = with maintainers; [ eraserhd ]; - platforms = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-auto-pairs.nix b/pkgs/applications/editors/kakoune/plugins/kak-auto-pairs.nix deleted file mode 100644 index 36842f999a5..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-auto-pairs.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ lib, stdenv, fetchFromGitHub }: -stdenv.mkDerivation { - name = "kak-auto-pairs"; - version = "2020-07-14"; - src = fetchFromGitHub { - owner = "alexherbo2"; - repo = "auto-pairs.kak"; - rev = "5b4b3b723c34c8b7f40cee60868204974349bf9f"; - sha256 = "1wgrv03f1lkzflbbaz8n23glij5rvfxf8pcqysd668mbx1hcrk9i"; - }; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r rc $out/share/kak/autoload/plugins/auto-pairs - ''; - - meta = with lib; - { description = "Kakoune extension to enable automatic closing of pairs"; - homepage = "https://github.com/alexherbo2/auto-pairs.kak"; - license = licenses.unlicense; - maintainers = with maintainers; [ nrdxp ]; - platform = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-buffers.nix b/pkgs/applications/editors/kakoune/plugins/kak-buffers.nix deleted file mode 100644 index 7075ae3ef0a..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-buffers.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ stdenv, fetchFromGitHub, lib }: -stdenv.mkDerivation { - name = "kak-buffers"; - version = "2019-04-03"; - src = fetchFromGitHub { - owner = "Delapouite"; - repo = "kakoune-buffers"; - rev = "3b35b23ac2be661a37c085d34dd04d066450f757"; - sha256 = "0f3g0v1sjinii3ig9753jjj35v2km4h9bcfw9xgzwz8b10d75bax"; - }; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r buffers.kak $out/share/kak/autoload/plugins - ''; - - meta = with lib; - { description = "Ease navigation between opened buffers in Kakoune"; - homepage = "https://github.com/Delapouite/kakoune-buffers"; - license = licenses.mit; - maintainers = with maintainers; [ nrdxp ]; - platform = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-fzf.nix b/pkgs/applications/editors/kakoune/plugins/kak-fzf.nix deleted file mode 100644 index b52584b4456..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-fzf.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ lib, stdenv, fetchFromGitHub, fzf }: - -assert lib.asserts.assertOneOf "fzf" fzf.pname [ "fzf" "skim" ]; - -stdenv.mkDerivation { - name = "kak-fzf"; - version = "2020-07-26"; - - src = fetchFromGitHub { - owner = "andreyorst"; - repo = "fzf.kak"; - rev = "f23daa698ad95493fbd675ae153e3cac13ef34e9"; - hash = "sha256-BfXHTJ371ThOizMI/4BAbdJoaltGSP586hz4HqX1KWA="; - }; - - configurePhase = '' - if [[ -x "${fzf}/bin/fzf" ]]; then - fzfImpl='${fzf}/bin/fzf' - else - fzfImpl='${fzf}/bin/sk' - fi - - substituteInPlace rc/fzf.kak \ - --replace \'fzf\' \'"$fzfImpl"\' - ''; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r rc $out/share/kak/autoload/plugins/fzf - ''; - - meta = with lib; - { description = "Kakoune plugin that brings integration with fzf"; - homepage = "https://github.com/andreyorst/fzf.kak"; - license = licenses.mit; - maintainers = with maintainers; [ nrdxp ]; - platform = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-plumb.nix b/pkgs/applications/editors/kakoune/plugins/kak-plumb.nix deleted file mode 100644 index 0c59ca9a08f..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-plumb.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ lib, stdenv, fetchFromGitHub, kakoune-unwrapped, plan9port, ... }: - -stdenv.mkDerivation rec { - pname = "kak-plumb"; - version = "0.1.1"; - - src = fetchFromGitHub { - owner = "eraserhd"; - repo = "kak-plumb"; - rev = "v${version}"; - sha256 = "1rz6pr786slnf1a78m3sj09axr4d2lb5rg7sfa4mfg1zcjh06ps6"; - }; - - installPhase = '' - mkdir -p $out/bin $out/share/kak/autoload/plugins/ - substitute rc/plumb.kak $out/share/kak/autoload/plugins/plumb.kak \ - --replace '9 plumb' '${plan9port}/bin/9 plumb' - substitute edit-client $out/bin/edit-client \ - --replace '9 9p' '${plan9port}/bin/9 9p' \ - --replace 'kak -p' '${kakoune-unwrapped}/bin/kak -p' - chmod +x $out/bin/edit-client - ''; - - meta = with lib; { - description = "Kakoune integration with the Plan 9 plumber"; - homepage = "https://github.com/eraserhd/kak-plumb"; - license = licenses.unlicense; - maintainers = with maintainers; [ eraserhd ]; - platforms = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-powerline.nix b/pkgs/applications/editors/kakoune/plugins/kak-powerline.nix deleted file mode 100644 index 318ae2958b6..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-powerline.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ stdenv, git, fetchFromGitHub, lib }: -stdenv.mkDerivation { - name = "kak-powerline"; - version = "2020-08-22"; - src = fetchFromGitHub { - owner = "jdugan6240"; - repo = "powerline.kak"; - rev = "d641b2cd8024f872bcda23f9256e7aff36da02ae"; - sha256 = "65948f5ef3ab2f46f6d186ad752665c251d887631d439949decc2654a67958a4"; - }; - - configurePhase = '' - substituteInPlace rc/modules/git.kak \ - --replace \'git\' \'${git}/bin/git\' - ''; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r rc $out/share/kak/autoload/plugins/powerline - ''; - - meta = with lib; - { description = "Kakoune modeline, but with passion"; - homepage = "https://github.com/jdugan6240/powerline.kak"; - license = licenses.mit; - maintainers = with maintainers; [ nrdxp ]; - platform = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-prelude.nix b/pkgs/applications/editors/kakoune/plugins/kak-prelude.nix deleted file mode 100644 index e6f06f4b8e6..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-prelude.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ lib, stdenv, fetchFromGitHub }: -stdenv.mkDerivation { - name = "kak-prelude"; - version = "2020-06-09"; - - src = fetchFromGitHub { - owner = "alexherbo2"; - repo = "prelude.kak"; - rev = "f1e0f4d5cb62a36924e3f8ba6824d6aed8c19d23"; - sha256 = "1pncr8azqvl2z9yvzhc68p1s9fld8cvak8yz88zgrp5ypx2cxl8c"; - }; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r rc $out/share/kak/autoload/plugins/prelude - ''; - - meta = with lib; - { description = "Prelude of shell blocks for Kakoune."; - homepage = "https://github.com/alexherbo2/prelude.kak"; - license = licenses.unlicense; - maintainers = with maintainers; [ buffet ]; - platform = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kak-vertical-selection.nix b/pkgs/applications/editors/kakoune/plugins/kak-vertical-selection.nix deleted file mode 100644 index 2ef9d44854f..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/kak-vertical-selection.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ stdenv, fetchFromGitHub, lib }: -stdenv.mkDerivation { - name = "kak-vertical-selection"; - version = "2019-04-11"; - src = fetchFromGitHub { - owner = "occivink"; - repo = "kakoune-vertical-selection"; - rev = "c420f8b867ce47375fac303886e31623669a42b7"; - sha256 = "13jdyd2j45wvgqvxdzw9zww14ly93bqjb6700zzxj7mkbiff6wsb"; - }; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins - cp -r vertical-selection.kak $out/share/kak/autoload/plugins - ''; - - meta = with lib; - { description = "Select up and down lines that match the same pattern in Kakoune"; - homepage = "https://github.com/occivink/kakoune-vertical-selection"; - license = licenses.unlicense; - maintainers = with maintainers; [ nrdxp ]; - platform = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/kakoune-plugin-names b/pkgs/applications/editors/kakoune/plugins/kakoune-plugin-names new file mode 100644 index 00000000000..6cd856cb6c2 --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/kakoune-plugin-names @@ -0,0 +1,17 @@ +alexherbo2/auto-pairs.kak +alexherbo2/connect.kak +alexherbo2/prelude.kak +alexherbo2/replace-mode.kak +alexherbo2/sleuth.kak +andreyorst/fzf.kak +danr/kakoune-easymotion +Delapouite/kakoune-buffers +Delapouite/kakoune-registers +enricozb/tabs.kak@main +greenfork/active-window.kak +jdugan6240/powerline.kak +kakoune-editor/kakoune-extra-filetypes +listentolist/kakoune-rainbow +mayjs/openscad.kak +occivink/kakoune-buffer-switcher +occivink/kakoune-vertical-selection diff --git a/pkgs/applications/editors/kakoune/plugins/kakoune-utils.nix b/pkgs/applications/editors/kakoune/plugins/kakoune-utils.nix new file mode 100644 index 00000000000..11a1cc130d6 --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/kakoune-utils.nix @@ -0,0 +1,4 @@ +{ lib, stdenv }: +{ + inherit (import ./build-kakoune-plugin.nix { inherit lib stdenv; }) buildKakounePlugin buildKakounePluginFrom2Nix; +} diff --git a/pkgs/applications/editors/kakoune/plugins/openscad.kak.nix b/pkgs/applications/editors/kakoune/plugins/openscad.kak.nix deleted file mode 100644 index 57c1033ebbd..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/openscad.kak.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ lib, stdenv, fetchFromGitHub }: - -stdenv.mkDerivation { - pname = "openscad.kak"; - version = "unstable-2019-11-08"; - - src = fetchFromGitHub { - owner = "mayjs"; - repo = "openscad.kak"; - rev = "d9143d5e7834e3356b49720664d5647cab9db7cc"; - sha256 = "0j4dqhrn56z77hdalfdxagwz8h6nwr8s9i4w0bs2644k72lsm2ix"; - }; - - installPhase = '' - install -Dm644 rc/openscad.kak -t $out/share/kak/autoload/plugins/ - ''; - - meta = with lib; { - description = "Syntax highlighting for OpenSCAD files"; - homepage = "https://github.com/mayjs/openscad.kak"; - license = licenses.unlicense; - maintainers = with maintainers; [ eraserhd ]; - platforms = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/overrides.nix b/pkgs/applications/editors/kakoune/plugins/overrides.nix new file mode 100644 index 00000000000..d75dcc9ac90 --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/overrides.nix @@ -0,0 +1,161 @@ +{ lib, stdenv, fetchFromGitHub, fetchFromGitLab, fetchgit +, buildKakounePluginFrom2Nix +, kak-lsp, parinfer-rust, rep +, fzf, git, guile, kakoune-unwrapped, lua5_3, plan9port +}: + +self: super: { + inherit kak-lsp parinfer-rust rep; + + case-kak = buildKakounePluginFrom2Nix { + pname = "case-kak"; + version = "2020-04-06"; + src = fetchFromGitLab { + owner = "FlyingWombat"; + repo = "case.kak"; + rev = "6f1511820aa3abfa118e0f856118adc8113e2185"; + sha256 = "002njrlwgakqgp74wivbppr9qyn57dn4n5bxkr6k6nglk9qndwdp"; + }; + meta.homepage = "https://gitlab.com/FlyingWombat/case.kak"; + }; + + fzf-kak = super.fzf-kak.overrideAttrs(oldAttrs: rec { + preFixup = '' + if [[ -x "${fzf}/bin/fzf" ]]; then + fzfImpl='${fzf}/bin/fzf' + else + fzfImpl='${fzf}/bin/sk' + fi + + substituteInPlace $out/share/kak/autoload/plugins/fzf-kak/rc/fzf.kak \ + --replace \'fzf\' \'"$fzfImpl"\' + ''; + }); + + kak-ansi = stdenv.mkDerivation rec { + pname = "kak-ansi"; + version = "0.2.1"; + + src = fetchFromGitHub { + owner = "eraserhd"; + repo = "kak-ansi"; + rev = "v${version}"; + sha256 = "0ddjih8hfyf6s4g7y46p1355kklaw1ydzzh61141i0r45wyb2d0d"; + }; + + installPhase = '' + mkdir -p $out/bin $out/share/kak/autoload/plugins/ + cp kak-ansi-filter $out/bin/ + # Hard-code path of filter and don't try to build when Kakoune boots + sed ' + /^declare-option.* ansi_filter /i\ +declare-option -hidden str ansi_filter %{'"$out"'/bin/kak-ansi-filter} + /^declare-option.* ansi_filter /,/^}/d + ' rc/ansi.kak >$out/share/kak/autoload/plugins/ansi.kak + ''; + + meta = with lib; { + description = "Kakoune support for rendering ANSI code"; + homepage = "https://github.com/eraserhd/kak-ansi"; + license = licenses.unlicense; + maintainers = with maintainers; [ eraserhd ]; + platforms = platforms.all; + }; + }; + + kak-plumb = stdenv.mkDerivation rec { + pname = "kak-plumb"; + version = "0.1.1"; + + src = fetchFromGitHub { + owner = "eraserhd"; + repo = "kak-plumb"; + rev = "v${version}"; + sha256 = "1rz6pr786slnf1a78m3sj09axr4d2lb5rg7sfa4mfg1zcjh06ps6"; + }; + + installPhase = '' + mkdir -p $out/bin $out/share/kak/autoload/plugins/ + substitute rc/plumb.kak $out/share/kak/autoload/plugins/plumb.kak \ + --replace '9 plumb' '${plan9port}/bin/9 plumb' + substitute edit-client $out/bin/edit-client \ + --replace '9 9p' '${plan9port}/bin/9 9p' \ + --replace 'kak -p' '${kakoune-unwrapped}/bin/kak -p' + chmod +x $out/bin/edit-client + ''; + + meta = with lib; { + description = "Kakoune integration with the Plan 9 plumber"; + homepage = "https://github.com/eraserhd/kak-plumb"; + license = licenses.unlicense; + maintainers = with maintainers; [ eraserhd ]; + platforms = platforms.all; + }; + }; + + kakoune-rainbow = super.kakoune-rainbow.overrideAttrs(oldAttrs: rec { + preFixup = '' + mkdir -p $out/bin + mv $out/share/kak/autoload/plugins/kakoune-rainbow/bin/kak-rainbow.scm $out/bin + substituteInPlace $out/bin/kak-rainbow.scm \ + --replace '/usr/bin/env -S guile' '${guile}/bin/guile' + substituteInPlace $out/share/kak/autoload/plugins/kakoune-rainbow/rainbow.kak \ + --replace '%sh{dirname "$kak_source"}' "'$out'" + ''; + }); + + kakoune-state-save = buildKakounePluginFrom2Nix { + pname = "kakoune-state-save"; + version = "2020-02-09"; + + src = fetchFromGitLab { + owner = "Screwtapello"; + repo = "kakoune-state-save"; + rev = "ab7c0c765326a4a80af78857469ee8c80814c52a"; + sha256 = "AAOCG0TY3G188NnkkwMCSbkkNe487F4gwiFWwG9Yo+A="; + }; + + meta = with lib; { + description = "Help Kakoune save and restore state between sessions"; + homepage = "https://gitlab.com/Screwtapello/kakoune-state-save"; + license = licenses.mit; + maintainers = with maintainers; [ Flakebi ]; + platforms = platforms.all; + }; + }; + + powerline-kak = super.powerline-kak.overrideAttrs(oldAttrs: rec { + preFixup = '' + substituteInPlace $out/share/kak/autoload/plugins/powerline-kak/rc/modules/git.kak \ + --replace ' git ' ' ${git}/bin/git ' + ''; + }); + + quickscope-kak = buildKakounePluginFrom2Nix rec { + pname = "quickscope-kak"; + version = "1.0.0"; + + src = fetchgit { + url = "https://git.sr.ht/~voroskoi/quickscope.kak"; + rev = "v${version}"; + sha256 = "0y1g3zpa2ql8l9rl5i2w84bka8a09kig9nq9zdchaff5pw660mcx"; + }; + + buildInputs = [ lua5_3 ]; + + installPhase = '' + mkdir -p $out/share/kak/autoload/plugins/ + cp quickscope.* $out/share/kak/autoload/plugins/ + # substituteInPlace does not like the pipe + sed -e 's,[|] *lua,|${lua5_3}/bin/lua,' quickscope.kak >$out/share/kak/autoload/plugins/quickscope.kak + ''; + + meta = with lib; { + description = "Highlight f and t jump positions"; + homepage = "https://sr.ht/~voroskoi/quickscope.kak/"; + license = licenses.unlicense; + maintainers = with maintainers; [ eraserhd ]; + platforms = platforms.all; + }; + }; +} diff --git a/pkgs/applications/editors/kakoune/plugins/quickscope.kak.nix b/pkgs/applications/editors/kakoune/plugins/quickscope.kak.nix deleted file mode 100644 index 8e60e17e792..00000000000 --- a/pkgs/applications/editors/kakoune/plugins/quickscope.kak.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ lib, stdenv, fetchgit, lua5_3 }: - -stdenv.mkDerivation rec { - pname = "quickscope-kak"; - version = "1.0.0"; - - src = fetchgit { - url = "https://git.sr.ht/~voroskoi/quickscope.kak"; - rev = "v${version}"; - sha256 = "0y1g3zpa2ql8l9rl5i2w84bka8a09kig9nq9zdchaff5pw660mcx"; - }; - - buildInputs = [ lua5_3 ]; - - installPhase = '' - mkdir -p $out/share/kak/autoload/plugins/ - cp quickscope.* $out/share/kak/autoload/plugins/ - # substituteInPlace does not like the pipe - sed -e 's,[|] *lua,|${lua5_3}/bin/lua,' quickscope.kak >$out/share/kak/autoload/plugins/quickscope.kak - ''; - - meta = with lib; { - description = "Highlight f and t jump positions"; - homepage = "https://sr.ht/~voroskoi/quickscope.kak/"; - license = licenses.unlicense; - maintainers = with maintainers; [ eraserhd ]; - platforms = platforms.all; - }; -} diff --git a/pkgs/applications/editors/kakoune/plugins/update.py b/pkgs/applications/editors/kakoune/plugins/update.py new file mode 100755 index 00000000000..b6a4bfe4f41 --- /dev/null +++ b/pkgs/applications/editors/kakoune/plugins/update.py @@ -0,0 +1,91 @@ +#!/usr/bin/env nix-shell +#!nix-shell -p nix-prefetch-git -p python3 -p python3Packages.GitPython nix -i python3 + +# format: +# $ nix run nixpkgs.python3Packages.black -c black update.py +# type-check: +# $ nix run nixpkgs.python3Packages.mypy -c mypy update.py +# linted: +# $ nix run nixpkgs.python3Packages.flake8 -c flake8 --ignore E501,E265,E402 update.py + +import inspect +import os +import sys +from typing import List, Tuple +from pathlib import Path + +# Import plugin update library from maintainers/scripts/pluginupdate.py +ROOT = Path(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) +sys.path.insert( + 0, os.path.join(ROOT.parent.parent.parent.parent.parent, "maintainers", "scripts") +) +import pluginupdate + +GET_PLUGINS = f"""(with import {{}}; +let + inherit (kakouneUtils.override {{}}) buildKakounePluginFrom2Nix; + generated = callPackage {ROOT}/generated.nix {{ + inherit buildKakounePluginFrom2Nix; + }}; + hasChecksum = value: lib.isAttrs value && lib.hasAttrByPath ["src" "outputHash"] value; + getChecksum = name: value: + if hasChecksum value then {{ + submodules = value.src.fetchSubmodules or false; + sha256 = value.src.outputHash; + rev = value.src.rev; + }} else null; + checksums = lib.mapAttrs getChecksum generated; +in lib.filterAttrs (n: v: v != null) checksums)""" + +HEADER = "# This file has been generated by ./pkgs/applications/editors/kakoune/plugins/update.py. Do not edit!" + + +def generate_nix(plugins: List[Tuple[str, str, pluginupdate.Plugin]], outfile: str): + sorted_plugins = sorted(plugins, key=lambda v: v[2].name.lower()) + + with open(outfile, "w+") as f: + f.write(HEADER) + f.write( + """ +{ lib, buildKakounePluginFrom2Nix, fetchFromGitHub, overrides ? (self: super: {}) }: +let + packages = ( self: +{""" + ) + for owner, repo, plugin in sorted_plugins: + if plugin.has_submodules: + submodule_attr = "\n fetchSubmodules = true;" + else: + submodule_attr = "" + + f.write( + f""" + {plugin.normalized_name} = buildKakounePluginFrom2Nix {{ + pname = "{plugin.normalized_name}"; + version = "{plugin.version}"; + src = fetchFromGitHub {{ + owner = "{owner}"; + repo = "{repo}"; + rev = "{plugin.commit}"; + sha256 = "{plugin.sha256}";{submodule_attr} + }}; + meta.homepage = "https://github.com/{owner}/{repo}/"; + }}; +""" + ) + f.write( + """ +}); +in lib.fix' (lib.extends overrides packages) +""" + ) + print(f"updated {outfile}") + + +def main(): + editor = pluginupdate.Editor("kakoune", ROOT, GET_PLUGINS, generate_nix) + pluginupdate.update_plugins(editor) + + +if __name__ == "__main__": + main() diff --git a/pkgs/misc/vim-plugins/update.py b/pkgs/misc/vim-plugins/update.py index f5d7434fe27..1180b4572d2 100755 --- a/pkgs/misc/vim-plugins/update.py +++ b/pkgs/misc/vim-plugins/update.py @@ -6,180 +6,18 @@ # type-check: # $ nix run nixpkgs.python3Packages.mypy -c mypy update.py # linted: -# $ nix run nixpkgs.python3Packages.flake8 -c flake8 --ignore E501,E265 update.py +# $ nix run nixpkgs.python3Packages.flake8 -c flake8 --ignore E501,E265,E402 update.py -import argparse -import functools -import http -import json +import inspect import os -import subprocess import sys -import time -import traceback -import urllib.error -import urllib.parse -import urllib.request -import xml.etree.ElementTree as ET -from datetime import datetime -from functools import wraps -from multiprocessing.dummy import Pool +from typing import List, Tuple from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union, Any, Callable -from urllib.parse import urljoin, urlparse -from tempfile import NamedTemporaryFile - -import git - -ATOM_ENTRY = "{http://www.w3.org/2005/Atom}entry" # " vim gets confused here -ATOM_LINK = "{http://www.w3.org/2005/Atom}link" # " -ATOM_UPDATED = "{http://www.w3.org/2005/Atom}updated" # " - -ROOT = Path(__file__).parent -DEFAULT_IN = ROOT.joinpath("vim-plugin-names") -DEFAULT_OUT = ROOT.joinpath("generated.nix") -DEPRECATED = ROOT.joinpath("deprecated.json") - -def retry(ExceptionToCheck: Any, tries: int = 4, delay: float = 3, backoff: float = 2): - """Retry calling the decorated function using an exponential backoff. - http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ - original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry - (BSD licensed) - :param ExceptionToCheck: the exception on which to retry - :param tries: number of times to try (not retry) before giving up - :param delay: initial delay between retries in seconds - :param backoff: backoff multiplier e.g. value of 2 will double the delay - each retry - """ - - def deco_retry(f: Callable) -> Callable: - @wraps(f) - def f_retry(*args: Any, **kwargs: Any) -> Any: - mtries, mdelay = tries, delay - while mtries > 1: - try: - return f(*args, **kwargs) - except ExceptionToCheck as e: - print(f"{str(e)}, Retrying in {mdelay} seconds...") - time.sleep(mdelay) - mtries -= 1 - mdelay *= backoff - return f(*args, **kwargs) - - return f_retry # true decorator - - return deco_retry - -def make_request(url: str) -> urllib.request.Request: - token = os.getenv("GITHUB_API_TOKEN") - headers = {} - if token is not None: - headers["Authorization"] = f"token {token}" - return urllib.request.Request(url, headers=headers) - -class Repo: - def __init__( - self, owner: str, name: str, branch: str, alias: Optional[str] - ) -> None: - self.owner = owner - self.name = name - self.branch = branch - self.alias = alias - self.redirect: Dict[str, str] = {} - - def url(self, path: str) -> str: - return urljoin(f"https://github.com/{self.owner}/{self.name}/", path) - - def __repr__(self) -> str: - return f"Repo({self.owner}, {self.name})" - - @retry(urllib.error.URLError, tries=4, delay=3, backoff=2) - def has_submodules(self) -> bool: - try: - req = make_request(self.url(f"blob/{self.branch}/.gitmodules")) - urllib.request.urlopen(req, timeout=10).close() - except urllib.error.HTTPError as e: - if e.code == 404: - return False - else: - raise - return True - - @retry(urllib.error.URLError, tries=4, delay=3, backoff=2) - def latest_commit(self) -> Tuple[str, datetime]: - commit_url = self.url(f"commits/{self.branch}.atom") - commit_req = make_request(commit_url) - with urllib.request.urlopen(commit_req, timeout=10) as req: - self.check_for_redirect(commit_url, req) - xml = req.read() - root = ET.fromstring(xml) - latest_entry = root.find(ATOM_ENTRY) - assert latest_entry is not None, f"No commits found in repository {self}" - commit_link = latest_entry.find(ATOM_LINK) - assert commit_link is not None, f"No link tag found feed entry {xml}" - url = urlparse(commit_link.get("href")) - updated_tag = latest_entry.find(ATOM_UPDATED) - assert ( - updated_tag is not None and updated_tag.text is not None - ), f"No updated tag found feed entry {xml}" - updated = datetime.strptime(updated_tag.text, "%Y-%m-%dT%H:%M:%SZ") - return Path(str(url.path)).name, updated - - def check_for_redirect(self, url: str, req: http.client.HTTPResponse): - response_url = req.geturl() - if url != response_url: - new_owner, new_name = ( - urllib.parse.urlsplit(response_url).path.strip("/").split("/")[:2] - ) - end_line = "\n" if self.alias is None else f" as {self.alias}\n" - plugin_line = "{owner}/{name}" + end_line - - old_plugin = plugin_line.format(owner=self.owner, name=self.name) - new_plugin = plugin_line.format(owner=new_owner, name=new_name) - self.redirect[old_plugin] = new_plugin - - def prefetch_git(self, ref: str) -> str: - data = subprocess.check_output( - ["nix-prefetch-git", "--fetch-submodules", self.url(""), ref] - ) - return json.loads(data)["sha256"] - - def prefetch_github(self, ref: str) -> str: - data = subprocess.check_output( - ["nix-prefetch-url", "--unpack", self.url(f"archive/{ref}.tar.gz")] - ) - return data.strip().decode("utf-8") - - -class Plugin: - def __init__( - self, - name: str, - commit: str, - has_submodules: bool, - sha256: str, - date: Optional[datetime] = None, - ) -> None: - self.name = name - self.commit = commit - self.has_submodules = has_submodules - self.sha256 = sha256 - self.date = date - - @property - def normalized_name(self) -> str: - return self.name.replace(".", "-") - - @property - def version(self) -> str: - assert self.date is not None - return self.date.strftime("%Y-%m-%d") - - def as_json(self) -> Dict[str, str]: - copy = self.__dict__.copy() - del copy["date"] - return copy +# Import plugin update library from maintainers/scripts/pluginupdate.py +ROOT = Path(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) +sys.path.insert(0, os.path.join(ROOT.parent.parent.parent, "maintainers", "scripts")) +import pluginupdate GET_PLUGINS = f"""(with import {{}}; let @@ -197,204 +35,16 @@ let checksums = lib.mapAttrs getChecksum generated; in lib.filterAttrs (n: v: v != null) checksums)""" - -class CleanEnvironment(object): - def __enter__(self) -> None: - self.old_environ = os.environ.copy() - local_pkgs = str(ROOT.joinpath("../../..")) - os.environ["NIX_PATH"] = f"localpkgs={local_pkgs}" - self.empty_config = NamedTemporaryFile() - self.empty_config.write(b"{}") - self.empty_config.flush() - os.environ["NIXPKGS_CONFIG"] = self.empty_config.name - - def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: - os.environ.update(self.old_environ) - self.empty_config.close() - - -def get_current_plugins() -> List[Plugin]: - with CleanEnvironment(): - out = subprocess.check_output(["nix", "eval", "--json", GET_PLUGINS]) - data = json.loads(out) - plugins = [] - for name, attr in data.items(): - p = Plugin(name, attr["rev"], attr["submodules"], attr["sha256"]) - plugins.append(p) - return plugins - - -def prefetch_plugin( - user: str, - repo_name: str, - branch: str, - alias: Optional[str], - cache: "Optional[Cache]" = None, -) -> Tuple[Plugin, Dict[str, str]]: - repo = Repo(user, repo_name, branch, alias) - commit, date = repo.latest_commit() - has_submodules = repo.has_submodules() - cached_plugin = cache[commit] if cache else None - if cached_plugin is not None: - cached_plugin.name = alias or repo_name - cached_plugin.date = date - return cached_plugin, repo.redirect - - print(f"prefetch {user}/{repo_name}") - if has_submodules: - sha256 = repo.prefetch_git(commit) - else: - sha256 = repo.prefetch_github(commit) - - return ( - Plugin(alias or repo_name, commit, has_submodules, sha256, date=date), - repo.redirect, - ) - - -def fetch_plugin_from_pluginline(plugin_line: str) -> Plugin: - plugin, _ = prefetch_plugin(*parse_plugin_line(plugin_line)) - return plugin - - -def print_download_error(plugin: str, ex: Exception): - print(f"{plugin}: {ex}", file=sys.stderr) - ex_traceback = ex.__traceback__ - tb_lines = [ - line.rstrip("\n") - for line in traceback.format_exception(ex.__class__, ex, ex_traceback) - ] - print("\n".join(tb_lines)) - - -def check_results( - results: List[Tuple[str, str, Union[Exception, Plugin], Dict[str, str]]] -) -> Tuple[List[Tuple[str, str, Plugin]], Dict[str, str]]: - failures: List[Tuple[str, Exception]] = [] - plugins = [] - redirects: Dict[str, str] = {} - for (owner, name, result, redirect) in results: - if isinstance(result, Exception): - failures.append((name, result)) - else: - plugins.append((owner, name, result)) - redirects.update(redirect) - - print(f"{len(results) - len(failures)} plugins were checked", end="") - if len(failures) == 0: - print() - return plugins, redirects - else: - print(f", {len(failures)} plugin(s) could not be downloaded:\n") - - for (plugin, exception) in failures: - print_download_error(plugin, exception) - - sys.exit(1) - - -def parse_plugin_line(line: str) -> Tuple[str, str, str, Optional[str]]: - branch = "master" - alias = None - name, repo = line.split("/") - if " as " in repo: - repo, alias = repo.split(" as ") - alias = alias.strip() - if "@" in repo: - repo, branch = repo.split("@") - - return (name.strip(), repo.strip(), branch.strip(), alias) - - -def load_plugin_spec(plugin_file: str) -> List[Tuple[str, str, str, Optional[str]]]: - plugins = [] - with open(plugin_file) as f: - for line in f: - plugin = parse_plugin_line(line) - if not plugin[0]: - msg = f"Invalid repository {line}, must be in the format owner/repo[ as alias]" - print(msg, file=sys.stderr) - sys.exit(1) - plugins.append(plugin) - return plugins - - -def get_cache_path() -> Optional[Path]: - xdg_cache = os.environ.get("XDG_CACHE_HOME", None) - if xdg_cache is None: - home = os.environ.get("HOME", None) - if home is None: - return None - xdg_cache = str(Path(home, ".cache")) - - return Path(xdg_cache, "vim-plugin-cache.json") - - -class Cache: - def __init__(self, initial_plugins: List[Plugin]) -> None: - self.cache_file = get_cache_path() - - downloads = {} - for plugin in initial_plugins: - downloads[plugin.commit] = plugin - downloads.update(self.load()) - self.downloads = downloads - - def load(self) -> Dict[str, Plugin]: - if self.cache_file is None or not self.cache_file.exists(): - return {} - - downloads: Dict[str, Plugin] = {} - with open(self.cache_file) as f: - data = json.load(f) - for attr in data.values(): - p = Plugin( - attr["name"], attr["commit"], attr["has_submodules"], attr["sha256"] - ) - downloads[attr["commit"]] = p - return downloads - - def store(self) -> None: - if self.cache_file is None: - return - - os.makedirs(self.cache_file.parent, exist_ok=True) - with open(self.cache_file, "w+") as f: - data = {} - for name, attr in self.downloads.items(): - data[name] = attr.as_json() - json.dump(data, f, indent=4, sort_keys=True) - - def __getitem__(self, key: str) -> Optional[Plugin]: - return self.downloads.get(key, None) - - def __setitem__(self, key: str, value: Plugin) -> None: - self.downloads[key] = value - - -def prefetch( - args: Tuple[str, str, str, Optional[str]], cache: Cache -) -> Tuple[str, str, Union[Exception, Plugin], dict]: - assert len(args) == 4 - owner, repo, branch, alias = args - try: - plugin, redirect = prefetch_plugin(owner, repo, branch, alias, cache) - cache[plugin.commit] = plugin - return (owner, repo, plugin, redirect) - except Exception as e: - return (owner, repo, e, {}) - - -header = ( +HEADER = ( "# This file has been generated by ./pkgs/misc/vim-plugins/update.py. Do not edit!" ) -def generate_nix(plugins: List[Tuple[str, str, Plugin]], outfile: str): +def generate_nix(plugins: List[Tuple[str, str, pluginupdate.Plugin]], outfile: str): sorted_plugins = sorted(plugins, key=lambda v: v[2].name.lower()) with open(outfile, "w+") as f: - f.write(header) + f.write(HEADER) f.write( """ { lib, buildVimPluginFrom2Nix, fetchFromGitHub, overrides ? (self: super: {}) }: @@ -432,136 +82,9 @@ in lib.fix' (lib.extends overrides packages) print(f"updated {outfile}") -def rewrite_input( - input_file: Path, redirects: Dict[str, str] = None, append: Tuple = () -): - with open(input_file, "r") as f: - lines = f.readlines() - - lines.extend(append) - - if redirects: - lines = [redirects.get(line, line) for line in lines] - - cur_date_iso = datetime.now().strftime("%Y-%m-%d") - with open(DEPRECATED, "r") as f: - deprecations = json.load(f) - for old, new in redirects.items(): - old_plugin = fetch_plugin_from_pluginline(old) - new_plugin = fetch_plugin_from_pluginline(new) - if old_plugin.normalized_name != new_plugin.normalized_name: - deprecations[old_plugin.normalized_name] = { - "new": new_plugin.normalized_name, - "date": cur_date_iso, - } - with open(DEPRECATED, "w") as f: - json.dump(deprecations, f, indent=4, sort_keys=True) - - lines = sorted(lines, key=str.casefold) - - with open(input_file, "w") as f: - f.writelines(lines) - - -def parse_args(): - parser = argparse.ArgumentParser( - description=( - "Updates nix derivations for vim plugins" - f"By default from {DEFAULT_IN} to {DEFAULT_OUT}" - ) - ) - parser.add_argument( - "--add", - dest="add_plugins", - default=[], - action="append", - help="Plugin to add to vimPlugins from Github in the form owner/repo", - ) - parser.add_argument( - "--input-names", - "-i", - dest="input_file", - default=DEFAULT_IN, - help="A list of plugins in the form owner/repo", - ) - parser.add_argument( - "--out", - "-o", - dest="outfile", - default=DEFAULT_OUT, - help="Filename to save generated nix code", - ) - parser.add_argument( - "--proc", - "-p", - dest="proc", - type=int, - default=30, - help="Number of concurrent processes to spawn.", - ) - return parser.parse_args() - - -def commit(repo: git.Repo, message: str, files: List[Path]) -> None: - repo.index.add([str(f.resolve()) for f in files]) - - if repo.index.diff("HEAD"): - print(f'committing to nixpkgs "{message}"') - repo.index.commit(message) - else: - print("no changes in working tree to commit") - - -def get_update(input_file: str, outfile: str, proc: int): - cache: Cache = Cache(get_current_plugins()) - _prefetch = functools.partial(prefetch, cache=cache) - - def update() -> dict: - plugin_names = load_plugin_spec(input_file) - - try: - pool = Pool(processes=proc) - results = pool.map(_prefetch, plugin_names) - finally: - cache.store() - - plugins, redirects = check_results(results) - - generate_nix(plugins, outfile) - - return redirects - - return update - - def main(): - args = parse_args() - nixpkgs_repo = git.Repo(ROOT, search_parent_directories=True) - update = get_update(args.input_file, args.outfile, args.proc) - - redirects = update() - rewrite_input(args.input_file, redirects) - commit(nixpkgs_repo, "vimPlugins: update", [args.outfile]) - - if redirects: - update() - commit( - nixpkgs_repo, - "vimPlugins: resolve github repository redirects", - [args.outfile, args.input_file, DEPRECATED], - ) - - for plugin_line in args.add_plugins: - rewrite_input(args.input_file, append=(plugin_line + "\n",)) - update() - plugin = fetch_plugin_from_pluginline(plugin_line) - commit( - nixpkgs_repo, - "vimPlugins.{name}: init at {version}".format( - name=plugin.normalized_name, version=plugin.version - ), - [args.outfile, args.input_file], - ) + editor = pluginupdate.Editor("vim", ROOT, GET_PLUGINS, generate_nix) + pluginupdate.update_plugins(editor) if __name__ == "__main__": diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index f05caaae535..b9f8e3e6ab4 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -5583,11 +5583,13 @@ in kalibrate-hackrf = callPackage ../applications/radio/kalibrate-hackrf { }; wrapKakoune = kakoune: attrs: callPackage ../applications/editors/kakoune/wrapper.nix (attrs // { inherit kakoune; }); - kakounePlugins = callPackage ../applications/editors/kakoune/plugins { }; + kakounePlugins = recurseIntoAttrs (callPackage ../applications/editors/kakoune/plugins { }); + kakoune-unwrapped = callPackage ../applications/editors/kakoune { }; kakoune = wrapKakoune kakoune-unwrapped { plugins = [ ]; # override with the list of desired plugins }; + kakouneUtils = callPackage ../applications/editors/kakoune/plugins/kakoune-utils.nix { }; kak-lsp = callPackage ../tools/misc/kak-lsp { inherit (darwin.apple_sdk.frameworks) Security;