Update the documentation about labeled (locked) narrowing

* src/xdisp.c (syms_of_xdisp) <fontification-functions>: Update
docstring.

* src/keyboard.c (syms_of_keyboard) <pre-command-hook>:
(syms_of_keyboard) <post-command-hook>: Update docstring.

* src/editfns.c:
(narrowing_locks): Explain why an alist is used instead of a
buffer-local variable.
(reset_outermost_narrowings): Point to recipes that demonstrate
why it is necessary to restore the user narrowing bounds when
redisplay starts.
(Fwiden): Update docstring.
(Fnarrow_to_region): Update docstring.
(Finternal__lock_narrowing): Update docstring.
(Finternal__unlock_narrowing): Update docstring.
(Fsave_restriction): Update docstring.

* src/buffer.c (syms_of_buffer)
<long-line-optimizations-region-size>: Update docstring.
(syms_of_buffer) <long-line-optimizations-bol-search-limit>:
Update docstring.

* lisp/subr.el (with-narrowing): Update docstring.
(without-narrowing): Update docstring.

* etc/NEWS: Mention the 'long-line-optimizations-region-size' and
'long-line-optimizations-bol-search-limit' options.
Announce the 'with-narrowing' and 'without-narrowing' forms.

* doc/lispref/positions.texi (Narrowing): Update the documentation
of 'narrow-to-region', 'widen' and 'save-restriction'.  Document
the 'with-narrowing' and 'without-narrowing' special forms.

* doc/lispref/display.texi (Auto Faces): Update the documentation.

* doc/lispref/commands.texi (Command Overview): Document the fact
that the buffer is narrowed around 'pre-command-hook' and
'post-command-hook' when the buffer text includes very long lines.
This commit is contained in:
Gregory Heytings 2023-02-09 01:09:10 +00:00
parent a6cd4553d4
commit 79ce185ad1
9 changed files with 168 additions and 100 deletions

View file

@ -99,6 +99,12 @@ is removed from the hook.
emacs, The GNU Emacs Manual}) runs these two hooks just as a keyboard emacs, The GNU Emacs Manual}) runs these two hooks just as a keyboard
command does. command does.
Note that, when the buffer text includes very long lines, these two
hooks are called as if they were in a @code{with-narrowing} form (see
@ref{Narrowing}), with a
@code{long-line-optimizations-in-command-hooks} label and with the
buffer narrowed to a portion around point.
@node Defining Commands @node Defining Commands
@section Defining Commands @section Defining Commands
@cindex defining commands @cindex defining commands

View file

@ -3501,11 +3501,11 @@ function finishes are the ones that really matter.
For efficiency, we recommend writing these functions so that they For efficiency, we recommend writing these functions so that they
usually assign faces to around 400 to 600 characters at each call. usually assign faces to around 400 to 600 characters at each call.
When the buffer text includes very long lines, these functions are Note that, when the buffer text includes very long lines, these
called with the buffer narrowed to a relatively small region around functions are called as if they were in a @code{with-narrowing} form
@var{pos}, and with narrowing locked, so the functions cannot use (see @ref{Narrowing}), with a
@code{widen} to gain access to the rest of the buffer. @code{long-line-optimizations-in-fontification-functions} label and
@xref{Narrowing}. with the buffer narrowed to a portion around @var{pos}.
@end defvar @end defvar
@node Basic Faces @node Basic Faces

View file

@ -1037,11 +1037,13 @@ positions.
In an interactive call, @var{start} and @var{end} are set to the bounds In an interactive call, @var{start} and @var{end} are set to the bounds
of the current region (point and the mark, with the smallest first). of the current region (point and the mark, with the smallest first).
Note that, in rare circumstances, Emacs may decide to leave, for However, when the narrowing has been set by @code{with-narrowing} with
performance reasons, the accessible portion of the buffer unchanged a label argument (see below), @code{narrow-to-region} can be used only
after a call to @code{narrow-to-region}. This can happen when a Lisp within the limits of that narrowing. If @var{start} or @var{end} are
program is called via low-level hooks, such as outside these limits, the corresponding limit set by
@code{jit-lock-functions}, @code{post-command-hook}, etc. @code{with-narrowing} is used instead. To gain access to other
portions of the buffer, use @code{without-narrowing} with the same
label.
@end deffn @end deffn
@deffn Command narrow-to-page &optional move-count @deffn Command narrow-to-page &optional move-count
@ -1065,13 +1067,13 @@ It is equivalent to the following expression:
@example @example
(narrow-to-region 1 (1+ (buffer-size))) (narrow-to-region 1 (1+ (buffer-size)))
@end example @end example
@end deffn
Note that, in rare circumstances, Emacs may decide to leave, for However, when a narrowing has been set by @code{with-narrowing} with a
performance reasons, the accessible portion of the buffer unchanged label argument (see below), the limits set by @code{with-narrowing}
after a call to @code{widen}. This can happen when a Lisp program is are restored, instead of canceling the narrowing. To gain access to
called via low-level hooks, such as @code{jit-lock-functions}, other portions of the buffer, use @code{without-narrowing} with the
@code{post-command-hook}, etc. same label.
@end deffn
@defun buffer-narrowed-p @defun buffer-narrowed-p
This function returns non-@code{nil} if the buffer is narrowed, and This 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
abnormal exit via @code{throw} or error (@pxref{Nonlocal Exits}). abnormal exit via @code{throw} or error (@pxref{Nonlocal Exits}).
Therefore, this construct is a clean way to narrow a buffer temporarily. Therefore, this construct is a clean way to narrow a buffer temporarily.
This construct also saves and restores the narrowings that were set by
@code{with-narrowing} with a label argument (see below).
The value returned by @code{save-restriction} is that returned by the The value returned by @code{save-restriction} is that returned by the
last form in @var{body}, or @code{nil} if no body forms were given. last form in @var{body}, or @code{nil} if no body forms were given.
@ -1135,3 +1140,58 @@ This is the contents of foo@point{}
@end group @end group
@end example @end example
@end defspec @end defspec
@defspec with-narrowing start end [:label label] body
This special form saves the current bounds of the accessible portion
of the buffer, sets the accessible portion to start at @var{start} and
end at @var{end}, evaluates the @var{body} forms, and restores the
saved bounds. In that case it is equivalent to
@example
(save-restriction
(narrow-to-region start end)
body)
@end example
When the optional @var{label} symbol argument is present however, the
narrowing is labeled. A labeled narrowing differs from a non-labeled
one in several ways:
@itemize @bullet
@item
During the evaluation of the @var{body} form, @code{narrow-to-region}
and @code{widen} can be used only within the @var{start} and @var{end}
limits.
@item
To lift the restriction introduced by @code{with-narrowing} and gain
access to other portions of the buffer, use @code{without-narrowing}
with the same @var{label} argument. (Another way to gain access to
other portions of the buffer is to use an indirect buffer
(@pxref{Indirect Buffers}).)
@item
Labeled narrowings can be nested.
@item
Labeled narrowings can only be used in Lisp programs: they are never
visible on display, and never interfere with narrowings set by the
user.
@end itemize
@end defspec
@defspec without-narrowing [:label label] body
This special form saves the current bounds of the accessible portion
of the buffer, widens the buffer, evaluates the @var{body} forms, and
restores the saved bounds. In that case it is equivalent to
@example
(save-restriction
(widen)
body)
@end example
When the optional @var{label} argument is present however, the
narrowing set by @code{with-narrowing} with the same @var{label}
argument are lifted.
@end defspec

View file

@ -615,8 +615,12 @@ with 'C-x x t', or try disabling all known slow minor modes with
and the major mode with 'M-x so-long-mode', or visit the file with and the major mode with 'M-x so-long-mode', or visit the file with
'M-x find-file-literally' instead of the usual 'C-x C-f'. 'M-x find-file-literally' instead of the usual 'C-x C-f'.
Note that the display optimizations in these cases may cause the In buffers in which these display optimizations are in effect, the
buffer to be occasionally mis-fontified. 'fontification-functions', 'pre-command-hook' and 'post-command-hook'
hooks are executed on a narrowed portion of the buffer, whose size is
controlled by the options 'long-line-optimizations-region-size' and
'long-line-optimizations-bol-search-limit'. This may, in particular,
cause occasional mis-fontifications in these buffers.
The new function 'long-line-optimizations-p' returns non-nil when The new function 'long-line-optimizations-p' returns non-nil when
these optimizations are in effect in the current buffer. these optimizations are in effect in the current buffer.
@ -3814,6 +3818,14 @@ TIMEOUT is the idle time after which to deactivate the transient map.
The default timeout value can be defined by the new variable The default timeout value can be defined by the new variable
'set-transient-map-timeout'. 'set-transient-map-timeout'.
+++
** New forms 'with-narrowing' and 'without-narrowing'.
These forms can be used as enhanced alternatives to the
'save-restriction' form combined with, respectively,
'narrow-to-region' and 'widen'. They also accept an optional label
argument, with which labeled narrowings can be created and lifted.
See the "(elisp) Narrowing" node for details.
** Connection Local Variables ** Connection Local Variables
+++ +++

View file

@ -3946,11 +3946,11 @@ See also `locate-user-emacs-file'.")
The current restrictions, if any, are restored upon return. The current restrictions, if any, are restored upon return.
With the optional :locked TAG argument, inside BODY, When the optional :label LABEL argument is present, in which
`narrow-to-region' and `widen' can be used only within the START LABEL is a symbol, inside BODY, `narrow-to-region' and `widen'
and END limits, unless the restrictions are unlocked by calling can be used only within the START and END limits. To gain access
`narrowing-unlock' with TAG. See `narrowing-lock' for a more to other portions of the buffer, use `without-narrowing' with the
detailed description. same LABEL argument.
\(fn START END [:label LABEL] BODY)" \(fn START END [:label LABEL] BODY)"
(if (eq (car rest) :label) (if (eq (car rest) :label)
@ -3971,6 +3971,10 @@ detailed description.
The current restrictions, if any, are restored upon return. The current restrictions, if any, are restored upon return.
When the optional :label LABEL argument is present, the
restrictions set by `with-narrowing' with the same LABEL argument
are lifted.
\(fn [:label LABEL] BODY)" \(fn [:label LABEL] BODY)"
(if (eq (car rest) :label) (if (eq (car rest) :label)
`(internal--without-narrowing (lambda () ,@(cddr rest)) `(internal--without-narrowing (lambda () ,@(cddr rest))

View file

@ -5918,18 +5918,18 @@ There is no reason to change that value except for debugging purposes. */);
DEFVAR_INT ("long-line-optimizations-region-size", DEFVAR_INT ("long-line-optimizations-region-size",
long_line_optimizations_region_size, long_line_optimizations_region_size,
doc: /* Region size for locked narrowing in buffers with long lines. doc: /* Region size for narrowing in buffers with long lines.
This variable has effect only in buffers which contain one or more This variable has effect only in buffers in which
lines whose length is above `long-line-threshold', which see. For `long-line-optimizations-p' is non-nil. For performance reasons, in
performance reasons, in such buffers, low-level hooks such as such buffers, the `fontification-functions', `pre-command-hook' and
`fontification-functions' or `post-command-hook' are executed on a `post-command-hook' hooks are executed on a narrowed buffer around
narrowed buffer, with a narrowing locked with `narrowing-lock'. This point, as if they were called in a `with-narrowing' form with a label.
variable specifies the size of the narrowed region around point. This variable specifies the size of the narrowed region around point.
To disable that narrowing, set this variable to 0. To disable that narrowing, set this variable to 0.
See also `long-line-locked-narrowing-bol-search-limit'. See also `long-line-optimizations-bol-search-limit'.
There is no reason to change that value except for debugging purposes. */); There is no reason to change that value except for debugging purposes. */);
long_line_optimizations_region_size = 500000; long_line_optimizations_region_size = 500000;
@ -5938,15 +5938,16 @@ There is no reason to change that value except for debugging purposes. */);
long_line_optimizations_bol_search_limit, long_line_optimizations_bol_search_limit,
doc: /* Limit for beginning of line search in buffers with long lines. doc: /* Limit for beginning of line search in buffers with long lines.
This variable has effect only in buffers which contain one or more This variable has effect only in buffers in which
lines whose length is above `long-line-threshold', which see. For `long-line-optimizations-p' is non-nil. For performance reasons, in
performance reasons, in such buffers, low-level hooks such as such buffers, the `fontification-functions', `pre-command-hook' and
`fontification-functions' or `post-command-hook' are executed on a `post-command-hook' hooks are executed on a narrowed buffer around
narrowed buffer, with a narrowing locked with `narrowing-lock'. The point, as if they were called in a `with-narrowing' form with a label.
variable `long-line-locked-narrowing-region-size' specifies the size The variable `long-line-optimizations-region-size' specifies the
of the narrowed region around point. This variable, which should be a size of the narrowed region around point. This variable, which should
small integer, specifies the number of characters by which that region be a small integer, specifies the number of characters by which that
can be extended backwards to make it start at the beginning of a line. region can be extended backwards to make it start at the beginning of
a line.
There is no reason to change that value except for debugging purposes. */); There is no reason to change that value except for debugging purposes. */);
long_line_optimizations_bol_search_limit = 128; long_line_optimizations_bol_search_limit = 128;

View file

@ -2659,7 +2659,11 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
the (uninterned) Qoutermost_narrowing tag and records the narrowing the (uninterned) Qoutermost_narrowing tag and records the narrowing
bounds that were set by the user and that are visible on display. bounds that were set by the user and that are visible on display.
This alist is used internally by narrow-to-region, widen, This alist is used internally by narrow-to-region, widen,
narrowing-lock, narrowing-unlock and save-restriction. */ internal--lock-narrowing, internal--unlock-narrowing and
save-restriction. For efficiency reasons, an alist is used instead
of a buffer-local variable: otherwise reset_outermost_narrowings,
which is called during each redisplay cycle, would have to loop
through all live buffers. */
static Lisp_Object narrowing_locks; static Lisp_Object narrowing_locks;
/* Add BUF with its LOCKS in the narrowing_locks alist. */ /* Add BUF with its LOCKS in the narrowing_locks alist. */
@ -2763,7 +2767,10 @@ unwind_reset_outermost_narrowing (Lisp_Object buf)
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 a locked narrowing is in effect, the locked narrowing will
not be visible on display. */ not be visible on display.
See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#140 and
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=57207#254 for example
recipes that demonstrate why this is necessary. */
void void
reset_outermost_narrowings (void) reset_outermost_narrowings (void)
{ {
@ -2829,10 +2836,12 @@ narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag)
DEFUN ("widen", Fwiden, Swiden, 0, 0, "", DEFUN ("widen", Fwiden, Swiden, 0, 0, "",
doc: /* Remove restrictions (narrowing) from current buffer. doc: /* Remove restrictions (narrowing) from current buffer.
This allows the buffer's full text to be seen and edited, unless This allows the buffer's full text to be seen and edited.
restrictions have been locked with `narrowing-lock', which see, in
which case the narrowing that was current when `narrowing-lock' was However, when restrictions have been set by `with-narrowing' with a
called is restored. */) label, `widen' restores the narrowing limits set by `with-narrowing'.
To gain access to other portions of the buffer, use
`without-narrowing' with the same label. */)
(void) (void)
{ {
Fset (Qoutermost_narrowing, Qnil); Fset (Qoutermost_narrowing, Qnil);
@ -2879,11 +2888,12 @@ When calling from Lisp, pass two arguments START and END:
positions (integers or markers) bounding the text that should positions (integers or markers) bounding the text that should
remain visible. remain visible.
When restrictions have been locked with `narrowing-lock', which see, However, when restrictions have been set by `with-narrowing' with a
`narrow-to-region' can be used only within the limits of the label, `narrow-to-region' can be used only within the limits of these
restrictions that were current when `narrowing-lock' was called. If restrictions. If the START or END arguments are outside these limits,
the START or END arguments are outside these limits, the corresponding the corresponding limit set by `with-narrowing' is used instead of the
limit of the locked restriction is used instead of the argument. */) argument. To gain access to other portions of the buffer, use
`without-narrowing' with the same label. */)
(Lisp_Object start, Lisp_Object end) (Lisp_Object start, Lisp_Object end)
{ {
EMACS_INT s = fix_position (start), e = fix_position (end); EMACS_INT s = fix_position (start), e = fix_position (end);
@ -2912,7 +2922,7 @@ limit of the locked restriction is used instead of the argument. */)
/* 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. It is used
only by narrowing-lock. */ only by internal--lock-narrowing. */
Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing, Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing,
Fpoint_min_marker (), Fpoint_min_marker (),
Fpoint_max_marker ())); Fpoint_max_marker ()));
@ -2934,30 +2944,16 @@ limit of the locked restriction is used instead of the argument. */)
DEFUN ("internal--lock-narrowing", Finternal__lock_narrowing, DEFUN ("internal--lock-narrowing", Finternal__lock_narrowing,
Sinternal__lock_narrowing, 1, 1, 0, Sinternal__lock_narrowing, 1, 1, 0,
doc: /* Lock the current narrowing with TAG. doc: /* Lock the current narrowing with LABEL.
When restrictions are locked, `narrow-to-region' and `widen' can be This is an internal function used by `with-narrowing'. */)
used only within the limits of the restrictions that were current when
`narrowing-lock' was called, unless the lock is removed by calling
`narrowing-unlock' with TAG.
Locking restrictions should be used sparingly, after carefully
considering the potential adverse effects on the code that will be
executed within locked restrictions. It is typically meant to be used
around portions of code that would become too slow, and make Emacs
unresponsive, if they were executed in a large buffer. For example,
restrictions are locked by Emacs around low-level hooks such as
`fontification-functions' or `post-command-hook'.
Locked restrictions are never visible on display, and can therefore
not be used as a stronger variant of normal restrictions. */)
(Lisp_Object tag) (Lisp_Object tag)
{ {
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
Lisp_Object outermost_narrowing Lisp_Object outermost_narrowing
= buffer_local_value (Qoutermost_narrowing, buf); = buffer_local_value (Qoutermost_narrowing, buf);
/* If narrowing-lock is called without being preceded by /* If internal--lock-narrowing is ever called without being preceded
narrow-to-region, do nothing. */ by narrow-to-region, do nothing. */
if (NILP (outermost_narrowing)) if (NILP (outermost_narrowing))
return Qnil; return Qnil;
if (NILP (narrowing_lock_peek_tag (buf))) if (NILP (narrowing_lock_peek_tag (buf)))
@ -2970,15 +2966,9 @@ not be used as a stronger variant of normal restrictions. */)
DEFUN ("internal--unlock-narrowing", Finternal__unlock_narrowing, DEFUN ("internal--unlock-narrowing", Finternal__unlock_narrowing,
Sinternal__unlock_narrowing, 1, 1, 0, Sinternal__unlock_narrowing, 1, 1, 0,
doc: /* Unlock a narrowing locked with (narrowing-lock TAG). doc: /* Unlock a narrowing locked with LABEL.
Unlocking restrictions locked with `narrowing-lock' should be used This is an internal function used by `without-narrowing'. */)
sparingly, after carefully considering the reasons why restrictions
were locked. Restrictions are typically locked around portions of
code that would become too slow, and make Emacs unresponsive, if they
were executed in a large buffer. For example, restrictions are locked
by Emacs around low-level hooks such as `fontification-functions' or
`post-command-hook'. */)
(Lisp_Object tag) (Lisp_Object tag)
{ {
Lisp_Object buf = Fcurrent_buffer (); Lisp_Object buf = Fcurrent_buffer ();
@ -3085,8 +3075,8 @@ DEFUN ("save-restriction", Fsave_restriction, Ssave_restriction, 0, UNEVALLED, 0
The buffer's restrictions make parts of the beginning and end invisible. The buffer's restrictions make parts of the beginning and end invisible.
\(They are set up with `narrow-to-region' and eliminated with `widen'.) \(They are set up with `narrow-to-region' and eliminated with `widen'.)
This special form, `save-restriction', saves the current buffer's This special form, `save-restriction', saves the current buffer's
restrictions, as well as their locks if they have been locked with restrictions, including those that were set by `with-narrowing' with a
`narrowing-lock', when it is entered, and restores them when it is exited. label argument, when it is entered, and restores them when it is exited.
So any `narrow-to-region' within BODY lasts only until the end of the form. So any `narrow-to-region' within BODY lasts only until the end of the form.
The old restrictions settings are restored even in case of abnormal exit The old restrictions settings are restored even in case of abnormal exit
\(throw or error). \(throw or error).

View file

@ -12731,13 +12731,11 @@ If an unhandled error happens in running this hook, the function in
which the error occurred is unconditionally removed, since otherwise which the error occurred is unconditionally removed, since otherwise
the error might happen repeatedly and make Emacs nonfunctional. the error might happen repeatedly and make Emacs nonfunctional.
Note that, when the current buffer contains one or more lines whose Note that, when `long-line-optimizations-p' is non-nil in the buffer,
length is above `long-line-threshold', these hook functions are called these functions are called as if they were in a `with-narrowing' form,
with the buffer narrowed to a small portion around point (whose size with a `long-line-optimizations-in-command-hooks' label and with the
is specified by `long-line-locked-narrowing-region-size'), and the buffer narrowed to a portion around point whose size is specified by
narrowing is locked (see `narrowing-lock'), so that these hook `long-line-optimizations-region-size'.
functions cannot use `widen' to gain access to other portions of
buffer text.
See also `post-command-hook'. */); See also `post-command-hook'. */);
Vpre_command_hook = Qnil; Vpre_command_hook = Qnil;
@ -12753,13 +12751,11 @@ It is a bad idea to use this hook for expensive processing. If
unavoidable, wrap your code in `(while-no-input (redisplay) CODE)' to unavoidable, wrap your code in `(while-no-input (redisplay) CODE)' to
avoid making Emacs unresponsive while the user types. avoid making Emacs unresponsive while the user types.
Note that, when the current buffer contains one or more lines whose Note that, when `long-line-optimizations-p' is non-nil in the buffer,
length is above `long-line-threshold', these hook functions are called these functions are called as if they were in a `with-narrowing' form,
with the buffer narrowed to a small portion around point (whose size with a `long-line-optimizations-in-command-hooks' label and with the
is specified by `long-line-locked-narrowing-region-size'), and the buffer narrowed to a portion around point whose size is specified by
narrowing is locked (see `narrowing-lock'), so that these hook `long-line-optimizations-region-size'.
functions cannot use `widen' to gain access to other portions of
buffer text.
See also `pre-command-hook'. */); See also `pre-command-hook'. */);
Vpost_command_hook = Qnil; Vpost_command_hook = Qnil;

View file

@ -36777,12 +36777,11 @@ Each function is called with one argument POS. Functions must
fontify a region starting at POS in the current buffer, and give fontify a region starting at POS in the current buffer, and give
fontified regions the property `fontified' with a non-nil value. fontified regions the property `fontified' with a non-nil value.
Note that, when the buffer contains one or more lines whose length is Note that, when `long-line-optimizations-p' is non-nil in the buffer,
above `long-line-threshold', these functions are called with the these functions are called as if they were in a `with-narrowing' form,
buffer narrowed to a small portion around POS (whose size is specified with a `long-line-optimizations-in-fontification-functions' label and
by `long-line-locked-narrowing-region-size'), and the narrowing is with the buffer narrowed to a portion around POS whose size is
locked (see `narrowing-lock'), so that these functions cannot use specified by `long-line-optimizations-region-size'. */);
`widen' to gain access to other portions of buffer text. */);
Vfontification_functions = Qnil; Vfontification_functions = Qnil;
Fmake_variable_buffer_local (Qfontification_functions); Fmake_variable_buffer_local (Qfontification_functions);