Skip to content

Commit

Permalink
magit-section-cycle: Pivot to tab-next if there is a binding conflict
Browse files Browse the repository at this point in the history
If `tab-bar-mode' is enable, then "C-<tab>" is bound to `tab-next'
in the global map.  That conflicts with our local (and much older)
binding for `magit-section-cycle'.

Address this conflict by teaching `magit-section-cycle' to pivot to
`tab-next', but only if `tab-bar-mode' is enabled.  That way, users
who do not use `tab-bar-mode' (i.e., the majority), are not affected
by this unfortunate conflict.

`tab-bar-mode' users will have to get used to the much less convenient
"C-c TAB" binding to cycle section visibility.  Alternatively they can
advice `tab-bar--define-keys' to bind another key to `tab-next'.  It
would be nice if `tab-bar-mode', instead of modifying the global map,
used a mode map, and thus didn't make it so very hard to change its
key bindings.
  • Loading branch information
tarsius committed Mar 11, 2024
1 parent eca60f3 commit 8a3c1cc
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 8 deletions.
11 changes: 11 additions & 0 deletions docs/magit.org
Original file line number Diff line number Diff line change
Expand Up @@ -1011,10 +1011,21 @@ but all you need to get started are the next two.

Toggle the visibility of the body of the current section.

- Key: C-c TAB (magit-section-cycle) ::
- Key: C-<tab> (magit-section-cycle) ::

Cycle the visibility of current section and its children.

If this command is invoked using ~C-<tab>~ and that is globally bound
to ~tab-next~, then this command pivots to behave like that command,
and you must instead use ~C-c TAB~ to cycle section visibility.

If you would like to keep using ~C-<tab>~ to cycle section visibility
but also want to use ~tab-bar-mode~, then you have to prevent that mode
from using this key and instead bind another key to ~tab-next~. Because
~tab-bar-mode~ does not use a mode map but instead manipulates the
global map, this involves advising ~tab-bar--define-keys~.

- Key: M-<tab> (magit-section-cycle-diffs) ::

Cycle the visibility of diff-related sections in the current buffer.
Expand Down
14 changes: 13 additions & 1 deletion docs/magit.texi
Original file line number Diff line number Diff line change
Expand Up @@ -1410,11 +1410,23 @@ but all you need to get started are the next two.
@findex magit-section-toggle
Toggle the visibility of the body of the current section.

@item @kbd{C-<tab>} (@code{magit-section-cycle})
@item @kbd{C-c @key{TAB}} (@code{magit-section-cycle})
@itemx @kbd{C-<tab>} (@code{magit-section-cycle})
@kindex C-c TAB
@kindex C-<tab>
@findex magit-section-cycle
Cycle the visibility of current section and its children.

If this command is invoked using @code{C-<tab>} and that is globally bound
to @code{tab-next}, then this command pivots to behave like that command,
and you must instead use @code{C-c TAB} to cycle section visibility.

If you would like to keep using @code{C-<tab>} to cycle section visibility
but also want to use @code{tab-bar-mode}, then you have to prevent that mode
from using this key and instead bind another key to @code{tab-next}. Because
@code{tab-bar-mode} does not use a mode map but instead manipulates the
global map, this involves advising @code{tab-bar--define-keys}.

@item @kbd{M-<tab>} (@code{magit-section-cycle-diffs})
@kindex M-<tab>
@findex magit-section-cycle-diffs
Expand Down
29 changes: 22 additions & 7 deletions lisp/magit-section.el
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ if any."
(keymap-set map "<left-fringe> <mouse-1>" #'magit-mouse-toggle-section)
(keymap-set map "<left-fringe> <mouse-2>" #'magit-mouse-toggle-section)
(keymap-set map "TAB" #'magit-section-toggle)
(keymap-set map "C-c TAB" #'magit-section-cycle)
(keymap-set map "C-<tab>" #'magit-section-cycle)
(keymap-set map "M-<tab>" #'magit-section-cycle)
;; <backtab> is the most portable binding for Shift+Tab.
Expand Down Expand Up @@ -1005,19 +1006,33 @@ hidden."
(magit-section-show-headings-1 child))))

(defun magit-section-cycle (section)
"Cycle visibility of current section and its children."
"Cycle visibility of current section and its children.
If this command is invoked using \\`C-<tab>' and that is globally bound
to `tab-next', then this command pivots to behave like that command, and
you must instead use \\`C-c TAB' to cycle section visibility.
If you would like to keep using \\`C-<tab>' to cycle section visibility
but also want to use `tab-bar-mode', then you have to prevent that mode
from using this key and instead bind another key to `tab-next'. Because
`tab-bar-mode' does not use a mode map but instead manipulates the
global map, this involves advising `tab-bar--define-keys'."
(interactive (list (magit-current-section)))
(if (oref section hidden)
(progn (magit-section-show section)
(magit-section-hide-children section))
(let ((children (oref section children)))
(cond
((and (equal (this-command-keys) [C-tab])
(eq (global-key-binding [C-tab]) 'tab-next)
(fboundp 'tab-bar-switch-to-next-tab))
(tab-bar-switch-to-next-tab current-prefix-arg))
((oref section hidden)
(magit-section-show section)
(magit-section-hide-children section))
((let ((children (oref section children)))
(cond ((and (--any-p (oref it hidden) children)
(--any-p (oref it children) children))
(magit-section-show-headings section))
((seq-some #'magit-section-hidden-body children)
(magit-section-show-children section))
(t
(magit-section-hide section))))))
((magit-section-hide section)))))))

(defun magit-section-cycle-global ()
"Cycle visibility of all sections in the current buffer."
Expand Down

0 comments on commit 8a3c1cc

Please sign in to comment.