aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory Heytings2023-02-13 11:44:37 +0100
committerGregory Heytings2023-02-13 11:44:37 +0100
commitb948d0d7efe4c73a34485238d6a4e1bb5f0cac9e (patch)
tree3c1fc837f7094c21d1d35f1e66c657020908ea7f
parentcc30422825a5acf460d026bfe912b327b70dedcf (diff)
parentdcb2379a463678bdadd05ee39d61e7da84c71c5e (diff)
downloademacs-b948d0d7efe4c73a34485238d6a4e1bb5f0cac9e.tar.gz
emacs-b948d0d7efe4c73a34485238d6a4e1bb5f0cac9e.zip
Merge branch 'scratch/fix-locked-narrowing'
-rw-r--r--doc/lispref/commands.texi6
-rw-r--r--doc/lispref/display.texi10
-rw-r--r--doc/lispref/positions.texi82
-rw-r--r--etc/NEWS17
-rw-r--r--lisp/emacs-lisp/bytecomp.el2
-rw-r--r--lisp/subr.el47
-rw-r--r--src/buffer.c47
-rw-r--r--src/bytecode.c2
-rw-r--r--src/comp.c2
-rw-r--r--src/editfns.c98
-rw-r--r--src/keyboard.c31
-rw-r--r--src/lisp.h2
-rw-r--r--src/xdisp.c27
-rw-r--r--test/src/buffer-tests.el106
14 files changed, 341 insertions, 138 deletions
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index dc78adc4520..9723c279a45 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -99,6 +99,12 @@ is removed from the hook.
99emacs, The GNU Emacs Manual}) runs these two hooks just as a keyboard 99emacs, The GNU Emacs Manual}) runs these two hooks just as a keyboard
100command does. 100command does.
101 101
102 Note that, when the buffer text includes very long lines, these two
103hooks are called as if they were in a @code{with-narrowing} form
104(@pxref{Narrowing}), with a
105@code{long-line-optimizations-in-command-hooks} label and with the
106buffer narrowed to a portion around point.
107
102@node Defining Commands 108@node Defining Commands
103@section Defining Commands 109@section Defining Commands
104@cindex defining commands 110@cindex defining commands
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index c5374e1481a..f0ca7440c60 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -3501,11 +3501,11 @@ function finishes are the ones that really matter.
3501For efficiency, we recommend writing these functions so that they 3501For efficiency, we recommend writing these functions so that they
3502usually assign faces to around 400 to 600 characters at each call. 3502usually assign faces to around 400 to 600 characters at each call.
3503 3503
3504When the buffer text includes very long lines, these functions are 3504Note that, when the buffer text includes very long lines, these
3505called with the buffer narrowed to a relatively small region around 3505functions are called as if they were in a @code{with-narrowing} form
3506@var{pos}, and with narrowing locked, so the functions cannot use 3506(@pxref{Narrowing}), with a
3507@code{widen} to gain access to the rest of the buffer. 3507@code{long-line-optimizations-in-fontification-functions} label and
3508@xref{Narrowing}. 3508with the buffer narrowed to a portion around @var{pos}.
3509@end defvar 3509@end defvar
3510 3510
3511@node Basic Faces 3511@node Basic Faces
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi
index f3824436246..bad83e1be2d 100644
--- a/doc/lispref/positions.texi
+++ b/doc/lispref/positions.texi
@@ -1037,11 +1037,13 @@ positions.
1037In an interactive call, @var{start} and @var{end} are set to the bounds 1037In an interactive call, @var{start} and @var{end} are set to the bounds
1038of the current region (point and the mark, with the smallest first). 1038of the current region (point and the mark, with the smallest first).
1039 1039
1040Note that, in rare circumstances, Emacs may decide to leave, for 1040However, when the narrowing has been set by @code{with-narrowing} with
1041performance reasons, the accessible portion of the buffer unchanged 1041a label argument (see below), @code{narrow-to-region} can be used only
1042after a call to @code{narrow-to-region}. This can happen when a Lisp 1042within the limits of that narrowing. If @var{start} or @var{end} are
1043program is called via low-level hooks, such as 1043outside these limits, the corresponding limit set by
1044@code{jit-lock-functions}, @code{post-command-hook}, etc. 1044@code{with-narrowing} is used instead. To gain access to other
1045portions of the buffer, use @code{without-narrowing} with the same
1046label.
1045@end deffn 1047@end deffn
1046 1048
1047@deffn Command narrow-to-page &optional move-count 1049@deffn Command narrow-to-page &optional move-count
@@ -1065,13 +1067,13 @@ It is equivalent to the following expression:
1065@example 1067@example
1066(narrow-to-region 1 (1+ (buffer-size))) 1068(narrow-to-region 1 (1+ (buffer-size)))
1067@end example 1069@end example
1068@end deffn
1069 1070
1070Note that, in rare circumstances, Emacs may decide to leave, for 1071However, when a narrowing has been set by @code{with-narrowing} with a
1071performance reasons, the accessible portion of the buffer unchanged 1072label argument (see below), the limits set by @code{with-narrowing}
1072after a call to @code{widen}. This can happen when a Lisp program is 1073are restored, instead of canceling the narrowing. To gain access to
1073called via low-level hooks, such as @code{jit-lock-functions}, 1074other portions of the buffer, use @code{without-narrowing} with the
1074@code{post-command-hook}, etc. 1075same label.
1076@end deffn
1075 1077
1076@defun buffer-narrowed-p 1078@defun buffer-narrowed-p
1077This function returns non-@code{nil} if the buffer is narrowed, and 1079This function returns non-@code{nil} if the buffer is narrowed, and
@@ -1086,6 +1088,9 @@ in effect. The state of narrowing is restored even in the event of an
1086abnormal exit via @code{throw} or error (@pxref{Nonlocal Exits}). 1088abnormal exit via @code{throw} or error (@pxref{Nonlocal Exits}).
1087Therefore, this construct is a clean way to narrow a buffer temporarily. 1089Therefore, this construct is a clean way to narrow a buffer temporarily.
1088 1090
1091This construct also saves and restores the narrowings that were set by
1092@code{with-narrowing} with a label argument (see below).
1093
1089The value returned by @code{save-restriction} is that returned by the 1094The value returned by @code{save-restriction} is that returned by the
1090last form in @var{body}, or @code{nil} if no body forms were given. 1095last form in @var{body}, or @code{nil} if no body forms were given.
1091 1096
@@ -1135,3 +1140,58 @@ This is the contents of foo@point{}
1135@end group 1140@end group
1136@end example 1141@end example
1137@end defspec 1142@end defspec
1143
1144@defspec with-narrowing start end [:label label] body
1145This special form saves the current bounds of the accessible portion
1146of the buffer, sets the accessible portion to start at @var{start} and
1147end at @var{end}, evaluates the @var{body} forms, and restores the
1148saved bounds. In that case it is equivalent to
1149
1150@example
1151(save-restriction
1152 (narrow-to-region start end)
1153 body)
1154@end example
1155
1156When the optional @var{label} symbol argument is present however, the
1157narrowing is labeled. A labeled narrowing differs from a non-labeled
1158one in several ways:
1159
1160@itemize @bullet
1161@item
1162During the evaluation of the @var{body} form, @code{narrow-to-region}
1163and @code{widen} can be used only within the @var{start} and @var{end}
1164limits.
1165
1166@item
1167To lift the restriction introduced by @code{with-narrowing} and gain
1168access to other portions of the buffer, use @code{without-narrowing}
1169with the same @var{label} argument. (Another way to gain access to
1170other portions of the buffer is to use an indirect buffer
1171(@pxref{Indirect Buffers}).)
1172
1173@item
1174Labeled narrowings can be nested.
1175
1176@item
1177Labeled narrowings can only be used in Lisp programs: they are never
1178visible on display, and never interfere with narrowings set by the
1179user.
1180@end itemize
1181@end defspec
1182
1183@defspec without-narrowing [:label label] body
1184This special form saves the current bounds of the accessible portion
1185of the buffer, widens the buffer, evaluates the @var{body} forms, and
1186restores the saved bounds. In that case it is equivalent to
1187
1188@example
1189(save-restriction
1190 (widen)
1191 body)
1192@end example
1193
1194When the optional @var{label} argument is present however, the
1195narrowing set by @code{with-narrowing} with the same @var{label}
1196argument is lifted.
1197@end defspec
diff --git a/etc/NEWS b/etc/NEWS
index 2d15e39036a..de4f65ebe62 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -615,8 +615,13 @@ with 'C-x x t', or try disabling all known slow minor modes with
615and the major mode with 'M-x so-long-mode', or visit the file with 615and the major mode with 'M-x so-long-mode', or visit the file with
616'M-x find-file-literally' instead of the usual 'C-x C-f'. 616'M-x find-file-literally' instead of the usual 'C-x C-f'.
617 617
618Note that the display optimizations in these cases may cause the 618In buffers in which these display optimizations are in effect, the
619buffer to be occasionally mis-fontified. 619'fontification-functions', 'pre-command-hook' and 'post-command-hook'
620hooks are executed on a narrowed portion of the buffer, whose size is
621controlled by the options 'long-line-optimizations-region-size' and
622'long-line-optimizations-bol-search-limit', as if they were in a
623'with-narrowing' form. This may, in particular, cause occasional
624mis-fontifications in these buffers.
620 625
621The new function 'long-line-optimizations-p' returns non-nil when 626The new function 'long-line-optimizations-p' returns non-nil when
622these optimizations are in effect in the current buffer. 627these optimizations are in effect in the current buffer.
@@ -3814,6 +3819,14 @@ TIMEOUT is the idle time after which to deactivate the transient map.
3814The default timeout value can be defined by the new variable 3819The default timeout value can be defined by the new variable
3815'set-transient-map-timeout'. 3820'set-transient-map-timeout'.
3816 3821
3822+++
3823** New forms 'with-narrowing' and 'without-narrowing'.
3824These forms can be used as enhanced alternatives to the
3825'save-restriction' form combined with, respectively,
3826'narrow-to-region' and 'widen'. They also accept an optional label
3827argument, with which labeled narrowings can be created and lifted.
3828See the "(elisp) Narrowing" node for details.
3829
3817** Connection Local Variables 3830** Connection Local Variables
3818 3831
3819+++ 3832+++
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 5df1205869c..c6cda6b588a 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -4900,7 +4900,7 @@ binding slots have been popped."
4900(defun byte-compile-save-restriction (form) 4900(defun byte-compile-save-restriction (form)
4901 (byte-compile-out 'byte-save-restriction 0) 4901 (byte-compile-out 'byte-save-restriction 0)
4902 (byte-compile-body-do-effect (cdr form)) 4902 (byte-compile-body-do-effect (cdr form))
4903 (byte-compile-out 'byte-unbind 1)) 4903 (byte-compile-out 'byte-unbind 2))
4904 4904
4905(defun byte-compile-save-current-buffer (form) 4905(defun byte-compile-save-current-buffer (form)
4906 (byte-compile-out 'byte-save-current-buffer 0) 4906 (byte-compile-out 'byte-save-current-buffer 0)
diff --git a/lisp/subr.el b/lisp/subr.el
index 9e6388987df..58a8e85b61d 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3946,25 +3946,46 @@ See also `locate-user-emacs-file'.")
3946 3946
3947The current restrictions, if any, are restored upon return. 3947The current restrictions, if any, are restored upon return.
3948 3948
3949With the optional :locked TAG argument, inside BODY, 3949When the optional :label LABEL argument is present, in which
3950`narrow-to-region' and `widen' can be used only within the START 3950LABEL is a symbol, inside BODY, `narrow-to-region' and `widen'
3951and END limits, unless the restrictions are unlocked by calling 3951can be used only within the START and END limits. To gain access
3952`narrowing-unlock' with TAG. See `narrowing-lock' for a more 3952to other portions of the buffer, use `without-narrowing' with the
3953detailed description. 3953same LABEL argument.
3954 3954
3955\(fn START END [:locked TAG] BODY)" 3955\(fn START END [:label LABEL] BODY)"
3956 (if (eq (car rest) :locked) 3956 (if (eq (car rest) :label)
3957 `(internal--with-narrowing ,start ,end (lambda () ,@(cddr rest)) 3957 `(internal--with-narrowing ,start ,end (lambda () ,@(cddr rest))
3958 ,(cadr rest)) 3958 ,(cadr rest))
3959 `(internal--with-narrowing ,start ,end (lambda () ,@rest)))) 3959 `(internal--with-narrowing ,start ,end (lambda () ,@rest))))
3960 3960
3961(defun internal--with-narrowing (start end body &optional tag) 3961(defun internal--with-narrowing (start end body &optional label)
3962 "Helper function for `with-narrowing', which see." 3962 "Helper function for `with-narrowing', which see."
3963 (save-restriction 3963 (save-restriction
3964 (progn 3964 (narrow-to-region start end)
3965 (narrow-to-region start end) 3965 (if label (internal--lock-narrowing label))
3966 (if tag (narrowing-lock tag)) 3966 (funcall body)))
3967 (funcall body)))) 3967
3968(defmacro without-narrowing (&rest rest)
3969 "Execute BODY without restrictions.
3970
3971The current restrictions, if any, are restored upon return.
3972
3973When the optional :label LABEL argument is present, the
3974restrictions set by `with-narrowing' with the same LABEL argument
3975are lifted.
3976
3977\(fn [:label LABEL] BODY)"
3978 (if (eq (car rest) :label)
3979 `(internal--without-narrowing (lambda () ,@(cddr rest))
3980 ,(cadr rest))
3981 `(internal--without-narrowing (lambda () ,@rest))))
3982
3983(defun internal--without-narrowing (body &optional label)
3984 "Helper function for `without-narrowing', which see."
3985 (save-restriction
3986 (if label (internal--unlock-narrowing label))
3987 (widen)
3988 (funcall body)))
3968 3989
3969(defun find-tag-default-bounds () 3990(defun find-tag-default-bounds ()
3970 "Determine the boundaries of the default tag, based on text at point. 3991 "Determine the boundaries of the default tag, based on text at point.
diff --git a/src/buffer.c b/src/buffer.c
index 38648519ba0..755061d0dee 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5916,40 +5916,41 @@ If nil, these display shortcuts will always remain disabled.
5916There is no reason to change that value except for debugging purposes. */); 5916There is no reason to change that value except for debugging purposes. */);
5917 XSETFASTINT (Vlong_line_threshold, 50000); 5917 XSETFASTINT (Vlong_line_threshold, 50000);
5918 5918
5919 DEFVAR_INT ("long-line-locked-narrowing-region-size", 5919 DEFVAR_INT ("long-line-optimizations-region-size",
5920 long_line_locked_narrowing_region_size, 5920 long_line_optimizations_region_size,
5921 doc: /* Region size for locked narrowing in buffers with long lines. 5921 doc: /* Region size for narrowing in buffers with long lines.
5922 5922
5923This variable has effect only in buffers which contain one or more 5923This variable has effect only in buffers in which
5924lines whose length is above `long-line-threshold', which see. For 5924`long-line-optimizations-p' is non-nil. For performance reasons, in
5925performance reasons, in such buffers, low-level hooks such as 5925such buffers, the `fontification-functions', `pre-command-hook' and
5926`fontification-functions' or `post-command-hook' are executed on a 5926`post-command-hook' hooks are executed on a narrowed buffer around
5927narrowed buffer, with a narrowing locked with `narrowing-lock'. This 5927point, as if they were called in a `with-narrowing' form with a label.
5928variable specifies the size of the narrowed region around point. 5928This variable specifies the size of the narrowed region around point.
5929 5929
5930To disable that narrowing, set this variable to 0. 5930To disable that narrowing, set this variable to 0.
5931 5931
5932See also `long-line-locked-narrowing-bol-search-limit'. 5932See also `long-line-optimizations-bol-search-limit'.
5933 5933
5934There is no reason to change that value except for debugging purposes. */); 5934There is no reason to change that value except for debugging purposes. */);
5935 long_line_locked_narrowing_region_size = 500000; 5935 long_line_optimizations_region_size = 500000;
5936 5936
5937 DEFVAR_INT ("long-line-locked-narrowing-bol-search-limit", 5937 DEFVAR_INT ("long-line-optimizations-bol-search-limit",
5938 long_line_locked_narrowing_bol_search_limit, 5938 long_line_optimizations_bol_search_limit,
5939 doc: /* Limit for beginning of line search in buffers with long lines. 5939 doc: /* Limit for beginning of line search in buffers with long lines.
5940 5940
5941This variable has effect only in buffers which contain one or more 5941This variable has effect only in buffers in which
5942lines whose length is above `long-line-threshold', which see. For 5942`long-line-optimizations-p' is non-nil. For performance reasons, in
5943performance reasons, in such buffers, low-level hooks such as 5943such buffers, the `fontification-functions', `pre-command-hook' and
5944`fontification-functions' or `post-command-hook' are executed on a 5944`post-command-hook' hooks are executed on a narrowed buffer around
5945narrowed buffer, with a narrowing locked with `narrowing-lock'. The 5945point, as if they were called in a `with-narrowing' form with a label.
5946variable `long-line-locked-narrowing-region-size' specifies the size 5946The variable `long-line-optimizations-region-size' specifies the
5947of the narrowed region around point. This variable, which should be a 5947size of the narrowed region around point. This variable, which should
5948small integer, specifies the number of characters by which that region 5948be a small integer, specifies the number of characters by which that
5949can be extended backwards to make it start at the beginning of a line. 5949region can be extended backwards to make it start at the beginning of
5950a line.
5950 5951
5951There is no reason to change that value except for debugging purposes. */); 5952There is no reason to change that value except for debugging purposes. */);
5952 long_line_locked_narrowing_bol_search_limit = 128; 5953 long_line_optimizations_bol_search_limit = 128;
5953 5954
5954 DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, 5955 DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold,
5955 doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. 5956 doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts.
diff --git a/src/bytecode.c b/src/bytecode.c
index 124348e5b35..8e214560f30 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -942,6 +942,8 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
942 CASE (Bsave_restriction): 942 CASE (Bsave_restriction):
943 record_unwind_protect (save_restriction_restore, 943 record_unwind_protect (save_restriction_restore,
944 save_restriction_save ()); 944 save_restriction_save ());
945 record_unwind_protect (narrowing_locks_restore,
946 narrowing_locks_save ());
945 NEXT; 947 NEXT;
946 948
947 CASE (Bcatch): /* Obsolete since 25. */ 949 CASE (Bcatch): /* Obsolete since 25. */
diff --git a/src/comp.c b/src/comp.c
index 10cf7962ba1..0e2dfd3913b 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -5063,6 +5063,8 @@ helper_save_restriction (void)
5063{ 5063{
5064 record_unwind_protect (save_restriction_restore, 5064 record_unwind_protect (save_restriction_restore,
5065 save_restriction_save ()); 5065 save_restriction_save ());
5066 record_unwind_protect (narrowing_locks_restore,
5067 narrowing_locks_save ());
5066} 5068}
5067 5069
5068static bool 5070static bool
diff --git a/src/editfns.c b/src/editfns.c
index 78d2c73ecbf..f9879662168 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -2659,7 +2659,11 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
2659 the (uninterned) Qoutermost_narrowing tag and records the narrowing 2659 the (uninterned) Qoutermost_narrowing tag and records the narrowing
2660 bounds that were set by the user and that are visible on display. 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, 2661 This alist is used internally by narrow-to-region, widen,
2662 narrowing-lock, narrowing-unlock and save-restriction. */ 2662 internal--lock-narrowing, internal--unlock-narrowing and
2663 save-restriction. For efficiency reasons, an alist is used instead
2664 of a buffer-local variable: otherwise reset_outermost_narrowings,
2665 which is called during each redisplay cycle, would have to loop
2666 through all live buffers. */
2663static Lisp_Object narrowing_locks; 2667static Lisp_Object narrowing_locks;
2664 2668
2665/* Add BUF with its LOCKS in the narrowing_locks alist. */ 2669/* Add BUF with its LOCKS in the narrowing_locks alist. */
@@ -2763,7 +2767,10 @@ unwind_reset_outermost_narrowing (Lisp_Object buf)
2763 In particular, this function is called when redisplay starts, so 2767 In particular, this function is called when redisplay starts, so
2764 that if a Lisp function executed during redisplay calls (redisplay) 2768 that if a Lisp function executed during redisplay calls (redisplay)
2765 while a locked narrowing is in effect, the locked narrowing will 2769 while a locked narrowing is in effect, the locked narrowing will
2766 not be visible on display. */ 2770 not be visible on display.
2771 See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#140 and
2772 https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#254 for example
2773 recipes that demonstrate why this is necessary. */
2767void 2774void
2768reset_outermost_narrowings (void) 2775reset_outermost_narrowings (void)
2769{ 2776{
@@ -2787,32 +2794,30 @@ reset_outermost_narrowings (void)
2787 2794
2788/* Helper functions to save and restore the narrowing locks of the 2795/* Helper functions to save and restore the narrowing locks of the
2789 current buffer in Fsave_restriction. */ 2796 current buffer in Fsave_restriction. */
2790static Lisp_Object 2797Lisp_Object
2791narrowing_locks_save (void) 2798narrowing_locks_save (void)
2792{ 2799{
2793 Lisp_Object buf = Fcurrent_buffer (); 2800 Lisp_Object buf = Fcurrent_buffer ();
2794 Lisp_Object locks = assq_no_quit (buf, narrowing_locks); 2801 Lisp_Object locks = assq_no_quit (buf, narrowing_locks);
2795 if (NILP (locks)) 2802 if (!NILP (locks))
2796 return Qnil; 2803 locks = XCAR (XCDR (locks));
2797 locks = XCAR (XCDR (locks));
2798 return Fcons (buf, Fcopy_sequence (locks)); 2804 return Fcons (buf, Fcopy_sequence (locks));
2799} 2805}
2800 2806
2801static void 2807void
2802narrowing_locks_restore (Lisp_Object buf_and_saved_locks) 2808narrowing_locks_restore (Lisp_Object buf_and_saved_locks)
2803{ 2809{
2804 if (NILP (buf_and_saved_locks))
2805 return;
2806 Lisp_Object buf = XCAR (buf_and_saved_locks); 2810 Lisp_Object buf = XCAR (buf_and_saved_locks);
2807 Lisp_Object saved_locks = XCDR (buf_and_saved_locks); 2811 Lisp_Object saved_locks = XCDR (buf_and_saved_locks);
2808 narrowing_locks_remove (buf); 2812 narrowing_locks_remove (buf);
2809 narrowing_locks_add (buf, saved_locks); 2813 if (!NILP (saved_locks))
2814 narrowing_locks_add (buf, saved_locks);
2810} 2815}
2811 2816
2812static void 2817static void
2813unwind_narrow_to_region_locked (Lisp_Object tag) 2818unwind_narrow_to_region_locked (Lisp_Object tag)
2814{ 2819{
2815 Fnarrowing_unlock (tag); 2820 Finternal__unlock_narrowing (tag);
2816 Fwiden (); 2821 Fwiden ();
2817} 2822}
2818 2823
@@ -2821,7 +2826,7 @@ void
2821narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag) 2826narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag)
2822{ 2827{
2823 Fnarrow_to_region (begv, zv); 2828 Fnarrow_to_region (begv, zv);
2824 Fnarrowing_lock (tag); 2829 Finternal__lock_narrowing (tag);
2825 record_unwind_protect (restore_point_unwind, Fpoint_marker ()); 2830 record_unwind_protect (restore_point_unwind, Fpoint_marker ());
2826 record_unwind_protect (unwind_narrow_to_region_locked, tag); 2831 record_unwind_protect (unwind_narrow_to_region_locked, tag);
2827} 2832}
@@ -2829,10 +2834,12 @@ narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag)
2829DEFUN ("widen", Fwiden, Swiden, 0, 0, "", 2834DEFUN ("widen", Fwiden, Swiden, 0, 0, "",
2830 doc: /* Remove restrictions (narrowing) from current buffer. 2835 doc: /* Remove restrictions (narrowing) from current buffer.
2831 2836
2832This allows the buffer's full text to be seen and edited, unless 2837This allows the buffer's full text to be seen and edited.
2833restrictions have been locked with `narrowing-lock', which see, in 2838
2834which case the narrowing that was current when `narrowing-lock' was 2839However, when restrictions have been set by `with-narrowing' with a
2835called is restored. */) 2840label, `widen' restores the narrowing limits set by `with-narrowing'.
2841To gain access to other portions of the buffer, use
2842`without-narrowing' with the same label. */)
2836 (void) 2843 (void)
2837{ 2844{
2838 Fset (Qoutermost_narrowing, Qnil); 2845 Fset (Qoutermost_narrowing, Qnil);
@@ -2879,11 +2886,12 @@ When calling from Lisp, pass two arguments START and END:
2879positions (integers or markers) bounding the text that should 2886positions (integers or markers) bounding the text that should
2880remain visible. 2887remain visible.
2881 2888
2882When restrictions have been locked with `narrowing-lock', which see, 2889However, when restrictions have been set by `with-narrowing' with a
2883`narrow-to-region' can be used only within the limits of the 2890label, `narrow-to-region' can be used only within the limits of these
2884restrictions that were current when `narrowing-lock' was called. If 2891restrictions. If the START or END arguments are outside these limits,
2885the START or END arguments are outside these limits, the corresponding 2892the corresponding limit set by `with-narrowing' is used instead of the
2886limit of the locked restriction is used instead of the argument. */) 2893argument. To gain access to other portions of the buffer, use
2894`without-narrowing' with the same label. */)
2887 (Lisp_Object start, Lisp_Object end) 2895 (Lisp_Object start, Lisp_Object end)
2888{ 2896{
2889 EMACS_INT s = fix_position (start), e = fix_position (end); 2897 EMACS_INT s = fix_position (start), e = fix_position (end);
@@ -2912,7 +2920,7 @@ limit of the locked restriction is used instead of the argument. */)
2912 2920
2913 /* Record the accessible range of the buffer when narrow-to-region 2921 /* Record the accessible range of the buffer when narrow-to-region
2914 is called, that is, before applying the narrowing. It is used 2922 is called, that is, before applying the narrowing. It is used
2915 only by narrowing-lock. */ 2923 only by internal--lock-narrowing. */
2916 Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing, 2924 Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing,
2917 Fpoint_min_marker (), 2925 Fpoint_min_marker (),
2918 Fpoint_max_marker ())); 2926 Fpoint_max_marker ()));
@@ -2932,31 +2940,18 @@ limit of the locked restriction is used instead of the argument. */)
2932 return Qnil; 2940 return Qnil;
2933} 2941}
2934 2942
2935DEFUN ("narrowing-lock", Fnarrowing_lock, Snarrowing_lock, 1, 1, 0, 2943DEFUN ("internal--lock-narrowing", Finternal__lock_narrowing,
2936 doc: /* Lock the current narrowing with TAG. 2944 Sinternal__lock_narrowing, 1, 1, 0,
2937 2945 doc: /* Lock the current narrowing with LABEL.
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 2946
2951Locked restrictions are never visible on display, and can therefore 2947This is an internal function used by `with-narrowing'. */)
2952not be used as a stronger variant of normal restrictions. */)
2953 (Lisp_Object tag) 2948 (Lisp_Object tag)
2954{ 2949{
2955 Lisp_Object buf = Fcurrent_buffer (); 2950 Lisp_Object buf = Fcurrent_buffer ();
2956 Lisp_Object outermost_narrowing 2951 Lisp_Object outermost_narrowing
2957 = buffer_local_value (Qoutermost_narrowing, buf); 2952 = buffer_local_value (Qoutermost_narrowing, buf);
2958 /* If narrowing-lock is called without being preceded by 2953 /* If internal--lock-narrowing is ever called without being preceded
2959 narrow-to-region, do nothing. */ 2954 by narrow-to-region, do nothing. */
2960 if (NILP (outermost_narrowing)) 2955 if (NILP (outermost_narrowing))
2961 return Qnil; 2956 return Qnil;
2962 if (NILP (narrowing_lock_peek_tag (buf))) 2957 if (NILP (narrowing_lock_peek_tag (buf)))
@@ -2967,16 +2962,11 @@ not be used as a stronger variant of normal restrictions. */)
2967 return Qnil; 2962 return Qnil;
2968} 2963}
2969 2964
2970DEFUN ("narrowing-unlock", Fnarrowing_unlock, Snarrowing_unlock, 1, 1, 0, 2965DEFUN ("internal--unlock-narrowing", Finternal__unlock_narrowing,
2971 doc: /* Unlock a narrowing locked with (narrowing-lock TAG). 2966 Sinternal__unlock_narrowing, 1, 1, 0,
2967 doc: /* Unlock a narrowing locked with LABEL.
2972 2968
2973Unlocking restrictions locked with `narrowing-lock' should be used 2969This is an internal function used by `without-narrowing'. */)
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) 2970 (Lisp_Object tag)
2981{ 2971{
2982 Lisp_Object buf = Fcurrent_buffer (); 2972 Lisp_Object buf = Fcurrent_buffer ();
@@ -3083,8 +3073,8 @@ DEFUN ("save-restriction", Fsave_restriction, Ssave_restriction, 0, UNEVALLED, 0
3083The buffer's restrictions make parts of the beginning and end invisible. 3073The buffer's restrictions make parts of the beginning and end invisible.
3084\(They are set up with `narrow-to-region' and eliminated with `widen'.) 3074\(They are set up with `narrow-to-region' and eliminated with `widen'.)
3085This special form, `save-restriction', saves the current buffer's 3075This special form, `save-restriction', saves the current buffer's
3086restrictions, as well as their locks if they have been locked with 3076restrictions, including those that were set by `with-narrowing' with a
3087`narrowing-lock', when it is entered, and restores them when it is exited. 3077label argument, when it is entered, and restores them when it is exited.
3088So any `narrow-to-region' within BODY lasts only until the end of the form. 3078So any `narrow-to-region' within BODY lasts only until the end of the form.
3089The old restrictions settings are restored even in case of abnormal exit 3079The old restrictions settings are restored even in case of abnormal exit
3090\(throw or error). 3080\(throw or error).
@@ -4903,8 +4893,8 @@ it to be non-nil. */);
4903 defsubr (&Sdelete_and_extract_region); 4893 defsubr (&Sdelete_and_extract_region);
4904 defsubr (&Swiden); 4894 defsubr (&Swiden);
4905 defsubr (&Snarrow_to_region); 4895 defsubr (&Snarrow_to_region);
4906 defsubr (&Snarrowing_lock); 4896 defsubr (&Sinternal__lock_narrowing);
4907 defsubr (&Snarrowing_unlock); 4897 defsubr (&Sinternal__unlock_narrowing);
4908 defsubr (&Ssave_restriction); 4898 defsubr (&Ssave_restriction);
4909 defsubr (&Stranspose_regions); 4899 defsubr (&Stranspose_regions);
4910} 4900}
diff --git a/src/keyboard.c b/src/keyboard.c
index 6f0f075e54e..1d0b907bd8e 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1910,12 +1910,13 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w)
1910 specbind (Qinhibit_quit, Qt); 1910 specbind (Qinhibit_quit, Qt);
1911 1911
1912 if (current_buffer->long_line_optimizations_p 1912 if (current_buffer->long_line_optimizations_p
1913 && long_line_locked_narrowing_region_size > 0) 1913 && long_line_optimizations_region_size > 0)
1914 { 1914 {
1915 ptrdiff_t begv = get_locked_narrowing_begv (PT); 1915 ptrdiff_t begv = get_locked_narrowing_begv (PT);
1916 ptrdiff_t zv = get_locked_narrowing_zv (PT); 1916 ptrdiff_t zv = get_locked_narrowing_zv (PT);
1917 if (begv != BEG || zv != Z) 1917 if (begv != BEG || zv != Z)
1918 narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), hook); 1918 narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv),
1919 Qlong_line_optimizations_in_command_hooks);
1919 } 1920 }
1920 1921
1921 run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), 1922 run_hook_with_args (2, ((Lisp_Object []) {hook, hook}),
@@ -12168,6 +12169,8 @@ syms_of_keyboard (void)
12168 /* Hooks to run before and after each command. */ 12169 /* Hooks to run before and after each command. */
12169 DEFSYM (Qpre_command_hook, "pre-command-hook"); 12170 DEFSYM (Qpre_command_hook, "pre-command-hook");
12170 DEFSYM (Qpost_command_hook, "post-command-hook"); 12171 DEFSYM (Qpost_command_hook, "post-command-hook");
12172 DEFSYM (Qlong_line_optimizations_in_command_hooks,
12173 "long-line-optimizations-in-command-hooks");
12171 12174
12172 /* Hook run after the region is selected. */ 12175 /* Hook run after the region is selected. */
12173 DEFSYM (Qpost_select_region_hook, "post-select-region-hook"); 12176 DEFSYM (Qpost_select_region_hook, "post-select-region-hook");
@@ -12728,13 +12731,11 @@ If an unhandled error happens in running this hook, the function in
12728which the error occurred is unconditionally removed, since otherwise 12731which the error occurred is unconditionally removed, since otherwise
12729the error might happen repeatedly and make Emacs nonfunctional. 12732the error might happen repeatedly and make Emacs nonfunctional.
12730 12733
12731Note that, when the current buffer contains one or more lines whose 12734Note that, when `long-line-optimizations-p' is non-nil in the buffer,
12732length is above `long-line-threshold', these hook functions are called 12735these functions are called as if they were in a `with-narrowing' form,
12733with the buffer narrowed to a small portion around point (whose size 12736with a `long-line-optimizations-in-command-hooks' label and with the
12734is specified by `long-line-locked-narrowing-region-size'), and the 12737buffer narrowed to a portion around point whose size is specified by
12735narrowing is locked (see `narrowing-lock'), so that these hook 12738`long-line-optimizations-region-size'.
12736functions cannot use `widen' to gain access to other portions of
12737buffer text.
12738 12739
12739See also `post-command-hook'. */); 12740See also `post-command-hook'. */);
12740 Vpre_command_hook = Qnil; 12741 Vpre_command_hook = Qnil;
@@ -12750,13 +12751,11 @@ It is a bad idea to use this hook for expensive processing. If
12750unavoidable, wrap your code in `(while-no-input (redisplay) CODE)' to 12751unavoidable, wrap your code in `(while-no-input (redisplay) CODE)' to
12751avoid making Emacs unresponsive while the user types. 12752avoid making Emacs unresponsive while the user types.
12752 12753
12753Note that, when the current buffer contains one or more lines whose 12754Note that, when `long-line-optimizations-p' is non-nil in the buffer,
12754length is above `long-line-threshold', these hook functions are called 12755these functions are called as if they were in a `with-narrowing' form,
12755with the buffer narrowed to a small portion around point (whose size 12756with a `long-line-optimizations-in-command-hooks' label and with the
12756is specified by `long-line-locked-narrowing-region-size'), and the 12757buffer narrowed to a portion around point whose size is specified by
12757narrowing is locked (see `narrowing-lock'), so that these hook 12758`long-line-optimizations-region-size'.
12758functions cannot use `widen' to gain access to other portions of
12759buffer text.
12760 12759
12761See also `pre-command-hook'. */); 12760See also `pre-command-hook'. */);
12762 Vpost_command_hook = Qnil; 12761 Vpost_command_hook = Qnil;
diff --git a/src/lisp.h b/src/lisp.h
index 1276285e2f2..93197d38176 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4684,6 +4684,8 @@ extern void save_excursion_save (union specbinding *);
4684extern void save_excursion_restore (Lisp_Object, Lisp_Object); 4684extern void save_excursion_restore (Lisp_Object, Lisp_Object);
4685extern Lisp_Object save_restriction_save (void); 4685extern Lisp_Object save_restriction_save (void);
4686extern void save_restriction_restore (Lisp_Object); 4686extern void save_restriction_restore (Lisp_Object);
4687extern Lisp_Object narrowing_locks_save (void);
4688extern void narrowing_locks_restore (Lisp_Object);
4687extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); 4689extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool);
4688extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, 4690extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
4689 ptrdiff_t, bool); 4691 ptrdiff_t, bool);
diff --git a/src/xdisp.c b/src/xdisp.c
index a19c9908616..1450b869d20 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -3536,11 +3536,11 @@ get_closer_narrowed_begv (struct window *w, ptrdiff_t pos)
3536ptrdiff_t 3536ptrdiff_t
3537get_locked_narrowing_begv (ptrdiff_t pos) 3537get_locked_narrowing_begv (ptrdiff_t pos)
3538{ 3538{
3539 if (long_line_locked_narrowing_region_size <= 0) 3539 if (long_line_optimizations_region_size <= 0)
3540 return BEGV; 3540 return BEGV;
3541 int len = long_line_locked_narrowing_region_size / 2; 3541 int len = long_line_optimizations_region_size / 2;
3542 int begv = max (pos - len, BEGV); 3542 int begv = max (pos - len, BEGV);
3543 int limit = long_line_locked_narrowing_bol_search_limit; 3543 int limit = long_line_optimizations_bol_search_limit;
3544 while (limit > 0) 3544 while (limit > 0)
3545 { 3545 {
3546 if (begv == BEGV || FETCH_BYTE (CHAR_TO_BYTE (begv) - 1) == '\n') 3546 if (begv == BEGV || FETCH_BYTE (CHAR_TO_BYTE (begv) - 1) == '\n')
@@ -3554,9 +3554,9 @@ get_locked_narrowing_begv (ptrdiff_t pos)
3554ptrdiff_t 3554ptrdiff_t
3555get_locked_narrowing_zv (ptrdiff_t pos) 3555get_locked_narrowing_zv (ptrdiff_t pos)
3556{ 3556{
3557 if (long_line_locked_narrowing_region_size <= 0) 3557 if (long_line_optimizations_region_size <= 0)
3558 return ZV; 3558 return ZV;
3559 int len = long_line_locked_narrowing_region_size / 2; 3559 int len = long_line_optimizations_region_size / 2;
3560 return min (pos + len, ZV); 3560 return min (pos + len, ZV);
3561} 3561}
3562 3562
@@ -4394,7 +4394,7 @@ handle_fontified_prop (struct it *it)
4394 eassert (it->end_charpos == ZV); 4394 eassert (it->end_charpos == ZV);
4395 4395
4396 if (current_buffer->long_line_optimizations_p 4396 if (current_buffer->long_line_optimizations_p
4397 && long_line_locked_narrowing_region_size > 0) 4397 && long_line_optimizations_region_size > 0)
4398 { 4398 {
4399 ptrdiff_t begv = it->locked_narrowing_begv; 4399 ptrdiff_t begv = it->locked_narrowing_begv;
4400 ptrdiff_t zv = it->locked_narrowing_zv; 4400 ptrdiff_t zv = it->locked_narrowing_zv;
@@ -4406,7 +4406,7 @@ handle_fontified_prop (struct it *it)
4406 } 4406 }
4407 if (begv != BEG || zv != Z) 4407 if (begv != BEG || zv != Z)
4408 narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), 4408 narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv),
4409 Qfontification_functions); 4409 Qlong_line_optimizations_in_fontification_functions);
4410 } 4410 }
4411 4411
4412 /* Don't allow Lisp that runs from 'fontification-functions' 4412 /* Don't allow Lisp that runs from 'fontification-functions'
@@ -36266,6 +36266,8 @@ be let-bound around code that needs to disable messages temporarily. */);
36266 DEFSYM (QCfile, ":file"); 36266 DEFSYM (QCfile, ":file");
36267 DEFSYM (Qfontified, "fontified"); 36267 DEFSYM (Qfontified, "fontified");
36268 DEFSYM (Qfontification_functions, "fontification-functions"); 36268 DEFSYM (Qfontification_functions, "fontification-functions");
36269 DEFSYM (Qlong_line_optimizations_in_fontification_functions,
36270 "long-line-optimizations-in-fontification-functions");
36269 36271
36270 /* Name of the symbol which disables Lisp evaluation in 'display' 36272 /* Name of the symbol which disables Lisp evaluation in 'display'
36271 properties. This is used by enriched.el. */ 36273 properties. This is used by enriched.el. */
@@ -36775,12 +36777,11 @@ Each function is called with one argument POS. Functions must
36775fontify a region starting at POS in the current buffer, and give 36777fontify a region starting at POS in the current buffer, and give
36776fontified regions the property `fontified' with a non-nil value. 36778fontified regions the property `fontified' with a non-nil value.
36777 36779
36778Note that, when the buffer contains one or more lines whose length is 36780Note that, when `long-line-optimizations-p' is non-nil in the buffer,
36779above `long-line-threshold', these functions are called with the 36781these functions are called as if they were in a `with-narrowing' form,
36780buffer narrowed to a small portion around POS (whose size is specified 36782with a `long-line-optimizations-in-fontification-functions' label and
36781by `long-line-locked-narrowing-region-size'), and the narrowing is 36783with the buffer narrowed to a portion around POS whose size is
36782locked (see `narrowing-lock'), so that these functions cannot use 36784specified by `long-line-optimizations-region-size'. */);
36783`widen' to gain access to other portions of buffer text. */);
36784 Vfontification_functions = Qnil; 36785 Vfontification_functions = Qnil;
36785 Fmake_variable_buffer_local (Qfontification_functions); 36786 Fmake_variable_buffer_local (Qfontification_functions);
36786 36787
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 9d4bbf3e040..0ae78c8d9d9 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -8539,4 +8539,110 @@ Finally, kill the buffer and its temporary file."
8539 (if f2 (delete-file f2)) 8539 (if f2 (delete-file f2))
8540 ))) 8540 )))
8541 8541
8542(ert-deftest test-labeled-narrowing ()
8543 "Test `with-narrowing' and `without-narrowing'."
8544 (with-current-buffer (generate-new-buffer " foo" t)
8545 (insert (make-string 5000 ?a))
8546 (should (= (point-min) 1))
8547 (should (= (point-max) 5001))
8548 (with-narrowing
8549 100 500 :label 'foo
8550 (should (= (point-min) 100))
8551 (should (= (point-max) 500))
8552 (widen)
8553 (should (= (point-min) 100))
8554 (should (= (point-max) 500))
8555 (narrow-to-region 1 5000)
8556 (should (= (point-min) 100))
8557 (should (= (point-max) 500))
8558 (narrow-to-region 50 150)
8559 (should (= (point-min) 100))
8560 (should (= (point-max) 150))
8561 (widen)
8562 (should (= (point-min) 100))
8563 (should (= (point-max) 500))
8564 (narrow-to-region 400 1000)
8565 (should (= (point-min) 400))
8566 (should (= (point-max) 500))
8567 (without-narrowing
8568 :label 'bar
8569 (should (= (point-min) 100))
8570 (should (= (point-max) 500)))
8571 (without-narrowing
8572 :label 'foo
8573 (should (= (point-min) 1))
8574 (should (= (point-max) 5001)))
8575 (should (= (point-min) 400))
8576 (should (= (point-max) 500))
8577 (widen)
8578 (should (= (point-min) 100))
8579 (should (= (point-max) 500))
8580 (with-narrowing
8581 50 250 :label 'bar
8582 (should (= (point-min) 100))
8583 (should (= (point-max) 250))
8584 (widen)
8585 (should (= (point-min) 100))
8586 (should (= (point-max) 250))
8587 (without-narrowing
8588 :label 'bar
8589 (should (= (point-min) 100))
8590 (should (= (point-max) 500))
8591 (without-narrowing
8592 :label 'foo
8593 (should (= (point-min) 1))
8594 (should (= (point-max) 5001)))
8595 (should (= (point-min) 100))
8596 (should (= (point-max) 500)))
8597 (should (= (point-min) 100))
8598 (should (= (point-max) 250)))
8599 (should (= (point-min) 100))
8600 (should (= (point-max) 500))
8601 (with-narrowing
8602 50 250 :label 'bar
8603 (should (= (point-min) 100))
8604 (should (= (point-max) 250))
8605 (with-narrowing
8606 150 500 :label 'baz
8607 (should (= (point-min) 150))
8608 (should (= (point-max) 250))
8609 (without-narrowing
8610 :label 'bar
8611 (should (= (point-min) 150))
8612 (should (= (point-max) 250)))
8613 (without-narrowing
8614 :label 'foo
8615 (should (= (point-min) 150))
8616 (should (= (point-max) 250)))
8617 (without-narrowing
8618 :label 'baz
8619 (should (= (point-min) 100))
8620 (should (= (point-max) 250))
8621 (without-narrowing
8622 :label 'foo
8623 (should (= (point-min) 100))
8624 (should (= (point-max) 250)))
8625 (without-narrowing
8626 :label 'bar
8627 (should (= (point-min) 100))
8628 (should (= (point-max) 500))
8629 (without-narrowing
8630 :label 'foobar
8631 (should (= (point-min) 100))
8632 (should (= (point-max) 500)))
8633 (without-narrowing
8634 :label 'foo
8635 (should (= (point-min) 1))
8636 (should (= (point-max) 5001)))
8637 (should (= (point-min) 100))
8638 (should (= (point-max) 500)))
8639 (should (= (point-min) 100))
8640 (should (= (point-max) 250)))
8641 (should (= (point-min) 150))
8642 (should (= (point-max) 250)))
8643 (should (= (point-min) 100))
8644 (should (= (point-max) 250))))
8645 (should (= (point-min) 1))
8646 (should (= (point-max) 5001))))
8647
8542;;; buffer-tests.el ends here 8648;;; buffer-tests.el ends here