From b36cc7e7bbb5a8d0c1b298b89a4cb562af746ecc Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Mon, 16 Jan 2023 20:32:15 -0800 Subject: [PATCH 01/34] ; * src/treesit.c (Ftreesit_induce_sparse_tree): Minor change. --- src/treesit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/treesit.c b/src/treesit.c index 3886fed346e..adbed1427be 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -3270,9 +3270,9 @@ a regexp. */) Lisp_Object parser = XTS_NODE (root)->parser; Lisp_Object parent = Fcons (Qnil, Qnil); - TSTreeCursor cursor; - if (!treesit_cursor_helper (&cursor, XTS_NODE (root)->node, parser)) - return Qnil; + /* In this function we never traverse above NODE, so we don't need + to use treesit_cursor_helper. */ + TSTreeCursor cursor = ts_tree_cursor_new (XTS_NODE (root)->node); treesit_build_sparse_tree (&cursor, parent, predicate, process_fn, the_limit, parser); From 7c61a304104fe3a35c47d412150d29b93a697c5e Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Tue, 17 Jan 2023 00:57:54 -0800 Subject: [PATCH 02/34] Fix treesit-node-first-child-for-pos (bug#60127) The problem is due to a bug in ts_node_first_child_for_pos, but tree-sitter is moving pretty slowly right now so I reimplemented a correct version of it in treesit.c. * src/treesit.c (treesit_cursor_first_child_for_byte): New function. (Ftreesit_node_first_child_for_pos): Use the new function. --- src/treesit.c | 52 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/treesit.c b/src/treesit.c index adbed1427be..644d323d5cb 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -2095,6 +2095,41 @@ return nil. */) return make_treesit_node (XTS_NODE (node)->parser, sibling); } +/* Our reimplementation of ts_node_first_child_for_byte. The current + implementation of that function has problems (see bug#60127), so + before it's fixed upstream, we use our own reimplementation of it. + Return true if there is a valid sibling, return false otherwise. + If the return value is false, the position of the cursor is + undefined. (We use cursor because technically we can't make a null + node for ourselves, also, using cursor is more convenient.) + + TODO: Remove this function once tree-sitter fixed the bug. */ +static bool treesit_cursor_first_child_for_byte +(TSTreeCursor *cursor, ptrdiff_t pos, bool named) +{ + if (!ts_tree_cursor_goto_first_child (cursor)) + return false; + + TSNode node = ts_tree_cursor_current_node (cursor); + while (ts_node_end_byte (node) <= pos) + { + if (ts_tree_cursor_goto_next_sibling (cursor)) + node = ts_tree_cursor_current_node (cursor); + else + /* Reached the end and still can't find a valid sibling. */ + return false; + } + while (named && (!ts_node_is_named (node))) + { + if (ts_tree_cursor_goto_next_sibling (cursor)) + node = ts_tree_cursor_current_node (cursor); + else + /* Reached the end and still can't find a named sibling. */ + return false; + } + return true; +} + DEFUN ("treesit-node-first-child-for-pos", Ftreesit_node_first_child_for_pos, Streesit_node_first_child_for_pos, 2, 3, 0, @@ -2119,16 +2154,17 @@ Note that this function returns an immediate child, not the smallest ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, XFIXNUM (pos)); TSNode treesit_node = XTS_NODE (node)->node; - TSNode child; - if (NILP (named)) - child = ts_node_first_child_for_byte (treesit_node, byte_pos - visible_beg); - else - child = ts_node_first_named_child_for_byte (treesit_node, - byte_pos - visible_beg); - if (ts_node_is_null (child)) + TSTreeCursor cursor = ts_tree_cursor_new (treesit_node); + ptrdiff_t treesit_pos = byte_pos - visible_beg; + bool success; + success = treesit_cursor_first_child_for_byte (&cursor, treesit_pos, + !NILP (named)); + TSNode child = ts_tree_cursor_current_node (&cursor); + ts_tree_cursor_delete (&cursor); + + if (!success) return Qnil; - return make_treesit_node (XTS_NODE (node)->parser, child); } From 9e7a5d58eea664b55e11f2ea5dc7da9ba26d500f Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Tue, 17 Jan 2023 01:20:51 -0800 Subject: [PATCH 03/34] ; Fix tree-sitter indent anchor preset * lisp/treesit.el: (treesit-simple-indent-presets): Fix prev-adaptive-prefix. --- lisp/treesit.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lisp/treesit.el b/lisp/treesit.el index 5e6f109531e..4c31ecb0d29 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1152,6 +1152,9 @@ See `treesit-simple-indent-presets'.") (and (>= (point) comment-start-bol) adaptive-fill-regexp (looking-at adaptive-fill-regexp) + ;; If previous line is an empty line, don't + ;; indent. + (not (looking-at (rx (* whitespace) eol))) (match-end 0)))))) ;; TODO: Document. (cons 'grand-parent From bd094207c76c500f8a3bffe8231d8c6ae0fd0778 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" Date: Wed, 11 Jan 2023 16:34:07 +0000 Subject: [PATCH 04/34] Fix buffer-list-update-hook for indirect buffers Fmake_indirect_buffer can be told whether to run buffer hooks since bug#49160, but until now it ran buffer-list-update-hook irrespective of this. * src/buffer.c (Fmake_indirect_buffer): Don't run buffer-list-update-hook when called with a non-nil INHIBIT-BUFFER-HOOKS argument. (run_buffer_list_update_hook): Don't special-case NULL argument, as no such callers remain. * test/src/buffer-tests.el (buffer-tests-inhibit-buffer-hooks-indirect): Test whether indirect buffer hooks are run regardless of whether base buffer hooks are inhibited. Check that all three buffer hooks, not just kill-buffer-query-functions, are inhibited. --- src/buffer.c | 10 ++++----- test/src/buffer-tests.el | 46 +++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 100e42fc1f9..88ca69b0dd8 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -525,14 +525,14 @@ get_truename_buffer (register Lisp_Object filename) return Qnil; } -/* Run buffer-list-update-hook if Vrun_hooks is non-nil, and BUF is NULL - or does not have buffer hooks inhibited. BUF is NULL when called by - make-indirect-buffer, since it does not inhibit buffer hooks. */ +/* Run buffer-list-update-hook if Vrun_hooks is non-nil and BUF does + not have buffer hooks inhibited. */ static void run_buffer_list_update_hook (struct buffer *buf) { - if (! (NILP (Vrun_hooks) || (buf && buf->inhibit_buffer_hooks))) + eassert (buf); + if (! (NILP (Vrun_hooks) || buf->inhibit_buffer_hooks)) call1 (Vrun_hooks, Qbuffer_list_update_hook); } @@ -907,7 +907,7 @@ does not run the hooks `kill-buffer-hook', set_buffer_internal_1 (old_b); } - run_buffer_list_update_hook (NULL); + run_buffer_list_update_hook (b); return buf; } diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index e5de8f3464a..9d4bbf3e040 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -8315,29 +8315,35 @@ dicta sunt, explicabo. ")) (remove-hook 'buffer-list-update-hook bluh)))) (ert-deftest buffer-tests-inhibit-buffer-hooks-indirect () - "Indirect buffers do not call `get-buffer-create'." - (dolist (inhibit '(nil t)) - (let ((base (get-buffer-create "foo" inhibit))) + "Test `make-indirect-buffer' argument INHIBIT-BUFFER-HOOKS." + (let* ( base run-bluh run-kbh run-kbqf + (bluh (lambda () (setq run-bluh t))) + (kbh (lambda () (setq run-kbh t))) + (kbqf (lambda () (setq run-kbqf t)))) + (dolist (inhibit-base '(nil t)) (unwind-protect - (dotimes (_i 11) - (let* (flag* - (flag (lambda () (prog1 t (setq flag* t)))) - (indirect (make-indirect-buffer base "foo[indirect]" nil - inhibit))) - (unwind-protect - (progn - (with-current-buffer indirect - (add-hook 'kill-buffer-query-functions flag nil t)) - (kill-buffer indirect) - (if inhibit - (should-not flag*) - (should flag*))) - (let (kill-buffer-query-functions) + (let (indirect) + (setq base (generate-new-buffer " base" inhibit-base)) + (dolist (inhibit-indirect '(nil t)) + (dotimes (_ 11) + (unwind-protect + (let ((name (generate-new-buffer-name " indirect"))) + (setq run-bluh nil run-kbh nil run-kbqf nil) + (add-hook 'buffer-list-update-hook bluh) + (with-current-buffer + (setq indirect (make-indirect-buffer + base name nil inhibit-indirect)) + (add-hook 'kill-buffer-hook kbh nil t) + (add-hook 'kill-buffer-query-functions kbqf nil t) + (kill-buffer)) + (should (xor inhibit-indirect run-bluh)) + (should (xor inhibit-indirect run-kbh)) + (should (xor inhibit-indirect run-kbqf))) + (remove-hook 'buffer-list-update-hook bluh) (when (buffer-live-p indirect) (kill-buffer indirect)))))) - (let (kill-buffer-query-functions) - (when (buffer-live-p base) - (kill-buffer base))))))) + (when (buffer-live-p base) + (kill-buffer base)))))) (ert-deftest zero-length-overlays-and-not () (with-temp-buffer From 09e9d7c749680fd3580e9b1795e39051e3709917 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 17 Jan 2023 15:44:51 +0200 Subject: [PATCH 05/34] Fix display of warnings on w32 console * lisp/emacs-lisp/warnings.el (warnings-suppress): Use alternative symbol for TTY frames on MS-Windows. --- lisp/emacs-lisp/warnings.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lisp/emacs-lisp/warnings.el b/lisp/emacs-lisp/warnings.el index 9505c935816..31b840d6c83 100644 --- a/lisp/emacs-lisp/warnings.el +++ b/lisp/emacs-lisp/warnings.el @@ -204,8 +204,12 @@ SUPPRESS-LIST is the list of kinds of warnings to suppress." some-match)) (define-icon warnings-suppress button - '((emoji "⛔") - (symbol " ■ ") + `((emoji "⛔") + ;; Many MS-Windows console fonts don't have good glyphs for U+25A0. + (symbol ,(if (and (eq system-type 'windows-nt) + (null window-system)) + " » " + " ■ ")) (text " stop ")) "Suppress warnings." :version "29.1" From 7b13422298a2613c506d41f52fa0c0ca1588f870 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Tue, 10 Jan 2023 11:59:57 -0800 Subject: [PATCH 06/34] ; Avoid plist-get as generalized var in erc-compat * lisp/erc/erc-compat.el (erc-compat--29-auth-source-pass-search): The gv expander for `plist-get' was added in Emacs 28. But ERC still supports 27, as of this function's introduction, in Emacs 29. --- lisp/erc/erc-compat.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el index 73ce612a33d..5601ede27a5 100644 --- a/lisp/erc/erc-compat.el +++ b/lisp/erc/erc-compat.el @@ -260,8 +260,8 @@ If START or END is negative, it counts from the end." (dolist (e rv out) (when-let* ((s (plist-get e :secret)) (v (auth-source--obfuscate s))) - (setf (plist-get e :secret) - (apply-partially #'auth-source--deobfuscate v))) + (setq e (plist-put e :secret (apply-partially + #'auth-source--deobfuscate v)))) (push e out))) rv))) From 7b8322f6285702faf5da0824b9b195619da9c698 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 16 Jan 2023 23:05:16 -0800 Subject: [PATCH 07/34] Use correct buffer for local-module vars in erc-open * lisp/erc/erc.el (erc--target-priors): New internal variable to do for target buffers what `erc--server-reconnecting' does for server buffers. (erc-open): Source the state of a local module's mode variable from its actual buffer rather than its server buffer. Additionally, make all local variables from a prior session available to module-activation functions and `erc-mode' hooks, even when `erc-reuse-buffers' is nil. This bug arrived with the introduction of "local-modules" (bug#57955). * test/lisp/erc/erc-scenarios-base-local-modules.el (erc-scenarios-base-local-modules--toggle-helpers): Remove useless `with-current-buffer'. (erc-scenarios-base-local-modules--local-var, erc--phony-sblm--enable, erc--phony-sblm--disable, erc--phony-sblm--mode): Add fake local module and data var for test scenario. (erc-scenarios-base-local-modules--var-persistence) Add slightly hacky test case with promise to improve later when splitting the file. --- lisp/erc/erc.el | 15 ++- .../erc/erc-scenarios-base-local-modules.el | 100 ++++++++++++++++-- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index ba7db15cf8c..e7f81f24ac4 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1305,6 +1305,14 @@ See also `erc-show-my-nick'." (defvar-local erc-dbuf nil) +;; See comments in `erc-scenarios-base-local-modules' explaining why +;; this is insufficient as a public interface. + +(defvar erc--target-priors nil + "Analogous to `erc--server-reconnecting' but for target buffers. +Bound to local variables from an existing (logical) session's +buffer during local-module setup and `erc-mode-hook' activation.") + (defun erc--target-from-string (string) "Construct an `erc--target' variant from STRING." (funcall (if (erc-channel-p string) @@ -1985,7 +1993,9 @@ Returns the buffer for the given server or channel." (let* ((target (and channel (erc--target-from-string channel))) (buffer (erc-get-buffer-create server port nil target id)) (old-buffer (current-buffer)) - (old-vars (and target (buffer-local-variables))) + (erc--target-priors (and target ; buf from prior session + (buffer-local-value 'erc--target buffer) + (buffer-local-variables buffer))) (old-recon-count erc-server-reconnect-count) (old-point nil) (delayed-modules nil) @@ -1998,7 +2008,8 @@ Returns the buffer for the given server or channel." (setq old-point (point)) (setq delayed-modules (erc--merge-local-modes (erc--update-modules) - (or erc--server-reconnecting old-vars))) + (or erc--server-reconnecting + erc--target-priors))) (delay-mode-hooks (erc-mode)) diff --git a/test/lisp/erc/erc-scenarios-base-local-modules.el b/test/lisp/erc/erc-scenarios-base-local-modules.el index d4001df45de..916d105779a 100644 --- a/test/lisp/erc/erc-scenarios-base-local-modules.el +++ b/test/lisp/erc/erc-scenarios-base-local-modules.el @@ -19,8 +19,17 @@ ;;; Commentary: -;; These tests all use `sasl' because, as of ERC 5.5, it's the one -;; and only local module. +;; A local module doubles as a minor mode whose mode variable and +;; associated local data can withstand service disruptions. +;; Unfortunately, the current implementation is too unwieldy to be +;; made public because it doesn't perform any of the boiler plate +;; needed to save and restore buffer-local and "network-local" copies +;; of user options. Ultimately, a user-friendly framework must fill +;; this void if third-party local modules are ever to become +;; practical. +;; +;; The following tests all use `sasl' because, as of ERC 5.5, it's the +;; only local module. ;;; Code: @@ -206,7 +215,7 @@ (erc-cmd-QUIT "") (funcall expect 10 "finished"))) - (ert-info ("Disabling works from a target buffer.") + (ert-info ("Disabling works from a target buffer") (with-current-buffer "#chan" (should erc-sasl-mode) (call-interactively #'erc-sasl-disable) @@ -214,10 +223,9 @@ (should (local-variable-p 'erc-sasl-mode)) (should-not (buffer-local-value 'erc-sasl-mode (get-buffer "foonet"))) (erc-cmd-RECONNECT) - (with-current-buffer "#chan" - (funcall expect 10 "Some enigma, some riddle") - (should-not erc-sasl-mode) ; regression - (should (local-variable-p 'erc-sasl-mode)))) + (funcall expect 10 "Some enigma, some riddle") + (should-not erc-sasl-mode) ; regression + (should (local-variable-p 'erc-sasl-mode))) (with-current-buffer "foonet" (should (local-variable-p 'erc-sasl-mode)) @@ -239,4 +247,82 @@ (should erc-sasl-mode) (funcall expect 10 "User modes for tester"))))) +(defvar-local erc-scenarios-base-local-modules--local-var nil) + +(define-erc-module -phony-sblm- nil + "Test module for `erc-scenarios-base-local-modules--var-persistence'." + ((when-let ((vars (or erc--server-reconnecting erc--target-priors))) + (should (assq 'erc--phony-sblm--mode vars)) + (setq erc-scenarios-base-local-modules--local-var + (alist-get 'erc-scenarios-base-local-modules--local-var vars))) + (setq erc-scenarios-base-local-modules--local-var + (or erc-scenarios-base-local-modules--local-var + (if erc--target 100 0)))) + ((kill-local-variable 'erc-scenarios-base-local-modules--local-var)) + 'local) + +;; Note: this file has grown too expensive (time-wise) and must be +;; split up. When that happens, this test should be rewritten without +;; any time-saving hacks, namely, server-initiated JOINs and an +;; absence of QUITs. (That said, three connections in under 2 seconds +;; is pretty nice.) + +(ert-deftest erc-scenarios-base-local-modules--var-persistence () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'options 'options 'options)) + (port (process-contact dumb-server :service)) + (erc-modules (cons '-phony-sblm- (remq 'autojoin erc-modules))) + (expect (erc-d-t-make-expecter)) + (server-buffer-name (format "127.0.0.1:%d" port))) + + (ert-info ("Initial authentication succeeds as expected") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) server-buffer-name))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "FooNet")) + (funcall expect 10 "This server is in debug mode") + (should erc--phony-sblm--mode) + (should (eql erc-scenarios-base-local-modules--local-var 0)) + (setq erc-scenarios-base-local-modules--local-var 1))) + + (ert-info ("Save module's local var in target buffer") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (should (eql erc-scenarios-base-local-modules--local-var 100)) + (setq erc-scenarios-base-local-modules--local-var 101) + (funcall expect 20 "welcome"))) + + (with-current-buffer "FooNet" (funcall expect 20 "terminated")) + + (ert-info ("Vars reused when mode was left enabled") + (with-current-buffer "#chan" + (erc-cmd-RECONNECT) + (funcall expect 20 "welcome") + (should (eql erc-scenarios-base-local-modules--local-var 101)) + (erc--phony-sblm--mode -1)) + + (with-current-buffer "FooNet" + (funcall expect 10 "User modes for tester") + (should (eql erc-scenarios-base-local-modules--local-var 1)))) + + (with-current-buffer "FooNet" (funcall expect 20 "terminated")) + + (ert-info ("Local binding gone when mode disabled in target") + (with-current-buffer "#chan" + (erc-cmd-RECONNECT) + (funcall expect 20 "welcome") + (should-not erc--phony-sblm--mode) + (should-not erc-scenarios-base-local-modules--local-var)) + + ;; But value retained in server buffer, where mode is active. + (with-current-buffer "FooNet" + (funcall expect 10 "User modes for tester") + (should (eql erc-scenarios-base-local-modules--local-var 1)))))) + ;;; erc-scenarios-local-modules.el ends here From 183e749270208ad7f63114ea8ab05e7612a645a1 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Fri, 13 Jan 2023 06:03:15 -0800 Subject: [PATCH 08/34] Don't preserve non-module minor modes in erc-open * lisp/erc/erc-common.el (define-erc-module): Add symbol property `erc-module' to minor modes defined as part of a module. * lisp/erc/erc.el (erc--merge-local-modes): Be more conservative when persisting local minor-mode state across ERC sessions. User and third-party modes that were not defined via `define-erc-modules' should be left alone. (erc-open): Run major-mode hooks and enable minor modes after prompt has been set up. This ensures that module-setup code can access a fully initialized `erc-input-marker'. * test/lisp/erc/erc-tests.el (erc--merge-local-modes): Add mocks for `erc-module' symbol property and a test case covering some foreign ERC mode. (define-erc-module--global, define-erc-module--local): Expect the `erc-module' symbol property to be defined for mode symbols and aliases. (Bug#60784.) --- lisp/erc/erc-common.el | 13 +++++++------ lisp/erc/erc.el | 11 +++++++---- test/lisp/erc/erc-tests.el | 37 ++++++++++++++++++++++++------------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el index 9eb4f1a9000..994555acecf 100644 --- a/lisp/erc/erc-common.el +++ b/lisp/erc/erc-common.el @@ -202,12 +202,13 @@ if ARG is omitted or nil. (,disable))) ,(erc--assemble-toggle local-p name enable mode t enable-body) ,(erc--assemble-toggle local-p name disable mode nil disable-body) - ,(when (and alias (not (eq name alias))) - `(defalias - ',(intern - (format "erc-%s-mode" - (downcase (symbol-name alias)))) - #',mode)) + ,@(and-let* ((alias) + ((not (eq name alias))) + (aname (intern (format "erc-%s-mode" + (downcase (symbol-name alias)))))) + `((defalias ',aname #',mode) + (put ',aname 'erc-module ',(erc--normalize-module-symbol name)))) + (put ',mode 'erc-module ',(erc--normalize-module-symbol name)) ;; For find-function and find-variable. (put ',mode 'definition-name ',name) (put ',enable 'definition-name ',name) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index e7f81f24ac4..7f51b7bfb2e 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1958,7 +1958,8 @@ nil." (let ((out (list (reverse new-modes)))) (pcase-dolist (`(,k . ,v) old-vars) (when (and (string-prefix-p "erc-" (symbol-name k)) - (string-suffix-p "-mode" (symbol-name k))) + (string-suffix-p "-mode" (symbol-name k)) + (get k 'erc-module)) (if v (cl-pushnew k (car out)) (setf (car out) (delq k (car out))) @@ -2082,9 +2083,7 @@ Returns the buffer for the given server or channel." (erc-determine-parameters server port nick full-name user passwd) - (save-excursion (run-mode-hooks)) - (dolist (mod (car delayed-modules)) (funcall mod +1)) - (dolist (var (cdr delayed-modules)) (set var nil)) + ;; FIXME consolidate this prompt-setup logic with the pass above. ;; set up prompt (unless continued-session @@ -2097,6 +2096,10 @@ Returns the buffer for the given server or channel." (erc-display-prompt) (goto-char (point-max))) + (save-excursion (run-mode-hooks) + (dolist (mod (car delayed-modules)) (funcall mod +1)) + (dolist (var (cdr delayed-modules)) (set var nil))) + ;; Saving log file on exit (run-hook-with-args 'erc-connect-pre-hook buffer) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 85506c3d27e..40a2d2de657 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -1251,18 +1251,28 @@ (setq calls nil))))) (ert-deftest erc--merge-local-modes () + (cl-letf (((get 'erc-b-mode 'erc-module) 'b) + ((get 'erc-c-mode 'erc-module) 'c) + ((get 'erc-d-mode 'erc-module) 'd) + ((get 'erc-e-mode 'erc-module) 'e)) - (ert-info ("No existing modes") - (let ((old '((a) (b . t))) - (new '(erc-c-mode erc-d-mode))) - (should (equal (erc--merge-local-modes new old) - '((erc-c-mode erc-d-mode)))))) + (ert-info ("No existing modes") + (let ((old '((a) (b . t))) + (new '(erc-c-mode erc-d-mode))) + (should (equal (erc--merge-local-modes new old) + '((erc-c-mode erc-d-mode)))))) - (ert-info ("Active existing added, inactive existing removed, deduped") - (let ((old '((a) (erc-b-mode) (c . t) (erc-d-mode . t) (erc-e-mode . t))) - (new '(erc-b-mode erc-d-mode))) - (should (equal (erc--merge-local-modes new old) - '((erc-d-mode erc-e-mode) . (erc-b-mode))))))) + (ert-info ("Active existing added, inactive existing removed, deduped") + (let ((old '((a) (erc-b-mode) (c . t) (erc-d-mode . t) (erc-e-mode . t))) + (new '(erc-b-mode erc-d-mode))) + (should (equal (erc--merge-local-modes new old) + '((erc-d-mode erc-e-mode) . (erc-b-mode)))))) + + (ert-info ("Non-module erc-prefixed mode ignored") + (let ((old '((erc-b-mode) (erc-f-mode . t) (erc-d-mode . t))) + (new '(erc-b-mode))) + (should (equal (erc--merge-local-modes new old) + '((erc-d-mode) . (erc-b-mode)))))))) (ert-deftest define-erc-module--global () (let ((global-module '(define-erc-module mname malias @@ -1300,13 +1310,15 @@ Some docstring" (ignore c) (ignore d)) (defalias 'erc-malias-mode #'erc-mname-mode) + (put 'erc-malias-mode 'erc-module 'mname) + (put 'erc-mname-mode 'erc-module 'mname) (put 'erc-mname-mode 'definition-name 'mname) (put 'erc-mname-enable 'definition-name 'mname) (put 'erc-mname-disable 'definition-name 'mname)))))) (ert-deftest define-erc-module--local () - (let* ((global-module '(define-erc-module mname malias + (let* ((global-module '(define-erc-module mname nil ; no alias "Some docstring" ((ignore a) (ignore b)) ((ignore c) (ignore d)) @@ -1353,8 +1365,7 @@ When called interactively, do so in all buffers for the current connection." (setq erc-mname-mode nil) (ignore c) (ignore d)))) - (defalias 'erc-malias-mode #'erc-mname-mode) - + (put 'erc-mname-mode 'erc-module 'mname) (put 'erc-mname-mode 'definition-name 'mname) (put 'erc-mname-enable 'definition-name 'mname) (put 'erc-mname-disable 'definition-name 'mname)))))) From 56d69c2fc4782dc23bd79ddcbccfbae9b263ecac Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Tue, 17 Jan 2023 06:26:34 -0800 Subject: [PATCH 09/34] ; Relax timeouts for failing ERC test * test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld: Bump timeouts to 10 seconds. * test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld: Bump timeouts to 10 seconds. --- .../erc/resources/base/netid/bouncer/barnet-again.eld | 10 +++++----- .../erc/resources/base/netid/bouncer/foonet-again.eld | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld index e2fe1430283..a270c743d90 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- ((pass 10 "PASS :barnet:changeme")) -((nick 3 "NICK tester")) -((user 3 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") (0 ":irc.barnet.org 003 tester :This server was created Wed, 12 May 2021 07:41:08 UTC") @@ -17,7 +17,7 @@ (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") (0 ":irc.barnet.org 422 tester :MOTD File is missing")) -((mode-user 10.2 "MODE tester +i") +((mode-user 10 "MODE tester +i") ;; No mode answer ^ (0 ":tester!~u@xrir8fpe4d7ak.irc JOIN #chan") @@ -36,9 +36,9 @@ (0 ":irc.znc.in 306 tester :You have been marked as being away") (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) -((~join 3 "JOIN #chan")) +((~join 10 "JOIN #chan")) -((mode 5 "MODE #chan") +((mode 10 "MODE #chan") (0 ":irc.barnet.org 324 tester #chan +nt") (0 ":irc.barnet.org 329 tester #chan 1620805269") (0.1 ":joe!~u@svpn88yjcdj42.irc PRIVMSG #chan :mike: But, in defense, by mercy, 'tis most just.") diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld index bf8712305a4..a8c352daaa7 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- ((pass 10 "PASS :foonet:changeme")) -((nick 3 "NICK tester")) -((user 3 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") (0 ":irc.foonet.org 003 tester :This server was created Wed, 12 May 2021 07:41:09 UTC") @@ -17,7 +17,7 @@ (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") (0 ":irc.foonet.org 422 tester :MOTD File is missing")) -((mode-user 10.2 "MODE tester +i") +((mode-user 10 "MODE tester +i") ;; No mode answer ^ (0 ":tester!~u@nvfhxvqm92rm6.irc JOIN #chan") (0 ":irc.foonet.org 353 tester = #chan :alice @bob tester") @@ -36,9 +36,9 @@ (0 ":irc.foonet.org NOTICE tester :[07:00:32] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) -((~join 3 "JOIN #chan")) +((~join 10 "JOIN #chan")) -((mode 8 "MODE #chan") +((mode 10 "MODE #chan") (0 ":irc.foonet.org 324 tester #chan +nt") (0 ":irc.foonet.org 329 tester #chan 1620805271") (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: Grows, lives, and dies, in single blessedness.") From dbac923b9df97706d3944c21edfc9117b408d80c Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Tue, 17 Jan 2023 18:15:45 +0000 Subject: [PATCH 10/34] CC Mode: On removal of "typedef", remove pertinent types from c-found-types For this purpose, record the type names declared by typedef in a text property, c-typedef, on the typedef. On any change to that "typedef" or a type, remove the old identifier(s) from c-found-types. This should fix bug #59671. * lisp/progmodes/cc-defs.el (c-search-forward-non-nil-char-property): New macro. * lisp/progmodes/cc-engine.el (c-forward-decl-or-cast-1): Move the scope of identifier-start from the "inner" let form to the outer one. Amend the return value such that the middle element of the second element is now the position of the "typedef", not merely non-nil. * lisp/progmodes/cc-fonts.el (c-font-lock-declarators): Disregard the LIMIT parameter when fontifying the declarators of a typedef construct. Also in this case, set the c-typedef text property on the "typedef" to the list of declared types. Amend this list when these declared types change. (c-font-lock-single-decl): Massage the `types' argument given to c-font-lock-declarators. (c-font-lock-cut-off-declarators): Amend to work when the starting point of the fontification is inside a brace block. * lisp/progmodes/cc-mode.el (c-before-change-de-typedef) (c-after-change-de-typedef): New functions. (c-update-new-id): Replace the erroneous c-end-of-current-token with a clause containing c-forward-token-2. (c-before-change): Call c-before-change-de-typedef. (c-after-change): Call c-after-change-de-typedef. --- lisp/progmodes/cc-defs.el | 22 +++++++++++++ lisp/progmodes/cc-engine.el | 40 ++++++++++++++++------- lisp/progmodes/cc-fonts.el | 64 +++++++++++++++++++++++++++++-------- lisp/progmodes/cc-mode.el | 40 ++++++++++++++++++++++- 4 files changed, 140 insertions(+), 26 deletions(-) diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el index 493035d38b4..bdbc03e7c94 100644 --- a/lisp/progmodes/cc-defs.el +++ b/lisp/progmodes/cc-defs.el @@ -1361,6 +1361,28 @@ nil; point is then left undefined." (search-forward-regexp "\\(\n\\|.\\)") ; to set the match-data. (point)))) +(defmacro c-search-forward-non-nil-char-property (property &optional limit) + "Search forward for a text-property PROPERTY value non-nil. +LIMIT bounds the search. + +Leave point just after the character. The match data remain +unchanged. Return the value of PROPERTY. If a non-nil value +isn't found, return nil; point is then left undefined." + (declare (debug t)) + `(let* ((-limit- (or ,limit (point-max))) + (value (c-get-char-property (point) ,property))) + (cond + ((>= (point) -limit-) + nil) + (value + (forward-char) + value) + (t (let ((place (c-next-single-property-change + (point) ,property nil -limit-))) + (when place + (goto-char (1+ place)) + (c-get-char-property place ,property))))))) + (defmacro c-search-backward-char-property (property value &optional limit) "Search backward for a text-property PROPERTY having value VALUE. LIMIT bounds the search. The comparison is done with `equal'. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 45d90ea2431..3fa407dd338 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -142,6 +142,10 @@ ;; Put on the brace which introduces a brace list and on the commas ;; which separate the elements within it. ;; +;; 'c-typedef This property is applied to the first character of a +;; "typedef" keyword. It's value is a list of the identifiers that +;; the "typedef" declares as types. +;; ;; 'c-<>-c-types-set ;; This property is set on an opening angle bracket, and indicates that ;; any "," separators within the template/generic expression have been @@ -10024,10 +10028,10 @@ This function might do hidden buffer changes." ;; an identifier instead. (declare (debug nil)) `(progn + (setq identifier-start type-start) ,(unless short ;; These identifiers are bound only in the inner let. '(setq identifier-type at-type - identifier-start type-start got-parens nil got-identifier t got-suffix t @@ -10102,10 +10106,11 @@ This function might do hidden buffer changes." ;; The second element of the return value is non-nil when something ;; indicating the identifier is a type occurs in the declaration. ;; Specifically it is nil, or a three element list (A B C) where C is t - ;; when context is '<> and the "identifier" is a found type, B is t when a - ;; `c-typedef-kwds' ("typedef") is present, and A is t when some other - ;; `c-typedef-decl-kwds' (e.g. class, struct, enum) specifier is present. - ;; I.e., (some of) the declared identifier(s) are types. + ;; when context is '<> and the "identifier" is a found type, B is the + ;; position of the `c-typedef-kwds' keyword ("typedef") when such is + ;; present, and A is t when some other `c-typedef-decl-kwds' (e.g. class, + ;; struct, enum) specifier is present. I.e., (some of) the declared + ;; identifier(s) are types. ;; ;; The third element of the return value is non-nil when the declaration ;; parsed might be an expression. The fourth element is the position of @@ -10173,6 +10178,9 @@ This function might do hidden buffer changes." ;; `c-decl-hangon-kwds' and their associated clauses that ;; occurs after the type. id-start + ;; The earlier value of `type-start' if we've shifted the type + ;; backwards. + identifier-start ;; These store `at-type', `type-start' and `id-start' of the ;; identifier before the one in those variables. The previous ;; identifier might turn out to be the real type in a @@ -10183,7 +10191,8 @@ This function might do hidden buffer changes." ;; Set if we've found a specifier (apart from "typedef") that makes ;; the defined identifier(s) types. at-type-decl - ;; Set if we've a "typedef" keyword. + ;; If we've a "typedef" keyword (?or similar), the buffer position of + ;; its first character. at-typedef ;; Set if `context' is '<> and the identifier is definitely a type, or ;; has already been recorded as a found type. @@ -10266,7 +10275,7 @@ This function might do hidden buffer changes." (looking-at "@[A-Za-z0-9]+"))) (save-match-data (if (looking-at c-typedef-key) - (setq at-typedef t))) + (setq at-typedef (point)))) (setq kwd-sym (c-keyword-sym (match-string 1))) (save-excursion (c-forward-keyword-clause 1) @@ -10486,9 +10495,9 @@ This function might do hidden buffer changes." ;; True if we've parsed the type decl to a token that is ;; known to end declarations in this context. at-decl-end - ;; The earlier values of `at-type' and `type-start' if we've - ;; shifted the type backwards. - identifier-type identifier-start + ;; The earlier value of `at-type' if we've shifted the type + ;; backwards. + identifier-type ;; If `c-parse-and-markup-<>-arglists' is set we need to ;; turn it off during the name skipping below to avoid ;; getting `c-type' properties that might be bogus. That @@ -10530,6 +10539,10 @@ This function might do hidden buffer changes." (progn (setq got-identifier nil) t) ;; It turned out to be the real identifier, ;; so stop. + (save-excursion + (c-backward-syntactic-ws) + (c-simple-skip-symbol-backward) + (setq identifier-start (point))) nil)) t)) @@ -10555,6 +10568,10 @@ This function might do hidden buffer changes." (and (looking-at c-identifier-start) (setq pos (point)) (setq got-identifier (c-forward-name)) + (save-excursion + (c-backward-syntactic-ws) + (c-simple-skip-symbol-backward) + (setq identifier-start (point))) (setq name-start pos)) (when (looking-at "[0-9]") (setq got-number t)) ; We probably have an arithmetic expression. @@ -10573,7 +10590,8 @@ This function might do hidden buffer changes." (setq at-type nil name-start type-start id-start type-start - got-identifier t))) + got-identifier t) + (setq identifier-start type-start))) ;; Skip over type decl suffix operators and trailing noise macros. (while diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 4dcc3e0ade9..405ffb9f1f3 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1109,10 +1109,12 @@ casts and declarations are fontified. Used on level 2 and higher." ;; additionally, mark the commas with c-type property 'c-decl-id-start or ;; 'c-decl-type-start (according to TYPES). Stop at LIMIT. ;; - ;; If TYPES is t, fontify all identifiers as types, if it is nil fontify as - ;; either variables or functions, otherwise TYPES is a face to use. If - ;; NOT-TOP is non-nil, we are not at the top-level ("top-level" includes - ;; being directly inside a class or namespace, etc.). + ;; If TYPES is t, fontify all identifiers as types; if it is a number, a + ;; buffer position, additionally set the `c-deftype' text property on the + ;; keyword at that position; if it is nil fontify as either variables or + ;; functions, otherwise TYPES is a face to use. If NOT-TOP is non-nil, we + ;; are not at the top-level ("top-level" includes being directly inside a + ;; class or namespace, etc.). ;; ;; TEMPLATE-CLASS is non-nil when the declaration is in template delimiters ;; and was introduced by, e.g. "typename" or "class", such that if there is @@ -1129,17 +1131,28 @@ casts and declarations are fontified. Used on level 2 and higher." ;;(message "c-font-lock-declarators from %s to %s" (point) limit) (c-fontify-types-and-refs () + ;; If we're altering the declarators in a typedef, we need to scan ALL of + ;; them because of the way we check for changes. + (let ((c-do-decl-limit (if (numberp types) (point-max) limit)) + decl-ids) (c-do-declarators - limit list not-top - (cond ((eq types t) 'c-decl-type-start) + c-do-decl-limit + list not-top + (cond ((or (numberp types) + (eq types t)) + 'c-decl-type-start) ((null types) 'c-decl-id-start)) (lambda (id-start id-end end-pos _not-top is-function init-char) - (if (eq types t) + (if (or (numberp types) + (eq types t)) (when id-start ;; Register and fontify the identifier as a type. (let ((c-promote-possible-types t)) (goto-char id-start) - (c-forward-type))) + (c-forward-type)) + (when (numberp types) + (push (buffer-substring-no-properties id-start id-end) + decl-ids))) (when id-start (goto-char id-start) (when c-opt-identifier-prefix-key @@ -1147,7 +1160,7 @@ casts and declarations are fontified. Used on level 2 and higher." (eq (match-end 1) id-end)) (while (and (< (point) id-end) (re-search-forward c-opt-identifier-prefix-key id-end t)) - (c-forward-syntactic-ws limit)))) + (c-forward-syntactic-ws c-do-decl-limit)))) ;; Only apply the face when the text doesn't have one yet. ;; Exception: The "" in C++'s operator"" will already wrongly have ;; string face. @@ -1164,7 +1177,7 @@ casts and declarations are fontified. Used on level 2 and higher." (equal (buffer-substring-no-properties id-start id-end) "\"\"")) (goto-char id-end) - (c-forward-syntactic-ws limit) + (c-forward-syntactic-ws c-do-decl-limit) (when (c-on-identifier) (c-put-font-lock-face (point) @@ -1174,10 +1187,21 @@ casts and declarations are fontified. Used on level 2 and higher." (eq init-char ?=) ; C++ ""? (progn (goto-char end-pos) - (c-forward-token-2 1 nil limit) ; Over "=" + (c-forward-token-2 1 nil c-do-decl-limit) ; Over "=" (let ((c-promote-possible-types t)) (c-forward-type t))))) accept-anon) ; Last argument to c-do-declarators. + ;; If we've changed types declared by a "typedef", update the `c-typedef' + ;; text property. + (when (numberp types) + (let* ((old-decl-ids (c-get-char-property types 'c-typedef)) + (old-types (c--set-difference old-decl-ids decl-ids :test #'equal)) + (new-types (c--set-difference decl-ids old-decl-ids :test #'equal))) + (dolist (type old-types) + (c-unfind-type type)) + ;; The new types have already been added to `c-found-types', as needed. + (when (or old-types new-types) + (c-put-char-property types 'c-typedef decl-ids))))) nil)) (defun c-get-fontification-context (match-pos not-front-decl &optional toplev) @@ -1433,7 +1457,10 @@ casts and declarations are fontified. Used on level 2 and higher." (c-font-lock-declarators (min limit (point-max)) decl-list - (not (null (cadr decl-or-cast))) + (cond ((null (cadr decl-or-cast)) + nil) + ((cadr (cadr decl-or-cast))) + (t t)) (not toplev) template-class (memq context '(decl <>)))) @@ -1749,12 +1776,21 @@ casts and declarations are fontified. Used on level 2 and higher." ; speeds up lisp.h tremendously. (save-excursion (when (not (c-back-over-member-initializers decl-search-lim)) + (setq paren-state (c-parse-state)) (unless (or (eobp) (looking-at "\\s(\\|\\s)")) (forward-char)) (c-syntactic-skip-backward "^;{}" decl-search-lim t) - (when (eq (char-before) ?}) - (c-go-list-backward) ; brace block of struct, etc.? + ;; Do we have the brace block of a struct, etc.? + (when (cond + ((and (consp (car paren-state)) + (eq (char-before) ?})) + (goto-char (caar paren-state)) + t) + ((and (numberp (car paren-state)) + (eq (char-after (car paren-state)) ?{)) + (goto-char (car paren-state)) + t)) (c-syntactic-skip-backward "^;{}" decl-search-lim t)) (when (or (bobp) (memq (char-before) '(?\; ?{ ?}))) diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index b04ed7584c4..330202bb5f9 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -2077,6 +2077,37 @@ with // and /*, not more generic line and block comments." (not (eobp)))) (forward-char)))))) +(defun c-before-change-de-typedef (beg end) + ;; For each "typedef" starting in (BEG END), remove the defined types from + ;; c-found-types + (let (prop) + (save-excursion + (goto-char beg) + (while (and (< (point) end) + (setq prop (c-search-forward-non-nil-char-property + 'c-typedef))) + (dolist (type prop) + (c-unfind-type type)))))) + +(defun c-after-change-de-typedef (beg end _old-len) + ;; For each former "typedef" in (BEG END), remove the defined types from + ;; those which are no longer typedefs. + (let (prop) + (save-excursion + (goto-char beg) + (c-backward-token-2 + 1 nil (- (point) 20)) + (while (and (< (point) end) + (setq prop (c-search-forward-non-nil-char-property + 'c-typedef end))) + (backward-char) + (when (or (not (looking-at c-typedef-key)) + (<= (match-end 1) beg)) + (dolist (type prop) + (c-unfind-type type)) + (c-clear-char-property (point) 'c-typedef)) + (forward-char))))) + (defun c-update-new-id (end) ;; Note the bounds of any identifier that END is in or just after, in ;; `c-new-id-start' and `c-new-id-end'. Otherwise set these variables to @@ -2086,7 +2117,9 @@ with // and /*, not more generic line and block comments." (let ((id-beg (c-on-identifier))) (setq c-new-id-start id-beg c-new-id-end (and id-beg - (progn (c-end-of-current-token) (point))) + (progn (goto-char id-beg) + (c-forward-token-2) + (point))) c-new-id-is-type nil)))) (defun c-post-command () @@ -2215,6 +2248,10 @@ with // and /*, not more generic line and block comments." term-pos) (buffer-substring-no-properties beg end))))))) + ;; If we're about to delete "typedef"s, clear the identifiers from + ;; `c-found-types'. + (c-before-change-de-typedef beg end) + (if c-get-state-before-change-functions (mapc (lambda (fn) (funcall fn beg end)) @@ -2306,6 +2343,7 @@ with // and /*, not more generic line and block comments." (c-update-new-id end) (c-trim-found-types beg end old-len) ; maybe we don't ; need all of these. + (c-after-change-de-typedef beg end old-len) (c-invalidate-sws-region-after beg end old-len) ;; (c-invalidate-state-cache beg) ; moved to ;; `c-before-change'. From c4f0b6ccea128d52a7b4a9ddc1e81dcf13bb25ea Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sun, 15 Jan 2023 18:35:31 -0800 Subject: [PATCH 11/34] Add more detail about how to invoke Eshell commands * doc/misc/eshell.texi (Variables): Move footnote explaining "REPL" from here... (Top): ... to its first use here. (Commands): Move explanation about kernel functions to here. (Invocation): Describe command form and Lisp form. Fix documentation about priority of commands in command form. (Arguments): Add a cross reference to the Invocation node. --- doc/misc/eshell.texi | 136 +++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 36 deletions(-) diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 4ad1c3f74f6..1789cded9d3 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -64,10 +64,11 @@ modify this GNU manual.'' Eshell is a shell-like command interpreter implemented in Emacs Lisp. It invokes no external processes except for those requested by the -user. It is intended to be an alternative to the IELM (@pxref{Lisp Interaction, Emacs Lisp Interaction, , emacs, The Emacs Editor}) -REPL for Emacs @emph{and} with an interface similar to command shells -such as @command{bash}, @command{zsh}, @command{rc}, or -@command{4dos}. +user. It is intended to be an alternative to the IELM (@pxref{Lisp +Interaction, Emacs Lisp Interaction, , emacs, The Emacs Editor}) +REPL@footnote{Short for ``Read-Eval-Print Loop''.} for Emacs +@emph{and} with an interface similar to command shells such as +@command{bash}, @command{zsh}, @command{rc}, or @command{4dos}. @c This manual is updated to release 2.4 of Eshell. @insertcopying @@ -193,6 +194,13 @@ In a command shell, everything is done by invoking commands. This chapter covers command invocations in Eshell, including the command history and invoking commands in a script file. +Unlike regular system shells, Eshell never invokes kernel functions +directly, such as @code{exec(3)}. Instead, it uses the Lisp functions +available in the Emacs Lisp library. It does this by transforming the +input line into a callable Lisp form.@footnote{To see the Lisp form +that will be invoked, type this as the Eshell prompt: +@kbd{eshell-parse-command 'echo hello'}} + @menu * Invocation:: * Arguments:: @@ -207,23 +215,16 @@ history and invoking commands in a script file. @node Invocation @section Invocation -Unlike regular system shells, Eshell never invokes kernel functions -directly, such as @code{exec(3)}. Instead, it uses the Lisp functions -available in the Emacs Lisp library. It does this by transforming the -input line into a callable Lisp form.@footnote{To see the Lisp form that will be invoked, type: @samp{eshell-parse-command "echo hello"}} +Eshell is both a command shell and an Emacs Lisp @acronym{REPL}. As a +result, you can invoke commands in two different ways: in @dfn{command +form} or in @dfn{lisp form}. -The command can be either an Elisp function or an external command. -Eshell looks first for an alias (@pxref{Aliases}) with the same name as the -command, then a built-in (@pxref{Built-ins}) or a function with the -same name; if there is no match, it then tries to execute it as an -external command. - -The semicolon (@code{;}) can be used to separate multiple command -invocations on a single line. You can also separate commands with -@code{&&} or @code{||}. When using @code{&&}, Eshell will execute the -second command only if the first succeeds (i.e.@: has an exit -status of 0); with @code{||}, Eshell will execute the second command -only if the first fails. +You can use the semicolon (@code{;}) to separate multiple command +invocations on a single line, executing each in turn. You can also +separate commands with @code{&&} or @code{||}. When using @code{&&}, +Eshell will execute the second command only if the first succeeds +(i.e.@: has an exit status of 0); with @code{||}, Eshell will execute +the second command only if the first fails. A command invocation followed by an ampersand (@code{&}) will be run in the background. Eshell has no job control, so you can not suspend @@ -232,12 +233,80 @@ the foreground. That said, background processes invoked from Eshell can be controlled the same way as any other background process in Emacs. +@subsection Command form +Command form looks much the same as in other shells. A command +consists of arguments separated by spaces; the first argument is the +command to run, with any subsequent arguments being passed to that +command. + +@example +~ $ echo hello +hello +@end example + +@cindex order of looking for commands +@cindex command lookup order +The command can be either an Elisp function or an external command. +Eshell looks for the command in the following order: + +@enumerate +@item +As a command alias (@pxref{Aliases}) + +@item +As a built-in command (@pxref{Built-ins}) + +@item +As an external program + +@item +As an ordinary Lisp function +@end enumerate + +@vindex eshell-prefer-lisp-functions +If you would prefer to use ordinary Lisp functions over external +programs, set the option @code{eshell-prefer-lisp-functions} to +@code{t}. This will swap the lookup order of the last two items. + +You can also group command forms together into a subcommand with curly +braces (@code{@{@}}). This lets you use the output of a subcommand as +an argument to another command, or within control flow statements +(@pxref{Control Flow}). + +@example +~ $ echo @{echo hello; echo there@} +hellothere +@end example + +@subsection Lisp form +Lisp form looks like ordinary Emacs Lisp code, because that's what it +is. As a result, you can use any syntax normally available to an +Emacs Lisp program (@pxref{Top, , , elisp, The Emacs Lisp Reference +Manual}). + +@example +~ $ (format "hello, %s" user-login-name) +hello, user +@end example + +In addition, you can @emph{combine} command forms and Lisp forms +together into single statements, letting you use whatever form is the +most convenient for expressing your intentions. + +@example +~ $ ls *.patch > (format-time-string "%F.log") +@end example + +This command writes a list of all files matching the glob pattern +@code{*.patch} (@pxref{Globbing}) to a file named +@code{@var{current-date}.log} (@pxref{Redirection}). + @node Arguments @section Arguments -Ordinarily, command arguments are parsed by Eshell as either strings +Ordinarily, Eshell parses arguments in command form as either strings or numbers, depending on what the parser thinks they look like. To -specify an argument of some other data type, you can use an -@ref{Dollars Expansion, Elisp expression}: +specify an argument of some other data type, you can use a Lisp form +(@pxref{Invocation}): @example ~ $ echo (list 1 2 3) @@ -354,10 +423,6 @@ eshell/sudo is a compiled Lisp function in `em-tramp.el'. sudo is an alias, defined as "*sudo $*" @end example -@vindex eshell-prefer-lisp-functions -If you would prefer to use the built-in commands instead of the external -commands, set @code{eshell-prefer-lisp-functions} to @code{t}. - Some of the built-in commands have different behavior from their external counterparts, and some have no external counterpart. Most of these will print a usage message when given the @code{--help} option. @@ -923,15 +988,14 @@ For example, you could handle a subset of the options for the @node Variables @section Variables @vindex eshell-prefer-lisp-variables -Since Eshell is a combination of an Emacs @acronym{REPL}@footnote{ -Short for ``Read-Eval-Print Loop''. -} and a command shell, it can refer to variables from two different -sources: ordinary Emacs Lisp variables, as well as environment -variables. By default, when using a variable in Eshell, it will first -look in the list of built-in variables, then in the list of -environment variables, and finally in the list of Lisp variables. If -you would prefer to use Lisp variables over environment variables, you -can set @code{eshell-prefer-lisp-variables} to @code{t}. +Since Eshell is a combination of an Emacs @acronym{REPL} and a command +shell, it can refer to variables from two different sources: ordinary +Emacs Lisp variables, as well as environment variables. By default, +when using a variable in Eshell, it will first look in the list of +built-in variables, then in the list of environment variables, and +finally in the list of Lisp variables. If you would prefer to use +Lisp variables over environment variables, you can set +@code{eshell-prefer-lisp-variables} to @code{t}. You can set variables in a few different ways. To set a Lisp variable, you can use the command @samp{setq @var{name} @var{value}}, From 9ed9ff4690a8b26ac9729a66aa22f2e14856cd0c Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Tue, 17 Jan 2023 23:46:41 +0200 Subject: [PATCH 12/34] ruby-ts-mode: Fix the rules for hanging arrays and hashes * lisp/progmodes/ruby-ts-mode.el (ruby-ts--indent-rules): Fix the rules for hanging arrays and hashes (to line up to parent-bol instead of the opening brace). * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: New file with examples. * test/lisp/progmodes/ruby-ts-mode-tests.el: Use it here. --- lisp/progmodes/ruby-ts-mode.el | 18 ++++----------- .../progmodes/ruby-mode-resources/ruby-ts.rb | 23 +++++++++++++++++++ test/lisp/progmodes/ruby-ts-mode-tests.el | 1 + 3 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 939c054b041..19b766ceadb 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -749,20 +749,10 @@ i.e. expr of def foo(args) = expr is returned." (not ruby-ts--same-line-hash-array-p)) grand-parent ruby-indent-level) - ((n-p-gp "}" "hash" "assignment") (ruby-ts--bol ruby-ts--grand-parent-node) 0) - ((n-p-gp nil "hash" "assignment") (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level) - ((n-p-gp "]" "array" "assignment") (ruby-ts--bol ruby-ts--grand-parent-node) 0) - ((n-p-gp nil "array" "assignment") (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level) - - ((n-p-gp "}" "hash" "argument_list") first-sibling 0) - ((n-p-gp nil "hash" "argument_list") first-sibling ruby-indent-level) - ((n-p-gp "]" "array" "argument_list") first-sibling 0) - ((n-p-gp nil "array" "argument_list") first-sibling ruby-indent-level) - - ((match "}" "hash") first-sibling 0) - ((parent-is "hash") first-sibling ruby-indent-level) - ((match "]" "array") first-sibling 0) - ((parent-is "array") first-sibling ruby-indent-level) + ((match "}" "hash") parent-bol 0) + ((parent-is "hash") parent-bol ruby-indent-level) + ((match "]" "array") parent-bol 0) + ((parent-is "array") parent-bol ruby-indent-level) ;; If the previous method isn't finished yet, this will get ;; the next method indented properly. diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb new file mode 100644 index 00000000000..6b4107ef97b --- /dev/null +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -0,0 +1,23 @@ +variable = foo( + [ + qwe + ], [ + rty + ], { + a: 3 + } +) + +tee = [ + qwe +] + +qux = [1, + 2] + +att = {a: 1, + b: 2} + +# Local Variables: +# mode: ruby-ts +# End: diff --git a/test/lisp/progmodes/ruby-ts-mode-tests.el b/test/lisp/progmodes/ruby-ts-mode-tests.el index eaf6367a306..d7b6258385b 100644 --- a/test/lisp/progmodes/ruby-ts-mode-tests.el +++ b/test/lisp/progmodes/ruby-ts-mode-tests.el @@ -250,6 +250,7 @@ The whitespace before and including \"|\" on each line is removed." (should (equal (buffer-string) orig)))) (kill-buffer buf))))) +(ruby-ts-deftest-indent "ruby-ts.rb") (ruby-ts-deftest-indent "ruby-method-params-indent.rb") (ruby-ts-deftest-indent "ruby-block-indent.rb") From 5e2e68a0c2d7d4e06747ea9fc6112dae8d5e32b6 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Wed, 18 Jan 2023 00:41:14 +0200 Subject: [PATCH 13/34] ruby-ts-mode: Fix indent inside parenthesized_expr and else/end after unless * lisp/progmodes/ruby-ts-mode.el (ruby-ts--indent-rules): Fix indentation for parenthesized_expression and else/end after 'unless'. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: New examples. --- lisp/progmodes/ruby-ts-mode.el | 7 +++++-- test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 19b766ceadb..853c869a319 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -587,11 +587,11 @@ i.e. expr of def foo(args) = expr is returned." ;; ;; I'm using very restrictive patterns hoping to reduce rules ;; triggering unintentionally. - ((match "else" "if") + ((match "else" "if\\|unless") (ruby-ts--align-keywords ruby-ts--parent-node) 0) ((match "elsif" "if") (ruby-ts--align-keywords ruby-ts--parent-node) 0) - ((match "end" "if") + ((match "end" "if\\|unless") (ruby-ts--align-keywords ruby-ts--parent-node) 0) ((n-p-gp nil "then\\|else\\|elsif" "if\\|unless") (ruby-ts--align-keywords ruby-ts--grand-parent-node) ruby-indent-level) @@ -754,6 +754,9 @@ i.e. expr of def foo(args) = expr is returned." ((match "]" "array") parent-bol 0) ((parent-is "array") parent-bol ruby-indent-level) + ((match ")" "parenthesized_statements") parent-bol 0) + ((parent-is "parenthesized_statements") parent-bol ruby-indent-level) + ;; If the previous method isn't finished yet, this will get ;; the next method indented properly. ((n-p-gp ,ruby-ts--method-regex "body_statement" ,ruby-ts--class-or-module-regex) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index 6b4107ef97b..7de94ceadec 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -18,6 +18,16 @@ qux = [1, att = {a: 1, b: 2} +a = 1 ? 2 :( + 2 + 3 +) + +unless bismark + sink += 12 +else + dog = 99 +end + # Local Variables: # mode: ruby-ts # End: From ac5516bd7d568bbcea4fe46273d4f44c891a71ae Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Wed, 18 Jan 2023 00:55:09 +0200 Subject: [PATCH 14/34] ruby-ts-mode: Fix/change indentation of a continuation method call * lisp/progmodes/ruby-ts-mode.el (ruby-ts--indent-rules): Fix/change indentation of a continuation method call. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: New examples. --- lisp/progmodes/ruby-ts-mode.el | 4 ++-- test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 853c869a319..cbf86544bed 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -697,9 +697,9 @@ i.e. expr of def foo(args) = expr is returned." ;; 2) With paren, 1st arg on next line ((and (query "(argument_list \"(\" _ @indent)") (node-is ")")) - (ruby-ts--bol ruby-ts--grand-parent-node) 0) + parent-bol 0) ((query "(argument_list \"(\" _ @indent)") - (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level) + parent-bol ruby-indent-level) ;; 3) No paren, ruby-parenless-call-arguments-indent is t ((and ruby-ts--parenless-call-arguments-indent-p (parent-is "argument_list")) first-sibling 0) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index 7de94ceadec..1a07ababc46 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -28,6 +28,19 @@ else dog = 99 end +foo1 = + subject.update( + 1 + ) + +foo2 = + subject. + update( + # Might make sense to indent this to 'subject' instead; but this + # style seems more popular. + 2 + ) + # Local Variables: # mode: ruby-ts # End: From 300ca6ac37250711b7d6484e0a870bf37e9e00cb Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Wed, 18 Jan 2023 02:40:00 +0200 Subject: [PATCH 15/34] ruby-ts-mode: Fix indent after operator or conditional Make it match ruby-mode's indentation behavior. * lisp/progmodes/ruby-ts-mode.el (ruby-ts--binary-indent-anchor): New function. (ruby-ts--indent-rules): Use it instead of a composite matcher. Add a rule for 'conditional'. (ruby-ts--assignment-ancestor, ruby-ts--is-in-condition) (ruby-ts--endless-method): Remove. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: Add examples. --- lisp/progmodes/ruby-ts-mode.el | 41 ++++++------------- .../progmodes/ruby-mode-resources/ruby-ts.rb | 13 ++++++ 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index cbf86544bed..e629ff19672 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -512,10 +512,6 @@ array or hash." (first-child (ruby-ts--first-non-comment-child parent))) (= (ruby-ts--lineno open-brace) (ruby-ts--lineno first-child)))) -(defun ruby-ts--assignment-ancestor (node &rest _) - "Return the assignment ancestor of NODE if any." - (treesit-parent-until node (ruby-ts--type-pred "\\`assignment\\'"))) - (defun ruby-ts--statement-ancestor (node &rest _) "Return the statement ancestor of NODE if any. A statement is defined as a child of a statement container where @@ -531,26 +527,6 @@ a statement container is a node that matches parent (treesit-node-parent parent))) statement)) -(defun ruby-ts--is-in-condition (node &rest _) - "Return the condition node if NODE is within a condition." - (while (and node - (not (equal "condition" (treesit-node-field-name node))) - (not (string-match-p ruby-ts--statement-container-regexp - (treesit-node-type node)))) - (setq node (treesit-node-parent node))) - (and (equal "condition" (treesit-node-field-name node)) node)) - -(defun ruby-ts--endless-method (node &rest _) - "Return the expression node if NODE is in an endless method. -i.e. expr of def foo(args) = expr is returned." - (let* ((method node)) - (while (and method - (not (string-match-p ruby-ts--method-regex (treesit-node-type method)))) - (setq method (treesit-node-parent method))) - (when method - (if (equal "=" (treesit-node-type (treesit-node-child method 3 nil))) - (treesit-node-child method 4 nil))))) - ;; ;; end of functions that can be used for queries ;; @@ -709,11 +685,10 @@ i.e. expr of def foo(args) = expr is returned." ;; Old... probably too simple ((parent-is "block_parameters") first-sibling 1) - ((and (parent-is "binary") - (or ruby-ts--assignment-ancestor - ruby-ts--is-in-condition - ruby-ts--endless-method)) - first-sibling 0) + ((parent-is "binary") + ruby-ts--binary-indent-anchor 0) + + ((parent-is "conditional") parent ruby-indent-level) ;; ruby-mode does not touch these... ((match "bare_string" "string_array") no-indent 0) @@ -807,6 +782,14 @@ i.e. expr of def foo(args) = expr is returned." (back-to-indentation) (point))))) +(defun ruby-ts--binary-indent-anchor (_node parent _bol &rest _) + (save-excursion + (goto-char (treesit-node-start parent)) + (when (string-match-p ruby-ts--statement-container-regexp + (treesit-node-type (treesit-node-parent parent))) + (forward-char ruby-indent-level)) + (point))) + (defun ruby-ts--class-or-module-p (node) "Predicate if NODE is a class or module." (string-match-p ruby-ts--class-or-module-regex (treesit-node-type node))) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index 1a07ababc46..92d62f92e52 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -41,6 +41,19 @@ foo2 = 2 ) +foo > bar && + tee < qux + +1 .. 2 && + 3 + +a = foo(j, k) - + bar_tee + +qux = foo.fee ? + bar : + tee + # Local Variables: # mode: ruby-ts # End: From 045404d1aac1a9bd4ad2a3af8db577d7f05b9d03 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Wed, 18 Jan 2023 02:55:09 +0200 Subject: [PATCH 16/34] ruby-ts-mode: Obey the option ruby-after-operator-indent * lisp/progmodes/ruby-ts-mode.el (ruby-ts--after-op-indent-p): New function. (ruby-ts--indent-rules): Use it. * test/lisp/progmodes/ruby-ts-mode-tests.el: Run indent test for ruby-after-operator-indent.rb. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: Make sure indentation vars are at their default values. --- lisp/progmodes/ruby-ts-mode.el | 7 +++++++ test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb | 3 +++ test/lisp/progmodes/ruby-ts-mode-tests.el | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index e629ff19672..7e30ece1fd7 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -685,6 +685,10 @@ a statement container is a node that matches ;; Old... probably too simple ((parent-is "block_parameters") first-sibling 1) + ((and (not ruby-ts--after-op-indent-p) + (parent-is "binary\\|conditional")) + (ruby-ts--bol ruby-ts--statement-ancestor) ruby-indent-level) + ((parent-is "binary") ruby-ts--binary-indent-anchor 0) @@ -790,6 +794,9 @@ a statement container is a node that matches (forward-char ruby-indent-level)) (point))) +(defun ruby-ts--after-op-indent-p (&rest _) + ruby-after-operator-indent) + (defun ruby-ts--class-or-module-p (node) "Predicate if NODE is a class or module." (string-match-p ruby-ts--class-or-module-regex (treesit-node-type node))) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index 92d62f92e52..be98465881f 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -56,4 +56,7 @@ qux = foo.fee ? # Local Variables: # mode: ruby-ts +# ruby-after-operator-indent: t +# ruby-block-indent: t +# ruby-method-params-indent: t # End: diff --git a/test/lisp/progmodes/ruby-ts-mode-tests.el b/test/lisp/progmodes/ruby-ts-mode-tests.el index d7b6258385b..1d686a2ac53 100644 --- a/test/lisp/progmodes/ruby-ts-mode-tests.el +++ b/test/lisp/progmodes/ruby-ts-mode-tests.el @@ -251,8 +251,9 @@ The whitespace before and including \"|\" on each line is removed." (kill-buffer buf))))) (ruby-ts-deftest-indent "ruby-ts.rb") -(ruby-ts-deftest-indent "ruby-method-params-indent.rb") +(ruby-ts-deftest-indent "ruby-after-operator-indent.rb") (ruby-ts-deftest-indent "ruby-block-indent.rb") +(ruby-ts-deftest-indent "ruby-method-params-indent.rb") (provide 'ruby-ts-mode-tests) From 343b9b3dfe370a7e65d499b499621f87e722ea71 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Wed, 18 Jan 2023 03:21:32 +0200 Subject: [PATCH 17/34] ruby-ts-mode: Obey the option ruby-method-call-indent * lisp/progmodes/ruby-ts-mode.el (ruby-ts--method-call-indent-p): New function. (ruby-ts--indent-rules): Use it. * test/lisp/progmodes/ruby-ts-mode-tests.el: Run indent test for ruby-method-call-indent.rb. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: Add explicit value for ruby-method-call-indent. --- lisp/progmodes/ruby-ts-mode.el | 10 ++++++++++ test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb | 1 + test/lisp/progmodes/ruby-ts-mode-tests.el | 1 + 3 files changed, 12 insertions(+) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 7e30ece1fd7..27e5d002881 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -640,6 +640,13 @@ a statement container is a node that matches ;; else the second query aligns ;; `ruby-indent-level' spaces in from the parent. ((and ruby-ts--align-chain-p (match "\\." "call")) ruby-ts--align-chain 0) + ;; Obery ruby-method-call-indent, whether the dot is on + ;; this line or the previous line. + ((and (not ruby-ts--method-call-indent-p) + (or + (match "\\." "call") + (query "(call \".\" (identifier) @indent)"))) + parent 0) ((match "\\." "call") parent ruby-indent-level) ;; ruby-indent-after-block-in-continued-expression @@ -797,6 +804,9 @@ a statement container is a node that matches (defun ruby-ts--after-op-indent-p (&rest _) ruby-after-operator-indent) +(defun ruby-ts--method-call-indent-p (&rest _) + ruby-method-call-indent) + (defun ruby-ts--class-or-module-p (node) "Predicate if NODE is a class or module." (string-match-p ruby-ts--class-or-module-regex (treesit-node-type node))) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index be98465881f..9959de4fe71 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -58,5 +58,6 @@ qux = foo.fee ? # mode: ruby-ts # ruby-after-operator-indent: t # ruby-block-indent: t +# ruby-method-call-indent: t # ruby-method-params-indent: t # End: diff --git a/test/lisp/progmodes/ruby-ts-mode-tests.el b/test/lisp/progmodes/ruby-ts-mode-tests.el index 1d686a2ac53..1d2cfbfb901 100644 --- a/test/lisp/progmodes/ruby-ts-mode-tests.el +++ b/test/lisp/progmodes/ruby-ts-mode-tests.el @@ -253,6 +253,7 @@ The whitespace before and including \"|\" on each line is removed." (ruby-ts-deftest-indent "ruby-ts.rb") (ruby-ts-deftest-indent "ruby-after-operator-indent.rb") (ruby-ts-deftest-indent "ruby-block-indent.rb") +(ruby-ts-deftest-indent "ruby-method-call-indent.rb") (ruby-ts-deftest-indent "ruby-method-params-indent.rb") (provide 'ruby-ts-mode-tests) From bdd82fa7977570160523cdce907f1b5d2c786359 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Tue, 17 Jan 2023 09:37:04 -0800 Subject: [PATCH 18/34] ; * src/treesit.c: Remove unused boilerplate. These two functions are not used after 7c61a304104. --- src/treesit.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/treesit.c b/src/treesit.c index 644d323d5cb..917db582676 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -42,8 +42,6 @@ along with GNU Emacs. If not, see . */ #undef ts_node_end_byte #undef ts_node_eq #undef ts_node_field_name_for_child -#undef ts_node_first_child_for_byte -#undef ts_node_first_named_child_for_byte #undef ts_node_has_error #undef ts_node_is_extra #undef ts_node_is_missing @@ -99,8 +97,6 @@ DEF_DLL_FN (TSNode, ts_node_descendant_for_byte_range, DEF_DLL_FN (uint32_t, ts_node_end_byte, (TSNode)); DEF_DLL_FN (bool, ts_node_eq, (TSNode, TSNode)); DEF_DLL_FN (const char *, ts_node_field_name_for_child, (TSNode, uint32_t)); -DEF_DLL_FN (TSNode, ts_node_first_child_for_byte, (TSNode, uint32_t)); -DEF_DLL_FN (TSNode, ts_node_first_named_child_for_byte, (TSNode, uint32_t)); DEF_DLL_FN (bool, ts_node_has_error, (TSNode)); DEF_DLL_FN (bool, ts_node_is_extra, (TSNode)); DEF_DLL_FN (bool, ts_node_is_missing, (TSNode)); @@ -174,8 +170,6 @@ init_treesit_functions (void) LOAD_DLL_FN (library, ts_node_end_byte); LOAD_DLL_FN (library, ts_node_eq); LOAD_DLL_FN (library, ts_node_field_name_for_child); - LOAD_DLL_FN (library, ts_node_first_child_for_byte); - LOAD_DLL_FN (library, ts_node_first_named_child_for_byte); LOAD_DLL_FN (library, ts_node_has_error); LOAD_DLL_FN (library, ts_node_is_extra); LOAD_DLL_FN (library, ts_node_is_missing); @@ -232,8 +226,6 @@ init_treesit_functions (void) #define ts_node_end_byte fn_ts_node_end_byte #define ts_node_eq fn_ts_node_eq #define ts_node_field_name_for_child fn_ts_node_field_name_for_child -#define ts_node_first_child_for_byte fn_ts_node_first_child_for_byte -#define ts_node_first_named_child_for_byte fn_ts_node_first_named_child_for_byte #define ts_node_has_error fn_ts_node_has_error #define ts_node_is_extra fn_ts_node_is_extra #define ts_node_is_missing fn_ts_node_is_missing From ac3bc775b6fd934c972d9e2542f384cdc92d2754 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Tue, 17 Jan 2023 22:26:21 -0800 Subject: [PATCH 19/34] Make it harder to misactivate tree-sitter font-lock fast mode This has been brought up in bug#60691 and bug#60223. I proposed a fix by testing the size of the tree rather than measuring the query time. But after some thought, I fear that just looking at the size will give us false-negatives. So I kept the time-based activation, just added a grace count to reduce false-positives. * lisp/treesit.el: (treesit--font-lock-fast-mode-grace-count): New variable. (treesit--font-lock-notifier): Only activate fast mode after 5 offenses. --- lisp/treesit.el | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lisp/treesit.el b/lisp/treesit.el index 4c31ecb0d29..3dde304eb8b 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -905,6 +905,14 @@ This is not a general optimization and should be RARELY needed! See comments in `treesit-font-lock-fontify-region' for more detail.") +(defvar-local treesit--font-lock-fast-mode-grace-count 5 + "Grace counts before we turn on the fast mode. + +When query takes abnormally long time to execute, we turn on the +\"fast mode\", but just to be on the safe side, we only turn on +the fast mode after this number of offenses. See bug#60691, +bug#60223.") + ;; Some details worth explaining: ;; ;; 1. When we apply face to a node, we clip the face into the @@ -927,13 +935,13 @@ detail.") ;; parse it into a enormously tall tree (10k levels tall). In that ;; case querying the root node is very slow. So we try to get ;; top-level nodes and query them. This ensures that querying is fast -;; everywhere else, except for the problematic region. +;; everywhere else, except for the problematic region. (Bug#59415). ;; ;; Some other time the source file has a top-level node that contains -;; a huge number of children (say, 10k children), querying that node -;; is also very slow, so instead of getting the top-level node, we -;; recursively go down the tree to find nodes that cover the region -;; but are reasonably small. +;; a huge number of immediate children (say, 10k children), querying +;; that node is also very slow, so instead of getting the top-level +;; node, we recursively go down the tree to find nodes that cover the +;; region but are reasonably small. (Bug#59738). ;; ;; 3. It is possible to capture a node that's completely outside the ;; region between START and END: as long as the whole pattern @@ -941,8 +949,8 @@ detail.") ;; returned. If the node is outside of that region, (max node-start ;; start) and friends return bad values, so we filter them out. ;; However, we don't filter these nodes out if a function will process -;; the node, because could (and often do) fontify the relatives of the -;; captured node, not just the node itself. If we took out those +;; the node, because it could (and often do) fontify the relatives of +;; the captured node, not just the node itself. If we took out those ;; nodes author of those functions would be very confused. (defun treesit-font-lock-fontify-region (start end &optional loudly) "Fontify the region between START and END. @@ -979,9 +987,13 @@ If LOUDLY is non-nil, display some debugging information." (end-time (current-time))) ;; If for any query the query time is strangely long, ;; switch to fast mode (see comments above). - (when (> (time-to-seconds (time-subtract end-time start-time)) - 0.01) - (setq-local treesit--font-lock-fast-mode t)) + (when (and (eq 'undecided treesit--font-lock-fast-mode) + (> (time-to-seconds + (time-subtract end-time start-time)) + 0.01)) + (if (> treesit--font-lock-fast-mode-grace-count 0) + (cl-decf treesit--font-lock-fast-mode-grace-count) + (setq-local treesit--font-lock-fast-mode t))) ;; For each captured node, fontify that node. (with-silent-modifications From 24f0dfd3731dcbabab0932792462636870b7bcba Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Tue, 17 Jan 2023 22:30:09 -0800 Subject: [PATCH 20/34] Revert "Revert "Add c-or-c++-ts-mode (bug#59613)"" This reverts commit d46f7f4edcce14e6cbd8e2d7091dbabbe08defc1. Aaaactually, we need this, otherwise we can't use tree-sitter based C mode for header files. --- etc/NEWS | 5 +++++ lisp/progmodes/c-ts-mode.el | 44 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index d1ddd0194c1..95dd4a24ec3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3235,6 +3235,11 @@ programs in the C language. An optional major mode based on the tree-sitter library for editing programs in the C++ language. ++++ +*** New command 'c-or-c++-ts-mode'. +A command that automatically guesses the language of a header file, +and enables either 'c-ts-mode' or 'c++-ts-mode' accordingly. + +++ *** New major mode 'java-ts-mode'. An optional major mode based on the tree-sitter library for editing diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 89a08a6fa9c..f9f75a0e452 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -972,6 +972,50 @@ This mode is independent from the classic cc-mode.el based (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp)) (treesit-major-mode-setup))) +;; We could alternatively use parsers, but if this works well, I don't +;; see the need to change. This is copied verbatim from cc-guess.el. +(defconst c-ts-mode--c-or-c++-regexp + (eval-when-compile + (let ((id "[a-zA-Z_][a-zA-Z0-9_]*") (ws "[ \t]+") (ws-maybe "[ \t]*") + (headers '("string" "string_view" "iostream" "map" "unordered_map" + "set" "unordered_set" "vector" "tuple"))) + (concat "^" ws-maybe "\\(?:" + "using" ws "\\(?:namespace" ws + "\\|" id "::" + "\\|" id ws-maybe "=\\)" + "\\|" "\\(?:inline" ws "\\)?namespace" + "\\(:?" ws "\\(?:" id "::\\)*" id "\\)?" ws-maybe "{" + "\\|" "class" ws id + "\\(?:" ws "final" "\\)?" ws-maybe "[:{;\n]" + "\\|" "struct" ws id "\\(?:" ws "final" ws-maybe "[:{\n]" + "\\|" ws-maybe ":\\)" + "\\|" "template" ws-maybe "<.*?>" + "\\|" "#include" ws-maybe "<" (regexp-opt headers) ">" + "\\)"))) + "A regexp applied to C header files to check if they are really C++.") + +;;;###autoload +(defun c-or-c++-ts-mode () + "Analyze buffer and enable either C or C++ mode. + +Some people and projects use .h extension for C++ header files +which is also the one used for C header files. This makes +matching on file name insufficient for detecting major mode that +should be used. + +This function attempts to use file contents to determine whether +the code is C or C++ and based on that chooses whether to enable +`c-ts-mode' or `c++-ts-mode'." + (interactive) + (if (save-excursion + (save-restriction + (save-match-data ; Why `save-match-data'? + (widen) + (goto-char (point-min)) + (re-search-forward c-ts-mode--c-or-c++-regexp nil t)))) + (c++-ts-mode) + (c-ts-mode))) + (provide 'c-ts-mode) ;;; c-ts-mode.el ends here From faee7e1f1bd0167e455a0e1e5fe02e21d23fd77f Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Tue, 17 Jan 2023 22:52:22 -0800 Subject: [PATCH 21/34] ; * lisp/treesit.el (treesit-font-lock-fontify-region): Minor fix. --- lisp/treesit.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lisp/treesit.el b/lisp/treesit.el index 3dde304eb8b..34d288226fa 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -987,8 +987,7 @@ If LOUDLY is non-nil, display some debugging information." (end-time (current-time))) ;; If for any query the query time is strangely long, ;; switch to fast mode (see comments above). - (when (and (eq 'undecided treesit--font-lock-fast-mode) - (> (time-to-seconds + (when (and (> (time-to-seconds (time-subtract end-time start-time)) 0.01)) (if (> treesit--font-lock-fast-mode-grace-count 0) From 1798ff5a6636d6b34d23ab2dadb966e94cc57467 Mon Sep 17 00:00:00 2001 From: Ikumi Keita Date: Wed, 18 Jan 2023 14:28:59 +0200 Subject: [PATCH 22/34] ; Fix minor mistakes in documentation * lisp/emacs-lisp/cl-macs.el (cl-letf): Correct Info reference. * doc/lispref/strings.texi (String Conversion): Fix typo. (Bug#60926) --- doc/lispref/strings.texi | 4 ++-- lisp/emacs-lisp/cl-macs.el | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index ca18f0a9cc1..3d86a87516b 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -911,8 +911,8 @@ This function returns a new string containing one character, @end defun @defun string-to-char string - This function returns the first character in @var{string}. This -mostly identical to @code{(aref string 0)}, except that it returns 0 + This function returns the first character in @var{string}. This is +mostly identical to @w{@code{(aref string 0)}}, except that it returns 0 if the string is empty. (The value is also 0 when the first character of @var{string} is the null character, @acronym{ASCII} code 0.) This function may be eliminated in the future if it does not seem useful diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 685ab99c26e..43207ce7026 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -2810,7 +2810,7 @@ values. Note that this macro is *not* available in Common Lisp. As a special case, if `(PLACE)' is used instead of `(PLACE VALUE)', the PLACE is not modified before executing BODY. -See info node `(cl) Function Bindings' for details. +See info node `(cl) Modify Macros' for details. \(fn ((PLACE VALUE) ...) BODY...)" (declare (indent 1) (debug ((&rest [&or (symbolp form) From bd5ef3ef95e989fa7c2c4e9f24851d7e165abbdf Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Wed, 18 Jan 2023 16:01:12 +0200 Subject: [PATCH 23/34] Improve the documentation of 'auto-mode-alist' search * doc/emacs/modes.texi (Choosing Modes): Expand the description of "recursive extension stripping" using 'auto-mode-alist'. (Bug#60930) --- doc/emacs/modes.texi | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi index d0eacce0842..06f9929092c 100644 --- a/doc/emacs/modes.texi +++ b/doc/emacs/modes.texi @@ -430,10 +430,15 @@ For example, one element normally found in the list has the form mode for files whose names end in @file{.c}. (Note that @samp{\\} is needed in Lisp syntax to include a @samp{\} in the string, which must be used to suppress the special meaning of @samp{.} in regexps.) If -the element has the form @code{(@var{regexp} @var{mode-function} -@var{flag})} and @var{flag} is non-@code{nil}, then after calling -@var{mode-function}, Emacs discards the suffix that matched -@var{regexp} and searches the list again for another match. +the element has the form @w{@code{(@var{regexp} @var{mode-function} +@var{flag})}} and @var{flag} is non-@code{nil}, then after calling +@var{mode-function} (if it is non-@code{nil}), Emacs discards the +suffix that matched @var{regexp} and searches the list again for +another match. This ``recursive extension stripping'' is used for +files which have multiple extensions, and the ``outer'' extension +hides the ``inner'' one that actually specifies the right mode. For +example, backup files and GPG-encrypted files with @file{.gpg} +extension use this feature. @vindex auto-mode-case-fold On GNU/Linux and other systems with case-sensitive file names, Emacs From 9186be20aeb99d157a558a4a437bd41377bcb9b7 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Wed, 18 Jan 2023 16:19:10 +0200 Subject: [PATCH 24/34] ; Clarify doc strings of some functions in files.el * lisp/files.el (file-name-sans-extension, file-name-extension) (file-name-sans-versions): Doc fixes. (Bug#60929) --- lisp/files.el | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lisp/files.el b/lisp/files.el index a9a5baf1ba3..0d24852358e 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5059,7 +5059,8 @@ This is a separate procedure so your site-init or startup file can redefine it. If the optional argument KEEP-BACKUP-VERSION is non-nil, we do not remove backup version numbers, only true file version numbers. -See also `file-name-version-regexp'." +See `file-name-version-regexp' for what constitutes backup versions +and version strings." (let ((handler (find-file-name-handler name 'file-name-sans-versions))) (if handler (funcall handler 'file-name-sans-versions name keep-backup-version) @@ -5111,9 +5112,12 @@ the group would be preserved too." (file-attribute-group-id attributes))))))))))) (defun file-name-sans-extension (filename) - "Return FILENAME sans final \"extension\". + "Return FILENAME sans final \"extension\" and any backup version strings. The extension, in a file name, is the part that begins with the last `.', -except that a leading `.' of the file name, if there is one, doesn't count." +except that a leading `.' of the file name, if there is one, doesn't count. +Any extensions that indicate backup versions and version strings are +removed by calling `file-name-sans-versions', which see, before looking +for the \"real\" file extension." (save-match-data (let ((file (file-name-sans-versions (file-name-nondirectory filename))) directory) @@ -5127,12 +5131,14 @@ except that a leading `.' of the file name, if there is one, doesn't count." filename)))) (defun file-name-extension (filename &optional period) - "Return FILENAME's final \"extension\". + "Return FILENAME's final \"extension\" sans any backup version strings. The extension, in a file name, is the part that begins with the last `.', -excluding version numbers and backup suffixes, except that a leading `.' -of the file name, if there is one, doesn't count. +except that a leading `.' of the file name, if there is one, doesn't count. +This function calls `file-name-sans-versions', which see, to remove from +the extension it returns any parts that indicate backup versions and +version strings. Return nil for extensionless file names such as `foo'. -Return the empty string for file names such as `foo.'. +Return the empty string for file names such as `foo.' that end in a period. By default, the returned value excludes the period that starts the extension, but if the optional argument PERIOD is non-nil, the period From 6a8338a8bc85d891a42644354925ffb4a93d93a6 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Wed, 18 Jan 2023 17:22:48 +0200 Subject: [PATCH 25/34] ; Avoid byte-compiler warning in cc-fonts.el. --- lisp/progmodes/cc-fonts.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 405ffb9f1f3..c220d8d8789 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -85,6 +85,8 @@ (cc-bytecomp-defvar parse-sexp-lookup-properties) ; Emacs only. +(declare-function cl-set-difference "cl-seq" (cl-list1 cl-list2 &rest cl-keys)) + ;; Need to declare these local symbols during compilation since ;; they're referenced from lambdas in `byte-compile' calls that are ;; executed at compile time. They don't need to have the proper From dc3f85fd4b00b4f43d781eb5803a995019a57d57 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 17 Jan 2023 20:51:15 -0800 Subject: [PATCH 26/34] Use proper types for Eshell warnings * lisp/eshell/esh-var.el (eshell-get-variable): * lisp/eshell/em-basic (eshell/echo): Don't use ':warning'; that's a warning level, not a warning type. --- lisp/eshell/em-basic.el | 3 ++- lisp/eshell/esh-var.el | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lisp/eshell/em-basic.el b/lisp/eshell/em-basic.el index dfbe4db0896..bfff3bdf56e 100644 --- a/lisp/eshell/em-basic.el +++ b/lisp/eshell/em-basic.el @@ -132,7 +132,8 @@ or `eshell-printn' for display." ;; bug#27361. (when (equal output-newline '(nil)) (display-warning - :warning "To terminate with a newline, you should use -N instead.")) + '(eshell echo) + "To terminate with a newline, you should use -N instead.")) (eshell-echo args output-newline)))) (defun eshell/printnl (&rest args) diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index df1413c2de7..dfc52083acb 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el @@ -613,9 +613,10 @@ If QUOTED is non-nil, this was invoked inside double-quotes." (if (or (eq max-arity 'many) (>= max-arity 2)) (funcall target indices quoted) (display-warning - :warning (concat "Function for `eshell-variable-aliases-list' " - "entry should accept two arguments: INDICES " - "and QUOTED.'")) + '(eshell variable-alias) + (concat "Function for `eshell-variable-aliases-list' " + "entry should accept two arguments: INDICES " + "and QUOTED.'")) (funcall target indices))))) ((symbolp target) (eshell-apply-indices (symbol-value target) indices quoted)) From 78f93d92b2871cc05f5293c7344fe5fe188a763c Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Wed, 18 Jan 2023 19:55:10 +0200 Subject: [PATCH 27/34] =?UTF-8?q?*=20lisp/vc/vc-dir.el:=20Make=20keys=20?= =?UTF-8?q?=E2=80=98%=20m=E2=80=99=20and=20=E2=80=98*=20%=E2=80=99=20compa?= =?UTF-8?q?tible=20with=20Dired?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (vc-dir-mode-map): Replace ‘%’ for vc-dir-mark-by-regexp with ‘% m’ in regexp-map and ‘* %’ in mark-map (bug#60887). * doc/emacs/maintaining.texi (VC Directory Commands): Replace ‘%’ with ‘% m’ and ‘* %’. Mention vc-dir-mark-registered-files. --- doc/emacs/maintaining.texi | 9 ++++++++- etc/NEWS | 2 +- lisp/vc/vc-dir.el | 8 ++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 8c77ded55d3..5191bb2918d 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -1331,11 +1331,18 @@ point is on a directory entry, mark all files in that directory tree listed files and directories. @findex vc-dir-mark-by-regexp -@item % +@item % m +@itemx * % You can use this command to mark files by regexp (@code{vc-dir-mark-by-regexp}). If given a prefix, unmark files instead. +@findex vc-dir-mark-registered-files +@item * r +You can use this command to mark files that are in one of registered +states, including edited, added or removed. +(@code{vc-dir-mark-registered-files}). + @item G Add the file under point to the list of files that the VC should ignore (@code{vc-dir-ignore}). For instance, if the VC is Git, it diff --git a/etc/NEWS b/etc/NEWS index 95dd4a24ec3..14941b906ef 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2079,7 +2079,7 @@ The VC Directory buffer now uses the prefix 'b' for these branch-related commands. +++ -*** New command '%' ('vc-dir-mark-by-regexp'). +*** New command 'vc-dir-mark-by-regexp' bound to '% m' and '* %'. This command marks files based on a regexp. If given a prefix argument, unmark instead. diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index 312556f644a..53d58870b32 100644 --- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el @@ -325,7 +325,6 @@ See `run-hooks'." (define-key map "U" #'vc-dir-unmark-all-files) (define-key map "\C-?" #'vc-dir-unmark-file-up) (define-key map "\M-\C-?" #'vc-dir-unmark-all-files) - (define-key map "%" #'vc-dir-mark-by-regexp) ;; Movement. (define-key map "n" #'vc-dir-next-line) (define-key map " " #'vc-dir-next-line) @@ -361,8 +360,13 @@ See `run-hooks'." (define-key branch-map "l" #'vc-print-branch-log) (define-key branch-map "s" #'vc-switch-branch)) + (let ((regexp-map (make-sparse-keymap))) + (define-key map "%" regexp-map) + (define-key regexp-map "m" #'vc-dir-mark-by-regexp)) + (let ((mark-map (make-sparse-keymap))) (define-key map "*" mark-map) + (define-key mark-map "%" #'vc-dir-mark-by-regexp) (define-key mark-map "r" #'vc-dir-mark-registered-files)) ;; Hook up the menu. @@ -791,7 +795,7 @@ MARK-FILES should be a list of absolute filenames." vc-ewoc)) (defun vc-dir-mark-registered-files () - "Mark files that are in one of registered state: edited, added or removed." + "Mark files that are in one of registered states: edited, added or removed." (interactive) (vc-dir-mark-state-files '(edited added removed))) From 8e9783b4ce42122a8670f16f21a73597a226b674 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Wed, 18 Jan 2023 20:05:19 +0200 Subject: [PATCH 28/34] =?UTF-8?q?Rebind=20in=20read-regexp-map=20=E2=80=98?= =?UTF-8?q?M-c=E2=80=99=20to=20=E2=80=98M-s=20c=E2=80=99=20compatible=20wi?= =?UTF-8?q?th=20search-map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also it's compatible with 'M-s c' (isearch-toggle-case-fold) used during Isearch. Also makes possible to use the global keybinding 'M-c' (capitalize-dwim) in the minibuffer. * doc/lispref/minibuf.texi (Text from Minibuffer): Rename ‘M-c’ to ‘M-s c’. * lisp/replace.el (read-regexp-map): Rebind ‘M-c’ to ‘M-s c’ (bug#60741). (read-regexp-toggle-case-fold): Rename from read-regexp-toggle-case-folding to more standard name. --- doc/lispref/minibuf.texi | 2 +- etc/NEWS | 2 +- lisp/replace.el | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index 18125c372ce..114e5d38a80 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -312,7 +312,7 @@ to @code{regexp-history}. @cindex @code{case-fold}, text property @findex read-regexp-case-fold-search -The user can use the @kbd{M-c} command to indicate whether case +The user can use the @kbd{M-s c} command to indicate whether case folding should be on or off. If the user has used this command, the returned string will have the text property @code{case-fold} set to either @code{fold} or @code{inhibit-fold}. It is up to the caller of diff --git a/etc/NEWS b/etc/NEWS index 14941b906ef..9f735bec443 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3807,7 +3807,7 @@ These function now take an optional comparison PREDICATE argument. ** 'read-multiple-choice' can now use long-form answers. +++ -** 'M-c' in 'read-regexp' now toggles case folding. +** 'M-s c' in 'read-regexp' now toggles case folding. +++ ** 'completing-read' now allows a function as its REQUIRE-MATCH argument. diff --git a/lisp/replace.el b/lisp/replace.el index 2f063bbf66b..3c2b925ea92 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -824,11 +824,11 @@ by this function to the end of values available via (defvar-keymap read-regexp-map :parent minibuffer-local-map - "M-c" #'read-regexp-toggle-case-folding) + "M-s c" #'read-regexp-toggle-case-fold) (defvar read-regexp--case-fold nil) -(defun read-regexp-toggle-case-folding () +(defun read-regexp-toggle-case-fold () (interactive) (setq read-regexp--case-fold (if (or (eq read-regexp--case-fold 'fold) @@ -875,7 +875,7 @@ in \":\", followed by optional whitespace), DEFAULT is added to the prompt. The optional argument HISTORY is a symbol to use for the history list. If nil, use `regexp-history'. -If the user has used the \\\\[read-regexp-toggle-case-folding] command to specify case +If the user has used the \\\\[read-regexp-toggle-case-fold] command to specify case sensitivity, the returned string will have a text property named `case-fold' that has a value of either `fold' or `inhibit-fold'. (It's up to the caller of `read-regexp' to From 758ac5eabbe05fa5244e1bf863e45069035f311c Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Wed, 18 Jan 2023 20:24:26 +0200 Subject: [PATCH 29/34] Fix split-window-below for the case when split-window-keep-point is nil. * lisp/window.el (split-window-below): Don't try to move point when split-window-keep-point is nil and window-to-split is not the selected window or nil (that defaults to the selected window) because code makes sense only for the selected window. (split-window-below, split-window-right): Improve docstrings (bug#60886) --- lisp/window.el | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lisp/window.el b/lisp/window.el index 4099b707009..84f5c5c3f5a 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -5670,7 +5670,8 @@ the original point in both windows." (defun split-window-below (&optional size window-to-split) "Split WINDOW-TO-SPLIT into two windows, one above the other. -WINDOW-TO-SPLIT is above. The newly split-off window is +WINDOW-TO-SPLIT defaults to the selected window and and will be above +the other window after splitting. The newly split-off window is below and displays the same buffer. Return the new window. If optional argument SIZE is omitted or nil, both windows get the @@ -5691,7 +5692,9 @@ amount of redisplay; this is convenient on slow terminals." ;; `split-window' would not signal an error here. (error "Size of new window too small")) (setq new-window (split-window window-to-split size)) - (unless split-window-keep-point + (when (and (null split-window-keep-point) + (or (null window-to-split) + (eq window-to-split (selected-window)))) (with-current-buffer (window-buffer window-to-split) ;; Use `save-excursion' around vertical movements below ;; (Bug#10971). Note: When WINDOW-TO-SPLIT's buffer has a @@ -5732,8 +5735,9 @@ handled as in `split-window-below'." (defun split-window-right (&optional size window-to-split) "Split WINDOW-TO-SPLIT into two side-by-side windows. -WINDOW-TO-SPLIT is on the left. The newly split-off window is on -the right and displays the same buffer. Return the new window. +WINDOW-TO-SPLIT defaults to the selected window and and will be on the +left after splitting. The newly split-off window is on the right and +displays the same buffer. Return the new window. If optional argument SIZE is omitted or nil, both windows get the same width, or close to it. If SIZE is positive, the left-hand From f2bedf695c15da93e8e240ad11a350a8dc8b5549 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Thu, 19 Jan 2023 03:38:58 +0200 Subject: [PATCH 30/34] ruby-ts-mode: Handle indent in parenless calls much closer to ruby-mode * lisp/progmodes/ruby-ts-mode.el (ruby-ts--parent-call-or-bol): New function. (ruby-ts--indent-rules): Use it for cases which need special anchoring logic when inside a parenless method call. Remove the ad-hoc handling of pair-hash-pair etc indentation, which was there only for the parenless cases, apparently. Have "No paren, ruby-parenless-call-arguments-indent is nil" case align to the statement, if only because ruby-mode does that. * test/lisp/progmodes/ruby-ts-mode-tests.el: Run indent test for ruby-parenless-call-arguments-indent.rb. --- lisp/progmodes/ruby-ts-mode.el | 69 +++++++++++++++-------- test/lisp/progmodes/ruby-ts-mode-tests.el | 1 + 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 27e5d002881..3a6d513c330 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -680,14 +680,15 @@ a statement container is a node that matches ;; 2) With paren, 1st arg on next line ((and (query "(argument_list \"(\" _ @indent)") (node-is ")")) - parent-bol 0) + ruby-ts--parent-call-or-bol 0) ((query "(argument_list \"(\" _ @indent)") - parent-bol ruby-indent-level) + ruby-ts--parent-call-or-bol ruby-indent-level) ;; 3) No paren, ruby-parenless-call-arguments-indent is t ((and ruby-ts--parenless-call-arguments-indent-p (parent-is "argument_list")) first-sibling 0) ;; 4) No paren, ruby-parenless-call-arguments-indent is nil - ((parent-is "argument_list") (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level) + ((parent-is "argument_list") + (ruby-ts--bol ruby-ts--statement-ancestor) ruby-indent-level) ;; Old... probably too simple ((parent-is "block_parameters") first-sibling 1) @@ -718,27 +719,10 @@ a statement container is a node that matches ((and ruby-ts--same-line-hash-array-p (parent-is "array")) (nth-sibling 0 ruby-ts--true) 0) - ;; NOTE to folks trying to understand my insanity... - ;; I having trouble understanding the "logic" of why things - ;; are indented like they are so I am adding special cases - ;; hoping at some point I will be struck by lightning. - ((and (n-p-gp "}" "hash" "pair") - (not ruby-ts--same-line-hash-array-p)) - grand-parent 0) - ((and (n-p-gp "pair" "hash" "pair") - (not ruby-ts--same-line-hash-array-p)) - grand-parent ruby-indent-level) - ((and (n-p-gp "}" "hash" "method") - (not ruby-ts--same-line-hash-array-p)) - grand-parent 0) - ((and (n-p-gp "pair" "hash" "method") - (not ruby-ts--same-line-hash-array-p)) - grand-parent ruby-indent-level) - - ((match "}" "hash") parent-bol 0) - ((parent-is "hash") parent-bol ruby-indent-level) - ((match "]" "array") parent-bol 0) - ((parent-is "array") parent-bol ruby-indent-level) + ((match "}" "hash") ruby-ts--parent-call-or-bol 0) + ((parent-is "hash") ruby-ts--parent-call-or-bol ruby-indent-level) + ((match "]" "array") ruby-ts--parent-call-or-bol 0) + ((parent-is "array") ruby-ts--parent-call-or-bol ruby-indent-level) ((match ")" "parenthesized_statements") parent-bol 0) ((parent-is "parenthesized_statements") parent-bol ruby-indent-level) @@ -798,9 +782,46 @@ a statement container is a node that matches (goto-char (treesit-node-start parent)) (when (string-match-p ruby-ts--statement-container-regexp (treesit-node-type (treesit-node-parent parent))) + ;; Hack alert: it's not the proper place to alter the offset. + ;; Redoing the analysis in the OFFSET form seems annoying, + ;; though. (**) (forward-char ruby-indent-level)) (point))) +(defun ruby-ts--parent-call-or-bol (_not parent _bol &rest _) + (let* ((parent-bol (save-excursion + (goto-char (treesit-node-start parent)) + (back-to-indentation) + (point))) + (found + (treesit-parent-until + parent + (lambda (node) + (or (<= (treesit-node-start node) parent-bol) + (and + ;; Parenless call. + (equal (treesit-node-type node) "argument_list") + (not (equal (treesit-node-type + (treesit-node-child node 0)) + "("))))) + t))) + (cond + ;; No parenless call found on the current line. + ((<= (treesit-node-start found) parent-bol) + parent-bol) + ;; Parenless call found: indent to stmt with offset. + ((not ruby-parenless-call-arguments-indent) + (save-excursion + (goto-char (treesit-node-start + (ruby-ts--statement-ancestor found))) + ;; (**) Same. + (+ (point) ruby-indent-level))) + ;; Indent to the parenless call args beginning. + (t + (save-excursion + (goto-char (treesit-node-start found)) + (point)))))) + (defun ruby-ts--after-op-indent-p (&rest _) ruby-after-operator-indent) diff --git a/test/lisp/progmodes/ruby-ts-mode-tests.el b/test/lisp/progmodes/ruby-ts-mode-tests.el index 1d2cfbfb901..d34c235e82b 100644 --- a/test/lisp/progmodes/ruby-ts-mode-tests.el +++ b/test/lisp/progmodes/ruby-ts-mode-tests.el @@ -255,6 +255,7 @@ The whitespace before and including \"|\" on each line is removed." (ruby-ts-deftest-indent "ruby-block-indent.rb") (ruby-ts-deftest-indent "ruby-method-call-indent.rb") (ruby-ts-deftest-indent "ruby-method-params-indent.rb") +(ruby-ts-deftest-indent "ruby-parenless-call-arguments-indent.rb") (provide 'ruby-ts-mode-tests) From ba33b83ce4b27b353441a174faaba024d59e4614 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Thu, 19 Jan 2023 03:51:51 +0200 Subject: [PATCH 31/34] (ruby-ts--statement-container-regexp): Remove "parenthesized_statements" * lisp/progmodes/ruby-ts-mode.el (ruby-ts--statement-container-regexp): Remove "parenthesized_statements", it's not really a statement container, not one we'd use for indentation alignment anyway. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: Add examples. --- lisp/progmodes/ruby-ts-mode.el | 1 - test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 3a6d513c330..5df7e397f03 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -152,7 +152,6 @@ "then" "ensure" "body_statement" - "parenthesized_statements" "interpolation") string-end) "Regular expression of the nodes that can contain statements.") diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index 9959de4fe71..1f7caf64c34 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -54,6 +54,14 @@ qux = foo.fee ? bar : tee +with_paren = (a + b * + c * d + + 12) + +without_paren = a + b * + c * d + + 12 + # Local Variables: # mode: ruby-ts # ruby-after-operator-indent: t From 94b9cbf96fbb61b53242d205ff559deee36279c6 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Thu, 19 Jan 2023 05:10:05 +0200 Subject: [PATCH 32/34] (ruby-ts--parent-call-or-bol): Handle more cases with nested literals * lisp/progmodes/ruby-ts-mode.el (ruby-ts--parent-call-or-bol): Handle more cases with nested literals. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: Add examples. --- lisp/progmodes/ruby-ts-mode.el | 29 ++++++++++++------- .../progmodes/ruby-mode-resources/ruby-ts.rb | 17 +++++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 5df7e397f03..a2b2721dc1b 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -796,18 +796,21 @@ a statement container is a node that matches (treesit-parent-until parent (lambda (node) - (or (<= (treesit-node-start node) parent-bol) - (and - ;; Parenless call. - (equal (treesit-node-type node) "argument_list") - (not (equal (treesit-node-type - (treesit-node-child node 0)) - "("))))) - t))) + (or (< (treesit-node-start node) parent-bol) + (string-match-p "\\`array\\|hash\\'" (treesit-node-type node)) + ;; Method call on same line. + (equal (treesit-node-type node) "argument_list")))))) (cond - ;; No parenless call found on the current line. - ((<= (treesit-node-start found) parent-bol) + ((null found) parent-bol) + ;; No paren/curly/brace found on the same line. + ((< (treesit-node-start found) parent-bol) + parent-bol) + ;; Hash or array opener on the same line. + ((string-match-p "\\`array\\|hash\\'" (treesit-node-type found)) + (save-excursion + (goto-char (treesit-node-start (treesit-node-child found 1))) + (point))) ;; Parenless call found: indent to stmt with offset. ((not ruby-parenless-call-arguments-indent) (save-excursion @@ -815,6 +818,12 @@ a statement container is a node that matches (ruby-ts--statement-ancestor found))) ;; (**) Same. (+ (point) ruby-indent-level))) + ;; Call with parens -- ident to first arg. + ((equal (treesit-node-type (treesit-node-child found 0)) + "(") + (save-excursion + (goto-char (treesit-node-start (treesit-node-child found 1))) + (point))) ;; Indent to the parenless call args beginning. (t (save-excursion diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index 1f7caf64c34..fa16107c56e 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -62,6 +62,23 @@ without_paren = a + b * c * d + 12 +{'a' => { + 'b' => 'c', + 'd' => %w(e f) + } +} + +[1, 2, { + 'b' => 'c', + 'd' => %w(e f) + } +] + +foo(a, { + a: b, + c: d + }) + # Local Variables: # mode: ruby-ts # ruby-after-operator-indent: t From 819719330ad9d1c2836079ef3006c5790fa7f93f Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Thu, 19 Jan 2023 05:26:03 +0200 Subject: [PATCH 33/34] (ruby-ts--indent-rules): Add a rule for continuation of a hash pair * lisp/progmodes/ruby-ts-mode.el (ruby-ts--indent-rules): Add a rule for continuation of a hash pair. * test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb: Add examples. --- lisp/progmodes/ruby-ts-mode.el | 2 ++ test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index a2b2721dc1b..c334c4aff0c 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -723,6 +723,8 @@ a statement container is a node that matches ((match "]" "array") ruby-ts--parent-call-or-bol 0) ((parent-is "array") ruby-ts--parent-call-or-bol ruby-indent-level) + ((parent-is "pair") ruby-ts--parent-call-or-bol 0) + ((match ")" "parenthesized_statements") parent-bol 0) ((parent-is "parenthesized_statements") parent-bol ruby-indent-level) diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb index fa16107c56e..4be532a5e9d 100644 --- a/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb +++ b/test/lisp/progmodes/ruby-mode-resources/ruby-ts.rb @@ -79,6 +79,12 @@ foo(a, { c: d }) +foo(foo, bar: + tee) + +foo(foo, :bar => + tee) + # Local Variables: # mode: ruby-ts # ruby-after-operator-indent: t From db727873803a974ba210c4942ae7cbcc3d6268ab Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Thu, 19 Jan 2023 05:43:10 +0200 Subject: [PATCH 34/34] ruby-ts-mode: Use font-lock-constant-face for true/false/nil * lisp/progmodes/ruby-ts-mode.el (ruby-ts--font-lock-settings): Use font-lock-constant-face for true/false/nil. --- lisp/progmodes/ruby-ts-mode.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index c334c4aff0c..f075824591d 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -220,9 +220,9 @@ values of OVERRIDE" :language language :feature 'constant - '((true) @font-lock-doc-markup-face - (false) @font-lock-doc-markup-face - (nil) @font-lock-doc-markup-face) + '((true) @font-lock-constant-face + (false) @font-lock-constant-face + (nil) @font-lock-constant-face) ;; Before 'operator so (unary) works. :language language