aboutsummaryrefslogtreecommitdiffstats
path: root/src/undo.c
diff options
context:
space:
mode:
authorBarry O'Reilly2014-03-24 22:47:39 -0400
committerBarry O'Reilly2014-03-24 22:47:39 -0400
commit37ea8275f7faad1192ddaba9f4a0789580675e17 (patch)
treecb6242b0298180f32d8c253c1b3aa8af3c6572fa /src/undo.c
parent3e2377ce2f4eeb141ffbd000723c55813f78b08f (diff)
downloademacs-37ea8275f7faad1192ddaba9f4a0789580675e17.tar.gz
emacs-37ea8275f7faad1192ddaba9f4a0789580675e17.zip
Undo in region after markers in undo history relocated
* simple.el (primitive-undo): Only process marker adjustments validated against their corresponding (TEXT . POS). Issue warning for lone marker adjustments in undo history. (Bug#16818) (undo-make-selective-list): Add marker adjustments to selective undo list based on whether their corresponding (TEXT . POS) is in the region. Remove variable adjusted-markers, which was unused and only non nil during undo-make-selective-list. (undo-elt-in-region): Return nil when passed a marker adjustment and explain in function doc. Have (MARKER . ADJUSTMENT) undo records always be immediately after their corresponding (TEXT . POS) record in undo list. (Bug#16818) * lisp.h (record-delete): New arg record_markers. (record_marker_adjustment): No longer needed outside undo.c. * insdel.c (adjust_markers_for_delete): Move calculation of marker adjustments to undo.c's record_marker_adjustments. Note that fileio.c's decide_coding_unwind is another caller to adjust_markers_for_delete. Because it has undo list bound to t, it does not rely on adjust_markers_for_delete to record marker adjustments. (del_range_2): Swap call to record_delete and adjust_markers_for_delete so as undo marker adjustments are recorded before current deletion's adjustments, as before. (adjust_after_replace): (replace_range): Pass value for new record_markers arg to delete_record. * undo.c (record_marker_adjustment): Renamed to record_marker_adjustments and made static. (record_delete): Check record_markers arg and call record_marker_adjustments. (record_change): Pass value for new record_markers arg to delete_record. (record_point): at_boundary calculation no longer needs to account for marker adjustments. * undo-tests.el (undo-test-marker-adjustment-nominal): (undo-test-region-t-marker): New tests of marker adjustments. (undo-test-marker-adjustment-moved): (undo-test-region-mark-adjustment): New tests to demonstrate bug#16818, which fail without the fix. * markers.texi (Moving Marker Positions): The 2014-03-02 doc change mentioning undo's inability to handle relocated markers no longer applies. See bug#16818. * text.texi (Undo): Expand documentation of (TEXT . POS) and (MARKER . ADJUSTMENT) undo elements.
Diffstat (limited to 'src/undo.c')
-rw-r--r--src/undo.c112
1 files changed, 62 insertions, 50 deletions
diff --git a/src/undo.c b/src/undo.c
index 7286d40b2e5..2dde02b99a9 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -75,27 +75,8 @@ record_point (ptrdiff_t pt)
75 Fundo_boundary (); 75 Fundo_boundary ();
76 last_undo_buffer = current_buffer; 76 last_undo_buffer = current_buffer;
77 77
78 if (CONSP (BVAR (current_buffer, undo_list))) 78 at_boundary = ! CONSP (BVAR (current_buffer, undo_list))
79 { 79 || NILP (XCAR (BVAR (current_buffer, undo_list)));
80 /* Set AT_BOUNDARY only when we have nothing other than
81 marker adjustment before undo boundary. */
82
83 Lisp_Object tail = BVAR (current_buffer, undo_list), elt;
84
85 while (1)
86 {
87 if (NILP (tail))
88 elt = Qnil;
89 else
90 elt = XCAR (tail);
91 if (NILP (elt) || ! (CONSP (elt) && MARKERP (XCAR (elt))))
92 break;
93 tail = XCDR (tail);
94 }
95 at_boundary = NILP (elt);
96 }
97 else
98 at_boundary = 1;
99 80
100 if (MODIFF <= SAVE_MODIFF) 81 if (MODIFF <= SAVE_MODIFF)
101 record_first_change (); 82 record_first_change ();
@@ -147,11 +128,61 @@ record_insert (ptrdiff_t beg, ptrdiff_t length)
147 Fcons (Fcons (lbeg, lend), BVAR (current_buffer, undo_list))); 128 Fcons (Fcons (lbeg, lend), BVAR (current_buffer, undo_list)));
148} 129}
149 130
150/* Record that a deletion is about to take place, 131/* Record the fact that markers in the region of FROM, TO are about to
151 of the characters in STRING, at location BEG. */ 132 be adjusted. This is done only when a marker points within text
133 being deleted, because that's the only case where an automatic
134 marker adjustment won't be inverted automatically by undoing the
135 buffer modification. */
136
137static void
138record_marker_adjustments (ptrdiff_t from, ptrdiff_t to)
139{
140 Lisp_Object marker;
141 register struct Lisp_Marker *m;
142 register ptrdiff_t charpos, adjustment;
143
144 /* Allocate a cons cell to be the undo boundary after this command. */
145 if (NILP (pending_boundary))
146 pending_boundary = Fcons (Qnil, Qnil);
147
148 if (current_buffer != last_undo_buffer)
149 Fundo_boundary ();
150 last_undo_buffer = current_buffer;
151
152 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
153 {
154 charpos = m->charpos;
155 eassert (charpos <= Z);
156
157 if (from <= charpos && charpos <= to)
158 {
159 /* insertion_type nil markers will end up at the beginning of
160 the re-inserted text after undoing a deletion, and must be
161 adjusted to move them to the correct place.
162
163 insertion_type t markers will automatically move forward
164 upon re-inserting the deleted text, so we have to arrange
165 for them to move backward to the correct position. */
166 adjustment = (m->insertion_type ? to : from) - charpos;
167
168 if (adjustment)
169 {
170 XSETMISC (marker, m);
171 bset_undo_list
172 (current_buffer,
173 Fcons (Fcons (marker, make_number (adjustment)),
174 BVAR (current_buffer, undo_list)));
175 }
176 }
177 }
178}
179
180/* Record that a deletion is about to take place, of the characters in
181 STRING, at location BEG. Optionally record adjustments for markers
182 in the region STRING occupies in the current buffer. */
152 183
153void 184void
154record_delete (ptrdiff_t beg, Lisp_Object string) 185record_delete (ptrdiff_t beg, Lisp_Object string, bool record_markers)
155{ 186{
156 Lisp_Object sbeg; 187 Lisp_Object sbeg;
157 188
@@ -169,34 +200,15 @@ record_delete (ptrdiff_t beg, Lisp_Object string)
169 record_point (beg); 200 record_point (beg);
170 } 201 }
171 202
172 bset_undo_list 203 /* primitive-undo assumes marker adjustments are recorded
173 (current_buffer, 204 immediately before the deletion is recorded. See bug 16818
174 Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list))); 205 discussion. */
175} 206 if (record_markers)
176 207 record_marker_adjustments (beg, beg + SCHARS (string));
177/* Record the fact that MARKER is about to be adjusted by ADJUSTMENT.
178 This is done only when a marker points within text being deleted,
179 because that's the only case where an automatic marker adjustment
180 won't be inverted automatically by undoing the buffer modification. */
181
182void
183record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment)
184{
185 if (EQ (BVAR (current_buffer, undo_list), Qt))
186 return;
187
188 /* Allocate a cons cell to be the undo boundary after this command. */
189 if (NILP (pending_boundary))
190 pending_boundary = Fcons (Qnil, Qnil);
191
192 if (current_buffer != last_undo_buffer)
193 Fundo_boundary ();
194 last_undo_buffer = current_buffer;
195 208
196 bset_undo_list 209 bset_undo_list
197 (current_buffer, 210 (current_buffer,
198 Fcons (Fcons (marker, make_number (adjustment)), 211 Fcons (Fcons (string, sbeg), BVAR (current_buffer, undo_list)));
199 BVAR (current_buffer, undo_list)));
200} 212}
201 213
202/* Record that a replacement is about to take place, 214/* Record that a replacement is about to take place,
@@ -206,7 +218,7 @@ record_marker_adjustment (Lisp_Object marker, ptrdiff_t adjustment)
206void 218void
207record_change (ptrdiff_t beg, ptrdiff_t length) 219record_change (ptrdiff_t beg, ptrdiff_t length)
208{ 220{
209 record_delete (beg, make_buffer_string (beg, beg + length, 1)); 221 record_delete (beg, make_buffer_string (beg, beg + length, 1), false);
210 record_insert (beg, length); 222 record_insert (beg, length);
211} 223}
212 224