Code cleanup for long line optimizations

This commit does not change any code, it merely renames functions
and clarifies the documentation, to make the code hopefully easier
to grasp.

* src/dispextern.h (struct it): Rename the 'narrowed_begv',
'narrowed_zv', 'locked_narrowing_begv', 'locked_narrowing_zv' to
'medium_narrowing_begv', 'medium_narrowing_zv',
'large_narrowing_begv', 'large_narrowing_zv'.  Clarify the
comments.
Update the prototypes of the functions renamed in xdisp.c.

* src/lisp.h: Update the prototypes of the functions renamed in
editfns.c.  Remove the prototype of
'safe_run_hooks_maybe_narrowed', which is used only in keyboard.c.

* src/xdisp.c
(get_small_narrowing_begv): Renamed from
'get_closer_narrowed_begv'.
(get_medium_narrowing_begv): Renamed from 'get_narrowed_begv'.
(get_medium_narrowing_zv): Renamed from 'get_narrowed_zv'.
(get_large_narrowing_begv): Renamed from 'get_locked_narrowing_begv'.
(get_large_narrowing_zv): Renamed from 'get_locked_narrowing_zv'.
(SET_WITH_NARROWED_BEGV): Use the new field names.
(handle_fontified_prop): Use the new function and new field names.
(back_to_previous_line_start): Use the new field name.
(back_to_previous_visible_line_start): Use the new field name.
(reseat): Use the new function and new field names.
(get_visually_first_element): Use the new field name.
(move_it_vertically_backward): Use the new function name.
(redisplay_internal): Use the new function name.
Also add a large comment to explain how Emacs deals with long lines.

* src/keyboard.c:
(safe_run_hooks_maybe_narrowed): Use the new function names from
xdisp.c and editfns.c.  Make the function static, and add a
prototype.

* src/editfns.c:
(labeled_restrictions): Renamed from 'narrowing_locks'.
(labeled_restrictions_add): Renamed from 'narrowing_locks_add'.
(labeled_restrictions_remove): Renamed from
'narrowing_locks_remove'.
(labeled_restrictions_get_bound): Renamed from
'narrowing_lock_get_bound'.
(labeled_restrictions_peek_label): Renamed from
'narrowing_lock_peek_tag'.
(labeled_restrictions_push): Renamed from 'narrowing_lock_push'.
(labeled_restrictions_pop): Renamed from 'narrowing_lock_pop'.
(unwind_reset_outermost_restriction): Renamed from
'unwind_reset_outermost_narrowing'.
(reset_outermost_restrictions): Renamed from
'reset_outermost_narrowings'.
(labeled_restrictions_save): Renamed from 'narrowing_locks_save'.
(labeled_restrictions_restore): Renamed from
'narrowing_locks_restore'.
(unwind_labeled_narrow_to_region): Renamed from
'unwind_narrow_to_region_locked'.
(labeled_narrow_to_region): Renamed from
'narrow_to_region_locked'.
(Finternal__label_restriction): Renamed from
'Finternal__lock_narrowing'.
(Finternal__unlabel_restriction): Renamed from
'Finternal__unlock_narrowing'.
(Fwiden): Use the new function names.
(Fnarrow_to_region): Use the new function names.
(save_restriction_save): Use the new function names.
(syms_of_editfns): Use the new function names.
<outermost-restriction>: Renamed from 'outermost-narrowing'.

* lisp/subr.el (internal--with-restriction): Use the new internal
function name.
(internal--without-restriction): Use the new internal function
name.

* src/composite.c (composition_compute_stop_pos):
(find_automatic_composition): Use the new function name.

* doc/lispref/positions.texi (Narrowing): Add index entry.
This commit is contained in:
Gregory Heytings 2023-03-28 23:06:54 +00:00
parent 3965c65d5e
commit 85ed1c9ca6
8 changed files with 314 additions and 207 deletions

View file

@ -1154,6 +1154,7 @@ saved bounds. In that case it is equivalent to
@end example @end example
@cindex labeled narrowing @cindex labeled narrowing
@cindex labeled restriction
When the optional argument @var{label}, a symbol, is present, the When the optional argument @var{label}, a symbol, is present, the
narrowing is @dfn{labeled}. A labeled narrowing differs from a narrowing is @dfn{labeled}. A labeled narrowing differs from a
non-labeled one in several ways: non-labeled one in several ways:

View file

@ -3975,7 +3975,7 @@ same LABEL argument.
"Helper function for `with-restriction', which see." "Helper function for `with-restriction', which see."
(save-restriction (save-restriction
(narrow-to-region start end) (narrow-to-region start end)
(if label (internal--lock-narrowing label)) (if label (internal--label-restriction label))
(funcall body))) (funcall body)))
(defmacro without-restriction (&rest rest) (defmacro without-restriction (&rest rest)
@ -3997,7 +3997,7 @@ are lifted.
(defun internal--without-restriction (body &optional label) (defun internal--without-restriction (body &optional label)
"Helper function for `without-restriction', which see." "Helper function for `without-restriction', which see."
(save-restriction (save-restriction
(if label (internal--unlock-narrowing label)) (if label (internal--unlabel-restriction label))
(widen) (widen)
(funcall body))) (funcall body)))

View file

@ -1075,7 +1075,7 @@ composition_compute_stop_pos (struct composition_it *cmp_it, ptrdiff_t charpos,
with long lines, however, NL might be far away, so with long lines, however, NL might be far away, so
pretend that the buffer is smaller. */ pretend that the buffer is smaller. */
if (current_buffer->long_line_optimizations_p) if (current_buffer->long_line_optimizations_p)
endpos = get_closer_narrowed_begv (cmp_it->parent_it->w, charpos); endpos = get_small_narrowing_begv (cmp_it->parent_it->w, charpos);
} }
} }
cmp_it->id = -1; cmp_it->id = -1;
@ -1654,7 +1654,7 @@ find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit, ptrdiff_t backlim,
{ {
/* In buffers with very long lines, this function becomes very /* In buffers with very long lines, this function becomes very
slow. Pretend that the buffer is narrowed to make it fast. */ slow. Pretend that the buffer is narrowed to make it fast. */
ptrdiff_t begv = get_closer_narrowed_begv (w, window_point (w)); ptrdiff_t begv = get_small_narrowing_begv (w, window_point (w));
if (pos > begv) if (pos > begv)
head = begv; head = begv;
} }

View file

@ -2334,21 +2334,20 @@ struct it
with which display_string was called. */ with which display_string was called. */
ptrdiff_t end_charpos; ptrdiff_t end_charpos;
/* Alternate begin position of the buffer that may be used to /* Alternate begin and end positions of the buffer that are used to
optimize display (see the SET_WITH_NARROWED_BEGV macro). */ optimize display of buffers with long lines. These two fields
ptrdiff_t narrowed_begv; hold the return value of the 'get_medium_narrowing_begv' and
'get_medium_narrowing_zv' functions. */
ptrdiff_t medium_narrowing_begv;
ptrdiff_t medium_narrowing_zv;
/* Alternate end position of the buffer that may be used to /* Alternate begin and end positions of the buffer that are used for
optimize display. */ labeled narrowings around low-level hooks in buffers with long
ptrdiff_t narrowed_zv; lines. These two fields hold the return value of the
'get_large_narrowing_begv' and 'get_large_narrowing_zv'
/* Begin position of the buffer for the locked narrowing around functions. */
low-level hooks. */ ptrdiff_t large_narrowing_begv;
ptrdiff_t locked_narrowing_begv; ptrdiff_t large_narrowing_zv;
/* End position of the buffer for the locked narrowing around
low-level hooks. */
ptrdiff_t locked_narrowing_zv;
/* C string to iterate over. Non-null means get characters from /* C string to iterate over. Non-null means get characters from
this string, otherwise characters are read from current_buffer this string, otherwise characters are read from current_buffer
@ -3410,11 +3409,11 @@ void mark_window_display_accurate (Lisp_Object, bool);
void redisplay_preserve_echo_area (int); void redisplay_preserve_echo_area (int);
void init_iterator (struct it *, struct window *, ptrdiff_t, void init_iterator (struct it *, struct window *, ptrdiff_t,
ptrdiff_t, struct glyph_row *, enum face_id); ptrdiff_t, struct glyph_row *, enum face_id);
ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t); ptrdiff_t get_medium_narrowing_begv (struct window *, ptrdiff_t);
ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t); ptrdiff_t get_medium_narrowing_zv (struct window *, ptrdiff_t);
ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t); ptrdiff_t get_small_narrowing_begv (struct window *, ptrdiff_t);
ptrdiff_t get_locked_narrowing_begv (ptrdiff_t); ptrdiff_t get_large_narrowing_begv (ptrdiff_t);
ptrdiff_t get_locked_narrowing_zv (ptrdiff_t); ptrdiff_t get_large_narrowing_zv (ptrdiff_t);
void init_iterator_to_row_start (struct it *, struct window *, void init_iterator_to_row_start (struct it *, struct window *,
struct glyph_row *); struct glyph_row *);
void start_display (struct it *, struct window *, struct text_pos); void start_display (struct it *, struct window *, struct text_pos);

View file

@ -2653,182 +2653,197 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1); return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);
} }
/* Alist of buffers in which locked narrowing is used. The car of /* Alist of buffers in which labeled restrictions are used. The car
each list element is a buffer, the cdr is a list of triplets (tag of each list element is a buffer, the cdr is a list of triplets
begv-marker zv-marker). The last element of that list always uses (label begv-marker zv-marker). The last triplet of that list
the (uninterned) Qoutermost_narrowing tag and records the narrowing always uses the (uninterned) Qoutermost_restriction label, and
bounds that were set by the user and that are visible on display. records the restriction bounds that were current when the first
This alist is used internally by narrow-to-region, widen, labeled restriction was entered (which may be a narrowing that was
internal--lock-narrowing, internal--unlock-narrowing and set by the user and is visible on display). This alist is used
save-restriction. For efficiency reasons, an alist is used instead internally by narrow-to-region, widen, internal--label-restriction,
of a buffer-local variable: otherwise reset_outermost_narrowings, internal--unlabel-restriction and save-restriction. For efficiency
which is called during each redisplay cycle, would have to loop reasons, an alist is used instead of a buffer-local variable:
through all live buffers. */ otherwise reset_outermost_restrictions, which is called during each
static Lisp_Object narrowing_locks; redisplay cycle, would have to loop through all live buffers. */
static Lisp_Object labeled_restrictions;
/* Add BUF with its LOCKS in the narrowing_locks alist. */ /* Add BUF with its list of labeled RESTRICTIONS in the
labeled_restrictions alist. */
static void static void
narrowing_locks_add (Lisp_Object buf, Lisp_Object locks) labeled_restrictions_add (Lisp_Object buf, Lisp_Object restrictions)
{ {
narrowing_locks = nconc2 (list1 (list2 (buf, locks)), narrowing_locks); labeled_restrictions = nconc2 (list1 (list2 (buf, restrictions)),
labeled_restrictions);
} }
/* Remove BUF and its locks from the narrowing_locks alist. Do /* Remove BUF and its list of labeled restrictions from the
nothing if BUF is not present in narrowing_locks. */ labeled_restrictions alist. Do nothing if BUF is not present in
labeled_restrictions. */
static void static void
narrowing_locks_remove (Lisp_Object buf) labeled_restrictions_remove (Lisp_Object buf)
{ {
narrowing_locks = Fdelq (Fassoc (buf, narrowing_locks, Qnil), labeled_restrictions = Fdelq (Fassoc (buf, labeled_restrictions, Qnil),
narrowing_locks); labeled_restrictions);
} }
/* Retrieve one of the BEGV/ZV bounds of a narrowing in BUF from the /* Retrieve one of the labeled restriction bounds in BUF from the
narrowing_locks alist, as a pointer to a struct Lisp_Marker, or labeled_restrictions alist, as a pointer to a struct Lisp_Marker,
NULL if BUF is not in narrowing_locks or is a killed buffer. When or return NULL if BUF is not in labeled_restrictions or is a killed
OUTERMOST is true, the bounds that were set by the user and that buffer. When OUTERMOST is true, the restriction bounds that were
are visible on display are returned. Otherwise the innermost current when the first labeled restriction was entered are
locked narrowing bounds are returned. */ returned. Otherwise the bounds of the innermost labeled
restriction are returned. */
static struct Lisp_Marker * static struct Lisp_Marker *
narrowing_lock_get_bound (Lisp_Object buf, bool begv, bool outermost) labeled_restrictions_get_bound (Lisp_Object buf, bool begv, bool outermost)
{ {
if (NILP (Fbuffer_live_p (buf))) if (NILP (Fbuffer_live_p (buf)))
return NULL; return NULL;
Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); Lisp_Object restrictions = assq_no_quit (buf, labeled_restrictions);
if (NILP (buffer_locks)) if (NILP (restrictions))
return NULL; return NULL;
buffer_locks = XCAR (XCDR (buffer_locks)); restrictions = XCAR (XCDR (restrictions));
Lisp_Object bounds Lisp_Object bounds
= outermost = outermost
? XCDR (assq_no_quit (Qoutermost_narrowing, buffer_locks)) ? XCDR (assq_no_quit (Qoutermost_restriction, restrictions))
: XCDR (XCAR (buffer_locks)); : XCDR (XCAR (restrictions));
eassert (! NILP (bounds)); eassert (! NILP (bounds));
Lisp_Object marker = begv ? XCAR (bounds) : XCAR (XCDR (bounds)); Lisp_Object marker = begv ? XCAR (bounds) : XCAR (XCDR (bounds));
eassert (EQ (Fmarker_buffer (marker), buf)); eassert (EQ (Fmarker_buffer (marker), buf));
return XMARKER (marker); return XMARKER (marker);
} }
/* Retrieve the tag of the innermost narrowing in BUF. Return nil if /* Retrieve the label of the innermost labeled restriction in BUF.
BUF is not in narrowing_locks or is a killed buffer. */ Return nil if BUF is not in labeled_restrictions or is a killed
buffer. */
static Lisp_Object static Lisp_Object
narrowing_lock_peek_tag (Lisp_Object buf) labeled_restrictions_peek_label (Lisp_Object buf)
{ {
if (NILP (Fbuffer_live_p (buf))) if (NILP (Fbuffer_live_p (buf)))
return Qnil; return Qnil;
Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); Lisp_Object restrictions = assq_no_quit (buf, labeled_restrictions);
if (NILP (buffer_locks)) if (NILP (restrictions))
return Qnil; return Qnil;
Lisp_Object tag = XCAR (XCAR (XCAR (XCDR (buffer_locks)))); Lisp_Object label = XCAR (XCAR (XCAR (XCDR (restrictions))));
eassert (! NILP (tag)); eassert (! NILP (label));
return tag; return label;
} }
/* Add a LOCK for BUF in the narrowing_locks alist. */ /* Add a labeled RESTRICTION for BUF in the labeled_restrictions
alist. */
static void static void
narrowing_lock_push (Lisp_Object buf, Lisp_Object lock) labeled_restrictions_push (Lisp_Object buf, Lisp_Object restriction)
{ {
Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); Lisp_Object restrictions = assq_no_quit (buf, labeled_restrictions);
if (NILP (buffer_locks)) if (NILP (restrictions))
narrowing_locks_add (buf, list1 (lock)); labeled_restrictions_add (buf, list1 (restriction));
else else
XSETCDR (buffer_locks, list1 (nconc2 (list1 (lock), XSETCDR (restrictions, list1 (nconc2 (list1 (restriction),
XCAR (XCDR (buffer_locks))))); XCAR (XCDR (restrictions)))));
} }
/* Remove the innermost lock in BUF from the narrowing_locks alist. /* Remove the innermost labeled restriction in BUF from the
Do nothing if BUF is not present in narrowing_locks. */ labeled_restrictions alist. Do nothing if BUF is not present in
labeled_restrictions. */
static void static void
narrowing_lock_pop (Lisp_Object buf) labeled_restrictions_pop (Lisp_Object buf)
{ {
Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); Lisp_Object restrictions = assq_no_quit (buf, labeled_restrictions);
if (NILP (buffer_locks)) if (NILP (restrictions))
return; return;
if (EQ (narrowing_lock_peek_tag (buf), Qoutermost_narrowing)) if (EQ (labeled_restrictions_peek_label (buf), Qoutermost_restriction))
narrowing_locks_remove (buf); labeled_restrictions_remove (buf);
else else
XSETCDR (buffer_locks, list1 (XCDR (XCAR (XCDR (buffer_locks))))); XSETCDR (restrictions, list1 (XCDR (XCAR (XCDR (restrictions)))));
} }
static void static void
unwind_reset_outermost_narrowing (Lisp_Object buf) unwind_reset_outermost_restriction (Lisp_Object buf)
{ {
struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); struct Lisp_Marker *begv
struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); = labeled_restrictions_get_bound (buf, true, false);
struct Lisp_Marker *zv
= labeled_restrictions_get_bound (buf, false, false);
if (begv != NULL && zv != NULL) if (begv != NULL && zv != NULL)
{ {
SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos); SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos);
SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos); SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos);
} }
else else
narrowing_locks_remove (buf); labeled_restrictions_remove (buf);
} }
/* Restore the narrowing bounds that were set by the user, and restore /* Restore the restriction bounds that were current when the first
the bounds of the locked narrowing upon return. labeled restriction was entered, and restore the bounds of the
innermost labeled restriction upon return.
In particular, this function is called when redisplay starts, so In particular, this function is called when redisplay starts, so
that if a Lisp function executed during redisplay calls (redisplay) that if a Lisp function executed during redisplay calls (redisplay)
while a locked narrowing is in effect, the locked narrowing will while labeled restrictions are in effect, these restrictions will
not be visible on display. not become visible on display.
See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#140 and See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#140 and
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#254 for example https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#254 for example
recipes that demonstrate why this is necessary. */ recipes that demonstrate why this is necessary. */
void void
reset_outermost_narrowings (void) reset_outermost_restrictions (void)
{ {
Lisp_Object val, buf; Lisp_Object val, buf;
for (val = narrowing_locks; CONSP (val); val = XCDR (val)) for (val = labeled_restrictions; CONSP (val); val = XCDR (val))
{ {
buf = XCAR (XCAR (val)); buf = XCAR (XCAR (val));
eassert (BUFFERP (buf)); eassert (BUFFERP (buf));
struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, true); struct Lisp_Marker *begv
struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, true); = labeled_restrictions_get_bound (buf, true, true);
struct Lisp_Marker *zv
= labeled_restrictions_get_bound (buf, false, true);
if (begv != NULL && zv != NULL) if (begv != NULL && zv != NULL)
{ {
SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos); SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos);
SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos); SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos);
record_unwind_protect (unwind_reset_outermost_narrowing, buf); record_unwind_protect (unwind_reset_outermost_restriction, buf);
} }
else else
narrowing_locks_remove (buf); labeled_restrictions_remove (buf);
} }
} }
/* Helper functions to save and restore the narrowing locks of the /* Helper functions to save and restore the labeled restrictions of
current buffer in Fsave_restriction. */ the current buffer in Fsave_restriction. */
static Lisp_Object static Lisp_Object
narrowing_locks_save (void) labeled_restrictions_save (void)
{ {
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
Lisp_Object locks = assq_no_quit (buf, narrowing_locks); Lisp_Object restrictions = assq_no_quit (buf, labeled_restrictions);
if (!NILP (locks)) if (! NILP (restrictions))
locks = XCAR (XCDR (locks)); restrictions = XCAR (XCDR (restrictions));
return Fcons (buf, Fcopy_sequence (locks)); return Fcons (buf, Fcopy_sequence (restrictions));
} }
static void static void
narrowing_locks_restore (Lisp_Object buf_and_saved_locks) labeled_restrictions_restore (Lisp_Object buf_and_restrictions)
{ {
Lisp_Object buf = XCAR (buf_and_saved_locks); Lisp_Object buf = XCAR (buf_and_restrictions);
Lisp_Object saved_locks = XCDR (buf_and_saved_locks); Lisp_Object restrictions = XCDR (buf_and_restrictions);
narrowing_locks_remove (buf); labeled_restrictions_remove (buf);
if (!NILP (saved_locks)) if (! NILP (restrictions))
narrowing_locks_add (buf, saved_locks); labeled_restrictions_add (buf, restrictions);
} }
static void static void
unwind_narrow_to_region_locked (Lisp_Object tag) unwind_labeled_narrow_to_region (Lisp_Object label)
{ {
Finternal__unlock_narrowing (tag); Finternal__unlabel_restriction (label);
Fwiden (); Fwiden ();
} }
/* Narrow current_buffer to BEGV-ZV with a narrowing locked with TAG. */ /* Narrow current_buffer to BEGV-ZV with a restriction labeled with
LABEL. */
void void
narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag) labeled_narrow_to_region (Lisp_Object begv, Lisp_Object zv,
Lisp_Object label)
{ {
Fnarrow_to_region (begv, zv); Fnarrow_to_region (begv, zv);
Finternal__lock_narrowing (tag); Finternal__label_restriction (label);
record_unwind_protect (restore_point_unwind, Fpoint_marker ()); record_unwind_protect (restore_point_unwind, Fpoint_marker ());
record_unwind_protect (unwind_narrow_to_region_locked, tag); record_unwind_protect (unwind_labeled_narrow_to_region, label);
} }
DEFUN ("widen", Fwiden, Swiden, 0, 0, "", DEFUN ("widen", Fwiden, Swiden, 0, 0, "",
@ -2842,11 +2857,11 @@ To gain access to other portions of the buffer, use
`without-restriction' with the same label. */) `without-restriction' with the same label. */)
(void) (void)
{ {
Fset (Qoutermost_narrowing, Qnil); Fset (Qoutermost_restriction, Qnil);
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
Lisp_Object tag = narrowing_lock_peek_tag (buf); Lisp_Object label = labeled_restrictions_peek_label (buf);
if (NILP (tag)) if (NILP (label))
{ {
if (BEG != BEGV || Z != ZV) if (BEG != BEGV || Z != ZV)
current_buffer->clip_changed = 1; current_buffer->clip_changed = 1;
@ -2856,19 +2871,21 @@ To gain access to other portions of the buffer, use
} }
else else
{ {
struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); struct Lisp_Marker *begv
struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); = labeled_restrictions_get_bound (buf, true, false);
struct Lisp_Marker *zv
= labeled_restrictions_get_bound (buf, false, false);
eassert (begv != NULL && zv != NULL); eassert (begv != NULL && zv != NULL);
if (begv->charpos != BEGV || zv->charpos != ZV) if (begv->charpos != BEGV || zv->charpos != ZV)
current_buffer->clip_changed = 1; current_buffer->clip_changed = 1;
SET_BUF_BEGV_BOTH (current_buffer, begv->charpos, begv->bytepos); SET_BUF_BEGV_BOTH (current_buffer, begv->charpos, begv->bytepos);
SET_BUF_ZV_BOTH (current_buffer, zv->charpos, zv->bytepos); SET_BUF_ZV_BOTH (current_buffer, zv->charpos, zv->bytepos);
/* If the only remaining bounds in narrowing_locks for /* If the only remaining bounds in labeled_restrictions for
current_buffer are the bounds that were set by the user, no current_buffer are the bounds that were set by the user, no
locked narrowing is in effect in current_buffer anymore: labeled restriction is in effect in current_buffer anymore:
remove it from the narrowing_locks alist. */ remove it from the labeled_restrictions alist. */
if (EQ (tag, Qoutermost_narrowing)) if (EQ (label, Qoutermost_restriction))
narrowing_lock_pop (buf); labeled_restrictions_pop (buf);
} }
/* Changing the buffer bounds invalidates any recorded current column. */ /* Changing the buffer bounds invalidates any recorded current column. */
invalidate_current_column (); invalidate_current_column ();
@ -2905,13 +2922,15 @@ argument. To gain access to other portions of the buffer, use
args_out_of_range (start, end); args_out_of_range (start, end);
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
if (! NILP (narrowing_lock_peek_tag (buf))) if (! NILP (labeled_restrictions_peek_label (buf)))
{ {
struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); /* Limit the start and end positions to those of the innermost
struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); labeled restriction. */
struct Lisp_Marker *begv
= labeled_restrictions_get_bound (buf, true, false);
struct Lisp_Marker *zv
= labeled_restrictions_get_bound (buf, false, false);
eassert (begv != NULL && zv != NULL); eassert (begv != NULL && zv != NULL);
/* Limit the start and end positions to those of the locked
narrowing. */
if (s < begv->charpos) s = begv->charpos; if (s < begv->charpos) s = begv->charpos;
if (s > zv->charpos) s = zv->charpos; if (s > zv->charpos) s = zv->charpos;
if (e < begv->charpos) e = begv->charpos; if (e < begv->charpos) e = begv->charpos;
@ -2919,9 +2938,9 @@ argument. To gain access to other portions of the buffer, use
} }
/* Record the accessible range of the buffer when narrow-to-region /* Record the accessible range of the buffer when narrow-to-region
is called, that is, before applying the narrowing. It is used is called, that is, before applying the narrowing. That
only by internal--lock-narrowing. */ information is used only by internal--label-restriction. */
Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing, Fset (Qoutermost_restriction, list3 (Qoutermost_restriction,
Fpoint_min_marker (), Fpoint_min_marker (),
Fpoint_max_marker ())); Fpoint_max_marker ()));
@ -2940,38 +2959,38 @@ argument. To gain access to other portions of the buffer, use
return Qnil; return Qnil;
} }
DEFUN ("internal--lock-narrowing", Finternal__lock_narrowing, DEFUN ("internal--label-restriction", Finternal__label_restriction,
Sinternal__lock_narrowing, 1, 1, 0, Sinternal__label_restriction, 1, 1, 0,
doc: /* Lock the current narrowing with LABEL. doc: /* Label the current restriction with LABEL.
This is an internal function used by `with-restriction'. */) This is an internal function used by `with-restriction'. */)
(Lisp_Object tag) (Lisp_Object label)
{ {
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
Lisp_Object outermost_narrowing Lisp_Object outermost_restriction
= buffer_local_value (Qoutermost_narrowing, buf); = buffer_local_value (Qoutermost_restriction, buf);
/* If internal--lock-narrowing is ever called without being preceded /* If internal--label-restriction is ever called without being
by narrow-to-region, do nothing. */ preceded by narrow-to-region, do nothing. */
if (NILP (outermost_narrowing)) if (NILP (outermost_restriction))
return Qnil; return Qnil;
if (NILP (narrowing_lock_peek_tag (buf))) if (NILP (labeled_restrictions_peek_label (buf)))
narrowing_lock_push (buf, outermost_narrowing); labeled_restrictions_push (buf, outermost_restriction);
narrowing_lock_push (buf, list3 (tag, labeled_restrictions_push (buf, list3 (label,
Fpoint_min_marker (), Fpoint_min_marker (),
Fpoint_max_marker ())); Fpoint_max_marker ()));
return Qnil; return Qnil;
} }
DEFUN ("internal--unlock-narrowing", Finternal__unlock_narrowing, DEFUN ("internal--unlabel-restriction", Finternal__unlabel_restriction,
Sinternal__unlock_narrowing, 1, 1, 0, Sinternal__unlabel_restriction, 1, 1, 0,
doc: /* Unlock a narrowing locked with LABEL. doc: /* If the current restriction is labeled with LABEL, remove its label.
This is an internal function used by `without-restriction'. */) This is an internal function used by `without-restriction'. */)
(Lisp_Object tag) (Lisp_Object label)
{ {
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
if (EQ (narrowing_lock_peek_tag (buf), tag)) if (EQ (labeled_restrictions_peek_label (buf), label))
narrowing_lock_pop (buf); labeled_restrictions_pop (buf);
return Qnil; return Qnil;
} }
@ -3071,15 +3090,15 @@ save_restriction_restore_1 (Lisp_Object data)
Lisp_Object Lisp_Object
save_restriction_save (void) save_restriction_save (void)
{ {
Lisp_Object restr = save_restriction_save_1 (); Lisp_Object restriction = save_restriction_save_1 ();
Lisp_Object locks = narrowing_locks_save (); Lisp_Object labeled_restrictions = labeled_restrictions_save ();
return Fcons (restr, locks); return Fcons (restriction, labeled_restrictions);
} }
void void
save_restriction_restore (Lisp_Object data) save_restriction_restore (Lisp_Object data)
{ {
narrowing_locks_restore (XCDR (data)); labeled_restrictions_restore (XCDR (data));
save_restriction_restore_1 (XCAR (data)); save_restriction_restore_1 (XCAR (data));
} }
@ -4748,7 +4767,7 @@ syms_of_editfns (void)
DEFSYM (Qwall, "wall"); DEFSYM (Qwall, "wall");
DEFSYM (Qpropertize, "propertize"); DEFSYM (Qpropertize, "propertize");
staticpro (&narrowing_locks); staticpro (&labeled_restrictions);
DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
doc: /* Non-nil means text motion commands don't notice fields. */); doc: /* Non-nil means text motion commands don't notice fields. */);
@ -4809,12 +4828,12 @@ This variable is experimental; email 32252@debbugs.gnu.org if you need
it to be non-nil. */); it to be non-nil. */);
binary_as_unsigned = false; binary_as_unsigned = false;
DEFVAR_LISP ("outermost-narrowing", Voutermost_narrowing, DEFVAR_LISP ("outermost-restriction", Voutermost_restriction,
doc: /* Outermost narrowing bounds, if any. Internal use only. */); doc: /* Outermost narrowing bounds, if any. Internal use only. */);
Voutermost_narrowing = Qnil; Voutermost_restriction = Qnil;
Fmake_variable_buffer_local (Qoutermost_narrowing); Fmake_variable_buffer_local (Qoutermost_restriction);
DEFSYM (Qoutermost_narrowing, "outermost-narrowing"); DEFSYM (Qoutermost_restriction, "outermost-restriction");
Funintern (Qoutermost_narrowing, Qnil); Funintern (Qoutermost_restriction, Qnil);
defsubr (&Spropertize); defsubr (&Spropertize);
defsubr (&Schar_equal); defsubr (&Schar_equal);
@ -4907,8 +4926,8 @@ it to be non-nil. */);
defsubr (&Sdelete_and_extract_region); defsubr (&Sdelete_and_extract_region);
defsubr (&Swiden); defsubr (&Swiden);
defsubr (&Snarrow_to_region); defsubr (&Snarrow_to_region);
defsubr (&Sinternal__lock_narrowing); defsubr (&Sinternal__label_restriction);
defsubr (&Sinternal__unlock_narrowing); defsubr (&Sinternal__unlabel_restriction);
defsubr (&Ssave_restriction); defsubr (&Ssave_restriction);
defsubr (&Stranspose_regions); defsubr (&Stranspose_regions);
} }

View file

@ -318,6 +318,8 @@ static Lisp_Object command_loop (void);
static void echo_now (void); static void echo_now (void);
static ptrdiff_t echo_length (void); static ptrdiff_t echo_length (void);
static void safe_run_hooks_maybe_narrowed (Lisp_Object, struct window *);
/* Incremented whenever a timer is run. */ /* Incremented whenever a timer is run. */
unsigned timers_run; unsigned timers_run;
@ -1909,7 +1911,7 @@ safe_run_hooks (Lisp_Object hook)
unbind_to (count, Qnil); unbind_to (count, Qnil);
} }
void static void
safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w) safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w)
{ {
specpdl_ref count = SPECPDL_INDEX (); specpdl_ref count = SPECPDL_INDEX ();
@ -1919,10 +1921,10 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w)
if (current_buffer->long_line_optimizations_p if (current_buffer->long_line_optimizations_p
&& long_line_optimizations_region_size > 0) && long_line_optimizations_region_size > 0)
{ {
ptrdiff_t begv = get_locked_narrowing_begv (PT); ptrdiff_t begv = get_large_narrowing_begv (PT);
ptrdiff_t zv = get_locked_narrowing_zv (PT); ptrdiff_t zv = get_large_narrowing_zv (PT);
if (begv != BEG || zv != Z) if (begv != BEG || zv != Z)
narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), labeled_narrow_to_region (make_fixnum (begv), make_fixnum (zv),
Qlong_line_optimizations_in_command_hooks); Qlong_line_optimizations_in_command_hooks);
} }

View file

@ -4687,8 +4687,8 @@ extern void save_restriction_restore (Lisp_Object);
extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool);
extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
ptrdiff_t, bool); ptrdiff_t, bool);
extern void narrow_to_region_locked (Lisp_Object, Lisp_Object, Lisp_Object); extern void labeled_narrow_to_region (Lisp_Object, Lisp_Object, Lisp_Object);
extern void reset_outermost_narrowings (void); extern void reset_outermost_restrictions (void);
extern void init_editfns (void); extern void init_editfns (void);
extern void syms_of_editfns (void); extern void syms_of_editfns (void);
@ -4857,7 +4857,6 @@ extern bool detect_input_pending (void);
extern bool detect_input_pending_ignore_squeezables (void); extern bool detect_input_pending_ignore_squeezables (void);
extern bool detect_input_pending_run_timers (bool); extern bool detect_input_pending_run_timers (bool);
extern void safe_run_hooks (Lisp_Object); extern void safe_run_hooks (Lisp_Object);
extern void safe_run_hooks_maybe_narrowed (Lisp_Object, struct window *);
extern void safe_run_hooks_2 (Lisp_Object, Lisp_Object, Lisp_Object); extern void safe_run_hooks_2 (Lisp_Object, Lisp_Object, Lisp_Object);
extern void cmd_error_internal (Lisp_Object, const char *); extern void cmd_error_internal (Lisp_Object, const char *);
extern Lisp_Object command_loop_2 (Lisp_Object); extern Lisp_Object command_loop_2 (Lisp_Object);

View file

@ -3482,7 +3482,7 @@ init_iterator (struct it *it, struct window *w,
/* This is set only when long_line_optimizations_p is non-zero /* This is set only when long_line_optimizations_p is non-zero
for the current buffer. */ for the current buffer. */
it->narrowed_begv = 0; it->medium_narrowing_begv = 0;
/* Compute faces etc. */ /* Compute faces etc. */
reseat (it, it->current.pos, true); reseat (it, it->current.pos, true);
@ -3491,9 +3491,91 @@ init_iterator (struct it *it, struct window *w,
CHECK_IT (it); CHECK_IT (it);
} }
/* Compute a suitable alternate value for BEGV and ZV that may be used /* How Emacs deals with long lines.
temporarily to optimize display if the buffer in window W contains
long lines. */ (1) When a buffer is about to be (re)displayed, 'redisplay_window'
detects, with a heuristic, whether it contains long lines.
This happens in 'redisplay_window' because it is only displaying
buffers with long lines that is problematic. In other words, none
of the optimizations described below is ever used in buffers that
are never displayed.
This happens with a heuristic, which checks whether a buffer
contains long lines, each time its contents have changed "enough"
between two redisplay cycles, because a buffer without long lines
can become a buffer with long lines at any time, for example after
a yank command, or after a replace command, or while the output of
an external process is inserted in a buffer.
When Emacs has detected that a buffer contains long lines, the
buffer-local variable 'long_line_optimizations_p' (in 'struct
buffer') is set, and Emacs does not try to detect whether the
buffer does or does not contain long lines anymore.
What a long line is depends on the variable 'long-line-threshold',
whose default value is 50000 (characters).
(2) When a buffer with long lines is (re)displayed, the amount of
data that the display routines consider is, in a few well-chosen
places, limited with a temporary restriction, whose bounds are
calculated with the functions below.
(2.1) 'get_small_narrowing_begv' is used to create a restriction
which starts a few hundred characters before point. The exact
number of characters depends on the width of the window in which
the buffer is displayed.
There is no corresponding 'get_small_narrowing_zv' function,
because it is not necessary to set the end limit of that
restriction.
This restriction is used in four places, namely:
'back_to_previous_line_start' and 'move_it_vertically_backward'
(with the 'SET_WITH_NARROWED_BEGV' macro), and in
'composition_compute_stop_pos' and 'find_automatic_composition' (in
a conditional statement depending on 'long_line_optimizations_p').
(2.2) 'get_medium_narrowing_begv' is used to create a restriction
which starts a few thousand characters before point. The exact
number of characters depends on the size (width and height) of the
window in which the buffer is displayed. For performance reasons,
the return value of that function is cached in 'struct it', in the
'medium_narrowing_begv' field.
The corresponding function 'get_medium_narrowing_zv' (and
'medium_narrowing_zv' field in 'struct it') is not used to set the
end limit of a the restriction, which is again unnecessary, but to
determine, in 'reseat', whether the iterator has moved far enough
from its original position, and whether the start position of the
restriction must be computed anew.
This restriction is used in a single place:
'get_visually_first_element', with the 'SET_WITH_NARROWED_BEGV'
macro.
(2.3) 'get_large_narrowing_begv' and 'get_large_narrowing_zv' are
used to create a restriction which starts a few hundred thousand
characters before point and ends a few hundred thousand characters
after point. The size of that restriction depends on the variable
'long-line-optimizations-region-size', whose default value is
500000 (characters); it can be adjusted by a few hundred characters
depending on 'long-line-optimizations-bol-search-limit', whose
default value is 128 (characters).
For performance reasons again, the return values of these functions
are stored in the 'large_narrowing_begv' and 'large_narrowing_zv'
fields in 'struct it'.
The restriction defined by these values is used around three
low-level hooks: around 'fontification-functions', in
'handle_fontified_prop', and around 'pre-command-hook' and
'post-command-hook', in 'safe_run_hooks_maybe_narrowed', which is
called in 'command_loop_1'. These restrictions are set around
these hooks with 'labeled_narrow_to_region'; the restrictions are
labeled, and cannot be removed with a call to 'widen', but can be
removed with 'without-restriction' with a :label argument.
*/
static int static int
get_narrowed_width (struct window *w) get_narrowed_width (struct window *w)
@ -3513,28 +3595,28 @@ get_narrowed_len (struct window *w)
} }
ptrdiff_t ptrdiff_t
get_narrowed_begv (struct window *w, ptrdiff_t pos) get_medium_narrowing_begv (struct window *w, ptrdiff_t pos)
{ {
int len = get_narrowed_len (w); int len = get_narrowed_len (w);
return max ((pos / len - 1) * len, BEGV); return max ((pos / len - 1) * len, BEGV);
} }
ptrdiff_t ptrdiff_t
get_narrowed_zv (struct window *w, ptrdiff_t pos) get_medium_narrowing_zv (struct window *w, ptrdiff_t pos)
{ {
int len = get_narrowed_len (w); int len = get_narrowed_len (w);
return min ((pos / len + 1) * len, ZV); return min ((pos / len + 1) * len, ZV);
} }
ptrdiff_t ptrdiff_t
get_closer_narrowed_begv (struct window *w, ptrdiff_t pos) get_small_narrowing_begv (struct window *w, ptrdiff_t pos)
{ {
int len = get_narrowed_width (w); int len = get_narrowed_width (w);
return max ((pos / len - 1) * len, BEGV); return max ((pos / len - 1) * len, BEGV);
} }
ptrdiff_t ptrdiff_t
get_locked_narrowing_begv (ptrdiff_t pos) get_large_narrowing_begv (ptrdiff_t pos)
{ {
if (long_line_optimizations_region_size <= 0) if (long_line_optimizations_region_size <= 0)
return BEGV; return BEGV;
@ -3552,7 +3634,7 @@ get_locked_narrowing_begv (ptrdiff_t pos)
} }
ptrdiff_t ptrdiff_t
get_locked_narrowing_zv (ptrdiff_t pos) get_large_narrowing_zv (ptrdiff_t pos)
{ {
if (long_line_optimizations_region_size <= 0) if (long_line_optimizations_region_size <= 0)
return ZV; return ZV;
@ -3571,7 +3653,7 @@ unwind_narrowed_begv (Lisp_Object point_min)
#define SET_WITH_NARROWED_BEGV(IT,DST,EXPR,BV) \ #define SET_WITH_NARROWED_BEGV(IT,DST,EXPR,BV) \
do { \ do { \
if (IT->narrowed_begv) \ if (IT->medium_narrowing_begv) \
{ \ { \
specpdl_ref count = SPECPDL_INDEX (); \ specpdl_ref count = SPECPDL_INDEX (); \
record_unwind_protect (unwind_narrowed_begv, Fpoint_min ()); \ record_unwind_protect (unwind_narrowed_begv, Fpoint_min ()); \
@ -4396,16 +4478,16 @@ handle_fontified_prop (struct it *it)
if (current_buffer->long_line_optimizations_p if (current_buffer->long_line_optimizations_p
&& long_line_optimizations_region_size > 0) && long_line_optimizations_region_size > 0)
{ {
ptrdiff_t begv = it->locked_narrowing_begv; ptrdiff_t begv = it->large_narrowing_begv;
ptrdiff_t zv = it->locked_narrowing_zv; ptrdiff_t zv = it->large_narrowing_zv;
ptrdiff_t charpos = IT_CHARPOS (*it); ptrdiff_t charpos = IT_CHARPOS (*it);
if (charpos < begv || charpos > zv) if (charpos < begv || charpos > zv)
{ {
begv = get_locked_narrowing_begv (charpos); begv = get_large_narrowing_begv (charpos);
zv = get_locked_narrowing_zv (charpos); zv = get_large_narrowing_zv (charpos);
} }
if (begv != BEG || zv != Z) if (begv != BEG || zv != Z)
narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), labeled_narrow_to_region (make_fixnum (begv), make_fixnum (zv),
Qlong_line_optimizations_in_fontification_functions); Qlong_line_optimizations_in_fontification_functions);
} }
@ -7041,7 +7123,7 @@ back_to_previous_line_start (struct it *it)
dec_both (&cp, &bp); dec_both (&cp, &bp);
SET_WITH_NARROWED_BEGV (it, IT_CHARPOS (*it), SET_WITH_NARROWED_BEGV (it, IT_CHARPOS (*it),
find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it)), find_newline_no_quit (cp, bp, -1, &IT_BYTEPOS (*it)),
get_closer_narrowed_begv (it->w, IT_CHARPOS (*it))); get_small_narrowing_begv (it->w, IT_CHARPOS (*it)));
} }
/* Find in the current buffer the first display or overlay string /* Find in the current buffer the first display or overlay string
@ -7345,7 +7427,7 @@ back_to_previous_visible_line_start (struct it *it)
it->continuation_lines_width = 0; it->continuation_lines_width = 0;
eassert (IT_CHARPOS (*it) >= BEGV); eassert (IT_CHARPOS (*it) >= BEGV);
eassert (it->narrowed_begv > 0 /* long-line optimizations: all bets off */ eassert (it->medium_narrowing_begv > 0 /* long-line optimizations: all bets off */
|| IT_CHARPOS (*it) == BEGV || IT_CHARPOS (*it) == BEGV
|| FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n'); || FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n');
CHECK_IT (it); CHECK_IT (it);
@ -7463,24 +7545,29 @@ reseat (struct it *it, struct text_pos pos, bool force_p)
if (current_buffer->long_line_optimizations_p) if (current_buffer->long_line_optimizations_p)
{ {
if (!it->narrowed_begv) if (!it->medium_narrowing_begv)
{ {
it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); it->medium_narrowing_begv
it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); = get_medium_narrowing_begv (it->w, window_point (it->w));
it->locked_narrowing_begv it->medium_narrowing_zv
= get_locked_narrowing_begv (window_point (it->w)); = get_medium_narrowing_zv (it->w, window_point (it->w));
it->locked_narrowing_zv it->large_narrowing_begv
= get_locked_narrowing_zv (window_point (it->w)); = get_large_narrowing_begv (window_point (it->w));
it->large_narrowing_zv
= get_large_narrowing_zv (window_point (it->w));
} }
else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) else if ((pos.charpos < it->medium_narrowing_begv
|| pos.charpos > it->medium_narrowing_zv)
&& (!redisplaying_p || it->line_wrap == TRUNCATE)) && (!redisplaying_p || it->line_wrap == TRUNCATE))
{ {
it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); it->medium_narrowing_begv
it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); = get_medium_narrowing_begv (it->w, pos.charpos);
it->locked_narrowing_begv it->medium_narrowing_zv
= get_locked_narrowing_begv (window_point (it->w)); = get_medium_narrowing_zv (it->w, pos.charpos);
it->locked_narrowing_zv it->large_narrowing_begv
= get_locked_narrowing_zv (window_point (it->w)); = get_large_narrowing_begv (window_point (it->w));
it->large_narrowing_zv
= get_large_narrowing_zv (window_point (it->w));
} }
} }
@ -8789,7 +8876,7 @@ get_visually_first_element (struct it *it)
SET_WITH_NARROWED_BEGV (it, bob, SET_WITH_NARROWED_BEGV (it, bob,
string_p ? 0 : string_p ? 0 :
IT_CHARPOS (*it) < BEGV ? obegv : BEGV, IT_CHARPOS (*it) < BEGV ? obegv : BEGV,
it->narrowed_begv); it->medium_narrowing_begv);
if (STRINGP (it->string)) if (STRINGP (it->string))
{ {
@ -8833,7 +8920,7 @@ get_visually_first_element (struct it *it)
find_newline_no_quit (IT_CHARPOS (*it), find_newline_no_quit (IT_CHARPOS (*it),
IT_BYTEPOS (*it), -1, IT_BYTEPOS (*it), -1,
&it->bidi_it.bytepos), &it->bidi_it.bytepos),
it->narrowed_begv); it->medium_narrowing_begv);
bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, true); bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it, true);
do do
{ {
@ -10722,7 +10809,7 @@ move_it_vertically_backward (struct it *it, int dy)
dec_both (&cp, &bp); dec_both (&cp, &bp);
SET_WITH_NARROWED_BEGV (it, cp, SET_WITH_NARROWED_BEGV (it, cp,
find_newline_no_quit (cp, bp, -1, NULL), find_newline_no_quit (cp, bp, -1, NULL),
get_closer_narrowed_begv (it->w, IT_CHARPOS (*it))); get_small_narrowing_begv (it->w, IT_CHARPOS (*it)));
move_it_to (it, cp, -1, -1, -1, MOVE_TO_POS); move_it_to (it, cp, -1, -1, -1, MOVE_TO_POS);
} }
bidi_unshelve_cache (it3data, true); bidi_unshelve_cache (it3data, true);
@ -16394,7 +16481,7 @@ redisplay_internal (void)
FOR_EACH_FRAME (tail, frame) FOR_EACH_FRAME (tail, frame)
XFRAME (frame)->already_hscrolled_p = false; XFRAME (frame)->already_hscrolled_p = false;
reset_outermost_narrowings (); reset_outermost_restrictions ();
retry: retry:
/* Remember the currently selected window. */ /* Remember the currently selected window. */