Compare commits

...

2 commits

Author SHA1 Message Date
Robert Helgesson 0fa829cd11
wip 2018-11-29 00:46:44 +01:00
Mats Rauhala f7dad7ef24
neomutt: add module
Co-authored-by: Robert Helgesson <robert@rycee.net>
2018-11-26 21:44:20 +01:00
5 changed files with 425 additions and 0 deletions

View file

@ -383,6 +383,7 @@ in
(import ../programs/astroid-accounts.nix) (import ../programs/astroid-accounts.nix)
(import ../programs/mbsync-accounts.nix) (import ../programs/mbsync-accounts.nix)
(import ../programs/msmtp-accounts.nix) (import ../programs/msmtp-accounts.nix)
(import ../programs/neomutt-accounts.nix)
(import ../programs/notmuch-accounts.nix) (import ../programs/notmuch-accounts.nix)
(import ../programs/offlineimap-accounts.nix) (import ../programs/offlineimap-accounts.nix)
]); ]);

View file

@ -878,6 +878,13 @@ in
A new module is available: 'programs.vscode'. A new module is available: 'programs.vscode'.
''; '';
} }
{
time = "2018-11-26T20:42:42+00:00";
message = ''
A new module is available: 'programs.neomutt'.
'';
}
]; ];
}; };
} }

View file

@ -52,6 +52,7 @@ let
./programs/mbsync.nix ./programs/mbsync.nix
./programs/mercurial.nix ./programs/mercurial.nix
./programs/msmtp.nix ./programs/msmtp.nix
./programs/neomutt.nix
./programs/neovim.nix ./programs/neovim.nix
./programs/newsboat.nix ./programs/newsboat.nix
./programs/noti.nix ./programs/noti.nix

View file

@ -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.
'';
};
};
}

View file

@ -0,0 +1,374 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.neomutt;
# Accounts for which neomutt is enabled.
neomuttAccounts =
filter
(a: a.neomutt.enable != "none")
(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.string;
default = "%B%?F? [%F]?%* %?N?%N/?%S";
description = ''
Sidebar format. Check neomutt documentation for details.
'';
};
};
};
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.string;
example = "<left>";
description = "The key to bind.";
};
action = mkOption {
type = types.string;
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"];
# 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";
smtpBaseUrl = "${smtpProto}://${escape userName}@${smtp.host}";
passCmd = concatStringsSep " " passwordCommand;
in
{
imap_user = null;
imap_pass = null;
imap_idle = null;
tunnel = null;
from = "'${address}'";
realname = "'${realName}'";
spoolfile = "'+${folders.inbox}'";
record = if folders.sent == null then null else "'+${folders.sent}'";
postponed = "'+${folders.drafts}'";
trash = "'+${folders.trash}'";
smtp_url = "'${smtpBaseUrl}'";
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
accountFileName = genAccountFileName name;
in
{
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
imapProto = if imap.tls.enable then "imaps" else "imap";
imapBaseUrl = "${imapProto}://${userName}@${imap.host}";
imapBaseUrlEscape = "${imapProto}://${escape userName}@${imap.host}";
passCmd = concatStringsSep " " passwordCommand;
accountFileName = genAccountFileName name;
in
{
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
genImapAccountConfig account
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
{
options = {
programs.neomutt = {
enable = mkEnableOption "the neomutt mail client";
gpg = mkOption {
type = types.bool;
default = false;
description = "Enable gpg support.";
};
sidebar = mkOption {
type = sidebarModule;
default = {};
description = "Options related to the sidebar.";
};
binds = mkOption {
type = types.listOf bindModule;
default = [];
description = "List of keybindings.";
};
macros = mkOption {
# I'm sharing the definition of bind, because the fields are pretty
# much the same
type = types.listOf bindModule;
default = [];
description = "List of macros.";
};
sort = mkOption {
type = types.enum [
"date"
"date-received"
"from"
"mailbox-order"
"score"
"size"
"spam"
"subject"
"threads"
"to"
];
default = "threads";
description = "Sorting method on messages.";
};
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.string;
default = "$EDITOR";
example = "${pkgs.nano}/bin/nano";
description = "Select the editor used for writing mail.";
};
theme = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to theme file. Warning this can contain any muttrc
configuration, including system calls.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Extra configuration appended to the end.";
};
};
};
config = mkIf cfg.enable {
assertions = [
(
let
badAccounts =
filter
(a: a.neomutt.enable == "maildir" && a.maildir == null)
neomuttAccounts;
in
{
assertion = badAccounts == [];
message = "neomutt: Missing maildir configuration for accounts: "
+ concatMapStringsSep ", " (a: a.name) badAccounts;
}
)
(
let
badAccounts =
filter
(a: a.neomutt.enable == "imap" && a.imap == null)
neomuttAccounts;
in
{
assertion = badAccounts == [];
message = "neomutt: Missing IMAP configuration for accounts: "
+ concatMapStringsSep ", " (a: a.name) badAccounts;
}
)
];
home.packages = [ pkgs.neomutt ];
home.file = map (getAttr "folderHookFile") accountConfigs;
xdg.configFile."neomutt/neomuttrc".text = neomuttrcStr;
};
}