Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wrapFish: add fish shell wrapper package #108491

Merged
merged 4 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions doc/builders/packages/fish.section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Fish {#sec-fish}

Fish is a "smart and user-friendly command line shell" with support for plugins.


## Vendor Fish scripts {#sec-fish-vendor}

Any package may ship its own Fish completions, configuration snippets, and
functions. Those should be installed to
`$out/share/fish/vendor_{completions,conf,functions}.d` respectively.

When the `programs.fish.enable` and
`programs.fish.vendor.{completions,config,functions}.enable` options from the
cole-h marked this conversation as resolved.
Show resolved Hide resolved
NixOS Fish module are set to true, those paths are symlinked in the current
system environment and automatically loaded by Fish.


## Packaging Fish plugins {#sec-fish-plugins-pkg}

While packages providing standalone executables belong to the top level,
packages which have the sole purpose of extending Fish belong to the
`fishPlugins` scope and should be registered in
`pkgs/shells/fish/plugins/default.nix`.

The `buildFishPlugin` utility function can be used to automatically copy Fish
scripts from `$src/{completions,conf,conf.d,functions}` to the standard vendor
installation paths. It also sets up the test environment so that the optional
`checkPhase` is executed in a Fish shell with other already packaged plugins
and package-local Fish functions specified in `checkPlugins` and
cole-h marked this conversation as resolved.
Show resolved Hide resolved
`checkFunctionDirs` respectively.

See `pkgs/shells/fish/plugins/pure.nix` for an example of Fish plugin package
using `buildFishPlugin` and running unit tests with the `fishtape` test runner.


## Fish wrapper {#sec-fish-wrapper}

The `wrapFish` package is a wrapper around Fish which can be used to create
Fish shells initialised with some plugins as well as completions, configuration
snippets and functions sourced from the given paths. This provides a convenient
way to test Fish plugins and scripts without having to alter the environment.

```nix
wrapFish {
pluginPkgs = with fishPlugins; [ pure foreign-env ];
completionDirs = [];
functionDirs = [];
confDirs = [ "/path/to/some/fish/init/dir/" ];
}
```
1 change: 1 addition & 0 deletions doc/builders/packages/index.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<xi:include href="elm.xml" />
<xi:include href="emacs.section.xml" />
<xi:include href="firefox.section.xml" />
<xi:include href="fish.section.xml" />
<xi:include href="ibus.xml" />
<xi:include href="kakoune.section.xml" />
<xi:include href="linux.section.xml" />
Expand Down
36 changes: 13 additions & 23 deletions pkgs/shells/fish/plugins/build-fish-plugin.nix
Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
{ stdenv, lib, writeShellScriptBin, writeScript, fish }:
{ stdenv, lib, writeScript, wrapFish }:

let
rtpPath = "share/fish";

mapToFuncPath = v:
if lib.isString v
then v
else "${v}/${rtpPath}/vendor_functions.d";

fishWithFunctionPath = plugins: let
funcPaths = map mapToFuncPath plugins;
in writeShellScriptBin "fish" ''
${fish}/bin/fish \
--init-command \
"set --prepend fish_function_path ${lib.escapeShellArgs funcPaths}" \
"$@"
'';

in attrs@{
attrs@{
pname,
version,
src,
Expand All @@ -32,8 +15,10 @@ in attrs@{
installPath ? lib.getName pname,

checkInputs ? [],
# plugins or paths to add to the function path of the test fish shell
checkFunctionPath ? [],
# plugin packages to add to the vendor paths of the test fish shell
checkPlugins ? [],
# vendor directories to add to the function path of the test fish shell
checkFunctionDirs ? [],
# test script to be executed in a fish shell
checkPhase ? "",
doCheck ? checkPhase != "",
Expand All @@ -52,7 +37,7 @@ stdenv.mkDerivation (attrs // {
(
install_vendor_files() {
source="$1"
target="$out/${rtpPath}/vendor_$2.d"
target="$out/share/fish/vendor_$2.d"

[ -d $source ] || return 0
mkdir -p $target
Expand All @@ -69,7 +54,12 @@ stdenv.mkDerivation (attrs // {
'';

inherit doCheck;
checkInputs = [ (fishWithFunctionPath checkFunctionPath) ] ++ checkInputs;

checkInputs = [ (wrapFish {
pluginPkgs = checkPlugins;
functionDirs = checkFunctionDirs;
}) ] ++ checkInputs;

checkPhase = ''
export HOME=$(mktemp -d) # fish wants a writable home
fish "${writeScript "${name}-test" checkPhase}"
Expand Down
2 changes: 1 addition & 1 deletion pkgs/shells/fish/plugins/fishtape.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ buildFishPlugin rec {
sha256 = "0dxcyhs2shhgy5xnwcimqja8vqsyk841x486lgq13i3y1h0kp2kd";
};

checkFunctionPath = [ "./" ]; # fishtape is introspective
checkFunctionDirs = [ "./" ]; # fishtape is introspective
checkPhase = ''
rm test/tty.fish # test expects a tty
fishtape test/*.fish
Expand Down
2 changes: 1 addition & 1 deletion pkgs/shells/fish/plugins/pure.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ buildFishPlugin rec {
};

checkInputs = [ git ];
checkFunctionPath = [ fishtape ];
checkPlugins = [ fishtape ];
checkPhase = ''
# https://github.com/rafaelrinaldi/pure/issues/264
rm tests/_pure_string_width.test.fish
Expand Down
25 changes: 25 additions & 0 deletions pkgs/shells/fish/wrapper.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{ lib, writeShellScriptBin, fish }:

with lib;

makeOverridable ({
completionDirs ? [],
functionDirs ? [],
confDirs ? [],
pluginPkgs ? []
}:

let
vendorDir = kind: plugin: "${plugin}/share/fish/vendor_${kind}.d";
complPath = completionDirs ++ map (vendorDir "completions") pluginPkgs;
funcPath = functionDirs ++ map (vendorDir "functions") pluginPkgs;
confPath = confDirs ++ map (vendorDir "conf") pluginPkgs;
safeConfPath = map escapeShellArg confPath;

in writeShellScriptBin "fish" ''
${fish}/bin/fish --init-command "
set --prepend fish_complete_path ${escapeShellArgs complPath}
set --prepend fish_function_path ${escapeShellArgs funcPath}
for c in {${concatStringsSep "," safeConfPath}}/*; source $c; end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pacien I've just discovered an issue with this line.
When safeConfPath only contains a single element, due to some peculiarity w/ brace expansion in fish [1], the expansion will produce a single value of just that single element wrapped into braces, which in turn will result in a silent failure since that path will not actually exist in the file system.

e.g. if safeConfPath just contains foo, that line produce something like for c in '{foo}/*'; source $c; end which will silently fail, and the corresponding scripts won't be sourced.

Not sure how to fix this. May add a dummy path in the brace force proper expansion?

[1] http://fishshell.com/docs/current/index.html#expand-brace # see examples in the second code block

" "$@"
'')
2 changes: 2 additions & 0 deletions pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8843,6 +8843,8 @@ in

fish = callPackage ../shells/fish { };

wrapFish = callPackage ../shells/fish/wrapper.nix { };

fishPlugins = recurseIntoAttrs (callPackage ../shells/fish/plugins { });

ion = callPackage ../shells/ion {
Expand Down