From 79ce185ad1373845781646812638d4872b8aee69 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Thu, 9 Feb 2023 01:09:10 +0000 Subject: [PATCH] Update the documentation about labeled (locked) narrowing * src/xdisp.c (syms_of_xdisp) : Update docstring. * src/keyboard.c (syms_of_keyboard) : (syms_of_keyboard) : 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) : Update docstring. (syms_of_buffer) : 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. --- doc/lispref/commands.texi | 6 +++ doc/lispref/display.texi | 10 ++--- doc/lispref/positions.texi | 82 +++++++++++++++++++++++++++++++++----- etc/NEWS | 16 +++++++- lisp/subr.el | 14 ++++--- src/buffer.c | 35 ++++++++-------- src/editfns.c | 74 +++++++++++++++------------------- src/keyboard.c | 24 +++++------ src/xdisp.c | 11 +++-- 9 files changed, 170 insertions(+), 102 deletions(-) diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index dc78adc4520..be34027d21d 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -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 diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index c5374e1481a..1b7ef006634 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -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 diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi index f3824436246..e7d5c610d67 100644 --- a/doc/lispref/positions.texi +++ b/doc/lispref/positions.texi @@ -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 diff --git a/etc/NEWS b/etc/NEWS index 2d15e39036a..01e7f2cfb09 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -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 +++ diff --git a/lisp/subr.el b/lisp/subr.el index af3f1f1abd5..7ed0cb02a70 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -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)) diff --git a/src/buffer.c b/src/buffer.c index 07723a7c6ff..755061d0dee 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -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; diff --git a/src/editfns.c b/src/editfns.c index e1c57502805..64906671be7 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -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. - -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. */) + doc: /* Lock the current narrowing with LABEL. + +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). - -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'. */) + doc: /* Unlock a narrowing locked with LABEL. + +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). diff --git a/src/keyboard.c b/src/keyboard.c index 4417aa97d28..1d0b907bd8e 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -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; diff --git a/src/xdisp.c b/src/xdisp.c index 8034b20d5f8..1450b869d20 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -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); -- 2.39.2