diff --git a/modules/accounts/email.nix b/modules/accounts/email.nix index 857622c0..f7bba080 100644 --- a/modules/accounts/email.nix +++ b/modules/accounts/email.nix @@ -383,6 +383,7 @@ in (import ../programs/astroid-accounts.nix) (import ../programs/mbsync-accounts.nix) (import ../programs/msmtp-accounts.nix) + (import ../programs/neomutt-accounts.nix) (import ../programs/notmuch-accounts.nix) (import ../programs/offlineimap-accounts.nix) ]); diff --git a/modules/programs/neomutt-accounts.nix b/modules/programs/neomutt-accounts.nix new file mode 100644 index 00000000..89444901 --- /dev/null +++ b/modules/programs/neomutt-accounts.nix @@ -0,0 +1,42 @@ +{ config, lib, ... }: + +with lib; + +{ + options.neomutt = { + enable = mkOption { + type = types.enum [ "none" "maildir" "imap" ]; + default = "none"; + description = '' + Whether to enable this account in NeoMutt and which + account protocol to use. + ''; + }; + + imap.idle = mkOption { + type = types.bool; + default = false; + description = '' + If set, NeoMutt will attempt to use the IDLE extension. + ''; + }; + + mailboxes = mkOption { + type = types.listOf types.str; + default = []; + example = ["github" "Lists/nix" "Lists/haskell-cafe"]; + description = '' + A list of mailboxes. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = "color status cyan default"; + description = '' + Extra lines to add to the folder hook for this account. + ''; + }; + }; +} diff --git a/modules/programs/neomutt.nix b/modules/programs/neomutt.nix index 269ceb6b..4c0afdaf 100644 --- a/modules/programs/neomutt.nix +++ b/modules/programs/neomutt.nix @@ -77,11 +77,15 @@ let }; }; - yesno = x: if x then "yes" else "no"; setOption = n: v: if v == null then "unset ${n}" else "set ${n}=${v}"; escape = replaceStrings ["%"] ["%25"]; + # Path to the file that will contain the account folder-hook + # settings. + genAccountFileName = accountName: + "${config.xdg.configHome}/neomutt/account-${accountName}"; + genCommonFolderHooks = account: with account; let smtpProto = if smtp.tls.enable then "smtps" else "smtp"; @@ -103,25 +107,35 @@ let smtp_pass = "'`${passCmd}`'"; }; + genFolderHookFile = account: customConf: with account; + { + target = genAccountFileName name; + source = pkgs.writeText "neomutt-account-${name}" ( + concatStringsSep "\n" ( + [ "# Generated by Home Manager.\n" ] + ++ mapAttrsToList setOption (genCommonFolderHooks account // customConf) + ++ optional (neomutt.extraConfig != "") neomutt.extraConfig + ) + ); + }; + genMaildirAccountConfig = account: with account; let - folderHook = - mapAttrsToList setOption ( - genCommonFolderHooks account - // { - folder = "'${account.maildir.absPath}'"; - } - ) - ++ optional (neomutt.extraConfig != "") neomutt.extraConfig; + accountFileName = genAccountFileName name; in - '' - mailboxes ${account.maildir.absPath}/${folders.inbox} - folder-hook ${account.maildir.absPath}/ " \ - ${concatStringsSep "; \\\n " folderHook}" - '' - + optionalString account.primary '' - ${concatStringsSep "\n" folderHook} - ''; + { + mainConfig = '' + mailboxes ${account.maildir.absPath}/${folders.inbox} + folder-hook ${account.maildir.absPath}/ source ${accountFileName} + '' + + optionalString account.primary '' + source ${genAccountFileName name} + ''; + + folderHookFile = genFolderHookFile account { + folder = "'${account.maildir.absPath}'"; + }; + }; genImapAccountConfig = account: with account; let @@ -130,30 +144,29 @@ let imapBaseUrlEscape = "${imapProto}://${escape userName}@${imap.host}"; passCmd = concatStringsSep " " passwordCommand; - folderHook = - mapAttrsToList setOption ( - genCommonFolderHooks account - // { - imap_user="'${userName}'"; - imap_pass="'`${passCmd}`'"; - imap_idle = yesno neomutt.imap.idle; - folder = "'${imapBaseUrlEscape}'"; - } - ) - ++ optional (neomutt.extraConfig != "") neomutt.extraConfig; + accountFileName = genAccountFileName name; in - '' - account-hook '${imapBaseUrl}/' " \ - set imap_user='${userName}' \ - set imap_pass='`${passCmd}`' \ - unset tunnel" - mailboxes '${imapBaseUrlEscape}/${folders.inbox}' - folder-hook '${imapBaseUrlEscape}/' " \ - ${concatStringsSep "; \\\n " folderHook}" - '' - + optionalString account.primary '' - ${concatStringsSep "\n" folderHook} - ''; + { + mainConfig = + '' + account-hook '${imapBaseUrl}/' " \ + set imap_user='${userName}' \ + set imap_pass='`${passCmd}`' \ + unset tunnel" + mailboxes '${imapBaseUrlEscape}/${folders.inbox}' + folder-hook '${imapBaseUrlEscape}/' source ${accountFileName}" + '' + + optionalString account.primary '' + source ${genAccountFileName name} + ''; + + folderHookFile = genFolderHookFile account { + imap_user="'${userName}'"; + imap_pass="'`${passCmd}`'"; + imap_idle = yesno neomutt.imap.idle; + folder = "'${imapBaseUrlEscape}'"; + }; + }; genAccountConfig = account: with account; if account.neomutt.enable == "imap" && account.imap != null then @@ -161,7 +174,85 @@ let else if account.neomutt.enable == "maildir" && account.maildir != null then genMaildirAccountConfig account else - ""; + null; + + accountConfigs = map genAccountConfig neomuttAccounts; + + neomuttrcStr = + let + + gpgSection = '' + set crypt_use_gpgme = yes + set crypt_autosign = yes + set pgp_use_gpg_agent = yes + ''; + + 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; + + accountsSection = + concatMapStringsSep "\n" (getAttr "mainConfig") accountConfigs; + + mailCheckSection = '' + set mail_check_stats + set mail_check_stats_interval = ${toString cfg.checkStatsInterval} + ''; + + in + + # Some defaults are left unconfigurable + '' + # Generated by Home Manager. + + set ssl_force_tls = yes + + set mbox_type = Maildir + set sort = "${cfg.sort}" + set header_cache = "${config.xdg.cacheHome}/neomutt/headers/" + set message_cachedir = "${config.xdg.cacheHome}/neomutt/messages/" + set editor = "${cfg.editor}" + + ${optionalString cfg.gpg gpgSection} + + ${optionalString (cfg.checkStatsInterval != null) mailCheckSection} + + set implicit_autoview = yes + + alternative_order text/enriched text/plain text + + set delete = yes + + # Binds and macros + ${bindSection} + ${macroSection} + + ${optionalString cfg.sidebar.enable sidebarSection} + + ${optionalString (cfg.theme != null) "source ${cfg.theme}"} + + ${accountsSection} + + # Extra configuration + ${cfg.extraConfig} + ''; + in { @@ -241,49 +332,6 @@ in description = "Extra configuration appended to the end."; }; }; - - accounts.email.accounts = mkOption { - options = [ - { - neomutt = { - enable = mkOption { - type = types.enum [ "none" "maildir" "imap" ]; - default = "none"; - description = '' - Whether to enable this account in NeoMutt and which - account protocol to use. - ''; - }; - - imap.idle = mkOption { - type = types.bool; - default = false; - description = '' - If set, neomutt will attempt to use the IDLE extension. - ''; - }; - - mailboxes = mkOption { - type = types.listOf types.str; - default = []; - example = ["github" "Lists/nix" "Lists/haskell-cafe"]; - description = '' - A list of mailboxes. - ''; - }; - - extraConfig = mkOption { - type = types.lines; - default = ""; - example = "color status cyan default"; - description = '' - Extra lines to add to the folder hook for this account. - ''; - }; - }; - } - ]; - }; }; config = mkIf cfg.enable { @@ -319,79 +367,8 @@ in home.packages = [ pkgs.neomutt ]; - xdg.configFile."neomutt/neomuttrc".text = - let + home.file = map (getAttr "folderHookFile") accountConfigs; - gpgSection = '' - set crypt_use_gpgme = yes - set crypt_autosign = yes - set pgp_use_gpg_agent = yes - ''; - - 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; - - accountsSection = - concatMapStringsSep "\n" genAccountConfig neomuttAccounts; - - mailCheckSection = '' - set mail_check_stats - set mail_check_stats_interval = ${toString cfg.checkStatsInterval} - ''; - - in - - # Some defaults are left unconfigurable - '' - # Generated by Home Manager. - - set ssl_force_tls = yes - - set mbox_type = Maildir - set sort = "${cfg.sort}" - set header_cache = "${config.xdg.cacheHome}/neomutt/headers/" - set message_cachedir = "${config.xdg.cacheHome}/neomutt/messages/" - set editor = "${cfg.editor}" - - ${optionalString cfg.gpg gpgSection} - - ${optionalString (cfg.checkStatsInterval != null) mailCheckSection} - - set implicit_autoview = yes - - alternative_order text/enriched text/plain text - - set delete = yes - - # Binds and macros - ${bindSection} - ${macroSection} - - ${optionalString cfg.sidebar.enable sidebarSection} - - ${optionalString (cfg.theme != null) "source ${cfg.theme}"} - - ${accountsSection} - - # Extra configuration - ${cfg.extraConfig} - ''; + xdg.configFile."neomutt/neomuttrc".text = neomuttrcStr; }; }