aboutsummaryrefslogtreecommitdiffstats
path: root/src/editfns.c
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/editfns.c
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/editfns.c')
-rw-r--r--src/editfns.c150
1 files changed, 102 insertions, 48 deletions
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);