commit aa0037aaf7c2cd7052925fdeca73c77c91254de1 (HEAD, refs/remotes/origin/master) Author: Ihor Radchenko Date: Fri Dec 15 11:47:45 2023 +0100 Improve performance let-binding `case-fold-search' (bug#66117) * src/buffer.h: Remove case_fold_search_ buffer object slot. * src/buffer.c (bset_case_fold_search): Remove - no longer needed. (init_buffer_once): Remove removed buffer slot init. (syms_of_buffer): Use DEFVAR_LISP to define `case-fold-search' and declare it buffer-local. * src/minibuf.c (syms_of_minibuf): Remove DEFSYM call for `case-fold-search' symbol. It now lives in `syms_of_buffer'. * src/editfns.c (Fcompare_buffer_substrings): (Fchar_equal): * src/search.c (looking_at_1): (string_match_1): (search_command): (Fre__describe_compiled): Adjust C queries to `case-fold-search' value to use C globals instead of BVAR macro. * doc/lispref/internals.texi (Buffer Internals): Do not list `case_fold_search' slot. See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=66117#259 When used as buffer slot, let-binding `case-fold-search' would scale with the number of live buffers and can be slow. This change makes let-binding much faster at the cost of slightly slower `set-buffer'. diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 1fba683223e..4b4edda6d46 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -2519,7 +2519,6 @@ Buffer Internals @item mode_line_format @itemx header_line_format -@itemx case_fold_search @itemx tab_width @itemx fill_column @itemx left_margin diff --git a/src/buffer.c b/src/buffer.c index d08ea44d797..637830704e6 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -210,11 +210,6 @@ bset_buffer_file_coding_system (struct buffer *b, Lisp_Object val) b->buffer_file_coding_system_ = val; } static void -bset_case_fold_search (struct buffer *b, Lisp_Object val) -{ - b->case_fold_search_ = val; -} -static void bset_ctl_arrow (struct buffer *b, Lisp_Object val) { b->ctl_arrow_ = val; @@ -4692,7 +4687,6 @@ init_buffer_once (void) XSETFASTINT (BVAR (&buffer_local_flags, mode_line_format), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, abbrev_mode), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, overwrite_mode), idx); ++idx; - XSETFASTINT (BVAR (&buffer_local_flags, case_fold_search), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, auto_fill_function), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, selective_display), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, selective_display_ellipses), idx); ++idx; @@ -4785,7 +4779,6 @@ init_buffer_once (void) bset_tab_line_format (&buffer_defaults, Qnil); bset_abbrev_mode (&buffer_defaults, Qnil); bset_overwrite_mode (&buffer_defaults, Qnil); - bset_case_fold_search (&buffer_defaults, Qt); bset_auto_fill_function (&buffer_defaults, Qnil); bset_selective_display (&buffer_defaults, Qnil); bset_selective_display_ellipses (&buffer_defaults, Qt); @@ -5215,10 +5208,6 @@ syms_of_buffer (void) doc: /* Non-nil if Abbrev mode is enabled. Use the command `abbrev-mode' to change this variable. */); - DEFVAR_PER_BUFFER ("case-fold-search", &BVAR (current_buffer, case_fold_search), - Qnil, - doc: /* Non-nil if searches and matches should ignore case. */); - DEFVAR_PER_BUFFER ("fill-column", &BVAR (current_buffer, fill_column), Qintegerp, doc: /* Column beyond which automatic line-wrapping should happen. @@ -5951,6 +5940,12 @@ Functions (implicitly) running this hook are `get-buffer-create', This is the default. If nil, auto-save file deletion is inhibited. */); delete_auto_save_files = 1; + DEFVAR_LISP ("case-fold-search", Vcase_fold_search, + doc: /* Non-nil if searches and matches should ignore case. */); + Vcase_fold_search = Qt; + DEFSYM (Qcase_fold_search, "case-fold-search"); + Fmake_variable_buffer_local (Qcase_fold_search); + DEFVAR_LISP ("clone-indirect-buffer-hook", Vclone_indirect_buffer_hook, doc: /* Normal hook to run in the new buffer at the end of `make-indirect-buffer'. diff --git a/src/buffer.h b/src/buffer.h index b2bd15657dc..399c6585158 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -379,7 +379,6 @@ #define BVAR(buf, field) ((buf)->field ## _) /* Values of several buffer-local variables. */ /* tab-width is buffer-local so that redisplay can find it in buffers that are not current. */ - Lisp_Object case_fold_search_; Lisp_Object tab_width_; Lisp_Object fill_column_; Lisp_Object left_margin_; diff --git a/src/editfns.c b/src/editfns.c index 49d552c4a75..aa410ea7091 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -1785,7 +1785,7 @@ DEFUN ("compare-buffer-substrings", Fcompare_buffer_substrings, Scompare_buffer_ register EMACS_INT begp1, endp1, begp2, endp2, temp; register struct buffer *bp1, *bp2; register Lisp_Object trt - = (!NILP (BVAR (current_buffer, case_fold_search)) + = (!NILP (Vcase_fold_search) ? BVAR (current_buffer, case_canon_table) : Qnil); ptrdiff_t chars = 0; ptrdiff_t i1, i2, i1_byte, i2_byte; @@ -4367,7 +4367,7 @@ DEFUN ("char-equal", Fchar_equal, Schar_equal, 2, 2, 0, if (XFIXNUM (c1) == XFIXNUM (c2)) return Qt; - if (NILP (BVAR (current_buffer, case_fold_search))) + if (NILP (Vcase_fold_search)) return Qnil; i1 = XFIXNAT (c1); diff --git a/src/minibuf.c b/src/minibuf.c index 58adde1bf66..0d5b375e450 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -2320,7 +2320,6 @@ syms_of_minibuf (void) DEFSYM (Qcurrent_input_method, "current-input-method"); DEFSYM (Qactivate_input_method, "activate-input-method"); - DEFSYM (Qcase_fold_search, "case-fold-search"); DEFSYM (Qmetadata, "metadata"); DEFSYM (Qcycle_sort_function, "cycle-sort-function"); diff --git a/src/search.c b/src/search.c index 2996d32fca1..75a67ff3223 100644 --- a/src/search.c +++ b/src/search.c @@ -281,7 +281,7 @@ looking_at_1 (Lisp_Object string, bool posix, bool modify_data) struct regexp_cache *cache_entry = compile_pattern ( string, modify_match_data ? &search_regs : NULL, - (!NILP (BVAR (current_buffer, case_fold_search)) + (!NILP (Vcase_fold_search) ? BVAR (current_buffer, case_canon_table) : Qnil), posix, !NILP (BVAR (current_buffer, enable_multibyte_characters))); @@ -402,7 +402,7 @@ string_match_1 (Lisp_Object regexp, Lisp_Object string, Lisp_Object start, struct regexp_cache *cache_entry = compile_pattern (regexp, modify_match_data ? &search_regs : NULL, - (!NILP (BVAR (current_buffer, case_fold_search)) + (!NILP (Vcase_fold_search) ? BVAR (current_buffer, case_canon_table) : Qnil), posix, @@ -1068,10 +1068,10 @@ search_command (Lisp_Object string, Lisp_Object bound, Lisp_Object noerror, BVAR (current_buffer, case_eqv_table)); np = search_buffer (string, PT, PT_BYTE, lim, lim_byte, n, RE, - (!NILP (BVAR (current_buffer, case_fold_search)) + (!NILP (Vcase_fold_search) ? BVAR (current_buffer, case_canon_table) : Qnil), - (!NILP (BVAR (current_buffer, case_fold_search)) + (!NILP (Vcase_fold_search) ? BVAR (current_buffer, case_eqv_table) : Qnil), posix); @@ -3402,7 +3402,7 @@ DEFUN ("re--describe-compiled", Fre__describe_compiled, Sre__describe_compiled, { struct regexp_cache *cache_entry = compile_pattern (regexp, NULL, - (!NILP (BVAR (current_buffer, case_fold_search)) + (!NILP (Vcase_fold_search) ? BVAR (current_buffer, case_canon_table) : Qnil), false, !NILP (BVAR (current_buffer, commit 784acce8425fb3143719d34be1e39be499564dfb Author: Eli Zaretskii Date: Sat Dec 30 10:01:31 2023 +0200 ; Fix last change * src/buffer.c (Ffind_buffer): Doc fix. (Bug#66117) * test/manual/etags/c-src/emacs/src/lisp.h (get_truename_buffer): Revert removal of prototype: that breaks the test results. diff --git a/src/buffer.c b/src/buffer.c index 73e9839f883..d08ea44d797 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -537,8 +537,8 @@ DEFUN ("get-truename-buffer", Fget_truename_buffer, Sget_truename_buffer, 1, 1, } DEFUN ("find-buffer", Ffind_buffer, Sfind_buffer, 2, 2, 0, - doc: /* Return the buffer with buffer-local VARIABLE equal to VALUE. - If there is no such live buffer, return nil. + doc: /* Return the buffer with buffer-local VARIABLE `equal' to VALUE. +If there is no such live buffer, return nil. See also `find-buffer-visiting'. */) (Lisp_Object variable, Lisp_Object value) { diff --git a/test/manual/etags/c-src/emacs/src/lisp.h b/test/manual/etags/c-src/emacs/src/lisp.h index 19463828270..aa8dc8c9a66 100644 --- a/test/manual/etags/c-src/emacs/src/lisp.h +++ b/test/manual/etags/c-src/emacs/src/lisp.h @@ -4075,6 +4075,7 @@ intern_c_string (const char *str) Lisp_Object, Lisp_Object, Lisp_Object); extern bool overlay_touches_p (ptrdiff_t); extern Lisp_Object other_buffer_safely (Lisp_Object); +extern Lisp_Object get_truename_buffer (Lisp_Object); extern void init_buffer_once (void); extern void init_buffer (int); extern void syms_of_buffer (void); commit b7a737ef49e787120ea7a7e9f4d4ef04dd1a0723 Author: Ihor Radchenko Date: Sun Oct 8 11:48:42 2023 +0300 Improve performance of `find-buffer-visiting' (bug#66117) * src/buffer.c (Fget_truename_buffer): Expose `get_truename_buffer' to Elisp. (Ffind_buffer): New subr searching for a live buffer with a given value of buffer-local variable. (syms_of_buffer): Register the new added subroutines. * src/filelock.c (lock_file): Use the new `Fget_truename_buffer' name. * src/lisp.h: * test/manual/etags/c-src/emacs/src/lisp.h: Remove no-longer-necessary extern declarations for `get_truename_buffer'. * lisp/files.el (find-buffer-visiting): Refactor, using subroutines to search for buffers instead of slow manual Elisp iterations. diff --git a/lisp/files.el b/lisp/files.el index caa0622ab08..315ba831e8c 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2208,37 +2208,29 @@ find-buffer-visiting the only argument, but not with the buffer as the current buffer. If there is no such live buffer, return nil." - (let ((predicate (or predicate #'identity)) - (truename (abbreviate-file-name (file-truename filename)))) - (or (let ((buf (get-file-buffer filename))) - (when (and buf (funcall predicate buf)) buf)) - (let ((list (buffer-list)) found) - (while (and (not found) list) - (with-current-buffer (car list) - (if (and buffer-file-name - (string= buffer-file-truename truename) - (funcall predicate (current-buffer))) - (setq found (car list)))) - (setq list (cdr list))) - found) - (let* ((attributes (file-attributes truename)) - (number (file-attribute-file-identifier attributes)) - (list (buffer-list)) found) - (and buffer-file-numbers-unique - (car-safe number) ;Make sure the inode is not just nil. - (while (and (not found) list) - (with-current-buffer (car list) - (if (and buffer-file-name - (equal buffer-file-number number) - ;; Verify this buffer's file number - ;; still belongs to its file. - (file-exists-p buffer-file-name) - (equal (file-attributes buffer-file-truename) - attributes) - (funcall predicate (current-buffer))) - (setq found (car list)))) - (setq list (cdr list)))) - found)))) + (or (let ((buf (get-file-buffer filename))) + (when (and buf (or (not predicate) (funcall predicate buf))) buf)) + (let ((truename (abbreviate-file-name (file-truename filename)))) + (or + (let ((buf (get-truename-buffer truename))) + (when (and buf (buffer-local-value 'buffer-file-name buf) + (or (not predicate) (funcall predicate buf))) + buf)) + (let* ((attributes (file-attributes truename)) + (number (file-attribute-file-identifier attributes))) + (and buffer-file-numbers-unique + (car-safe number) ;Make sure the inode is not just nil. + (let ((buf (find-buffer 'buffer-file-number number))) + (when (and buf (buffer-local-value 'buffer-file-name buf) + ;; Verify this buffer's file number + ;; still belongs to its file. + (file-exists-p buffer-file-name) + (equal (file-attributes buffer-file-truename) + attributes) + (or (not predicate) + (funcall predicate (current-buffer)))) + buf)))))))) + (defcustom find-file-wildcards t "Non-nil means file-visiting commands should handle wildcards. diff --git a/src/buffer.c b/src/buffer.c index ea0c23192b7..73e9839f883 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -519,8 +519,11 @@ DEFUN ("get-file-buffer", Fget_file_buffer, Sget_file_buffer, 1, 1, 0, return Qnil; } -Lisp_Object -get_truename_buffer (register Lisp_Object filename) +DEFUN ("get-truename-buffer", Fget_truename_buffer, Sget_truename_buffer, 1, 1, 0, + doc: /* Return the buffer with `file-truename' equal to FILENAME (a string). +If there is no such live buffer, return nil. +See also `find-buffer-visiting'. */) + (register Lisp_Object filename) { register Lisp_Object tail, buf; @@ -533,6 +536,22 @@ get_truename_buffer (register Lisp_Object filename) return Qnil; } +DEFUN ("find-buffer", Ffind_buffer, Sfind_buffer, 2, 2, 0, + doc: /* Return the buffer with buffer-local VARIABLE equal to VALUE. + If there is no such live buffer, return nil. +See also `find-buffer-visiting'. */) + (Lisp_Object variable, Lisp_Object value) +{ + register Lisp_Object tail, buf; + + FOR_EACH_LIVE_BUFFER (tail, buf) + { + if (!NILP (Fequal (value, Fbuffer_local_value(variable, buf)))) + return buf; + } + return Qnil; +} + /* Run buffer-list-update-hook if Vrun_hooks is non-nil and BUF does not have buffer hooks inhibited. */ @@ -6010,6 +6029,8 @@ Functions (implicitly) running this hook are `get-buffer-create', defsubr (&Sbuffer_list); defsubr (&Sget_buffer); defsubr (&Sget_file_buffer); + defsubr (&Sget_truename_buffer); + defsubr (&Sfind_buffer); defsubr (&Sget_buffer_create); defsubr (&Smake_indirect_buffer); defsubr (&Sgenerate_new_buffer_name); diff --git a/src/filelock.c b/src/filelock.c index c2b306ab47d..9ce51c724b1 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -563,7 +563,7 @@ lock_file (Lisp_Object fn) /* See if this file is visited and has changed on disk since it was visited. */ - Lisp_Object subject_buf = get_truename_buffer (fn); + Lisp_Object subject_buf = Fget_truename_buffer (fn); if (!NILP (subject_buf) && NILP (Fverify_visited_file_modtime (subject_buf)) && !NILP (Ffile_exists_p (fn)) diff --git a/src/lisp.h b/src/lisp.h index ed1b007d4c5..efd36465e62 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4664,7 +4664,6 @@ XMODULE_FUNCTION (Lisp_Object o) Lisp_Object, Lisp_Object, Lisp_Object); extern bool overlay_touches_p (ptrdiff_t); extern Lisp_Object other_buffer_safely (Lisp_Object); -extern Lisp_Object get_truename_buffer (Lisp_Object); extern void init_buffer_once (void); extern void init_buffer (void); extern void syms_of_buffer (void); diff --git a/test/manual/etags/c-src/emacs/src/lisp.h b/test/manual/etags/c-src/emacs/src/lisp.h index aa8dc8c9a66..19463828270 100644 --- a/test/manual/etags/c-src/emacs/src/lisp.h +++ b/test/manual/etags/c-src/emacs/src/lisp.h @@ -4075,7 +4075,6 @@ intern_c_string (const char *str) Lisp_Object, Lisp_Object, Lisp_Object); extern bool overlay_touches_p (ptrdiff_t); extern Lisp_Object other_buffer_safely (Lisp_Object); -extern Lisp_Object get_truename_buffer (Lisp_Object); extern void init_buffer_once (void); extern void init_buffer (int); extern void syms_of_buffer (void); commit 0cb252cf21efec49f693f5a8873ed02c1bcbd713 Author: Ihor Radchenko Date: Thu Dec 28 11:58:42 2023 +0100 * lisp/textmodes/remember.el: Remove org-remember support. org-remember has been removed from Org mode proper and then from org-contrib 10 years ago in https://git.sr.ht/~bzg/org-contrib/commit/be8736195e (Bug#68076) diff --git a/lisp/textmodes/remember.el b/lisp/textmodes/remember.el index 84a0185f7b5..b4d1ea6c919 100644 --- a/lisp/textmodes/remember.el +++ b/lisp/textmodes/remember.el @@ -185,7 +185,7 @@ remember (defcustom remember-mode-hook nil "Functions run upon entering `remember-mode'." :type 'hook - :options '(flyspell-mode turn-on-auto-fill org-remember-apply-template)) + :options '(flyspell-mode turn-on-auto-fill)) (defcustom remember-in-new-frame nil "Non-nil means use a separate frame for capturing remember data." @@ -210,8 +210,7 @@ remember-handler-functions :options '(remember-store-in-mailbox remember-append-to-file remember-store-in-files - remember-diary-extract-entries - org-remember-handler)) + remember-diary-extract-entries)) (defcustom remember-all-handler-functions nil "If non-nil every function in `remember-handler-functions' is called." @@ -235,7 +234,7 @@ remember-save-after-remembering (defcustom remember-annotation-functions '(buffer-file-name) "Hook that returns an annotation to be inserted into the remember buffer." :type 'hook - :options '(org-remember-annotation buffer-file-name)) + :options '(buffer-file-name)) (defvar remember-annotation nil "Current annotation.") commit 35f8629a3f42b1d053cd5c9e6445f0240131be42 Author: Daniel Martín Date: Mon Dec 25 11:43:39 2023 +0100 Fix previous page and next page tooltips in doc-view.el * lisp/doc-view.el (doc-view-tool-bar-map): Fix toolbar items to go to next and previous pages in a document. (Bug#68018) diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 2fdb49f3e42..c70b60d9f58 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -698,12 +698,12 @@ doc-view-tool-bar-map (tool-bar-local-item-from-menu 'doc-view-previous-page "last-page" map doc-view-mode-map :vert-only t :enable '(> (doc-view-current-page) 1) - :help "Move to the next page.") + :help "Move to the previous page.") (tool-bar-local-item-from-menu 'doc-view-next-page "next-page" map doc-view-mode-map :vert-only t :enable '(< (doc-view-current-page) (doc-view-last-page-number)) - :help "Move to the last page.") + :help "Move to the next page.") map) "Like the default `tool-bar-map', but with additions for DocView.") commit bb0a5eb41f183b37015d09190b56ac8ce6e3c5c8 Author: Ihor Radchenko Date: Sat Dec 23 15:29:36 2023 +0100 Add tests for `let' (bug#66117) * test/src/eval-tests.el (eval-tests/default-value): New test. diff --git a/test/src/eval-tests.el b/test/src/eval-tests.el index 4589763b2f5..c1219591e40 100644 --- a/test/src/eval-tests.el +++ b/test/src/eval-tests.el @@ -282,4 +282,25 @@ eval-tests-defvaralias (should-error (defvaralias 'eval-tests--my-c 'eval-tests--my-d) :type 'cyclic-variable-indirection)) +(defvar eval-tests/global-var 'value) +(defvar-local eval-tests/buffer-local-var 'value) +(ert-deftest eval-tests/default-value () + ;; `let' overrides the default value for global variables. + (should (default-boundp 'eval-tests/global-var)) + (should (eq 'value (default-value 'eval-tests/global-var))) + (should (eq 'value eval-tests/global-var)) + (let ((eval-tests/global-var 'bar)) + (should (eq 'bar (default-value 'eval-tests/global-var))) + (should (eq 'bar eval-tests/global-var))) + ;; `let' overrides the default value everywhere, but leaves + ;; buffer-local values unchanged in current buffer and in the + ;; buffers where there is no explicitly set buffer-local value. + (should (default-boundp 'eval-tests/buffer-local-var)) + (should (eq 'value (default-value 'eval-tests/buffer-local-var))) + (should (eq 'value eval-tests/buffer-local-var)) + (with-temp-buffer + (let ((eval-tests/buffer-local-var 'bar)) + (should (eq 'bar (default-value 'eval-tests/buffer-local-var))) + (should (eq 'bar eval-tests/buffer-local-var))))) + ;;; eval-tests.el ends here commit 94e3d1159334d08fd1d54464bf6173755ba606b7 Author: Po Lu Date: Sat Dec 30 10:57:11 2023 +0800 Simplify code relating to UI thread synchronization * java/org/gnu/emacs/EmacsContextMenu.java (display): * java/org/gnu/emacs/EmacsDialog.java (display): * java/org/gnu/emacs/EmacsService.java (getEmacsView) (getLocationOnScreen, getClipboardManager) (requestDirectoryAccess): Replace manual synchronization within Runnable objects by usage of FutureTask. (syncRunnable): Accept FutureTask in place of Runnables, and obtain and return results from calls to its get method. diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 2652f35b545..b6c63c3cbe1 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -22,6 +22,9 @@ import java.util.List; import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; + import android.content.Context; import android.content.Intent; @@ -344,8 +347,7 @@ private static final class Item implements MenuItem.OnMenuItemClickListener display (final EmacsWindow window, final int xPosition, final int yPosition, final int serial) { - Runnable runnable; - final EmacsHolder rc; + FutureTask task; /* Android will permanently cease to display any popup menus at all if the list of menu items is empty. Prevent this by @@ -354,25 +356,17 @@ private static final class Item implements MenuItem.OnMenuItemClickListener if (menuItems.isEmpty ()) return false; - rc = new EmacsHolder (); - rc.thing = false; - - runnable = new Runnable () { + task = new FutureTask (new Callable () { @Override - public void - run () + public Boolean + call () { - synchronized (this) - { - lastMenuEventSerial = serial; - rc.thing = display1 (window, xPosition, yPosition); - notify (); - } + lastMenuEventSerial = serial; + return display1 (window, xPosition, yPosition); } - }; + }); - EmacsService.syncRunnable (runnable); - return rc.thing; + return EmacsService.syncRunnable (task); } /* Dismiss this context menu. WINDOW is the window where the diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index bad1ddde227..7552b16b370 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -22,6 +22,9 @@ import java.util.List; import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; + import android.app.AlertDialog; import android.content.Context; @@ -388,26 +391,18 @@ else if (EmacsOpenActivity.currentActivity != null) public boolean display () { - Runnable runnable; - final EmacsHolder rc; + FutureTask task; - rc = new EmacsHolder (); - rc.thing = false; - runnable = new Runnable () { + task = new FutureTask (new Callable () { @Override - public void - run () + public Boolean + call () { - synchronized (this) - { - rc.thing = display1 (); - notify (); - } + return display1 (); } - }; + }); - EmacsService.syncRunnable (runnable); - return rc.thing; + return EmacsService.syncRunnable (task); } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index c71670b3e47..7934d6f9cd3 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -27,6 +27,10 @@ import java.util.HashSet; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + import java.util.concurrent.atomic.AtomicInteger; import android.database.Cursor; @@ -331,52 +335,45 @@ invocation of app_process (through android-emacs) can final boolean isFocusedByDefault) { Runnable runnable; - final EmacsHolder view; - - view = new EmacsHolder (); + FutureTask task; - runnable = new Runnable () { + task = new FutureTask (new Callable () { @Override - public void - run () + public EmacsView + call () { - synchronized (this) - { - view.thing = new EmacsView (window); - view.thing.setVisibility (visibility); + EmacsView view; - /* The following function is only present on Android 26 - or later. */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - view.thing.setFocusedByDefault (isFocusedByDefault); + view = new EmacsView (window); + view.setVisibility (visibility); - notify (); - } + /* The following function is only present on Android 26 + or later. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + view.setFocusedByDefault (isFocusedByDefault); + + return view; } - }; + }); - syncRunnable (runnable); - return view.thing; + return EmacsService.syncRunnable (task); } public void getLocationOnScreen (final EmacsView view, final int[] coordinates) { - Runnable runnable; + FutureTask task; - runnable = new Runnable () { - public void - run () + task = new FutureTask (new Callable () { + public Void + call () { - synchronized (this) - { - view.getLocationOnScreen (coordinates); - notify (); - } + view.getLocationOnScreen (coordinates); + return null; } - }; + }); - syncRunnable (runnable); + EmacsService.syncRunnable (task); } @@ -702,28 +699,17 @@ invocation of app_process (through android-emacs) can public ClipboardManager getClipboardManager () { - final EmacsHolder manager; - Runnable runnable; + FutureTask task; - manager = new EmacsHolder (); - - runnable = new Runnable () { - public void - run () + task = new FutureTask (new Callable () { + public Object + call () { - Object tem; - - synchronized (this) - { - tem = getSystemService (Context.CLIPBOARD_SERVICE); - manager.thing = (ClipboardManager) tem; - notify (); - } + return getSystemService (Context.CLIPBOARD_SERVICE); } - }; + }); - syncRunnable (runnable); - return manager.thing; + return (ClipboardManager) EmacsService.syncRunnable (task); } public void @@ -738,33 +724,37 @@ invocation of app_process (through android-emacs) can System.exit (0); } - /* Wait synchronously for the specified RUNNABLE to complete in the - UI thread. Must be called from the Emacs thread. */ + /* Wait synchronously for the specified TASK to complete in the UI + thread, then return its result. Must be called from the Emacs + thread. */ - public static void - syncRunnable (Runnable runnable) + public static V + syncRunnable (FutureTask task) { + V object; + EmacsNative.beginSynchronous (); + SERVICE.runOnUiThread (task); - synchronized (runnable) + try { - SERVICE.runOnUiThread (runnable); - - while (true) - { - try - { - runnable.wait (); - break; - } - catch (InterruptedException e) - { - continue; - } - } + object = task.get (); + } + catch (ExecutionException exception) + { + /* Wrap this exception in a RuntimeException and signal it to + the caller. */ + throw new RuntimeException (exception.getCause ()); + } + catch (InterruptedException exception) + { + EmacsNative.emacsAbort (); + object = null; } EmacsNative.endSynchronous (); + + return object; } @@ -1283,71 +1273,61 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) public int requestDirectoryAccess () { - Runnable runnable; - final EmacsHolder rc; + FutureTask task; /* Return 1 if Android is too old to support this feature. */ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return 1; - rc = new EmacsHolder (); - rc.thing = Integer.valueOf (1); - - runnable = new Runnable () { + task = new FutureTask (new Callable () { @Override - public void - run () + public Integer + call () { EmacsActivity activity; Intent intent; - int id; + int id, rc; + + /* Try to obtain an activity that will receive the response + from the file chooser dialog. */ - synchronized (this) + if (EmacsActivity.focusedActivities.isEmpty ()) { - /* Try to obtain an activity that will receive the - response from the file chooser dialog. */ + /* If focusedActivities is empty then this dialog may + have been displayed immediately after another popup + dialog was dismissed. Try the EmacsActivity to be + focused. */ - if (EmacsActivity.focusedActivities.isEmpty ()) - { - /* If focusedActivities is empty then this dialog - may have been displayed immediately after another - popup dialog was dismissed. Try the - EmacsActivity to be focused. */ - - activity = EmacsActivity.lastFocusedActivity; - - if (activity == null) - { - /* Still no luck. Return failure. */ - notify (); - return; - } - } - else - activity = EmacsActivity.focusedActivities.get (0); + activity = EmacsActivity.lastFocusedActivity; - /* Now create the intent. */ - intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE); + if (activity == null) + /* Still no luck. Return failure. */ + return 1; + } + else + activity = EmacsActivity.focusedActivities.get (0); - try - { - id = EmacsActivity.ACCEPT_DOCUMENT_TREE; - activity.startActivityForResult (intent, id, null); - rc.thing = Integer.valueOf (0); - } - catch (Exception e) - { - e.printStackTrace (); - } + /* Now create the intent. */ + intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE); + rc = 1; - notify (); + try + { + id = EmacsActivity.ACCEPT_DOCUMENT_TREE; + activity.startActivityForResult (intent, id, null); + rc = 0; } + catch (Exception e) + { + e.printStackTrace (); + } + + return rc; } - }; + }); - syncRunnable (runnable); - return rc.thing; + return EmacsService.syncRunnable (task); } /* Return an array of each tree provided by the document PROVIDER @@ -1969,7 +1949,7 @@ In addition, arbitrary runtime exceptions (such as /* Now request these permissions. */ activity.requestPermissions (new String[] { permission, permission1, }, - 0); + 0); } }; commit fe2b68d405251498518b279a17a87f4cbcf82479 Author: Stefan Kangas Date: Sat Dec 30 01:06:41 2023 +0100 Revert "Silence warning when requiring ruby-ts-mode" This reverts commit bf81706988f6b1b9d6e8033c8227f0129e04ef03. We deliberately don't silence these warnings, so that a user who loads the mode without having the grammar installed sees the warning and realizes the problem in time to fix it. diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 284880a0a36..c7e0b8f49ad 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -1206,7 +1206,7 @@ ruby-ts--parser-after-change (syntax-ppss-flush-cache (cl-loop for r in ranges minimize (car r)))))) -(if (treesit-ready-p 'ruby t) +(if (treesit-ready-p 'ruby) ;; Copied from ruby-mode.el. (add-to-list 'auto-mode-alist (cons (concat "\\(?:\\.\\(?:" commit 291763feca34174290345055a6748436839f7606 Author: Eli Zaretskii Date: Fri Dec 29 21:19:50 2023 +0200 Improve documentation of 'require-with-check' * doc/lispref/loading.texi (Named Features): Document 'require-with-check'. * etc/NEWS: Fix wording of entry about 'require-with-check'. diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi index 125011c780f..d35800fb4e0 100644 --- a/doc/lispref/loading.texi +++ b/doc/lispref/loading.texi @@ -1011,6 +1011,19 @@ Named Features @code{require} signals an error about the missing feature. @end defun +@defun require-with-check feature &optional filename noerror +This function works like @code{require}, except if @var{feature} is +already loaded (i.e.@: is already a member of the list in +@code{features}, see below). If @var{feature} is already loaded, this +function checks if @var{feature} was provided by a file different from +@var{filename}, and if so, it by default signals an error. If the +value of the optional argument @var{noerror} is @code{reload}, the +function doesn't signal an error, but instead forcibly reloads +@var{filename}; if @var{noerror} is some other non-@code{nil} value, +the function emits a warning about @var{feature} being already +provided by another file. +@end defun + @defun featurep feature &optional subfeature This function returns @code{t} if @var{feature} has been provided in the current Emacs session (i.e., if @var{feature} is a member of diff --git a/etc/NEWS b/etc/NEWS index f60f3f04158..a174cf52d50 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1361,7 +1361,11 @@ values. * Lisp Changes in Emacs 30.1 ++++ ** New function 'require-with-check' to detect new versions shadowing. +This is like 'require', but it checks whether the argument 'feature' +is already loaded, in which case it either signals an error or +forcibly reloads the file that defines the feature. +++ ** New 'pop-up-frames' action alist entry for 'display-buffer'. commit d7ff14fcba6f0830eeadb98981bb626cf7314c53 Author: Sean Whitton Date: Fri Dec 29 17:41:35 2023 +0000 pcomplete--entries: In predicate, check file exists * lisp/pcomplete.el (pcomplete--entries): In the predicate passed to completion-table-with-predicate, when PREDICATE is nil, take ourselves to be responsible for calling file-exists-p (bug#67661). diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el index 3dde001328d..109968399aa 100644 --- a/lisp/pcomplete.el +++ b/lisp/pcomplete.el @@ -902,14 +902,16 @@ pcomplete--entries (and dir-ignore (string-match dir-ignore file)) (and file-ignore (string-match file-ignore file)))))))) (reg-pred (if regexp (lambda (file) (string-match regexp file)))) - (pred (cond - ((null (or ign-pred reg-pred)) predicate) - ((null (or ign-pred predicate)) reg-pred) - ((null (or reg-pred predicate)) ign-pred) - (t (lambda (f) + ;; `completion-file-name-table' calls `file-exists-p' when + ;; the predicate is nil. + ;; So likewise, defer to PREDICATE if it's there, else take + ;; ourselves to be responsible for calling `file-exists-p'. + (pred (if (or ign-pred reg-pred) + (lambda (f) (and (or (null reg-pred) (funcall reg-pred f)) (or (null ign-pred) (funcall ign-pred f)) - (or (null predicate) (funcall predicate f)))))))) + (funcall (or predicate #'file-exists-p) f))) + predicate))) (lambda (s p a) (if (and (eq a 'metadata) pcomplete-compare-entry-function) `(metadata (cycle-sort-function commit b0bead793f880a9434c38caeaa744f7d7a0e519b Author: Stefan Monnier Date: Fri Dec 29 11:21:38 2023 -0500 * lisp/progmodes/eglot.el (toplevel): Fix usual last minute typo diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 21b627e746a..048cf19213c 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -128,7 +128,7 @@ (load (symbol-name feature) nil 'nomessage))))) (funcall reload 'eldoc nil 'reload) - (funcall reload nil 'reload) + (funcall reload 'seq nil 'reload) ;; For those packages which are not preloaded OTOH, signal an error if ;; the loaded file is not the one that should have been loaded. (mapc reload '(project flymake xref jsonrpc external-completion)))) commit 2a8e6c8c84ed33674e525625644d5ce84ee8c59a Author: Stefan Monnier Date: Fri Dec 29 11:17:51 2023 -0500 (require-with-check): New function (bug#67696) * lisp/files.el (require-with-check): New function. * lisp/progmodes/eglot.el: Use it (and prefer #' to quote function names). diff --git a/etc/NEWS b/etc/NEWS index c002ec33d45..f60f3f04158 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1361,6 +1361,8 @@ values. * Lisp Changes in Emacs 30.1 +** New function 'require-with-check' to detect new versions shadowing. + +++ ** New 'pop-up-frames' action alist entry for 'display-buffer'. This has the same effect as the variable of the same name and takes diff --git a/lisp/files.el b/lisp/files.el index 5efd4309214..caa0622ab08 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1246,6 +1246,29 @@ load-library (interactive (list (read-library-name))) (load library)) +(defun require-with-check (feature &optional filename noerror) + "If FEATURE is not already loaded, load it from FILENAME. +This is like `require' except if FEATURE is already a member of the list +`features’, then we check if this was provided by a different file than the +one that we would load now (presumably because `load-path' has been +changed since the file was loaded). +If it's the case, we either signal an error (the default), or forcibly reload +the new file (if NOERROR is equal to `reload'), or otherwise emit a warning." + (let ((lh load-history) + (res (require feature filename (if (eq noerror 'reload) nil noerror)))) + ;; If the `feature' was not yet provided, `require' just loaded the right + ;; file, so we're done. + (when (eq lh load-history) + ;; If `require' did nothing, we need to make sure that was warranted. + (let ((fn (locate-file (or filename (symbol-name feature)) + load-path (get-load-suffixes)))) + (cond + ((assoc fn load-history) nil) ;We loaded the right file. + ((eq noerror 'reload) (load fn nil 'nomessage)) + (t (funcall (if noerror #'warn #'error) + "Feature provided by other file: %S" feature))))) + res)) + (defun file-remote-p (file &optional identification connected) "Test whether FILE specifies a location on a remote system. A file is considered remote if accessing it is likely to diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f267d089e3a..21b627e746a 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -116,13 +116,22 @@ ;; having installed them, didn't correctly re-load them over the ;; built-in versions. (eval-and-compile - (load "project") - (load "eldoc") - (load "seq") - (load "flymake") - (load "xref") - (load "jsonrpc") - (load "external-completion")) + ;; For those packages that are preloaded, reload them if needed, + ;; since that's the best we can do anyway. + ;; FIXME: Maybe the ELPA packages for those preloaded packages should + ;; force-reload themselves eagerly when the package is activated! + (let ((reload (if (fboundp 'require-with-check) ;Emacs≥30 + #'require-with-check + (lambda (feature &rest _) + ;; Just blindly reload like we used to do before + ;; `require-with-check'. + (load (symbol-name feature) nil 'nomessage))))) + + (funcall reload 'eldoc nil 'reload) + (funcall reload nil 'reload) + ;; For those packages which are not preloaded OTOH, signal an error if + ;; the loaded file is not the one that should have been loaded. + (mapc reload '(project flymake xref jsonrpc external-completion)))) ;; forward-declare, but don't require (Emacs 28 doesn't seem to care) (defvar markdown-fontify-code-blocks-natively) @@ -140,27 +149,27 @@ 'eglot-confirm-server-initiated-edits 'eglot-confirm-server-edits "1.16") (make-obsolete-variable 'eglot-events-buffer-size 'eglot-events-buffer-config "1.16") -(define-obsolete-function-alias 'eglot--uri-to-path 'eglot-uri-to-path "1.16") -(define-obsolete-function-alias 'eglot--path-to-uri 'eglot-path-to-uri "1.16") -(define-obsolete-function-alias 'eglot--range-region 'eglot-range-region "1.16") -(define-obsolete-function-alias 'eglot--server-capable 'eglot-server-capable "1.16") -(define-obsolete-function-alias 'eglot--server-capable-or-lose 'eglot-server-capable-or-lose "1.16") +(define-obsolete-function-alias 'eglot--uri-to-path #'eglot-uri-to-path "1.16") +(define-obsolete-function-alias 'eglot--path-to-uri #'eglot-path-to-uri "1.16") +(define-obsolete-function-alias 'eglot--range-region #'eglot-range-region "1.16") +(define-obsolete-function-alias 'eglot--server-capable #'eglot-server-capable "1.16") +(define-obsolete-function-alias 'eglot--server-capable-or-lose #'eglot-server-capable-or-lose "1.16") (define-obsolete-function-alias - 'eglot-lsp-abiding-column 'eglot-utf-16-linepos "1.12") + 'eglot-lsp-abiding-column #'eglot-utf-16-linepos "1.12") (define-obsolete-function-alias - 'eglot-current-column 'eglot-utf-32-linepos "1.12") + 'eglot-current-column #'eglot-utf-32-linepos "1.12") (define-obsolete-variable-alias 'eglot-current-column-function 'eglot-current-linepos-function "1.12") (define-obsolete-function-alias - 'eglot-move-to-current-column 'eglot-move-to-utf-32-linepos "1.12") + 'eglot-move-to-current-column #'eglot-move-to-utf-32-linepos "1.12") (define-obsolete-function-alias - 'eglot-move-to-lsp-abiding-column 'eglot-move-to-utf-16-linepos "1.12") + 'eglot-move-to-lsp-abiding-column #'eglot-move-to-utf-16-linepos "1.12") (define-obsolete-variable-alias -'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12") + 'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12") (define-obsolete-variable-alias 'eglot-ignored-server-capabilites 'eglot-ignored-server-capabilities "1.8") ;;;###autoload -(define-obsolete-function-alias 'eglot-update 'eglot-upgrade-eglot "29.1") +(define-obsolete-function-alias 'eglot-update #'eglot-upgrade-eglot "29.1") ;;; User tweakable stuff @@ -1926,23 +1935,23 @@ eglot--managed-mode ("utf-8" (eglot--setq-saving eglot-current-linepos-function #'eglot-utf-8-linepos) (eglot--setq-saving eglot-move-to-linepos-function #'eglot-move-to-utf-8-linepos))) - (add-hook 'after-change-functions 'eglot--after-change nil t) - (add-hook 'before-change-functions 'eglot--before-change nil t) + (add-hook 'after-change-functions #'eglot--after-change nil t) + (add-hook 'before-change-functions #'eglot--before-change nil t) (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t) ;; Prepend "didClose" to the hook after the "nonoff", so it will run first - (add-hook 'kill-buffer-hook 'eglot--signal-textDocument/didClose nil t) - (add-hook 'before-revert-hook 'eglot--signal-textDocument/didClose nil t) - (add-hook 'after-revert-hook 'eglot--after-revert-hook nil t) - (add-hook 'before-save-hook 'eglot--signal-textDocument/willSave nil t) - (add-hook 'after-save-hook 'eglot--signal-textDocument/didSave nil t) + (add-hook 'kill-buffer-hook #'eglot--signal-textDocument/didClose nil t) + (add-hook 'before-revert-hook #'eglot--signal-textDocument/didClose nil t) + (add-hook 'after-revert-hook #'eglot--after-revert-hook nil t) + (add-hook 'before-save-hook #'eglot--signal-textDocument/willSave nil t) + (add-hook 'after-save-hook #'eglot--signal-textDocument/didSave nil t) (unless (eglot--stay-out-of-p 'xref) - (add-hook 'xref-backend-functions 'eglot-xref-backend nil t)) + (add-hook 'xref-backend-functions #'eglot-xref-backend nil t)) (add-hook 'completion-at-point-functions #'eglot-completion-at-point nil t) (add-hook 'completion-in-region-mode-hook #'eglot--capf-session-flush nil t) (add-hook 'company-after-completion-hook #'eglot--capf-session-flush nil t) (add-hook 'change-major-mode-hook #'eglot--managed-mode-off nil t) - (add-hook 'post-self-insert-hook 'eglot--post-self-insert-hook nil t) - (add-hook 'pre-command-hook 'eglot--pre-command-hook nil t) + (add-hook 'post-self-insert-hook #'eglot--post-self-insert-hook nil t) + (add-hook 'pre-command-hook #'eglot--pre-command-hook nil t) (eglot--setq-saving xref-prompt-for-identifier nil) (eglot--setq-saving flymake-diagnostic-functions '(eglot-flymake-backend)) (eglot--setq-saving company-backends '(company-capf)) @@ -1961,21 +1970,21 @@ eglot--managed-mode (eldoc-mode 1)) (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) (t - (remove-hook 'after-change-functions 'eglot--after-change t) - (remove-hook 'before-change-functions 'eglot--before-change t) + (remove-hook 'after-change-functions #'eglot--after-change t) + (remove-hook 'before-change-functions #'eglot--before-change t) (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t) - (remove-hook 'kill-buffer-hook 'eglot--signal-textDocument/didClose t) - (remove-hook 'before-revert-hook 'eglot--signal-textDocument/didClose t) - (remove-hook 'after-revert-hook 'eglot--after-revert-hook t) - (remove-hook 'before-save-hook 'eglot--signal-textDocument/willSave t) - (remove-hook 'after-save-hook 'eglot--signal-textDocument/didSave t) - (remove-hook 'xref-backend-functions 'eglot-xref-backend t) + (remove-hook 'kill-buffer-hook #'eglot--signal-textDocument/didClose t) + (remove-hook 'before-revert-hook #'eglot--signal-textDocument/didClose t) + (remove-hook 'after-revert-hook #'eglot--after-revert-hook t) + (remove-hook 'before-save-hook #'eglot--signal-textDocument/willSave t) + (remove-hook 'after-save-hook #'eglot--signal-textDocument/didSave t) + (remove-hook 'xref-backend-functions #'eglot-xref-backend t) (remove-hook 'completion-at-point-functions #'eglot-completion-at-point t) (remove-hook 'completion-in-region-mode-hook #'eglot--capf-session-flush t) (remove-hook 'company-after-completion-hook #'eglot--capf-session-flush t) (remove-hook 'change-major-mode-hook #'eglot--managed-mode-off t) - (remove-hook 'post-self-insert-hook 'eglot--post-self-insert-hook t) - (remove-hook 'pre-command-hook 'eglot--pre-command-hook t) + (remove-hook 'post-self-insert-hook #'eglot--post-self-insert-hook t) + (remove-hook 'pre-command-hook #'eglot--pre-command-hook t) (remove-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function t) (remove-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function t) (cl-loop for (var . saved-binding) in eglot--saved-bindings @@ -2042,7 +2051,7 @@ eglot--maybe-activate-editing-mode (eglot-inlay-hints-mode 1) (run-hooks 'eglot-managed-mode-hook)))) -(add-hook 'after-change-major-mode-hook 'eglot--maybe-activate-editing-mode) +(add-hook 'after-change-major-mode-hook #'eglot--maybe-activate-editing-mode) (defun eglot-clear-status (server) "Clear the last JSONRPC error for SERVER." @@ -2215,12 +2224,12 @@ eglot--mode-line-format (put 'eglot-warning 'flymake-category 'flymake-warning) (put 'eglot-error 'flymake-category 'flymake-error) -(defalias 'eglot--make-diag 'flymake-make-diagnostic) -(defalias 'eglot--diag-data 'flymake-diagnostic-data) +(defalias 'eglot--make-diag #'flymake-make-diagnostic) +(defalias 'eglot--diag-data #'flymake-diagnostic-data) (defvar eglot-diagnostics-map (let ((map (make-sparse-keymap))) - (define-key map [mouse-2] 'eglot-code-actions-at-mouse) + (define-key map [mouse-2] #'eglot-code-actions-at-mouse) map) "Keymap active in Eglot-backed Flymake diagnostic overlays.") @@ -2618,7 +2627,7 @@ eglot-workspace-configuration format described above.") ;;;###autoload -(put 'eglot-workspace-configuration 'safe-local-variable 'listp) +(put 'eglot-workspace-configuration 'safe-local-variable #'listp) (defun eglot-show-workspace-configuration (&optional server) "Dump `eglot-workspace-configuration' as JSON for debugging." commit f27dd1f954bcc6f35f4248370a7fe09de5f66096 Author: Eli Zaretskii Date: Fri Dec 29 14:32:56 2023 +0200 ; Fix style of comments and doc strings in last change in ses.el * lisp/ses.el (ses-cell-set-formula): Fix comments style. (cl-member): Fix byte-compilation warning. * test/lisp/ses-tests.el (ses-set-formula-write-cells-with-changed-references): Doc fix. diff --git a/lisp/ses.el b/lisp/ses.el index b5193c3e4b8..618a85aab70 100644 --- a/lisp/ses.el +++ b/lisp/ses.el @@ -69,6 +69,8 @@ (require 'macroexp) (eval-when-compile (require 'cl-lib)) +;; Autoloaded, but we have not loaded cl-loaddefs yet. +(declare-function cl-member "cl-seq" (cl-item cl-list &rest cl-keys)) ;;---------------------------------------------------------------------------- ;; User-customizable variables @@ -890,15 +892,15 @@ ses-cell-set-formula ;;in the new list. (dolist (ref oldref) (unless (memq ref newref) - ;; because we do not cancel edit when the user provides a + ;; Because we do not cancel edit when the user provides a ;; false reference in it, then we need to check that ref ;; points to a cell that is within the spreadsheet. (when (and (setq x (ses-sym-rowcol ref)) (< (setq xrow (car x)) ses--numrows) (< (setq xcol (cdr x)) ses--numcols)) - ;; cell ref has to be re-written to data area as its - ;; reference list is changed + ;; Cell reference has to be re-written to data area as its + ;; reference list is changed. (cl-pushnew x ses--deferred-write :test #'equal) (ses-set-cell xrow xcol 'references (delq sym (ses-cell-references xrow xcol)))))) @@ -910,8 +912,8 @@ ses-cell-set-formula (< (setq xrow (car x)) ses--numrows) (< (setq xcol (cdr x)) ses--numcols)) (unless (memq sym (setq xref (ses-cell-references xrow xcol))) - ;; cell ref has to be re-written to data area as its - ;; reference list is changed + ;; Cell reference has to be re-written to data area as + ;; its reference list is changed. (cl-pushnew x ses--deferred-write :test #'equal) (ses-set-cell xrow xcol 'references (cons sym xref))) (cl-pushnew ref not-a-cell-ref-list))) diff --git a/test/lisp/ses-tests.el b/test/lisp/ses-tests.el index e2ff41c5875..265b448226f 100644 --- a/test/lisp/ses-tests.el +++ b/test/lisp/ses-tests.el @@ -242,8 +242,9 @@ ses-jump-B2-renamed (should (eq (ses--cell-at-pos (point)) 'ses--toto))))) (ert-deftest ses-set-formula-write-cells-with-changed-references () - "Fix of bug#5852. When setting a formula has some cell with -changed references, this cell has to be rewritten to data area." + "Test fix of bug#5852. +When setting a formula has some cell with changed references, this +cell has to be rewritten to data area." (let ((ses-initial-size '(4 . 3)) ses-after-entry-functions beg) (with-temp-buffer commit 825b896acfe2b2d2b3a8f29b4ed1d5810d222663 Author: Vincent Belaïche Date: Fri Dec 29 12:11:51 2023 +0100 Fix 13 years old bug#5852 ! Sometimes recalculating a cell does not change its value, however it still have to be written to the data area when the reference list is changed * lisp/ses.el (ses-cell-set-formula): Fix bug#5852, at last ! * test/lisp/ses-tests.el (ses-set-formula-write-cells-with-changed-references): Rename test from ses-bug5852, do not expect failure, and update description. diff --git a/lisp/ses.el b/lisp/ses.el index 02ed2faae3c..b5193c3e4b8 100644 --- a/lisp/ses.el +++ b/lisp/ses.el @@ -884,7 +884,7 @@ ses-cell-set-formula (newref (ses-formula-references formula)) (inhibit-quit t) not-a-cell-ref-list - x xrow xcol) + x xref xrow xcol) (cl-pushnew sym ses--deferred-recalc) ;;Delete old references from this cell. Skip the ones that are also ;;in the new list. @@ -893,24 +893,27 @@ ses-cell-set-formula ;; because we do not cancel edit when the user provides a ;; false reference in it, then we need to check that ref ;; points to a cell that is within the spreadsheet. - (setq x (ses-sym-rowcol ref)) - (and x - (< (setq xrow (car x)) ses--numrows) - (< (setq xcol (cdr x)) ses--numcols) - (ses-set-cell xrow xcol 'references - (delq sym (ses-cell-references xrow xcol)))))) + (when + (and (setq x (ses-sym-rowcol ref)) + (< (setq xrow (car x)) ses--numrows) + (< (setq xcol (cdr x)) ses--numcols)) + ;; cell ref has to be re-written to data area as its + ;; reference list is changed + (cl-pushnew x ses--deferred-write :test #'equal) + (ses-set-cell xrow xcol 'references + (delq sym (ses-cell-references xrow xcol)))))) ;;Add new ones. Skip ones left over from old list (dolist (ref newref) - (setq x (ses-sym-rowcol ref)) ;;Do not trust the user, the reference may be outside the spreadsheet (if (and - x + (setq x (ses-sym-rowcol ref)) (< (setq xrow (car x)) ses--numrows) (< (setq xcol (cdr x)) ses--numcols)) - (progn - (setq x (ses-cell-references xrow xcol)) - (or (memq sym x) - (ses-set-cell xrow xcol 'references (cons sym x)))) + (unless (memq sym (setq xref (ses-cell-references xrow xcol))) + ;; cell ref has to be re-written to data area as its + ;; reference list is changed + (cl-pushnew x ses--deferred-write :test #'equal) + (ses-set-cell xrow xcol 'references (cons sym xref))) (cl-pushnew ref not-a-cell-ref-list))) (ses-formula-record formula) (ses-set-cell row col 'formula formula) diff --git a/test/lisp/ses-tests.el b/test/lisp/ses-tests.el index 9983ec0d09f..e2ff41c5875 100644 --- a/test/lisp/ses-tests.el +++ b/test/lisp/ses-tests.el @@ -241,13 +241,9 @@ ses-jump-B2-renamed (ses-command-hook) (should (eq (ses--cell-at-pos (point)) 'ses--toto))))) -(ert-deftest ses-bug5852 () - "This this bug is not yet fixed, the test is expected to fail. -The bug is that after modifying formula of B4 reference list of -cell B2 is correct in the memory data structure, but not in the -written ses-cell macros in the data area, this is why the second -`should' statement fails after reloading the sheet." - :expected-result :failed +(ert-deftest ses-set-formula-write-cells-with-changed-references () + "Fix of bug#5852. When setting a formula has some cell with +changed references, this cell has to be rewritten to data area." (let ((ses-initial-size '(4 . 3)) ses-after-entry-functions beg) (with-temp-buffer commit b96087d42714ebd4cbf610fc01eebc4f2adae5f7 Author: Vincent Belaïche Date: Fri Dec 29 12:29:20 2023 +0100 Simplify test ses-bug5852 * test/lisp/ses-tests.el (ses-bug5852): Siumplify test to demonstrate that the bug is not connected to yanking, but has to do with ses-cell-set-formula. diff --git a/test/lisp/ses-tests.el b/test/lisp/ses-tests.el index 3cc48f8a34d..9983ec0d09f 100644 --- a/test/lisp/ses-tests.el +++ b/test/lisp/ses-tests.el @@ -1,4 +1,4 @@ -;;; ses-tests.el --- Tests for ses.el -*- lexical-binding: t; -*- +;;; SES-tests.el --- Tests for ses.el -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2023 Free Software Foundation, Inc. @@ -243,11 +243,10 @@ ses-jump-B2-renamed (ert-deftest ses-bug5852 () "This this bug is not yet fixed, the test is expected to fail. -The bug is that after the second yank of the same formula the -reference list of cell B2 is correct in the memory data -structure, but not in the written ses-cell macros in the data -area, this is why the second should statement fails after -reloading the sheet." +The bug is that after modifying formula of B4 reference list of +cell B2 is correct in the memory data structure, but not in the +written ses-cell macros in the data area, this is why the second +`should' statement fails after reloading the sheet." :expected-result :failed (let ((ses-initial-size '(4 . 3)) ses-after-entry-functions beg) @@ -259,15 +258,9 @@ ses-bug5852 (3 0 3) (3 1 (+ B2 A4))));A4 B4 (apply 'ses-cell-set-formula c) (apply 'ses-calculate-cell (list (car c) (cadr c) nil))) - (ses-jump 'B2) - (setq beg (point)) - (ses-jump 'C2) - (kill-ring-save beg (point)) - (ses-jump 'B3) - (yank) + (ses-cell-set-formula 2 1 '(+ B2 A3)); B3 (ses-command-hook) - (ses-jump 'B4) - (yank) + (ses-cell-set-formula 3 1 (+ B3 A4)); B4 (ses-command-hook) (should (equal (ses-cell-references 1 1) '(B3))) (ses-mode) commit 0ffcfd015d383e4f04ea41af7ac480a27c6f5da5 Author: Vincent Belaïche Date: Thu Dec 28 19:08:46 2023 +0100 Add test reproducting bug #5852 diff --git a/test/lisp/ses-tests.el b/test/lisp/ses-tests.el index a941605a4d8..3cc48f8a34d 100644 --- a/test/lisp/ses-tests.el +++ b/test/lisp/ses-tests.el @@ -241,6 +241,38 @@ ses-jump-B2-renamed (ses-command-hook) (should (eq (ses--cell-at-pos (point)) 'ses--toto))))) +(ert-deftest ses-bug5852 () + "This this bug is not yet fixed, the test is expected to fail. +The bug is that after the second yank of the same formula the +reference list of cell B2 is correct in the memory data +structure, but not in the written ses-cell macros in the data +area, this is why the second should statement fails after +reloading the sheet." + :expected-result :failed + (let ((ses-initial-size '(4 . 3)) + ses-after-entry-functions beg) + (with-temp-buffer + (ses-mode) + (dolist (c '((0 1 1); B1 + (1 0 2) (1 1 (+ B1 A2)); A2 B2 + (2 0 4); A3 + (3 0 3) (3 1 (+ B2 A4))));A4 B4 + (apply 'ses-cell-set-formula c) + (apply 'ses-calculate-cell (list (car c) (cadr c) nil))) + (ses-jump 'B2) + (setq beg (point)) + (ses-jump 'C2) + (kill-ring-save beg (point)) + (ses-jump 'B3) + (yank) + (ses-command-hook) + (ses-jump 'B4) + (yank) + (ses-command-hook) + (should (equal (ses-cell-references 1 1) '(B3))) + (ses-mode) + (should (equal (ses-cell-references 1 1) '(B3)))))) + (provide 'ses-tests) ;;; ses-tests.el ends here commit ae9657e1f4a01552d5ab1602fffa817f1fe2e56c Author: Stefan Kangas Date: Fri Dec 29 11:39:28 2023 +0100 ; Declare unused cperl-mode function obsolete * lisp/progmodes/cperl-mode.el (cperl-font-lock-unfontify-region-function): Make unused function obsolete. diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el index 46ef155d07c..d9d907b1846 100644 --- a/lisp/progmodes/cperl-mode.el +++ b/lisp/progmodes/cperl-mode.el @@ -8823,6 +8823,7 @@ cperl-get-help-defer ;;; Plug for wrong font-lock: (defun cperl-font-lock-unfontify-region-function (beg end) + (declare (obsolete nil "30.1")) (with-silent-modifications (remove-text-properties beg end '(face nil)))) commit 5cf22cacda0d14155794cba8c26549e05f073440 Author: Stefan Kangas Date: Fri Dec 29 11:25:53 2023 +0100 ; Delete some commented out code from ange-ftp.el * lisp/net/ange-ftp.el: Delete some code commented out since 2002. diff --git a/lisp/net/ange-ftp.el b/lisp/net/ange-ftp.el index a1eb57baa20..13b7e502141 100644 --- a/lisp/net/ange-ftp.el +++ b/lisp/net/ange-ftp.el @@ -4404,40 +4404,6 @@ ange-ftp-hook-function (error (signal (car err) (cdr err)))) (ange-ftp-run-real-handler operation args)))) -;; The following code is commented out because Tramp now deals with -;; Ange-FTP filenames, too. - -;;-;;; This regexp takes care of real ange-ftp file names (with a slash -;;-;;; and colon). -;;-;;; Don't allow the host name to end in a period--some systems use /.: -;;-;;;###autoload -;;-(or (assoc "^/[^/:]*[^/:.]:" file-name-handler-alist) -;;- (setq file-name-handler-alist -;;- (cons '("^/[^/:]*[^/:.]:" . ange-ftp-hook-function) -;;- file-name-handler-alist))) -;;- -;;-;;; This regexp recognizes absolute filenames with only one component, -;;-;;; for the sake of hostname completion. -;;-;;;###autoload -;;-(or (assoc "^/[^/:]*\\'" file-name-handler-alist) -;;- (setq file-name-handler-alist -;;- (cons '("^/[^/:]*\\'" . ange-ftp-completion-hook-function) -;;- file-name-handler-alist))) -;;- -;;-;;; This regexp recognizes absolute filenames with only one component -;;-;;; on Windows, for the sake of hostname completion. -;;-;;; NB. Do not mark this as autoload, because it is very common to -;;-;;; do completions in the root directory of drives on Windows. -;;-(and (memq system-type '(ms-dos windows-nt)) -;;- (or (assoc "^[a-zA-Z]:/[^/:]*\\'" file-name-handler-alist) -;;- (setq file-name-handler-alist -;;- (cons '("^[a-zA-Z]:/[^/:]*\\'" . -;;- ange-ftp-completion-hook-function) -;;- file-name-handler-alist)))) - -;;; The above two forms are sufficient to cause this file to be loaded -;;; if the user ever uses a file name with a colon in it. - ;;; This sets the mode (add-hook 'find-file-hook 'ange-ftp-set-buffer-mode) commit 82eb4f00203f912cc0be902c8151c07c4792425d Author: Stefan Kangas Date: Fri Dec 29 11:24:48 2023 +0100 Don't recommend using `cperl-hairy` Enabling `cperl-hairy` makes cperl-mode break many Emacs conventions and can be confusing to new users. Let's not say that this is the "recommended mode of use", instead leaving it up to the user. * lisp/progmodes/cperl-mode.el (cperl-mode): Don't recommend using `cperl-hairy'. diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el index 58cf2728f61..46ef155d07c 100644 --- a/lisp/progmodes/cperl-mode.el +++ b/lisp/progmodes/cperl-mode.el @@ -1696,19 +1696,18 @@ cperl-mode \\{cperl-mode-map} -Setting the variable `cperl-font-lock' to t switches on `font-lock-mode' -\(even with older Emacsen), `cperl-electric-lbrace-space' to t switches -on electric space between $ and {, `cperl-electric-parens-string' is -the string that contains parentheses that should be electric in CPerl -\(see also `cperl-electric-parens-mark' and `cperl-electric-parens'), -setting `cperl-electric-keywords' enables electric expansion of -control structures in CPerl. `cperl-electric-linefeed' governs which -one of two linefeed behavior is preferable. You can enable all these -options simultaneously (recommended mode of use) by setting -`cperl-hairy' to t. In this case you can switch separate options off -by setting them to `null'. Note that one may undo the extra -whitespace inserted by semis and braces in `auto-newline'-mode by -consequent \\[cperl-electric-backspace]. +Setting the variable `cperl-font-lock' to t switches on `font-lock-mode', +`cperl-electric-lbrace-space' to t switches on electric space between $ +and {, `cperl-electric-parens-string' is the string that contains +parentheses that should be electric in CPerl (see also +`cperl-electric-parens-mark' and `cperl-electric-parens'), setting +`cperl-electric-keywords' enables electric expansion of control +structures in CPerl. `cperl-electric-linefeed' governs which one of two +linefeed behavior is preferable. You can enable all these options +simultaneously by setting `cperl-hairy' to t. In this case you can +switch separate options off by setting them to `null'. Note that one may +undo the extra whitespace inserted by semis and braces in +`auto-newline'-mode by consequent \\[cperl-electric-backspace]. Short one-liner-style help is available on \\[cperl-get-help], and one can run perldoc or man via menu.