=== modified file 'src/alloc.c' --- src/alloc.c 2012-07-23 11:15:43 +0000 +++ src/alloc.c 2012-07-24 04:57:58 +0000 @@ -3658,11 +3658,8 @@ val = allocate_misc (Lisp_Misc_Marker); p = XMARKER (val); - p->buffer = 0; - p->bytepos = 0; - p->charpos = 0; + INIT_MARKER (p, NULL, 0, 0, 0); p->next = NULL; - p->insertion_type = 0; return val; } @@ -3683,10 +3680,7 @@ obj = allocate_misc (Lisp_Misc_Marker); m = XMARKER (obj); - m->buffer = buf; - m->charpos = charpos; - m->bytepos = bytepos; - m->insertion_type = 0; + INIT_MARKER (m, buf, charpos, bytepos, 0); m->next = BUF_MARKERS (buf); BUF_MARKERS (buf) = m; return obj; @@ -5851,6 +5845,8 @@ static void mark_buffer (struct buffer *buffer) { + struct excursion *ex; + /* This is handled much like other pseudovectors... */ mark_vectorlike ((struct Lisp_Vector *) buffer); @@ -5865,6 +5861,11 @@ mark_overlay (buffer->overlays_before); mark_overlay (buffer->overlays_after); + /* In a struct excursion, markers are allocated in place and + invisible for GC, so only the window should be marked. */ + for (ex = buffer->excursions; ex; ex = ex->next) + mark_vectorlike ((struct Lisp_Vector *) ex->window); + /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && !VECTOR_MARKED_P (buffer->base_buffer)) mark_buffer (buffer->base_buffer); === modified file 'src/buffer.c' --- src/buffer.c 2012-07-23 11:15:43 +0000 +++ src/buffer.c 2012-07-24 04:35:50 +0000 @@ -366,6 +366,7 @@ *(BUF_GPT_ADDR (b)) = *(BUF_Z_ADDR (b)) = 0; /* Put an anchor '\0'. */ b->text->inhibit_shrinking = 0; + b->excursions = NULL; b->newline_cache = 0; b->width_run_cache = 0; BVAR (b, width_table) = Qnil; @@ -586,6 +587,7 @@ b->begv_byte = b->base_buffer->begv_byte; b->zv_byte = b->base_buffer->zv_byte; + b->excursions = NULL; b->newline_cache = 0; b->width_run_cache = 0; BVAR (b, width_table) = Qnil; === modified file 'src/buffer.h' --- src/buffer.h 2012-07-22 03:44:35 +0000 +++ src/buffer.h 2012-07-24 05:11:38 +0000 @@ -472,6 +472,30 @@ int inhibit_shrinking; }; +/* Used to record buffer state for save-excursion. */ + +struct excursion +{ + /* Saved value of XWINDOW (selected_window). */ + struct window *window; + + /* Non-zero if the window above has displayed this buffer. */ + unsigned visible : 1; + + /* Non-zero if this buffer has mark active. */ + unsigned active : 1; + + /* Saved point. */ + struct Lisp_Marker point; + + /* Saved mark. May point to [0, 0]. */ + struct Lisp_Marker mark; + + /* When the calls to save-excursion are nested, this points + to an outer save-excursion state, or NULL otherwise. */ + struct excursion *next; +}; + /* Lisp fields in struct buffer are hidden from most code and accessed via the BVAR macro, below. Only select pieces of code, like the GC, are allowed to use BUFFER_INTERNAL_FIELD. */ @@ -856,6 +880,9 @@ /* Position where the overlay lists are centered. */ ptrdiff_t overlay_center; + /* List of recorded excursions. */ + struct excursion *excursions; + /* Changes in the buffer are recorded here for undo, and t means don't record anything. This information belongs to the base buffer of an indirect buffer. But we can't store it in the === modified file 'src/editfns.c' --- src/editfns.c 2012-07-17 07:43:01 +0000 +++ src/editfns.c 2012-07-24 05:02:05 +0000 @@ -223,6 +223,19 @@ return build_marker (current_buffer, PT, PT_BYTE); } +/* Fast path to set point at MARK. */ + +static inline void +set_position (struct Lisp_Marker *mark) +{ + if (mark->charpos < BEGV) + SET_PT_BOTH (BEGV, BEGV_BYTE); + else if (mark->charpos > ZV) + SET_PT_BOTH (ZV, ZV_BYTE); + else + SET_PT_BOTH (mark->charpos, mark->bytepos); +} + DEFUN ("goto-char", Fgoto_char, Sgoto_char, 1, 1, "NGoto char: ", doc: /* Set point to POSITION, a number or marker. Beginning of buffer is position (point-min), end is (point-max). @@ -235,14 +248,7 @@ if (MARKERP (position) && current_buffer == XMARKER (position)->buffer) { - pos = marker_position (position); - if (pos < BEGV) - SET_PT_BOTH (BEGV, BEGV_BYTE); - else if (pos > ZV) - SET_PT_BOTH (ZV, ZV_BYTE); - else - SET_PT_BOTH (pos, marker_byte_position (position)); - + set_position (XMARKER (position)); return position; } @@ -821,104 +827,101 @@ Qnil, Qt, Qnil); } - +/* Save current buffer state before entering Fsave_excursion. */ + Lisp_Object save_excursion_save (void) { - int visible = (XBUFFER (XWINDOW (selected_window)->buffer) - == current_buffer); - - return Fcons (Fpoint_marker (), - Fcons (Fcopy_marker (BVAR (current_buffer, mark), Qnil), - Fcons (visible ? Qt : Qnil, - Fcons (BVAR (current_buffer, mark_active), - selected_window)))); + Lisp_Object buf; + struct excursion *ex; + struct window *w = XWINDOW (selected_window); + struct Lisp_Marker *m = XMARKER (BVAR (current_buffer, mark)); + + ex = xmalloc (sizeof *ex); + ex->window = w; + ex->visible = (XBUFFER (w->buffer) == current_buffer); + ex->active = !NILP (BVAR (current_buffer, mark_active)); + + /* We do not initialize type and gcmarkbit since this marker + is never referenced via Lisp_Object and invisible for GC. */ + INIT_MARKER (&ex->point, current_buffer, PT, PT_BYTE, 0); + ex->point.type = Lisp_Misc_Marker; + ex->point.next = BUF_MARKERS (current_buffer); + BUF_MARKERS (current_buffer) = &ex->point; + + /* Likewise. Note that charpos and bytepos may be zero. */ + INIT_MARKER (&ex->mark, current_buffer, m->charpos, + m->bytepos, m->insertion_type); + ex->mark.type = Lisp_Misc_Marker; + ex->mark.next = BUF_MARKERS (current_buffer); + BUF_MARKERS (current_buffer) = &ex->mark; + + ex->next = current_buffer->excursions; + current_buffer->excursions = ex; + XSETBUFFER (buf, current_buffer); + return buf; } +/* Restore BUFFER's values before leaving Fsave_excursion. */ + Lisp_Object -save_excursion_restore (Lisp_Object info) +save_excursion_restore (Lisp_Object buffer) { - Lisp_Object tem, tem1, omark, nmark; - struct gcpro gcpro1, gcpro2, gcpro3; - int visible_p; - - tem = Fmarker_buffer (XCAR (info)); - /* If buffer being returned to is now deleted, avoid error */ - /* Otherwise could get error here while unwinding to top level - and crash */ - /* In that case, Fmarker_buffer returns nil now. */ - if (NILP (tem)) - return Qnil; - - omark = nmark = Qnil; - GCPRO3 (info, omark, nmark); - - Fset_buffer (tem); - - /* Point marker. */ - tem = XCAR (info); - Fgoto_char (tem); - unchain_marker (XMARKER (tem)); - - /* Mark marker. */ - info = XCDR (info); - tem = XCAR (info); - omark = Fmarker_position (BVAR (current_buffer, mark)); - Fset_marker (BVAR (current_buffer, mark), tem, Fcurrent_buffer ()); - nmark = Fmarker_position (tem); - unchain_marker (XMARKER (tem)); - - /* visible */ - info = XCDR (info); - visible_p = !NILP (XCAR (info)); - -#if 0 /* We used to make the current buffer visible in the selected window - if that was true previously. That avoids some anomalies. - But it creates others, and it wasn't documented, and it is simpler - and cleaner never to alter the window/buffer connections. */ - tem1 = Fcar (tem); - if (!NILP (tem1) - && current_buffer != XBUFFER (XWINDOW (selected_window)->buffer)) - Fswitch_to_buffer (Fcurrent_buffer (), Qnil); -#endif /* 0 */ - - /* Mark active */ - info = XCDR (info); - tem = XCAR (info); - tem1 = BVAR (current_buffer, mark_active); - BVAR (current_buffer, mark_active) = tem; + int active; + struct buffer *buf; + struct excursion *ex; + struct Lisp_Marker *m; + ptrdiff_t oldmark, newmark; + + CHECK_BUFFER (buffer); + buf = XBUFFER (buffer); + eassert (!NILP (BVAR (buf, name))); + ex = buf->excursions; + eassert (ex != NULL); + + /* Restore current buffer. */ + set_buffer_internal (buf); + + /* Restore buffer position. */ + set_position (&ex->point); + unchain_marker (&ex->point); + + /* Restore mark if it was non-zero. */ + m = XMARKER (BVAR (buf, mark)); + oldmark = m->charpos; + if (BEGV <= ex->mark.charpos) + attach_marker (m, buf, ex->mark.charpos, ex->mark.bytepos); + newmark = ex->mark.charpos; + unchain_marker (&ex->mark); + + /* If mark and region was active, restore them. */ + active = !NILP (BVAR (buf, mark_active)); + BVAR (buf, mark_active) = ex->active ? Qt : Qnil; /* If mark is active now, and either was not active or was at a different place, run the activate hook. */ - if (! NILP (tem)) + if (ex->active && oldmark != newmark) { - if (! EQ (omark, nmark)) - { - tem = intern ("activate-mark-hook"); - Frun_hooks (1, &tem); - } + Lisp_Object tem = intern ("activate-mark-hook"); + Frun_hooks (1, &tem); } /* If mark has ceased to be active, run deactivate hook. */ - else if (! NILP (tem1)) + else if (active) { - tem = intern ("deactivate-mark-hook"); + Lisp_Object tem = intern ("deactivate-mark-hook"); Frun_hooks (1, &tem); } - /* If buffer was visible in a window, and a different window was - selected, and the old selected window is still showing this - buffer, restore point in that window. */ - tem = XCDR (info); - if (visible_p - && !EQ (tem, selected_window) - && (tem1 = XWINDOW (tem)->buffer, - (/* Window is live... */ - BUFFERP (tem1) - /* ...and it shows the current buffer. */ - && XBUFFER (tem1) == current_buffer))) - Fset_window_point (tem, make_number (PT)); + /* If buffer was visible in a window, and a different window + was selected, and the old selected window is still showing + this buffer, restore point in that window. */ + if (ex->visible + && ex->window != XWINDOW (selected_window) + && EQ (ex->window->buffer, buffer)) + set_marker_restricted (ex->window->pointm, make_number (PT), buffer); - UNGCPRO; + buf->excursions = ex->next; + xfree (ex); return Qnil; } === modified file 'src/lisp.h' --- src/lisp.h 2012-07-23 11:15:43 +0000 +++ src/lisp.h 2012-07-24 05:02:53 +0000 @@ -1283,6 +1283,12 @@ ptrdiff_t bytepos; }; +/* Used to setup base fields of Lisp_Marker. */ + +#define INIT_MARKER(mark, buf, cpos, bpos, itype) \ + ((mark)->buffer = (buf), (mark)->charpos = (cpos), \ + (mark)->bytepos = (bpos), (mark)->insertion_type = (itype), 1) + /* START and END are markers in the overlay's buffer, and PLIST is the overlay's property list. */ struct Lisp_Overlay @@ -2869,8 +2875,10 @@ extern Lisp_Object set_marker_restricted (Lisp_Object, Lisp_Object, Lisp_Object); extern Lisp_Object set_marker_both (Lisp_Object, Lisp_Object, ptrdiff_t, ptrdiff_t); extern Lisp_Object set_marker_restricted_both (Lisp_Object, Lisp_Object, - ptrdiff_t, ptrdiff_t); + ptrdiff_t, ptrdiff_t); extern Lisp_Object build_marker (struct buffer *, ptrdiff_t, ptrdiff_t); +extern void attach_marker (struct Lisp_Marker *, struct buffer *, + ptrdiff_t, ptrdiff_t); extern void syms_of_marker (void); /* Defined in fileio.c */ === modified file 'src/marker.c' --- src/marker.c 2012-07-22 05:37:24 +0000 +++ src/marker.c 2012-07-24 04:33:25 +0000 @@ -427,7 +427,7 @@ /* Change M so it points to B at CHARPOS and BYTEPOS. */ -static inline void +void attach_marker (struct Lisp_Marker *m, struct buffer *b, ptrdiff_t charpos, ptrdiff_t bytepos) {