1# This module provides suggestions of packages to install if the user
2# tries to run a missing command in Bash. This is implemented using a
3# SQLite database that maps program names to Nix package names (e.g.,
4# "pdflatex" is mapped to "tetex").
5
6{ config, lib, pkgs, ... }:
7
8with lib;
9
10let
11
12 commandNotFound = pkgs.substituteAll {
13 name = "command-not-found";
14 dir = "bin";
15 src = ./command-not-found.pl;
16 isExecutable = true;
17 inherit (pkgs) perl;
18 perlFlags = concatStrings (map (path: "-I ${path}/lib/perl5/site_perl ")
19 [ pkgs.perlPackages.DBI pkgs.perlPackages.DBDSQLite pkgs.perlPackages.StringShellQuote ]);
20 };
21
22in
23
24{
25
26 programs.bash.interactiveShellInit =
27 ''
28 # This function is called whenever a command is not found.
29 command_not_found_handle() {
30 local p=/run/current-system/sw/bin/command-not-found
31 if [ -x $p -a -f /nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite ]; then
32 # Run the helper program.
33 $p "$@"
34 # Retry the command if we just installed it.
35 if [ $? = 126 ]; then
36 "$@"
37 else
38 return 127
39 fi
40 else
41 echo "$1: command not found" >&2
42 return 127
43 fi
44 }
45 '';
46
47 programs.zsh.interactiveShellInit =
48 ''
49 # This function is called whenever a command is not found.
50 command_not_found_handler() {
51 local p=/run/current-system/sw/bin/command-not-found
52 if [ -x $p -a -f /nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite ]; then
53 # Run the helper program.
54 $p "$@"
55
56 # Retry the command if we just installed it.
57 if [ $? = 126 ]; then
58 "$@"
59 fi
60 else
61 # Indicate than there was an error so ZSH falls back to its default handler
62 return 127
63 fi
64 }
65 '';
66
67 environment.systemPackages = [ commandNotFound ];
68
69 # TODO: tab completion for uninstalled commands! :-)
70
71}