/* Buffer insertion/deletion and gap motion for GNU Emacs.
- Copyright (C) 1985-1986, 1993-1995, 1997-2013 Free Software
- Foundation, Inc.
+
+Copyright (C) 1985-1986, 1993-1995, 1997-2014 Free Software Foundation, Inc.
This file is part of GNU Emacs.
adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte)
{
- Lisp_Object marker;
- register struct Lisp_Marker *m;
- register ptrdiff_t charpos;
+ struct Lisp_Marker *m;
+ ptrdiff_t charpos;
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
{
/* Here's the case where a marker is inside text being deleted. */
else if (charpos > from)
{
- if (! m->insertion_type)
- { /* Normal markers will end up at the beginning of the
- re-inserted text after undoing a deletion, and must be
- adjusted to move them to the correct place. */
- XSETMISC (marker, m);
- record_marker_adjustment (marker, from - charpos);
- }
- else if (charpos < to)
- { /* Before-insertion markers will automatically move forward
- upon re-inserting the deleted text, so we have to arrange
- for them to move backward to the correct position. */
- XSETMISC (marker, m);
- record_marker_adjustment (marker, to - charpos);
- }
m->charpos = from;
m->bytepos = from_byte;
}
- /* Here's the case where a before-insertion marker is immediately
- before the deleted region. */
- else if (charpos == from && m->insertion_type)
- {
- /* Undoing the change uses normal insertion, which will
- incorrectly make MARKER move forward, so we arrange for it
- to then move backward to the correct place at the beginning
- of the deleted region. */
- XSETMISC (marker, m);
- record_marker_adjustment (marker, to - from);
- }
}
}
(2) POS is the last of the current buffer.
(3) A character at POS can't be a following byte of multibyte
character. */
- if (length > 0 && ASCII_BYTE_P (string[length - 1])) /* case (1) */
+ if (length > 0 && ASCII_CHAR_P (string[length - 1])) /* case (1) */
return 0;
if (pos_byte == Z_BYTE) /* case (2) */
return 0;
\f
/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
- starting at STRING. INHERIT, PREPARE and BEFORE_MARKERS
- are the same as in insert_1. */
+ starting at STRING. INHERIT non-zero means inherit the text
+ properties from neighboring characters; zero means inserted text
+ will have no text properties. PREPARE non-zero means call
+ prepare_to_modify_buffer, which checks that the region is not
+ read-only, and calls before-change-function and any modification
+ properties the text may have. BEFORE_MARKERS non-zero means adjust
+ all markers that point at the insertion place to point after it. */
void
insert_1_both (const char *string,
eassert (GPT <= GPT_BYTE);
- /* The insert may have been in the unchanged region, so check again. */
+ /* The insert may have been in the unchanged region, so check again. */
if (Z - GPT < END_UNCHANGED)
END_UNCHANGED = Z - GPT;
eassert (GPT <= GPT_BYTE);
- /* The insert may have been in the unchanged region, so check again. */
+ /* The insert may have been in the unchanged region, so check again. */
if (Z - GPT < END_UNCHANGED)
END_UNCHANGED = Z - GPT;
}
\f
/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
- starting at GPT_ADDR. */
+ starting at GAP_END_ADDR - NBYTES (if text_at_gap_tail) and at
+ GPT_ADDR (if not text_at_gap_tail). */
void
-insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes)
+insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail)
{
+ ptrdiff_t ins_charpos = GPT, ins_bytepos = GPT_BYTE;
+
if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
nchars = nbytes;
+ /* No need to call prepare_to_modify_buffer, since this is called
+ from places that replace some region with a different text, so
+ prepare_to_modify_buffer was already called by the deletion part
+ of this dance. */
+ invalidate_buffer_caches (current_buffer, GPT, GPT);
record_insert (GPT, nchars);
MODIFF++;
GAP_SIZE -= nbytes;
- GPT += nchars;
+ if (! text_at_gap_tail)
+ {
+ GPT += nchars;
+ GPT_BYTE += nbytes;
+ }
ZV += nchars;
Z += nchars;
- GPT_BYTE += nbytes;
ZV_BYTE += nbytes;
Z_BYTE += nbytes;
if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
eassert (GPT <= GPT_BYTE);
- adjust_overlays_for_insert (GPT - nchars, nchars);
- adjust_markers_for_insert (GPT - nchars, GPT_BYTE - nbytes,
- GPT, GPT_BYTE, 0);
+ adjust_overlays_for_insert (ins_charpos, nchars);
+ adjust_markers_for_insert (ins_charpos, ins_bytepos,
+ ins_charpos + nchars, ins_bytepos + nbytes, 0);
if (buffer_intervals (current_buffer))
{
- offset_intervals (current_buffer, GPT - nchars, nchars);
- graft_intervals_into_buffer (NULL, GPT - nchars, nchars,
+ offset_intervals (current_buffer, ins_charpos, nchars);
+ graft_intervals_into_buffer (NULL, ins_charpos, nchars,
current_buffer, 0);
}
- if (GPT - nchars < PT)
+ if (ins_charpos < PT)
adjust_point (nchars, nbytes);
check_markers ();
ptrdiff_t outgoing_nbytes = incoming_nbytes;
INTERVAL intervals;
+ if (nchars == 0)
+ return;
+
/* Make OUTGOING_NBYTES describe the text
as it will be inserted in this buffer. */
eassert (GPT <= GPT_BYTE);
- /* The insert may have been in the unchanged region, so check again. */
+ /* The insert may have been in the unchanged region, so check again. */
if (Z - GPT < END_UNCHANGED)
END_UNCHANGED = Z - GPT;
adjust_markers_for_insert (from, from_byte,
from + len, from_byte + len_byte, 0);
- if (! EQ (BVAR (current_buffer, undo_list), Qt))
- {
- if (nchars_del > 0)
- record_delete (from, prev_text);
- record_insert (from, len);
- }
+ if (nchars_del > 0)
+ record_delete (from, prev_text, false);
+ record_insert (from, len);
if (len > nchars_del)
adjust_overlays_for_insert (from, len - nchars_del);
emacs_abort ();
#endif
- if (! EQ (BVAR (current_buffer, undo_list), Qt))
+ /* Record the insertion first, so that when we undo,
+ the deletion will be undone first. Thus, undo
+ will insert before deleting, and thus will keep
+ the markers before and after this text separate. */
+ if (!NILP (deletion))
{
- /* Record the insertion first, so that when we undo,
- the deletion will be undone first. Thus, undo
- will insert before deleting, and thus will keep
- the markers before and after this text separate. */
record_insert (from + SCHARS (deletion), inschars);
- record_delete (from, deletion);
+ record_delete (from, deletion, false);
}
GAP_SIZE -= outgoing_insbytes;
else
deletion = Qnil;
- /* 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. */
+ /* Record marker adjustments, and text deletion into undo
+ history. */
+ record_delete (from, deletion, true);
+
+ /* 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 (! EQ (BVAR (current_buffer, undo_list), Qt))
- record_delete (from, deletion);
MODIFF++;
CHARS_MODIFF = MODIFF;
return deletion;
}
-/* Call this if you're about to change the region of current buffer
+/* Call this if you're about to change the text of current buffer
from character positions START to END. This checks the read-only
properties of the region, calls the necessary modification hooks,
and warns the next redisplay that it should pay attention to that
- area.
-
- If PRESERVE_CHARS_MODIFF, do not update CHARS_MODIFF.
- Otherwise set CHARS_MODIFF to the new value of MODIFF. */
+ area. */
void
-modify_region_1 (ptrdiff_t start, ptrdiff_t end, bool preserve_chars_modiff)
+modify_text (ptrdiff_t start, ptrdiff_t end)
{
prepare_to_modify_buffer (start, end, NULL);
BUF_COMPUTE_UNCHANGED (current_buffer, start - 1, end);
-
if (MODIFF <= SAVE_MODIFF)
record_first_change ();
MODIFF++;
- if (! preserve_chars_modiff)
- CHARS_MODIFF = MODIFF;
+ CHARS_MODIFF = MODIFF;
bset_point_before_scroll (current_buffer, Qnil);
}
+Lisp_Object Qregion_extract_function;
+
/* Check that it is okay to modify the buffer between START and END,
which are char positions.
by holding its value temporarily in a marker. */
void
-prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
- ptrdiff_t *preserve_ptr)
+prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end,
+ ptrdiff_t *preserve_ptr)
{
struct buffer *base_buffer;
if (!NILP (BVAR (current_buffer, read_only)))
Fbarf_if_buffer_read_only ();
- /* If we're modifying the buffer other than shown in a selected window,
- let redisplay consider other windows if this buffer is visible. */
- if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer
- && buffer_window_count (current_buffer))
- ++windows_or_buffers_changed;
+ bset_redisplay (current_buffer);
if (buffer_intervals (current_buffer))
{
else
base_buffer = current_buffer;
-#ifdef CLASH_DETECTION
+ if (inhibit_modification_hooks)
+ return;
+
if (!NILP (BVAR (base_buffer, file_truename))
/* Make binding buffer-file-name to nil effective. */
&& !NILP (BVAR (base_buffer, filename))
&& SAVE_MODIFF >= MODIFF)
lock_file (BVAR (base_buffer, file_truename));
-#else
- /* At least warn if this file has changed on disk since it was visited. */
- if (!NILP (BVAR (base_buffer, filename))
- && SAVE_MODIFF >= MODIFF
- && NILP (Fverify_visited_file_modtime (Fcurrent_buffer ()))
- && !NILP (Ffile_exists_p (BVAR (base_buffer, filename))))
- call1 (intern ("ask-user-about-supersession-threat"),
- BVAR (base_buffer,filename));
-#endif /* not CLASH_DETECTION */
/* If `select-active-regions' is non-nil, save the region text. */
+ /* FIXME: Move this to Elisp (via before-change-functions). */
if (!NILP (BVAR (current_buffer, mark_active))
- && !inhibit_modification_hooks
&& XMARKER (BVAR (current_buffer, mark))->buffer
&& NILP (Vsaved_region_selection)
&& (EQ (Vselect_active_regions, Qonly)
? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
: (!NILP (Vselect_active_regions)
&& !NILP (Vtransient_mark_mode))))
- {
- ptrdiff_t b = marker_position (BVAR (current_buffer, mark));
- ptrdiff_t e = PT;
- if (b < e)
- Vsaved_region_selection = make_buffer_string (b, e, 0);
- else if (b > e)
- Vsaved_region_selection = make_buffer_string (e, b, 0);
- }
+ Vsaved_region_selection
+ = call1 (Fsymbol_value (Qregion_extract_function), Qnil);
signal_before_change (start, end, preserve_ptr);
+ Vdeactivate_mark = Qt;
+}
- if (current_buffer->newline_cache)
- invalidate_region_cache (current_buffer,
- current_buffer->newline_cache,
- start - BEG, Z - end);
- if (current_buffer->width_run_cache)
- invalidate_region_cache (current_buffer,
- current_buffer->width_run_cache,
- start - BEG, Z - end);
+/* Like above, but called when we know that the buffer text
+ will be modified and region caches should be invalidated. */
- Vdeactivate_mark = Qt;
+void
+prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
+ ptrdiff_t *preserve_ptr)
+{
+ prepare_to_modify_buffer_1 (start, end, preserve_ptr);
+ invalidate_buffer_caches (current_buffer, start, end);
}
-\f
+
+/* Invalidate the caches maintained by the buffer BUF, if any, for the
+ region between buffer positions START and END. */
+void
+invalidate_buffer_caches (struct buffer *buf, ptrdiff_t start, ptrdiff_t end)
+{
+ /* Indirect buffers usually have their caches set to NULL, but we
+ need to consider the caches of their base buffer. */
+ if (buf->base_buffer)
+ buf = buf->base_buffer;
+ /* The bidi_paragraph_cache must be invalidated first, because doing
+ so might need to use the newline_cache (via find_newline_no_quit,
+ see below). */
+ if (buf->bidi_paragraph_cache)
+ {
+ if (start != end
+ && start > BUF_BEG (buf))
+ {
+ /* If we are deleting or replacing characters, we could
+ create a paragraph start, because all of the characters
+ from START to the beginning of START's line are
+ whitespace. Therefore, we must extend the region to be
+ invalidated up to the newline before START. */
+ ptrdiff_t line_beg = start;
+ ptrdiff_t start_byte = buf_charpos_to_bytepos (buf, start);
+
+ if (BUF_FETCH_BYTE (buf, start_byte - 1) != '\n')
+ {
+ struct buffer *old = current_buffer;
+
+ set_buffer_internal (buf);
+
+ line_beg = find_newline_no_quit (start, start_byte, -1,
+ &start_byte);
+ set_buffer_internal (old);
+ }
+ start = line_beg - (line_beg > BUF_BEG (buf));
+ }
+ invalidate_region_cache (buf,
+ buf->bidi_paragraph_cache,
+ start - BUF_BEG (buf), BUF_Z (buf) - end);
+ }
+ if (buf->newline_cache)
+ invalidate_region_cache (buf,
+ buf->newline_cache,
+ start - BUF_BEG (buf), BUF_Z (buf) - end);
+ if (buf->width_run_cache)
+ invalidate_region_cache (buf,
+ buf->width_run_cache,
+ start - BUF_BEG (buf), BUF_Z (buf) - end);
+}
+
/* These macros work with an argument named `preserve_ptr'
and a local variable named `preserve_marker'. */
VARIABLE is the variable to maybe set to nil.
NO-ERROR-FLAG is nil if there was an error,
anything else meaning no error (so this function does nothing). */
-static Lisp_Object
-reset_var_on_error (Lisp_Object val)
+struct rvoe_arg
{
- if (NILP (XCDR (val)))
- Fset (XCAR (val), Qnil);
- return Qnil;
+ Lisp_Object *location;
+ bool errorp;
+};
+
+static void
+reset_var_on_error (void *ptr)
+{
+ struct rvoe_arg *p = ptr;
+ if (p->errorp)
+ *p->location = Qnil;
}
/* Signal a change to the buffer immediately before it happens.
Lisp_Object preserve_marker;
struct gcpro gcpro1, gcpro2, gcpro3;
ptrdiff_t count = SPECPDL_INDEX ();
-
- if (inhibit_modification_hooks)
- return;
+ struct rvoe_arg rvoe_arg;
start = make_number (start_int);
end = make_number (end_int);
specbind (Qinhibit_modification_hooks, Qt);
/* If buffer is unmodified, run a special hook for that case. The
- check for Vfirst_change_hook is just a minor optimization. */
+ check for Vfirst_change_hook is just a minor optimization. */
if (SAVE_MODIFF >= MODIFF
&& !NILP (Vfirst_change_hook))
{
if (!NILP (Vbefore_change_functions))
{
Lisp_Object args[3];
- Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil);
+ rvoe_arg.location = &Vbefore_change_functions;
+ rvoe_arg.errorp = 1;
PRESERVE_VALUE;
PRESERVE_START_END;
/* Mark before-change-functions to be reset to nil in case of error. */
- record_unwind_protect (reset_var_on_error, rvoe_arg);
+ record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
/* Actually run the hook functions. */
args[0] = Qbefore_change_functions;
Frun_hook_with_args (3, args);
/* There was no error: unarm the reset_on_error. */
- XSETCDR (rvoe_arg, Qt);
+ rvoe_arg.errorp = 0;
}
if (buffer_has_overlays ())
FETCH_START, FETCH_END, Qnil);
}
- if (! NILP (start_marker))
- free_marker (start_marker);
- if (! NILP (end_marker))
- free_marker (end_marker);
RESTORE_VALUE;
UNGCPRO;
signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
{
ptrdiff_t count = SPECPDL_INDEX ();
+ struct rvoe_arg rvoe_arg;
+
if (inhibit_modification_hooks)
return;
&& current_buffer != XBUFFER (combine_after_change_buffer))
Fcombine_after_change_execute ();
- elt = Fcons (make_number (charpos - BEG),
- Fcons (make_number (Z - (charpos - lendel + lenins)),
- Fcons (make_number (lenins - lendel), Qnil)));
+ elt = list3i (charpos - BEG, Z - (charpos - lendel + lenins),
+ lenins - lendel);
combine_after_change_list
= Fcons (elt, combine_after_change_list);
combine_after_change_buffer = Fcurrent_buffer ();
if (!NILP (Vafter_change_functions))
{
Lisp_Object args[4];
- Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil);
+ rvoe_arg.location = &Vafter_change_functions;
+ rvoe_arg.errorp = 1;
/* Mark after-change-functions to be reset to nil in case of error. */
- record_unwind_protect (reset_var_on_error, rvoe_arg);
+ record_unwind_protect_ptr (reset_var_on_error, &rvoe_arg);
/* Actually run the hook functions. */
args[0] = Qafter_change_functions;
Frun_hook_with_args (4, args);
/* There was no error: unarm the reset_on_error. */
- XSETCDR (rvoe_arg, Qt);
+ rvoe_arg.errorp = 0;
}
if (buffer_has_overlays ())
unbind_to (count, Qnil);
}
-static Lisp_Object
+static void
Fcombine_after_change_execute_1 (Lisp_Object val)
{
Vcombine_after_change_calls = val;
- return val;
}
DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
inhibit_modification_hooks = 0;
DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
+ DEFSYM (Qregion_extract_function, "region-extract-function");
+
defsubr (&Scombine_after_change_execute);
}