From: Lars Ingebrigtsen Date: Sat, 31 Jul 2021 15:44:43 +0000 (+0200) Subject: Adjust how `replace-match' runs modification hooks X-Git-Tag: emacs-28.0.90~1625 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=f6c5a801efdad6cc7ed16ac0b1fa53599b84bc91;p=emacs.git Adjust how `replace-match' runs modification hooks * src/editfns.c (Fsubst_char_in_region) (Ftranslate_region_internal): * src/cmds.c (internal_self_insert): Update callers. * src/insdel.c (replace_range): Allow inhibiting signal_after_change/update_compositions. * src/lisp.h: Update. * src/search.c (Freplace_match): Run the modification hooks at the end instead of before adjusting point (bug#42424). --- diff --git a/etc/NEWS b/etc/NEWS index ef115d0ae01..40a5e512fb2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2920,6 +2920,15 @@ This is to keep the same behavior as Eshell. * Incompatible Lisp Changes in Emacs 28.1 +--- +** 'replace-match' now runs modification hooks slightly later. +The function is documented to leave point after the replacement text, +but this was not always the case if a modification hook inserted text +in front of the replaced text -- 'replace-match' would instead leave +point where the end of the inserted text would have been before the +hook ran. 'replace-match' now always leaves point after the +replacement text. + --- ** 'kill-all-local-variables' has changed how it handles non-symbol hooks. The function is documented to eliminate all buffer-local bindings diff --git a/src/cmds.c b/src/cmds.c index c8a96d918cd..00fde0ef79b 100644 --- a/src/cmds.c +++ b/src/cmds.c @@ -455,7 +455,7 @@ internal_self_insert (int c, EMACS_INT n) ptrdiff_t to; if (INT_ADD_WRAPV (PT, chars_to_delete, &to)) to = PTRDIFF_MAX; - replace_range (PT, to, string, 1, 1, 1, 0); + replace_range (PT, to, string, 1, 1, 1, 0, false); Fforward_char (make_fixnum (n)); } else if (n > 1) diff --git a/src/editfns.c b/src/editfns.c index 8ab17ebc9f9..c8219decb06 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -2371,7 +2371,7 @@ Both characters must have the same length of multi-byte form. */) /* replace_range is less efficient, because it moves the gap, but it handles combining correctly. */ replace_range (pos, pos + 1, string, - false, false, true, false); + false, false, true, false, false); pos_byte_next = CHAR_TO_BYTE (pos); if (pos_byte_next > pos_byte) /* Before combining happened. We should not increment @@ -2578,7 +2578,7 @@ It returns the number of characters changed. */) but it should handle multibyte characters correctly. */ string = make_multibyte_string ((char *) str, 1, str_len); replace_range (pos, pos + 1, string, - true, false, true, false); + true, false, true, false, false); len = str_len; } else @@ -2613,7 +2613,8 @@ It returns the number of characters changed. */) = (VECTORP (val) ? Fconcat (1, &val) : Fmake_string (make_fixnum (1), val, Qnil)); - replace_range (pos, pos + len, string, true, false, true, false); + replace_range (pos, pos + len, string, true, false, true, false, + false); pos_byte += SBYTES (string); pos += SCHARS (string); characters_changed += SCHARS (string); diff --git a/src/insdel.c b/src/insdel.c index e66120eb08a..40674e15e45 100644 --- a/src/insdel.c +++ b/src/insdel.c @@ -1392,7 +1392,7 @@ adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte, void replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, bool prepare, bool inherit, bool markers, - bool adjust_match_data) + bool adjust_match_data, bool inhibit_mod_hooks) { ptrdiff_t inschars = SCHARS (new); ptrdiff_t insbytes = SBYTES (new); @@ -1552,8 +1552,11 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, if (adjust_match_data) update_search_regs (from, to, from + SCHARS (new)); - signal_after_change (from, nchars_del, GPT - from); - update_compositions (from, GPT, CHECK_BORDER); + if (!inhibit_mod_hooks) + { + signal_after_change (from, nchars_del, GPT - from); + update_compositions (from, GPT, CHECK_BORDER); + } } /* Replace the text from character positions FROM to TO with diff --git a/src/lisp.h b/src/lisp.h index 15a42a44562..1206a0d1f6c 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3717,7 +3717,8 @@ extern void adjust_markers_for_delete (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t); extern void adjust_markers_bytepos (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, int); -extern void replace_range (ptrdiff_t, ptrdiff_t, Lisp_Object, bool, bool, bool, bool); +extern void replace_range (ptrdiff_t, ptrdiff_t, Lisp_Object, bool, bool, + bool, bool, bool); extern void replace_range_2 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, const char *, ptrdiff_t, ptrdiff_t, bool); extern void syms_of_insdel (void); diff --git a/src/search.c b/src/search.c index df384e1dcff..14adeb58e96 100644 --- a/src/search.c +++ b/src/search.c @@ -30,6 +30,7 @@ along with GNU Emacs. If not, see . */ #include "blockinput.h" #include "intervals.h" #include "pdumper.h" +#include "composite.h" #include "regex-emacs.h" @@ -2725,7 +2726,7 @@ since only regular expressions have distinguished subexpressions. */) newpoint = sub_start + SCHARS (newtext); /* Replace the old text with the new in the cleanest possible way. */ - replace_range (sub_start, sub_end, newtext, 1, 0, 1, true); + replace_range (sub_start, sub_end, newtext, 1, 0, 1, true, true); if (case_action == all_caps) Fupcase_region (make_fixnum (search_regs.start[sub]), @@ -2750,6 +2751,9 @@ since only regular expressions have distinguished subexpressions. */) /* Now move point "officially" to the end of the inserted replacement. */ move_if_not_intangible (newpoint); + signal_after_change (sub_start, sub_end - sub_start, SCHARS (newtext)); + update_compositions (sub_start, newpoint, CHECK_BORDER); + return Qnil; } diff --git a/test/src/search-tests.el b/test/src/search-tests.el new file mode 100644 index 00000000000..b7b4ab9a8ff --- /dev/null +++ b/test/src/search-tests.el @@ -0,0 +1,42 @@ +;;; search-tests.el --- tests for search.c functions -*- lexical-binding: t -*- + +;; Copyright (C) 2015-2016, 2018-2021 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert) + +(ert-deftest test-replace-match-modification-hooks () + (let ((ov-set nil)) + (with-temp-buffer + (insert "1 abc") + (setq ov-set (make-overlay 3 5)) + (overlay-put + ov-set 'modification-hooks + (list (lambda (o after &rest _args) + (when after + (let ((inhibit-modification-hooks t)) + (save-excursion + (goto-char 2) + (insert "234"))))))) + (goto-char 3) + (if (search-forward "bc") + (replace-match "bcd")) + (should (= (point) 10))))) + +;;; search-tests.el ends here