aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGregory Heytings2022-11-27 22:19:41 +0100
committerGregory Heytings2022-11-27 22:19:41 +0100
commit849223fba1ef899f90a6edff05bce24b90fbb043 (patch)
treeb5ab707f2da7d13ba2cb10c8af441547152c83ef /src
parent89a10ffcc49c5832619649b7876cc339fa9d0dcf (diff)
parent18fa159fa91b515f2281b83648961fdc5e21aca7 (diff)
downloademacs-849223fba1ef899f90a6edff05bce24b90fbb043.tar.gz
emacs-849223fba1ef899f90a6edff05bce24b90fbb043.zip
Merge branch 'feature/improved-locked-narrowing'
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c37
-rw-r--r--src/dispextern.h10
-rw-r--r--src/editfns.c373
-rw-r--r--src/keyboard.c16
-rw-r--r--src/lisp.h3
-rw-r--r--src/xdisp.c59
6 files changed, 407 insertions, 91 deletions
diff --git a/src/buffer.c b/src/buffer.c
index ac7f4f8e9d4..71be7ed9e13 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5898,7 +5898,42 @@ this threshold.
5898If nil, these display shortcuts will always remain disabled. 5898If nil, these display shortcuts will always remain disabled.
5899 5899
5900There is no reason to change that value except for debugging purposes. */); 5900There is no reason to change that value except for debugging purposes. */);
5901 XSETFASTINT (Vlong_line_threshold, 10000); 5901 XSETFASTINT (Vlong_line_threshold, 50000);
5902
5903 DEFVAR_INT ("long-line-locked-narrowing-region-size",
5904 long_line_locked_narrowing_region_size,
5905 doc: /* Region size for locked narrowing in buffers with long lines.
5906
5907This variable has effect only in buffers which contain one or more
5908lines whose length is above `long-line-threshold', which see. For
5909performance reasons, in such buffers, low-level hooks such as
5910`fontification-functions' or `post-command-hook' are executed on a
5911narrowed buffer, with a narrowing locked with `narrowing-lock'. This
5912variable specifies the size of the narrowed region around point.
5913
5914To disable that narrowing, set this variable to 0.
5915
5916See also `long-line-locked-narrowing-bol-search-limit'.
5917
5918There is no reason to change that value except for debugging purposes. */);
5919 long_line_locked_narrowing_region_size = 500000;
5920
5921 DEFVAR_INT ("long-line-locked-narrowing-bol-search-limit",
5922 long_line_locked_narrowing_bol_search_limit,
5923 doc: /* Limit for beginning of line search in buffers with long lines.
5924
5925This variable has effect only in buffers which contain one or more
5926lines whose length is above `long-line-threshold', which see. For
5927performance reasons, in such buffers, low-level hooks such as
5928`fontification-functions' or `post-command-hook' are executed on a
5929narrowed buffer, with a narrowing locked with `narrowing-lock'. The
5930variable `long-line-locked-narrowing-region-size' specifies the size
5931of the narrowed region around point. This variable, which should be a
5932small integer, specifies the number of characters by which that region
5933can be extended backwards to make it start at the beginning of a line.
5934
5935There is no reason to change that value except for debugging purposes. */);
5936 long_line_locked_narrowing_bol_search_limit = 128;
5902 5937
5903 DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, 5938 DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold,
5904 doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. 5939 doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts.
diff --git a/src/dispextern.h b/src/dispextern.h
index 2afbdeabaab..df6134e68f0 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2342,6 +2342,14 @@ struct it
2342 optimize display. */ 2342 optimize display. */
2343 ptrdiff_t narrowed_zv; 2343 ptrdiff_t narrowed_zv;
2344 2344
2345 /* Begin position of the buffer for the locked narrowing around
2346 low-level hooks. */
2347 ptrdiff_t locked_narrowing_begv;
2348
2349 /* End position of the buffer for the locked narrowing around
2350 low-level hooks. */
2351 ptrdiff_t locked_narrowing_zv;
2352
2345 /* C string to iterate over. Non-null means get characters from 2353 /* C string to iterate over. Non-null means get characters from
2346 this string, otherwise characters are read from current_buffer 2354 this string, otherwise characters are read from current_buffer
2347 or it->string. */ 2355 or it->string. */
@@ -3405,6 +3413,8 @@ void init_iterator (struct it *, struct window *, ptrdiff_t,
3405ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t); 3413ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t);
3406ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t); 3414ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t);
3407ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t); 3415ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t);
3416ptrdiff_t get_locked_narrowing_begv (ptrdiff_t);
3417ptrdiff_t get_locked_narrowing_zv (ptrdiff_t);
3408void init_iterator_to_row_start (struct it *, struct window *, 3418void init_iterator_to_row_start (struct it *, struct window *,
3409 struct glyph_row *); 3419 struct glyph_row *);
3410void start_display (struct it *, struct window *, struct text_pos); 3420void start_display (struct it *, struct window *, struct text_pos);
diff --git a/src/editfns.c b/src/editfns.c
index 17dca4708ed..b364f441b53 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -2653,88 +2653,216 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
2653 return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1); 2653 return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);
2654} 2654}
2655 2655
2656DEFUN ("widen", Fwiden, Swiden, 0, 0, "", 2656/* Alist of buffers in which locked narrowing is used. The car of
2657 doc: /* Remove restrictions (narrowing) from current buffer. 2657 each list element is a buffer, the cdr is a list of triplets (tag
2658This allows the buffer's full text to be seen and edited. 2658 begv-marker zv-marker). The last element of that list always uses
2659 the (uninterned) Qoutermost_narrowing tag and records the narrowing
2660 bounds that were set by the user and that are visible on display.
2661 This alist is used internally by narrow-to-region, widen,
2662 narrowing-lock, narrowing-unlock and save-restriction. */
2663static Lisp_Object narrowing_locks;
2664
2665/* Add BUF with its LOCKS in the narrowing_locks alist. */
2666static void
2667narrowing_locks_add (Lisp_Object buf, Lisp_Object locks)
2668{
2669 narrowing_locks = nconc2 (list1 (list2 (buf, locks)), narrowing_locks);
2670}
2659 2671
2660Note that, when the current buffer contains one or more lines whose 2672/* Remove BUF and its locks from the narrowing_locks alist. Do
2661length is above `long-line-threshold', Emacs may decide to leave, for 2673 nothing if BUF is not present in narrowing_locks. */
2662performance reasons, the accessible portion of the buffer unchanged 2674static void
2663after this function is called from low-level hooks, such as 2675narrowing_locks_remove (Lisp_Object buf)
2664`jit-lock-functions' or `post-command-hook'. */) 2676{
2665 (void) 2677 narrowing_locks = Fdelq (Fassoc (buf, narrowing_locks, Qnil),
2678 narrowing_locks);
2679}
2680
2681/* Retrieve one of the BEGV/ZV bounds of a narrowing in BUF from the
2682 narrowing_locks alist, as a pointer to a struct Lisp_Marker, or
2683 NULL if BUF is not in narrowing_locks or is a killed buffer. When
2684 OUTERMOST is true, the bounds that were set by the user and that
2685 are visible on display are returned. Otherwise the innermost
2686 locked narrowing bounds are returned. */
2687static struct Lisp_Marker *
2688narrowing_lock_get_bound (Lisp_Object buf, bool begv, bool outermost)
2689{
2690 if (NILP (Fbuffer_live_p (buf)))
2691 return NULL;
2692 Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
2693 if (NILP (buffer_locks))
2694 return NULL;
2695 buffer_locks = XCAR (XCDR (buffer_locks));
2696 Lisp_Object bounds
2697 = outermost
2698 ? XCDR (assq_no_quit (Qoutermost_narrowing, buffer_locks))
2699 : XCDR (XCAR (buffer_locks));
2700 eassert (! NILP (bounds));
2701 Lisp_Object marker = begv ? XCAR (bounds) : XCAR (XCDR (bounds));
2702 eassert (EQ (Fmarker_buffer (marker), buf));
2703 return XMARKER (marker);
2704}
2705
2706/* Retrieve the tag of the innermost narrowing in BUF. Return nil if
2707 BUF is not in narrowing_locks or is a killed buffer. */
2708static Lisp_Object
2709narrowing_lock_peek_tag (Lisp_Object buf)
2666{ 2710{
2667 if (! NILP (Vrestrictions_locked)) 2711 if (NILP (Fbuffer_live_p (buf)))
2668 return Qnil; 2712 return Qnil;
2669 if (BEG != BEGV || Z != ZV) 2713 Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
2670 current_buffer->clip_changed = 1; 2714 if (NILP (buffer_locks))
2671 BEGV = BEG; 2715 return Qnil;
2672 BEGV_BYTE = BEG_BYTE; 2716 Lisp_Object tag = XCAR (XCAR (XCAR (XCDR (buffer_locks))));
2673 SET_BUF_ZV_BOTH (current_buffer, Z, Z_BYTE); 2717 eassert (! NILP (tag));
2674 /* Changing the buffer bounds invalidates any recorded current column. */ 2718 return tag;
2675 invalidate_current_column ();
2676 return Qnil;
2677} 2719}
2678 2720
2721/* Add a LOCK for BUF in the narrowing_locks alist. */
2679static void 2722static void
2680unwind_locked_begv (Lisp_Object point_min) 2723narrowing_lock_push (Lisp_Object buf, Lisp_Object lock)
2681{ 2724{
2682 SET_BUF_BEGV (current_buffer, XFIXNUM (point_min)); 2725 Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
2726 if (NILP (buffer_locks))
2727 narrowing_locks_add (buf, list1 (lock));
2728 else
2729 XSETCDR (buffer_locks, list1 (nconc2 (list1 (lock),
2730 XCAR (XCDR (buffer_locks)))));
2683} 2731}
2684 2732
2733/* Remove the innermost lock in BUF from the narrowing_locks alist.
2734 Do nothing if BUF is not present in narrowing_locks. */
2685static void 2735static void
2686unwind_locked_zv (Lisp_Object point_max) 2736narrowing_lock_pop (Lisp_Object buf)
2687{ 2737{
2688 SET_BUF_ZV (current_buffer, XFIXNUM (point_max)); 2738 Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
2739 if (NILP (buffer_locks))
2740 return;
2741 if (EQ (narrowing_lock_peek_tag (buf), Qoutermost_narrowing))
2742 narrowing_locks_remove (buf);
2743 else
2744 XSETCDR (buffer_locks, list1 (XCDR (XCAR (XCDR (buffer_locks)))));
2689} 2745}
2690 2746
2691/* Internal function for Fnarrow_to_region, meant to be used with a 2747static void
2692 third argument 'true', in which case it should be followed by "specbind 2748unwind_reset_outermost_narrowing (Lisp_Object buf)
2693 (Qrestrictions_locked, Qt)". */
2694Lisp_Object
2695narrow_to_region_internal (Lisp_Object start, Lisp_Object end, bool lock)
2696{ 2749{
2697 EMACS_INT s = fix_position (start), e = fix_position (end); 2750 struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false);
2698 2751 struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false);
2699 if (e < s) 2752 if (begv != NULL && zv != NULL)
2700 { 2753 {
2701 EMACS_INT tem = s; s = e; e = tem; 2754 SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos);
2755 SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos);
2702 } 2756 }
2757 else
2758 narrowing_locks_remove (buf);
2759}
2703 2760
2704 if (lock) 2761/* Restore the narrowing bounds that were set by the user, and restore
2762 the bounds of the locked narrowing upon return.
2763 In particular, this function is called when redisplay starts, so
2764 that if a Lisp function executed during redisplay calls (redisplay)
2765 while a locked narrowing is in effect, the locked narrowing will
2766 not be visible on display. */
2767void
2768reset_outermost_narrowings (void)
2769{
2770 Lisp_Object val, buf;
2771 for (val = narrowing_locks; CONSP (val); val = XCDR (val))
2705 { 2772 {
2706 if (!(BEGV <= s && s <= e && e <= ZV)) 2773 buf = XCAR (XCAR (val));
2707 args_out_of_range (start, end); 2774 eassert (BUFFERP (buf));
2775 struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, true);
2776 struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, true);
2777 if (begv != NULL && zv != NULL)
2778 {
2779 SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos);
2780 SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos);
2781 record_unwind_protect (unwind_reset_outermost_narrowing, buf);
2782 }
2783 else
2784 narrowing_locks_remove (buf);
2785 }
2786}
2708 2787
2709 if (BEGV != s || ZV != e) 2788/* Helper functions to save and restore the narrowing locks of the
2710 current_buffer->clip_changed = 1; 2789 current buffer in Fsave_restriction. */
2790static Lisp_Object
2791narrowing_locks_save (void)
2792{
2793 Lisp_Object buf = Fcurrent_buffer ();
2794 Lisp_Object locks = assq_no_quit (buf, narrowing_locks);
2795 if (NILP (locks))
2796 return Qnil;
2797 locks = XCAR (XCDR (locks));
2798 return Fcons (buf, Fcopy_sequence (locks));
2799}
2711 2800
2712 record_unwind_protect (restore_point_unwind, Fpoint_marker ()); 2801static void
2713 record_unwind_protect (unwind_locked_begv, Fpoint_min ()); 2802narrowing_locks_restore (Lisp_Object buf_and_saved_locks)
2714 record_unwind_protect (unwind_locked_zv, Fpoint_max ()); 2803{
2804 if (NILP (buf_and_saved_locks))
2805 return;
2806 Lisp_Object buf = XCAR (buf_and_saved_locks);
2807 Lisp_Object saved_locks = XCDR (buf_and_saved_locks);
2808 narrowing_locks_remove (buf);
2809 narrowing_locks_add (buf, saved_locks);
2810}
2715 2811
2716 SET_BUF_BEGV (current_buffer, s); 2812static void
2717 SET_BUF_ZV (current_buffer, e); 2813unwind_narrow_to_region_locked (Lisp_Object tag)
2814{
2815 Fnarrowing_unlock (tag);
2816 Fwiden ();
2817}
2818
2819/* Narrow current_buffer to BEGV-ZV with a narrowing locked with TAG. */
2820void
2821narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag)
2822{
2823 Fnarrow_to_region (begv, zv);
2824 Fnarrowing_lock (tag);
2825 record_unwind_protect (restore_point_unwind, Fpoint_marker ());
2826 record_unwind_protect (unwind_narrow_to_region_locked, tag);
2827}
2828
2829DEFUN ("widen", Fwiden, Swiden, 0, 0, "",
2830 doc: /* Remove restrictions (narrowing) from current buffer.
2831
2832This allows the buffer's full text to be seen and edited, unless
2833restrictions have been locked with `narrowing-lock', which see, in
2834which case the narrowing that was current when `narrowing-lock' was
2835called is restored. */)
2836 (void)
2837{
2838 Fset (Qoutermost_narrowing, Qnil);
2839 Lisp_Object buf = Fcurrent_buffer ();
2840 Lisp_Object tag = narrowing_lock_peek_tag (buf);
2841
2842 if (NILP (tag))
2843 {
2844 if (BEG != BEGV || Z != ZV)
2845 current_buffer->clip_changed = 1;
2846 BEGV = BEG;
2847 BEGV_BYTE = BEG_BYTE;
2848 SET_BUF_ZV_BOTH (current_buffer, Z, Z_BYTE);
2718 } 2849 }
2719 else 2850 else
2720 { 2851 {
2721 if (! NILP (Vrestrictions_locked)) 2852 struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false);
2722 return Qnil; 2853 struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false);
2723 2854 eassert (begv != NULL && zv != NULL);
2724 if (!(BEG <= s && s <= e && e <= Z)) 2855 if (begv->charpos != BEGV || zv->charpos != ZV)
2725 args_out_of_range (start, end);
2726
2727 if (BEGV != s || ZV != e)
2728 current_buffer->clip_changed = 1; 2856 current_buffer->clip_changed = 1;
2729 2857 SET_BUF_BEGV_BOTH (current_buffer, begv->charpos, begv->bytepos);
2730 SET_BUF_BEGV (current_buffer, s); 2858 SET_BUF_ZV_BOTH (current_buffer, zv->charpos, zv->bytepos);
2731 SET_BUF_ZV (current_buffer, e); 2859 /* If the only remaining bounds in narrowing_locks for
2860 current_buffer are the bounds that were set by the user, no
2861 locked narrowing is in effect in current_buffer anymore:
2862 remove it from the narrowing_locks alist. */
2863 if (EQ (tag, Qoutermost_narrowing))
2864 narrowing_lock_pop (buf);
2732 } 2865 }
2733
2734 if (PT < s)
2735 SET_PT (s);
2736 if (e < PT)
2737 SET_PT (e);
2738 /* Changing the buffer bounds invalidates any recorded current column. */ 2866 /* Changing the buffer bounds invalidates any recorded current column. */
2739 invalidate_current_column (); 2867 invalidate_current_column ();
2740 return Qnil; 2868 return Qnil;
@@ -2751,14 +2879,110 @@ When calling from Lisp, pass two arguments START and END:
2751positions (integers or markers) bounding the text that should 2879positions (integers or markers) bounding the text that should
2752remain visible. 2880remain visible.
2753 2881
2754Note that, when the current buffer contains one or more lines whose 2882When restrictions have been locked with `narrowing-lock', which see,
2755length is above `long-line-threshold', Emacs may decide to leave, for 2883`narrow-to-region' can be used only within the limits of the
2756performance reasons, the accessible portion of the buffer unchanged 2884restrictions that were current when `narrowing-lock' was called. If
2757after this function is called from low-level hooks, such as 2885the START or END arguments are outside these limits, the corresponding
2758`jit-lock-functions' or `post-command-hook'. */) 2886limit of the locked restriction is used instead of the argument. */)
2759 (Lisp_Object start, Lisp_Object end) 2887 (Lisp_Object start, Lisp_Object end)
2760{ 2888{
2761 return narrow_to_region_internal (start, end, false); 2889 EMACS_INT s = fix_position (start), e = fix_position (end);
2890
2891 if (e < s)
2892 {
2893 EMACS_INT tem = s; s = e; e = tem;
2894 }
2895
2896 if (!(BEG <= s && s <= e && e <= Z))
2897 args_out_of_range (start, end);
2898
2899 Lisp_Object buf = Fcurrent_buffer ();
2900 if (! NILP (narrowing_lock_peek_tag (buf)))
2901 {
2902 struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false);
2903 struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false);
2904 eassert (begv != NULL && zv != NULL);
2905 /* Limit the start and end positions to those of the locked
2906 narrowing. */
2907 if (s < begv->charpos) s = begv->charpos;
2908 if (s > zv->charpos) s = zv->charpos;
2909 if (e < begv->charpos) e = begv->charpos;
2910 if (e > zv->charpos) e = zv->charpos;
2911 }
2912
2913 /* Record the accessible range of the buffer when narrow-to-region
2914 is called, that is, before applying the narrowing. It is used
2915 only by narrowing-lock. */
2916 Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing,
2917 Fpoint_min_marker (),
2918 Fpoint_max_marker ()));
2919
2920 if (BEGV != s || ZV != e)
2921 current_buffer->clip_changed = 1;
2922
2923 SET_BUF_BEGV (current_buffer, s);
2924 SET_BUF_ZV (current_buffer, e);
2925
2926 if (PT < s)
2927 SET_PT (s);
2928 if (e < PT)
2929 SET_PT (e);
2930 /* Changing the buffer bounds invalidates any recorded current column. */
2931 invalidate_current_column ();
2932 return Qnil;
2933}
2934
2935DEFUN ("narrowing-lock", Fnarrowing_lock, Snarrowing_lock, 1, 1, 0,
2936 doc: /* Lock the current narrowing with TAG.
2937
2938When restrictions are locked, `narrow-to-region' and `widen' can be
2939used only within the limits of the restrictions that were current when
2940`narrowing-lock' was called, unless the lock is removed by calling
2941`narrowing-unlock' with TAG.
2942
2943Locking restrictions should be used sparingly, after carefully
2944considering the potential adverse effects on the code that will be
2945executed within locked restrictions. It is typically meant to be used
2946around portions of code that would become too slow, and make Emacs
2947unresponsive, if they were executed in a large buffer. For example,
2948restrictions are locked by Emacs around low-level hooks such as
2949`fontification-functions' or `post-command-hook'.
2950
2951Locked restrictions are never visible on display, and can therefore
2952not be used as a stronger variant of normal restrictions. */)
2953 (Lisp_Object tag)
2954{
2955 Lisp_Object buf = Fcurrent_buffer ();
2956 Lisp_Object outermost_narrowing
2957 = buffer_local_value (Qoutermost_narrowing, buf);
2958 /* If narrowing-lock is called without being preceded by
2959 narrow-to-region, do nothing. */
2960 if (NILP (outermost_narrowing))
2961 return Qnil;
2962 if (NILP (narrowing_lock_peek_tag (buf)))
2963 narrowing_lock_push (buf, outermost_narrowing);
2964 narrowing_lock_push (buf, list3 (tag,
2965 Fpoint_min_marker (),
2966 Fpoint_max_marker ()));
2967 return Qnil;
2968}
2969
2970DEFUN ("narrowing-unlock", Fnarrowing_unlock, Snarrowing_unlock, 1, 1, 0,
2971 doc: /* Unlock a narrowing locked with (narrowing-lock TAG).
2972
2973Unlocking restrictions locked with `narrowing-lock' should be used
2974sparingly, after carefully considering the reasons why restrictions
2975were locked. Restrictions are typically locked around portions of
2976code that would become too slow, and make Emacs unresponsive, if they
2977were executed in a large buffer. For example, restrictions are locked
2978by Emacs around low-level hooks such as `fontification-functions' or
2979`post-command-hook'. */)
2980 (Lisp_Object tag)
2981{
2982 Lisp_Object buf = Fcurrent_buffer ();
2983 if (EQ (narrowing_lock_peek_tag (buf), tag))
2984 narrowing_lock_pop (buf);
2985 return Qnil;
2762} 2986}
2763 2987
2764Lisp_Object 2988Lisp_Object
@@ -2858,11 +3082,12 @@ DEFUN ("save-restriction", Fsave_restriction, Ssave_restriction, 0, UNEVALLED, 0
2858 doc: /* Execute BODY, saving and restoring current buffer's restrictions. 3082 doc: /* Execute BODY, saving and restoring current buffer's restrictions.
2859The buffer's restrictions make parts of the beginning and end invisible. 3083The buffer's restrictions make parts of the beginning and end invisible.
2860\(They are set up with `narrow-to-region' and eliminated with `widen'.) 3084\(They are set up with `narrow-to-region' and eliminated with `widen'.)
2861This special form, `save-restriction', saves the current buffer's restrictions 3085This special form, `save-restriction', saves the current buffer's
2862when it is entered, and restores them when it is exited. 3086restrictions, as well as their locks if they have been locked with
3087`narrowing-lock', when it is entered, and restores them when it is exited.
2863So any `narrow-to-region' within BODY lasts only until the end of the form. 3088So any `narrow-to-region' within BODY lasts only until the end of the form.
2864The old restrictions settings are restored 3089The old restrictions settings are restored even in case of abnormal exit
2865even in case of abnormal exit (throw or error). 3090\(throw or error).
2866 3091
2867The value returned is the value of the last form in BODY. 3092The value returned is the value of the last form in BODY.
2868 3093
@@ -2877,6 +3102,7 @@ usage: (save-restriction &rest BODY) */)
2877 specpdl_ref count = SPECPDL_INDEX (); 3102 specpdl_ref count = SPECPDL_INDEX ();
2878 3103
2879 record_unwind_protect (save_restriction_restore, save_restriction_save ()); 3104 record_unwind_protect (save_restriction_restore, save_restriction_save ());
3105 record_unwind_protect (narrowing_locks_restore, narrowing_locks_save ());
2880 val = Fprogn (body); 3106 val = Fprogn (body);
2881 return unbind_to (count, val); 3107 return unbind_to (count, val);
2882} 3108}
@@ -4518,6 +4744,8 @@ syms_of_editfns (void)
4518 DEFSYM (Qwall, "wall"); 4744 DEFSYM (Qwall, "wall");
4519 DEFSYM (Qpropertize, "propertize"); 4745 DEFSYM (Qpropertize, "propertize");
4520 4746
4747 staticpro (&narrowing_locks);
4748
4521 DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, 4749 DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
4522 doc: /* Non-nil means text motion commands don't notice fields. */); 4750 doc: /* Non-nil means text motion commands don't notice fields. */);
4523 Vinhibit_field_text_motion = Qnil; 4751 Vinhibit_field_text_motion = Qnil;
@@ -4577,11 +4805,12 @@ This variable is experimental; email 32252@debbugs.gnu.org if you need
4577it to be non-nil. */); 4805it to be non-nil. */);
4578 binary_as_unsigned = false; 4806 binary_as_unsigned = false;
4579 4807
4580 DEFSYM (Qrestrictions_locked, "restrictions-locked"); 4808 DEFVAR_LISP ("outermost-narrowing", Voutermost_narrowing,
4581 DEFVAR_LISP ("restrictions-locked", Vrestrictions_locked, 4809 doc: /* Outermost narrowing bounds, if any. Internal use only. */);
4582 doc: /* If non-nil, restrictions are currently locked. */); 4810 Voutermost_narrowing = Qnil;
4583 Vrestrictions_locked = Qnil; 4811 Fmake_variable_buffer_local (Qoutermost_narrowing);
4584 Funintern (Qrestrictions_locked, Qnil); 4812 DEFSYM (Qoutermost_narrowing, "outermost-narrowing");
4813 Funintern (Qoutermost_narrowing, Qnil);
4585 4814
4586 defsubr (&Spropertize); 4815 defsubr (&Spropertize);
4587 defsubr (&Schar_equal); 4816 defsubr (&Schar_equal);
@@ -4674,6 +4903,8 @@ it to be non-nil. */);
4674 defsubr (&Sdelete_and_extract_region); 4903 defsubr (&Sdelete_and_extract_region);
4675 defsubr (&Swiden); 4904 defsubr (&Swiden);
4676 defsubr (&Snarrow_to_region); 4905 defsubr (&Snarrow_to_region);
4906 defsubr (&Snarrowing_lock);
4907 defsubr (&Snarrowing_unlock);
4677 defsubr (&Ssave_restriction); 4908 defsubr (&Ssave_restriction);
4678 defsubr (&Stranspose_regions); 4909 defsubr (&Stranspose_regions);
4679} 4910}
diff --git a/src/keyboard.c b/src/keyboard.c
index 811998823cc..b82a5e1a3ef 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1911,9 +1911,9 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w)
1911 specbind (Qinhibit_quit, Qt); 1911 specbind (Qinhibit_quit, Qt);
1912 1912
1913 if (current_buffer->long_line_optimizations_p) 1913 if (current_buffer->long_line_optimizations_p)
1914 narrow_to_region_internal (make_fixnum (get_narrowed_begv (w, PT)), 1914 narrow_to_region_locked (make_fixnum (get_locked_narrowing_begv (PT)),
1915 make_fixnum (get_narrowed_zv (w, PT)), 1915 make_fixnum (get_locked_narrowing_zv (PT)),
1916 true); 1916 hook);
1917 1917
1918 run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), 1918 run_hook_with_args (2, ((Lisp_Object []) {hook, hook}),
1919 safe_run_hook_funcall); 1919 safe_run_hook_funcall);
@@ -12727,8 +12727,9 @@ the error might happen repeatedly and make Emacs nonfunctional.
12727 12727
12728Note that, when the current buffer contains one or more lines whose 12728Note that, when the current buffer contains one or more lines whose
12729length is above `long-line-threshold', these hook functions are called 12729length is above `long-line-threshold', these hook functions are called
12730with the buffer narrowed to a small portion around point, and the 12730with the buffer narrowed to a small portion around point (whose size
12731narrowing is locked (see `narrow-to-region'), so that these hook 12731is specified by `long-line-locked-narrowing-region-size'), and the
12732narrowing is locked (see `narrowing-lock'), so that these hook
12732functions cannot use `widen' to gain access to other portions of 12733functions cannot use `widen' to gain access to other portions of
12733buffer text. 12734buffer text.
12734 12735
@@ -12748,8 +12749,9 @@ avoid making Emacs unresponsive while the user types.
12748 12749
12749Note that, when the current buffer contains one or more lines whose 12750Note that, when the current buffer contains one or more lines whose
12750length is above `long-line-threshold', these hook functions are called 12751length is above `long-line-threshold', these hook functions are called
12751with the buffer narrowed to a small portion around point, and the 12752with the buffer narrowed to a small portion around point (whose size
12752narrowing is locked (see `narrow-to-region'), so that these hook 12753is specified by `long-line-locked-narrowing-region-size'), and the
12754narrowing is locked (see `narrowing-lock'), so that these hook
12753functions cannot use `widen' to gain access to other portions of 12755functions cannot use `widen' to gain access to other portions of
12754buffer text. 12756buffer text.
12755 12757
diff --git a/src/lisp.h b/src/lisp.h
index 6a24a538172..0f70f60d75c 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4687,7 +4687,8 @@ extern void save_restriction_restore (Lisp_Object);
4687extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); 4687extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool);
4688extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, 4688extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
4689 ptrdiff_t, bool); 4689 ptrdiff_t, bool);
4690extern Lisp_Object narrow_to_region_internal (Lisp_Object, Lisp_Object, bool); 4690extern void narrow_to_region_locked (Lisp_Object, Lisp_Object, Lisp_Object);
4691extern void reset_outermost_narrowings (void);
4691extern void init_editfns (void); 4692extern void init_editfns (void);
4692extern void syms_of_editfns (void); 4693extern void syms_of_editfns (void);
4693 4694
diff --git a/src/xdisp.c b/src/xdisp.c
index 5dcf21dc4ce..0002c3d611c 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -3533,6 +3533,33 @@ get_closer_narrowed_begv (struct window *w, ptrdiff_t pos)
3533 return max ((pos / len - 1) * len, BEGV); 3533 return max ((pos / len - 1) * len, BEGV);
3534} 3534}
3535 3535
3536ptrdiff_t
3537get_locked_narrowing_begv (ptrdiff_t pos)
3538{
3539 if (long_line_locked_narrowing_region_size == 0)
3540 return BEGV;
3541 int len = long_line_locked_narrowing_region_size / 2;
3542 int begv = max (pos - len, BEGV);
3543 int limit = long_line_locked_narrowing_bol_search_limit;
3544 while (limit)
3545 {
3546 if (begv == BEGV || FETCH_BYTE (CHAR_TO_BYTE (begv) - 1) == '\n')
3547 return begv;
3548 begv--;
3549 limit--;
3550 }
3551 return begv;
3552}
3553
3554ptrdiff_t
3555get_locked_narrowing_zv (ptrdiff_t pos)
3556{
3557 if (long_line_locked_narrowing_region_size == 0)
3558 return ZV;
3559 int len = long_line_locked_narrowing_region_size / 2;
3560 return min (pos + len, ZV);
3561}
3562
3536static void 3563static void
3537unwind_narrowed_begv (Lisp_Object point_min) 3564unwind_narrowed_begv (Lisp_Object point_min)
3538{ 3565{
@@ -4368,16 +4395,16 @@ handle_fontified_prop (struct it *it)
4368 4395
4369 if (current_buffer->long_line_optimizations_p) 4396 if (current_buffer->long_line_optimizations_p)
4370 { 4397 {
4371 ptrdiff_t begv = it->narrowed_begv; 4398 ptrdiff_t begv = it->locked_narrowing_begv;
4372 ptrdiff_t zv = it->narrowed_zv; 4399 ptrdiff_t zv = it->locked_narrowing_zv;
4373 ptrdiff_t charpos = IT_CHARPOS (*it); 4400 ptrdiff_t charpos = IT_CHARPOS (*it);
4374 if (charpos < begv || charpos > zv) 4401 if (charpos < begv || charpos > zv)
4375 { 4402 {
4376 begv = get_narrowed_begv (it->w, charpos); 4403 begv = get_locked_narrowing_begv (charpos);
4377 zv = get_narrowed_zv (it->w, charpos); 4404 zv = get_locked_narrowing_zv (charpos);
4378 } 4405 }
4379 narrow_to_region_internal (make_fixnum (begv), make_fixnum (zv), true); 4406 narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv),
4380 specbind (Qrestrictions_locked, Qt); 4407 Qfontification_functions);
4381 } 4408 }
4382 4409
4383 /* Don't allow Lisp that runs from 'fontification-functions' 4410 /* Don't allow Lisp that runs from 'fontification-functions'
@@ -7435,12 +7462,20 @@ reseat (struct it *it, struct text_pos pos, bool force_p)
7435 { 7462 {
7436 it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); 7463 it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w));
7437 it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); 7464 it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w));
7465 it->locked_narrowing_begv
7466 = get_locked_narrowing_begv (window_point (it->w));
7467 it->locked_narrowing_zv
7468 = get_locked_narrowing_zv (window_point (it->w));
7438 } 7469 }
7439 else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) 7470 else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv)
7440 && (!redisplaying_p || it->line_wrap == TRUNCATE)) 7471 && (!redisplaying_p || it->line_wrap == TRUNCATE))
7441 { 7472 {
7442 it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); 7473 it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos);
7443 it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); 7474 it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos);
7475 it->locked_narrowing_begv
7476 = get_locked_narrowing_begv (window_point (it->w));
7477 it->locked_narrowing_zv
7478 = get_locked_narrowing_zv (window_point (it->w));
7444 } 7479 }
7445 } 7480 }
7446 7481
@@ -16266,7 +16301,6 @@ do { if (! polling_stopped_here) stop_polling (); \
16266do { if (polling_stopped_here) start_polling (); \ 16301do { if (polling_stopped_here) start_polling (); \
16267 polling_stopped_here = false; } while (false) 16302 polling_stopped_here = false; } while (false)
16268 16303
16269
16270/* Perhaps in the future avoid recentering windows if it 16304/* Perhaps in the future avoid recentering windows if it
16271 is not necessary; currently that causes some problems. */ 16305 is not necessary; currently that causes some problems. */
16272 16306
@@ -16352,6 +16386,8 @@ redisplay_internal (void)
16352 FOR_EACH_FRAME (tail, frame) 16386 FOR_EACH_FRAME (tail, frame)
16353 XFRAME (frame)->already_hscrolled_p = false; 16387 XFRAME (frame)->already_hscrolled_p = false;
16354 16388
16389 reset_outermost_narrowings ();
16390
16355 retry: 16391 retry:
16356 /* Remember the currently selected window. */ 16392 /* Remember the currently selected window. */
16357 sw = w; 16393 sw = w;
@@ -36711,10 +36747,11 @@ fontify a region starting at POS in the current buffer, and give
36711fontified regions the property `fontified' with a non-nil value. 36747fontified regions the property `fontified' with a non-nil value.
36712 36748
36713Note that, when the buffer contains one or more lines whose length is 36749Note that, when the buffer contains one or more lines whose length is
36714above `long-line-threshold', these functions are called with the buffer 36750above `long-line-threshold', these functions are called with the
36715narrowed to a small portion around POS, and the narrowing is locked (see 36751buffer narrowed to a small portion around POS (whose size is specified
36716`narrow-to-region'), so that these functions cannot use `widen' to gain 36752by `long-line-locked-narrowing-region-size'), and the narrowing is
36717access to other portions of buffer text. */); 36753locked (see `narrowing-lock'), so that these functions cannot use
36754`widen' to gain access to other portions of buffer text. */);
36718 Vfontification_functions = Qnil; 36755 Vfontification_functions = Qnil;
36719 Fmake_variable_buffer_local (Qfontification_functions); 36756 Fmake_variable_buffer_local (Qfontification_functions);
36720 36757