#include "charset.h"
#include "window.h"
#include "blockinput.h"
+#include "region-cache.h"
#ifndef NULL
#define NULL 0
/* Buffer which combine_after_change_list is about. */
Lisp_Object combine_after_change_buffer;
\f
+/* Check all markers in the current buffer, looking for something invalid. */
+
+static int check_markers_debug_flag;
+
+#define CHECK_MARKERS() \
+ if (check_markers_debug_flag) \
+ check_markers (); \
+ else
+
+void
+check_markers ()
+{
+ register Lisp_Object tail, prev, next;
+
+ tail = BUF_MARKERS (current_buffer);
+
+ while (XSYMBOL (tail) != XSYMBOL (Qnil))
+ {
+ if (XMARKER (tail)->buffer->text != current_buffer->text)
+ abort ();
+ if (XMARKER (tail)->charpos > Z)
+ abort ();
+ if (XMARKER (tail)->bytepos > Z_BYTE)
+ abort ();
+
+ tail = XMARKER (tail)->chain;
+ }
+}
+\f
/* Move gap to position CHARPOS.
Note that this can quit! */
&& PT != PT_BYTE)
abort ();
}
+\f
+/* Adjust markers for a replacement of a text at FROM (FROM_BYTE) of
+ length OLD_CHARS (OLD_BYTES) to a new text of length NEW_CHARS
+ (NEW_BYTES).
+
+ See the comment of adjust_markers_for_insert for the args
+ COMBINED_BEFORE_BYTES and COMBINED_AFTER_BYTES. */
+
+static void
+adjust_markers_for_replace (from, from_byte, old_chars, old_bytes,
+ new_chars, new_bytes,
+ combined_before_bytes, combined_after_bytes)
+ int from, from_byte, old_chars, old_bytes, new_chars, new_bytes;
+ int combined_before_bytes, combined_after_bytes;
+{
+ Lisp_Object marker = BUF_MARKERS (current_buffer);
+ int prev_to_byte = from_byte + old_bytes;
+ int diff_chars = new_chars - old_chars;
+ int diff_bytes = new_bytes - old_bytes;
+
+ while (!NILP (marker))
+ {
+ register struct Lisp_Marker *m = XMARKER (marker);
+
+ if (m->bytepos >= prev_to_byte)
+ {
+ if (m->bytepos < prev_to_byte + combined_after_bytes)
+ {
+ /* Put it after the combining bytes. */
+ m->bytepos = from_byte + new_bytes;
+ m->charpos = from + new_chars;
+ }
+ else
+ {
+ m->charpos += diff_chars;
+ m->bytepos += diff_bytes;
+ }
+ if (m->charpos == from + new_chars)
+ record_marker_adjustment (marker, - old_chars);
+ }
+ else if (m->bytepos > from_byte)
+ {
+ record_marker_adjustment (marker, from - m->charpos);
+ m->charpos = from;
+ m->bytepos = from_byte;
+ }
+ else if (m->bytepos == from_byte)
+ {
+ if (combined_before_bytes)
+ {
+ DEC_BOTH (m->charpos, m->bytepos);
+ INC_BOTH (m->charpos, m->bytepos);
+ }
+ }
+
+ marker = m->chain;
+ }
+}
+
\f
/* Make the gap NBYTES_ADDED bytes longer. */
void
insert (string, nbytes)
register unsigned char *string;
- register nbytes;
+ register int nbytes;
{
if (nbytes > 0)
{
void
insert_and_inherit (string, nbytes)
register unsigned char *string;
- register nbytes;
+ register int nbytes;
{
if (nbytes > 0)
{
if (BUF_INTERVALS (current_buffer) != 0)
/* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES. */
offset_intervals (current_buffer, pos, - nbytes);
+
+ CHECK_MARKERS ();
}
\f
/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
register int nchars, nbytes;
int inherit, prepare, before_markers;
{
- register Lisp_Object temp;
+ register Lisp_Object temp, deletion;
int combined_before_bytes, combined_after_bytes;
if (NILP (current_buffer->enable_multibyte_characters))
if (combined_after_bytes)
{
+ deletion = make_buffer_string_both (PT, PT_BYTE,
+ PT + combined_after_bytes,
+ PT_BYTE + combined_after_bytes, 1);
+
adjust_markers_for_record_delete (PT, PT_BYTE,
PT + combined_after_bytes,
PT_BYTE + combined_after_bytes);
- record_delete (PT, combined_after_bytes);
+ record_delete (PT, deletion);
}
if (combined_before_bytes)
{
+ deletion = make_buffer_string_both (PT - 1, CHAR_TO_BYTE (PT - 1),
+ PT, PT_BYTE, 1);
adjust_markers_for_record_delete (PT - 1, CHAR_TO_BYTE (PT - 1),
PT, PT_BYTE);
- record_delete (PT - 1, 1);
+ record_delete (PT - 1, deletion);
}
record_insert (PT - !!combined_before_bytes,
int combined_before_bytes, combined_after_bytes;
int adjusted_nchars;
INTERVAL intervals;
+ Lisp_Object deletion;
/* Make OUTGOING_NBYTES describe the text
as it will be inserted in this buffer. */
if (NILP (current_buffer->enable_multibyte_characters))
outgoing_nbytes = nchars;
- else if (nchars == nbytes)
+ else if (! STRING_MULTIBYTE (string))
outgoing_nbytes
= count_size_as_multibyte (&XSTRING (string)->data[pos_byte],
nbytes);
/* Copy the string text into the buffer, perhaps converting
between single-byte and multibyte. */
copy_text (XSTRING (string)->data + pos_byte, GPT_ADDR, nbytes,
- /* If these are equal, it is a single-byte string.
- Its chars are either ASCII, in which case copy_text
- won't change it, or single-byte non-ASCII chars,
- that need to be changed. */
- nchars != nbytes,
+ STRING_MULTIBYTE (string),
! NILP (current_buffer->enable_multibyte_characters));
/* We have copied text into the gap, but we have not altered
if (combined_after_bytes)
{
+ deletion = make_buffer_string_both (PT, PT_BYTE,
+ PT + combined_after_bytes,
+ PT_BYTE + combined_after_bytes, 1);
+
adjust_markers_for_record_delete (PT, PT_BYTE,
PT + combined_after_bytes,
PT_BYTE + combined_after_bytes);
- record_delete (PT, combined_after_bytes);
+ record_delete (PT, deletion);
}
if (combined_before_bytes)
{
+ deletion = make_buffer_string_both (PT - 1, CHAR_TO_BYTE (PT - 1),
+ PT, PT_BYTE, 1);
adjust_markers_for_record_delete (PT - 1, CHAR_TO_BYTE (PT - 1),
PT, PT_BYTE);
- record_delete (PT - 1, 1);
+ record_delete (PT - 1, deletion);
}
record_insert (PT - !!combined_before_bytes,
intervals = XSTRING (string)->intervals;
/* Get the intervals for the part of the string we are inserting--
not including the combined-before bytes. */
- if (nbytes < XSTRING (string)->size_byte)
+ if (nbytes < STRING_BYTES (XSTRING (string)))
intervals = copy_intervals (intervals, pos, nchars);
/* Insert those intervals. */
int from, nchars;
int inherit;
{
- register Lisp_Object temp;
+ register Lisp_Object temp, deletion;
int chunk;
int from_byte = buf_charpos_to_bytepos (buf, from);
int to_byte = buf_charpos_to_bytepos (buf, from + nchars);
if (combined_after_bytes)
{
+ deletion = make_buffer_string_both (PT, PT_BYTE,
+ PT + combined_after_bytes,
+ PT_BYTE + combined_after_bytes, 1);
+
adjust_markers_for_record_delete (PT, PT_BYTE,
PT + combined_after_bytes,
PT_BYTE + combined_after_bytes);
- record_delete (PT, combined_after_bytes);
+ record_delete (PT, deletion);
}
if (combined_before_bytes)
{
+ deletion = make_buffer_string_both (PT - 1, CHAR_TO_BYTE (PT - 1),
+ PT, PT_BYTE, 1);
adjust_markers_for_record_delete (PT - 1, CHAR_TO_BYTE (PT - 1),
PT, PT_BYTE);
- record_delete (PT - 1, 1);
+ record_delete (PT - 1, deletion);
}
record_insert (PT - !!combined_before_bytes,
adjust_before_replace (from, from_byte, to, to_byte)
int from, from_byte, to, to_byte;
{
+ Lisp_Object deletion;
+ deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
+
+ CHECK_MARKERS ();
+
adjust_markers_for_delete (from, from_byte, to, to_byte);
- record_delete (from, to - from);
+ record_delete (from, deletion);
adjust_overlays_for_delete (from, to - from);
}
-/* This function should be called after altering the text between FROM
- and TO to a new text of LEN chars (LEN_BYTE bytes), but before
- making the text a buffer contents. It exists just after GPT_ADDR. */
+/* Record undo information and adjust markers and position keepers for
+ a replacement of a text PREV_TEXT at FROM to a new text of LEN
+ chars (LEN_BYTE bytes) which resides in the gap just after
+ GPT_ADDR.
+
+ PREV_TEXT nil means the new text was just inserted. */
void
-adjust_after_replace (from, from_byte, to, to_byte, len, len_byte, replace)
- int from, from_byte, to, to_byte, len, len_byte, replace;
+adjust_after_replace (from, from_byte, prev_text, len, len_byte)
+ int from, from_byte, len, len_byte;
+ Lisp_Object prev_text;
{
int combined_before_bytes
= count_combining_before (GPT_ADDR, len_byte, from, from_byte);
int combined_after_bytes
= count_combining_after (GPT_ADDR, len_byte, from, from_byte);
+ Lisp_Object deletion;
+ int nchars_del = 0, nbytes_del = 0;
if (combined_after_bytes)
{
+ deletion = make_buffer_string_both (from, from_byte,
+ from + combined_after_bytes,
+ from_byte + combined_after_bytes, 1);
+
adjust_markers_for_record_delete (from, from_byte,
from + combined_after_bytes,
from_byte + combined_after_bytes);
- record_delete (from, combined_after_bytes);
+ record_delete (from, deletion);
}
if (combined_before_bytes)
{
+ deletion = make_buffer_string_both (from - 1, CHAR_TO_BYTE (from - 1),
+ from, from_byte, 1);
adjust_markers_for_record_delete (from - 1, CHAR_TO_BYTE (from - 1),
from, from_byte);
- record_delete (from - 1, 1);
+ record_delete (from - 1, deletion);
}
/* Update various buffer positions for the new text. */
move_gap_both (GPT + combined_after_bytes,
GPT_BYTE + combined_after_bytes);
+ if (STRINGP (prev_text))
+ {
+ nchars_del = XSTRING (prev_text)->size;
+ nbytes_del = STRING_BYTES (XSTRING (prev_text));
+ }
+ adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
+ len, len_byte,
+ combined_before_bytes, combined_after_bytes);
+ if (STRINGP (prev_text))
+ record_delete (from, prev_text);
record_insert (from - !!combined_before_bytes,
len - combined_before_bytes + !!combined_before_bytes);
- adjust_overlays_for_insert (from, len);
- adjust_markers_for_insert (from, from_byte,
- from + len, from_byte + len_byte,
- combined_before_bytes, combined_after_bytes, 0);
+
+ if (len > nchars_del)
+ adjust_overlays_for_insert (from, len - nchars_del);
+ else if (len < nchars_del)
+ adjust_overlays_for_delete (from, nchars_del - len);
#ifdef USE_TEXT_PROPERTIES
if (BUF_INTERVALS (current_buffer) != 0)
- /* REPLACE zero means that we have not yet adjusted the interval
- tree for the text between FROM and TO, thus, we must treat the
- new text as a newly inserted text, not as a replacement of
- something. */
- offset_intervals (current_buffer, from, len - (replace ? to - from : 0));
+ offset_intervals (current_buffer, from, len - nchars_del);
#endif
{
int pos = PT, pos_byte = PT_BYTE;
if (from < PT)
- adjust_point (len - (to - from) + combined_after_bytes,
- len_byte - (to_byte - from_byte) + combined_after_bytes);
+ adjust_point (len - nchars_del + combined_after_bytes,
+ len_byte - nbytes_del + combined_after_bytes);
else if (from == PT && combined_before_bytes)
adjust_point (0, combined_before_bytes);
combine_bytes (from, from_byte, combined_before_bytes);
}
+ CHECK_MARKERS ();
+
if (len == 0)
evaporate_overlays (from);
MODIFF++;
}
+/* Record undo information, adjust markers and position keepers for an
+ insertion of a text from FROM (FROM_BYTE) to TO (TO_BYTE). The
+ text already exists in the current buffer but character length (TO
+ - FROM) may be incorrect, the correct length is NEWLEN. */
+
+void
+adjust_after_insert (from, from_byte, to, to_byte, newlen)
+ int from, from_byte, to, to_byte, newlen;
+{
+ int len = to - from, len_byte = to_byte - from_byte;
+
+ if (GPT != to)
+ move_gap_both (to, to_byte);
+ GAP_SIZE += len_byte;
+ GPT -= len; GPT_BYTE -= len_byte;
+ ZV -= len; ZV_BYTE -= len_byte;
+ Z -= len; Z_BYTE -= len_byte;
+ adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
+}
+
/* Replace the text from character positions FROM to TO with NEW,
If PREPARE is nonzero, call prepare_to_modify_buffer.
If INHERIT, the newly inserted text should inherit text properties
That way, undo will also handle markers properly. */
void
-replace_range (from, to, new, prepare, inherit)
+replace_range (from, to, new, prepare, inherit, nomarkers)
Lisp_Object new;
- int from, to, prepare, inherit;
+ int from, to, prepare, inherit, nomarkers;
{
int inschars = XSTRING (new)->size;
- int insbytes = XSTRING (new)->size_byte;
+ int insbytes = STRING_BYTES (XSTRING (new));
int from_byte, to_byte;
int nbytes_del, nchars_del;
register Lisp_Object temp;
int adjusted_inschars;
INTERVAL intervals;
int outgoing_insbytes = insbytes;
+ Lisp_Object deletion;
+
+ CHECK_MARKERS ();
GCPRO1 (new);
if (NILP (current_buffer->enable_multibyte_characters))
outgoing_insbytes = inschars;
- else if (inschars == insbytes)
+ else if (! STRING_MULTIBYTE (new))
outgoing_insbytes
= count_size_as_multibyte (XSTRING (new)->data, insbytes);
if (to < GPT)
gap_left (to, to_byte, 0);
- /* Relocate all markers pointing into the new, larger gap
- to point at the end of the text before the gap.
- Do this before recording the deletion,
- so that undo handles this after reinserting the text. */
- adjust_markers_for_delete (from, from_byte, to, to_byte);
+ deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
+
+ if (nomarkers)
+ /* Relocate all markers pointing into the new, larger gap
+ to point at the end of the text before the gap.
+ Do this before recording the deletion,
+ so that undo handles this after reinserting the text. */
+ adjust_markers_for_delete (from, from_byte, to, to_byte);
- record_delete (from, nchars_del);
+ record_delete (from, deletion);
GAP_SIZE += nbytes_del;
ZV -= nchars_del;
/* Copy the string text into the buffer, perhaps converting
between single-byte and multibyte. */
copy_text (XSTRING (new)->data, GPT_ADDR, insbytes,
- /* If these are equal, it is a single-byte string.
- Its chars are either ASCII, in which case copy_text
- won't change it, or single-byte non-ASCII chars,
- that need to be changed. */
- inschars != insbytes,
+ STRING_MULTIBYTE (new),
! NILP (current_buffer->enable_multibyte_characters));
/* We have copied text into the gap, but we have not altered
if (combined_after_bytes)
{
+ deletion = make_buffer_string_both (PT, PT_BYTE,
+ PT + combined_after_bytes,
+ PT_BYTE + combined_after_bytes, 1);
+
adjust_markers_for_record_delete (PT, PT_BYTE,
PT + combined_after_bytes,
PT_BYTE + combined_after_bytes);
- record_delete (PT, combined_after_bytes);
+ record_delete (PT, deletion);
}
if (combined_before_bytes)
{
+ deletion = make_buffer_string_both (PT - 1, CHAR_TO_BYTE (PT - 1),
+ PT, PT_BYTE, 1);
adjust_markers_for_record_delete (PT - 1, CHAR_TO_BYTE (PT - 1),
PT, PT_BYTE);
- record_delete (PT - 1, 1);
+ record_delete (PT - 1, deletion);
}
record_insert (PT - !!combined_before_bytes,
adjusting the markers that bound the overlays. */
adjust_overlays_for_delete (from, nchars_del);
adjust_overlays_for_insert (from, inschars);
- adjust_markers_for_insert (from, from_byte,
- from + inschars, from_byte + outgoing_insbytes,
- combined_before_bytes, combined_after_bytes, 0);
+ if (nomarkers)
+ adjust_markers_for_insert (from, from_byte,
+ from + inschars, from_byte + outgoing_insbytes,
+ combined_before_bytes, combined_after_bytes, 0);
#ifdef USE_TEXT_PROPERTIES
offset_intervals (current_buffer, PT, inschars - nchars_del);
if (outgoing_insbytes == 0)
evaporate_overlays (from);
+ CHECK_MARKERS ();
+
MODIFF++;
UNGCPRO;
{
register int nbytes_del, nchars_del;
int combined_after_bytes;
+ Lisp_Object deletion;
+ int from_byte_1;
+
+ CHECK_MARKERS ();
nchars_del = to - from;
nbytes_del = to_byte - from_byte;
combined_after_bytes
= count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
ZV_BYTE - to_byte, from, from_byte);
+ if (combined_after_bytes)
+ {
+ from_byte_1 = from_byte;
+ DEC_POS (from_byte_1);
+ }
+ else
+ from_byte_1 = from_byte;
+
+ deletion
+ = make_buffer_string_both (from - !!combined_after_bytes,
+ from_byte_1,
+ to + combined_after_bytes,
+ to_byte + combined_after_bytes, 1);
+ if (combined_after_bytes)
+ /* COMBINED_AFTER_BYTES nonzero means that the above code moved
+ the gap. We must move the gap again to a proper place. */
+ move_gap_both (from, from_byte);
/* Relocate all markers pointing into the new, larger gap
to point at the end of the text before the gap.
adjust_markers_for_delete (from, from_byte, to, to_byte);
if (combined_after_bytes)
{
- int from_byte_1 = from_byte;
- DEC_POS (from_byte_1);
-
/* Adjust markers for the phony deletion
that we are about to call record_undo for. */
adjust_markers_for_record_delete (from - 1, from_byte_1,
from, from_byte);
}
- record_delete (from - !!combined_after_bytes,
- nchars_del + combined_after_bytes + !!combined_after_bytes);
-
- if (combined_after_bytes)
- /* COMBINED_AFTER_BYTES nonzero means that the above record_delete
- moved the gap by calling Fbuffer_substring. We must move the
- gap again to a proper place. */
- move_gap_both (from, from_byte);
+ record_delete (from - !!combined_after_bytes, deletion);
MODIFF++;
/* Relocate point as if it were a marker. */
record_insert (GPT - 1, 1);
}
+ CHECK_MARKERS ();
+
evaporate_overlays (from);
signal_after_change (from, nchars_del, 0);
}
/* After an insertion, call the text properties
insert-behind-hooks or insert-in-front-hooks. */
if (lendel == 0)
- report_interval_modification (charpos, charpos + lenins);
+ report_interval_modification (make_number (charpos),
+ make_number (charpos + lenins));
}
Lisp_Object
return unbind_to (count, val);
}
\f
+void
syms_of_insdel ()
{
staticpro (&combine_after_change_list);
combine_after_change_list = Qnil;
+ DEFVAR_BOOL ("check-markers-debug-flag", &check_markers_debug_flag,
+ "Non-nil means enable debugging checks for invalid marker positions.");
+ check_markers_debug_flag = 0;
DEFVAR_LISP ("combine-after-change-calls", &Vcombine_after_change_calls,
- "Used internally by the `combine-after-change-calls' macro.");
+ "Used internally by the `combine-after-change-calls' macro.");
Vcombine_after_change_calls = Qnil;
defsubr (&Scombine_after_change_execute);