aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Monnier2025-03-28 00:46:53 -0400
committerStefan Monnier2025-03-29 17:49:05 -0400
commit7c82cc8b975175aebbad1c43ec1cd98b3232f482 (patch)
tree2f2f5ac19ec7055442da0bd58507314d213b9bdd /src
parentf60fc1287d499e8c93857b1b96e8bd2467b22c8d (diff)
downloademacs-7c82cc8b975175aebbad1c43ec1cd98b3232f482.tar.gz
emacs-7c82cc8b975175aebbad1c43ec1cd98b3232f482.zip
(replace-region-contents): Improve and promote (bug#76313)
Swap the role of `replace-region-contents` and `replace-buffer-contents`, so `replace-region-contents` is the main function, implemented in C, and `replace-buffer-contents` is a mere wrapper (marked as obsolete). Also remove the need to rely on narrowing and on describing the new text as a function. Finally, allow MAX-SECS==0 to require a cheap replacement, and add an INHERIT argument. * src/editfns.c: Include `coding.h`. (Freplace_region_contents): Rename from `Freplace_buffer_contents`. Change calling convention to that of `replace-region-contents`. Add more options for the SOURCE argument. Add INHERIT argument. Skip the costly algorithm if MAX-SECS is 0. * src/insdel.c (replace_range): Allow NEW to be a buffer. * lisp/subr.el (replace-buffer-contents): New implementation. * lisp/emacs-lisp/subr-x.el (replace-region-contents): Delete. * doc/lispref/text.texi (Replacing): Document new API for `replace-region-contents`. Remove documentation of `replace-buffer-contents`. * test/src/editfns-tests.el (replace-buffer-contents-1) (replace-buffer-contents-2, replace-buffer-contents-bug31837): Use `replace-region-contents`. (editfns--replace-region): Delete. (editfns-tests--replace-region): Use `replace-region-contents`. Adds tests for new types of SOURCE args.
Diffstat (limited to 'src')
-rw-r--r--src/coding.c2
-rw-r--r--src/editfns.c150
-rw-r--r--src/insdel.c12
3 files changed, 113 insertions, 51 deletions
diff --git a/src/coding.c b/src/coding.c
index b0bd5d3a9ab..63b0dbeb18b 100644
--- a/src/coding.c
+++ b/src/coding.c
@@ -7898,6 +7898,8 @@ code_conversion_save (bool with_work_buf, bool multibyte)
7898 bset_enable_multibyte_characters (current_buffer, multibyte ? Qt : Qnil); 7898 bset_enable_multibyte_characters (current_buffer, multibyte ? Qt : Qnil);
7899 if (EQ (workbuf, Vcode_conversion_reused_workbuf)) 7899 if (EQ (workbuf, Vcode_conversion_reused_workbuf))
7900 reused_workbuf_in_use = true; 7900 reused_workbuf_in_use = true;
7901 /* FIXME: Maybe we should stay in the new workbuf, because we often
7902 switch right back to it anyway in order to initialize it further. */
7901 set_buffer_internal (current); 7903 set_buffer_internal (current);
7902 } 7904 }
7903 7905
diff --git a/src/editfns.c b/src/editfns.c
index 53d6cce7c82..25625793c42 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -54,6 +54,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
54#include "buffer.h" 54#include "buffer.h"
55#include "window.h" 55#include "window.h"
56#include "blockinput.h" 56#include "blockinput.h"
57#include "coding.h"
57 58
58#ifdef WINDOWSNT 59#ifdef WINDOWSNT
59# include "w32common.h" 60# include "w32common.h"
@@ -1914,11 +1915,14 @@ static bool compareseq_early_abort (struct context *);
1914#include "minmax.h" 1915#include "minmax.h"
1915#include "diffseq.h" 1916#include "diffseq.h"
1916 1917
1917DEFUN ("replace-buffer-contents", Freplace_buffer_contents, 1918DEFUN ("replace-region-contents", Freplace_region_contents,
1918 Sreplace_buffer_contents, 1, 3, "bSource buffer: ", 1919 Sreplace_region_contents, 3, 6, 0,
1919 doc: /* Replace accessible portion of current buffer with that of SOURCE. 1920 doc: /* Replace the region between BEG and END with that of SOURCE.
1920SOURCE can be a buffer or a string that names a buffer. 1921SOURCE can be a buffer, a string, or a vector [SBUF SBEG SEND]
1921Interactively, prompt for SOURCE. 1922denoting the subtring SBEG..SEND of buffer SBUF.
1923
1924If optional argument INHERIT is non-nil, the inserted text will inherit
1925properties from adjoining text.
1922 1926
1923As far as possible the replacement is non-destructive, i.e. existing 1927As far as possible the replacement is non-destructive, i.e. existing
1924buffer contents, markers, properties, and overlays in the current 1928buffer contents, markers, properties, and overlays in the current
@@ -1940,18 +1944,85 @@ computation. If the actual costs exceed this limit, heuristics are
1940used to provide a faster but suboptimal solution. The default value 1944used to provide a faster but suboptimal solution. The default value
1941is 1000000. 1945is 1000000.
1942 1946
1947Note: If the replacement is a string, it’ll usually be placed internally
1948in a temporary buffer. Therefore, all else being equal, it is preferable
1949to pass a buffer rather than a string as SOURCE argument.
1950
1943This function returns t if a non-destructive replacement could be 1951This function returns t if a non-destructive replacement could be
1944performed. Otherwise, i.e., if MAX-SECS was exceeded, it returns 1952performed. Otherwise, i.e., if MAX-SECS was exceeded, it returns
1945nil. */) 1953nil.
1946 (Lisp_Object source, Lisp_Object max_secs, Lisp_Object max_costs) 1954
1955SOURCE can also be a function that will be called with no arguments
1956and with current buffer narrowed to BEG..END, and should return
1957a buffer or a string. But this is deprecated. */)
1958 (Lisp_Object beg, Lisp_Object end, Lisp_Object source,
1959 Lisp_Object max_secs, Lisp_Object max_costs, Lisp_Object inherit)
1947{ 1960{
1948 struct buffer *a = current_buffer; 1961 validate_region (&beg, &end);
1949 Lisp_Object source_buffer = Fget_buffer (source); 1962 ptrdiff_t min_a = XFIXNUM (beg);
1950 if (NILP (source_buffer)) 1963 ptrdiff_t size_a = XFIXNUM (end) - min_a;
1951 nsberror (source); 1964 eassume (size_a >= 0);
1952 struct buffer *b = XBUFFER (source_buffer); 1965 bool a_empty = size_a == 0;
1953 if (! BUFFER_LIVE_P (b)) 1966 bool inh = !NILP (inherit);
1967
1968 if (FUNCTIONP (source))
1969 {
1970 specpdl_ref count = SPECPDL_INDEX ();
1971 record_unwind_protect (save_restriction_restore,
1972 save_restriction_save ());
1973 Fnarrow_to_region (beg, end);
1974 source = calln (source);
1975 unbind_to (count, Qnil);
1976 }
1977 ptrdiff_t min_b, size_b;
1978 struct buffer *b;
1979 if (STRINGP (source))
1980 {
1981 min_b = BEG; /* Assuming we'll copy it into a buffer. */
1982 size_b = SCHARS (source);
1983 b = NULL;
1984 }
1985 else if (BUFFERP (source))
1986 {
1987 b = XBUFFER (source);
1988 min_b = BUF_BEGV (b);
1989 size_b = BUF_ZV (b) - min_b;
1990 }
1991 else
1992 {
1993 CHECK_TYPE (VECTORP (source),
1994 list (Qor, Qstring, Qbuffer, Qvector), source);
1995 /* Let `Faref' signal an error if it's too small. */
1996 Lisp_Object send = Faref (source, make_fixnum (2));
1997 Lisp_Object sbeg = AREF (source, 1);
1998 CHECK_BUFFER (AREF (source, 0));
1999 b = XBUFFER (AREF (source, 0));
2000 specpdl_ref count = SPECPDL_INDEX ();
2001 record_unwind_current_buffer ();
2002 set_buffer_internal (b);
2003 validate_region (&sbeg, &send);
2004 unbind_to (count, Qnil);
2005 min_b = XFIXNUM (sbeg);
2006 size_b = XFIXNUM (send) - min_b;
2007 }
2008 bool b_empty = size_b == 0;
2009 if (b && !BUFFER_LIVE_P (b))
1954 error ("Selecting deleted buffer"); 2010 error ("Selecting deleted buffer");
2011
2012 /* Handle trivial cases where at least one accessible portion is
2013 empty. */
2014
2015 if (a_empty && b_empty)
2016 return Qt;
2017 else if (a_empty || b_empty
2018 || EQ (max_secs, make_fixnum (0))
2019 || EQ (max_costs, make_fixnum (0)))
2020 {
2021 replace_range (min_a, min_a + size_a, source, true, false, inh);
2022 return Qt;
2023 }
2024
2025 struct buffer *a = current_buffer;
1955 if (a == b) 2026 if (a == b)
1956 error ("Cannot replace a buffer with itself"); 2027 error ("Cannot replace a buffer with itself");
1957 2028
@@ -1977,36 +2048,8 @@ nil. */)
1977 time_limit = tlim; 2048 time_limit = tlim;
1978 } 2049 }
1979 2050
1980 ptrdiff_t min_a = BEGV;
1981 ptrdiff_t min_b = BUF_BEGV (b);
1982 ptrdiff_t size_a = ZV - min_a;
1983 ptrdiff_t size_b = BUF_ZV (b) - min_b;
1984 eassume (size_a >= 0);
1985 eassume (size_b >= 0);
1986 bool a_empty = size_a == 0;
1987 bool b_empty = size_b == 0;
1988
1989 /* Handle trivial cases where at least one accessible portion is
1990 empty. */
1991
1992 if (a_empty && b_empty)
1993 return Qt;
1994
1995 if (a_empty)
1996 {
1997 Finsert_buffer_substring (source, Qnil, Qnil);
1998 return Qt;
1999 }
2000
2001 if (b_empty)
2002 {
2003 del_range_both (BEGV, BEGV_BYTE, ZV, ZV_BYTE, true);
2004 return Qt;
2005 }
2006
2007 specpdl_ref count = SPECPDL_INDEX (); 2051 specpdl_ref count = SPECPDL_INDEX ();
2008 2052
2009
2010 ptrdiff_t diags = size_a + size_b + 3; 2053 ptrdiff_t diags = size_a + size_b + 3;
2011 ptrdiff_t del_bytes = size_a / CHAR_BIT + 1; 2054 ptrdiff_t del_bytes = size_a / CHAR_BIT + 1;
2012 ptrdiff_t ins_bytes = size_b / CHAR_BIT + 1; 2055 ptrdiff_t ins_bytes = size_b / CHAR_BIT + 1;
@@ -2020,6 +2063,18 @@ nil. */)
2020 unsigned char *deletions_insertions = memset (buffer + 2 * diags, 0, 2063 unsigned char *deletions_insertions = memset (buffer + 2 * diags, 0,
2021 del_bytes + ins_bytes); 2064 del_bytes + ins_bytes);
2022 2065
2066 /* The rest of the code is not prepared to handle a string SOURCE. */
2067 if (!b)
2068 {
2069 Lisp_Object workbuf
2070 = code_conversion_save (true, STRING_MULTIBYTE (source));
2071 b = XBUFFER (workbuf);
2072 set_buffer_internal (b);
2073 CALLN (Finsert, source);
2074 set_buffer_internal (a);
2075 }
2076 Lisp_Object source_buffer = make_lisp_ptr (b, Lisp_Vectorlike);
2077
2023 /* FIXME: It is not documented how to initialize the contents of the 2078 /* FIXME: It is not documented how to initialize the contents of the
2024 context structure. This code cargo-cults from the existing 2079 context structure. This code cargo-cults from the existing
2025 caller in src/analyze.c of GNU Diffutils, which appears to 2080 caller in src/analyze.c of GNU Diffutils, which appears to
@@ -2053,7 +2108,7 @@ nil. */)
2053 Lisp_Object src = CALLN (Fvector, source_buffer, 2108 Lisp_Object src = CALLN (Fvector, source_buffer,
2054 make_fixnum (BUF_BEGV (b)), 2109 make_fixnum (BUF_BEGV (b)),
2055 make_fixnum (BUF_ZV (b))); 2110 make_fixnum (BUF_ZV (b)));
2056 replace_range (BEGV, ZV, src, true, false, false); 2111 replace_range (min_a, min_a + size_a, src, true, false, inh);
2057 SAFE_FREE_UNBIND_TO (count, Qnil); 2112 SAFE_FREE_UNBIND_TO (count, Qnil);
2058 return Qnil; 2113 return Qnil;
2059 } 2114 }
@@ -2069,7 +2124,7 @@ nil. */)
2069 modification hooks, because then they don't want that. */ 2124 modification hooks, because then they don't want that. */
2070 if (!inhibit_modification_hooks) 2125 if (!inhibit_modification_hooks)
2071 { 2126 {
2072 prepare_to_modify_buffer (BEGV, ZV, NULL); 2127 prepare_to_modify_buffer (min_a, min_a + size_a, NULL);
2073 specbind (Qinhibit_modification_hooks, Qt); 2128 specbind (Qinhibit_modification_hooks, Qt);
2074 modification_hooks_inhibited = true; 2129 modification_hooks_inhibited = true;
2075 } 2130 }
@@ -2102,10 +2157,9 @@ nil. */)
2102 eassert (beg_a <= end_a); 2157 eassert (beg_a <= end_a);
2103 eassert (beg_b <= end_b); 2158 eassert (beg_b <= end_b);
2104 eassert (beg_a < end_a || beg_b < end_b); 2159 eassert (beg_a < end_a || beg_b < end_b);
2105 /* FIXME: Use 'replace_range'! */
2106 ASET (src, 1, make_fixed_natnum (beg_b)); 2160 ASET (src, 1, make_fixed_natnum (beg_b));
2107 ASET (src, 2, make_fixed_natnum (end_b)); 2161 ASET (src, 2, make_fixed_natnum (end_b));
2108 replace_range (beg_a, end_a, src, true, false, false); 2162 replace_range (beg_a, end_a, src, true, false, inh);
2109 } 2163 }
2110 --i; 2164 --i;
2111 --j; 2165 --j;
@@ -2115,8 +2169,8 @@ nil. */)
2115 2169
2116 if (modification_hooks_inhibited) 2170 if (modification_hooks_inhibited)
2117 { 2171 {
2118 signal_after_change (BEGV, size_a, ZV - BEGV); 2172 signal_after_change (min_a, size_a, size_b);
2119 update_compositions (BEGV, ZV, CHECK_INSIDE); 2173 update_compositions (min_a, min_a + size_b, CHECK_INSIDE);
2120 /* We've locked the buffer's file above in 2174 /* We've locked the buffer's file above in
2121 prepare_to_modify_buffer; if the buffer is unchanged at this 2175 prepare_to_modify_buffer; if the buffer is unchanged at this
2122 point, i.e. no insertions or deletions have been made, unlock 2176 point, i.e. no insertions or deletions have been made, unlock
@@ -4787,7 +4841,7 @@ it to be non-nil. */);
4787 4841
4788 defsubr (&Sinsert_buffer_substring); 4842 defsubr (&Sinsert_buffer_substring);
4789 defsubr (&Scompare_buffer_substrings); 4843 defsubr (&Scompare_buffer_substrings);
4790 defsubr (&Sreplace_buffer_contents); 4844 defsubr (&Sreplace_region_contents);
4791 defsubr (&Ssubst_char_in_region); 4845 defsubr (&Ssubst_char_in_region);
4792 defsubr (&Stranslate_region_internal); 4846 defsubr (&Stranslate_region_internal);
4793 defsubr (&Sdelete_region); 4847 defsubr (&Sdelete_region);
diff --git a/src/insdel.c b/src/insdel.c
index 9b770725971..20267265ab8 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -1409,9 +1409,9 @@ adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte,
1409 adjust_after_replace (from, from_byte, Qnil, newlen, len_byte); 1409 adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
1410} 1410}
1411 1411
1412/* Replace the text from character positions FROM to TO with NEW. 1412/* Replace the text from character positions FROM to TO with the
1413 NEW could either be a string, the replacement text, or a vector 1413 replacement text NEW. NEW could either be a string, a buffer, or
1414 [BUFFER BEG END], where BUFFER is the buffer with the replacement 1414 a vector [BUFFER BEG END], where BUFFER is the buffer with the replacement
1415 text and BEG and END are buffer positions in BUFFER that give the 1415 text and BEG and END are buffer positions in BUFFER that give the
1416 replacement text beginning and end. 1416 replacement text beginning and end.
1417 If PREPARE, call prepare_to_modify_buffer. 1417 If PREPARE, call prepare_to_modify_buffer.
@@ -1439,6 +1439,12 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
1439 insbeg = 0; 1439 insbeg = 0;
1440 inschars = SCHARS (new); 1440 inschars = SCHARS (new);
1441 } 1441 }
1442 else if (BUFFERP (new))
1443 {
1444 insbuf = XBUFFER (new);
1445 insbeg = BUF_BEGV (insbuf);
1446 inschars = BUF_ZV (insbuf) - insbeg;
1447 }
1442 else 1448 else
1443 { 1449 {
1444 CHECK_VECTOR (new); 1450 CHECK_VECTOR (new);