Subject: bug#76018: 31.0.50; wrap-prefix properties from visual-wrap-prefix-mode proliferate

From: Stefan Monnier <[email-masked]>
Date: Tue, 27 May 2025 16:23:48 -0400

>> AKA `font-lock-append-text-property`.
>> We also have `add-face-text-property` for text-properties containing faces.
> Yeah, this is a generalization of those, though it doesn't convert scalars
> to lists.  In this case, I needed something like those, but for the
> stickiness text properties.

FWIW, I'd welcome a more coherent set of primitives.

> In that commit, the 'remove-text-properties' call actually got moved from
> 'visual-wrap-prefix-function' to 'visual-wrap--content-prefix', where it
> does the same thing as before, or at least attempts to. See here:

Ah, I see.

>>     ;; If we want to repeat the first-line prefix on subsequent lines,
>>     ;; return its string value.  However, we remove any `wrap-prefix'
>>     ;; property that might have been added earlier.  Otherwise, we end
>>     ;; up with a string containing a `wrap-prefix' string containing a
>>     ;; `wrap-prefix' string...
>>     (remove-text-properties 0 (length prefix) '(wrap-prefix) prefix)
>
> I haven't checked to be sure that the original code handles this
> differently, but since it originally only removed the 'wrap-prefix' from the
> *prefix* (see the '(length pfx)' bit in the first code snippet), I don't
> think it should do anything different from the current implementation...

Oh, indeed that `remove-text-properties` was applied to the string, not
to the buffer.  Duh!

> For the removal function, are you imagining that we just delete all the
> relevant properties from a specified region of text?

Traditionally jit-lock functions start by removing the property they
manage over the whole region that they're about to process.
E.g. in `adaptive-wrap.el` I think the patch below is missing.
For `visual-wrap.el` we additionally need to remove the `display`
properties we set up, which can be a fair bit more difficult (unless we
decide to just remove all `display` properties).

> I'm a little worried that we'd clobber other modes' properties if we're not
> careful, but maybe it's better to err on the side of deletion so we don't
> have stray properties.  If only we could convert all the properties to use
> overlays (I tried)...

So far, we generally don't try to interact properly with other packages
using the same property.  It's usually good enough.
In the present case, our minor mode tends to place a `wrap-prefix`
property over pretty much the hole buffer anyway, so there's not much
room for another package to use that property without conflict.

        Stefan

diff --git a/adaptive-wrap.el b/adaptive-wrap.el
index e5b906f892..140e50293e 100644
--- a/adaptive-wrap.el
+++ b/adaptive-wrap.el
@@ -1,6 +1,6 @@
 ;;; adaptive-wrap.el --- Smart line-wrapping with wrap-prefix  -*- lexical-binding: t; -*-
 
-;; Copyright (C) 2011-2021  Free Software Foundation, Inc.
+;; Copyright (C) 2011-2025  Free Software Foundation, Inc.
 
 ;; Author: Stephen Berman <[email-masked]>
 ;;         Stefan Monnier <[email-masked]>
@@ -136,6 +136,7 @@ How much to adjust it is decided by `adaptive-wrap-extra-indent'."
   (goto-char beg)
   (forward-line 0)
   (setq beg (point))
+  (remove-text-properties beg end '(wrap-prefix))
   (while (< (point) end)
     (let ((lbp (point)))
       (put-text-property