aboutsummaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/ChangeLog28
-rw-r--r--src/insdel.c40
-rw-r--r--src/lisp.h3
-rw-r--r--src/undo.c112
4 files changed, 99 insertions, 84 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index da82eca9df2..cbe10d89690 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,31 @@
12014-03-24 Barry O'Reilly <gundaetiapo@gmail.com>
2
3 Have (MARKER . ADJUSTMENT) undo records always be immediately
4 after their corresponding (TEXT . POS) record in undo list.
5 (Bug#16818)
6 * lisp.h (record-delete): New arg record_markers.
7 (record_marker_adjustment): No longer needed outside undo.c.
8 * insdel.c (adjust_markers_for_delete): Move calculation of marker
9 adjustments to undo.c's record_marker_adjustments. Note that
10 fileio.c's decide_coding_unwind is another caller to
11 adjust_markers_for_delete. Because it has undo list bound to t,
12 it does not rely on adjust_markers_for_delete to record marker
13 adjustments.
14 (del_range_2): Swap call to record_delete and
15 adjust_markers_for_delete so as undo marker adjustments are
16 recorded before current deletion's adjustments, as before.
17 (adjust_after_replace):
18 (replace_range): Pass value for new record_markers arg to
19 delete_record.
20 * undo.c (record_marker_adjustment): Renamed to
21 record_marker_adjustments and made static.
22 (record_delete): Check record_markers arg and call
23 record_marker_adjustments.
24 (record_change): Pass value for new record_markers arg to
25 delete_record.
26 (record_point): at_boundary calculation no longer needs to account
27 for marker adjustments.
28
12014-03-24 Martin Rudalics <rudalics@gmx.at> 292014-03-24 Martin Rudalics <rudalics@gmx.at>
2 30
3 * w32term.c (x_set_window_size): Refine fix from 2014-03-14 31 * w32term.c (x_set_window_size): Refine fix from 2014-03-14
diff --git a/src/insdel.c b/src/insdel.c
index 1c9bafd6004..eb1ad627f66 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -233,34 +233,9 @@ adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
233 /* Here's the case where a marker is inside text being deleted. */ 233 /* Here's the case where a marker is inside text being deleted. */
234 else if (charpos > from) 234 else if (charpos > from)
235 { 235 {
236 if (! m->insertion_type)
237 { /* Normal markers will end up at the beginning of the
238 re-inserted text after undoing a deletion, and must be
239 adjusted to move them to the correct place. */
240 XSETMISC (marker, m);
241 record_marker_adjustment (marker, from - charpos);
242 }
243 else if (charpos < to)
244 { /* Before-insertion markers will automatically move forward
245 upon re-inserting the deleted text, so we have to arrange
246 for them to move backward to the correct position. */
247 XSETMISC (marker, m);
248 record_marker_adjustment (marker, to - charpos);
249 }
250 m->charpos = from; 236 m->charpos = from;
251 m->bytepos = from_byte; 237 m->bytepos = from_byte;
252 } 238 }
253 /* Here's the case where a before-insertion marker is immediately
254 before the deleted region. */
255 else if (charpos == from && m->insertion_type)
256 {
257 /* Undoing the change uses normal insertion, which will
258 incorrectly make MARKER move forward, so we arrange for it
259 to then move backward to the correct place at the beginning
260 of the deleted region. */
261 XSETMISC (marker, m);
262 record_marker_adjustment (marker, to - from);
263 }
264 } 239 }
265} 240}
266 241
@@ -1219,7 +1194,7 @@ adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
1219 from + len, from_byte + len_byte, 0); 1194 from + len, from_byte + len_byte, 0);
1220 1195
1221 if (nchars_del > 0) 1196 if (nchars_del > 0)
1222 record_delete (from, prev_text); 1197 record_delete (from, prev_text, false);
1223 record_insert (from, len); 1198 record_insert (from, len);
1224 1199
1225 if (len > nchars_del) 1200 if (len > nchars_del)
@@ -1384,7 +1359,7 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
1384 if (!NILP (deletion)) 1359 if (!NILP (deletion))
1385 { 1360 {
1386 record_insert (from + SCHARS (deletion), inschars); 1361 record_insert (from + SCHARS (deletion), inschars);
1387 record_delete (from, deletion); 1362 record_delete (from, deletion, false);
1388 } 1363 }
1389 1364
1390 GAP_SIZE -= outgoing_insbytes; 1365 GAP_SIZE -= outgoing_insbytes;
@@ -1716,13 +1691,14 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1716 else 1691 else
1717 deletion = Qnil; 1692 deletion = Qnil;
1718 1693
1719 /* Relocate all markers pointing into the new, larger gap 1694 /* Record marker adjustments, and text deletion into undo
1720 to point at the end of the text before the gap. 1695 history. */
1721 Do this before recording the deletion, 1696 record_delete (from, deletion, true);
1722 so that undo handles this after reinserting the text. */ 1697
1698 /* Relocate all markers pointing into the new, larger gap to point
1699 at the end of the text before the gap. */
1723 adjust_markers_for_delete (from, from_byte, to, to_byte); 1700 adjust_markers_for_delete (from, from_byte, to, to_byte);
1724 1701
1725 record_delete (from, deletion);
1726 MODIFF++; 1702 MODIFF++;
1727 CHARS_MODIFF = MODIFF; 1703 CHARS_MODIFF = MODIFF;
1728 1704
diff --git a/src/lisp.h b/src/lisp.h
index 2f9a30fdfe9..30f52b9070c 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4198,9 +4198,8 @@ extern void syms_of_macros (void);
4198extern Lisp_Object Qapply; 4198extern Lisp_Object Qapply;
4199extern Lisp_Object Qinhibit_read_only; 4199extern Lisp_Object Qinhibit_read_only;
4200extern void truncate_undo_list (struct buffer *); 4200extern void truncate_undo_list (struct buffer *);
4201extern void record_marker_adjustment (Lisp_Object, ptrdiff_t);
4202extern void record_insert (ptrdiff_t, ptrdiff_t); 4201extern void record_insert (ptrdiff_t, ptrdiff_t);
4203extern void record_delete (ptrdiff_t, Lisp_Object); 4202extern void record_delete (ptrdiff_t, Lisp_Object, bool);
4204extern void record_first_change (void); 4203extern void record_first_change (void);
4205extern void record_change (ptrdiff_t, ptrdiff_t); 4204extern void record_change (ptrdiff_t, ptrdiff_t);
4206extern void record_property_change (ptrdiff_t, ptrdiff_t, 4205extern void record_property_change (ptrdiff_t, ptrdiff_t,
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