diff --git a/lib/maintainers.nix b/lib/maintainers.nix index 2c2318f5..c88503b2 100644 --- a/lib/maintainers.nix +++ b/lib/maintainers.nix @@ -12,4 +12,14 @@ githubId = 1176328; name = "Alison Jenkins"; }; + MattSturgeon = { + email = "matt@sturgeon.me.uk"; + matrix = "@mattsturg:matrix.org"; + github = "MattSturgeon"; + githubId = 5046562; + name = "Matt Sturgeon"; + keys = [ + {fingerprint = "7082 22EA 1808 E39A 83AC 8B18 4F91 844C ED1A 8299";} + ]; + }; } diff --git a/plugins/utils/tmux-navigator.nix b/plugins/utils/tmux-navigator.nix index 1462f5bf..3587c134 100644 --- a/plugins/utils/tmux-navigator.nix +++ b/plugins/utils/tmux-navigator.nix @@ -6,37 +6,173 @@ ... }: with lib; let - cfg = config.plugins.tmux-navigator; -in { - options.plugins.tmux-navigator = { - enable = mkEnableOption "Tmux-Navigator (see https://github.com/christoomey/vim-tmux-navigator for tmux installation instruction)"; + # TODO: Introduced 2024-03-19, remove on 2024-05-19 + deprecations = let + pluginPath = ["plugins" "tmux-navigator"]; + option = s: pluginPath ++ [s]; + setting = s: pluginPath ++ ["settings" s]; + settingStr = s: concatStringsSep "." (setting s); + in [ + ( + mkRenamedOptionModule + (option "tmuxNavigatorSaveOnSwitch") + (setting "save_on_switch") + ) + ( + mkRemovedOptionModule + (option "tmuxNavigatorDisableWhenZoomed") + "Use `${settingStr "disable_when_zoomed"}` option." + ) + ( + mkRemovedOptionModule + (option "tmuxNavigatorNoWrap") + "Use `${settingStr "no_wrap"}` option." + ) + ]; +in + helpers.vim-plugin.mkVimPlugin config { + name = "tmux-navigator"; + originalName = "vim-tmux-navigator"; + defaultPackage = pkgs.vimPlugins.tmux-navigator; + globalPrefix = "tmux_navigator_"; - package = helpers.mkPackageOption "tmux-navigator" pkgs.vimPlugins.tmux-navigator; + maintainers = [helpers.maintainers.MattSturgeon]; - tmuxNavigatorSaveOnSwitch = helpers.mkNullOrOption (lib.types.enum [1 2]) '' - null: don't save on switch (default value) - 1: update (write the current buffer, but only if changed) - 2: wall (write all buffers) + description = '' + When combined with a set of tmux key bindings, the plugin will allow you to navigate seamlessly between vim splits and tmux panes using a consistent set of hotkeys. + + **WARNING:** to work correctly, you must configure tmux separately. + + ## Usage + + This plugin provides the following mappings which allow you to move between vim splits and tmux panes seamlessly. + + - `` => Left + - `` => Down + - `` => Up + - `` => Right + - `` => Previous split + + To use alternative key mappings, see [`plugins.tmux-navigator.settings.no_mappings`][no_mappings]. + + ## Configure tmux + + There are two main ways to configure tmux. Either install the `tmuxPlugins.vim-tmux-navigator` plugin or add a snippet to your tmux config: + + ```shell + # Smart pane switching with awareness of vim splits. + # See: https://github.com/christoomey/vim-tmux-navigator + is_vim="ps -o state= -o comm= -t '#{pane_tty}' \ + | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?|fzf)(diff)?$'" + + bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L' + bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D' + bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U' + bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R' + + # Forwarding needs different syntax, depending on tmux version + tmux_version='$(tmux -V | sed -En "s/^tmux ([0-9]+(.[0-9]+)?).*/\1/p")' + if-shell -b '[ "$(echo "$tmux_version < 3.0" | bc)" = 1 ]' \ + "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\' 'select-pane -l'" + if-shell -b '[ "$(echo "$tmux_version >= 3.0" | bc)" = 1 ]' \ + "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\\\' 'select-pane -l'" + + bind-key -T copy-mode-vi 'C-h' select-pane -L + bind-key -T copy-mode-vi 'C-j' select-pane -D + bind-key -T copy-mode-vi 'C-k' select-pane -U + bind-key -T copy-mode-vi 'C-l' select-pane -R + bind-key -T copy-mode-vi 'C-\' select-pane -l + ``` + + See the [upstream docs] for more info. + + [no_mappings]: ./settings.html#pluginstmux-navigatorsettingsno_mappings + [upstream docs]: https://github.com/christoomey/vim-tmux-navigator#installation ''; - tmuxNavigatorDisableWhenZoomed = helpers.mkNullOrOption (lib.types.enum [1]) '' - null: unzoom when moving from Vim to another pane (default value) - 1: If the tmux window is zoomed, keep it zoomed when moving from Vim to another pane - ''; + imports = deprecations; - tmuxNavigatorNoWrap = helpers.mkNullOrOption (lib.types.enum [1]) '' - null: move past the edge of the screen, tmux/vim will wrap around to the opposite side (default value) - 1: disable wrap - ''; - }; + settingsOptions = { + save_on_switch = helpers.mkNullOrOption (types.enum [1 2]) '' + You can configure the plugin to write the current buffer, or all buffers, when navigating from vim to tmux. - config = mkIf cfg.enable { - extraPlugins = [cfg.package]; + null: don't save on switch (default value) + 1: `:update` (write the current buffer, but only if changed) + 2: `:wall` (write all buffers) + ''; - globals = { - tmux_navigator_save_on_switch = cfg.tmuxNavigatorSaveOnSwitch; - tmux_navigator_disable_when_zoomed = cfg.tmuxNavigatorDisableWhenZoomed; - tmux_navigator_no_wrap = cfg.tmuxNavigatorNoWrap; + disable_when_zoomed = helpers.defaultNullOpts.mkBool false '' + By default, if you zoom the tmux pane running vim and then attempt to navigate "past" the edge of the vim session, tmux will unzoom the pane. + This is the default tmux behavior, but may be confusing if you've become accustomed to navigation "wrapping" around the sides due to this plugin. + + This option disables the unzooming behavior, keeping all navigation within vim until the tmux pane is explicitly unzoomed. + ''; + + preserve_zoom = helpers.defaultNullOpts.mkBool false '' + As noted in `disable_when_zoomed`, navigating from a vim pane to another tmux pane normally causes the window to be unzoomed. + Some users may prefer the behavior of tmux's `-Z` option to `select-pane`, which keeps the window zoomed if it was zoomed. + + This option enables that behavior. + + Naturally, if `disable_when_zoomed` is enabled, this option will have no effect. + ''; + + no_wrap = helpers.defaultNullOpts.mkBool false '' + By default, if you try to move past the edge of the screen, tmux/vim will "wrap" around to the opposite side. + + This option disables "wrapping" in vim, but tmux will need to be configured separately. + + Tmux doesn't have a "no_wrap" option, so whatever key bindings you have need to conditionally wrap based on position on screen: + + ```shell + is_vim="ps -o state= -o comm= -t '#{pane_tty}' \ + | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?|fzf)(diff)?$'" + + bind-key -n 'C-h' if-shell "$is_vim" { send-keys C-h } { if-shell -F '#{pane_at_left}' {} { select-pane -L } } + bind-key -n 'C-j' if-shell "$is_vim" { send-keys C-j } { if-shell -F '#{pane_at_bottom}' {} { select-pane -D } } + bind-key -n 'C-k' if-shell "$is_vim" { send-keys C-k } { if-shell -F '#{pane_at_top}' {} { select-pane -U } } + bind-key -n 'C-l' if-shell "$is_vim" { send-keys C-l } { if-shell -F '#{pane_at_right}' {} { select-pane -R } } + + bind-key -T copy-mode-vi 'C-h' if-shell -F '#{pane_at_left}' {} { select-pane -L } + bind-key -T copy-mode-vi 'C-j' if-shell -F '#{pane_at_bottom}' {} { select-pane -D } + bind-key -T copy-mode-vi 'C-k' if-shell -F '#{pane_at_top}' {} { select-pane -U } + bind-key -T copy-mode-vi 'C-l' if-shell -F '#{pane_at_right}' {} { select-pane -R } + ``` + ''; + + no_mappings = helpers.defaultNullOpts.mkBool false '' + By default ``, ``, ``, ``, & `` are mapped to navigating left, down, up, right, & previous, respectively. + + This option disables those default mappings being created. + + You can use the plugin's five commands to define your own custom mappings: + + ```nix + keymaps = [ + { + key = "h"; + action = "TmuxNavigateLeft"; + } + { + key = "j"; + action = "TmuxNavigateDown"; + } + { + key = "k"; + action = "TmuxNavigateUp"; + } + { + key = "l"; + action = "TmuxNavigateRight"; + } + { + key = "\\"; + action = "TmuxNavigatePrevious"; + } + ]; + ``` + + You will also need to update your tmux bindings to match. + ''; }; - }; -} + } diff --git a/tests/test-sources/plugins/utils/tmux-navigator.nix b/tests/test-sources/plugins/utils/tmux-navigator.nix index 53849289..23ebc974 100644 --- a/tests/test-sources/plugins/utils/tmux-navigator.nix +++ b/tests/test-sources/plugins/utils/tmux-navigator.nix @@ -9,11 +9,13 @@ plugins.tmux-navigator = { enable = true; - tmuxNavigatorSaveOnSwitch = 2; - - tmuxNavigatorDisableWhenZoomed = 1; - - tmuxNavigatorNoWrap = 1; + settings = { + save_on_switch = 2; + disable_when_zoomed = true; + preserve_zoom = true; + no_wrap = true; + no_mappings = true; + }; }; }; }