nix-community / nix-index

Quickly locate nix packages with specific files [maintainers=@bennofs @figsoda @raitobezarius]
Other
780 stars 49 forks source link

Command-not-found utility for fish #126

Open anka-213 opened 3 years ago

anka-213 commented 3 years ago

Here's an example of the corresponding function for nixos' command-not-found program. https://github.com/beatgammit/fish-shell/blob/793784c08778e8b3fb9d8a2a5621bd968db1dfbe/share/functions/__fish_config_interactive.fish#L229-L231

function __fish_command_not_found_handler --on-event fish_command_not_found
    /path/to/command-not-found $argv
end

I'm not entirely sure where the function should be placed and if it should have a different name though.


I made a wrapper for the shell script to create an executable for fish to run:

#!/usr/bin/env bash

source /nix/store/kk3d66hmjxsn32qdnak6p7w8s77gig8r-nix-index-0.1.2/etc/profile.d/command-not-found.sh

command_not_found_handle $@

but this version is extremely fragile and should be nixified.

kamaln7 commented 3 years ago

Thanks @anka-213! The function:

function __fish_command_not_found_handler --on-event fish_command_not_found
    /path/to/command-not-found $argv
end

can be added to either ~/.config/fish/config.fish or in its own file at ~/.config/fish/functions/any_name_you_want.fish. I use home-manager to manage my homedir, like this:

user: { pkgs, ... }:
{
  home-manager.users."${user}" = {    
    # ...

    home.file = {
      ".config" = {
        source = ../../dotfiles/.config;
        recursive = true;
      };

      "bin/nix-command-not-found" = {
        text = ''
          #!/usr/bin/env bash
          source ${pkgs.nix-index}/etc/profile.d/command-not-found.sh
          command_not_found_handle "$@"
        '';

        executable = true;
      };

      # ...
    };
  };
}

The ../../dotfiles/.config path references the .config directory in my git repo where I keep my nix config. I have the function in .config/fish/functions/command_not_found.fish:

function __fish_command_not_found_handler --on-event fish_command_not_found
    ~/bin/command-not-found $argv
end

I also added nix-index to the system packages in environment.systemPackages.

malob commented 3 years ago

I'm using a similar workaround right now. It would be nice if nix-index provided a native Fish version though. Maybe the easiest way would be to use Babelfish? Version 1.1.0, works on command-not-found.sh. Here's the output from Babelfish, with the first line changed to have the correct function name and event handler, and @out@ substituted back in instead of the store path for nix-index.

function __fish_command_not_found_handler --on-event fish_command_not_found
  # TODO: use "command not found" gettext translations
  # taken from http://www.linuxjournal.com/content/bash-command-not-found
  # - do not run when inside Midnight Commander or within a Pipe
  if [ -n "$MC_SID" ] || ! [ -t 1 ]
    echo $argv[1]': command not found' >&2
    return 127
  end
  # nixpkgs should always be available even in NixOS
  set toplevel 'nixpkgs'
  set cmd $argv[1]
  set attrs (@out@/bin/nix-locate --minimal --no-group --type x --type s --top-level --whole-name --at-root '/bin/'"$cmd" | string collect; or echo)
  set len (echo -n "$attrs" | grep -c '^' | string collect; or echo)
  switch "$len"
  case '0'
    echo "$cmd"': command not found' >&2
  case '1'
    # if only 1 package provides this, then we can invoke it
    # without asking the users if they have opted in with one
    # of 2 environment variables
    # they are based on the ones found in
    # command-not-found.sh:
    #   NIX_AUTO_INSTALL : install the missing command into the
    #                      user’s environment
    #   NIX_AUTO_RUN     : run the command transparently inside of
    #                      nix shell
    # these will not return 127 if they worked correctly
    if ! [ -z "$NIX_AUTO_INSTALL" ]
      cat >&2 <(echo 'The program \''"$cmd"'\' is currently not installed. It is provided by
the package \''"$toplevel"'.'"$attrs"'\', which I will now install for you.
'| psub)
      nix-env -iA $toplevel.$attrs
      if [ "$status" -eq 0 ]
        # TODO: handle pipes correctly if AUTO_RUN/INSTALL is possible
        $argv
        return $status
      else
        cat >&2 <(echo 'Failed to install '"$toplevel"'.attrs.
'"$cmd"': command not found
'| psub)
      end
    else if ! [ -z "$NIX_AUTO_RUN" ]
      nix-build --no-out-link -A $attrs '<'"$toplevel"'>'
      if [ "$status" -eq 0 ]
        # how nix-shell handles commands is weird
        # $(echo $@) is need to handle this
        nix-shell -p $attrs --run (echo $argv | string collect; or echo)
        return $status
      else
        cat >&2 <(echo 'Failed to install '"$toplevel"'.attrs.
'"$cmd"': command not found
'| psub)
      end
    else
      cat >&2 <(echo 'The program \''"$cmd"'\' is currently not installed. You can install it
by typing:
  nix-env -iA '"$toplevel"'.'"$attrs"'
'| psub)
    end
  case '*'
    cat >&2 <(echo 'The program \''"$cmd"'\' is currently not installed. It is provided by
several packages. You can install it by typing one of the following:
'| psub)
    # ensure we get each element of attrs
    # in a cross platform way
    while read attr
      echo '  nix-env -iA '"$toplevel"'.'"$attr" >&2
    end <(echo "$attrs"| psub)
  end
  # command not found should always exit with 127
  return 127
end
lilyball commented 2 years ago

nixpkgs packages babelfish as a package. NixOS has a programs.fish.useBabelfish option that makes it uses babelfish instead of fish-foreign-env for translating its init scripts as well, which is good precedent. It seems reasonable to me to have your default.nix use nixpkgs.babelfish to translate the command-not-found handler.