2020-04-24 23:36:52 +00:00
{ config , lib , pkgs , . . . }:
with lib ;
let
cfge = config . environment ;
cfg = config . programs . fish ;
2021-04-15 00:37:46 +00:00
fishAbbrs = concatStringsSep " \n " (
mapAttrsFlatten ( k : v : " a b b r - a g ${ k } ${ escapeShellArg v } " )
cfg . shellAbbrs
) ;
2020-04-24 23:36:52 +00:00
fishAliases = concatStringsSep " \n " (
mapAttrsFlatten ( k : v : " a l i a s ${ k } ${ escapeShellArg v } " )
( filterAttrs ( k : v : v != null ) cfg . shellAliases )
) ;
2021-02-05 17:12:51 +00:00
envShellInit = pkgs . writeText " s h e l l I n i t " cfge . shellInit ;
envLoginShellInit = pkgs . writeText " l o g i n S h e l l I n i t " cfge . loginShellInit ;
envInteractiveShellInit = pkgs . writeText " i n t e r a c t i v e S h e l l I n i t " cfge . interactiveShellInit ;
sourceEnv = file :
if cfg . useBabelfish then
" s o u r c e / e t c / f i s h / ${ file } . f i s h "
else
''
set fish_function_path $ { pkgs . fishPlugins . foreign-env } /share/fish/vendor_functions.d $ fish_function_path
fenv source /etc/fish/foreign-env / $ { file } > /dev/null
set - e fish_function_path [ 1 ]
'' ;
babelfishTranslate = path : name :
2022-11-04 12:27:35 +00:00
pkgs . runCommandLocal " ${ name } . f i s h " {
2021-02-05 17:12:51 +00:00
nativeBuildInputs = [ pkgs . babelfish ] ;
} " ${ pkgs . babelfish } / b i n / b a b e l f i s h < ${ path } > $ o u t ; " ;
2020-04-24 23:36:52 +00:00
in
{
options = {
programs . fish = {
enable = mkOption {
default = false ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Whether to configure fish as an interactive shell .
'' ;
type = types . bool ;
} ;
2021-02-05 17:12:51 +00:00
useBabelfish = mkOption {
type = types . bool ;
default = false ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
If enabled , the configured environment will be translated to native fish using [ babelfish ] ( https://github.com/bouk/babelfish ) .
Otherwise , [ foreign-env ] ( https://github.com/oh-my-fish/plugin-foreign-env ) will be used .
2021-02-05 17:12:51 +00:00
'' ;
} ;
2020-04-24 23:36:52 +00:00
vendor . config . enable = mkOption {
type = types . bool ;
default = true ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Whether fish should source configuration snippets provided by other packages .
'' ;
} ;
vendor . completions . enable = mkOption {
type = types . bool ;
default = true ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Whether fish should use completion files provided by other packages .
'' ;
} ;
vendor . functions . enable = mkOption {
type = types . bool ;
default = true ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Whether fish should autoload fish functions provided by other packages .
'' ;
} ;
2021-04-15 00:37:46 +00:00
shellAbbrs = mkOption {
default = { } ;
example = {
gco = " g i t c h e c k o u t " ;
npu = " n i x - p r e f e t c h - u r l " ;
} ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2021-04-15 00:37:46 +00:00
Set of fish abbreviations .
'' ;
type = with types ; attrsOf str ;
} ;
2020-04-24 23:36:52 +00:00
shellAliases = mkOption {
default = { } ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
Set of aliases for fish shell , which overrides { option } ` environment . shellAliases ` .
See { option } ` environment . shellAliases ` for an option format description .
2020-04-24 23:36:52 +00:00
'' ;
type = with types ; attrsOf ( nullOr ( either str path ) ) ;
} ;
shellInit = mkOption {
default = " " ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Shell script code called during fish shell initialisation .
'' ;
type = types . lines ;
} ;
loginShellInit = mkOption {
default = " " ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Shell script code called during fish login shell initialisation .
'' ;
type = types . lines ;
} ;
interactiveShellInit = mkOption {
default = " " ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Shell script code called during interactive fish shell initialisation .
'' ;
type = types . lines ;
} ;
promptInit = mkOption {
default = " " ;
2022-08-12 12:06:08 +00:00
description = lib . mdDoc ''
2020-04-24 23:36:52 +00:00
Shell script code used to initialise fish prompt .
'' ;
type = types . lines ;
} ;
} ;
} ;
config = mkIf cfg . enable {
programs . fish . shellAliases = mapAttrs ( name : mkDefault ) cfge . shellAliases ;
2020-07-18 16:06:22 +00:00
# Required for man completions
2020-12-07 07:45:13 +00:00
documentation . man . generateCaches = lib . mkDefault true ;
2020-07-18 16:06:22 +00:00
2021-02-05 17:12:51 +00:00
environment = mkMerge [
( mkIf cfg . useBabelfish
{
etc . " f i s h / s e t E n v i r o n m e n t . f i s h " . source = babelfishTranslate config . system . build . setEnvironment " s e t E n v i r o n m e n t " ;
etc . " f i s h / s h e l l I n i t . f i s h " . source = babelfishTranslate envShellInit " s h e l l I n i t " ;
etc . " f i s h / l o g i n S h e l l I n i t . f i s h " . source = babelfishTranslate envLoginShellInit " l o g i n S h e l l I n i t " ;
etc . " f i s h / i n t e r a c t i v e S h e l l I n i t . f i s h " . source = babelfishTranslate envInteractiveShellInit " i n t e r a c t i v e S h e l l I n i t " ;
} )
( mkIf ( ! cfg . useBabelfish )
{
etc . " f i s h / f o r e i g n - e n v / s h e l l I n i t " . source = envShellInit ;
etc . " f i s h / f o r e i g n - e n v / l o g i n S h e l l I n i t " . source = envLoginShellInit ;
etc . " f i s h / f o r e i g n - e n v / i n t e r a c t i v e S h e l l I n i t " . source = envInteractiveShellInit ;
} )
{
etc . " f i s h / n i x o s - e n v - p r e i n i t . f i s h " . text =
if cfg . useBabelfish
then ''
# source the NixOS environment config
if [ - z " $ _ _ N I X O S _ S E T _ E N V I R O N M E N T _ D O N E " ]
source /etc/fish/setEnvironment.fish
end
''
else ''
# This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
# unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
set fish_function_path $ { pkgs . fishPlugins . foreign-env } /share/fish/vendor_functions.d $ __fish_datadir/functions
# source the NixOS environment config
if [ - z " $ _ _ N I X O S _ S E T _ E N V I R O N M E N T _ D O N E " ]
fenv source $ { config . system . build . setEnvironment }
end
# clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
set - e fish_function_path
'' ;
}
{
etc . " f i s h / c o n f i g . f i s h " . text = ''
# /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
# if we haven't sourced the general config, do it
if not set - q __fish_nixos_general_config_sourced
$ { sourceEnv " s h e l l I n i t " }
$ { cfg . shellInit }
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew)
set - g __fish_nixos_general_config_sourced 1
end
# if we haven't sourced the login config, do it
status - - is-login ; and not set - q __fish_nixos_login_config_sourced
and begin
$ { sourceEnv " l o g i n S h e l l I n i t " }
$ { cfg . loginShellInit }
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew)
set - g __fish_nixos_login_config_sourced 1
end
# if we haven't sourced the interactive config, do it
status - - is-interactive ; and not set - q __fish_nixos_interactive_config_sourced
and begin
2021-04-15 00:37:46 +00:00
$ { fishAbbrs }
2021-02-05 17:12:51 +00:00
$ { fishAliases }
$ { sourceEnv " i n t e r a c t i v e S h e l l I n i t " }
$ { cfg . promptInit }
$ { cfg . interactiveShellInit }
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew,
# allowing configuration changes in, e.g, aliases, to propagate)
set - g __fish_nixos_interactive_config_sourced 1
end
'' ;
}
{
etc . " f i s h / g e n e r a t e d _ c o m p l e t i o n s " . source =
let
patchedGenerator = pkgs . stdenv . mkDerivation {
name = " f i s h _ p a t c h e d - c o m p l e t i o n - g e n e r a t o r " ;
srcs = [
" ${ pkgs . fish } / s h a r e / f i s h / t o o l s / c r e a t e _ m a n p a g e _ c o m p l e t i o n s . p y "
" ${ pkgs . fish } / s h a r e / f i s h / t o o l s / d e r o f f . p y "
] ;
unpackCmd = " c p $ c u r S r c $ ( b a s e n a m e $ c u r S r c ) " ;
sourceRoot = " . " ;
patches = [ ./fish_completion-generator.patch ] ; # to prevent collisions of identical completion files
dontBuild = true ;
installPhase = ''
mkdir - p $ out
cp * $ out /
'' ;
preferLocalBuild = true ;
allowSubstitutes = false ;
} ;
generateCompletions = package : pkgs . runCommand
" ${ package . name } _ f i s h - c o m p l e t i o n s "
(
{
inherit package ;
preferLocalBuild = true ;
allowSubstitutes = false ;
}
// optionalAttrs ( package ? meta . priority ) { meta . priority = package . meta . priority ; }
)
''
mkdir - p $ out
if [ - d $ package/share/man ] ; then
find $ package/share/man - type f | xargs $ { pkgs . python3 . interpreter } $ { patchedGenerator } /create_manpage_completions.py - - directory $ out > /dev/null
fi
'' ;
in
pkgs . buildEnv {
name = " s y s t e m _ f i s h - c o m p l e t i o n s " ;
ignoreCollisions = true ;
paths = map generateCompletions config . environment . systemPackages ;
} ;
}
# include programs that bring their own completions
{
pathsToLink = [ ]
++ optional cfg . vendor . config . enable " / s h a r e / f i s h / v e n d o r _ c o n f . d "
++ optional cfg . vendor . completions . enable " / s h a r e / f i s h / v e n d o r _ c o m p l e t i o n s . d "
++ optional cfg . vendor . functions . enable " / s h a r e / f i s h / v e n d o r _ f u n c t i o n s . d " ;
}
{ systemPackages = [ pkgs . fish ] ; }
{
shells = [
" / r u n / c u r r e n t - s y s t e m / s w / b i n / f i s h "
" ${ pkgs . fish } / b i n / f i s h "
] ;
}
] ;
2020-04-24 23:36:52 +00:00
programs . fish . interactiveShellInit = ''
# add completions generated by NixOS to $fish_complete_path
begin
2023-05-24 13:37:59 +00:00
# joins with null byte to accommodate all characters in paths, then respectively gets all paths before (exclusive) / after (inclusive) the first one including "generated_completions",
2020-04-24 23:36:52 +00:00
# splits by null byte, and then removes all empty lines produced by using 'string'
set - l prev ( string join0 $ fish_complete_path | string match - - regex " ^ . * ? ( ? = \x 0 0 [ ^ \x 0 0 ] * g e n e r a t e d _ c o m p l e t i o n s . * ) " | string split0 | string match - er " . " )
set - l post ( string join0 $ fish_complete_path | string match - - regex " [ ^ \x 0 0 ] * g e n e r a t e d _ c o m p l e t i o n s . * " | string split0 | string match - er " . " )
set fish_complete_path $ prev " / e t c / f i s h / g e n e r a t e d _ c o m p l e t i o n s " $ post
end
2020-05-03 17:38:23 +00:00
# prevent fish from generating completions on first run
if not test - d $ __fish_user_data_dir/generated_completions
$ { pkgs . coreutils } /bin/mkdir $ __fish_user_data_dir/generated_completions
end
2020-04-24 23:36:52 +00:00
'' ;
} ;
}