Subject: bug#58937: text-property-search-backward misses one-character regions

From: Nicolas Graner <[email-masked]>
Date: Thu, 03 Nov 2022 00:40:43 +0100

> Date: Tue, 01 Nov 2022 19:05:41 +0200
> From: Eli Zaretskii <[email-masked]>
> To: [email-masked]
> CC: [email-masked], [email-masked]

> diff --git a/lisp/emacs-lisp/text-property-search.el b/lisp/emacs-lisp/text-property-search.el
> index d11980f..d41222b 100644
> --- a/lisp/emacs-lisp/text-property-search.el
> +++ b/lisp/emacs-lisp/text-property-search.el
> @@ -208,8 +208,14 @@ text-property--find-end-backward
>                  (goto-char end)
>                  (setq ended t)))))
>        ;; End this at the first place the property changes value.
> -      (setq end (previous-single-property-change
> -                 (point) property nil (point-min)))
> +      (setq end
> +            (if (and (> (point) (point-min))
> +                     (text-property--match-p
> +                      value (get-text-property (1- (point)) property)
> +                      predicate))
> +                (previous-single-property-change (point)
> +                                                 property nil (point-min))
> +              (point)))
>        (goto-char end))
>      (make-prop-match :beginning end

This works fine in all the cases I have tested, although I don't claim
to have tested all relevant combinations of arguments and region lengths
and positions...

In the process, I think I uncovered another bug common to
text-property-search-forward and -backward. Before filing a bug report
I'd like to make sure my understanding is correct. It may also be a
documentation error.

Here, the PREDICATE argument is a function, not the usual t or nil:

(with-current-buffer (generate-new-buffer "test")
  (insert "123456789")
  (put-text-property 2 4 'foo "abcd")
  (put-text-property 4 6 'foo "efgh")
  (goto-char 1)
  (text-property-search-forward 'foo 4 (lambda (l str) (= l (length str)))))

This returns:
#s(prop-match 2 4 "abcd")
but I think it should be:
#s(prop-match 2 6 "abcd")
since both the (2 4) and the (4 6) regions satisfy the predicate.

The docstring for text-property-search-forward says:
    If PREDICATE is nil (which is the default value), a value will
    match if is not ‘equal’ to VALUE.  Furthermore, a nil PREDICATE
    means that the match region is ended if the value changes.

This implies that when PREDICATE is not nil, the match region should not
end when the value changes, but only when the predicate is no longer
satisfied. Is this correct?

Nicolas