aboutsummaryrefslogtreecommitdiff
path: root/home-manager/modules/programs/neomutt.nix
diff options
context:
space:
mode:
Diffstat (limited to 'home-manager/modules/programs/neomutt.nix')
-rw-r--r--home-manager/modules/programs/neomutt.nix312
1 files changed, 312 insertions, 0 deletions
diff --git a/home-manager/modules/programs/neomutt.nix b/home-manager/modules/programs/neomutt.nix
new file mode 100644
index 00000000000..f2a6bbfff08
--- /dev/null
+++ b/home-manager/modules/programs/neomutt.nix
@@ -0,0 +1,312 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.programs.neomutt;
+
+ neomuttAccounts =
+ filter (a: a.neomutt.enable) (attrValues config.accounts.email.accounts);
+
+ sidebarModule = types.submodule {
+ options = {
+ enable = mkEnableOption "sidebar support";
+
+ width = mkOption {
+ type = types.int;
+ default = 22;
+ description = "Width of the sidebar";
+ };
+
+ shortPath = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ By default sidebar shows the full path of the mailbox, but
+ with this enabled only the relative name is shown.
+ '';
+ };
+
+ format = mkOption {
+ type = types.str;
+ default = "%B%?F? [%F]?%* %?N?%N/?%S";
+ description = ''
+ Sidebar format. Check neomutt documentation for details.
+ '';
+ };
+ };
+ };
+
+ sortOptions = [
+ "date"
+ "date-received"
+ "from"
+ "mailbox-order"
+ "score"
+ "size"
+ "spam"
+ "subject"
+ "threads"
+ "to"
+ ];
+
+ bindModule = types.submodule {
+ options = {
+ map = mkOption {
+ type = types.enum [
+ "alias"
+ "attach"
+ "browser"
+ "compose"
+ "editor"
+ "generic"
+ "index"
+ "mix"
+ "pager"
+ "pgp"
+ "postpone"
+ "query"
+ "smime"
+ ];
+ default = "index";
+ description = "Select the menu to bind the command to.";
+ };
+
+ key = mkOption {
+ type = types.str;
+ example = "<left>";
+ description = "The key to bind.";
+ };
+
+ action = mkOption {
+ type = types.str;
+ example = "<enter-command>toggle sidebar_visible<enter><refresh>";
+ description = "Specify the action to take.";
+ };
+ };
+ };
+
+ yesno = x: if x then "yes" else "no";
+ setOption = n: v: if v == null then "unset ${n}" else "set ${n}=${v}";
+ escape = replaceStrings [ "%" ] [ "%25" ];
+
+ accountFilename = account: config.xdg.configHome + "/neomutt/" + account.name;
+
+ genCommonFolderHooks = account:
+ with account; {
+ from = "'${address}'";
+ realname = "'${realName}'";
+ spoolfile = "'+${folders.inbox}'";
+ record = if folders.sent == null then null else "'+${folders.sent}'";
+ postponed = "'+${folders.drafts}'";
+ trash = "'+${folders.trash}'";
+ };
+
+ mtaSection = account:
+ with account;
+ let passCmd = concatStringsSep " " passwordCommand;
+ in if neomutt.sendMailCommand != null then {
+ sendmail = "'${neomutt.sendMailCommand}'";
+ } else
+ let
+ smtpProto = if smtp.tls.enable then "smtps" else "smtp";
+ smtpPort = if smtp.port != null then ":${toString smtp.port}" else "";
+ smtpBaseUrl =
+ "${smtpProto}://${escape userName}@${smtp.host}${smtpPort}";
+ in {
+ smtp_url = "'${smtpBaseUrl}'";
+ smtp_pass = "'`${passCmd}`'";
+ };
+
+ genMaildirAccountConfig = account:
+ with account;
+ let
+ folderHook = mapAttrsToList setOption (genCommonFolderHooks account // {
+ folder = "'${account.maildir.absPath}'";
+ }) ++ optional (neomutt.extraConfig != "") neomutt.extraConfig;
+ in ''
+ ${concatStringsSep "\n" folderHook}
+ '';
+
+ registerAccount = account:
+ with account; ''
+ # register account ${name}
+ mailboxes "${account.maildir.absPath}/${folders.inbox}"
+ folder-hook ${account.maildir.absPath}/ " \
+ source ${accountFilename account} "
+ '';
+
+ mraSection = account:
+ with account;
+ if account.maildir != null then
+ genMaildirAccountConfig account
+ else
+ throw "Only maildir is supported at the moment";
+
+ optionsStr = attrs: concatStringsSep "\n" (mapAttrsToList setOption attrs);
+
+ sidebarSection = ''
+ # Sidebar
+ set sidebar_visible = yes
+ set sidebar_short_path = ${yesno cfg.sidebar.shortPath}
+ set sidebar_width = ${toString cfg.sidebar.width}
+ set sidebar_format = '${cfg.sidebar.format}'
+ '';
+
+ bindSection = concatMapStringsSep "\n"
+ (bind: ''bind ${bind.map} ${bind.key} "${bind.action}"'') cfg.binds;
+
+ macroSection = concatMapStringsSep "\n"
+ (bind: ''macro ${bind.map} ${bind.key} "${bind.action}"'') cfg.macros;
+
+ mailCheckSection = ''
+ set mail_check_stats
+ set mail_check_stats_interval = ${toString cfg.checkStatsInterval}
+ '';
+
+ notmuchSection = account:
+ with account; ''
+ # notmuch section
+ set nm_default_uri = "notmuch://${config.accounts.email.maildirBasePath}"
+ virtual-mailboxes "My INBOX" "notmuch://?query=tag:inbox"
+ '';
+
+ accountStr = account:
+ with account;
+ ''
+ # Generated by Home Manager.
+ set ssl_force_tls = yes
+ set certificate_file=${config.accounts.email.certificatesFile}
+
+ # GPG section
+ set crypt_use_gpgme = yes
+ set crypt_autosign = ${yesno (gpg.signByDefault or false)}
+ set pgp_use_gpg_agent = yes
+ set mbox_type = ${if maildir != null then "Maildir" else "mbox"}
+ set sort = "${cfg.sort}"
+
+ # MTA section
+ ${optionsStr (mtaSection account)}
+
+ ${optionalString (cfg.checkStatsInterval != null) mailCheckSection}
+
+ ${optionalString cfg.sidebar.enable sidebarSection}
+
+ # MRA section
+ ${mraSection account}
+
+ # Extra configuration
+ ${account.neomutt.extraConfig}
+ '' + optionalString (account.signature.showSignature != "none") ''
+ set signature = ${pkgs.writeText "signature.txt" account.signature.text}
+ '' + optionalString account.notmuch.enable (notmuchSection account);
+
+in {
+ options = {
+ programs.neomutt = {
+ enable = mkEnableOption "the NeoMutt mail client";
+
+ sidebar = mkOption {
+ type = sidebarModule;
+ default = { };
+ description = "Options related to the sidebar.";
+ };
+
+ binds = mkOption {
+ type = types.listOf bindModule;
+ default = [ ];
+ description = "List of keybindings.";
+ };
+
+ macros = mkOption {
+ type = types.listOf bindModule;
+ default = [ ];
+ description = "List of macros.";
+ };
+
+ sort = mkOption {
+ # allow users to choose any option from sortOptions, or any option prefixed with "reverse-"
+ type = types.enum
+ (sortOptions ++ (map (option: "reverse-" + option) sortOptions));
+ default = "threads";
+ description = "Sorting method on messages.";
+ };
+
+ vimKeys = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enable vim-like bindings.";
+ };
+
+ checkStatsInterval = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ example = 60;
+ description = "Enable and set the interval of automatic mail check.";
+ };
+
+ editor = mkOption {
+ type = types.str;
+ default = "$EDITOR";
+ description = "Select the editor used for writing mail.";
+ };
+
+ settings = mkOption {
+ type = types.attrsOf types.str;
+ default = { };
+ description = "Extra configuration appended to the end.";
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Extra configuration appended to the end.";
+ };
+ };
+
+ accounts.email.accounts = mkOption {
+ type = with types; attrsOf (submodule (import ./neomutt-accounts.nix));
+ };
+ };
+
+ config = mkIf cfg.enable {
+ home.packages = [ pkgs.neomutt ];
+ home.file = let
+ rcFile = account: {
+ "${accountFilename account}".text = accountStr account;
+ };
+ in foldl' (a: b: a // b) { } (map rcFile neomuttAccounts);
+
+ xdg.configFile."neomutt/neomuttrc" = mkIf (neomuttAccounts != [ ]) {
+ text = let primary = filter (a: a.primary) neomuttAccounts;
+ in ''
+ # Generated by Home Manager.
+ set header_cache = "${config.xdg.cacheHome}/neomutt/headers/"
+ set message_cachedir = "${config.xdg.cacheHome}/neomutt/messages/"
+ set editor = "${cfg.editor}"
+ set implicit_autoview = yes
+
+ alternative_order text/enriched text/plain text
+
+ set delete = yes
+
+ # Binds
+ ${bindSection}
+
+ # Macros
+ ${macroSection}
+
+ ${optionalString cfg.vimKeys
+ "source ${pkgs.neomutt}/share/doc/neomutt/vim-keys/vim-keys.rc"}
+
+ # Extra configuration
+ ${optionsStr cfg.settings}
+
+ ${cfg.extraConfig}
+ '' + concatMapStringsSep "\n" registerAccount neomuttAccounts +
+ # source primary account
+ "source ${accountFilename (builtins.head primary)}";
+ };
+ };
+}