diff --git a/modules/programs/command-not-found/command-not-found.pl b/modules/programs/command-not-found/command-not-found.pl index 220d057b7..64f0f1a49 100644 --- a/modules/programs/command-not-found/command-not-found.pl +++ b/modules/programs/command-not-found/command-not-found.pl @@ -10,6 +10,24 @@ my $program = $ARGV[0]; my $dbPath = "@dbPath@"; +if (! -e $dbPath) { + print STDERR "$program: command not found\n"; + print STDERR "\n"; + print STDERR "command-not-found: Missing package database\n"; + print STDERR "command-not-found is a tool for searching for missing packages.\n"; + print STDERR "No database was found, this likely means the database hasn't been generated yet.\n"; + print STDERR "This tool requires nix-channels to generate the database for the `nixos` channel.\n"; + print STDERR "\n"; + print STDERR "If you are using nix-channels you can run:\n"; + print STDERR " sudo nix-channels --update\n"; + print STDERR "\n"; + print STDERR "If you are using flakes, see nix-index and nix-index-database.\n"; + print STDERR "\n"; + print STDERR "If you would like to disable this message you can set:\n"; + print STDERR " programs.command-not-found.enable = false;\n"; + exit 127; +} + my $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "") or die "cannot open database `$dbPath'"; $dbh->{RaiseError} = 0; @@ -21,11 +39,24 @@ my $res = $dbh->selectall_arrayref( "select package from Programs where system = ? and name = ?", { Slice => {} }, $system, $program); -if (!defined $res || scalar @$res == 0) { +my $len = !defined $res ? 0 : scalar @$res; + +if ($len == 0) { print STDERR "$program: command not found\n"; -} elsif (scalar @$res == 1) { +} elsif ($len == 1) { my $package = @$res[0]->{package}; if ($ENV{"NIX_AUTO_RUN"} // "") { + if ($ENV{"NIX_AUTO_RUN_INTERACTIVE"} // "") { + while (1) { + print STDERR "'$program' from package '$package' will be run, confirm? [yn]: "; + chomp(my $comfirm = ); + if (lc $comfirm eq "n") { + exit 0; + } elsif (lc $comfirm eq "y") { + last; + } + } + } exec("nix-shell", "-p", $package, "--run", shell_quote("exec", @ARGV)); } else { print STDERR <{package}\n"; + } + my $choice = 0; + while (1) { # exec will break this loop + no warnings "numeric"; + print STDERR "Your choice [1-${len}]: "; + # 0 can be invalid user input like non-number string + # so we start from 1 + $choice = + 0; + if (1 <= $choice && $choice <= $len) { + exec("nix-shell", "-p", @$res[$choice - 1]->{package}, + "--run", shell_quote("exec", @ARGV)); + } + } + } else { + print STDERR <{package}\n" foreach @$res; + print STDERR " nix-shell -p $_->{package}\n" foreach @$res; + } } exit 127; diff --git a/modules/programs/command-not-found/default.nix b/modules/programs/command-not-found/default.nix index fe14ed484..3181c9753 100644 --- a/modules/programs/command-not-found/default.nix +++ b/modules/programs/command-not-found/default.nix @@ -18,21 +18,6 @@ let commandNotFound = pkgs.runCommand "command-not-found" { } '' install -Dm555 ${cnfScript} $out/bin/command-not-found ''; - - shInit = commandNotFoundHandlerName: '' - # This function is called whenever a command is not found. - ${commandNotFoundHandlerName}() { - local p=${commandNotFound}/bin/command-not-found - if [ -x $p -a -f ${cfg.dbPath} ]; then - # Run the helper program. - $p "$@" - else - echo "$1: command not found" >&2 - return 127 - fi - } - ''; - in { options.programs.command-not-found = { @@ -50,8 +35,24 @@ in }; config = lib.mkIf cfg.enable { - programs.bash.initExtra = shInit "command_not_found_handle"; - programs.zsh.initContent = shInit "command_not_found_handler"; + programs.bash.initExtra = '' + command_not_found_handle() { + '${commandNotFound}/bin/command-not-found' "$@" + } + ''; + + programs.zsh.initContent = '' + command_not_found_handler() { + '${commandNotFound}/bin/command-not-found' "$@" + } + ''; + + # NOTE: Fish by itself checks for nixos command-not-found, let's instead makes it explicit. + programs.fish.interactiveShellInit = '' + function fish_command_not_found + "${commandNotFound}/bin/command-not-found" $argv + end + ''; home.packages = [ commandNotFound ]; };