3

I noticed a rather peculiar behavior when using the display property (using emacs 28.2). I tried to boil things down to a minimal example: The code

  (defun finsert ()
    (insert (propertize "." 'display "|")))

  (switch-to-buffer (get-buffer-create "test"))

  ;; (finsert)
  (insert (propertize "." 'display "|"))
  
  (insert "1")
  (finsert)
  (insert "2")

  (goto-char 2)
  (delete-char 1)

inserts some propertized text into a new buffer test and removes one char. Then the new buffer shows ||2 (with each | using the display property).

I replaced (insert (propertize "." 'display "|")) with (finsert) (which I assumed would give the same result):

  (defun finsert ()
    (insert (propertize "." 'display "|")))

  (switch-to-buffer (get-buffer-create "test"))

  (finsert)
  ;; (insert (propertize "." 'display "|"))
  
  (insert "1")
  (finsert)
  (insert "2")

  (goto-char 2)
  (delete-char 1)

But that shows only |2 (previously it showed ||2). Moreover, the 2 will be at position 3 (not 2) and both | seemed to got kind of merged...

Why is that happening? And why are both code snippets not producing the same result?

1 Answer 1

3

I think this is exactly answered by the doc in (elisp) Replacing Specs.

Consecutive text with the same display property is handled as a block, replacing it by the display property.

If a list of display specifications includes more than one replacing display specification, the first overrides the rest. Replacing display specifications make most other display specifications irrelevant, since those don’t apply to the replacement.

But what matters is what "the same" means. Here it means eq, which for a string, such as "|", means it has to be the same string object, not just two different string objects that have the same sequence of chars.

For replacing display specifications, “the text that has the property” means all the consecutive characters that have the same Lisp object as their ‘display’ property; these characters are replaced as a single unit. If two characters have different Lisp objects as their ‘display’ properties (i.e., objects which are not ‘eq’), they are handled separately.

In your first example you propertize the string "." with a string "|", and you insert that. Then you do the same thing with another string "|". Two different strings, even though they're applied to consecutive chars, means two separate display properties.

In your second example, you use function finsert to insert the same propertized string multiple times.

This is a bit like the problem of using '(some list) in a let binding and then modifying that list. The source code "." puts that single string into the function definition. This is different from having Lisp read and eval separate propertize calls, reading separate strings that each look like "|".

Please read all of the cited node of the manual. It shows a clear example that corresponds exactly to what you do and see. The example puts two different (non-eq) strings with the same replacement text on consecutive text, with two uses of property display.

All of the chars that get each of the properties are handled as a block/unit. But the two uses of display, with two different strings, mean that there are two such blocks of consecutive text, each with a different string used for property display. The two strings are equal (string=), but they are not eq: they are not the same string object.

If you don't want the same string (eq) to be used every time you call finsert, then tell it to create a new string each time, using make-string:

(defun finsert ()
  (insert (propertize "." 'display (make-string 1 ?|))))

Then you'll always see ||2, because a different string "|" will be used each time finsert is called.

1
  • 1
    Wow, I am very impressed!!! I was already assuming a kind of bug... But now this makes total sense! Thank you very much!!!
    – dmw64
    Commented Aug 29, 2023 at 6:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.