kakoune: rework plugin support

The previous implementation of plugin-support for the kakoune derivation
was based on generating, at build time, a `plugins.kak` file that would
source all .kak files in the list of plugins, and wrap the `kak` binary
in a script that would add some command-line arguments so that this
file gets loaded on start-up. The main problem with this approach
is that the plugins' code get executed *after* the user's configuration
file is loaded, so effectively one cannot automatically activate/configure
these plugins.

The idiomatic way of loading plugins is ensuring they end up installed
somwhere under `share/kak/autoload`. Because plugins are already being
packaged to have their code in `share/kak/autoload/plugins/<name-of-plugin>`,
we can obtain a derivation that includes the plugins simply by doing a
`symlinkJoin` of `kakoune-unwrapped` and all the requested plugins.

For this to work, we need to fix two issues:

  1. By default, kakoune makes `share/kak/autoload` a symbolic link to
     `share/kak/rc`, which contains all builtin definitions. We need
     to patch this to put the symlink under `share/kak/autoload/rc`, so that
     the join works.

  2. By default kakoune expects the `autoload` directory to be in
     `../share/kak/autoload` relative to the location of the `kak` binary.
     We need to set the `KAKOUNE_RUNTIME` to point the symlinked
     share/kak for this to work.
This commit is contained in:
Daniel Gorin 2020-06-29 21:25:16 +01:00
parent 0fb9276e51
commit 550389392a
5 changed files with 31 additions and 72 deletions

View file

@ -6,9 +6,7 @@
<para>
Kakoune can be built to autoload plugins:
<programlisting>(kakoune.override {
configure = {
plugins = with pkgs.kakounePlugins; [ parinfer-rust ];
};
plugins = with pkgs.kakounePlugins; [ parinfer-rust ];
})</programlisting>
</para>
</section>

View file

@ -30,6 +30,15 @@ stdenv.mkDerivation rec {
$out/bin/kak -ui json -E "kill 0"
'';
postInstall = ''
# make share/kak/autoload a directory, so we can use symlinkJoin with plugins
cd "$out/share/kak"
autoload_target=$(readlink autoload)
rm autoload
mkdir autoload
ln -s --relative "$autoload_target" autoload
'';
meta = {
homepage = "http://kakoune.org/";
description = "A vim inspired text editor";

View file

@ -1,44 +1,24 @@
{ stdenv, bash }:
with stdenv.lib;
kakoune:
{ symlinkJoin, makeWrapper, kakoune, plugins ? [], configure ? {} }:
let
getPlugins = { plugins ? [] }: plugins;
# "plugins" is the preferred way, but some configurations may be
# using "configure.plugins", so accept both
requestedPlugins = plugins ++ (configure.plugins or []);
wrapper = { configure ? {} }:
stdenv.mkDerivation rec {
pname = "kakoune";
version = getVersion kakoune;
in
symlinkJoin {
name = "kakoune-${kakoune.version}";
src = ./.;
buildCommand = ''
mkdir -p $out/share/kak
for plugin in ${strings.escapeShellArgs (getPlugins configure)}; do
if [[ -d $plugin/share/kak/autoload ]]; then
find "$plugin/share/kak/autoload" -type f -name '*.kak'| while read rcfile; do
printf 'source "%s"\n' "$rcfile"
done
fi
done >>$out/share/kak/plugins.kak
buildInputs = [ makeWrapper ];
mkdir -p $out/bin
substitute ${src}/wrapper.sh $out/bin/kak \
--subst-var-by bash "${bash}" \
--subst-var-by kakoune "${kakoune}" \
--subst-var-by out "$out"
chmod +x $out/bin/kak
paths = [ kakoune ] ++ requestedPlugins;
postBuild = ''
# location of kak binary is used to find ../share/kak/autoload,
# unless explicitly overriden with KAKOUNE_RUNTIME
rm "$out/bin/kak"
makeWrapper "${kakoune}/bin/kak" "$out/bin/kak" --set KAKOUNE_RUNTIME "$out/share/kak"
'';
preferLocalBuild = true;
buildInputs = [ bash kakoune ];
passthru = { unwrapped = kakoune; };
meta = kakoune.meta // {
# prefer wrapper over the package
priority = (kakoune.meta.priority or 0) - 1;
hydraPlatforms = [];
};
};
in
makeOverridable wrapper
meta = kakoune.meta // { priority = (kakoune.meta.priority or 0) - 1; };
}

View file

@ -1,30 +0,0 @@
#!@bash@/bin/bash
# We use the -E option to load plugins. This only makes sense when we are
# starting a new session, so we detect that. Also, Kakoune can only handle
# one -E option, so we prepend loading plugins to an existing one.
args=( "$@" )
loadPlugins=true
EValueOffset=-1
pluginScript='@out@/share/kak/plugins.kak'
for (( i = 0; i < ${#args[@]}; i++ )); do
case "${args[i]}" in
-n|-c|-l|-p|-clear|-version) loadPlugins=false;;
-E) EValueOffset=$(( i + 1 ));;
--) break;;
esac
case "${args[i]}" in
-E|-c|-e|-s|-p|-f|-i|-ui|-debug) i=$(( i + 1 ));;
esac
done
if [[ $loadPlugins = true ]]; then
if (( EValueOffset >= 0 )); then
args[EValueOffset]="source '$pluginScript'"$'\n'"${args[EValueOffset]}"
else
args=( "-E" "source '$pluginScript'" "${args[@]}" )
fi
fi
exec @kakoune@/bin/kak "${args[@]}"

View file

@ -4905,10 +4905,12 @@ in
kalibrate-hackrf = callPackage ../applications/radio/kalibrate-hackrf { };
wrapKakoune = callPackage ../applications/editors/kakoune/wrapper.nix { };
wrapKakoune = kakoune: attrs: callPackage ../applications/editors/kakoune/wrapper.nix (attrs // { inherit kakoune; });
kakounePlugins = callPackage ../applications/editors/kakoune/plugins { };
kakoune-unwrapped = callPackage ../applications/editors/kakoune { };
kakoune = wrapKakoune kakoune-unwrapped { };
kakoune = wrapKakoune kakoune-unwrapped {
plugins = [ ]; # override with the list of desired plugins
};
kak-lsp = callPackage ../tools/misc/kak-lsp { };