at 23.05-pre 6.2 kB view raw
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 ${lib.optionalString (cfg.plugins != []) '' 56 # Run plugins 57 ${lib.concatMapStringsSep "\n" (x: "run-shell ${x.rtp}") cfg.plugins} 58 59 ''} 60 61 ${cfg.extraConfig} 62 ''; 63 64in { 65 ###### interface 66 67 options = { 68 programs.tmux = { 69 70 enable = mkOption { 71 type = types.bool; 72 default = false; 73 description = lib.mdDoc "Whenever to configure {command}`tmux` system-wide."; 74 relatedPackages = [ "tmux" ]; 75 }; 76 77 aggressiveResize = mkOption { 78 default = false; 79 type = types.bool; 80 description = lib.mdDoc '' 81 Resize the window to the size of the smallest session for which it is the current window. 82 ''; 83 }; 84 85 baseIndex = mkOption { 86 default = 0; 87 example = 1; 88 type = types.int; 89 description = lib.mdDoc "Base index for windows and panes."; 90 }; 91 92 clock24 = mkOption { 93 default = false; 94 type = types.bool; 95 description = lib.mdDoc "Use 24 hour clock."; 96 }; 97 98 customPaneNavigationAndResize = mkOption { 99 default = false; 100 type = types.bool; 101 description = lib.mdDoc "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode."; 102 }; 103 104 escapeTime = mkOption { 105 default = 500; 106 example = 0; 107 type = types.int; 108 description = lib.mdDoc "Time in milliseconds for which tmux waits after an escape is input."; 109 }; 110 111 extraConfig = mkOption { 112 default = ""; 113 description = lib.mdDoc '' 114 Additional contents of /etc/tmux.conf 115 ''; 116 type = types.lines; 117 }; 118 119 historyLimit = mkOption { 120 default = 2000; 121 example = 5000; 122 type = types.int; 123 description = lib.mdDoc "Maximum number of lines held in window history."; 124 }; 125 126 keyMode = mkOption { 127 default = defaultKeyMode; 128 example = "vi"; 129 type = types.enum [ "emacs" "vi" ]; 130 description = lib.mdDoc "VI or Emacs style shortcuts."; 131 }; 132 133 newSession = mkOption { 134 default = false; 135 type = types.bool; 136 description = lib.mdDoc "Automatically spawn a session if trying to attach and none are running."; 137 }; 138 139 reverseSplit = mkOption { 140 default = false; 141 type = types.bool; 142 description = lib.mdDoc "Reverse the window split shortcuts."; 143 }; 144 145 resizeAmount = mkOption { 146 default = defaultResize; 147 example = 10; 148 type = types.int; 149 description = lib.mdDoc "Number of lines/columns when resizing."; 150 }; 151 152 shortcut = mkOption { 153 default = defaultShortcut; 154 example = "a"; 155 type = types.str; 156 description = lib.mdDoc "Ctrl following by this key is used as the main shortcut."; 157 }; 158 159 terminal = mkOption { 160 default = defaultTerminal; 161 example = "screen-256color"; 162 type = types.str; 163 description = lib.mdDoc "Set the $TERM variable."; 164 }; 165 166 secureSocket = mkOption { 167 default = true; 168 type = types.bool; 169 description = lib.mdDoc '' 170 Store tmux socket under /run, which is more secure than /tmp, but as a 171 downside it doesn't survive user logout. 172 ''; 173 }; 174 175 plugins = mkOption { 176 default = []; 177 type = types.listOf types.package; 178 description = lib.mdDoc "List of plugins to install."; 179 example = lib.literalExpression "[ pkgs.tmuxPlugins.nord ]"; 180 }; 181 182 withUtempter = mkOption { 183 description = lib.mdDoc '' 184 Whether to enable libutempter for tmux. 185 This is required so that tmux can write to /var/run/utmp (which can be queried with `who` to display currently connected user sessions). 186 Note, this will add a guid wrapper for the group utmp! 187 ''; 188 default = true; 189 type = types.bool; 190 }; 191 }; 192 }; 193 194 ###### implementation 195 196 config = mkIf cfg.enable { 197 environment = { 198 etc."tmux.conf".text = tmuxConf; 199 200 systemPackages = [ pkgs.tmux ] ++ cfg.plugins; 201 202 variables = { 203 TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/$(id -u)"}''; 204 }; 205 }; 206 security.wrappers = mkIf cfg.withUtempter { 207 utempter = { 208 source = "${pkgs.libutempter}/lib/utempter/utempter"; 209 owner = "root"; 210 group = "utmp"; 211 setuid = false; 212 setgid = true; 213 }; 214 }; 215 }; 216 217 imports = [ 218 (lib.mkRenamedOptionModule [ "programs" "tmux" "extraTmuxConf" ] [ "programs" "tmux" "extraConfig" ]) 219 ]; 220}