diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix index 34a0dc6a2df3..d3f7d3eb1af5 100644 --- a/nixos/modules/programs/fish.nix +++ b/nixos/modules/programs/fish.nix @@ -13,6 +13,27 @@ let (filterAttrs (k: v: v != null) cfg.shellAliases) ); + envShellInit = pkgs.writeText "shellInit" cfge.shellInit; + + envLoginShellInit = pkgs.writeText "loginShellInit" cfge.loginShellInit; + + envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit; + + sourceEnv = file: + if cfg.useBabelfish then + "source /etc/fish/${file}.fish" + else + '' + set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish-foreign-env/functions $fish_function_path + fenv source /etc/fish/foreign-env/${file} > /dev/null + set -e fish_function_path[1] + ''; + + babelfishTranslate = path: name: + pkgs.runCommand "${name}.fish" { + nativeBuildInputs = [ pkgs.babelfish ]; + } "${pkgs.babelfish}/bin/babelfish < ${path} > $out;"; + in { @@ -29,6 +50,15 @@ in type = types.bool; }; + useBabelfish = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, the configured environment will be translated to native fish using babelfish. + Otherwise, foreign-env will be used. + ''; + }; + vendor.config.enable = mkOption { type = types.bool; default = true; @@ -105,72 +135,152 @@ in # Required for man completions documentation.man.generateCaches = lib.mkDefault true; - environment.etc."fish/foreign-env/shellInit".text = cfge.shellInit; - environment.etc."fish/foreign-env/loginShellInit".text = cfge.loginShellInit; - environment.etc."fish/foreign-env/interactiveShellInit".text = cfge.interactiveShellInit; + environment = mkMerge [ + (mkIf cfg.useBabelfish + { + etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment"; + etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit"; + etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit"; + etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit"; + }) - environment.etc."fish/nixos-env-preinit.fish".text = '' - # 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 + (mkIf (!cfg.useBabelfish) + { + etc."fish/foreign-env/shellInit".source = envShellInit; + etc."fish/foreign-env/loginShellInit".source = envLoginShellInit; + etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit; + }) - # source the NixOS environment config - if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ] - fenv source ${config.system.build.setEnvironment} - end + { + etc."fish/nixos-env-preinit.fish".text = + if cfg.useBabelfish + then '' + # source the NixOS environment config + if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ] + 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 - # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish - set -e fish_function_path - ''; + # source the NixOS environment config + if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ] + fenv source ${config.system.build.setEnvironment} + end - environment.etc."fish/config.fish".text = '' - # /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically. + # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish + set -e fish_function_path + ''; + } - # if we haven't sourced the general config, do it - if not set -q __fish_nixos_general_config_sourced - set --prepend fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d - fenv source /etc/fish/foreign-env/shellInit > /dev/null - set -e fish_function_path[1] + { + etc."fish/config.fish".text = '' + # /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically. - ${cfg.shellInit} + # if we haven't sourced the general config, do it + if not set -q __fish_nixos_general_config_sourced + ${sourceEnv "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 + ${cfg.shellInit} - # if we haven't sourced the login config, do it - status --is-login; and not set -q __fish_nixos_login_config_sourced - and begin - set --prepend fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d - fenv source /etc/fish/foreign-env/loginShellInit > /dev/null - set -e fish_function_path[1] + # 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 - ${cfg.loginShellInit} + # 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 "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 + ${cfg.loginShellInit} - # if we haven't sourced the interactive config, do it - status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced - and begin - ${fishAliases} + # 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 - set --prepend fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d - fenv source /etc/fish/foreign-env/interactiveShellInit > /dev/null - set -e fish_function_path[1] + # if we haven't sourced the interactive config, do it + status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced + and begin + ${fishAliases} - ${cfg.promptInit} - ${cfg.interactiveShellInit} + ${sourceEnv "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 - ''; + ${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."fish/generated_completions".source = + let + patchedGenerator = pkgs.stdenv.mkDerivation { + name = "fish_patched-completion-generator"; + srcs = [ + "${pkgs.fish}/share/fish/tools/create_manpage_completions.py" + "${pkgs.fish}/share/fish/tools/deroff.py" + ]; + unpackCmd = "cp $curSrc $(basename $curSrc)"; + 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}_fish-completions" + ( + { + 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 = "system_fish-completions"; + ignoreCollisions = true; + paths = map generateCompletions config.environment.systemPackages; + }; + } + + # include programs that bring their own completions + { + pathsToLink = [] + ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d" + ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d" + ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d"; + } + + { systemPackages = [ pkgs.fish ]; } + + { + shells = [ + "/run/current-system/sw/bin/fish" + "${pkgs.fish}/bin/fish" + ]; + } + ]; programs.fish.interactiveShellInit = '' # add completions generated by NixOS to $fish_complete_path @@ -187,61 +297,6 @@ in end ''; - environment.etc."fish/generated_completions".source = - let - patchedGenerator = pkgs.stdenv.mkDerivation { - name = "fish_patched-completion-generator"; - srcs = [ - "${pkgs.fish}/share/fish/tools/create_manpage_completions.py" - "${pkgs.fish}/share/fish/tools/deroff.py" - ]; - unpackCmd = "cp $curSrc $(basename $curSrc)"; - 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}_fish-completions" - ( - { - 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 = "system_fish-completions"; - ignoreCollisions = true; - paths = map generateCompletions config.environment.systemPackages; - }; - - # include programs that bring their own completions - environment.pathsToLink = [] - ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d" - ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d" - ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d"; - - environment.systemPackages = [ pkgs.fish ]; - - environment.shells = [ - "/run/current-system/sw/bin/fish" - "${pkgs.fish}/bin/fish" - ]; - }; }