neomutt: add module

PR #1002
This commit is contained in:
Matthieu Coudron 2020-01-22 00:52:01 +01:00 committed by Robert Helgesson
parent d8d5f85ab7
commit fba87f8998
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
10 changed files with 477 additions and 0 deletions

View file

@ -391,6 +391,7 @@ in
(import ../programs/getmail-accounts.nix) (import ../programs/getmail-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

@ -1311,6 +1311,13 @@ in
A new module is available: 'services.grobi'. A new module is available: 'services.grobi'.
''; '';
} }
{
time = "2020-01-26T19:37:57+00:00";
message = ''
A new module is available: 'programs.neomutt'.
'';
}
]; ];
}; };
} }

View file

@ -78,6 +78,7 @@ let
(loadModule ./programs/mercurial.nix { }) (loadModule ./programs/mercurial.nix { })
(loadModule ./programs/mpv.nix { }) (loadModule ./programs/mpv.nix { })
(loadModule ./programs/msmtp.nix { }) (loadModule ./programs/msmtp.nix { })
(loadModule ./programs/neomutt.nix { })
(loadModule ./programs/neovim.nix { }) (loadModule ./programs/neovim.nix { })
(loadModule ./programs/newsboat.nix { }) (loadModule ./programs/newsboat.nix { })
(loadModule ./programs/noti.nix { }) (loadModule ./programs/noti.nix { })

View file

@ -0,0 +1,35 @@
{ config, lib, ... }:
with lib;
{
options.neomutt = {
enable = mkEnableOption "NeoMutt";
sendMailCommand = mkOption {
type = types.nullOr types.str;
default = null;
example = "msmtpq --read-envelope-from --read-recipients";
description = ''
Command to send a mail. If not set, neomutt will be in charge of sending mails.
'';
};
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 config.neomutt.enable {
neomutt.sendMailCommand = mkOptionDefault (
if config.msmtp.enable
then "msmtpq --read-envelope-from --read-recipients"
else null
);
};
}

View file

@ -0,0 +1,319 @@
{ 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.
'';
};
};
};
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";
smtpBaseUrl = "${smtpProto}://${escape userName}@${smtp.host}";
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 {
type = types.enum [
"date"
"date-received"
"from"
"mailbox-order"
"score"
"size"
"spam"
"subject"
"threads"
"to"
];
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.";
};
};
};
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)}";
};
};
}

View file

@ -33,6 +33,7 @@ import nmt {
// import ./modules/misc/xdg // import ./modules/misc/xdg
// import ./modules/misc/xsession // import ./modules/misc/xsession
// import ./modules/programs/firefox // import ./modules/programs/firefox
// import ./modules/programs/neomutt
// import ./modules/programs/rofi // import ./modules/programs/rofi
// import ./modules/services/sxhkd // import ./modules/services/sxhkd
// import ./modules/systemd // import ./modules/systemd

View file

@ -0,0 +1,3 @@
{
neomutt-simple = ./neomutt.nix;
}

View file

@ -0,0 +1,37 @@
# Generated by Home Manager.
set ssl_force_tls = yes
set certificate_file=/etc/ssl/certs/ca-certificates.crt
# GPG section
set crypt_use_gpgme = yes
set crypt_autosign = no
set pgp_use_gpg_agent = yes
set mbox_type = Maildir
set sort = "threads"
# MTA section
set smtp_pass='`password-command`'
set smtp_url='smtps://home.manager@smtp.example.com'
# MRA section
set folder='/home/hm-user/Mail/hm@example.com'
set from='hm@example.com'
set postponed='+Drafts'
set realname='H. M. Test'
set record='+Sent'
set spoolfile='+Inbox'
set trash='+Trash'
color status cyan default
# Extra configuration
color status cyan default
# notmuch section
set nm_default_uri = "notmuch:///home/hm-user/Mail"
virtual-mailboxes "My INBOX" "notmuch://?query=tag:inbox"

View file

@ -0,0 +1,27 @@
# Generated by Home Manager.
set header_cache = "/home/hm-user/.cache/neomutt/headers/"
set message_cachedir = "/home/hm-user/.cache/neomutt/messages/"
set editor = "$EDITOR"
set implicit_autoview = yes
alternative_order text/enriched text/plain text
set delete = yes
# Binds
# Macros
# Extra configuration
# register account hm@example.com
mailboxes "/home/hm-user/Mail/hm@example.com/Inbox"
folder-hook /home/hm-user/Mail/hm@example.com/ " \
source /home/hm-user/.config/neomutt/hm@example.com "
source /home/hm-user/.config/neomutt/hm@example.com

View file

@ -0,0 +1,46 @@
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ../../accounts/email-test-accounts.nix ];
config = {
home.username = "hm-user";
home.homeDirectory = "/home/hm-user";
xdg.configHome = mkForce "/home/hm-user/.config";
xdg.cacheHome = mkForce "/home/hm-user/.cache";
accounts.email.accounts = {
"hm@example.com" = {
primary = true;
notmuch.enable = true;
neomutt = {
enable = true;
extraConfig = ''
color status cyan default
'';
};
imap.port = 993;
};
};
programs.neomutt = {
enable = true;
vimKeys = false;
};
nixpkgs.overlays = [
(self: super: {
neomutt = pkgs.writeScriptBin "dummy-neomutt" "";
})
];
nmt.script = ''
assertFileExists home-files/.config/neomutt/neomuttrc
assertFileExists home-files/.config/neomutt/hm@example.com
assertFileContent home-files/.config/neomutt/neomuttrc ${./neomutt-expected.conf}
assertFileContent home-files/.config/neomutt/hm@example.com ${./hm-example.com-expected}
'';
};
}