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 cfg = config.programs.command-not-found;
12 commandNotFound = pkgs.substituteAll {
13 name = "command-not-found";
14 dir = "bin";
15 src = ./command-not-found.pl;
16 isExecutable = true;
17 inherit (cfg) dbPath;
18 perl = pkgs.perl.withPackages (p: [ p.DBDSQLite p.StringShellQuote ]);
19 };
20
21in
22
23{
24 options.programs.command-not-found = {
25
26 enable = mkOption {
27 type = types.bool;
28 default = true;
29 description = lib.mdDoc ''
30 Whether interactive shells should show which Nix package (if
31 any) provides a missing command.
32 '';
33 };
34
35 dbPath = mkOption {
36 default = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite" ;
37 description = lib.mdDoc ''
38 Absolute path to programs.sqlite.
39
40 By default this file will be provided by your channel
41 (nixexprs.tar.xz).
42 '';
43 type = types.path;
44 };
45 };
46
47 config = mkIf cfg.enable {
48 programs.bash.interactiveShellInit =
49 ''
50 # This function is called whenever a command is not found.
51 command_not_found_handle() {
52 local p='${commandNotFound}/bin/command-not-found'
53 if [ -x "$p" ] && [ -f '${cfg.dbPath}' ]; then
54 # Run the helper program.
55 "$p" "$@"
56 # Retry the command if we just installed it.
57 if [ $? = 126 ]; then
58 "$@"
59 else
60 return 127
61 fi
62 else
63 echo "$1: command not found" >&2
64 return 127
65 fi
66 }
67 '';
68
69 programs.zsh.interactiveShellInit =
70 ''
71 # This function is called whenever a command is not found.
72 command_not_found_handler() {
73 local p='${commandNotFound}/bin/command-not-found'
74 if [ -x "$p" ] && [ -f '${cfg.dbPath}' ]; then
75 # Run the helper program.
76 "$p" "$@"
77
78 # Retry the command if we just installed it.
79 if [ $? = 126 ]; then
80 "$@"
81 else
82 return 127
83 fi
84 else
85 # Indicate than there was an error so ZSH falls back to its default handler
86 echo "$1: command not found" >&2
87 return 127
88 fi
89 }
90 '';
91
92 environment.systemPackages = [ commandNotFound ];
93 };
94
95}