emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/improved-locked-narrowing 9dee6df39c: Reworked locked narrowing.


From: Gregory Heytings
Subject: feature/improved-locked-narrowing 9dee6df39c: Reworked locked narrowing.
Date: Fri, 25 Nov 2022 12:52:09 -0500 (EST)

branch: feature/improved-locked-narrowing
commit 9dee6df39cd14be78ff96cb24169842f4772488a
Author: Gregory Heytings <gregory@heytings.org>
Commit: Gregory Heytings <gregory@heytings.org>

    Reworked locked narrowing.
    
    * src/editfns.c: (narrowing_locks): New alist to hold the narrowing
    locks and their buffers.
    (narrowing_lock_get_bound, narrowing_lock_peek_tag)
    (narrowing_lock_push, narrowing_lock_pop): New functions to access
    and update 'narrowing_locks'.
    (reset_outermost_narrowings, unwind_reset_outermost_narrowing):
    Functions moved from src/xdisp.c, and rewritten with the above
    functions.
    (Fwiden): Use the above functions. Update docstring.
    (Fnarrow_to_region, Fnarrowing_lock, Fnarrowing_unlock): Use the above
    functions.
    (syms_of_editfns): Remove the 'narrowing-locks' variable.
    
    * src/lisp.h: Make 'reset_outermost_narrowings' externally visible.
    
    * src/xdisp.c (reset_outermost_narrowings)
    unwind_reset_outermost_narrowing): Functions moved to src/editfns.c.
    
    * lisp/subr.el (with-locked-narrowing): Improved macro, with a helper
    function.
---
 lisp/subr.el  |  19 +++---
 src/editfns.c | 212 +++++++++++++++++++++++++++++++++++++++++++++-------------
 src/lisp.h    |   1 +
 src/xdisp.c   |  34 ----------
 4 files changed, 179 insertions(+), 87 deletions(-)

diff --git a/lisp/subr.el b/lisp/subr.el
index 7dd8ff2081..196e7f881b 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3943,14 +3943,17 @@ 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.  The current
 restrictions, if any, are restored upon return."
-  `(save-restriction
-     (unwind-protect
-         (progn
-           (narrow-to-region ,start ,end)
-           (narrowing-lock ,tag)
-           ,@body)
-       (narrowing-unlock ,tag)
-       (widen))))
+  `(with-locked-narrowing-1 ,start ,end ,tag (lambda () ,@body)))
+
+(defun with-locked-narrowing-1 (start end tag body)
+  "Helper function for `with-locked-narrowing', which see."
+  (save-restriction
+    (unwind-protect
+        (progn
+           (narrow-to-region start end)
+           (narrowing-lock tag)
+           (funcall body))
+       (narrowing-unlock tag))))
 
 (defun find-tag-default-bounds ()
   "Determine the boundaries of the default tag, based on text at point.
diff --git a/src/editfns.c b/src/editfns.c
index c7cc63d8d3..9c81d9c723 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -2653,18 +2653,144 @@ DEFUN ("delete-and-extract-region", 
Fdelete_and_extract_region,
   return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1);
 }
 
+/* Alist of buffers in which locked narrowing is used.  The car of
+   each list element is a buffer, the cdr is a list of triplets (tag
+   begv-marker zv-marker).  The last element of that list always uses
+   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 and narrowing-unlock.  */
+static Lisp_Object narrowing_locks;
+
+/* Retrieve one of the BEGV/ZV bounds of a narrowing in BUF from the
+   narrowing_locks alist.  When OUTERMOST is true, the bounds that
+   were set by the user and that are visible on display are returned.
+   Otherwise the innermost locked narrowing bounds are returned.  */
+static ptrdiff_t
+narrowing_lock_get_bound (Lisp_Object buf, bool begv, bool outermost)
+{
+  if (NILP (Fbuffer_live_p (buf)))
+    return 0;
+  Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
+  if (NILP (buffer_locks))
+    return 0;
+  buffer_locks = Fcar (Fcdr (buffer_locks));
+  Lisp_Object bounds
+    = outermost
+      ? Fcdr (assq_no_quit (Qoutermost_narrowing, buffer_locks))
+      : Fcdr (Fcar (buffer_locks));
+  eassert (! NILP (bounds));
+  Lisp_Object marker = begv ? Fcar (bounds) : Fcar (Fcdr (bounds));
+  eassert (MARKERP (marker));
+  Lisp_Object pos = Fmarker_position (marker);
+  eassert (! NILP (pos));
+  return XFIXNUM (pos);
+}
+
+/* Retrieve the tag of the innermost narrowing in BUF.  */
+static Lisp_Object
+narrowing_lock_peek_tag (Lisp_Object buf)
+{
+  if (NILP (Fbuffer_live_p (buf)))
+    return Qnil;
+  Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
+  if (NILP (buffer_locks))
+    return Qnil;
+  Lisp_Object tag = Fcar (Fcar (Fcar (Fcdr (buffer_locks))));
+  eassert (! NILP (tag));
+  return tag;
+}
+
+/* Add a LOCK in BUF in the narrowing_locks alist.  */
+static void
+narrowing_lock_push (Lisp_Object buf, Lisp_Object lock)
+{
+  Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
+  if (NILP (buffer_locks))
+    narrowing_locks = nconc2 (list1 (list2 (buf, list1 (lock))),
+                             narrowing_locks);
+  else
+    Fsetcdr (buffer_locks, list1 (nconc2 (list1 (lock),
+                                         Fcar (Fcdr (buffer_locks)))));
+}
+
+/* Remove the innermost lock in BUF from the narrowing_lock alist.  */
+static void
+narrowing_lock_pop (Lisp_Object buf)
+{
+  Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks);
+  eassert (! NILP (buffer_locks));
+  if (EQ (narrowing_lock_peek_tag (buf), Qoutermost_narrowing))
+    narrowing_locks = Fdelq (Fassoc (buf, narrowing_locks, Qnil),
+                            narrowing_locks);
+  else
+    Fsetcdr (buffer_locks, list1 (Fcdr (Fcar (Fcdr (buffer_locks)))));
+}
+
+static void
+unwind_reset_outermost_narrowing (Lisp_Object buf)
+{
+  ptrdiff_t begv, zv;
+  begv = narrowing_lock_get_bound (buf, true, false);
+  zv = narrowing_lock_get_bound (buf, false, false);
+  if (begv && zv)
+    {
+      SET_BUF_BEGV (XBUFFER (buf), begv);
+      SET_BUF_ZV (XBUFFER (buf), zv);
+    }
+}
+
+/* When redisplay is called in a function executed while a locked
+   narrowing is in effect, restore the narrowing bounds that were set
+   by the user, and restore the bounds of the locked narrowing when
+   returning from redisplay.  */
+void
+reset_outermost_narrowings (void)
+{
+  Lisp_Object val, buf;
+  for (val = narrowing_locks; CONSP (val); val = XCDR (val))
+    {
+      buf = Fcar (Fcar (val));
+      eassert (BUFFERP (buf));
+      ptrdiff_t begv = narrowing_lock_get_bound (buf, true, true);
+      ptrdiff_t zv = narrowing_lock_get_bound (buf, false, true);
+      SET_BUF_BEGV (XBUFFER (buf), begv);
+      SET_BUF_ZV (XBUFFER (buf), zv);
+      record_unwind_protect (unwind_reset_outermost_narrowing, buf);
+    }
+}
+
+static void
+unwind_narrow_to_region_locked (Lisp_Object tag)
+{
+  Fnarrowing_unlock (tag);
+  Fwiden ();
+}
+
+/* Narrow current_buffer to BEGV-ZV with a locked narrowing */
+void
+narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag)
+{
+  Fnarrow_to_region (begv, zv);
+  Fnarrowing_lock (tag);
+  record_unwind_protect (restore_point_unwind, Fpoint_marker ());
+  record_unwind_protect (unwind_narrow_to_region_locked, 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 restrictions that were current when `narrowing-lock'
-was called are restored.  */)
+which case the narrowing that was current when `narrowing-lock' was
+called is restored.  */)
   (void)
 {
   Fset (Qoutermost_narrowing, Qnil);
+  Lisp_Object buf = Fcurrent_buffer ();
+  Lisp_Object tag = narrowing_lock_peek_tag (buf);
 
-  if (NILP (Vnarrowing_locks))
+  if (NILP (tag))
     {
       if (BEG != BEGV || Z != ZV)
        current_buffer->clip_changed = 1;
@@ -2674,14 +2800,18 @@ was called are restored.  */)
     }
   else
     {
-      ptrdiff_t begv = XFIXNUM (Fcar (Fcdr (Fcar (Vnarrowing_locks))));
-      ptrdiff_t zv = XFIXNUM (Fcdr (Fcdr (Fcar (Vnarrowing_locks))));
+      ptrdiff_t begv = narrowing_lock_get_bound (buf, true, false);
+      ptrdiff_t zv = narrowing_lock_get_bound (buf, false, false);
       if (begv != BEGV || zv != ZV)
        current_buffer->clip_changed = 1;
       SET_BUF_BEGV (current_buffer, begv);
       SET_BUF_ZV (current_buffer, zv);
-      if (EQ (Fcar (Fcar (Vnarrowing_locks)), Qoutermost_narrowing))
-       Fset (Qnarrowing_locks, Qnil);
+      /* If the only remaining bounds in narrowing_locks for
+        current_buffer are the bounds that were set by the user, no
+        locked narrowing is in effect in current_buffer anymore:
+        remove it from the narrowing_locks alist.  */
+      if (EQ (tag, Qoutermost_narrowing))
+       narrowing_lock_pop (buf);
     }
   /* Changing the buffer bounds invalidates any recorded current column.  */
   invalidate_current_column ();
@@ -2716,20 +2846,25 @@ limit of the locked restriction is used instead of the 
argument.  */)
   if (!(BEG <= s && s <= e && e <= Z))
     args_out_of_range (start, end);
 
-  if (! NILP (Vnarrowing_locks))
+  Lisp_Object buf = Fcurrent_buffer ();
+  if (! NILP (narrowing_lock_peek_tag (buf)))
     {
-      ptrdiff_t begv = XFIXNUM (Fcar (Fcdr (Fcar (Vnarrowing_locks))));
-      ptrdiff_t zv = XFIXNUM (Fcdr (Fcdr (Fcar (Vnarrowing_locks))));
+      ptrdiff_t begv = narrowing_lock_get_bound (buf, true, false);
+      ptrdiff_t zv = narrowing_lock_get_bound (buf, false, false);
+      /* Limit the start and end positions to those of the locked
+        narrowing.  */
       if (s < begv) s = begv;
       if (s > zv) s = zv;
       if (e < begv) e = begv;
       if (e > zv) e = zv;
     }
 
-  Fset (Qoutermost_narrowing,
-       Fcons (Fcons (Qoutermost_narrowing,
-                     Fcons (make_fixnum (BEGV), make_fixnum (ZV))),
-              Qnil));
+  /* 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.  */
+  Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing,
+                                    Fpoint_min_marker (),
+                                    Fpoint_max_marker ()));
 
   if (BEGV != s || ZV != e)
     current_buffer->clip_changed = 1;
@@ -2766,11 +2901,18 @@ Locked restrictions are never visible on display, and 
can therefore
 not be used as a stronger variant of normal restrictions.  */)
   (Lisp_Object tag)
 {
-  if (NILP (Vnarrowing_locks))
-    Fset (Qnarrowing_locks, Voutermost_narrowing);
-  Fset (Qnarrowing_locks,
-       Fcons (Fcons (tag, Fcons (make_fixnum (BEGV), make_fixnum (ZV))),
-              Vnarrowing_locks));
+  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 (NILP (outermost_narrowing))
+    return Qnil;
+  if (NILP (narrowing_lock_peek_tag (buf)))
+    narrowing_lock_push (buf, outermost_narrowing);
+  narrowing_lock_push (buf, list3 (tag,
+                                  Fpoint_min_marker (),
+                                  Fpoint_max_marker ()));
   return Qnil;
 }
 
@@ -2786,27 +2928,12 @@ by Emacs around low-level hooks such as 
`fontification-functions' or
 `post-command-hook'.  */)
   (Lisp_Object tag)
 {
-  if (EQ (Fcar (Fcar (Vnarrowing_locks)), tag))
-    Fset (Qnarrowing_locks, Fcdr (Vnarrowing_locks));
+  Lisp_Object buf = Fcurrent_buffer ();
+  if (EQ (narrowing_lock_peek_tag (buf), tag))
+    narrowing_lock_pop (buf);
   return Qnil;
 }
 
-static void
-unwind_narrow_to_region_locked (Lisp_Object tag)
-{
-  Fnarrowing_unlock (tag);
-  Fwiden ();
-}
-
-void
-narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag)
-{
-  Fnarrow_to_region (begv, zv);
-  Fnarrowing_lock (tag);
-  record_unwind_protect (restore_point_unwind, Fpoint_marker ());
-  record_unwind_protect (unwind_narrow_to_region_locked, tag);
-}
-
 Lisp_Object
 save_restriction_save (void)
 {
@@ -4564,6 +4691,8 @@ syms_of_editfns (void)
   DEFSYM (Qwall, "wall");
   DEFSYM (Qpropertize, "propertize");
 
+  staticpro (&narrowing_locks);
+
   DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
               doc: /* Non-nil means text motion commands don't notice fields.  
*/);
   Vinhibit_field_text_motion = Qnil;
@@ -4623,18 +4752,11 @@ This variable is experimental; email 
32252@debbugs.gnu.org if you need
 it to be non-nil.  */);
   binary_as_unsigned = false;
 
-  DEFSYM (Qnarrowing_locks, "narrowing-locks");
-  DEFVAR_LISP ("narrowing-locks", Vnarrowing_locks,
-              doc: /* List of narrowing locks in the current buffer.  Internal 
use only.  */);
-  Vnarrowing_locks = Qnil;
-  Fmake_variable_buffer_local (Qnarrowing_locks);
-  Funintern (Qnarrowing_locks, Qnil);
-
-  DEFSYM (Qoutermost_narrowing, "outermost-narrowing");
   DEFVAR_LISP ("outermost-narrowing", Voutermost_narrowing,
               doc: /* Outermost narrowing bounds, if any.  Internal use only.  
*/);
   Voutermost_narrowing = Qnil;
   Fmake_variable_buffer_local (Qoutermost_narrowing);
+  DEFSYM (Qoutermost_narrowing, "outermost-narrowing");
   Funintern (Qoutermost_narrowing, Qnil);
 
   defsubr (&Spropertize);
diff --git a/src/lisp.h b/src/lisp.h
index 8a5b8dad83..373aee2287 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4683,6 +4683,7 @@ extern Lisp_Object make_buffer_string (ptrdiff_t, 
ptrdiff_t, bool);
 extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
                                            ptrdiff_t, bool);
 extern void narrow_to_region_locked (Lisp_Object, Lisp_Object, Lisp_Object);
+extern void reset_outermost_narrowings (void);
 extern void init_editfns (void);
 extern void syms_of_editfns (void);
 
diff --git a/src/xdisp.c b/src/xdisp.c
index fa5ce84b1c..658ce57b7e 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -16266,40 +16266,6 @@ do { if (! polling_stopped_here) stop_polling ();      
\
 do { if (polling_stopped_here) start_polling ();       \
        polling_stopped_here = false; } while (false)
 
-static void
-unwind_reset_outermost_narrowing (Lisp_Object buf)
-{
-  Lisp_Object innermost_narrowing =
-    Fcar (buffer_local_value (Qnarrowing_locks, buf));
-  if (! NILP (innermost_narrowing))
-    {
-      SET_BUF_BEGV (XBUFFER (buf),
-                   XFIXNUM (Fcar (Fcdr (innermost_narrowing))));
-      SET_BUF_ZV (XBUFFER (buf),
-                 XFIXNUM (Fcdr (Fcdr (innermost_narrowing))));
-    }
-}
-
-static void
-reset_outermost_narrowings (void)
-{
-  Lisp_Object tail, buf, outermost_narrowing;
-  FOR_EACH_LIVE_BUFFER (tail, buf)
-    {
-      outermost_narrowing =
-       Fassq (Qoutermost_narrowing,
-              buffer_local_value (Qnarrowing_locks, buf));
-      if (!NILP (outermost_narrowing))
-       {
-         SET_BUF_BEGV (XBUFFER (buf),
-                       XFIXNUM (Fcar (Fcdr (outermost_narrowing))));
-         SET_BUF_ZV (XBUFFER (buf),
-                     XFIXNUM (Fcdr (Fcdr (outermost_narrowing))));
-         record_unwind_protect (unwind_reset_outermost_narrowing, buf);
-       }
-    }
-}
-
 /* Perhaps in the future avoid recentering windows if it
    is not necessary; currently that causes some problems.  */
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]