Subject: Re: bug#459: Zero-length overlays, overlay keymaps, and `overlays-at'

From: Lars Ingebrigtsen <[email-masked]>
Date: Tue, 20 Jul 2021 13:28:10 +0200

Eli Zaretskii <[email-masked]> writes:

> No, please don't make that the default behavior.  It makes no sense to
> change the default behavior of such a veteran API for the benefit of a
> rare use case.  The gains are very small, and don't justify the risks.
> An optional argument gets us the best of both worlds, so it's a clear
> winner.

I was just hedging in case somebody came up with a brilliant reason for
including them by default.  :-)

However...  after implementing this, I see that `overlays-in' is
something that exists?  And does include zero-length overlays?  *sigh*
So `(overlays-in 1 1)' is the answer to this bug report.

I've included the patch that won't be applied below for reference.
Instead I'll just mention `overlays-in' in the `overlays-at' doc string.

diff --git a/src/buffer.c b/src/buffer.c
index d3a5ffd149..93f3e3dbb6 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2859,7 +2859,8 @@ DEFUN ("kill-all-local-variables", Fkill_all_local_variables,
 ptrdiff_t
 overlays_at (EMACS_INT pos, bool extend, Lisp_Object **vec_ptr,
 	     ptrdiff_t *len_ptr,
-	     ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr, bool change_req)
+	     ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr, bool change_req,
+	     bool include_zero_length)
 {
   ptrdiff_t idx = 0;
   ptrdiff_t len = *len_ptr;
@@ -2886,7 +2887,8 @@ overlays_at (EMACS_INT pos, bool extend, Lisp_Object **vec_ptr,
 	 so its start counts for PREV_PTR if it's before POS.  */
       if (prev < startpos && startpos < pos)
 	prev = startpos;
-      if (endpos == pos)
+      if (endpos == pos
+	  && (!include_zero_length || endpos != startpos))
 	continue;
       if (startpos <= pos)
 	{
@@ -2928,7 +2930,8 @@ overlays_at (EMACS_INT pos, bool extend, Lisp_Object **vec_ptr,
 	  break;
 	}
       ptrdiff_t endpos = OVERLAY_POSITION (end);
-      if (pos < endpos)
+      if (pos < endpos
+	  || (include_zero_length && pos == endpos && startpos == endpos))
 	{
 	  if (idx == len)
 	    {
@@ -4220,10 +4223,13 @@ DEFUN ("overlay-properties", Foverlay_properties, Soverlay_properties, 1, 1, 0,
 }
 
 
-DEFUN ("overlays-at", Foverlays_at, Soverlays_at, 1, 2, 0,
+DEFUN ("overlays-at", Foverlays_at, Soverlays_at, 1, 3, 0,
        doc: /* Return a list of the overlays that contain the character at POS.
-If SORTED is non-nil, then sort them by decreasing priority.  */)
-  (Lisp_Object pos, Lisp_Object sorted)
+If SORTED is non-nil, then sort them by decreasing priority.
+
+If INCLUDE-ZERO-LENGTH is non-nil, also include zero-length overlays
+at POS.  (These are otherwise excluded.)  */)
+  (Lisp_Object pos, Lisp_Object sorted, Lisp_Object include_zero_length)
 {
   ptrdiff_t len, noverlays;
   Lisp_Object *overlay_vec;
@@ -4241,7 +4247,7 @@ DEFUN ("overlays-at", Foverlays_at, Soverlays_at, 1, 2, 0,
   /* Put all the overlays we want in a vector in overlay_vec.
      Store the length in len.  */
   noverlays = overlays_at (XFIXNUM (pos), 1, &overlay_vec, &len,
-			   NULL, NULL, 0);
+			   NULL, NULL, 0, !NILP (include_zero_length));
 
   if (!NILP (sorted))
     noverlays = sort_overlays (overlay_vec, noverlays,
@@ -4317,7 +4323,7 @@ DEFUN ("next-overlay-change", Fnext_overlay_change, Snext_overlay_change,
      Store the length in len.
      endpos gets the position where the next overlay starts.  */
   noverlays = overlays_at (XFIXNUM (pos), 1, &overlay_vec, &len,
-			   &endpos, 0, 1);
+			   &endpos, 0, 1, false);
 
   /* If any of these overlays ends before endpos,
      use its ending point instead.  */
@@ -4364,7 +4370,7 @@ DEFUN ("previous-overlay-change", Fprevious_overlay_change,
      Store the length in len.
      prevpos gets the position of the previous change.  */
   overlays_at (XFIXNUM (pos), 1, &overlay_vec, &len,
-	       0, &prevpos, 1);
+	       0, &prevpos, 1, false);
 
   xfree (overlay_vec);
   return make_fixnum (prevpos);
diff --git a/src/buffer.h b/src/buffer.h
index 24e9c3fcbc..daa666248d 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1145,7 +1145,8 @@ #define CHECK_FIXNUM_COERCE_MARKER(x) ((x) = make_fixnum (fix_position (x)))
 extern void compact_buffer (struct buffer *);
 extern void evaporate_overlays (ptrdiff_t);
 extern ptrdiff_t overlays_at (EMACS_INT, bool, Lisp_Object **,
-			      ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, bool);
+			      ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, bool,
+			      bool);
 extern ptrdiff_t sort_overlays (Lisp_Object *, ptrdiff_t, struct window *);
 extern void recenter_overlay_lists (struct buffer *, ptrdiff_t);
 extern ptrdiff_t overlay_strings (ptrdiff_t, struct window *, unsigned char **);
@@ -1204,13 +1205,13 @@ #define GET_OVERLAYS_AT(posn, overlays, noverlays, nextp, chrq)		\
     ptrdiff_t maxlen = 40;						\
     SAFE_NALLOCA (overlays, 1, maxlen);					\
     (noverlays) = overlays_at (posn, false, &(overlays), &maxlen,	\
-			       nextp, NULL, chrq);			\
+			       nextp, NULL, chrq, false);		\
     if ((noverlays) > maxlen)						\
       {									\
 	maxlen = noverlays;						\
 	SAFE_NALLOCA (overlays, 1, maxlen);				\
 	(noverlays) = overlays_at (posn, false, &(overlays), &maxlen,	\
-				   nextp, NULL, chrq);			\
+				   nextp, NULL, chrq, false);		\
       }									\
   } while (false)
 
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 2adffc024a..5e83c0beab 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -1386,4 +1386,17 @@ buffer-tests-inhibit-buffer-hooks-indirect
           (when (buffer-live-p base)
             (kill-buffer base)))))))
 
+(ert-deftest zero-length-overlays-and-not ()
+  (with-temp-buffer
+    (insert "hello")
+    (let ((long-overlay (make-overlay 2 4))
+          (zero-overlay (make-overlay 3 3)))
+      ;; Exclude.
+      (should (= (length (overlays-at 3)) 1))
+      (should (eq (car (overlays-at 3)) long-overlay))
+      ;; Include.
+      (should (= (length (overlays-at 3 nil t)) 2))
+      (should (memq long-overlay (overlays-at 3 nil t)))
+      (should (memq zero-overlay (overlays-at 3 nil t))))))
+
 ;;; buffer-tests.el ends here

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no