0

I'm implementing a minor mode that "folds" certain strings. It does this by adding a font-lock keywords rule to the buffer, that applies display properties that make it look like it's a single character. (For example, the six-character sequence \u00B6 might be displayed as when the mode is active.)

The documentation says that Emacs will not let point get into the middle of a run of text that's hidden this way, and that's mostly true, but I found a way to do it. Start with point right before the visible . Type C-d, which deletes the \; the font-lock rule no longer applies and the u00B6 reappears; point is now right before the u. Then type a new \ . The font-lock rule applies again, the \u00B6 disappears and reappears. Point appears to be after the , but it is actually in between the \ and the u. Motion commands will remove point from the inside of the run, but if the very next thing you type is a self-inserting character, it will be inserted in between the \ and the u. Depending on the details of the font-lock rule, it will either not appear at all, or it will make the rule cease to match.

What can I do from within my minor mode to prevent this from happening? It seems like it might be a bug in Emacs, but I'd rather not wait for it to be fixed.

(All testing with Emacs 27.1. Adding the cursor-intangible property to the run of text, in the same way, does not help. Cut down version of the mode follows.)

(define-minor-mode fold-demo-mode
  "Folding bug demo"
  :init-value nil
  :lighter " FD"
  :keymap nil
  (make-variable-buffer-local 'font-lock-extra-managed-props)
  (let ((case-fold-search nil)
        (demo-fl-rule '(("\\\\u00[bB]6" 0
                         '(face font-lock-string-face
                           display "¶"))))
        (refontify
         (lambda ()
           (save-excursion
             (goto-char (point-min))
             (while (re-search-forward "\\\\u00[bB]6" nil t)
               (save-excursion
                 (font-lock-fontify-region
                  (match-beginning 0) (match-end 0))))))))

  (if fold-demo-mode
      (progn
        ;; When the mode is being turned on, refontify after
        ;; adding to font-lock-extra-managed-props and
        ;; adding the extra fontification rule.
        (push 'display font-lock-extra-managed-props)
        (font-lock-add-keywords nil demo-fl-rule)
        (funcall refontify))

    ;; But when the mode is being turned off, remove the extra
    ;; fontification rule, refontify, and only then remove 'display
    ;; from font-lock-extra-managed-props; otherwise the display
    ;; spec won't get cleared.
    (font-lock-remove-keywords nil demo-fl-rule)
    (funcall refontify)
    (setq font-lock-extra-managed-props
          (cl-remove 'display font-lock-extra-managed-props
                     :count 1)))))
3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.