diff options
Diffstat (limited to 'infra/libkookie/home-manager/modules/programs/neomutt.nix')
-rw-r--r-- | infra/libkookie/home-manager/modules/programs/neomutt.nix | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/infra/libkookie/home-manager/modules/programs/neomutt.nix b/infra/libkookie/home-manager/modules/programs/neomutt.nix new file mode 100644 index 000000000000..d990f02eaca5 --- /dev/null +++ b/infra/libkookie/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}'"; + }); + 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)}"; + }; + }; +} |