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
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
@section 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
usually assign faces to around 400 to 600 characters at each call.
When the buffer text includes very long lines, these functions are
called with the buffer narrowed to a relatively small region around
@var{pos}, and with narrowing locked, so the functions cannot use
@code{widen} to gain access to the rest of the buffer.
@xref{Narrowing}.
Note that, when the buffer text includes very long lines, these
functions are called as if they were in a @code{with-narrowing} form
(see @ref{Narrowing}), with a
@code{long-line-optimizations-in-fontification-functions} label and
with the buffer narrowed to a portion around @var{pos}.
@end defvar
@node Basic Faces

View file

@ -1037,11 +1037,13 @@ positions.
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).
Note that, in rare circumstances, Emacs may decide to leave, for
performance reasons, the accessible portion of the buffer unchanged
after a call to @code{narrow-to-region}. This can happen when a Lisp
program is called via low-level hooks, such as
@code{jit-lock-functions}, @code{post-command-hook}, etc.
However, when the narrowing has been set by @code{with-narrowing} with
a label argument (see below), @code{narrow-to-region} can be used only
within the limits of that narrowing. If @var{start} or @var{end} are
outside these limits, the corresponding limit set by
@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
@deffn Command narrow-to-page &optional move-count
@ -1065,13 +1067,13 @@ It is equivalent to the following expression:
@example
(narrow-to-region 1 (1+ (buffer-size)))
@end example
@end deffn
Note that, in rare circumstances, Emacs may decide to leave, for
performance reasons, the accessible portion of the buffer unchanged
after a call to @code{widen}. This can happen when a Lisp program is
called via low-level hooks, such as @code{jit-lock-functions},
@code{post-command-hook}, etc.
However, when a narrowing has been set by @code{with-narrowing} with a
label argument (see below), the limits set by @code{with-narrowing}
are restored, instead of canceling the narrowing. To gain access to
other portions of the buffer, use @code{without-narrowing} with the
same label.
@end deffn
@defun buffer-narrowed-p
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}).
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
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 example
@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
'M-x find-file-literally' instead of the usual 'C-x C-f'.
Note that the display optimizations in these cases may cause the
buffer to be occasionally mis-fontified.
In buffers in which these display optimizations are in effect, the
'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
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
'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
+++

View file

@ -3946,11 +3946,11 @@ See also `locate-user-emacs-file'.")
The current restrictions, if any, are restored upon return.
With the optional :locked TAG argument, inside BODY,
`narrow-to-region' and `widen' can be used only within the START
and END limits, unless the restrictions are unlocked by calling
`narrowing-unlock' with TAG. See `narrowing-lock' for a more
detailed description.
When the optional :label LABEL argument is present, in which
LABEL is a symbol, inside BODY, `narrow-to-region' and `widen'
can be used only within the START and END limits. To gain access
to other portions of the buffer, use `without-narrowing' with the
same LABEL argument.
\(fn START END [:label LABEL] BODY)"
(if (eq (car rest) :label)
@ -3971,6 +3971,10 @@ detailed description.
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)"
(if (eq (car rest) :label)
`(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",
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
lines whose length is above `long-line-threshold', which see. For
performance reasons, in such buffers, low-level hooks such as
`fontification-functions' or `post-command-hook' are executed on a
narrowed buffer, with a narrowing locked with `narrowing-lock'. This
variable specifies the size of the narrowed region around point.
This variable has effect only in buffers in which
`long-line-optimizations-p' is non-nil. For performance reasons, in
such buffers, the `fontification-functions', `pre-command-hook' and
`post-command-hook' hooks are executed on a narrowed buffer around
point, as if they were called in a `with-narrowing' form with a label.
This variable specifies the size of the narrowed region around point.
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. */);
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,
doc: /* Limit for beginning of line search in buffers with long lines.
This variable has effect only in buffers which contain one or more
lines whose length is above `long-line-threshold', which see. For
performance reasons, in such buffers, low-level hooks such as
`fontification-functions' or `post-command-hook' are executed on a
narrowed buffer, with a narrowing locked with `narrowing-lock'. The
variable `long-line-locked-narrowing-region-size' specifies the size
of the narrowed region around point. This variable, which should be a
small integer, specifies the number of characters by which that region
can be extended backwards to make it start at the beginning of a line.
This variable has effect only in buffers in which
`long-line-optimizations-p' is non-nil. For performance reasons, in
such buffers, the `fontification-functions', `pre-command-hook' and
`post-command-hook' hooks are executed on a narrowed buffer around
point, as if they were called in a `with-narrowing' form with a label.
The variable `long-line-optimizations-region-size' specifies the
size of the narrowed region around point. This variable, which should
be a small integer, specifies the number of characters by which that
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. */);
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
bounds that were set by the user and that are visible on display.
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;
/* 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
that if a Lisp function executed during redisplay calls (redisplay)
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
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, "",
doc: /* Remove restrictions (narrowing) from current buffer.
This allows the buffer's full text to be seen and edited, unless
restrictions have been locked with `narrowing-lock', which see, in
which case the narrowing that was current when `narrowing-lock' was
called is restored. */)
This allows the buffer's full text to be seen and edited.
However, when restrictions have been set by `with-narrowing' with a
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)
{
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
remain visible.
When restrictions have been locked with `narrowing-lock', which see,
`narrow-to-region' can be used only within the limits of the
restrictions that were current when `narrowing-lock' was called. If
the START or END arguments are outside these limits, the corresponding
limit of the locked restriction is used instead of the argument. */)
However, when restrictions have been set by `with-narrowing' with a
label, `narrow-to-region' can be used only within the limits of these
restrictions. If the START or END arguments are outside these limits,
the corresponding limit set by `with-narrowing' is used instead of the
argument. To gain access to other portions of the buffer, use
`without-narrowing' with the same label. */)
(Lisp_Object start, Lisp_Object 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
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,
Fpoint_min_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,
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
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. */)
This is an internal function used by `with-narrowing'. */)
(Lisp_Object tag)
{
Lisp_Object buf = Fcurrent_buffer ();
Lisp_Object outermost_narrowing
= buffer_local_value (Qoutermost_narrowing, buf);
/* If narrowing-lock is called without being preceded by
narrow-to-region, do nothing. */
/* If internal--lock-narrowing is ever called without being preceded
by narrow-to-region, do nothing. */
if (NILP (outermost_narrowing))
return Qnil;
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,
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
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'. */)
This is an internal function used by `without-narrowing'. */)
(Lisp_Object tag)
{
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.
\(They are set up with `narrow-to-region' and eliminated with `widen'.)
This special form, `save-restriction', saves the current buffer's
restrictions, as well as their locks if they have been locked with
`narrowing-lock', when it is entered, and restores them when it is exited.
restrictions, including those that were set by `with-narrowing' with a
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.
The old restrictions settings are restored even in case of abnormal exit
\(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
the error might happen repeatedly and make Emacs nonfunctional.
Note that, when the current buffer contains one or more lines whose
length is above `long-line-threshold', these hook functions are called
with the buffer narrowed to a small portion around point (whose size
is specified by `long-line-locked-narrowing-region-size'), and the
narrowing is locked (see `narrowing-lock'), so that these hook
functions cannot use `widen' to gain access to other portions of
buffer text.
Note that, when `long-line-optimizations-p' is non-nil in the buffer,
these functions are called as if they were in a `with-narrowing' form,
with a `long-line-optimizations-in-command-hooks' label and with the
buffer narrowed to a portion around point whose size is specified by
`long-line-optimizations-region-size'.
See also `post-command-hook'. */);
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
avoid making Emacs unresponsive while the user types.
Note that, when the current buffer contains one or more lines whose
length is above `long-line-threshold', these hook functions are called
with the buffer narrowed to a small portion around point (whose size
is specified by `long-line-locked-narrowing-region-size'), and the
narrowing is locked (see `narrowing-lock'), so that these hook
functions cannot use `widen' to gain access to other portions of
buffer text.
Note that, when `long-line-optimizations-p' is non-nil in the buffer,
these functions are called as if they were in a `with-narrowing' form,
with a `long-line-optimizations-in-command-hooks' label and with the
buffer narrowed to a portion around point whose size is specified by
`long-line-optimizations-region-size'.
See also `pre-command-hook'. */);
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
fontified regions the property `fontified' with a non-nil value.
Note that, when the buffer contains one or more lines whose length is
above `long-line-threshold', these functions are called with the
buffer narrowed to a small portion around POS (whose size is specified
by `long-line-locked-narrowing-region-size'), and the narrowing is
locked (see `narrowing-lock'), so that these functions cannot use
`widen' to gain access to other portions of buffer text. */);
Note that, when `long-line-optimizations-p' is non-nil in the buffer,
these functions are called as if they were in a `with-narrowing' form,
with a `long-line-optimizations-in-fontification-functions' label and
with the buffer narrowed to a portion around POS whose size is
specified by `long-line-optimizations-region-size'. */);
Vfontification_functions = Qnil;
Fmake_variable_buffer_local (Qfontification_functions);