branch: elpa/git-commit commit 0168be93666c85caae888c01f91a73488eff1a5b Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
Add basic context-menu --- docs/magit.org | 13 +++++++ docs/magit.texi | 16 ++++++++ lisp/magit-section.el | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) diff --git a/docs/magit.org b/docs/magit.org index 87e8ad5a36..1a4341faa2 100644 --- a/docs/magit.org +++ b/docs/magit.org @@ -368,6 +368,9 @@ In file visiting buffers ~C-c M-g~ brings up a similar menu featuring commands that act on just the visited file, see [[*Commands for Buffers Visiting Files]]. +Magit also provides a context menu and other mouse commands, see +[[*Mouse Support]]. + It is not necessary that you do so now, but if you stick with Magit, then it is highly recommended that you read the next section too. @@ -1783,6 +1786,16 @@ will also have to install the ~ido-completing-read+~ package and use for-each-ref~. By default, refs are sorted alphabetically by their full name (e.g., "refs/heads/master"). +** Mouse Support + +Double clicking on a section heading toggles the visibility of its +body, if any. Likewise clicking in the left fringe toggles the +visibility of the appropriate section. + +A context menu is provided but has to be enabled explicitly. In Emacs +28 and greater, enable the global mode ~context-menu-mode~. If you use an +older Emacs release, set ~magit-section-show-context-menu-for-emacs<28~. + ** Running Git *** Viewing Git Output diff --git a/docs/magit.texi b/docs/magit.texi index 92e3199962..b4a505383d 100644 --- a/docs/magit.texi +++ b/docs/magit.texi @@ -104,6 +104,7 @@ Interface Concepts * Transient Commands:: * Transient Arguments and Buffer Variables:: * Completion, Confirmation and the Selection: Completion Confirmation and the Selection. +* Mouse Support:: * Running Git:: Modes and Buffers @@ -702,6 +703,9 @@ non-Magit buffers. The global binding is @code{C-x M-g}. In file visiting buffers @code{C-c M-g} brings up a similar menu featuring commands that act on just the visited file, see @ref{Commands for Buffers Visiting Files}. +Magit also provides a context menu and other mouse commands, see +@ref{Mouse Support}. + It is not necessary that you do so now, but if you stick with Magit, then it is highly recommended that you read the next section too. @@ -714,6 +718,7 @@ then it is highly recommended that you read the next section too. * Transient Commands:: * Transient Arguments and Buffer Variables:: * Completion, Confirmation and the Selection: Completion Confirmation and the Selection. +* Mouse Support:: * Running Git:: @end menu @@ -2304,6 +2309,17 @@ values include any key accepted by the @code{--sort} flag of @code{git full name (e.g., "refs/heads/master"). @end defopt +@node Mouse Support +@section Mouse Support + +Double clicking on a section heading toggles the visibility of its +body, if any. Likewise clicking in the left fringe toggles the +visibility of the appropriate section. + +A context menu is provided but has to be enabled explicitly. In Emacs +28 and greater, enable the global mode @code{context-menu-mode}. If you use an +older Emacs release, set @code{magit-section-show-context-menu-for-emacs<28}. + @node Running Git @section Running Git diff --git a/lisp/magit-section.el b/lisp/magit-section.el index 4401280d44..7978d80954 100644 --- a/lisp/magit-section.el +++ b/lisp/magit-section.el @@ -248,6 +248,16 @@ but that ship has sailed, thus this option." :group 'magit-section :type 'boolean) +(defcustom magit-section-show-context-menu-for-emacs<28 nil + "Whether `mouse-3' shows a context menu for Emacs < 28. + +This has to be set before loading `magit-section' or it has +no effect. This also has no effect for Emacs >= 28, where +`context-menu-mode' should be enabled instead." + :package-version '(magit-section . "3.4.0") + :group 'magit-section + :type 'boolean) + ;;; Faces (defgroup magit-section-faces nil @@ -336,6 +346,17 @@ if any.") (defvar magit-section-mode-map (let ((map (make-keymap))) (suppress-keymap map t) + (when (and magit-section-show-context-menu-for-emacs<28 + (< emacs-major-version 28)) + (define-key map [mouse-3] nil) + (define-key + map [down-mouse-3] + `( menu-item "" ,(make-sparse-keymap) + :filter ,(lambda (_) + (let ((menu (make-sparse-keymap))) + (context-menu-local menu last-input-event) + (magit-section-context-menu menu last-input-event) + menu))))) (define-key map [left-fringe mouse-1] 'magit-mouse-toggle-section) (define-key map [left-fringe mouse-2] 'magit-mouse-toggle-section) (define-key map (kbd "TAB") 'magit-section-toggle) @@ -385,6 +406,8 @@ Magit-Section is documented in info node `(magit-section)'." 'magit-section--highlight-region) (setq-local redisplay-unhighlight-region-function 'magit-section--unhighlight-region) + (when (fboundp 'magit-section-context-menu) + (add-hook 'context-menu-functions 'magit-section-context-menu 10 t)) (when magit-section-disable-line-numbers (when (bound-and-true-p global-linum-mode) (linum-mode -1)) @@ -480,6 +503,84 @@ The return value has the form (TYPE...)." (defvar magit-insert-section--parent nil "For internal use only.") (defvar magit-insert-section--oldroot nil "For internal use only.") +;;; Menu + +(defun magit-section-context-menu (menu click) + "Populate MENU with Magit-Section commands at CLICK." + (mouse-set-point click) + (magit-section-update-highlight t) + (when-let ((section (magit-section-at))) + (when (magit-section-content-p section) + (define-key-after menu [magit-section-toggle] + `(menu-item + ,(if (oref section hidden) "Expand section" "Collapse section") + magit-section-toggle)) + (unless (oref section hidden) + (when-let ((children (oref section children))) + (when (seq-some #'magit-section-content-p children) + (when (seq-some (lambda (c) (oref c hidden)) children) + (define-key-after menu [magit-section-show-children] + `(menu-item "Expand children" + magit-section-show-children))) + (when (seq-some (lambda (c) (not (oref c hidden))) children) + (define-key-after menu [magit-section-hide-children] + `(menu-item "Collapse children" + magit-section-hide-children)))))) + (define-key-after menu [separator-magit-1] menu-bar-separator)) + (define-key-after menu [magit-describe-section] + `(menu-item "Describe section" magit-describe-section)) + (when-let ((map (oref section keymap))) + (define-key-after menu [separator-magit-2] menu-bar-separator) + (when (symbolp map) + (setq map (symbol-value map))) + (map-keymap (lambda (key binding) + (when (consp binding) + (define-key-after menu (vector key) + (copy-sequence binding)))) + (menu-bar-keymap map)))) + menu) + +(defun magit-menu-set (keymap key def desc &optional props after) + "In KEYMAP, define KEY and a menu entry for DEF. + +Add the menu item (menu-item DESC DEF . PROPS) at the end of +KEYMAP, or if optional AFTER is non-nil, then after that. + +Because it is so common, and would otherwise result in overlong +lines or else unsightly line wrapping, a definition [remap CMD] +can be written as just [CMD]. As a result KEY might have to be +a string when otherwise a vector would have worked." + (declare (indent defun)) + (when (vectorp key) + ;; Expand the short-hand. + (unless (eq (aref key 0) 'remap) + (setq key (vconcat [remap] key))) + ;; The default binding is RET, but in my configuration it + ;; is <return>. In that case the displayed binding would + ;; be <CMD> instead of <return>, for unknown reasons. The + ;; same does not happen for similar events, such as <tab>. + (when (and (equal key [remap magit-visit-thing]) + (boundp 'magit-mode-map) + (ignore-errors (eq (lookup-key magit-mode-map [return]) + 'magit-visit-thing))) + (setq key [return])) + ;; `define-key-after' cannot deal with [remap CMD], + ;; so we have to add the key binding separately. + (define-key keymap key def) + (unless (symbolp def) + (error "When KEY is a remapping, then DEF must be a symbol: %s" def)) + (setq key (vector def))) + (define-key-after keymap key + `(menu-item ,desc ,def ,@props) + after)) + +(advice-add 'context-menu-region :around + (lambda (fn menu click) + "Disable in `magit-section-mode' buffers." + (if (derived-mode-p 'magit-section-mode) + menu + (funcall fn menu click)))) + ;;; Commands ;;;; Movement