This commit is contained in:
Robert Helgesson 2020-02-20 00:07:04 +01:00
commit 57bd27b3e7
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
6 changed files with 284 additions and 28 deletions

View file

@ -6,83 +6,160 @@ let
cfg = config.programs.fish; cfg = config.programs.fish;
abbrsStr = concatStringsSep "\n" ( pluginModule = types.submodule ({ config, ... }: {
mapAttrsToList (k: v: "abbr --add --global ${k} '${v}'") cfg.shellAbbrs options = {
); src = mkOption {
type = types.path;
description = ''
Path to the plugin folder.
</para><para>
Relevant pieces will be added to the fish function path and
the completion path. The <filename>init.fish</filename> and
<filename>key_binding.fish</filename> files are sourced if
they exist.
'';
};
aliasesStr = concatStringsSep "\n" ( name = mkOption {
mapAttrsToList (k: v: "alias ${k}='${v}'") cfg.shellAliases type = types.str;
); description = ''
The name of the plugin.
'';
};
};
});
abbrsStr = concatStringsSep "\n"
(mapAttrsToList (k: v: "abbr --add --global -- ${k} ${escapeShellArg v}")
cfg.shellAbbrs);
aliasesStr = concatStringsSep "\n"
(mapAttrsToList (k: v: "alias ${k} ${escapeShellArg v}") cfg.shellAliases);
in in
{ {
options = { options = {
programs.fish = { programs.fish = {
enable = mkEnableOption "fish friendly interactive shell"; enable = mkEnableOption "fish, the friendly interactive shell";
package = mkOption { package = mkOption {
type = types.package;
default = pkgs.fish; default = pkgs.fish;
defaultText = literalExample "pkgs.fish"; defaultText = literalExample "pkgs.fish";
description = '' description = ''
The fish package to install. May be used to change the version. The fish package to install. May be used to change the version.
''; '';
type = types.package;
}; };
shellAliases = mkOption { shellAliases = mkOption {
default = {}; type = with types; attrsOf str;
default = { };
example = { ".." = "cd .."; ll = "ls -l"; };
description = '' description = ''
Set of aliases for fish shell. See An attribute set that maps aliases (the top level attribute names
<option>environment.shellAliases</option> for an option in this option) to command strings or directly to build outputs.
format description.
''; '';
type = types.attrs;
}; };
shellAbbrs = mkOption { shellAbbrs = mkOption {
default = {}; type = with types; attrsOf str;
default = { };
example = {
l = "less";
gco = "git checkout";
};
description = '' description = ''
Set of abbreviations for fish shell. An attribute set that maps aliases (the top level attribute names
in this option) to abbreviations. Abbreviations are expanded with
the longer phrase after they are entered.
''; '';
type = types.attrs;
}; };
shellInit = mkOption { shellInit = mkOption {
type = types.lines;
default = ""; default = "";
description = '' description = ''
Shell script code called during fish shell initialisation. Shell script code called during fish shell
initialisation.
''; '';
type = types.lines;
}; };
loginShellInit = mkOption { loginShellInit = mkOption {
type = types.lines;
default = ""; default = "";
description = '' description = ''
Shell script code called during fish login shell initialisation. Shell script code called during fish login shell
initialisation.
''; '';
type = types.lines;
}; };
interactiveShellInit = mkOption { interactiveShellInit = mkOption {
type = types.lines;
default = ""; default = "";
description = '' description = ''
Shell script code called during interactive fish shell initialisation. Shell script code called during interactive fish shell
initialisation.
''; '';
type = types.lines;
}; };
promptInit = mkOption { promptInit = mkOption {
type = types.lines;
default = ""; default = "";
description = '' description = ''
Shell script code used to initialise fish prompt. Shell script code used to initialise fish prompt.
''; '';
type = types.lines;
}; };
}; };
programs.fish.plugins = mkOption {
type = types.listOf pluginModule;
default = [ ];
example = literalExample ''
[
{
name = "z";
src = pkgs.fetchFromGitHub {
owner = "jethrokuan";
repo = "z";
rev = "ddeb28a7b6a1f0ec6dae40c636e5ca4908ad160a";
sha256 = "0c5i7sdrsp0q3vbziqzdyqn4fmp235ax4mn4zslrswvn8g3fvdyh";
};
}
# oh-my-fish plugins are stored in their own repositories, which
# makes them simple to import into home-manager.
{
name = "fasd";
src = pkgs.fetchFromGitHub {
owner = "oh-my-fish";
repo = "plugin-fasd";
rev = "38a5b6b6011106092009549e52249c6d6f501fba";
sha256 = "06v37hqy5yrv5a6ssd1p3cjd9y3hnp19d3ab7dag56fs1qmgyhbs";
};
}
]
'';
description = ''
The plugins to source in
<filename>conf.d/99plugins.fish</filename>.
'';
};
programs.fish.functions = mkOption {
type = types.attrsOf types.lines;
default = { };
example = { gitignore = "curl -sL https://www.gitignore.io/api/$argv"; };
description = ''
Basic functions to add to fish. For more information see
<link xlink:href="https://fishshell.com/docs/current/commands.html#function"/>.
'';
};
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable (mkMerge [{
home.packages = [ cfg.package ]; home.packages = [ cfg.package ];
xdg.dataFile."fish/home-manager_generated_completions".source = xdg.dataFile."fish/home-manager_generated_completions".source =
@ -149,10 +226,13 @@ in
''; '';
xdg.configFile."fish/config.fish".text = '' xdg.configFile."fish/config.fish".text = ''
# ~/.config/fish/config.fish: DO NOT EDIT -- this file has been generated automatically. # ~/.config/fish/config.fish: DO NOT EDIT -- this file has been generated
# automatically by home-manager.
# if we haven't sourced the general config, do it # if we haven't sourced the general config, do it
if not set -q __fish_general_config_sourced if not set -q __fish_general_config_sourced
set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
set -p fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions
fenv source ${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh > /dev/null fenv source ${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh > /dev/null
set -e fish_function_path[1] set -e fish_function_path[1]
@ -160,32 +240,90 @@ in
# and leave a note so we don't source this config section again from # and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew) # this very shell (children will source the general config anew)
set -g __fish_general_config_sourced 1 set -g __fish_general_config_sourced 1
end end
# if we haven't sourced the login config, do it # if we haven't sourced the login config, do it
status --is-login; and not set -q __fish_login_config_sourced status --is-login; and not set -q __fish_login_config_sourced
and begin and begin
# Login shell initialisation
${cfg.loginShellInit} ${cfg.loginShellInit}
# and leave a note so we don't source this config section again from # and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew) # this very shell (children will source the general config anew)
set -g __fish_login_config_sourced 1 set -g __fish_login_config_sourced 1
end end
# if we haven't sourced the interactive config, do it # if we haven't sourced the interactive config, do it
status --is-interactive; and not set -q __fish_interactive_config_sourced status --is-interactive; and not set -q __fish_interactive_config_sourced
and begin and begin
# Abbrs
# Abbreviations
${abbrsStr} ${abbrsStr}
# Aliases # Aliases
${aliasesStr} ${aliasesStr}
# Prompt initialisation
${cfg.promptInit} ${cfg.promptInit}
# Interactive shell intialisation
${cfg.interactiveShellInit} ${cfg.interactiveShellInit}
# and leave a note so we don't source this config section again from # and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew, # this very shell (children will source the general config anew,
# allowing configuration changes in, e.g, aliases, to propagate) # allowing configuration changes in, e.g, aliases, to propagate)
set -g __fish_interactive_config_sourced 1 set -g __fish_interactive_config_sourced 1
end end
''; '';
};
} {
xdg.configFile = mapAttrs' (fName: fBody: {
name = "fish/functions/${fName}.fish";
value = {"text" = ''
function ${fName}
${fBody}
end
'';};
}) cfg.functions;
}
# Each plugin gets a corresponding conf.d/plugin-NAME.fish file to load
# in the paths and any initialization scripts.
(mkIf (length cfg.plugins > 0) {
xdg.configFile = mkMerge (
(map (plugin: { "fish/conf.d/plugin-${plugin.name}.fish".text = ''
# Plugin ${plugin.name}
set -l plugin_dir ${plugin.src}
# Set paths to import plugin components
if test -d $plugin_dir/functions
set fish_function_path $fish_function_path[1] $plugin_dir/functions $fish_function_path[2..-1]
end
if test -d $plugin_dir/completions
set fish_complete_path $fish_function_path[1] $plugin_dir/completions $fish_complete_path[2..-1]
end
# Source initialization code if it exists.
if test -d $plugin_dir/conf.d
source $plugin_dir/conf.d/*.fish
end
if test -f $plugin_dir/key_bindings.fish
source $plugin_dir/key_bindings.fish
end
if test -f $plugin_dir/init.fish
source $plugin_dir/init.fish
end
'';
}) cfg.plugins));
})
]);
} }

View file

@ -29,6 +29,7 @@ import nmt {
./modules/programs/alacritty ./modules/programs/alacritty
./modules/programs/bash ./modules/programs/bash
./modules/programs/browserpass ./modules/programs/browserpass
./modules/programs/fish
./modules/programs/git ./modules/programs/git
./modules/programs/gpg ./modules/programs/gpg
./modules/programs/mbsync ./modules/programs/mbsync

View file

@ -0,0 +1,5 @@
{
fish-functions = ./functions.nix;
fish-no-functions = ./no-functions.nix;
fish-plugins = ./plugins.nix;
}

View file

@ -0,0 +1,32 @@
{ config, lib, pkgs, ... }:
with lib;
let
func = pkgs.writeText "func.fish" ''
function func
echo "Hello"
end
'';
in {
config = {
programs.fish = {
enable = true;
functions = { func = ''echo "Hello"''; };
};
nmt = {
description =
"if fish.function is set, check file exists and contents match";
script = ''
assertFileExists home-files/.config/fish/functions/func.fish
echo ${func}
assertFileContent home-files/.config/fish/functions/func.fish ${func}
'';
};
};
}

View file

@ -0,0 +1,22 @@
{ config, lib, ... }:
with lib;
{
config = {
programs.fish = {
enable = true;
functions = { };
};
nmt = {
description =
"if fish.functions is blank, the functions folder should not exist.";
script = ''
assertPathNotExists home-files/.config/fish/functions
'';
};
};
}

View file

@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:
with lib;
let
fooPluginSrc = pkgs.writeText "fooPluginSrc" "";
generatedConfdFile = pkgs.writeText "plugin-foo.fish" ''
# Plugin foo
set -l plugin_dir ${fooPluginSrc}
# Set paths to import plugin components
if test -d $plugin_dir/functions
set fish_function_path $fish_function_path[1] $plugin_dir/functions $fish_function_path[2..-1]
end
if test -d $plugin_dir/completions
set fish_complete_path $fish_function_path[1] $plugin_dir/completions $fish_complete_path[2..-1]
end
# Source initialization code if it exists.
if test -d $plugin_dir/conf.d
source $plugin_dir/conf.d/*.fish
end
if test -f $plugin_dir/key_bindings.fish
source $plugin_dir/key_bindings.fish
end
if test -f $plugin_dir/init.fish
source $plugin_dir/init.fish
end
'';
in {
config = {
programs.fish = {
enable = true;
plugins = [{
name = "foo";
src = fooPluginSrc;
}];
};
nmt = {
description =
"if fish.plugins set, check conf.d file exists and contents match";
script = ''
assertDirectoryExists home-files/.config/fish/conf.d
assertFileExists home-files/.config/fish/conf.d/plugin-foo.fish
assertFileContent home-files/.config/fish/conf.d/plugin-foo.fish ${generatedConfdFile}
'';
};
};
}