1{ config, pkgs, lib, ... }:
2
3let
4 inherit (lib) mkOption mkIf types;
5
6 cfg = config.programs.tmux;
7
8 defaultKeyMode = "emacs";
9 defaultResize = 5;
10 defaultShortcut = "b";
11 defaultTerminal = "screen";
12
13 boolToStr = value: if value then "on" else "off";
14
15 tmuxConf = ''
16 set -g default-terminal "${cfg.terminal}"
17 set -g base-index ${toString cfg.baseIndex}
18 setw -g pane-base-index ${toString cfg.baseIndex}
19
20 ${if cfg.newSession then "new-session" else ""}
21
22 ${if cfg.reverseSplit then ''
23 bind v split-window -h
24 bind s split-window -v
25 '' else ""}
26
27 set -g status-keys ${cfg.keyMode}
28 set -g mode-keys ${cfg.keyMode}
29
30 ${if cfg.keyMode == "vi" && cfg.customPaneNavigationAndResize then ''
31 bind h select-pane -L
32 bind j select-pane -D
33 bind k select-pane -U
34 bind l select-pane -R
35
36 bind -r H resize-pane -L ${toString cfg.resizeAmount}
37 bind -r J resize-pane -D ${toString cfg.resizeAmount}
38 bind -r K resize-pane -U ${toString cfg.resizeAmount}
39 bind -r L resize-pane -R ${toString cfg.resizeAmount}
40 '' else ""}
41
42 ${if (cfg.shortcut != defaultShortcut) then ''
43 # rebind main key: C-${cfg.shortcut}
44 unbind C-${defaultShortcut}
45 set -g prefix C-${cfg.shortcut}
46 bind ${cfg.shortcut} send-prefix
47 bind C-${cfg.shortcut} last-window
48 '' else ""}
49
50 setw -g aggressive-resize ${boolToStr cfg.aggressiveResize}
51 setw -g clock-mode-style ${if cfg.clock24 then "24" else "12"}
52 set -s escape-time ${toString cfg.escapeTime}
53 set -g history-limit ${toString cfg.historyLimit}
54
55 ${cfg.extraTmuxConf}
56 '';
57
58in {
59 ###### interface
60
61 options = {
62 programs.tmux = {
63
64 enable = mkOption {
65 type = types.bool;
66 default = false;
67 description = "Whenever to configure <command>tmux</command> system-wide.";
68 relatedPackages = [ "tmux" ];
69 };
70
71 aggressiveResize = mkOption {
72 default = false;
73 type = types.bool;
74 description = ''
75 Resize the window to the size of the smallest session for which it is the current window.
76 '';
77 };
78
79 baseIndex = mkOption {
80 default = 0;
81 example = 1;
82 type = types.int;
83 description = "Base index for windows and panes.";
84 };
85
86 clock24 = mkOption {
87 default = false;
88 type = types.bool;
89 description = "Use 24 hour clock.";
90 };
91
92 customPaneNavigationAndResize = mkOption {
93 default = false;
94 type = types.bool;
95 description = "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode.";
96 };
97
98 escapeTime = mkOption {
99 default = 500;
100 example = 0;
101 type = types.int;
102 description = "Time in milliseconds for which tmux waits after an escape is input.";
103 };
104
105 extraTmuxConf = mkOption {
106 default = "";
107 description = ''
108 Additional contents of /etc/tmux.conf
109 '';
110 type = types.lines;
111 };
112
113 historyLimit = mkOption {
114 default = 2000;
115 example = 5000;
116 type = types.int;
117 description = "Maximum number of lines held in window history.";
118 };
119
120 keyMode = mkOption {
121 default = defaultKeyMode;
122 example = "vi";
123 type = types.enum [ "emacs" "vi" ];
124 description = "VI or Emacs style shortcuts.";
125 };
126
127 newSession = mkOption {
128 default = false;
129 type = types.bool;
130 description = "Automatically spawn a session if trying to attach and none are running.";
131 };
132
133 reverseSplit = mkOption {
134 default = false;
135 type = types.bool;
136 description = "Reverse the window split shortcuts.";
137 };
138
139 resizeAmount = mkOption {
140 default = defaultResize;
141 example = 10;
142 type = types.int;
143 description = "Number of lines/columns when resizing.";
144 };
145
146 shortcut = mkOption {
147 default = defaultShortcut;
148 example = "a";
149 type = types.str;
150 description = "Ctrl following by this key is used as the main shortcut.";
151 };
152
153 terminal = mkOption {
154 default = defaultTerminal;
155 example = "screen-256color";
156 type = types.str;
157 description = "Set the $TERM variable.";
158 };
159
160 secureSocket = mkOption {
161 default = true;
162 type = types.bool;
163 description = ''
164 Store tmux socket under /run, which is more secure than /tmp, but as a
165 downside it doesn't survive user logout.
166 '';
167 };
168 };
169 };
170
171 ###### implementation
172
173 config = mkIf cfg.enable {
174 environment = {
175 etc."tmux.conf".text = tmuxConf;
176
177 systemPackages = [ pkgs.tmux ];
178
179 variables = {
180 TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/\$(id -u)"}'';
181 };
182 };
183 };
184}