commit bf93e94060d2c2b99bc9a16fa69d811bb8a454c1 (HEAD, refs/remotes/origin/master) Author: Daanturo Date: Fri Nov 25 00:06:37 2022 +0700 Show package name in package-vc--unpack prompt * lisp/emacs-lisp/package-vc.el (package-vc--unpack): Display the package name when asking whether to overwrite its previous checkout. (Bug#59548) diff --git a/lisp/emacs-lisp/package-vc.el b/lisp/emacs-lisp/package-vc.el index bf1ea2bdf4..deb0debab4 100644 --- a/lisp/emacs-lisp/package-vc.el +++ b/lisp/emacs-lisp/package-vc.el @@ -523,7 +523,7 @@ package-vc--unpack (pkg-dir (expand-file-name dirname package-user-dir))) (setf (package-desc-dir pkg-desc) pkg-dir) (when (file-exists-p pkg-dir) - (if (yes-or-no-p "Overwrite previous checkout?") + (if (yes-or-no-p (format "Overwrite previous checkout for package `%s'?" name)) (package--delete-directory pkg-dir) (error "There already exists a checkout for %s" name))) (package-vc--clone pkg-desc pkg-spec pkg-dir rev) commit 4ee5618c6b1d53bc64edf6cf37adb8a0ffdacb0c Author: Stefan Kangas Date: Fri Nov 25 08:57:57 2022 +0100 ; Remove unused variable treesit-imenu-function * lisp/treesit.el (treesit-imenu-function): Remove unused variable. (Bug#59475) diff --git a/lisp/treesit.el b/lisp/treesit.el index b8372d4f51..8a8f6f02c7 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1609,14 +1609,6 @@ treesit-end-of-defun (top (treesit--defun-maybe-top-level node))) (goto-char (treesit-node-end top)))) -;;; Imenu - -(defvar-local treesit-imenu-function nil - "Tree-sitter version of `imenu-create-index-function'. - -Set this variable to a function and `treesit-mode' will bind it -to `imenu-create-index-function'.") - ;;; Activating tree-sitter (defun treesit-ready-p (language &optional quiet) commit 28c444f72a9843ce335032db1fa0f484dfeb4833 Author: Jim Porter Date: Mon Nov 21 11:47:08 2022 -0800 Don't explicitly delete client frames when killing Emacs anyway This eliminates a useless error prompt when killing Emacs from a client frame when there are no other frames (bug#58877). * lisp/server.el (server-running-external): New error. (server--file-name): New function... (server-eval-at): ... use it. (server-start): Factor out server stopping code into... (server-stop): ... here. (server-force-stop): Use 'server-stop', and tell it not to delete frames. * test/lisp/server-tests.el (server-tests/server-force-stop/keeps-frames): New test. diff --git a/lisp/server.el b/lisp/server.el index 2973b783e6..f7aaf6a6c6 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -287,6 +287,8 @@ server-socket-dir "The directory in which to place the server socket. If local sockets are not supported, this is nil.") +(define-error 'server-running-external "External server running") + (defun server-clients-with (property value) "Return a list of clients with PROPERTY set to VALUE." (let (result) @@ -610,6 +612,54 @@ server-get-auth-key (error "The key `%s' is invalid" server-auth-key)) (server-generate-key))) +(defsubst server--file-name () + "Return the file name to use for the server socket." + (let ((server-dir (if server-use-tcp server-auth-dir server-socket-dir))) + (expand-file-name server-name server-dir))) + +(defun server-stop (&optional noframe) + "If this Emacs process has a server communication subprocess, stop it. +If the server is running in some other Emacs process (see +`server-running-p'), signal a `server-running-external' error. + +If NOFRAME is non-nil, don't delete any existing frames +associated with a client process. This is useful, for example, +when killing Emacs, in which case the frames will get deleted +anyway." + (let ((server-file (server--file-name))) + (when server-process + ;; Kill it dead! + (ignore-errors (delete-process server-process)) + (unless noframe + (server-log (message "Server stopped"))) + (setq server-process nil + server-mode nil + global-minor-modes (delq 'server-mode global-minor-modes))) + (unwind-protect + ;; Delete the socket files made by previous server + ;; invocations. + (if (not (eq t (server-running-p server-name))) + ;; Remove any leftover socket or authentication file. + (ignore-errors + (let (delete-by-moving-to-trash) + (delete-file server-file) + ;; Also delete the directory that the server file was + ;; created in -- but only in /tmp (see bug#44644). + ;; There may be other servers running, too, so this may + ;; fail. + (when (equal (file-name-directory + (directory-file-name + (file-name-directory server-file))) + "/tmp/") + (ignore-errors + (delete-directory (file-name-directory server-file)))))) + (signal 'server-running-external + (list (format "There is an existing Emacs server, named %S" + server-name)))) + ;; If this Emacs already had a server, clear out associated status. + (while server-clients + (server-delete-client (car server-clients) noframe))))) + ;;;###autoload (defun server-start (&optional leave-dead inhibit-prompt) "Allow this Emacs process to be a server for client processes. @@ -643,55 +693,30 @@ server-start (inhibit-prompt t) (t (yes-or-no-p "The current server still has clients; delete them? ")))) - (let* ((server-dir (if server-use-tcp server-auth-dir server-socket-dir)) - (server-file (expand-file-name server-name server-dir))) - (when server-process - ;; kill it dead! - (ignore-errors (delete-process server-process))) - ;; Check to see if an uninitialized external socket has been - ;; passed in, if that is the case, skip checking - ;; `server-running-p' as this will return the wrong result. - (if (and internal--daemon-sockname - (not server--external-socket-initialized)) - (setq server--external-socket-initialized t) - ;; Delete the socket files made by previous server invocations. - (if (not (eq t (server-running-p server-name))) - ;; Remove any leftover socket or authentication file. - (ignore-errors - (let (delete-by-moving-to-trash) - (delete-file server-file) - ;; Also delete the directory that the server file was - ;; created in -- but only in /tmp (see bug#44644). - ;; There may be other servers running, too, so this may - ;; fail. - (when (equal (file-name-directory - (directory-file-name - (file-name-directory server-file))) - "/tmp/") - (ignore-errors - (delete-directory (file-name-directory server-file)))))) - (display-warning - 'server - (concat "Unable to start the Emacs server.\n" - (format "There is an existing Emacs server, named %S.\n" - server-name) - (substitute-command-keys - "To start the server in this Emacs process, stop the existing -server or call `\\[server-force-delete]' to forcibly disconnect it.")) - :warning) - (setq leave-dead t))) - ;; If this Emacs already had a server, clear out associated status. - (while server-clients - (server-delete-client (car server-clients))) + ;; If a server is already running, try to stop it. + (condition-case err + ;; Check to see if an uninitialized external socket has been + ;; passed in. If that is the case, don't try to stop the + ;; server. (`server-stop' checks `server-running-p', which + ;; would return the wrong result). + (if (and internal--daemon-sockname + (not server--external-socket-initialized)) + (setq server--external-socket-initialized t) + (server-stop)) + (server-running-external + (display-warning + 'server + (concat "Unable to start the Emacs server.\n" + (cadr err) + (substitute-command-keys + "\nTo start the server in this Emacs process, stop the existingserver or call `\\[server-force-delete]' to forcibly disconnect it.")) + :warning) + (setq leave-dead t))) ;; Now any previous server is properly stopped. - (if leave-dead - (progn - (unless (eq t leave-dead) (server-log (message "Server stopped"))) - (setq server-mode nil - global-minor-modes (delq 'server-mode global-minor-modes) - server-process nil)) + (unless leave-dead + (let ((server-file (server--file-name))) ;; Make sure there is a safe directory in which to place the socket. - (server-ensure-safe-dir server-dir) + (server-ensure-safe-dir (file-name-directory server-file)) (when server-process (server-log (message "Restarting server"))) (with-file-modes ?\700 @@ -748,7 +773,7 @@ server-start (defun server-force-stop () "Kill all connections to the current server. This function is meant to be called from `kill-emacs-hook'." - (server-start t t)) + (ignore-errors (server-stop 'noframe))) ;;;###autoload (defun server-force-delete (&optional name) @@ -1869,11 +1894,10 @@ server-eval-at cannot contact the specified server. For example: (server-eval-at \"server\" \\='(emacs-pid)) returns the process ID of the Emacs instance running \"server\"." - (let* ((server-dir (if server-use-tcp server-auth-dir server-socket-dir)) - (server-file (expand-file-name server server-dir)) - (coding-system-for-read 'binary) - (coding-system-for-write 'binary) - address port secret process) + (let ((server-file (server--file-name)) + (coding-system-for-read 'binary) + (coding-system-for-write 'binary) + address port secret process) (unless (file-exists-p server-file) (error "No such server: %s" server)) (with-temp-buffer diff --git a/test/lisp/server-tests.el b/test/lisp/server-tests.el index 48ef110943..370cf86148 100644 --- a/test/lisp/server-tests.el +++ b/test/lisp/server-tests.el @@ -131,4 +131,39 @@ server-tests/emacsclient/eval "--eval" (format "(setq server-tests/variable %d)" value)) (server-tests/wait-until (eq server-tests/variable value))))) +(ert-deftest server-tests/server-force-stop/keeps-frames () + "Ensure that `server-force-stop' doesn't delete frames. See bug#58877. +Note: since that bug is about a behavior when killing Emacs, this +test is somewhat indirect. (Killing the current Emacs instance +would make it hard to check test results!) Instead, it only +tests that `server-force-stop' doesn't delete frames (and even +then, requires a few tricks to run as a regression test). So +long as this works, the problem in bug#58877 shouldn't occur." + (let (terminal) + (unwind-protect + (server-tests/with-server + (let ((emacsclient (server-tests/start-emacsclient "-c"))) + (server-tests/wait-until (length= (frame-list) 2)) + (should (eq (process-status emacsclient) 'run)) + + ;; Don't delete the terminal for the client; that would + ;; kill its frame immediately too. (This is only an issue + ;; when running these tests via the command line; + ;; normally, in an interactive session, we don't need to + ;; worry about this. But since we want to check that + ;; `server-force-stop' doesn't delete frames under normal + ;; circumstances, we need to bypass terminal deletion + ;; here.) + (setq terminal (process-get (car server-clients) 'terminal)) + (process-put (car server-clients) 'no-delete-terminal t) + + (server-force-stop)) + ;; Ensure we didn't delete the frame. + (should (length= (frame-list) 2))) + ;; Clean up after ourselves and delete the terminal. + (when (and terminal + (eq (terminal-live-p terminal) t) + (not (eq system-type 'windows-nt))) + (delete-terminal terminal))))) + ;;; server-tests.el ends here commit 339893f2e3b5cb7263ba5204e083d5605df72446 Author: Jim Porter Date: Sat Nov 19 22:26:45 2022 -0800 ; Add more tests for the Emacs server * test/lisp/server-tests.el (server-tests/emacs-client) (server-tests/max-wait-time): New constants. (server-tests/start-emacsclient): New function. (server-tests/with-server, server-tests/wait-until): New macros. (server-tests/variable): New variable. (server-test/server-start-sets-minor-mode): Rename to... (server-tests/server-start/sets-minor-mode): ... this. (server-tests/server-start/stop-prompt-with-client) (server-tests/server-start/no-stop-prompt-without-client) (server-tests/emacsclient/server-edit) (server-tests/emacsclient/create-frame, server-test/emacsclient/eval): New tests. * test/lib-src/emacsclient-tests.el: Mention the above file. diff --git a/test/lib-src/emacsclient-tests.el b/test/lib-src/emacsclient-tests.el index 1302fbe30c..0fa3c6facf 100644 --- a/test/lib-src/emacsclient-tests.el +++ b/test/lib-src/emacsclient-tests.el @@ -19,7 +19,9 @@ ;;; Commentary: -;; +;; Tests for the emacsclient executable. For tests involving the +;; interaction between emacsclient and an Emacs server, see +;; test/lisp/server-tests.el. ;;; Code: diff --git a/test/lisp/server-tests.el b/test/lisp/server-tests.el index 351b8ef8d1..48ef110943 100644 --- a/test/lisp/server-tests.el +++ b/test/lisp/server-tests.el @@ -22,20 +22,113 @@ (require 'ert) (require 'server) +(defconst server-tests/emacsclient + (if installation-directory + (expand-file-name "lib-src/emacsclient" installation-directory) + "emacsclient") + "The emacsclient binary to test.") + +(defun server-tests/start-emacsclient (&rest args) + "Run emacsclient, passing ARGS as arguments to it." + (let ((socket-name (process-get server-process :server-file))) + (make-process + :name server-tests/emacsclient + :command (append (list server-tests/emacsclient + "--socket-name" socket-name) + args)))) + +(defmacro server-tests/with-server (&rest body) + "Start the Emacs server, evaluate BODY, and then stop the server." + (declare (indent 0)) + `(progn + (server-start) + (unwind-protect + (progn (should (processp server-process)) + ,@body) + (let ((inhibit-message t)) + (server-start t t)) + (should (null server-process)) + (should (null server-clients))))) + +(defconst server-tests/max-wait-time 5 + "The maximum time to wait in `server-tests/wait-until', in seconds.") + +(defmacro server-tests/wait-until (form) + "Wait until FORM is non-nil, timing out and failing if it takes too long." + `(let ((start (current-time))) + (while (not ,form) + (when (> (float-time (time-since start)) + server-tests/max-wait-time) + (ert-fail (format "timed out waiting for %S to be non-nil" ',form))) + (sit-for 0.1)))) + +(defvar server-tests/variable nil) + ;;; Tests: -(ert-deftest server-test/server-start-sets-minor-mode () +(ert-deftest server-tests/server-start/sets-minor-mode () "Ensure that calling `server-start' also sets `server-mode' properly." - (server-start) - (unwind-protect - (progn - ;; Make sure starting the server activates the minor mode. - (should (eq server-mode t)) - (should (memq 'server-mode global-minor-modes))) - ;; Always stop the server, even if the above checks fail. - (server-start t)) + (server-tests/with-server + ;; Make sure starting the server activates the minor mode. + (should (eq server-mode t)) + (should (memq 'server-mode global-minor-modes))) ;; Make sure stopping the server deactivates the minor mode. (should (eq server-mode nil)) (should-not (memq 'server-mode global-minor-modes))) +(ert-deftest server-tests/server-start/stop-prompt-with-client () + "Ensure that stopping the server prompts when there are clients." + (server-tests/with-server + (let ((yes-or-no-p-called nil) + (emacsclient (server-tests/start-emacsclient "-c"))) + (server-tests/wait-until (length= (frame-list) 2)) + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (_prompt) + (setq yes-or-no-p-called t)))) + (server-start t) + (should yes-or-no-p-called)) + (server-tests/wait-until (eq (process-status emacsclient) 'exit))))) + +(ert-deftest server-tests/server-start/no-stop-prompt-without-client () + "Ensure that stopping the server doesn't prompt when there are no clients." + (server-tests/with-server + (let ((yes-or-no-p-called nil)) + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (_prompt) + (setq yes-or-no-p-called t)))) + (let ((inhibit-message t)) + (server-start t)) + (should-not yes-or-no-p-called))))) + +(ert-deftest server-tests/emacsclient/server-edit () + "Test that calling `server-edit' from a client buffer exits the client." + (server-tests/with-server + (let ((emacsclient (server-tests/start-emacsclient "file.txt"))) + (server-tests/wait-until (get-buffer "file.txt")) + (should (eq (process-status emacsclient) 'run)) + (should (length= server-clients 1)) + (with-current-buffer "file.txt" + (server-edit)) + (server-tests/wait-until (eq (process-status emacsclient) 'exit))))) + +(ert-deftest server-tests/emacsclient/create-frame () + "Test that \"emacsclient -c\" creates a frame." + (server-tests/with-server + (let ((emacsclient (server-tests/start-emacsclient "-c"))) + (server-tests/wait-until (length= (frame-list) 2)) + (should (eq (process-status emacsclient) 'run)) + (should (length= server-clients 1)) + (should (eq (frame-parameter (car (frame-list)) 'client) + (car server-clients))))) + ;; The client frame should go away after the server stops. + (should (length= (frame-list) 1))) + +(ert-deftest server-tests/emacsclient/eval () + "Test that \"emacsclient --eval\" works correctly." + (server-tests/with-server + (let ((value (random))) + (server-tests/start-emacsclient + "--eval" (format "(setq server-tests/variable %d)" value)) + (server-tests/wait-until (eq server-tests/variable value))))) + ;;; server-tests.el ends here commit 1c1a82bbedf2a70d97e266d403ba6c45cc139c9b Author: Jim Porter Date: Thu Nov 24 17:22:53 2022 -0800 ; Fix up some NEWS entries for Eshell * etc/NEWS (Incompatible changes in Emacs 29.1): Move an entry from here... (Changes in Specialized Modes and Packages in Emacs 29.1): ... to here. Additionally, improve wording of another entry to be more precise. diff --git a/etc/NEWS b/etc/NEWS index 13f2138124..7b64752f46 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -371,11 +371,6 @@ node in the Eshell manual for more details. *** Eshell pipelines now only pipe stdout by default. To pipe both stdout and stderr, use the '|&' operator instead of '|'. -*** New eshell built-in command 'doas'. -The privilege-escalation program 'doas' has been added to the existing -'su' and 'sudo' commands from the 'eshell-tramp' module. The external -command may still be accessed by using '*doas'. - --- ** The 'delete-forward-char' command now deletes by grapheme clusters. This command is by default bound to the function key @@ -2729,6 +2724,12 @@ example, you can now redirect both stdout and stderr via '&>' or duplicate one output handle to another via 'NEW-FD>&OLD-FD'. For more information, see the "(eshell) Redirection" node in the Eshell manual. ++++ +*** New eshell built-in command 'doas'. +The privilege-escalation program 'doas' has been added to the existing +'su' and 'sudo' commands from the 'eshell-tramp' module. The external +command may still be accessed by using '*doas'. + +++ *** Double-quoting an Eshell expansion now treats the result as a single string. If an Eshell expansion like '$FOO' is surrounded by double quotes, the @@ -2765,10 +2766,11 @@ symlinks in the latter case). +++ *** Lisp forms in Eshell now treat a 'nil' result as a failed exit status. -When executing a command that looks like '(lisp form)', Eshell will -set the exit status (available in the '$?' variable) to 2. This -allows commands like that to be used as conditionals. To change this -behavior, customize the new 'eshell-lisp-form-nil-is-failure' option. +When executing a command that looks like '(lisp form)' and returns +'nil', Eshell will set the exit status (available in the '$?' +variable) to 2. This allows commands like that to be used in +conditionals. To change this behavior, customize the new +'eshell-lisp-form-nil-is-failure' option. ** Shell commit 75101583c3f645aa845fb78f88b02eae92bfdc44 Author: Gabriel do Nascimento Ribeiro Date: Thu Nov 10 06:11:03 2022 -0300 Make ibuffer directory filter buffer aware * lisp/ibuf-ext.el (define-ibuffer-filter): Make ibuffer directory filter buffer aware for buffer not visiting files. (Bug#59165) diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el index 6b5cccec51..adffef4325 100644 --- a/lisp/ibuf-ext.el +++ b/lisp/ibuf-ext.el @@ -1336,10 +1336,12 @@ directory matches against the value of `default-directory' in that buffer." ( :description "directory name" :reader (read-from-minibuffer "Filter by directory name (regex): ")) - (if-let ((it (with-current-buffer buf (ibuffer-buffer-file-name)))) - (when-let ((dirname (file-name-directory it))) - (string-match qualifier dirname)) - (when default-directory (string-match qualifier default-directory)))) + (with-current-buffer buf + (if-let* ((filename (ibuffer-buffer-file-name)) + (dirname (file-name-directory filename))) + (string-match qualifier dirname) + (when default-directory + (string-match qualifier default-directory))))) ;;;###autoload (autoload 'ibuffer-filter-by-size-gt "ibuf-ext") (define-ibuffer-filter size-gt commit 65f35b7f6f4115291affa754855ad62a697524af Author: Ackerley Tng Date: Fri Nov 25 02:58:33 2022 +0200 Add support for window-local xref history * lisp/progmodes/xref.el (xref-history-storage): New user option (bug#59381). (xref--make-xref-history): New function. (xref--history): Use it. (xref-global-history, xref-window-local-history): New function. (xref--get-history): New function. (xref--push-backward, xref--push-forward) (xref-push-marker-stack, xref-go-back, xref-go-forward) (xref-clear-marker-stack, xref-marker-stack-empty-p) (xref-forward-history-empty-p): Use it. diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index e220367a21..071636dd8d 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -1,7 +1,7 @@ ;;; xref.el --- Cross-referencing commands -*-lexical-binding:t-*- ;; Copyright (C) 2014-2022 Free Software Foundation, Inc. -;; Version: 1.5.1 +;; Version: 1.6.0 ;; Package-Requires: ((emacs "26.1")) ;; This is a GNU ELPA :core package. Avoid functionality that is not @@ -429,32 +429,80 @@ xref-auto-jump-to-first-xref :version "28.1" :package-version '(xref . "1.2.0")) +(defcustom xref-history-storage #'xref-global-history + "Function that returns xref history. + +The following functions are predefined: + +- `xref-global-history' + Return a single, global history used across the entire Emacs + instance. +- `xref-window-local-history' + Return different xref histories, one per window. Allows you + to navigate code independently in different windows. A new + xref history is created for every new window." + :type '(radio + (function-item :tag "Per-window" xref-window-local-history) + (function-item :tag "Global history for Emacs instance" xref-global-history) + (function :tag "Other")) + :version "29.1" + :package-version '(xref . "1.6.0")) + (make-obsolete-variable 'xref--marker-ring 'xref--history "29.1") (defun xref-set-marker-ring-length (_var _val) (declare (obsolete nil "29.1")) nil) -(defvar xref--history (cons nil nil) +(defun xref--make-xref-history () + "Return a new xref history." + (cons nil nil)) + +(defvar xref--history (xref--make-xref-history) "(BACKWARD-STACK . FORWARD-STACK) of markers to visited Xref locations.") +(defun xref-global-history (&optional new-value) + "Return the xref history global to this Emacs instance. + +Override existing value with NEW-VALUE if NEW-VALUE is set." + (if new-value + (setq xref--history new-value) + xref--history)) + +(defun xref-window-local-history (&optional new-value) + "Return window-local xref history. + +Override existing value with NEW-VALUE if NEW-VALUE is set." + (let ((w (selected-window))) + (if new-value + (set-window-parameter w 'xref--history new-value) + (or (window-parameter w 'xref--history) + (set-window-parameter w 'xref--history (xref--make-xref-history)))))) + +(defun xref--get-history () + "Return xref history using xref-history-storage." + (funcall xref-history-storage)) + (defun xref--push-backward (m) "Push marker M onto the backward history stack." - (unless (equal m (caar xref--history)) - (push m (car xref--history)))) + (let ((history (xref--get-history))) + (unless (equal m (caar history)) + (push m (car history))))) (defun xref--push-forward (m) "Push marker M onto the forward history stack." - (unless (equal m (cadr xref--history)) - (push m (cdr xref--history)))) + (let ((history (xref--get-history))) + (unless (equal m (cadr history)) + (push m (cdr history))))) (defun xref-push-marker-stack (&optional m) "Add point M (defaults to `point-marker') to the marker stack. The future stack is erased." (xref--push-backward (or m (point-marker))) - (dolist (mk (cdr xref--history)) - (set-marker mk nil nil)) - (setcdr xref--history nil)) + (let ((history (xref--get-history))) + (dolist (mk (cdr history)) + (set-marker mk nil nil)) + (setcdr history nil))) ;;;###autoload (define-obsolete-function-alias 'xref-pop-marker-stack #'xref-go-back "29.1") @@ -464,29 +512,31 @@ xref-go-back "Go back to the previous position in xref history. To undo, use \\[xref-go-forward]." (interactive) - (if (null (car xref--history)) - (user-error "At start of xref history") - (let ((marker (pop (car xref--history)))) - (xref--push-forward (point-marker)) - (switch-to-buffer (or (marker-buffer marker) - (user-error "The marked buffer has been deleted"))) - (goto-char (marker-position marker)) - (set-marker marker nil nil) - (run-hooks 'xref-after-return-hook)))) + (let ((history (xref--get-history))) + (if (null (car history)) + (user-error "At start of xref history") + (let ((marker (pop (car history)))) + (xref--push-forward (point-marker)) + (switch-to-buffer (or (marker-buffer marker) + (user-error "The marked buffer has been deleted"))) + (goto-char (marker-position marker)) + (set-marker marker nil nil) + (run-hooks 'xref-after-return-hook))))) ;;;###autoload (defun xref-go-forward () "Got to the point where a previous \\[xref-go-back] was invoked." (interactive) - (if (null (cdr xref--history)) - (user-error "At end of xref history") - (let ((marker (pop (cdr xref--history)))) - (xref--push-backward (point-marker)) - (switch-to-buffer (or (marker-buffer marker) - (user-error "The marked buffer has been deleted"))) - (goto-char (marker-position marker)) - (set-marker marker nil nil) - (run-hooks 'xref-after-return-hook)))) + (let ((history (xref--get-history))) + (if (null (cdr history)) + (user-error "At end of xref history") + (let ((marker (pop (cdr history)))) + (xref--push-backward (point-marker)) + (switch-to-buffer (or (marker-buffer marker) + (user-error "The marked buffer has been deleted"))) + (goto-char (marker-position marker)) + (set-marker marker nil nil) + (run-hooks 'xref-after-return-hook))))) (define-obsolete-variable-alias 'xref--current-item @@ -512,22 +562,23 @@ xref-pulse-momentarily ;; etags.el needs this (defun xref-clear-marker-stack () "Discard all markers from the xref history." - (dolist (l (list (car xref--history) (cdr xref--history))) - (dolist (m l) - (set-marker m nil nil))) - (setq xref--history (cons nil nil)) + (let ((history (xref--get-history))) + (dolist (l (list (car history) (cdr history))) + (dolist (m l) + (set-marker m nil nil))) + (setq history (cons nil nil))) nil) ;;;###autoload (defun xref-marker-stack-empty-p () "Whether the xref back-history is empty." - (null (car xref--history))) + (null (car (xref--get-history)))) ;; FIXME: rename this to `xref-back-history-empty-p'. ;;;###autoload (defun xref-forward-history-empty-p () "Whether the xref forward-history is empty." - (null (cdr xref--history))) + (null (cdr (xref--get-history)))) (defun xref--goto-char (pos) commit e769a8eb4402c58adc355f0b15369e423893aa68 Author: Stefan Kangas Date: Fri Nov 25 01:38:03 2022 +0100 Bind Buffer-menu-view-other-window to "O" * lisp/buff-menu.el (Buffer-menu-mode-map): Bind 'Buffer-menu-view-other-window' to "O". (Buffer-menu-mode): Clean up docstring. (Bug#59280) diff --git a/etc/NEWS b/etc/NEWS index 8cb54ca740..13f2138124 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2906,6 +2906,11 @@ It copies the current line into the kill ring. The new face 'abbrev-table-name' is used to display the abbrev table name. +--- +*** New key binding "O" in `M-x list-buffer'. +This key is now bound to 'Buffer-menu-view-other-window', which will +view this line's buffer in View mode in another window. + * New Modes and Packages in Emacs 29.1 diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el index aa5f70edf2..588fe599a4 100644 --- a/lisp/buff-menu.el +++ b/lisp/buff-menu.el @@ -24,8 +24,8 @@ ;;; Commentary: ;; The Buffer Menu is used to view, edit, delete, or change attributes -;; of buffers. The entry points are C-x C-b (`list-buffers') and -;; M-x buffer-menu. +;; of buffers. The entry points are `C-x C-b' (`list-buffers') and +;; `M-x buffer-menu'. ;;; Code: @@ -135,6 +135,7 @@ Buffer-menu-mode-map "%" #'Buffer-menu-toggle-read-only "b" #'Buffer-menu-bury "V" #'Buffer-menu-view + "O" #'Buffer-menu-view-other-window "T" #'Buffer-menu-toggle-files-only "M-s a C-s" #'Buffer-menu-isearch-buffers "M-s a C-M-s" #'Buffer-menu-isearch-buffers-regexp @@ -208,26 +209,25 @@ Buffer-menu-mode In Buffer Menu mode, the following commands are defined: \\ \\[quit-window] Remove the Buffer Menu from the display. -\\[Buffer-menu-this-window] Select current line's buffer in place of the buffer menu. +\\[Buffer-menu-this-window] Select current line's buffer in place of the buffer menu. \\[Buffer-menu-other-window] Select that buffer in another window, so the Buffer Menu remains visible in its window. -\\[Buffer-menu-view] Select current line's buffer, in View mode. -\\[Buffer-menu-view-other-window] Select that buffer in - another window, in `view-mode'. +\\[Buffer-menu-view] Select current line's buffer, in `view-mode'. +\\[Buffer-menu-view-other-window] Select that buffer in another window, in `view-mode'. \\[Buffer-menu-switch-other-window] Make another window display that buffer. \\[Buffer-menu-mark] Mark current line's buffer to be displayed. \\[Buffer-menu-select] Select current line's buffer. - Also show buffers marked with m, in other windows. + Also show buffers marked with \"m\", in other windows. \\[Buffer-menu-1-window] Select that buffer in full-frame window. \\[Buffer-menu-2-window] Select that buffer in one window, together with the buffer selected before this one in another window. \\[Buffer-menu-isearch-buffers] Incremental search in the marked buffers. \\[Buffer-menu-isearch-buffers-regexp] Isearch for regexp in the marked buffers. -\\[Buffer-menu-multi-occur] Show lines matching regexp in the marked buffers. +\\[Buffer-menu-multi-occur] Show lines matching regexp in the marked buffers. \\[Buffer-menu-visit-tags-table] `visit-tags-table' this buffer. \\[Buffer-menu-not-modified] Clear modified-flag on that buffer. \\[Buffer-menu-save] Mark that buffer to be saved, and move down. -\\[Buffer-menu-delete] Mark that buffer to be deleted, and move down. +\\[Buffer-menu-delete] Mark that buffer to be deleted, and move down. \\[Buffer-menu-delete-backwards] Mark that buffer to be deleted, and move up. \\[Buffer-menu-execute] Delete or save marked buffers. \\[Buffer-menu-unmark] Remove all marks from current line. commit 60354fcf49da8773ad895b84356165cbd6ec8fca Author: Stefan Kangas Date: Fri Nov 25 01:26:05 2022 +0100 ; * lisp/net/goto-addr.el (goto-address-at-point): Fix last commit. diff --git a/lisp/net/goto-addr.el b/lisp/net/goto-addr.el index 03e14c9268..5b850b258c 100644 --- a/lisp/net/goto-addr.el +++ b/lisp/net/goto-addr.el @@ -222,14 +222,14 @@ goto-address-fontify-region ;;;###autoload (defun goto-address-at-point (&optional event) - "Start composing a new message to the e-mail address at point or -open URL at point. + "Compose a new message to the e-mail address or open URL at point. -If no e-mail address is found at point, use the URL at or before -point. See `goto-address-find-address-at-point'. +Compose message to address at point. See documentation for +`goto-address-find-address-at-point'. -With prefix argument, use the secondary browser to open the URL. -See `browse-url-button-open-url'." +If no e-mail address is found at point, open the URL at or before +point using `browse-url'. With a prefix argument, open the URL +using `browse-url-secondary-browser-function' instead." (interactive (list last-input-event)) (save-excursion (if event (posn-set-point (event-end event))) commit 29a262bfa7b33859e8215aee06a674dab85689fc Author: Gabriel do Nascimento Ribeiro Date: Mon Nov 21 08:08:52 2022 -0300 Allow goto-address-at-point to use secondary browser * lisp/net/goto-addr.el (goto-address-at-point): Call `browse-url-button-open-url', so a prefix argument uses `browse-url-secondary-browser-function'. (Bug#59443) diff --git a/lisp/net/goto-addr.el b/lisp/net/goto-addr.el index 86cf98004b..03e14c9268 100644 --- a/lisp/net/goto-addr.el +++ b/lisp/net/goto-addr.el @@ -222,25 +222,28 @@ goto-address-fontify-region ;;;###autoload (defun goto-address-at-point (&optional event) - "Send to the e-mail address or load the URL at point. -Send mail to address at point. See documentation for -`goto-address-find-address-at-point'. If no address is found -there, then load the URL at or before point." + "Start composing a new message to the e-mail address at point or +open URL at point. + +If no e-mail address is found at point, use the URL at or before +point. See `goto-address-find-address-at-point'. + +With prefix argument, use the secondary browser to open the URL. +See `browse-url-button-open-url'." (interactive (list last-input-event)) (save-excursion (if event (posn-set-point (event-end event))) (let ((address (save-excursion (goto-address-find-address-at-point)))) (if (and address - (save-excursion - (goto-char (previous-single-char-property-change - (point) 'goto-address nil - (line-beginning-position))) - (not (looking-at goto-address-url-regexp)))) - (compose-mail address) - (let ((url (browse-url-url-at-point))) - (if url - (browse-url url) - (error "No e-mail address or URL found"))))))) + (save-excursion + (goto-char (previous-single-char-property-change + (point) 'goto-address nil + (line-beginning-position))) + (not (looking-at goto-address-url-regexp)))) + (compose-mail address) + (if-let ((url (browse-url-url-at-point))) + (browse-url-button-open-url url) + (error "No e-mail address or URL found")))))) (defun goto-address-find-address-at-point () "Find e-mail address around or before point. commit 2b123edcf6704952447dcba95c18f324397c17c6 Author: Manuel Giraud Date: Fri Nov 18 22:58:19 2022 +0100 Fix easy menu separator in oldXMenu * lisp/emacs-lisp/easymenu.el (easy-menu-convert-item-1): Replace a string of dash with a menu separator as the doc says. (Bug#59370) diff --git a/lisp/emacs-lisp/easymenu.el b/lisp/emacs-lisp/easymenu.el index 41e3a197af..2a45c1ab1c 100644 --- a/lisp/emacs-lisp/easymenu.el +++ b/lisp/emacs-lisp/easymenu.el @@ -390,10 +390,13 @@ easy-menu-convert-item-1 (let ((key (easy-menu-intern name))) (cons key (and (not remove) - (cons 'menu-item - (cons label - (and name - (cons command prop))))))))) + (if (and (stringp label) + (seq-every-p (lambda (c) (char-equal c ?-)) label)) + menu-bar-separator + (cons 'menu-item + (cons label + (and name + (cons command prop)))))))))) (defun easy-menu-define-key (menu key item &optional before) "Add binding in MENU for KEY => ITEM. Similar to `define-key-after'. commit b0fa3b1a1f31c158131325f0f451c960ec54d938 Author: Jostein Kjønigsen Date: Thu Nov 24 13:41:29 2022 -0800 Improve csharp-ts-mode fontification * lisp/progmodes/csharp-mode.el (csharp-ts-mode--font-lock-settings): Add more pattern for definition feature. diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 3ea894733f..af8a4a8106 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -840,6 +840,18 @@ csharp-ts-mode--font-lock-settings (method_declaration type: (_) @font-lock-type-face) (method_declaration name: (_) @font-lock-function-name-face) + (invocation_expression + (member_access_expression + (generic_name (identifier) @font-lock-function-name-face))) + (invocation_expression + (member_access_expression + ((identifier) @font-lock-variable-name-face + (identifier) @font-lock-function-name-face))) + (invocation_expression + (identifier) @font-lock-function-name-face) + (invocation_expression + (member_access_expression (identifier) @font-lock-function-name-face)) + (variable_declaration (identifier) @font-lock-type-face) (variable_declarator (identifier) @font-lock-variable-name-face) commit fc9d7b3d047dbf60a07baa7fa92d5db90f562d28 Author: Yuan Fu Date: Thu Nov 24 10:18:14 2022 -0800 Improve python-ts-mode fontification (bug#59534) * lisp/progmodes/python.el (python--treesit-operators): Add operators. (python--treesit-fontify-string): Fontify BOF docstrings. diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f97ae81508..221e16f8f7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -988,7 +988,7 @@ python--treesit-constants (defvar python--treesit-operators '("-" "-=" "!=" "*" "**" "**=" "*=" "/" "//" "//=" "/=" "&" "%" "%=" - "^" "+" "+=" "<" "<<" "<=" "<>" "=" "==" ">" ">=" ">>" "|" "~")) + "^" "+" "+=" "<" "<<" "<=" "<>" "=" "==" ">" ">=" ">>" "|" "~" "@" "@=")) (defvar python--treesit-special-attributes '("__annotations__" "__closure__" "__code__" @@ -1033,12 +1033,27 @@ python--treesit-fontify-string (let* ((string-beg (treesit-node-start node)) (string-end (treesit-node-end node)) (maybe-expression (treesit-node-parent node)) - (maybe-defun (treesit-node-parent + (grandparent (treesit-node-parent (treesit-node-parent maybe-expression))) - (face (if (and (member (treesit-node-type maybe-defun) - '("function_definition" - "class_definition")) + (maybe-defun grandparent) + (face (if (and (or (member (treesit-node-type maybe-defun) + '("function_definition" + "class_definition")) + ;; If the grandparent is null, meaning the + ;; string is top-level, and the string has + ;; no node or only comment preceding it, + ;; it's a BOF docstring. + (and (null grandparent) + (cl-loop + for prev = (treesit-node-prev-sibling + maybe-expression) + then (treesit-node-prev-sibling prev) + while prev + if (not (equal (treesit-node-type prev) + "comment")) + return nil + finally return t))) ;; This check filters out this case: ;; def function(): ;; return "some string" commit 55f6f1c82a35f0589d3dbdd1f32fae7ea9a758d8 Author: Paul Eggert Date: Thu Nov 24 12:56:22 2022 -0800 Work around lsp-mode compatibility bug * src/timefns.c (Ftime_subtract): Respect current-time-list when args are eq (Bug#59506). diff --git a/src/timefns.c b/src/timefns.c index eed2edf1cc..9beec1ce38 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -1194,7 +1194,7 @@ DEFUN ("time-subtract", Ftime_subtract, Stime_subtract, 2, 2, 0, quicker while we're at it. This means (time-subtract X X) does not signal an error if X is not a valid time value, but that's OK. */ if (BASE_EQ (a, b)) - return timespec_to_lisp ((struct timespec) {0}); + return make_lisp_time ((struct timespec) {0}); return time_arith (a, b, true); } commit 149e6e03b3f860c3d78031bf3377d8a4c99a7a64 Author: Eli Zaretskii Date: Thu Nov 24 21:24:29 2022 +0200 ; Avoid treesit-related byte-compilation warnings * lisp/progmodes/csharp-mode.el: * lisp/progmodes/ts-mode.el: Declare treesit.c functions. diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index c507de94e0..3ea894733f 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -32,6 +32,11 @@ (eval-when-compile (require 'cc-fonts)) +(declare-function treesit-parser-create "treesit.c") +(declare-function treesit-induce-sparse-tree "treesit.c") +(declare-function treesit-node-start "treesit.c") +(declare-function treesit-node-child-by-field-name "treesit.c") + (defgroup csharp nil "Major mode for editing C# code." :group 'prog-mode) diff --git a/lisp/progmodes/ts-mode.el b/lisp/progmodes/ts-mode.el index 436b198f59..bdef1c4576 100644 --- a/lisp/progmodes/ts-mode.el +++ b/lisp/progmodes/ts-mode.el @@ -28,6 +28,8 @@ (require 'rx) (require 'js) +(declare-function treesit-parser-create "treesit.c") + (defcustom ts-mode-indent-offset 2 "Number of spaces for each indentation step in `ts-mode'." :version "29.1" commit 5ce34327eef604c27ba0d4223045c49fdc66fd38 Author: Brian Leung Date: Thu Nov 24 10:48:07 2022 -0800 Add more tree-sitter modes to eglot-server-programs * lisp/progmodes/eglot.el (eglot-server-programs): Add python-ts-mode and bash-ts-mode. (Bug#59550) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index d8fb88318a..a0fb253e10 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -184,7 +184,7 @@ eglot-alternatives (defvar eglot-server-programs `((rust-mode . ,(eglot-alternatives '("rust-analyzer" "rls"))) (cmake-mode . ("cmake-language-server")) (vimrc-mode . ("vim-language-server" "--stdio")) - (python-mode + ((python-mode python-ts-mode) . ,(eglot-alternatives '("pylsp" "pyls" ("pyright-langserver" "--stdio") "jedi-language-server"))) ((js-json-mode json-mode json-ts-mode) @@ -192,7 +192,7 @@ eglot-server-programs ("json-languageserver" "--stdio")))) ((js-mode ts-mode typescript-mode) . ("typescript-language-server" "--stdio")) - (sh-mode . ("bash-language-server" "start")) + ((bash-ts-mode sh-mode) . ("bash-language-server" "start")) ((php-mode phps-mode) . ,(eglot-alternatives '(("phpactor" "language-server") commit 697aaeb7b34315b4adbb95dfc39c4ab48a6319eb Author: Philip Kaludercic Date: Thu Nov 24 20:08:17 2022 +0100 ; * src/emacs.c (usage_message): Fix typo Author: diff --git a/src/emacs.c b/src/emacs.c index cc64050867..00e7f86e9a 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -295,7 +295,7 @@ #define MAIN_PROGRAM --no-site-lisp, -nsl do not add site-lisp directories to load-path\n\ --no-splash do not display a splash screen on startup\n\ --no-window-system, -nw do not communicate with X, ignoring $DISPLAY\n\ ---init-directory=DIR use DIR to when looking for the Emacs init files.\n\ +--init-directory=DIR use DIR when looking for the Emacs init files.\n\ ", "\ --quick, -Q equivalent to:\n\ commit 80b2534605698c231abc970ff4ae6b1b87cee13c Author: Eli Zaretskii Date: Thu Nov 24 20:54:38 2022 +0200 ; * lisp/progmodes/eglot.el (eglot-server-programs): Add csharp-ts-mode. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index bc43136923..d8fb88318a 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -238,7 +238,8 @@ eglot-server-programs (dockerfile-mode . ("docker-langserver" "--stdio")) ((clojure-mode clojurescript-mode clojurec-mode) . ("clojure-lsp")) - (csharp-mode . ("omnisharp" "-lsp")) + ((csharp-mode csharp-ts-mode) + . ("omnisharp" "-lsp")) (purescript-mode . ("purescript-language-server" "--stdio")) ((perl-mode cperl-mode) . ("perl" "-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run")) (markdown-mode . ("marksman" "server"))) commit 80ed35866e41bb98724c6862b19370336679e026 Author: Philip Kaludercic Date: Sun Nov 20 22:51:14 2022 +0100 * src/emacs.c (usage_message): Add missing --init-directory entry diff --git a/src/emacs.c b/src/emacs.c index 105539aa19..cc64050867 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -295,6 +295,7 @@ #define MAIN_PROGRAM --no-site-lisp, -nsl do not add site-lisp directories to load-path\n\ --no-splash do not display a splash screen on startup\n\ --no-window-system, -nw do not communicate with X, ignoring $DISPLAY\n\ +--init-directory=DIR use DIR to when looking for the Emacs init files.\n\ ", "\ --quick, -Q equivalent to:\n\ commit 94ec173a732641f00535997e8381b9ef2c2814cd Author: Brian Leung Date: Sat Nov 12 14:30:37 2022 -0800 Add tree-sitter-based modes to eglot-server-programs * lisp/progmodes/eglot.el (eglot-server-programs): Add tree-sitter-based major modes. (Bug#59229) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 5f13679b1e..bc43136923 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -187,7 +187,9 @@ eglot-server-programs (python-mode . ,(eglot-alternatives '("pylsp" "pyls" ("pyright-langserver" "--stdio") "jedi-language-server"))) - ((js-json-mode json-mode) . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio") ("json-languageserver" "--stdio")))) + ((js-json-mode json-mode json-ts-mode) + . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio") + ("json-languageserver" "--stdio")))) ((js-mode ts-mode typescript-mode) . ("typescript-language-server" "--stdio")) (sh-mode . ("bash-language-server" "start")) @@ -195,8 +197,9 @@ eglot-server-programs . ,(eglot-alternatives '(("phpactor" "language-server") ("php" "vendor/felixfbecker/language-server/bin/php-language-server.php")))) - ((c++-mode c-mode) . ,(eglot-alternatives - '("clangd" "ccls"))) + ((c-mode c-ts-mode c++-mode c++-ts-mode) + . ,(eglot-alternatives + '("clangd" "ccls"))) (((caml-mode :language-id "ocaml") (tuareg-mode :language-id "ocaml") reason-mode) . ("ocamllsp")) @@ -210,7 +213,7 @@ eglot-server-programs ((go-mode go-dot-mod-mode go-dot-work-mode) . ("gopls")) ((R-mode ess-r-mode) . ("R" "--slave" "-e" "languageserver::run()")) - (java-mode . ("jdtls")) + ((java-mode java-ts-mode) . ("jdtls")) (dart-mode . ("dart" "language-server" "--client-id" "emacs.eglot-dart")) (elixir-mode . ("language_server.sh")) @@ -228,7 +231,9 @@ eglot-server-programs (lua-mode . ,(eglot-alternatives '("lua-language-server" "lua-lsp"))) (zig-mode . ("zls")) - (css-mode . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") ("css-languageserver" "--stdio")))) + ((css-mode css-ts-mode) + . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") + ("css-languageserver" "--stdio")))) (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio")))) (dockerfile-mode . ("docker-langserver" "--stdio")) ((clojure-mode clojurescript-mode clojurec-mode) commit b604bef915e3ec2f2a08f50e5f4799bacdf057da Author: Ulf Jasper Date: Thu Nov 24 18:56:50 2022 +0100 icalendar: Adjust tests after fixing parsing of sexp entries (bug#56241) * test/lisp/calendar/icalendar-tests.el (icalendar-export-bug-56241-nested-sexps): New. * test/lisp/calendar/icalendar-tests.el (icalendar-real-world): Disable testcase for exporting sexp entries that has now become invalid. diff --git a/test/lisp/calendar/icalendar-tests.el b/test/lisp/calendar/icalendar-tests.el index d9631310ae..fa55eea95e 100644 --- a/test/lisp/calendar/icalendar-tests.el +++ b/test/lisp/calendar/icalendar-tests.el @@ -1014,15 +1014,15 @@ icalendar-export-bug-56241-dotted-pair ;; diary-string)))) ;; '("%%(diary-float 7 0 1) First Sunday in July 1" ;; "%%(icalendar-tests--diary-float 7 0 1) First Sunday in July 2")))) -;; -;; (ert-deftest icalendar-export-bug-56241-nested-sexps () -;; "Reported in #bug56241 -- needs to be fixed!" -;; (let ((icalendar-export-sexp-enumeration-days 366)) -;; (mapc (lambda (diary-string) -;; (should (string= "" (icalendar-tests--get-error-string-for-export -;; diary-string)))) -;; '("%%(= (calendar-day-of-week date) 0) Sunday 1" -;; "%%(= 0 (calendar-day-of-week date)) Sunday 2")))) + +(ert-deftest icalendar-export-bug-56241-nested-sexps () + "Reported in #bug56241 -- needs to be fixed!" + (let ((icalendar-export-sexp-enumeration-days 366)) + (mapc (lambda (diary-string) + (should (string= "" (icalendar-tests--get-error-string-for-export + diary-string)))) + '("%%(= (calendar-day-of-week date) 0) Sunday 1" + "%%(= 0 (calendar-day-of-week date)) Sunday 2")))) ;; ====================================================================== ;; Import tests @@ -1482,12 +1482,15 @@ icalendar-real-world RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=09;BYMONTHDAY=21 SUMMARY:ff birthday (%d years old)") - - (icalendar-tests--test-export - nil - nil - "%%(diary-offset '(diary-float t 3 4) 1) asdf" - nil) + ;; FIXME: this testcase verifies that icalendar-export fails to + ;; export the nested sexp. After repairing bug56241 icalendar-export + ;; works correctly for this sexp but now the testcase fails. + ;; Therefore this testcase is disabled for the time being. + ;; (icalendar-tests--test-export + ;; nil + ;; nil + ;; "%%(diary-offset '(diary-float t 3 4) 1) asdf" + ;; nil) ;; FIXME! commit c6c31b2903b82273da2671986a08edb1bda08214 Author: hokomo Date: Thu Nov 24 18:39:51 2022 +0100 icalendar.el: Fix parsing of sexp entries (bug#56241) * lisp/calendar/icalendar.el (icalendar--convert-sexp-to-ical): Use read-from-string for parsing sexp entries (bug#56241). Copyright-paperwork-exempt: yes diff --git a/lisp/calendar/icalendar.el b/lisp/calendar/icalendar.el index cf54293989..5575740040 100644 --- a/lisp/calendar/icalendar.el +++ b/lisp/calendar/icalendar.el @@ -1641,9 +1641,11 @@ icalendar--convert-sexp-to-ical entry-main) ;; regular sexp entry (icalendar--dmsg "diary-sexp %s" entry-main) - (let ((p1 (substring entry-main (match-beginning 1) (match-end 1))) - (p2 (substring entry-main (match-beginning 2) (match-end 2))) - (now (or start (current-time)))) + (let* ((entry-main (substring entry-main 2)) + (res (read-from-string entry-main)) + (p1 (prin1-to-string (car res))) + (p2 (substring entry-main (cdr res))) + (now (or start (current-time)))) (delete nil (mapcar (lambda (offset) commit d34fc7b7aa9d2779ebbada5cecd8bd2806e3e01e Author: dannyfreeman Date: Thu Nov 24 15:22:33 2022 +0000 Eglot: don't confuse URLs and windows file paths bug#59338 * lisp/progmodes/eglot.el (eglot--path-to-uri): Check for windows path diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index d05b5ee335..5f13679b1e 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1503,11 +1503,15 @@ eglot--uri-path-allowed-chars (defun eglot--path-to-uri (path) "URIfy PATH." (let ((truepath (file-truename path))) - (if (url-type (url-generic-parse-url truepath)) + (if (and (url-type (url-generic-parse-url path)) + ;; It might be MS Windows path which includes a drive + ;; letter that looks like a URL scheme (bug#59338) + (not (and (eq system-type 'windows-nt) + (file-name-absolute-p truepath)))) ;; Path is already a URI, so forward it to the LSP server ;; untouched. The server should be able to handle it, since ;; it provided this URI to clients in the first place. - truepath + path (concat "file://" ;; Add a leading "/" for local MS Windows-style paths. (if (and (eq system-type 'windows-nt) commit fcd5fde090022c2b1a6c8e130634539892857f9a Author: João Távora Date: Thu Nov 24 14:48:35 2022 +0000 Remove fboundp check in eglot--connect project.el is a GNU ELPA :core package, so this kind of trick isn't needed. * lisp/progmodes/eglot.el (eglot--connect): Don't fboundp project-name. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 94f718c865..d05b5ee335 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1185,9 +1185,7 @@ eglot--connect "Connect to MANAGED-MODES, LANGUAGE-ID, PROJECT, CLASS and CONTACT. This docstring appeases checkdoc, that's all." (let* ((default-directory (project-root project)) - (nickname (if (fboundp 'project-name) - (project-name project) - (file-name-base (directory-file-name default-directory)))) + (nickname (project-name project)) (readable-name (format "EGLOT (%s/%s)" nickname managed-modes)) autostart-inferior-process server-info commit cf439636d466d999a1f064fe9783013d3f3db17d Author: Marcin Pajkowski Date: Sun Nov 20 20:03:57 2022 +0100 Eglot: Advertise completion.resolveSupport capabilities Some servers avoid reporting completion items that require "additionalTextEdits" capability. Actually, 'eglot-completion-at-point' function supports such feature so it can be advertised to LSP server. * lisp/progmodes/eglot.el (eglot-client-capabilities): Advertise resolveSupport. (bug#59465) Copyright-paperwork-exempt: yes diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index e057b12e0e..94f718c865 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -737,6 +737,10 @@ eglot-client-capabilities t :json-false) :deprecatedSupport t + :resolveSupport (:properties + ["documentation" + "details" + "additionalTextEdits"]) :tagSupport (:valueSet [1])) :contextSupport t) :hover (list :dynamicRegistration :json-false commit 25e7290d36723525eef189e3b7b194e1110f4d31 Author: Mattias Engdegård Date: Thu Nov 24 13:47:55 2022 +0100 Add new SI prefixes * lisp/calc/calc-units.el (math-unit-prefixes): * lisp/files.el (file-size-human-readable): Add ronna, quetta, ronto and quecto. (We get the future IEC binary prefixes Ri and Qi (robi and quebi) in `file-size-human-readable` for free.) diff --git a/lisp/calc/calc-units.el b/lisp/calc/calc-units.el index c8405c7d1a..42156b9460 100644 --- a/lisp/calc/calc-units.el +++ b/lisp/calc/calc-units.el @@ -317,7 +317,9 @@ math-additional-units that the combined units table will be rebuilt.") (defvar math-unit-prefixes - '( ( ?Y (^ 10 24) "Yotta" ) + '( ( ?Q (^ 10 30) "quetta" ) + ( ?R (^ 10 27) "ronna" ) + ( ?Y (^ 10 24) "Yotta" ) ( ?Z (^ 10 21) "Zetta" ) ( ?E (^ 10 18) "Exa" ) ( ?P (^ 10 15) "Peta" ) @@ -340,7 +342,10 @@ math-unit-prefixes ( ?f (^ 10 -15) "Femto" ) ( ?a (^ 10 -18) "Atto" ) ( ?z (^ 10 -21) "zepto" ) - ( ?y (^ 10 -24) "yocto" ))) + ( ?y (^ 10 -24) "yocto" ) + ( ?r (^ 10 -27) "ronto" ) + ( ?q (^ 10 -30) "quecto" ) + )) (defvar math-standard-units-systems '( ( base nil ) diff --git a/lisp/files.el b/lisp/files.el index 60c0eb917e..f1f890430f 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1533,7 +1533,7 @@ file-size-human-readable (let ((power (if (or (null flavor) (eq flavor 'iec)) 1024.0 1000.0)) - (prefixes '("" "k" "M" "G" "T" "P" "E" "Z" "Y"))) + (prefixes '("" "k" "M" "G" "T" "P" "E" "Z" "Y" "R" "Q"))) (while (and (>= file-size power) (cdr prefixes)) (setq file-size (/ file-size power) prefixes (cdr prefixes))) commit 9fcff114b8fe2d538699172fe756606441e67b92 Author: Po Lu Date: Thu Nov 24 20:10:14 2022 +0800 Fix reentrancy problem/crash in xterm.c * src/xterm.c (x_ignore_errors_for_next_request) (x_stop_ignoring_errors): Be paranoid and block input inside the protected section. (x_focus_frame): Block input around critical section. diff --git a/src/xterm.c b/src/xterm.c index cfd8c385d1..7d855c92cc 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -25461,6 +25461,17 @@ x_clean_failable_requests (struct x_display_info *dpyinfo) + (last - first)); } +/* Protect a section of X requests: ignore errors generated by X + requests made from now until `x_stop_ignoring_errors'. Each call + must be paired with a call to `x_stop_ignoring_errors', and + recursive calls inside the protected section are not allowed. + + The advantage over x_catch_errors followed by + x_uncatch_errors_after_check is that this function does not sync to + catch errors if requests were made. It should be used instead of + those two functions for catching errors around requests that do not + require a reply. */ + void x_ignore_errors_for_next_request (struct x_display_info *dpyinfo) { @@ -25468,7 +25479,13 @@ x_ignore_errors_for_next_request (struct x_display_info *dpyinfo) unsigned long next_request; #ifdef HAVE_GTK3 GdkDisplay *gdpy; +#endif + /* This code is not reentrant, so be sure nothing calls it + recursively in response to input. */ + block_input (); + +#ifdef HAVE_GTK3 /* GTK 3 tends to override our own error handler inside certain callbacks, which this can be called from. Instead of trying to restore our own, add a trap for the following requests with @@ -25537,6 +25554,8 @@ x_stop_ignoring_errors (struct x_display_info *dpyinfo) if (gdpy) gdk_x11_display_error_trap_pop_ignored (gdpy); #endif + + unblock_input (); } /* Undo the last x_catch_errors call. @@ -27836,6 +27855,10 @@ x_focus_frame (struct frame *f, bool noactivate) struct x_display_info *dpyinfo; Time time; + /* The code below is not reentrant wrt to dpyinfo->x_focus_frame and + friends being set. */ + block_input (); + dpyinfo = FRAME_DISPLAY_INFO (f); if (FRAME_X_EMBEDDED_P (f)) @@ -27866,7 +27889,7 @@ x_focus_frame (struct frame *f, bool noactivate) the current workspace, and mapping it, etc, before moving input focus to the frame. */ x_ewmh_activate_frame (f); - return; + goto out; } if (NILP (Vx_no_window_manager)) @@ -27900,6 +27923,9 @@ x_focus_frame (struct frame *f, bool noactivate) matter. */ CurrentTime); } + + out: + unblock_input (); } commit 867e962cf5318399dfc17cc53c661db4bbd3c3d1 Author: Mike Kupfer Date: Sun Nov 20 16:44:20 2022 -0800 Fix trashing of symlink that points at a directory * lisp/files.el (move-file-to-trash): Redefine is-directory so that it is false for symlinks. diff --git a/lisp/files.el b/lisp/files.el index 127cf77240..60c0eb917e 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -8566,7 +8566,8 @@ move-file-to-trash ;; Make a .trashinfo file. Use O_EXCL, as per trash-spec 1.0. (let* ((files-base (file-name-nondirectory fn)) - (is-directory (file-directory-p fn)) + (is-directory (and (file-directory-p fn) + (not (file-symlink-p fn)))) (overwrite nil) info-fn) ;; We're checking further down whether the info file commit 95d827f21ef7bc3ed91335a6418e17fbb4ed5c63 Author: Mike Kupfer Date: Sun Oct 30 10:31:11 2022 -0700 Fix cross-filesystem directory trashing (Bug#58721) * lisp/files.el (move-file-to-trash): When trashing a directory with the same name as something that's already in the trash, copy it into the trash folder and then delete it, rather than using rename-file. diff --git a/lisp/files.el b/lisp/files.el index b947451369..127cf77240 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -8596,10 +8596,27 @@ move-file-to-trash (setq files-base (substring (file-name-nondirectory info-fn) 0 (- (length ".trashinfo")))) (write-region nil nil info-fn nil 'quiet info-fn))) - ;; Finally, try to move the file to the trashcan. + ;; Finally, try to move the item to the trashcan. If + ;; it's a file, just move it. Things are more + ;; complicated for directories. If the target + ;; directory already exists (due to uniquification) + ;; and the trash directory is in a different + ;; filesystem, rename-file will error out, even when + ;; 'overwrite' is non-nil. Rather than worry about + ;; whether we're crossing filesystems, just check if + ;; we've moving a directory and the target directory + ;; already exists. That handles both the + ;; same-filesystem and cross-filesystem cases. (let ((delete-by-moving-to-trash nil) (new-fn (file-name-concat trash-files-dir files-base))) - (rename-file fn new-fn overwrite))))))))) + (if (or (not is-directory) + (not (file-exists-p new-fn))) + (rename-file fn new-fn overwrite) + (copy-directory fn + (file-name-as-directory new-fn) + t nil t) + (delete-directory fn t)))))))))) + (defsubst file-attribute-type (attributes) commit 3208a42c47c4f98cb03c4b15164ca83113244b40 Author: Alan Mackenzie Date: Thu Nov 24 10:51:03 2022 +0000 CC Mode: Make it scroll fast over buffers with only #define's * lisp/progmodes/cc-engine.el (c-forward-over-token): New LIMIT parameter * lisp/progmodes/cc-langs.el (c-anchored-hash-define-no-parens): Replace ill-formed regular expression (which mixed \\sw and character alternative) with simpler efficient regexp. * lisp/progmodes/cc-mode.el (c-fl-decl-end): New forward limit LIM+ used in c-forward-declarator and c-forward-over-token. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 086166c822..9e09e5150d 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -4951,30 +4951,31 @@ c-jump-syntax-unbalanced "\\w\\|\\s_\\|\\s\"\\|\\s|" "\\w\\|\\s_\\|\\s\"")) -(defun c-forward-over-token (&optional balanced) +(defun c-forward-over-token (&optional balanced limit) "Move forward over a token. Return t if we moved, nil otherwise (i.e. we were at EOB, or a non-token or BALANCED is non-nil and we can't move). If we are at syntactic whitespace, move over this in place of a token. If BALANCED is non-nil move over any balanced parens we are at, and never move -out of an enclosing paren." +out of an enclosing paren. LIMIT is the limit to where we might move to." (let ((jump-syntax (if balanced c-jump-syntax-balanced c-jump-syntax-unbalanced)) - (here (point))) + (here (point)) + (limit (or limit (point-max)))) (condition-case nil (cond ((/= (point) - (progn (c-forward-syntactic-ws) (point))) + (progn (c-forward-syntactic-ws limit) (point))) ;; If we're at whitespace, count this as the token. t) ((eobp) nil) ((looking-at jump-syntax) - (goto-char (scan-sexps (point) 1)) + (goto-char (min limit (scan-sexps (point) 1))) t) ((looking-at c-nonsymbol-token-regexp) - (goto-char (match-end 0)) + (goto-char (min (match-end 0) limit)) t) ((save-restriction (widen) diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 47e05438ea..581685cad7 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -1188,7 +1188,7 @@ c-anchored-hash-define-no-parens t (if (c-lang-const c-opt-cpp-macro-define) (concat (c-lang-const c-anchored-cpp-prefix) (c-lang-const c-opt-cpp-macro-define) - "[ \t]+\\(\\sw\\|_\\)+\\([^(a-zA-Z0-9_]\\|$\\)"))) + "[ \t]+[a-zA-Z0-9_]+\\([^(a-zA-Z0-9_]\\|$\\)"))) (c-lang-defconst c-cpp-expr-directives "List of cpp directives (without the prefix) that are followed by an diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 99067e4761..6a2c2f2911 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -2482,7 +2482,8 @@ c-fl-decl-end (let* ((lim1 (save-excursion (and (c-beginning-of-macro) (progn (c-end-of-macro) (point))))) - (decl-res (c-forward-declarator))) + (lim+ (c-determine-+ve-limit 200)) + (decl-res (c-forward-declarator lim+))) (if (or (cadr (cddr (cddr decl-res))) ; We scanned an arglist. (and (eq (char-after) ?\() ; Move over a non arglist (...). (prog1 (c-go-list-forward) @@ -2499,7 +2500,7 @@ c-fl-decl-end (c-backward-syntactic-ws lim1) (eq (char-before) ?\()) (c-fl-decl-end (1- (point)))) - (c-forward-over-token) + (c-forward-over-token nil lim+) ; The , or ) after the declarator. (point)) (if (progn (c-forward-syntactic-ws) (not (eobp))) commit 005efce764c50f5fc68be84a7fb52565b9a2d2bc Author: Tassilo Horn Date: Thu Nov 24 10:47:54 2022 +0100 Fix face issues in show-paren context overlay (bug#59527) * lisp/paren.el (show-paren--show-context-in-overlay): Use show-paren-priority as overlay priority (fixes problem 2 of bug#59527). * lisp/simple.el (blink-paren-open-paren-line-string): Ensure the context lines are font-locked before taking the buffer-substring (fixes problem 1 of bug#59527). diff --git a/lisp/paren.el b/lisp/paren.el index e2c060ceb9..1d7fb1c462 100644 --- a/lisp/paren.el +++ b/lisp/paren.el @@ -410,6 +410,10 @@ show-paren--show-context-in-overlay (line-end-position)))) (setq show-paren--context-overlay (make-overlay beg end))) (overlay-put show-paren--context-overlay 'display text) + ;; Use the (default very high) `show-paren-priority' ensuring that + ;; not other overlays shine through (bug#59527). + (overlay-put show-paren--context-overlay 'priority + show-paren-priority) (overlay-put show-paren--context-overlay 'face `(:box ( :line-width (1 . -1) diff --git a/lisp/simple.el b/lisp/simple.el index e868736614..893a43b03f 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -9184,33 +9184,39 @@ blink-paren-open-paren-line-string "Return the line string that contains the openparen at POS." (save-excursion (goto-char pos) - ;; Show what precedes the open in its line, if anything. - (cond - ((save-excursion (skip-chars-backward " \t") (not (bolp))) - (buffer-substring (line-beginning-position) - (1+ pos))) - ;; Show what follows the open in its line, if anything. - ((save-excursion - (forward-char 1) - (skip-chars-forward " \t") - (not (eolp))) - (buffer-substring pos - (line-end-position))) - ;; Otherwise show the previous nonblank line, - ;; if there is one. - ((save-excursion (skip-chars-backward "\n \t") (not (bobp))) - (concat - (buffer-substring (progn - (skip-chars-backward "\n \t") - (line-beginning-position)) - (progn (end-of-line) - (skip-chars-backward " \t") - (point))) - ;; Replace the newline and other whitespace with `...'. - "..." - (buffer-substring pos (1+ pos)))) - ;; There is nothing to show except the char itself. - (t (buffer-substring pos (1+ pos)))))) + ;; Capture the regions in terms of (beg . end) conses whose + ;; buffer-substrings we want to show as a context string. Ensure + ;; they are font-locked (bug#59527). + (let (regions) + ;; Show what precedes the open in its line, if anything. + (cond + ((save-excursion (skip-chars-backward " \t") (not (bolp))) + (setq regions (list (cons (line-beginning-position) + (1+ pos))))) + ;; Show what follows the open in its line, if anything. + ((save-excursion + (forward-char 1) + (skip-chars-forward " \t") + (not (eolp))) + (setq regions (list (cons pos (line-end-position))))) + ;; Otherwise show the previous nonblank line, + ;; if there is one. + ((save-excursion (skip-chars-backward "\n \t") (not (bobp))) + (setq regions (list (cons (progn + (skip-chars-backward "\n \t") + (line-beginning-position)) + (progn (end-of-line) + (skip-chars-backward " \t") + (point))) + (cons pos (1+ pos))))) + ;; There is nothing to show except the char itself. + (t (setq regions (list (cons pos (1+ pos)))))) + ;; Ensure we've font-locked the context region. + (font-lock-ensure (caar regions) (cdar (last regions))) + (mapconcat (lambda (region) + (buffer-substring (car region) (cdr region))) + regions + "...")))) (defvar blink-paren-function 'blink-matching-open "Function called, if non-nil, whenever a close parenthesis is inserted. commit 825221183390f61f163258e09e91c0a75bf6d645 Author: Eli Zaretskii Date: Thu Nov 24 11:09:51 2022 +0200 Avoid assertion violations in treesit.c when editing non-ASCII * src/treesit.c (make_treesit_parser): Use byte positions when initializing 'lisp_parser'. This avoids assertion violations when the buffer has non-ASCII characters. diff --git a/src/treesit.c b/src/treesit.c index 7834eb1a68..3df53f2179 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -983,8 +983,8 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser, TSInput input = {lisp_parser, treesit_read_buffer, TSInputEncodingUTF8}; lisp_parser->input = input; lisp_parser->need_reparse = true; - lisp_parser->visible_beg = BUF_BEGV (XBUFFER (buffer)); - lisp_parser->visible_end = BUF_ZV (XBUFFER (buffer)); + lisp_parser->visible_beg = BUF_BEGV_BYTE (XBUFFER (buffer)); + lisp_parser->visible_end = BUF_ZV_BYTE (XBUFFER (buffer)); lisp_parser->timestamp = 0; lisp_parser->deleted = false; lisp_parser->has_range = false;