aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-08-11 00:12:24 +0530
committerArpit Chakladar <arpitchakladar+git@gmail.com>2026-08-29 03:50:19 +0530
commit3e5c85e31ade2101a432be7ee4183c77417d156c (patch)
treef5d8efc061a7875e6267242a027c2825861ae3e2
parent88d8ddb9e64ca3663c6fbff05727994d93529a0d (diff)
downloadhome-manager-config-3e5c85e31ade2101a432be7ee4183c77417d156c.tar.gz
home-manager-config-3e5c85e31ade2101a432be7ee4183c77417d156c.zip
feat: Using neovim for pager in neomutt, neomutt keybindings
- Using neovim as a pager for neomutt to make it easier to read emails - Using universal :q for back/exit instead of q
-rw-r--r--modules/communication/neomutt/assertions.nix8
-rw-r--r--modules/communication/neomutt/keybindings.nix601
-rw-r--r--modules/communication/neomutt/macros.nix12
-rw-r--r--modules/communication/neomutt/neomutt.nix13
-rw-r--r--modules/development/nixvim/colorscheme.nix46
5 files changed, 165 insertions, 515 deletions
diff --git a/modules/communication/neomutt/assertions.nix b/modules/communication/neomutt/assertions.nix
index aa28502..e8351f0 100644
--- a/modules/communication/neomutt/assertions.nix
+++ b/modules/communication/neomutt/assertions.nix
@@ -1,12 +1,12 @@
-# Validates neomutt dependencies are enabled
+# Validates NeoMutt dependencies.
{ config, ... }:
{
assertions = [
{
- assertion = !config.communication.neomutt.enable || config.terminal.less.enable;
+ assertion = !config.communication.neomutt.enable || config.development.nixvim.enable;
message = ''
- communication.neomutt is enabled but terminal.less.enable is not.
- neomutt requires less as its pager. Please enable terminal.less.
+ communication.neomutt is enabled but development.nixvim.enable is not.
+ neomutt uses Neovim as its external pager. Please enable development.nixvim.
'';
}
{
diff --git a/modules/communication/neomutt/keybindings.nix b/modules/communication/neomutt/keybindings.nix
index e9a3490..3d24daf 100644
--- a/modules/communication/neomutt/keybindings.nix
+++ b/modules/communication/neomutt/keybindings.nix
@@ -1,485 +1,132 @@
+# NeoMutt keybindings, grouped by the menu in which they apply.
{ config, lib, ... }:
+let
+ bind = keyMap: key: action: {
+ map = keyMap;
+ inherit key action;
+ };
+in
{
- config.programs.neomutt.binds = lib.mkIf config.communication.neomutt.enable (
- lib.mkMerge [
- # Force noop bindings to the top of neomuttrc
- (lib.mkBefore [
- {
- map = [
- "generic"
- "index"
- "pager"
- "browser"
- "attach"
- "compose"
- ];
- key = "z";
- action = "noop";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- "browser"
- "attach"
- "compose"
- ];
- key = "d";
- action = "noop";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- "browser"
- "attach"
- "compose"
- ];
- key = "g";
- action = "noop";
- }
- ])
+ config.programs.neomutt.binds = lib.mkIf config.communication.neomutt.enable [
+ # Index ------------------------------------------------------------------
+ (bind [ "index" ] "g" "noop")
+ (bind [ "index" ] "d" "noop")
+ (bind [ "index" ] "z" "noop")
+ (bind [ "index" ] ":" "noop")
+ (bind [ "index" ] "q" "noop")
+ (bind [ "index" ] ":q" "exit")
+ (bind [ "index" ] "j" "next-entry")
+ (bind [ "index" ] "k" "previous-entry")
+ (bind [ "index" ] "gg" "first-entry")
+ (bind [ "index" ] "G" "last-entry")
+ (bind [ "index" ] "l" "display-message")
+ (bind [ "index" ] "\\Cd" "half-down")
+ (bind [ "index" ] "\\Cu" "half-up")
+ (bind [ "index" ] "\\Cf" "next-page")
+ (bind [ "index" ] "\\Cb" "previous-page")
+ (bind [ "index" ] "/" "search")
+ (bind [ "index" ] "?" "search-reverse")
+ (bind [ "index" ] "n" "search-next")
+ (bind [ "index" ] "N" "search-opposite")
+ (bind [ "index" ] "gf" "limit")
+ (bind [ "index" ] "]" "next-unread")
+ (bind [ "index" ] "[" "previous-unread")
+ (bind [ "index" ] "}" "next-new")
+ (bind [ "index" ] "{" "previous-new")
+ (bind [ "index" ] "za" "collapse-thread")
+ (bind [ "index" ] "zA" "collapse-all")
+ (bind [ "index" ] "\\Ck" "sidebar-prev")
+ (bind [ "index" ] "\\Cj" "sidebar-next")
+ (bind [ "index" ] "\\Co" "sidebar-open")
+ (bind [ "index" ] "\\Cn" "sidebar-toggle-visible")
+ (bind [ "index" ] "m" "mail")
+ (bind [ "index" ] "r" "reply")
+ (bind [ "index" ] "R" "group-reply")
+ (bind [ "index" ] "f" "forward-message")
+ (bind [ "index" ] "c" "change-folder")
+ (bind [ "index" ] "s" "save-message")
+ (bind [ "index" ] "y" "copy-message")
+ (bind [ "index" ] "t" "tag-entry")
+ (bind [ "index" ] "T" "tag-thread")
+ (bind [ "index" ] "gh" "display-toggle-weed")
+ (bind [ "index" ] "dd" "delete-message")
+ (bind [ "index" ] "dT" "delete-thread")
+ (bind [ "index" ] "u" "undelete-message")
+ (bind [ "index" ] "U" "undelete-thread")
- # Other bindings
- [
- # Sidebar navigation
- {
- map = [
- "index"
- "pager"
- ];
- key = "\\Ck";
- action = "sidebar-prev";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "\\Cj";
- action = "sidebar-next";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "\\Co";
- action = "sidebar-open";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "\\Cn";
- action = "sidebar-toggle-visible";
- }
+ # Pager ------------------------------------------------------------------
+ # Messages open in Neovim via $pager. These bindings remain useful for
+ # NeoMutt's internal pager, e.g. when it is used by a mailcap viewer.
+ (bind [ "pager" ] "g" "noop")
+ (bind [ "pager" ] "d" "noop")
+ (bind [ "pager" ] ":" "noop")
+ (bind [ "pager" ] "q" "noop")
+ (bind [ "pager" ] ":q" "exit")
+ (bind [ "pager" ] "j" "next-line")
+ (bind [ "pager" ] "k" "previous-line")
+ (bind [ "pager" ] "gg" "top")
+ (bind [ "pager" ] "G" "bottom")
+ (bind [ "pager" ] "\\Cd" "half-down")
+ (bind [ "pager" ] "\\Cu" "half-up")
+ (bind [ "pager" ] "\\Cf" "next-page")
+ (bind [ "pager" ] "\\Cb" "previous-page")
+ (bind [ "pager" ] "/" "search")
+ (bind [ "pager" ] "?" "search-reverse")
+ (bind [ "pager" ] "n" "search-next")
+ (bind [ "pager" ] "N" "search-opposite")
+ (bind [ "pager" ] "\\Ck" "sidebar-prev")
+ (bind [ "pager" ] "\\Cj" "sidebar-next")
+ (bind [ "pager" ] "\\Co" "sidebar-open")
+ (bind [ "pager" ] "\\Cn" "sidebar-toggle-visible")
+ (bind [ "pager" ] "m" "mail")
+ (bind [ "pager" ] "r" "reply")
+ (bind [ "pager" ] "R" "group-reply")
+ (bind [ "pager" ] "f" "forward-message")
+ (bind [ "pager" ] "c" "change-folder")
+ (bind [ "pager" ] "s" "save-message")
+ (bind [ "pager" ] "y" "copy-message")
+ (bind [ "pager" ] "t" "tag-message")
+ (bind [ "pager" ] "gh" "display-toggle-weed")
+ (bind [ "pager" ] "dd" "delete-message")
+ (bind [ "pager" ] "u" "undelete-message")
- # Basic movement
- {
- map = [ "generic" ];
- key = "j";
- action = "next-entry";
- }
- {
- map = [ "generic" ];
- key = "k";
- action = "previous-entry";
- }
- {
- map = [ "generic" ];
- key = "gg";
- action = "first-entry";
- }
- {
- map = [
- "index"
- "generic"
- ];
- key = "G";
- action = "last-entry";
- }
- {
- map = [ "pager" ];
- key = "h";
- action = "exit";
- }
- {
- map = [ "pager" ];
- key = "l";
- action = "view-attachments";
- }
- {
- map = [ "index" ];
- key = "l";
- action = "display-message";
- }
+ # Attachment menu --------------------------------------------------------
+ (bind [ "attach" ] "g" "noop")
+ (bind [ "attach" ] "d" "noop")
+ (bind [ "attach" ] ":" "noop")
+ (bind [ "attach" ] "q" "noop")
+ (bind [ "attach" ] ":q" "exit")
+ (bind [ "attach" ] "j" "next-entry")
+ (bind [ "attach" ] "k" "previous-entry")
+ (bind [ "attach" ] "l" "view-attach")
+ (bind [ "attach" ] "s" "save-entry")
+ (bind [ "attach" ] "p" "print-entry")
+ (bind [ "attach" ] "dd" "delete-entry")
+ (bind [ "attach" ] "u" "undelete-entry")
- # Scrolling
- {
- map = [ "generic" ];
- key = "zt";
- action = "current-top";
- }
- {
- map = [ "generic" ];
- key = "zz";
- action = "current-middle";
- }
- {
- map = [ "generic" ];
- key = "zb";
- action = "current-bottom";
- }
- {
- map = [ "pager" ];
- key = "j";
- action = "next-line";
- }
- {
- map = [ "pager" ];
- key = "k";
- action = "previous-line";
- }
- {
- map = [ "pager" ];
- key = "gg";
- action = "top";
- }
- {
- map = [ "pager" ];
- key = "G";
- action = "bottom";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "\\Cd";
- action = "half-down";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "\\Cu";
- action = "half-up";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "\\Cf";
- action = "next-page";
- }
- {
- map = [ "index" ];
- key = "\\Cb";
- action = "previous-page";
- }
+ # Mailbox browser --------------------------------------------------------
+ (bind [ "browser" ] "g" "noop")
+ (bind [ "browser" ] ":" "noop")
+ (bind [ "browser" ] "q" "noop")
+ (bind [ "browser" ] ":q" "exit")
+ (bind [ "browser" ] "j" "next-entry")
+ (bind [ "browser" ] "k" "previous-entry")
+ (bind [ "browser" ] "gg" "first-entry")
+ (bind [ "browser" ] "G" "last-entry")
+ (bind [ "browser" ] "l" "select-entry")
+ (bind [ "browser" ] "\\Cd" "half-down")
+ (bind [ "browser" ] "\\Cu" "half-up")
- # Searching and limiting
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "/";
- action = "search";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "?";
- action = "search-reverse";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "n";
- action = "search-next";
- }
- {
- map = [
- "generic"
- "index"
- "pager"
- ];
- key = "N";
- action = "search-opposite";
- }
- {
- map = [ "index" ];
- key = "gf";
- action = "limit";
- }
-
- # Message states and threads
- {
- map = [ "index" ];
- key = "]";
- action = "next-unread";
- }
- {
- map = [ "index" ];
- key = "[";
- action = "previous-unread";
- }
- {
- map = [ "index" ];
- key = "}";
- action = "next-new";
- }
- {
- map = [ "index" ];
- key = "{";
- action = "previous-new";
- }
- {
- map = [ "index" ];
- key = "za";
- action = "collapse-thread";
- }
- {
- map = [ "index" ];
- key = "zA";
- action = "collapse-all";
- }
-
- # Core email actions
- {
- map = [
- "index"
- "pager"
- ];
- key = "m";
- action = "mail";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "r";
- action = "reply";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "R";
- action = "group-reply";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "f";
- action = "forward-message";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "c";
- action = "change-folder";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "s";
- action = "save-message";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "y";
- action = "copy-message";
- }
- {
- map = [ "index" ];
- key = "t";
- action = "tag-entry";
- }
- {
- map = [ "pager" ];
- key = "t";
- action = "tag-message";
- }
- {
- map = [ "index" ];
- key = "T";
- action = "tag-thread";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "gh";
- action = "display-toggle-weed";
- }
-
- # Deletion and undeletion
- {
- map = [
- "index"
- "pager"
- ];
- key = "dd";
- action = "delete-message";
- }
- {
- map = [ "index" ];
- key = "dT";
- action = "delete-thread";
- }
- {
- map = [
- "index"
- "pager"
- ];
- key = "u";
- action = "undelete-message";
- }
- {
- map = [ "index" ];
- key = "U";
- action = "undelete-thread";
- }
- {
- map = [ "attach" ];
- key = "dd";
- action = "delete-entry";
- }
- {
- map = [ "attach" ];
- key = "u";
- action = "undelete-entry";
- }
-
- # Menu specific
- {
- map = [ "attach" ];
- key = "l";
- action = "view-attach";
- }
- {
- map = [ "attach" ];
- key = "s";
- action = "save-entry";
- }
- {
- map = [ "attach" ];
- key = "p";
- action = "print-entry";
- }
-
- {
- map = [ "browser" ];
- key = "j";
- action = "next-entry";
- }
- {
- map = [ "browser" ];
- key = "k";
- action = "previous-entry";
- }
- {
- map = [ "browser" ];
- key = "gg";
- action = "first-entry";
- }
- {
- map = [ "browser" ];
- key = "G";
- action = "last-entry";
- }
- {
- map = [ "browser" ];
- key = "l";
- action = "select-entry";
- }
- {
- map = [ "browser" ];
- key = "\\Cd";
- action = "half-down";
- }
- {
- map = [ "browser" ];
- key = "\\Cu";
- action = "half-up";
- }
-
- {
- map = [ "compose" ];
- key = "j";
- action = "next-entry";
- }
- {
- map = [ "compose" ];
- key = "k";
- action = "previous-entry";
- }
- {
- map = [ "compose" ];
- key = "y";
- action = "send-message";
- }
- {
- map = [ "compose" ];
- key = "a";
- action = "attach-file";
- }
- {
- map = [ "compose" ];
- key = "e";
- action = "edit-message";
- }
- {
- map = [ "compose" ];
- key = "p";
- action = "postpone-message";
- }
-
- # Global exit
- {
- map = [
- "generic"
- "index"
- "pager"
- "browser"
- "attach"
- "compose"
- ];
- key = "q";
- action = "exit";
- }
- ]
- ]
- );
+ # Compose menu -----------------------------------------------------------
+ (bind [ "compose" ] ":" "noop")
+ (bind [ "compose" ] "q" "noop")
+ (bind [ "compose" ] ":q" "exit")
+ (bind [ "compose" ] "j" "next-entry")
+ (bind [ "compose" ] "k" "previous-entry")
+ (bind [ "compose" ] "y" "send-message")
+ (bind [ "compose" ] "a" "attach-file")
+ (bind [ "compose" ] "e" "edit-message")
+ (bind [ "compose" ] "p" "postpone-message")
+ ];
}
diff --git a/modules/communication/neomutt/macros.nix b/modules/communication/neomutt/macros.nix
index 9ad3593..57444d9 100644
--- a/modules/communication/neomutt/macros.nix
+++ b/modules/communication/neomutt/macros.nix
@@ -21,12 +21,6 @@
action = "<pipe-message>urlscan<enter>";
}
{
- # Open the message body in interactive chawan with vim-style navigation
- map = [ "pager" ];
- key = "\\Cb";
- action = "<enter-command>set my_pipe_decode=$pipe_decode pipe_decode=yes<enter><pipe-message>${lib.getExe config.web.chawan.package} -o \"title='neomutt'\"<enter><enter-command>set pipe_decode=$my_pipe_decode<enter>";
- }
- {
# Pipe the selected attachment instead of the whole message
map = [ "attach" ];
key = "gx";
@@ -44,11 +38,5 @@
key = "ZZ";
action = "<sync-mailbox><quit>";
}
- {
- # Quit without saving
- map = [ "index" ];
- key = "ZQ";
- action = "<exit>";
- }
];
}
diff --git a/modules/communication/neomutt/neomutt.nix b/modules/communication/neomutt/neomutt.nix
index e25d0b3..7008433 100644
--- a/modules/communication/neomutt/neomutt.nix
+++ b/modules/communication/neomutt/neomutt.nix
@@ -38,7 +38,7 @@
nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/neomutt \
- --prefix PATH : ${lib.makeBinPath [ pkgs.urlscan ]}
+ --prefix PATH : ${config.home.profileDirectory}/bin:${lib.makeBinPath [ pkgs.urlscan ]}
'';
meta.mainProgram = "neomutt";
};
@@ -47,7 +47,16 @@
vimKeys = false;
unmailboxes = true;
checkStatsInterval = 20;
- extraConfig = builtins.readFile ./.neomuttrc;
+ extraConfig = ''
+ # Read messages in Neovim. `-` makes Neovim read the message from
+ # stdin and -R prevents accidental edits to message content.
+ set pager = "nvim -R -n +'setlocal nolist | silent! 1,2g/^$/delete _ | nohlsearch | setlocal nomodified nomodifiable' -"
+ # Return directly to the index when Neovim exits and do not prepend
+ # NeoMutt’s pager status line to the message passed to Neovim.
+ set noprompt_after
+ set pager_format = ""
+ ''
+ + builtins.readFile ./.neomuttrc;
};
};
}
diff --git a/modules/development/nixvim/colorscheme.nix b/modules/development/nixvim/colorscheme.nix
index 1569cb5..3ab5bc4 100644
--- a/modules/development/nixvim/colorscheme.nix
+++ b/modules/development/nixvim/colorscheme.nix
@@ -1,25 +1,31 @@
# Base16 theme configuration for nixvim
-{ config, lib, ... }:
{
- config.programs.nixvim.colorschemes.base16 = lib.mkIf config.development.nixvim.enable {
- enable = true;
- colorscheme = with config.scheme.withHashtag; {
- base00 = base00;
- base01 = base01;
- base02 = base02;
- base03 = base03;
- base04 = base04;
- base05 = base05;
- base06 = base06;
- base07 = base07;
- base08 = base08;
- base09 = base09;
- base0A = base0A;
- base0B = base0B;
- base0C = base0C;
- base0D = base0D;
- base0E = base0E;
- base0F = base0F;
+ config,
+ lib,
+ ...
+}:
+{
+ config.programs.nixvim = lib.mkIf config.development.nixvim.enable {
+ colorschemes.base16 = {
+ enable = true;
+ colorscheme = with config.scheme.withHashtag; {
+ base00 = base00;
+ base01 = base01;
+ base02 = base02;
+ base03 = base03;
+ base04 = base04;
+ base05 = base05;
+ base06 = base06;
+ base07 = base07;
+ base08 = base08;
+ base09 = base09;
+ base0A = base0A;
+ base0B = base0B;
+ base0C = base0C;
+ base0D = base0D;
+ base0E = base0E;
+ base0F = base0F;
+ };
};
};
}