commit bf1ec4952e67b474bff813cd26e4d612a359baf1 (HEAD, refs/remotes/origin/master) Merge: 9e2cc406d3 d9eac0b426 Author: Juri Linkov Date: Tue Aug 17 11:11:35 2021 +0300 Merge branch 'feature/context-menu' commit 9e2cc406d3bc1a1f2f6008059091b9c1b8f12acf Author: Dmitry Gutov Date: Tue Aug 17 04:07:19 2021 +0300 ruby-mode imenu: Support methods with modifiers * lisp/progmodes/ruby-mode.el (ruby-imenu-create-index-in-block): Support methods with modifiers (visibility or otherwise) (bug#50079). diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 74b48ca4bd..c09f007a5e 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -679,7 +679,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." (let ((index-alist '()) (case-fold-search nil) name next pos decl sing) (goto-char beg) - (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^(<\n ]+\\)\\|\\(def\\|alias\\)\\s +\\([^(\n ]+\\)\\)" end t) + (while (re-search-forward "^\\s *\\(\\(class\\s +\\|\\(class\\s *<<\\s *\\)\\|module\\s +\\)\\([^(<\n ]+\\)\\|\\(\\(?:\\(?:private\\|protected\\|public\\) +\\)?def\\|alias\\)\\s +\\([^(\n ]+\\)\\)" end t) (setq sing (match-beginning 3)) (setq decl (match-string 5)) (setq next (match-end 0)) @@ -689,7 +689,7 @@ It is used when `ruby-encoding-magic-comment-style' is set to `custom'." ((string= "alias" decl) (if prefix (setq name (concat prefix name))) (push (cons name pos) index-alist)) - ((string= "def" decl) + ((not (null decl)) (if prefix (setq name (cond diff --git a/test/lisp/progmodes/ruby-mode-tests.el b/test/lisp/progmodes/ruby-mode-tests.el index e2ea0d9137..8bdfdc310f 100644 --- a/test/lisp/progmodes/ruby-mode-tests.el +++ b/test/lisp/progmodes/ruby-mode-tests.el @@ -875,6 +875,28 @@ VALUES-PLIST is a list with alternating index and value elements." (ruby-mode-set-encoding) (should (string= "# coding: iso-8859-15\nⓇ" (buffer-string)))))) +(ert-deftest ruby-imenu-with-private-modifier () + (ruby-with-temp-buffer + (ruby-test-string + "class Blub + | def hi + | 'Hi!' + | end + | + | def bye + | 'Bye!' + | end + | + | private def hiding + | 'You can't see me' + | end + |end") + (should (equal (mapcar #'car (ruby-imenu-create-index)) + '("Blub" + "Blub#hi" + "Blub#bye" + "Blub#hiding"))))) + (ert-deftest ruby--indent/converted-from-manual-test () :tags '(:expensive-test) ;; Converted from manual test. commit fd7f2077bc6165cfb3844d8be475ae056c80c4db Author: Dmitry Gutov Date: Tue Aug 17 03:32:12 2021 +0300 vc-git-region-history: Fix to call 'diff' more correctly * lisp/vc/vc-git.el (vc-git-region-history): Fix to pass a list to the backend 'diff' command (bug#39452). diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 6b26515430..935dc8b9ae 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1331,7 +1331,7 @@ This requires git 1.8.4 or later, for the \"-L\" option of \"git log\"." ;; but since Git is one of the two backends that support this operation ;; so far, it's hard to tell; hg doesn't need this. (with-temp-buffer - (vc-call-backend 'git 'diff file "HEAD" nil (current-buffer)) + (vc-call-backend 'git 'diff (list file) "HEAD" nil (current-buffer)) (goto-char (point-min)) (let ((last-offset 0) (from-offset nil) commit a126c06b0a5d585e7d7dd6d952b3f99447d15fc2 Author: João Távora Date: Mon Aug 16 23:15:12 2021 +0100 Make icomplete-vertical-mode take immediate effect (bug#49075) * etc/NEWS: Mention change. * lisp/icomplete.el (icomplete-vertical-mode): (fido-vertical-mode): Tweak docstring. Turn on icomplete-mode. and fido-mdoe diff --git a/etc/NEWS b/etc/NEWS index 3ccef6683a..aaff30bfa9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -655,13 +655,14 @@ This allows controlling the current/total number of matches for the prompt prefix. +++ -*** New minor mode 'icomplete-vertical-mode', alias 'fido-vertical-mode'. -This mode is intended to be used with Icomplete ('M-x icomplete-mode') -or Fido ('M-x fido-mode'), to display the list of completions -candidates vertically instead of horizontally. When used with -Icomplete, completions are rotated and selection kept at the top. -When used with Fido, completions scroll like a typical dropdown -widget. +*** New minor modes 'icomplete-vertical-mode' and 'fido-vertical-mode' +These modes are modify Icomplete ('M-x icomplete-mode') and Fido ('M-x +fido-mode'), to display completions candidates vertically instead of +horizontally. In Icomplete, completions are rotated and selection +kept at the top. In Fido, completions scroll like a typical dropdown +widget. Both these new minor modes will first turn on their +respective non-vertical counterparts first, if they are not on +already. --- *** Default value of 'icomplete-compute-delay' has been changed to 0.15 s. diff --git a/lisp/icomplete.el b/lisp/icomplete.el index cd1979d04a..e06b33e43b 100644 --- a/lisp/icomplete.el +++ b/lisp/icomplete.el @@ -626,6 +626,8 @@ Usually run by inclusion in `minibuffer-setup-hook'." (define-minor-mode icomplete-vertical-mode "Toggle vertical candidate display in `icomplete-mode' or `fido-mode'. +If none of these modes are on, turn on `icomplete-mode'. + As many completion candidates as possible are displayed, depending on the value of `max-mini-window-height', and the way the mini-window is resized depends on `resize-mini-windows'." @@ -633,10 +635,21 @@ resized depends on `resize-mini-windows'." (remove-hook 'icomplete-minibuffer-setup-hook #'icomplete--vertical-minibuffer-setup) (when icomplete-vertical-mode + (unless icomplete-mode + (icomplete-mode 1)) (add-hook 'icomplete-minibuffer-setup-hook #'icomplete--vertical-minibuffer-setup))) -(defalias 'fido-vertical-mode 'icomplete-vertical-mode) +;;;###autoload +(define-minor-mode fido-vertical-mode + "Toggle vertical candidate display in `fido-mode'. +When turning on, if non-vertical `fido-mode' is off, turn it on. +If it's on, just add the vertical display." + :global t + (icomplete-vertical-mode -1) + (when fido-vertical-mode + (unless fido-mode (fido-mode 1)) + (icomplete-vertical-mode 1))) commit ae751f2a098787aaf4dff41e8215a5609069076b Author: Stephen Berman Date: Mon Aug 16 22:55:50 2021 +0200 ; Fix menu entry alignment of last change * doc/lispref/elisp.texi (Top): Fix alignment. diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index 337c71c295..55bcf399d8 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -788,7 +788,7 @@ Defining Commands * Interactive Codes:: The standard letter-codes for reading arguments in various ways. * Interactive Examples:: Examples of how to read interactive arguments. -* Command Modes:: Specifying that commands are for a specific mode. +* Command Modes:: Specifying that commands are for a specific mode. * Generic Commands:: Select among command alternatives. commit bb6f1e2dd9d4b335ca53dc6df5a69e01db33a336 Author: Glenn Morris Date: Mon Aug 16 12:34:53 2021 -0700 * test/Makefile.in: Verbose logging for electric-tests on hydra. diff --git a/test/Makefile.in b/test/Makefile.in index 7047c24482..a3412d6b53 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -163,7 +163,7 @@ endif WRITE_LOG = > $@ 2>&1 || { STAT=$$?; cat $@; exit $$STAT; } ## On Hydra or Emba, always show logs for certain problematic tests. ifdef EMACS_HYDRA_CI -lisp/net/tramp-tests.log \ +lisp/net/tramp-tests.log lisp/electric-tests.log \ : WRITE_LOG = 2>&1 | tee $@ endif ifdef EMACS_EMBA_CI commit 6898ae6f8c0128c5ff06de3f5d7adb7f9dab6563 Author: Eli Zaretskii Date: Mon Aug 16 22:06:26 2021 +0300 Plug another hole for longjmp-ing from 'redisplay_window' * src/fringe.c (update_window_fringes): Inhibit quitting, so as not to longjmp out of redisplay_window. (Bug#44448) diff --git a/src/fringe.c b/src/fringe.c index 47615f51f9..b651a4eb0d 100644 --- a/src/fringe.c +++ b/src/fringe.c @@ -969,6 +969,14 @@ update_window_fringes (struct window *w, bool keep_current_p) if (w->pseudo_window_p) return 0; + ptrdiff_t count = SPECPDL_INDEX (); + + /* This function could be called for redisplaying non-selected + windows, in which case point has been temporarily moved to that + window's window-point. So we cannot afford quitting out of here, + as point is restored after this function returns. */ + specbind (Qinhibit_quit, Qt); + if (!MINI_WINDOW_P (w) && (ind = BVAR (XBUFFER (w->contents), indicate_buffer_boundaries), !NILP (ind))) { @@ -1331,6 +1339,8 @@ update_window_fringes (struct window *w, bool keep_current_p) row->fringe_bitmap_periodic_p = periodic_p; } + unbind_to (count, Qnil); + return redraw_p && !keep_current_p; } commit b21c9b0816e9bb923e00d6e69eed7c4341850dd0 Author: Stephen Berman Date: Mon Aug 16 19:13:53 2021 +0200 Add Command Modes to Elisp manual menu and add index entries * doc/lispref/commands.texi (Command Modes): Add index entries. * doc/lispref/elisp.texi (Top): Add entry for the Command Modes node to the detailed node listing. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index b4a8b733a0..6d45099867 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -601,6 +601,9 @@ Put them into three windows, selecting the last one." @node Command Modes @subsection Specifying Modes For Commands +@cindex commands, mode-specific +@cindex commands, specify as mode-specific +@cindex mode-specific commands Many commands in Emacs are general, and not tied to any specific mode. For instance, @kbd{M-x kill-region} can be used in pretty much any diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index 8b440c7977..337c71c295 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -788,6 +788,7 @@ Defining Commands * Interactive Codes:: The standard letter-codes for reading arguments in various ways. * Interactive Examples:: Examples of how to read interactive arguments. +* Command Modes:: Specifying that commands are for a specific mode. * Generic Commands:: Select among command alternatives. commit f0ef1e971d200940f5ebf1d50707112df659da8e Author: Eli Zaretskii Date: Mon Aug 16 20:11:33 2021 +0300 Fix recent documentation additions * doc/lispref/searching.texi (Search and Replace): Document the new functions by @defun. diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi index fe47e7ccf5..4d5ae3cb43 100644 --- a/doc/lispref/searching.texi +++ b/doc/lispref/searching.texi @@ -2553,21 +2553,31 @@ using @code{re-search-forward} and @code{replace-match}, like this: @xref{Replacing Match,, Replacing the Text that Matched}, for a description of @code{replace-match}. -@findex replace-regexp-in-region - If it's more convenient, you can also use the -@code{replace-regexp-in-region}, which does something similar to the -loop above, but is optionally delimited to a specific region (and -doesn't change point). Furthermore, it does the searches -case-sensitively, and performs the replacements without changing case -in the replacement. + It may be more convenient to limit the replacements to a specific +region. The function @code{replace-regexp-in-region} does that. + +@defun replace-regexp-in-region regexp replacement &optional start end +This function replaces all the occurrences of @var{regexp} with +@var{replacement} in the region of buffer text between @var{start} and +@var{end}; @var{start} defaults to position of point, and @var{end} +defaults to the last accessible position of the buffer. The search +for @var{regexp} is case-sensitive, and @var{replacement} is inserted +without changing its letter-case. The @var{replacement} string can +use the same special elements starting with @samp{\} as +@code{replace-match} does. The function returns the number of +replaced occurrences, or @code{nil} if @var{regexp} is not found. The +function preserves the position of point. @example (replace-regexp-in-region "foo[ \t]+bar" "foobar") @end example +@end defun -@findex replace-string-in-region - There's also @code{replace-string-in-region}, which works along the -same lines, but searches for literal strings instead. +@defun replace-string-in-region string replacement &optional start end + This function works similarly to @code{replace-regexp-in-region}, +but searches for, and replaces, literal @var{string}s instead of +regular expressions. +@end defun Emacs also has special functions for replacing matches in a string. commit 958f5915672f77f2b98c425fae4035dea5dc5116 Merge: 5d7b1d5fc7 9664ee182c Author: Glenn Morris Date: Mon Aug 16 08:01:03 2021 -0700 Merge from origin/emacs-27 9664ee182c (origin/emacs-27) C++ Mode: Don't confuse the pointer oper... b3aec9ee48 CC Mode: Fix unstable fontification of doc strings. # Conflicts: # lisp/progmodes/cc-fonts.el commit 5d7b1d5fc75752a986ed19d3fd333167ddc29d4e Author: Lars Ingebrigtsen Date: Mon Aug 16 15:40:43 2021 +0200 Make overlays-in treat zero-length overlays at point-max consistently * doc/lispref/display.texi (Finding Overlays): Adjust documentation. * src/buffer.c (overlays_in): Treat the end of the buffer and the end of the narrowed-to buffer the same (bug#19422). (Foverlays_in): Adjust doc string. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 13d0a1b458..79fb72a464 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -1917,7 +1917,8 @@ This function returns a list of the overlays that overlap the region contains one or more characters in the region; empty overlays (@pxref{Managing Overlays, empty overlay}) overlap if they are at @var{beg}, strictly between @var{beg} and @var{end}, or at @var{end} -when @var{end} denotes the position at the end of the buffer. +when @var{end} denotes the position at the end of the accessible part +of the buffer. @end defun @defun next-overlay-change pos diff --git a/etc/NEWS b/etc/NEWS index 54168e8266..3ccef6683a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3080,6 +3080,14 @@ This is to keep the same behavior as Eshell. * Incompatible Lisp Changes in Emacs 28.1 ++++ +** 'overlays-in' now handles zero-length overlays slightly differently. +Previosly, zero-length overlays at the end of the buffer were included +in the result (if the region queried for stopped at that position). +The same was not the case if the buffer had been narrowed to exclude +the real end of the buffer. This has now been changed, and +zero-length overlays at `point-max' are always included in the results. + --- ** 'replace-match' now runs modification hooks slightly later. The function is documented to leave point after the replacement text, diff --git a/src/buffer.c b/src/buffer.c index b177c5eaa7..7e4c84911b 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2995,7 +2995,7 @@ overlays_in (EMACS_INT beg, EMACS_INT end, bool extend, ptrdiff_t next = ZV; ptrdiff_t prev = BEGV; bool inhibit_storing = 0; - bool end_is_Z = end == Z; + bool end_is_Z = end == ZV; for (struct Lisp_Overlay *tail = current_buffer->overlays_before; tail; tail = tail->next) @@ -4268,9 +4268,10 @@ DEFUN ("overlays-in", Foverlays_in, Soverlays_in, 2, 2, 0, doc: /* Return a list of the overlays that overlap the region BEG ... END. Overlap means that at least one character is contained within the overlay and also contained within the specified region. + Empty overlays are included in the result if they are located at BEG, between BEG and END, or at END provided END denotes the position at the -end of the buffer. */) +end of the accessible part of the buffer. */) (Lisp_Object beg, Lisp_Object end) { ptrdiff_t len, noverlays; diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 11f842e8fe..118311c4d2 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -754,7 +754,7 @@ with parameters from the *Messages* buffer modification." (should-length 2 (overlays-in 1 (point-max))) (should-length 1 (overlays-in (point-max) (point-max))) (narrow-to-region 1 50) - (should-length 0 (overlays-in 1 (point-max))) + (should-length 1 (overlays-in 1 (point-max))) (should-length 1 (overlays-in (point-max) (point-max)))))) @@ -1399,4 +1399,25 @@ with parameters from the *Messages* buffer modification." (should (memq long-overlay (overlays-in 3 3))) (should (memq zero-overlay (overlays-in 3 3)))))) +(ert-deftest test-remove-overlays () + (with-temp-buffer + (insert "foo") + (make-overlay (point) (point)) + (should (= (length (overlays-in (point-min) (point-max))) 1)) + (remove-overlays) + (should (= (length (overlays-in (point-min) (point-max))) 0))) + + (with-temp-buffer + (insert "foo") + (goto-char 2) + (make-overlay (point) (point)) + ;; We only count zero-length overlays at the end of the buffer. + (should (= (length (overlays-in 1 2)) 0)) + (narrow-to-region 1 2) + ;; We've now narrowed, so the zero-length overlay is at the end of + ;; the (accessible part of the) buffer. + (should (= (length (overlays-in 1 2)) 1)) + (remove-overlays) + (should (= (length (overlays-in (point-min) (point-max))) 0)))) + ;;; buffer-tests.el ends here commit a3b31302dd1b7c1ffd3486b35de06c957785b919 Author: Lars Ingebrigtsen Date: Mon Aug 16 14:49:19 2021 +0200 Fix cursor movement on the Linux console with certain characters * lisp/term/linux.el (terminal-init-linux): Switch off auto-compositions, because they confuse cursor movement on the Linux console (bug#21363). diff --git a/lisp/term/linux.el b/lisp/term/linux.el index c6d84ab96c..bc61a3a7cc 100644 --- a/lisp/term/linux.el +++ b/lisp/term/linux.el @@ -12,6 +12,9 @@ ;; It can't really display underlines. (tty-no-underline) + ;; Compositions confuse cursor movement. + (global-auto-composition-mode -1) + (ignore-errors (when gpm-mouse-mode (require 't-mouse) (gpm-mouse-enable))) ;; Make Latin-1 input characters work, too. commit b9243b034b9218bbc0bf6a1e12903aea008316cb Author: Lars Ingebrigtsen Date: Mon Aug 16 14:24:53 2021 +0200 Minor clarification for define-minor-mode :variable * lisp/emacs-lisp/easy-mmode.el (define-minor-mode): * doc/lispref/modes.texi (Defining Minor Modes): Clarify what the setter function should do (bug#14875). diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 4274810cba..d9caeab3bc 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -1728,7 +1728,8 @@ anything that can be used with the @code{setf} function (@pxref{Generalized Variables}). @var{place} can also be a cons @code{(@var{get} . @var{set})}, where @var{get} is an expression that returns the current state, -and @var{set} is a function of one argument (a state) that sets it. +and @var{set} is a function of one argument (a state) which should be +assigned to @var{place}. @item :after-hook @var{after-hook} This defines a single Lisp form which is evaluated after the mode hooks diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 8a2b3b4626..d9b5ea74f6 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -178,9 +178,9 @@ BODY contains code to execute each time the mode is enabled or disabled. named variable, or a generalized variable. PLACE can also be of the form (GET . SET), where GET is an expression that returns the current state, and SET is - a function that takes one argument, the new state, and - sets it. If you specify a :variable, this function does - not define a MODE variable (nor any of the terms used + a function that takes one argument, the new state, which should + be assigned to PLACE. If you specify a :variable, this function + does not define a MODE variable (nor any of the terms used in :variable). :after-hook A single lisp form which is evaluated after the mode hooks have been run. It should not be quoted. commit adb450719a63621decab2bfb4addb030afd14db2 Author: Clément Pit-Claudel Date: Mon Aug 16 14:10:41 2021 +0200 Remove prettification for \par in tex-mode * lisp/textmodes/tex-mode.el (tex--prettify-symbols-alist): Remove prettified version of `\par'. Many fonts don't display anything for the character it was mapped to (#x2029 PARAGRAPH SEPARATOR), so enabling prettification makes every `\par' disappear (bug#50073). diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index 51216bdbe7..c53acf53e7 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -3342,7 +3342,6 @@ There might be text before point." ("\\oplus" . ?⊕) ("\\oslash" . ?⊘) ("\\otimes" . ?⊗) - ("\\par" . ?
) ("\\parallel" . ?∥) ("\\partial" . ?∂) ("\\perp" . ?⊥) commit 343dc9d41524cebfc88511a88df3969dbd766022 Author: Mattias Engdegård Date: Mon Aug 16 11:37:19 2021 +0200 ; * test/lisp/vc/diff-mode-tests.el: Remove useless \ diff --git a/test/lisp/vc/diff-mode-tests.el b/test/lisp/vc/diff-mode-tests.el index 521865906e..fefe50d517 100644 --- a/test/lisp/vc/diff-mode-tests.el +++ b/test/lisp/vc/diff-mode-tests.el @@ -475,7 +475,7 @@ baz")))) (should (equal (diff-hunk-file-names) '("/tmp/ange-ftp1351895K.el" "/tmp/ange-ftp13518wvE.el")))) (with-temp-buffer - (insert "diff -c -L /ftp\:slbhao\:/home/albinus/src/tramp/lisp/tramp.el -L /ftp\:slbhao\:/home/albinus/src/emacs/lisp/net/tramp.el /tmp/ange-ftp13518wvE.el /tmp/ange-ftp1351895K.el\n") + (insert "diff -c -L /ftp:slbhao:/home/albinus/src/tramp/lisp/tramp.el -L /ftp:slbhao:/home/albinus/src/emacs/lisp/net/tramp.el /tmp/ange-ftp13518wvE.el /tmp/ange-ftp1351895K.el\n") (goto-char (point-min)) (should (equal (diff-hunk-file-names) '("/tmp/ange-ftp1351895K.el" "/tmp/ange-ftp13518wvE.el"))))) commit 751f1707f009c714dbfe047ef43443a5c0c3df89 Author: Lars Ingebrigtsen Date: Mon Aug 16 13:20:35 2021 +0200 Add new functions to replace strings/regexp in a region * doc/lispref/searching.texi (Search and Replace): Document them. * lisp/subr.el (replace-string-in-region) (replace-regexp-in-region): New functions. * lisp/emacs-lisp/shortdoc.el (regexp, buffer): Mention them. diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi index 1d3e2d986c..fe47e7ccf5 100644 --- a/doc/lispref/searching.texi +++ b/doc/lispref/searching.texi @@ -2540,9 +2540,9 @@ associated with it still exists. @cindex replacement after search @cindex searching and replacing - If you want to find all matches for a regexp in part of the buffer, -and replace them, the best way is to write an explicit loop using -@code{re-search-forward} and @code{replace-match}, like this: + If you want to find all matches for a regexp in part of the buffer +and replace them, the most flexible way is to write an explicit loop +using @code{re-search-forward} and @code{replace-match}, like this: @example (while (re-search-forward "foo[ \t]+bar" nil t) @@ -2553,9 +2553,23 @@ and replace them, the best way is to write an explicit loop using @xref{Replacing Match,, Replacing the Text that Matched}, for a description of @code{replace-match}. - However, replacing matches in a string is more complex, especially -if you want to do it efficiently. So Emacs provides two functions to do -this. +@findex replace-regexp-in-region + If it's more convenient, you can also use the +@code{replace-regexp-in-region}, which does something similar to the +loop above, but is optionally delimited to a specific region (and +doesn't change point). Furthermore, it does the searches +case-sensitively, and performs the replacements without changing case +in the replacement. + +@example +(replace-regexp-in-region "foo[ \t]+bar" "foobar") +@end example + +@findex replace-string-in-region + There's also @code{replace-string-in-region}, which works along the +same lines, but searches for literal strings instead. + + Emacs also has special functions for replacing matches in a string. @defun replace-regexp-in-string regexp rep string &optional fixedcase literal subexp start This function copies @var{string} and searches it for matches for diff --git a/etc/NEWS b/etc/NEWS index c2b53e4705..54168e8266 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2443,6 +2443,12 @@ images are marked. ** Miscellaneous ++++ +*** New function 'replace-regexp-in-region'. + ++++ +*** New function 'replace-string-in-region'. + --- *** New function 'mail-header-parse-addresses-lax'. This takes a comma-separated string and returns a list of mail/name diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index 1b0fbfdf71..7d4a69f42a 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -700,6 +700,8 @@ There can be any number of :example/:result elements." (match-substitute-replacement :no-eval (match-substitute-replacement "new") :eg-result "new") + (replace-regexp-in-region + :no-value (replace-regexp-in-region "[0-9]+" "Num \\&")) "Utilities" (regexp-quote :eval (regexp-quote "foo.*bar")) @@ -894,6 +896,10 @@ There can be any number of :example/:result elements." :no-value (erase-buffer)) (insert :no-value (insert "This string will be inserted in the buffer\n")) + (subst-char-in-region + :no-eval "(subst-char-in-region (point-min) (point-max) ?+ ?-)") + (replace-string-in-region + :no-value (replace-string-in-region "foo" "bar")) "Locking" (lock-buffer :no-value (lock-buffer "/tmp/foo")) diff --git a/lisp/subr.el b/lisp/subr.el index 1cae3ee1a6..0a31ef2b29 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3859,6 +3859,67 @@ Point in BUFFER will be placed after the inserted text." (with-current-buffer buffer (insert-buffer-substring current start end)))) +(defun replace-string-in-region (string replacement &optional start end) + "Replace STRING with REPLACEMENT in the region from START to END. +The number of replaced occurrences are returned, or nil if STRING +doesn't exist in the region. + +If START is nil, use the current point. If END is nil, use `point-max'. + +Comparisons and replacements are done with fixed case." + (if start + (when (< start (point-min)) + (error "Start before start of buffer")) + (setq start (point))) + (if end + (when (> end (point-max)) + (error "End after end of buffer")) + (setq end (point-max))) + (save-excursion + (let ((matches 0) + (case-fold-search nil)) + (goto-char start) + (while (search-forward string end t) + (delete-region (match-beginning 0) (match-end 0)) + (insert replacement) + (setq matches (1+ matches))) + (and (not (zerop matches)) + matches)))) + +(defun replace-regexp-in-region (regexp replacement &optional start end) + "Replace REGEXP with REPLACEMENT in the region from START to END. +The number of replaced occurrences are returned, or nil if REGEXP +doesn't exist in the region. + +If START is nil, use the current point. If END is nil, use `point-max'. + +Comparisons and replacements are done with fixed case. + +REPLACEMENT can use the following special elements: + + `\\&' in NEWTEXT means substitute original matched text. + `\\N' means substitute what matched the Nth `\\(...\\)'. + If Nth parens didn't match, substitute nothing. + `\\\\' means insert one `\\'. + `\\?' is treated literally." + (if start + (when (< start (point-min)) + (error "Start before start of buffer")) + (setq start (point))) + (if end + (when (> end (point-max)) + (error "End after end of buffer")) + (setq end (point-max))) + (save-excursion + (let ((matches 0) + (case-fold-search nil)) + (goto-char start) + (while (re-search-forward regexp end t) + (replace-match replacement t) + (setq matches (1+ matches))) + (and (not (zerop matches)) + matches)))) + (defun yank-handle-font-lock-face-property (face start end) "If `font-lock-defaults' is nil, apply FACE as a `face' property. START and END denote the start and end of the text to act on. diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index b57982a705..21b8a27858 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -694,5 +694,51 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350." (should-not (buffer-local-boundp 'test-not-boundp buf)) (should (buffer-local-boundp 'test-global-boundp buf)))) +(ert-deftest test-replace-string-in-region () + (with-temp-buffer + (insert "foo bar zot foobar") + (should (= (replace-string-in-region "foo" "new" (point-min) (point-max)) + 2)) + (should (equal (buffer-string) "new bar zot newbar"))) + + (with-temp-buffer + (insert "foo bar zot foobar") + (should (= (replace-string-in-region "foo" "new" (point-min) 14) + 1)) + (should (equal (buffer-string) "new bar zot foobar"))) + + (with-temp-buffer + (insert "foo bar zot foobar") + (should-error (replace-string-in-region "foo" "new" (point-min) 30))) + + (with-temp-buffer + (insert "Foo bar zot foobar") + (should (= (replace-string-in-region "Foo" "new" (point-min)) + 1)) + (should (equal (buffer-string) "new bar zot foobar")))) + +(ert-deftest test-replace-regexp-in-region () + (with-temp-buffer + (insert "foo bar zot foobar") + (should (= (replace-regexp-in-region "fo+" "new" (point-min) (point-max)) + 2)) + (should (equal (buffer-string) "new bar zot newbar"))) + + (with-temp-buffer + (insert "foo bar zot foobar") + (should (= (replace-regexp-in-region "fo+" "new" (point-min) 14) + 1)) + (should (equal (buffer-string) "new bar zot foobar"))) + + (with-temp-buffer + (insert "foo bar zot foobar") + (should-error (replace-regexp-in-region "fo+" "new" (point-min) 30))) + + (with-temp-buffer + (insert "Foo bar zot foobar") + (should (= (replace-regexp-in-region "Fo+" "new" (point-min)) + 1)) + (should (equal (buffer-string) "new bar zot foobar")))) + (provide 'subr-tests) ;;; subr-tests.el ends here commit 42be41657813ae606427aa53d2f0f0b7039d3ef1 Author: Lars Ingebrigtsen Date: Mon Aug 16 13:17:25 2021 +0200 Don't remove `fontified' in nxml--buffer-substring-filter * lisp/nxml/nxml-mode.el (nxml--buffer-substring-filter): Removing `fontified' is probably unnecessary (bug#50061). diff --git a/lisp/nxml/nxml-mode.el b/lisp/nxml/nxml-mode.el index b7d1b190e8..405f803325 100644 --- a/lisp/nxml/nxml-mode.el +++ b/lisp/nxml/nxml-mode.el @@ -572,9 +572,7 @@ Many aspects this mode can be customized using (when (seq-find (lambda (elem) (plist-get (nth 2 elem) 'rng-state)) (object-intervals string)) - (remove-text-properties 0 (length string) - '(rng-state nil fontified nil) - string)) + (remove-text-properties 0 (length string) '(rng-state nil) string)) string) (defun nxml-cleanup () commit 9664ee182c3af476c1532354a867a421f9fbacf1 (refs/remotes/origin/emacs-27) Author: Alan Mackenzie Date: Sun Aug 15 19:43:58 2021 +0000 C++ Mode: Don't confuse the pointer operator -> with the type indicating -> This fixes bug #47468. * lisp/progmodes/cc-engine.el (c-looking-at-inexpr-block): While searching backwards for "->" which is a type indicating operator, disallow also commas. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 9cba87f4d9..f5e4c4b992 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -11993,7 +11993,7 @@ comment at the start of cc-engine.el for more info." (save-excursion (while (progn - (c-syntactic-skip-backward "^;=}>" closest-lim t) + (c-syntactic-skip-backward "^;=,}>" closest-lim t) (and (eq (char-before) ?>) (c-backward-token-2) (not (looking-at c-haskell-op-re))))) commit b3aec9ee483c82aa23e604ea61ce0777e9ef2ebd Author: Alan Mackenzie Date: Sun Aug 15 18:08:25 2021 +0000 CC Mode: Fix unstable fontification of doc strings. Also optimize a loop over several line doc-comments. * lisp/progmodes/cc-fonts.el (c-font-lock-doc-comments): New variable comment-mid, used as the starting point for applying c-doc-face-name in a line comments. In block comments, apply this face not from `comment-beg' but from `region-beg', no earlier than the start of the fontification region. diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index fd00d65e33..5f3e2eaaf8 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -2733,13 +2733,14 @@ need for `pike-font-lock-extra-types'.") ;; ;; This function might do hidden buffer changes. - (let (comment-beg region-beg) + (let (comment-beg region-beg comment-mid) (if (memq (get-text-property (point) 'face) '(font-lock-comment-face font-lock-comment-delimiter-face)) ;; Handle the case when the fontified region starts inside a ;; comment. (let ((start (c-literal-start))) - (setq region-beg (point)) + (setq region-beg (point) + comment-mid (point)) (when start (goto-char start)) (when (looking-at prefix) @@ -2765,7 +2766,8 @@ need for `pike-font-lock-extra-types'.") (goto-char comment-beg) (c-in-literal))))) (setq comment-beg nil)) - (setq region-beg comment-beg)) + (setq region-beg comment-beg + comment-mid comment-beg)) (if (elt (parse-partial-sexp comment-beg (+ comment-beg 2)) 7) ;; Collect a sequence of doc style line comments. @@ -2773,15 +2775,16 @@ need for `pike-font-lock-extra-types'.") (goto-char comment-beg) (while (and (progn (c-forward-single-comment) - (c-put-font-lock-face comment-beg (point) + (c-put-font-lock-face comment-mid (point) c-doc-face-name) (skip-syntax-forward " ") - (setq comment-beg (point)) + (setq comment-beg (point) + comment-mid (point)) (< (point) limit)) (looking-at prefix)))) (goto-char comment-beg) (c-forward-single-comment) - (c-put-font-lock-face comment-beg (point) c-doc-face-name)) + (c-put-font-lock-face region-beg (point) c-doc-face-name)) (if (> (point) limit) (goto-char limit)) (setq comment-beg nil) commit d9eac0b4263c10b2ab3a428cf8faa4b5e1d99a83 (refs/remotes/origin/feature/context-menu) Author: Juri Linkov Date: Sun Aug 15 19:27:06 2021 +0300 Use map-keymap in context-menu-global, context-menu-local, context-menu-minor diff --git a/lisp/mouse.el b/lisp/mouse.el index 1f5bd8e4a6..4215425547 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -317,22 +317,24 @@ the same menu with changes such as added new menu items." "Global submenus." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) (define-key-after menu [separator-global] menu-bar-separator) - (dolist (item (lookup-key global-map [menu-bar])) - (when (and (consp item) (consp (cdr item))) - (define-key-after menu (vector (car item)) - (copy-sequence (cdr item))))) + (map-keymap (lambda (key binding) + (when (consp binding) + (define-key-after menu (vector key) + (copy-sequence binding)))) + (lookup-key global-map [menu-bar])) menu) (defun context-menu-local (menu) "Major mode submenus." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) (define-key-after menu [separator-local] menu-bar-separator) - (dolist (item (local-key-binding [menu-bar])) - (when (and (consp item) (consp (cdr item))) - ;; Fix deep menu created by `imenu-add-to-menubar'. - (when (eq (car item) 'keymap) (setq item (cadr item))) - (define-key-after menu (vector (car item)) - (copy-sequence (cdr item))))) + (let ((keymap (local-key-binding [menu-bar]))) + (when keymap + (map-keymap (lambda (key binding) + (when (consp binding) + (define-key-after menu (vector key) + (copy-sequence binding)))) + keymap))) menu) (defun context-menu-minor (menu) @@ -341,10 +343,11 @@ the same menu with changes such as added new menu items." (define-key-after menu [separator-minor] menu-bar-separator) (dolist (mode (minor-mode-key-binding [menu-bar])) (when (and (consp mode) (symbol-value (car mode))) - (dolist (item (cdr mode)) - (when (and (consp item) (consp (cdr item))) - (define-key-after menu (vector (car item)) - (copy-sequence (cdr item))))))) + (map-keymap (lambda (key binding) + (when (consp binding) + (define-key-after menu (vector key) + (copy-sequence binding)))) + (cdr mode)))) menu) (defun context-menu-vc (menu) commit de5601f0902b05f594bd2254b23032ed4c598f9d Author: Juri Linkov Date: Tue Aug 3 11:23:57 2021 +0300 Improve handling of context menus for global, local, minor-mode menus * lisp/mouse.el (context-menu-global, context-menu-local, context-menu-minor): Better handling of possibly nested menu maps. diff --git a/lisp/mouse.el b/lisp/mouse.el index 49191e32ea..1f5bd8e4a6 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -318,11 +318,9 @@ the same menu with changes such as added new menu items." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) (define-key-after menu [separator-global] menu-bar-separator) (dolist (item (lookup-key global-map [menu-bar])) - (when (consp item) + (when (and (consp item) (consp (cdr item))) (define-key-after menu (vector (car item)) - (if (consp (cdr item)) - (copy-sequence (cdr item)) - (cdr item))))) + (copy-sequence (cdr item))))) menu) (defun context-menu-local (menu) @@ -330,11 +328,11 @@ the same menu with changes such as added new menu items." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) (define-key-after menu [separator-local] menu-bar-separator) (dolist (item (local-key-binding [menu-bar])) - (when (consp item) + (when (and (consp item) (consp (cdr item))) + ;; Fix deep menu created by `imenu-add-to-menubar'. + (when (eq (car item) 'keymap) (setq item (cadr item))) (define-key-after menu (vector (car item)) - (if (consp (cdr item)) - (copy-sequence (cdr item)) - (cdr item))))) + (copy-sequence (cdr item))))) menu) (defun context-menu-minor (menu) @@ -344,11 +342,9 @@ the same menu with changes such as added new menu items." (dolist (mode (minor-mode-key-binding [menu-bar])) (when (and (consp mode) (symbol-value (car mode))) (dolist (item (cdr mode)) - (when (consp item) + (when (and (consp item) (consp (cdr item))) (define-key-after menu (vector (car item)) - (if (consp (cdr item)) - (copy-sequence (cdr item)) - (cdr item))))))) + (copy-sequence (cdr item))))))) menu) (defun context-menu-vc (menu) commit 9f5946b6ff2780a95a85909cd61e74986f5acf8a Author: Juri Linkov Date: Sun Aug 1 23:07:29 2021 +0300 * lisp/help-mode.el (help-mode-context-menu): New function. (help-mode): Use it. diff --git a/lisp/help-mode.el b/lisp/help-mode.el index 3976a9ac4e..6a11fc1ed9 100644 --- a/lisp/help-mode.el +++ b/lisp/help-mode.el @@ -72,6 +72,35 @@ ["Customize" help-customize :help "Customize variable or face"])) +(defun help-mode-context-menu (menu) + (define-key menu [help-mode-separator] menu-bar-separator) + (let ((easy-menu (make-sparse-keymap "Help-Mode"))) + (easy-menu-define nil easy-menu nil + '("Help-Mode" + ["Previous Topic" help-go-back + :help "Go back to previous topic in this help buffer" + :active help-xref-stack] + ["Next Topic" help-go-forward + :help "Go back to next topic in this help buffer" + :active help-xref-forward-stack])) + (dolist (item (reverse (lookup-key easy-menu [menu-bar help-mode]))) + (when (consp item) + (define-key menu (vector (car item)) (cdr item))))) + + (when (and + ;; First check if `help-fns--list-local-commands' + ;; used `where-is-internal' to call this function + ;; with wrong `last-input-event'. + (eq (current-buffer) (window-buffer (posn-window (event-start last-input-event)))) + (mouse-posn-property (event-start last-input-event) 'mouse-face)) + (define-key menu [help-mode-push-button] + '(menu-item "Follow Link" (lambda (event) + (interactive "e") + (push-button event)) + :help "Follow the link at click"))) + + menu) + (defvar help-mode-tool-bar-map (let ((map (make-sparse-keymap))) (tool-bar-local-item "close" 'quit-window 'quit map @@ -342,6 +371,7 @@ Commands: \\{help-mode-map}" (setq-local revert-buffer-function #'help-mode-revert-buffer) + (add-hook 'context-menu-functions 'help-mode-context-menu 5 t) (setq-local tool-bar-map help-mode-tool-bar-map) (setq-local help-mode--current-data nil) commit ee1887bf54e7091a382ad24691ce0f482c61d7ea Author: Juri Linkov Date: Wed Jul 28 19:40:23 2021 +0300 Improve :type of defcustom 'context-menu-functions' and add documentation. * doc/emacs/frames.texi (Menu Mouse Clicks): Describe context-menu-mode and context-menu-functions instead of suggesting global-set-key. (Menu Bars): Mention context-menu-mode and context-menu-functions. * etc/NEWS: Add context-menu-mode and context-menu-functions. * lisp/mouse.el (context-menu-functions): Use :type with repeat/function-item. (context-menu-minor, context-menu-undo, context-menu-region) (context-menu-ffap): Improve docstrings displayed for function-item in defcustom of context-menu-functions. diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index 70615f68ed..e03fb95755 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -366,20 +366,15 @@ This menu is for changing the default face within the window's buffer. @xref{Text Scale}. @end table +@cindex context menu +@findex context-menu-mode +@vindex context-menu-functions +@kindex Down-mouse-3 Some graphical applications use @kbd{mouse-3} for a mode-specific -menu. If you prefer @kbd{mouse-3} in Emacs to bring up such a menu -instead of running the @code{mouse-save-then-kill} command, rebind -@kbd{mouse-3} by adding the following line to your init file -(@pxref{Init Rebinding}): - -@smallexample -(global-set-key [mouse-3] - '(menu-item "Menu Bar" ignore - :filter (lambda (_) - (if (zerop (or (frame-parameter nil 'menu-bar-lines) 0)) - (mouse-menu-bar-map) - (mouse-menu-major-mode-map))))) -@end smallexample +menu. If you prefer @kbd{mouse-3} in Emacs to bring up such a context +menu instead of running the @code{mouse-save-then-kill} command, +enable @code{context-menu-mode} and customize the variable +@code{context-menu-functions}. @node Mode Line Mouse @section Mode Line Mouse Commands @@ -1217,7 +1212,9 @@ the use of menu bars at startup, customize the variable terminals, where this makes one additional line available for text. If the menu bar is off, you can still pop up a menu of its contents with @kbd{C-mouse-3} on a display which supports pop-up menus. -@xref{Menu Mouse Clicks}. +Or you can enable @code{context-menu-mode} and customize the variable +@code{context-menu-functions} to pop up a context menu with +@kbd{mouse-3}. @xref{Menu Mouse Clicks}. @xref{Menu Bar}, for information on how to invoke commands with the menu bar. @xref{X Resources}, for how to customize the menu bar diff --git a/etc/NEWS b/etc/NEWS index 49396c321d..4ca2623e9a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -346,8 +346,17 @@ onto 'file-name-history'. +++ ** A prefix arg now causes 'delete-other-frames' to only iconify frames. +** Menus + ++++ +*** New mode 'context-menu-mode' for a context menu bound to 'mouse-3'. +When this mode is enabled, clicking 'down-mouse-3' anywhere in the buffer +pops up a context menu whose contents depends on surrounding context +near the mouse click. You can customize the order of the default submenus +in the context menu by customizing the user option 'context-menu-functions'. + +++ -** The "Edit => Clear" menu item now obeys a rectangular region. +*** The "Edit => Clear" menu item now obeys a rectangular region. +++ ** New command 'execute-extended-command-for-buffer'. diff --git a/lisp/mouse.el b/lisp/mouse.el index 60c4f4b85c..49191e32ea 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -286,14 +286,15 @@ not it is actually displayed." "List of functions that produce the contents of the context menu. Each function receives the menu as its argument and should return the same menu with changes such as added new menu items." - :type 'hook - :options '(context-menu-undo - context-menu-region - context-menu-global - context-menu-local - context-menu-minor - context-menu-vc - context-menu-ffap) + :type '(repeat + (choice (function-item context-menu-undo) + (function-item context-menu-region) + (function-item context-menu-global) + (function-item context-menu-local) + (function-item context-menu-minor) + (function-item context-menu-vc) + (function-item context-menu-ffap) + (function :tag "Custom function"))) :version "28.1") (defcustom context-menu-filter-function nil @@ -337,7 +338,7 @@ the same menu with changes such as added new menu items." menu) (defun context-menu-minor (menu) - "Minor mode submenus." + "Minor modes submenus." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) (define-key-after menu [separator-minor] menu-bar-separator) (dolist (mode (minor-mode-key-binding [menu-bar])) @@ -357,6 +358,7 @@ the same menu with changes such as added new menu items." menu) (defun context-menu-undo (menu) + "Undo menu." (when (cddr menu) (define-key-after menu [separator-undo] menu-bar-separator)) (define-key-after menu [undo] @@ -375,6 +377,7 @@ the same menu with changes such as added new menu items." menu) (defun context-menu-region (menu) + "Region commands menu." (when (cddr menu) (define-key-after menu [separator-region] menu-bar-separator)) (define-key-after menu [cut] @@ -424,6 +427,7 @@ the same menu with changes such as added new menu items." menu) (defun context-menu-ffap (menu) + "File at point menu." (save-excursion (mouse-set-point last-input-event) (when (ffap-guess-file-name-at-point) commit 2c2baa9d1231ad9e642e3a80fb1c641fdde71b33 Author: Juri Linkov Date: Tue Jul 27 23:48:07 2021 +0300 Add new context-menu options for menus "File At Point" and "Version Control". * lisp/mouse.el (context-menu-functions): Add more options. (context-menu-global, context-menu-local): Fix separators. (context-menu-minor): Rewrite to support list of submenus. (context-menu-vc, context-menu-ffap): New functions. (context-menu-undo, context-menu-region): Fix separators. * lisp/dired.el (dired-context-menu): * lisp/info.el (Info-context-menu): * lisp/net/goto-addr.el (goto-address-context-menu): * lisp/net/eww.el (eww-context-menu): * lisp/progmodes/prog-mode.el (prog-context-menu): Fix separators. diff --git a/lisp/dired.el b/lisp/dired.el index e16df189a7..4e82d57e21 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -2196,7 +2196,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." (defun dired-context-menu (menu) (when (mouse-posn-property (event-start last-input-event) 'dired-filename) - (define-key menu [dired-separator-2] menu-bar-separator) + (define-key menu [dired-separator] menu-bar-separator) (let ((easy-menu (make-sparse-keymap "Immediate"))) (easy-menu-define nil easy-menu nil '("Immediate" @@ -2206,8 +2206,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." :help "Edit file at mouse click in other window"])) (dolist (item (reverse (lookup-key easy-menu [menu-bar immediate]))) (when (consp item) - (define-key menu (vector (car item)) (cdr item))))) - (define-key menu [dired-separator-1] menu-bar-separator)) + (define-key menu (vector (car item)) (cdr item)))))) menu) diff --git a/lisp/info.el b/lisp/info.el index 14dc299295..0b3a9e4bf5 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -4147,7 +4147,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." ["Exit" quit-window :help "Stop reading Info"])) (defun Info-context-menu (menu) - (define-key menu [Info-separator-2] menu-bar-separator) + (define-key menu [Info-separator] menu-bar-separator) (let ((easy-menu (make-sparse-keymap "Info"))) (easy-menu-define nil easy-menu nil '("Info" @@ -4163,7 +4163,6 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (define-key menu [Info-mouse-follow-nearest-node] '(menu-item "Follow Link" Info-mouse-follow-nearest-node :help "Follow a link where you click"))) - (define-key menu [Info-separator-1] menu-bar-separator) menu) diff --git a/lisp/mouse.el b/lisp/mouse.el index 37721e6082..60c4f4b85c 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -291,7 +291,9 @@ the same menu with changes such as added new menu items." context-menu-region context-menu-global context-menu-local - context-menu-minor) + context-menu-minor + context-menu-vc + context-menu-ffap) :version "28.1") (defcustom context-menu-filter-function nil @@ -313,44 +315,50 @@ the same menu with changes such as added new menu items." (defun context-menu-global (menu) "Global submenus." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) - (define-key-after menu [separator-global-1] menu-bar-separator) + (define-key-after menu [separator-global] menu-bar-separator) (dolist (item (lookup-key global-map [menu-bar])) (when (consp item) (define-key-after menu (vector (car item)) (if (consp (cdr item)) (copy-sequence (cdr item)) (cdr item))))) - (define-key-after menu [separator-global-2] menu-bar-separator) menu) (defun context-menu-local (menu) "Major mode submenus." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) - (define-key-after menu [separator-local-1] menu-bar-separator) + (define-key-after menu [separator-local] menu-bar-separator) (dolist (item (local-key-binding [menu-bar])) (when (consp item) (define-key-after menu (vector (car item)) (if (consp (cdr item)) (copy-sequence (cdr item)) (cdr item))))) - (define-key-after menu [separator-local-2] menu-bar-separator) menu) (defun context-menu-minor (menu) "Minor mode submenus." (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) - (define-key-after menu [separator-minor-1] menu-bar-separator) - (dolist (item (minor-mode-key-binding [menu-bar])) - (when (and (consp item) (symbol-value (car item))) - (define-key-after menu (vector (cadr item)) - (if (consp (cddr item)) - (copy-sequence (cddr item)) - (cddr item))))) - (define-key-after menu [separator-minor-2] menu-bar-separator) + (define-key-after menu [separator-minor] menu-bar-separator) + (dolist (mode (minor-mode-key-binding [menu-bar])) + (when (and (consp mode) (symbol-value (car mode))) + (dolist (item (cdr mode)) + (when (consp item) + (define-key-after menu (vector (car item)) + (if (consp (cdr item)) + (copy-sequence (cdr item)) + (cdr item))))))) + menu) + +(defun context-menu-vc (menu) + "Version Control menu." + (define-key-after menu [separator-vc] menu-bar-separator) + (define-key-after menu [vc-menu] vc-menu-entry) menu) (defun context-menu-undo (menu) - (define-key-after menu [separator-undo-1] menu-bar-separator) + (when (cddr menu) + (define-key-after menu [separator-undo] menu-bar-separator)) (define-key-after menu [undo] '(menu-item "Undo" undo :visible (and (not buffer-read-only) @@ -364,11 +372,11 @@ the same menu with changes such as added new menu items." :visible (and (not buffer-read-only) (undo--last-change-was-undo-p buffer-undo-list)) :help "Redo last undone edits")) - (define-key-after menu [separator-undo-2] menu-bar-separator) menu) (defun context-menu-region (menu) - (define-key-after menu [separator-region-1] menu-bar-separator) + (when (cddr menu) + (define-key-after menu [separator-region] menu-bar-separator)) (define-key-after menu [cut] '(menu-item "Cut" kill-region :visible (and mark-active (not buffer-read-only)) @@ -413,7 +421,16 @@ the same menu with changes such as added new menu items." (define-key-after menu [mark-whole-buffer] '(menu-item "Select All" mark-whole-buffer :help "Mark the whole buffer for a subsequent cut/copy")) - (define-key-after menu [separator-region-2] menu-bar-separator) + menu) + +(defun context-menu-ffap (menu) + (save-excursion + (mouse-set-point last-input-event) + (when (ffap-guess-file-name-at-point) + (define-key menu [ffap-separator] menu-bar-separator) + (define-key menu [ffap-at-mouse] + '(menu-item "Find File or URL" ffap-at-mouse + :help "Find file or URL guessed from text around mouse click")))) menu) (defvar context-menu-entry diff --git a/lisp/net/eww.el b/lisp/net/eww.el index b876102986..5c46ccfe00 100644 --- a/lisp/net/eww.el +++ b/lisp/net/eww.el @@ -1022,7 +1022,7 @@ the like." map)) (defun eww-context-menu (menu) - (define-key menu [eww-separator-2] menu-bar-separator) + (define-key menu [eww-separator] menu-bar-separator) (let ((easy-menu (make-sparse-keymap "Eww"))) (easy-menu-define nil easy-menu nil '("Eww" @@ -1047,7 +1047,6 @@ the like." 'shr-mouse-browse-url-new-window 'shr-mouse-browse-url) :help "Browse the URL under the mouse cursor"))) - (define-key menu [eww-separator-1] menu-bar-separator) menu) diff --git a/lisp/net/goto-addr.el b/lisp/net/goto-addr.el index c270cf53cb..2c43d0f753 100644 --- a/lisp/net/goto-addr.el +++ b/lisp/net/goto-addr.el @@ -126,11 +126,10 @@ will have no effect.") (defun goto-address-context-menu (menu) (when (mouse-posn-property (event-start last-input-event) 'goto-address) - (define-key menu [goto-address-separator-2] menu-bar-separator) + (define-key menu [goto-address-separator] menu-bar-separator) (define-key menu [goto-address-at-mouse] '(menu-item "Follow Link" goto-address-at-mouse - :help "Follow a link where you click")) - (define-key menu [goto-address-separator-1] menu-bar-separator)) + :help "Follow a link where you click"))) menu) (defcustom goto-address-url-face 'link diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el index a10291a9c7..a8b608b018 100644 --- a/lisp/progmodes/prog-mode.el +++ b/lisp/progmodes/prog-mode.el @@ -45,22 +45,20 @@ (defun prog-context-menu (menu) (when (featurep 'xref) - (define-key-after menu [prog-separator-1] menu-bar-separator - 'separator-region-2) + (define-key-after menu [prog-separator] menu-bar-separator + 'mark-whole-buffer) (define-key-after menu [xref-find-def] '(menu-item "Find Definition" xref-find-definitions-at-mouse :visible (save-excursion (mouse-set-point last-input-event) (xref-backend-identifier-at-point (xref-find-backend))) :help "Find definition of function or variable") - 'prog-separator-1) + 'prog-separator) (define-key-after menu [xref-pop] '(menu-item "Back Definition" xref-pop-marker-stack :visible (not (xref-marker-stack-empty-p)) :help "Back to the position of the last search") - 'xref-find-def) - (define-key-after menu [prog-separator-2] menu-bar-separator - 'xref-pop)) + 'xref-find-def)) menu) (defvar prog-mode-map commit ebac285d0ee567ed86e14b871cf0bac2de8655db Author: Juri Linkov Date: Wed Jul 21 23:34:59 2021 +0300 Improve docstring of context-menu-functions and add eww-context-menu * lisp/mouse.el (context-menu-functions): Explain function args in docstring. * lisp/net/eww.el (eww-context-menu): New function. (eww-mode): Add it to context-menu-functions. * lisp/info.el (Info-context-menu): Move history items higher. * lisp/progmodes/prog-mode.el (prog-context-menu): Add menu items in the middle of the menu after the region menu items. diff --git a/lisp/dired.el b/lisp/dired.el index 5e44f524f6..e16df189a7 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -2196,7 +2196,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." (defun dired-context-menu (menu) (when (mouse-posn-property (event-start last-input-event) 'dired-filename) - (define-key menu [dired-separator-1] menu-bar-separator) + (define-key menu [dired-separator-2] menu-bar-separator) (let ((easy-menu (make-sparse-keymap "Immediate"))) (easy-menu-define nil easy-menu nil '("Immediate" @@ -2207,7 +2207,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." (dolist (item (reverse (lookup-key easy-menu [menu-bar immediate]))) (when (consp item) (define-key menu (vector (car item)) (cdr item))))) - (define-key menu [dired-separator-2] menu-bar-separator)) + (define-key menu [dired-separator-1] menu-bar-separator)) menu) diff --git a/lisp/info.el b/lisp/info.el index a8848a9475..14dc299295 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -4147,14 +4147,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." ["Exit" quit-window :help "Stop reading Info"])) (defun Info-context-menu (menu) - (when (mouse-posn-property (event-start last-input-event) 'mouse-face) - (define-key menu [Info-separator-link-1] menu-bar-separator) - (define-key menu [Info-mouse-follow-nearest-node] - '(menu-item "Follow Link" Info-mouse-follow-nearest-node - :help "Follow a link where you click")) - (define-key menu [Info-separator-link-2] menu-bar-separator)) - - (define-key-after menu [Info-separator-1] menu-bar-separator) + (define-key menu [Info-separator-2] menu-bar-separator) (let ((easy-menu (make-sparse-keymap "Info"))) (easy-menu-define nil easy-menu nil '("Info" @@ -4162,10 +4155,15 @@ If FORK is non-nil, it is passed to `Info-goto-node'." :help "Go back in history to the last node you were at"] ["Forward in History" Info-history-forward :visible Info-history-forward :help "Go forward in history"])) - (dolist (item (lookup-key easy-menu [menu-bar info])) + (dolist (item (reverse (lookup-key easy-menu [menu-bar info]))) (when (consp item) - (define-key-after menu (vector (car item)) (cdr item))))) - (define-key-after menu [Info-separator-2] menu-bar-separator) + (define-key menu (vector (car item)) (cdr item))))) + + (when (mouse-posn-property (event-start last-input-event) 'mouse-face) + (define-key menu [Info-mouse-follow-nearest-node] + '(menu-item "Follow Link" Info-mouse-follow-nearest-node + :help "Follow a link where you click"))) + (define-key menu [Info-separator-1] menu-bar-separator) menu) diff --git a/lisp/mouse.el b/lisp/mouse.el index 7a564f989c..37721e6082 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -283,7 +283,9 @@ not it is actually displayed." context-menu-region context-menu-local context-menu-minor) - "List of functions that produce the contents of the context menu." + "List of functions that produce the contents of the context menu. +Each function receives the menu as its argument and should return +the same menu with changes such as added new menu items." :type 'hook :options '(context-menu-undo context-menu-region diff --git a/lisp/net/eww.el b/lisp/net/eww.el index eec3ec7ba8..b876102986 100644 --- a/lisp/net/eww.el +++ b/lisp/net/eww.el @@ -1021,6 +1021,36 @@ the like." ["Toggle Paragraph Direction" eww-toggle-paragraph-direction])) map)) +(defun eww-context-menu (menu) + (define-key menu [eww-separator-2] menu-bar-separator) + (let ((easy-menu (make-sparse-keymap "Eww"))) + (easy-menu-define nil easy-menu nil + '("Eww" + ["Back to previous page" eww-back-url + :visible (not (zerop (length eww-history)))] + ["Forward to next page" eww-forward-url + :visible (not (zerop eww-history-position))] + ["Reload" eww-reload t])) + (dolist (item (reverse (lookup-key easy-menu [menu-bar eww]))) + (when (consp item) + (define-key menu (vector (car item)) (cdr item))))) + + (when (or (mouse-posn-property (event-start last-input-event) 'shr-url) + (mouse-posn-property (event-start last-input-event) 'image-url)) + (define-key menu [shr-mouse-browse-url-new-window] + `(menu-item "Follow URL in new window" ,(if browse-url-new-window-flag + 'shr-mouse-browse-url + 'shr-mouse-browse-url-new-window) + :help "Browse the URL under the mouse cursor in a new window")) + (define-key menu [shr-mouse-browse-url] + `(menu-item "Follow URL" ,(if browse-url-new-window-flag + 'shr-mouse-browse-url-new-window + 'shr-mouse-browse-url) + :help "Browse the URL under the mouse cursor"))) + (define-key menu [eww-separator-1] menu-bar-separator) + + menu) + (defvar eww-tool-bar-map (let ((map (make-sparse-keymap))) (dolist (tool-bar-item @@ -1044,6 +1074,7 @@ the like." (setq-local eww-data (list :title "")) (setq-local browse-url-browser-function #'eww-browse-url) (add-hook 'after-change-functions #'eww-process-text-input nil t) + (add-hook 'context-menu-functions 'eww-context-menu 5 t) (setq-local eww-history nil) (setq-local eww-history-position 0) (when (boundp 'tool-bar-map) diff --git a/lisp/net/goto-addr.el b/lisp/net/goto-addr.el index 2c43d0f753..c270cf53cb 100644 --- a/lisp/net/goto-addr.el +++ b/lisp/net/goto-addr.el @@ -126,10 +126,11 @@ will have no effect.") (defun goto-address-context-menu (menu) (when (mouse-posn-property (event-start last-input-event) 'goto-address) - (define-key menu [goto-address-separator] menu-bar-separator) + (define-key menu [goto-address-separator-2] menu-bar-separator) (define-key menu [goto-address-at-mouse] '(menu-item "Follow Link" goto-address-at-mouse - :help "Follow a link where you click"))) + :help "Follow a link where you click")) + (define-key menu [goto-address-separator-1] menu-bar-separator)) menu) (defcustom goto-address-url-face 'link diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el index ad1b13f4bd..a10291a9c7 100644 --- a/lisp/progmodes/prog-mode.el +++ b/lisp/progmodes/prog-mode.el @@ -45,18 +45,22 @@ (defun prog-context-menu (menu) (when (featurep 'xref) - (define-key-after menu [prog-separator-1] menu-bar-separator) + (define-key-after menu [prog-separator-1] menu-bar-separator + 'separator-region-2) (define-key-after menu [xref-find-def] '(menu-item "Find Definition" xref-find-definitions-at-mouse :visible (save-excursion (mouse-set-point last-input-event) (xref-backend-identifier-at-point (xref-find-backend))) - :help "Find definition of function or variable")) + :help "Find definition of function or variable") + 'prog-separator-1) (define-key-after menu [xref-pop] '(menu-item "Back Definition" xref-pop-marker-stack :visible (not (xref-marker-stack-empty-p)) - :help "Back to the position of the last search")) - (define-key-after menu [prog-separator-2] menu-bar-separator)) + :help "Back to the position of the last search") + 'xref-find-def) + (define-key-after menu [prog-separator-2] menu-bar-separator + 'xref-pop)) menu) (defvar prog-mode-map commit 1493145e3048e057d8ac9e8c9c56d1f99a97eb53 Author: Juri Linkov Date: Wed Jul 21 21:40:11 2021 +0300 Change the order of context-menu-functions and add more context menus. * lisp/mouse.el (context-menu-functions): Update default list. (context-menu-overriding-function): Remove variable. (context-menu-map): Reverse the order. (context-menu-global, context-menu-local, context-menu-minor): New functions. (context-menu-undo, context-menu-region): Add separators. Use define-key-after instead of bindings--define-key. (context-menu-entry): New variable. (context-menu-mode): Use it. * lisp/dired.el (dired-context-menu): New function. (dired-mode): Add it to context-menu-functions. * lisp/info.el (Info-context-menu): Reorder. * lisp/net/goto-addr.el (goto-address-at-mouse): Rename from goto-address-at-click. (goto-address-context-menu): Use goto-address-at-mouse. * lisp/progmodes/prog-mode.el (prog-context-menu): New function. (prog-mode): Add it to context-menu-functions. diff --git a/lisp/dired.el b/lisp/dired.el index 28448be06c..5e44f524f6 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -2194,6 +2194,22 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." ["Delete Image Tag..." image-dired-delete-tag :help "Delete image tag from current or marked files"])) +(defun dired-context-menu (menu) + (when (mouse-posn-property (event-start last-input-event) 'dired-filename) + (define-key menu [dired-separator-1] menu-bar-separator) + (let ((easy-menu (make-sparse-keymap "Immediate"))) + (easy-menu-define nil easy-menu nil + '("Immediate" + ["Find This File" dired-mouse-find-file + :help "Edit file at mouse click"] + ["Find in Other Window" dired-mouse-find-file-other-window + :help "Edit file at mouse click in other window"])) + (dolist (item (reverse (lookup-key easy-menu [menu-bar immediate]))) + (when (consp item) + (define-key menu (vector (car item)) (cdr item))))) + (define-key menu [dired-separator-2] menu-bar-separator)) + menu) + ;;; Dired mode @@ -2293,6 +2309,7 @@ Keybindings: (append dired-dnd-protocol-alist dnd-protocol-alist))) (add-hook 'file-name-at-point-functions #'dired-file-name-at-point nil t) (add-hook 'isearch-mode-hook #'dired-isearch-filenames-setup nil t) + (add-hook 'context-menu-functions 'dired-context-menu 5 t) (run-mode-hooks 'dired-mode-hook)) diff --git a/lisp/info.el b/lisp/info.el index 226ec76eb6..a8848a9475 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -4117,9 +4117,9 @@ If FORK is non-nil, it is passed to `Info-goto-node'." :help "Search for another occurrence of regular expression"] "---" ("History" - ["Back in history" Info-history-back :active Info-history + ["Back in History" Info-history-back :active Info-history :help "Go back in history to the last node you were at"] - ["Forward in history" Info-history-forward :active Info-history-forward + ["Forward in History" Info-history-forward :active Info-history-forward :help "Go forward in history"] ["Show History" Info-history :active Info-history-list :help "Go to menu of visited nodes"]) @@ -4148,34 +4148,25 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (defun Info-context-menu (menu) (when (mouse-posn-property (event-start last-input-event) 'mouse-face) - (bindings--define-key menu [Info-mouse-follow-nearest-node] - '(menu-item "Follow link" Info-mouse-follow-nearest-node - :help "Follow a link where you click"))) - - (bindings--define-key menu [Info-history-back] - '(menu-item "Back in history" Info-history-back :visible Info-history - :help "Go back in history to the last node you were at")) - (bindings--define-key menu [Info-history-forward] - '(menu-item "Forward in history" Info-history-forward :visible Info-history-forward - :help "Go forward in history")) - - (bindings--define-key menu [Info-up] - '(menu-item "Up" Info-up :visible (Info-check-pointer "up") - :help "Go up in the Info tree")) - (bindings--define-key menu [Info-next] - '(menu-item "Next" Info-next :visible (Info-check-pointer "next") - :help "Go to the next node")) - (bindings--define-key menu [Info-prev] - '(menu-item "Previous" Info-prev :visible (Info-check-pointer "prev[ious]*") - :help "Go to the previous node")) - (bindings--define-key menu [Info-backward-node] - '(menu-item "Backward" Info-backward-node - :help "Go backward one node, considering all as a sequence")) - (bindings--define-key menu [Info-forward-node] - '(menu-item "Forward" Info-forward-node - :help "Go forward one node, considering all as a sequence")) - - (define-key menu [Info-separator] menu-bar-separator) + (define-key menu [Info-separator-link-1] menu-bar-separator) + (define-key menu [Info-mouse-follow-nearest-node] + '(menu-item "Follow Link" Info-mouse-follow-nearest-node + :help "Follow a link where you click")) + (define-key menu [Info-separator-link-2] menu-bar-separator)) + + (define-key-after menu [Info-separator-1] menu-bar-separator) + (let ((easy-menu (make-sparse-keymap "Info"))) + (easy-menu-define nil easy-menu nil + '("Info" + ["Back in History" Info-history-back :visible Info-history + :help "Go back in history to the last node you were at"] + ["Forward in History" Info-history-forward :visible Info-history-forward + :help "Go forward in history"])) + (dolist (item (lookup-key easy-menu [menu-bar info])) + (when (consp item) + (define-key-after menu (vector (car item)) (cdr item))))) + (define-key-after menu [Info-separator-2] menu-bar-separator) + menu) (defvar info-tool-bar-map @@ -4477,7 +4468,7 @@ Advanced commands: (add-hook 'clone-buffer-hook 'Info-clone-buffer nil t) (add-hook 'change-major-mode-hook 'font-lock-defontify nil t) (add-hook 'isearch-mode-hook 'Info-isearch-start nil t) - (add-hook 'context-menu-functions 'Info-context-menu nil t) + (add-hook 'context-menu-functions 'Info-context-menu 5 t) (when Info-standalone (add-hook 'quit-window-hook 'save-buffers-kill-emacs nil t)) (setq-local isearch-search-fun-function #'Info-isearch-search) diff --git a/lisp/mouse.el b/lisp/mouse.el index 580fe8eb35..7a564f989c 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -279,34 +279,77 @@ not it is actually displayed." ;; Context menus. -(defcustom context-menu-functions '(context-menu-undo context-menu-region) +(defcustom context-menu-functions '(context-menu-undo + context-menu-region + context-menu-local + context-menu-minor) "List of functions that produce the contents of the context menu." :type 'hook + :options '(context-menu-undo + context-menu-region + context-menu-global + context-menu-local + context-menu-minor) :version "28.1") -(defvar context-menu-overriding-function nil - "Function that can override the list produced by `context-menu-functions'.") - (defcustom context-menu-filter-function nil "Function that can filter the list produced by `context-menu-functions'." :type 'function :version "28.1") (defun context-menu-map () + "Return composite menu map." (let ((menu (make-sparse-keymap "Context Menu"))) - (if (functionp context-menu-overriding-function) - (setq menu (funcall context-menu-overriding-function menu)) - (run-hook-wrapped 'context-menu-functions - (lambda (fun) - (setq menu (funcall fun menu)) - nil))) - (setq menu (cons (car menu) (nreverse (cdr menu)))) + (run-hook-wrapped 'context-menu-functions + (lambda (fun) + (setq menu (funcall fun menu)) + nil)) (when (functionp context-menu-filter-function) (setq menu (funcall context-menu-filter-function menu))) menu)) +(defun context-menu-global (menu) + "Global submenus." + (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) + (define-key-after menu [separator-global-1] menu-bar-separator) + (dolist (item (lookup-key global-map [menu-bar])) + (when (consp item) + (define-key-after menu (vector (car item)) + (if (consp (cdr item)) + (copy-sequence (cdr item)) + (cdr item))))) + (define-key-after menu [separator-global-2] menu-bar-separator) + menu) + +(defun context-menu-local (menu) + "Major mode submenus." + (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) + (define-key-after menu [separator-local-1] menu-bar-separator) + (dolist (item (local-key-binding [menu-bar])) + (when (consp item) + (define-key-after menu (vector (car item)) + (if (consp (cdr item)) + (copy-sequence (cdr item)) + (cdr item))))) + (define-key-after menu [separator-local-2] menu-bar-separator) + menu) + +(defun context-menu-minor (menu) + "Minor mode submenus." + (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) + (define-key-after menu [separator-minor-1] menu-bar-separator) + (dolist (item (minor-mode-key-binding [menu-bar])) + (when (and (consp item) (symbol-value (car item))) + (define-key-after menu (vector (cadr item)) + (if (consp (cddr item)) + (copy-sequence (cddr item)) + (cddr item))))) + (define-key-after menu [separator-minor-2] menu-bar-separator) + menu) + (defun context-menu-undo (menu) - (bindings--define-key menu [undo] + (define-key-after menu [separator-undo-1] menu-bar-separator) + (define-key-after menu [undo] '(menu-item "Undo" undo :visible (and (not buffer-read-only) (not (eq t buffer-undo-list)) @@ -314,20 +357,22 @@ not it is actually displayed." (listp pending-undo-list) (consp buffer-undo-list))) :help "Undo last edits")) - (bindings--define-key menu [undo-redo] + (define-key-after menu [undo-redo] '(menu-item "Redo" undo-redo :visible (and (not buffer-read-only) (undo--last-change-was-undo-p buffer-undo-list)) :help "Redo last undone edits")) + (define-key-after menu [separator-undo-2] menu-bar-separator) menu) (defun context-menu-region (menu) - (bindings--define-key menu [cut] + (define-key-after menu [separator-region-1] menu-bar-separator) + (define-key-after menu [cut] '(menu-item "Cut" kill-region :visible (and mark-active (not buffer-read-only)) :help "Cut (kill) text in region between mark and current position")) - (bindings--define-key menu [copy] + (define-key-after menu [copy] ;; ns-win.el said: Substitute a Copy function that works better ;; under X (for GNUstep). `(menu-item "Copy" ,(if (featurep 'ns) @@ -338,7 +383,7 @@ not it is actually displayed." :keys ,(if (featurep 'ns) "\\[ns-copy-including-secondary]" "\\[kill-ring-save]"))) - (bindings--define-key menu [paste] + (define-key-after menu [paste] `(menu-item "Paste" mouse-yank-primary :visible (funcall ',(lambda () @@ -349,25 +394,30 @@ not it is actually displayed." kill-ring)) (not buffer-read-only)))) :help "Paste (yank) text most recently cut/copied")) - (bindings--define-key menu (if (featurep 'ns) [select-paste] - [paste-from-menu]) + (define-key-after menu (if (featurep 'ns) [select-paste] + [paste-from-menu]) ;; ns-win.el said: Change text to be more consistent with ;; surrounding menu items `paste', etc." `(menu-item ,(if (featurep 'ns) "Select and Paste" "Paste from Kill Menu") yank-menu :visible (and (cdr yank-menu) (not buffer-read-only)) :help "Choose a string from the kill ring and paste it")) - (bindings--define-key menu [clear] + (define-key-after menu [clear] '(menu-item "Clear" delete-active-region :visible (and mark-active (not buffer-read-only)) :help "Delete the text in region between mark and current position")) - (bindings--define-key menu [mark-whole-buffer] + (define-key-after menu [mark-whole-buffer] '(menu-item "Select All" mark-whole-buffer :help "Mark the whole buffer for a subsequent cut/copy")) + (define-key-after menu [separator-region-2] menu-bar-separator) menu) +(defvar context-menu-entry + `(menu-item ,(purecopy "Context Menu") ignore + :filter (lambda (_) (context-menu-map)))) + (defvar context-menu--old-down-mouse-3 nil) (defvar context-menu--old-mouse-3 nil) @@ -382,9 +432,7 @@ activates the menu whose contents depends on its surrounding context." (setq context-menu--old-mouse-3 (global-key-binding [mouse-3])) (global-unset-key [mouse-3]) (setq context-menu--old-down-mouse-3 (global-key-binding [down-mouse-3])) - (global-set-key [down-mouse-3] - '(menu-item "Context Menu" ignore - :filter (lambda (_) (context-menu-map))))) + (global-set-key [down-mouse-3] context-menu-entry)) (t (if (not context-menu--old-down-mouse-3) (global-unset-key [down-mouse-3]) diff --git a/lisp/net/goto-addr.el b/lisp/net/goto-addr.el index 1e8a3cda15..2c43d0f753 100644 --- a/lisp/net/goto-addr.el +++ b/lisp/net/goto-addr.el @@ -126,10 +126,10 @@ will have no effect.") (defun goto-address-context-menu (menu) (when (mouse-posn-property (event-start last-input-event) 'goto-address) - (bindings--define-key menu [goto-address-at-click] - '(menu-item "Follow link" goto-address-at-click - :help "Follow a link where you click")) - (define-key menu [goto-address-separator] menu-bar-separator)) + (define-key menu [goto-address-separator] menu-bar-separator) + (define-key menu [goto-address-at-mouse] + '(menu-item "Follow Link" goto-address-at-mouse + :help "Follow a link where you click"))) menu) (defcustom goto-address-url-face 'link @@ -253,8 +253,8 @@ address. If no e-mail address found, return nil." (goto-char (match-beginning 0)))) (match-string-no-properties 0))) -(defun goto-address-at-click (click) - "Send to the e-mail address or load the URL at click." +(defun goto-address-at-mouse (click) + "Send to the e-mail address or load the URL at mouse click." (interactive "e") (goto-address-at-point click)) @@ -280,7 +280,7 @@ Also fontifies the buffer appropriately (see `goto-address-fontify-p' and (cond (goto-address-mode (jit-lock-register #'goto-address-fontify-region) - (add-hook 'context-menu-functions 'goto-address-context-menu -10 t)) + (add-hook 'context-menu-functions 'goto-address-context-menu 10 t)) (t (jit-lock-unregister #'goto-address-fontify-region) (save-restriction diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el index 19de7545bf..ad1b13f4bd 100644 --- a/lisp/progmodes/prog-mode.el +++ b/lisp/progmodes/prog-mode.el @@ -43,6 +43,22 @@ display-line-numbers-mode prettify-symbols-mode)) +(defun prog-context-menu (menu) + (when (featurep 'xref) + (define-key-after menu [prog-separator-1] menu-bar-separator) + (define-key-after menu [xref-find-def] + '(menu-item "Find Definition" xref-find-definitions-at-mouse + :visible (save-excursion + (mouse-set-point last-input-event) + (xref-backend-identifier-at-point (xref-find-backend))) + :help "Find definition of function or variable")) + (define-key-after menu [xref-pop] + '(menu-item "Back Definition" xref-pop-marker-stack + :visible (not (xref-marker-stack-empty-p)) + :help "Back to the position of the last search")) + (define-key-after menu [prog-separator-2] menu-bar-separator)) + menu) + (defvar prog-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-\M-q] 'prog-indent-sexp) @@ -249,6 +265,7 @@ support it." "Major mode for editing programming language source code." (setq-local require-final-newline mode-require-final-newline) (setq-local parse-sexp-ignore-comments t) + (add-hook 'context-menu-functions 'prog-context-menu 10 t) ;; Any programming language is always written left to right. (setq bidi-paragraph-direction 'left-to-right)) commit 292e6261be8d4b7b08f87e70eb8490e31b3e9a4f Author: Juri Linkov Date: Tue Jul 20 23:48:43 2021 +0300 Add new mode context-menu-mode and use it in info.el and goto-addr.el * lisp/mouse.el (context-menu-functions): New defcustom. (context-menu-overriding-function): New function. (context-menu-filter-function): New defcustom. (context-menu-map): New function. (context-menu-undo, context-menu-region): New menu functions. (context-menu-mode): New mode. * lisp/info.el (Info-context-menu): New function. (Info-mode): Add Info-context-menu to context-menu-functions. * lisp/net/goto-addr.el (goto-address-context-menu): New function. (goto-address-at-click): New command. (goto-address-mode): Add goto-address-context-menu to context-menu-functions. diff --git a/lisp/info.el b/lisp/info.el index b65728ba41..226ec76eb6 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -4146,6 +4146,37 @@ If FORK is non-nil, it is passed to `Info-goto-node'." "---" ["Exit" quit-window :help "Stop reading Info"])) +(defun Info-context-menu (menu) + (when (mouse-posn-property (event-start last-input-event) 'mouse-face) + (bindings--define-key menu [Info-mouse-follow-nearest-node] + '(menu-item "Follow link" Info-mouse-follow-nearest-node + :help "Follow a link where you click"))) + + (bindings--define-key menu [Info-history-back] + '(menu-item "Back in history" Info-history-back :visible Info-history + :help "Go back in history to the last node you were at")) + (bindings--define-key menu [Info-history-forward] + '(menu-item "Forward in history" Info-history-forward :visible Info-history-forward + :help "Go forward in history")) + + (bindings--define-key menu [Info-up] + '(menu-item "Up" Info-up :visible (Info-check-pointer "up") + :help "Go up in the Info tree")) + (bindings--define-key menu [Info-next] + '(menu-item "Next" Info-next :visible (Info-check-pointer "next") + :help "Go to the next node")) + (bindings--define-key menu [Info-prev] + '(menu-item "Previous" Info-prev :visible (Info-check-pointer "prev[ious]*") + :help "Go to the previous node")) + (bindings--define-key menu [Info-backward-node] + '(menu-item "Backward" Info-backward-node + :help "Go backward one node, considering all as a sequence")) + (bindings--define-key menu [Info-forward-node] + '(menu-item "Forward" Info-forward-node + :help "Go forward one node, considering all as a sequence")) + + (define-key menu [Info-separator] menu-bar-separator) + menu) (defvar info-tool-bar-map (let ((map (make-sparse-keymap))) @@ -4446,6 +4477,7 @@ Advanced commands: (add-hook 'clone-buffer-hook 'Info-clone-buffer nil t) (add-hook 'change-major-mode-hook 'font-lock-defontify nil t) (add-hook 'isearch-mode-hook 'Info-isearch-start nil t) + (add-hook 'context-menu-functions 'Info-context-menu nil t) (when Info-standalone (add-hook 'quit-window-hook 'save-buffers-kill-emacs nil t)) (setq-local isearch-search-fun-function #'Info-isearch-search) diff --git a/lisp/mouse.el b/lisp/mouse.el index 89e5d7c48a..580fe8eb35 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -276,6 +276,124 @@ not it is actually displayed." local-menu minor-mode-menus))) + +;; Context menus. + +(defcustom context-menu-functions '(context-menu-undo context-menu-region) + "List of functions that produce the contents of the context menu." + :type 'hook + :version "28.1") + +(defvar context-menu-overriding-function nil + "Function that can override the list produced by `context-menu-functions'.") + +(defcustom context-menu-filter-function nil + "Function that can filter the list produced by `context-menu-functions'." + :type 'function + :version "28.1") + +(defun context-menu-map () + (let ((menu (make-sparse-keymap "Context Menu"))) + (if (functionp context-menu-overriding-function) + (setq menu (funcall context-menu-overriding-function menu)) + (run-hook-wrapped 'context-menu-functions + (lambda (fun) + (setq menu (funcall fun menu)) + nil))) + (setq menu (cons (car menu) (nreverse (cdr menu)))) + (when (functionp context-menu-filter-function) + (setq menu (funcall context-menu-filter-function menu))) + menu)) + +(defun context-menu-undo (menu) + (bindings--define-key menu [undo] + '(menu-item "Undo" undo + :visible (and (not buffer-read-only) + (not (eq t buffer-undo-list)) + (if (eq last-command 'undo) + (listp pending-undo-list) + (consp buffer-undo-list))) + :help "Undo last edits")) + (bindings--define-key menu [undo-redo] + '(menu-item "Redo" undo-redo + :visible (and (not buffer-read-only) + (undo--last-change-was-undo-p buffer-undo-list)) + :help "Redo last undone edits")) + menu) + +(defun context-menu-region (menu) + (bindings--define-key menu [cut] + '(menu-item "Cut" kill-region + :visible (and mark-active (not buffer-read-only)) + :help + "Cut (kill) text in region between mark and current position")) + (bindings--define-key menu [copy] + ;; ns-win.el said: Substitute a Copy function that works better + ;; under X (for GNUstep). + `(menu-item "Copy" ,(if (featurep 'ns) + 'ns-copy-including-secondary + 'kill-ring-save) + :visible mark-active + :help "Copy text in region between mark and current position" + :keys ,(if (featurep 'ns) + "\\[ns-copy-including-secondary]" + "\\[kill-ring-save]"))) + (bindings--define-key menu [paste] + `(menu-item "Paste" mouse-yank-primary + :visible (funcall + ',(lambda () + (and (or + (gui-backend-selection-exists-p 'CLIPBOARD) + (if (featurep 'ns) ; like paste-from-menu + (cdr yank-menu) + kill-ring)) + (not buffer-read-only)))) + :help "Paste (yank) text most recently cut/copied")) + (bindings--define-key menu (if (featurep 'ns) [select-paste] + [paste-from-menu]) + ;; ns-win.el said: Change text to be more consistent with + ;; surrounding menu items `paste', etc." + `(menu-item ,(if (featurep 'ns) "Select and Paste" "Paste from Kill Menu") + yank-menu + :visible (and (cdr yank-menu) (not buffer-read-only)) + :help "Choose a string from the kill ring and paste it")) + (bindings--define-key menu [clear] + '(menu-item "Clear" delete-active-region + :visible (and mark-active + (not buffer-read-only)) + :help + "Delete the text in region between mark and current position")) + (bindings--define-key menu [mark-whole-buffer] + '(menu-item "Select All" mark-whole-buffer + :help "Mark the whole buffer for a subsequent cut/copy")) + menu) + +(defvar context-menu--old-down-mouse-3 nil) +(defvar context-menu--old-mouse-3 nil) + +(define-minor-mode context-menu-mode + "Toggle Context Menu mode. + +When Context Menu mode is enabled, clicking the mouse button down-mouse-3 +activates the menu whose contents depends on its surrounding context." + :global t :group 'mouse + (cond + (context-menu-mode + (setq context-menu--old-mouse-3 (global-key-binding [mouse-3])) + (global-unset-key [mouse-3]) + (setq context-menu--old-down-mouse-3 (global-key-binding [down-mouse-3])) + (global-set-key [down-mouse-3] + '(menu-item "Context Menu" ignore + :filter (lambda (_) (context-menu-map))))) + (t + (if (not context-menu--old-down-mouse-3) + (global-unset-key [down-mouse-3]) + (global-set-key [down-mouse-3] context-menu--old-down-mouse-3) + (setq context-menu--old-down-mouse-3 nil)) + (when context-menu--old-mouse-3 + (global-set-key [mouse-3] context-menu--old-mouse-3) + (setq context-menu--old-mouse-3 nil))))) + ;; Commands that operate on windows. diff --git a/lisp/net/goto-addr.el b/lisp/net/goto-addr.el index 8992ef736a..1e8a3cda15 100644 --- a/lisp/net/goto-addr.el +++ b/lisp/net/goto-addr.el @@ -124,6 +124,14 @@ will have no effect.") m) "Keymap to hold goto-addr's mouse key defs under highlighted URLs.") +(defun goto-address-context-menu (menu) + (when (mouse-posn-property (event-start last-input-event) 'goto-address) + (bindings--define-key menu [goto-address-at-click] + '(menu-item "Follow link" goto-address-at-click + :help "Follow a link where you click")) + (define-key menu [goto-address-separator] menu-bar-separator)) + menu) + (defcustom goto-address-url-face 'link "Face to use for URLs." :type 'face) @@ -245,6 +253,11 @@ address. If no e-mail address found, return nil." (goto-char (match-beginning 0)))) (match-string-no-properties 0))) +(defun goto-address-at-click (click) + "Send to the e-mail address or load the URL at click." + (interactive "e") + (goto-address-at-point click)) + ;;;###autoload (defun goto-address () "Sets up goto-address functionality in the current buffer. @@ -264,12 +277,16 @@ Also fontifies the buffer appropriately (see `goto-address-fontify-p' and (define-minor-mode goto-address-mode "Minor mode to buttonize URLs and e-mail addresses in the current buffer." :lighter "" - (if goto-address-mode - (jit-lock-register #'goto-address-fontify-region) + (cond + (goto-address-mode + (jit-lock-register #'goto-address-fontify-region) + (add-hook 'context-menu-functions 'goto-address-context-menu -10 t)) + (t (jit-lock-unregister #'goto-address-fontify-region) (save-restriction (widen) - (goto-address-unfontify (point-min) (point-max))))) + (goto-address-unfontify (point-min) (point-max))) + (remove-hook 'context-menu-functions 'goto-address-context-menu t)))) (defun goto-addr-mode--turn-on () (when (not goto-address-mode)