Function: subst-char-in-region
subst-char-in-region is a function defined in editfns.c.
Signature
(subst-char-in-region START END FROMCHAR TOCHAR &optional NOUNDO)
Documentation
From START to END, replace FROMCHAR with TOCHAR each time it occurs.
If optional arg NOUNDO is non-nil, don't record this change for undo and don't mark the buffer as really changed. Both characters must have the same length of multi-byte form.
Other relevant functions are documented in the buffer group.
Probably introduced at or before Emacs version 17.
Shortdoc
;; buffer
(subst-char-in-region (point-min) (point-max) ?+ ?-)
Source Code
// Defined in /usr/src/emacs/src/editfns.c
{
register ptrdiff_t pos, pos_byte, stop, i, len, end_byte;
/* Keep track of the first change in the buffer:
if 0 we haven't found it yet.
if < 0 we've found it and we've run the before-change-function.
if > 0 we've actually performed it and the value is its position. */
ptrdiff_t changed = 0;
unsigned char fromstr[MAX_MULTIBYTE_LENGTH], tostr[MAX_MULTIBYTE_LENGTH];
unsigned char *p;
specpdl_ref count = SPECPDL_INDEX ();
ptrdiff_t last_changed = 0;
bool multibyte_p
= !NILP (BVAR (current_buffer, enable_multibyte_characters));
int fromc, toc;
#ifdef HAVE_TREE_SITTER
ptrdiff_t start_char = fix_position (start);
ptrdiff_t old_end_char = fix_position (end);
ptrdiff_t start_byte = CHAR_TO_BYTE (start_char);
ptrdiff_t old_end_byte = CHAR_TO_BYTE (old_end_char);
struct ts_linecol start_linecol
= treesit_linecol_maybe (start_char, start_byte,
BUF_TS_LINECOL_POINT (current_buffer));
struct ts_linecol old_end_linecol
= treesit_linecol_maybe (old_end_char, old_end_byte,
BUF_TS_LINECOL_POINT (current_buffer));
#endif
restart:
validate_region (&start, &end);
CHECK_CHARACTER (fromchar);
CHECK_CHARACTER (tochar);
fromc = XFIXNAT (fromchar);
toc = XFIXNAT (tochar);
if (multibyte_p)
{
len = CHAR_STRING (fromc, fromstr);
if (CHAR_STRING (toc, tostr) != len)
error ("Characters in `subst-char-in-region' have different byte-lengths");
}
else
{
len = 1;
fromstr[0] = fromc;
tostr[0] = toc;
}
pos = XFIXNUM (start);
pos_byte = CHAR_TO_BYTE (pos);
stop = CHAR_TO_BYTE (XFIXNUM (end));
end_byte = stop;
/* If we don't want undo, turn off putting stuff on the list.
That's faster than getting rid of things,
and it prevents even the entry for a first change.
Also inhibit locking the file. */
if (!changed && !NILP (noundo))
{
record_unwind_protect (subst_char_in_region_unwind,
BVAR (current_buffer, undo_list));
bset_undo_list (current_buffer, Qt);
/* Don't do file-locking. */
record_unwind_protect (subst_char_in_region_unwind_1,
BVAR (current_buffer, filename));
bset_filename (current_buffer, Qnil);
}
if (pos_byte < GPT_BYTE)
stop = min (stop, GPT_BYTE);
while (1)
{
ptrdiff_t pos_byte_next = pos_byte;
if (pos_byte >= stop)
{
if (pos_byte >= end_byte) break;
stop = end_byte;
}
p = BYTE_POS_ADDR (pos_byte);
if (multibyte_p)
pos_byte_next += next_char_len (pos_byte_next);
else
++pos_byte_next;
if (pos_byte_next - pos_byte == len
&& p[0] == fromstr[0]
&& (len == 1
|| (p[1] == fromstr[1]
&& (len == 2 || (p[2] == fromstr[2]
&& (len == 3 || p[3] == fromstr[3]))))))
{
if (changed < 0)
/* We've already seen this and run the before-change-function;
this time we only need to record the actual position. */
changed = pos;
else if (!changed)
{
changed = -1;
modify_text (pos, XFIXNUM (end));
if (! NILP (noundo))
{
modiff_count m = MODIFF;
if (SAVE_MODIFF == m - 1)
SAVE_MODIFF = m;
if (BUF_AUTOSAVE_MODIFF (current_buffer) == m - 1)
BUF_AUTOSAVE_MODIFF (current_buffer) = m;
}
/* The before-change-function may have moved the gap
or even modified the buffer so we should start over. */
goto restart;
}
if (NILP (noundo))
record_change (pos, 1);
for (i = 0; i < len; i++) *p++ = tostr[i];
last_changed = pos + 1;
}
pos_byte = pos_byte_next;
pos++;
}
if (changed > 0)
{
#ifdef HAVE_TREE_SITTER
treesit_record_change (start_byte, old_end_byte, old_end_byte,
start_linecol, old_end_linecol, old_end_char);
#endif
signal_after_change (changed,
last_changed - changed, last_changed - changed);
update_compositions (changed, last_changed, CHECK_ALL);
}
return unbind_to (count, Qnil);
}