20

Q: How do I keep org-mode link faces from bleeding over into the ... selective display characters at the end of a folded header?

This is a visual tick that drives me a little nutty. When, in org-mode, a link is the very last thing on a line, the link face bleeds over into the ... that indicates that the header is folded up. If there is, say, a blank space after the link, there is no bleeding.

The screenshot I've posted demonstrates the issue. Line three is the problematic line with no characters between the end of the link and the end of line, while line four shows a link, followed by a space:

weird link face behavior

First of all, why does this happen? Second, and more to the point, how do I make it stop?

UPDATE 1: As per comments, posted below are screenshots of the buffer with the headers closed and open. I've opened Emacs with no init file (ie, emacs -Q), required org-mode, and opened this example file. So: it does not appear to be something kooky in my setup.

All headers closed: weird link face closed

All headers open: weird link face open

The theme I'd been using above is inkpot, although I get the same problem when using the solarized theme as well as the default theme (as in the new screenshots).

Emacs version is 24.3.1. I get the same results when using org version 7.9.3f (ie, the one bundled with that Emacs version), as well as 8.3beta.

UPDATE 2: here's a minimum working example in response to a comment request:

* here's a header with a [[~/somefile.txt][link at the end]]

  - This one's a problem
  - Interesting note:
    + put the cursor immediately *after* the *d* in "end" with the
      header closed/folded
      * the face no longer bleeds over into the dots
    + move the cursor anywhere else
      * the face bleeds over into the dots again

* here's another [[~/someotherfile.txt][go at it]]
  DEADLINE: <2014-10-26 Sun>

  - This one's also a problem

* here's another header with a [[~/anotherfile.txt][link followed by a space]] 

  - No bleed-over onto the dots with this one
9
  • 1
    I'm having a hard time reproducing it on Emacs 24.3.1 and the org-mode that comes with it. Even with the reproduction steps you mentioned. Could you show the raw org-mode buffer? (That said, I assume it is a bug in org-mode. Does adding an extra newline help?)
    – aerique
    Oct 20, 2014 at 10:59
  • Same as @aerique, I don't see this here. So, maybe this depends on the Emacs version, or some detail of th Org mode buffer.
    – Stefan
    Oct 20, 2014 at 11:34
  • @Dan, out of curiosity, what theme are you using? Oct 20, 2014 at 19:50
  • 1
    @Dan could you please provide the source of an example org file for testing? Oct 26, 2014 at 10:26
  • 2
    @Dan I can reproduce this on Emacs 24.4 with the file you provided.
    – user2005
    Oct 27, 2014 at 16:00

1 Answer 1

10
+50

This looks like a bug triggered by org-mode's org-activate-bracket-links function.

This is what this function looks like:

(defun org-activate-bracket-links (limit)
  "Run through the buffer and add overlays to bracketed links."
  (if (and (re-search-forward org-bracket-link-regexp limit t)
       (not (org-in-src-block-p)))
      (let* ((hl (org-match-string-no-properties 1))
         (help (concat "LINK: " (save-match-data (org-link-unescape hl))))
         (ip (org-maybe-intangible
          (list 'invisible 'org-link
            'keymap org-mouse-map 'mouse-face 'highlight
            'font-lock-multiline t 'help-echo help
            'htmlize-link `(:uri ,hl))))
         (Vp (list 'keymap org-mouse-map 'mouse-face 'highlight
               'font-lock-multiline t 'help-echo help
               'htmlize-link `(:uri ,hl))))
    ;; We need to remove the invisible property here.  Table narrowing
    ;; may have made some of this invisible.
    (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0))
    (remove-text-properties (match-beginning 0) (match-end 0)
                '(invisible nil))
    (if (match-end 3)
        (progn
          (add-text-properties (match-beginning 0) (match-beginning 3) ip)
          (org-rear-nonsticky-at (match-beginning 3))
          (add-text-properties (match-beginning 3) (match-end 3) vp)
          (org-rear-nonsticky-at (match-end 3))
          (add-text-properties (match-end 3) (match-end 0) ip)
          (org-rear-nonsticky-at (match-end 0)))
      (add-text-properties (match-beginning 0) (match-beginning 1) ip)
      (org-rear-nonsticky-at (match-beginning 1))
      (add-text-properties (match-beginning 1) (match-end 1) vp)
      (org-rear-nonsticky-at (match-end 1))
      (add-text-properties (match-end 1) (match-end 0) ip)
      (org-rear-nonsticky-at (match-end 0)))
    t)))

It searches for a match for a bracketed link (e.g. [[target][label]], hides the [[target][ part by adding ip to the text properties, then linkifies the label by adding vp to the text properties, and finally removes the trailing ]] by adding ip to the text properties again.

This all looks right. org-rear-nonsticky-at should take care of property bleeding.

This behaviour is triggered by (add-text-properties (match-end 3) (match-end 0) ip), which hides the trailing ]]. Only the 'invisible 'org-link property triggers this behaviour, the other properties appear to be innocent.

You could overwrite org-activate-bracket-links such that ip no longer sets 'invisible but 'display "", which has the same effect:

(defun org-activate-bracket-links (limit)
  "Run through the buffer and add overlays to bracketed links."
  (if (and (re-search-forward org-bracket-link-regexp limit t)
       (not (org-in-src-block-p)))
      (let* ((hl (org-match-string-no-properties 1))
         (help (concat "LINK: " (save-match-data (org-link-unescape hl))))
         (ip (org-maybe-intangible
          (list 'display ""
            'keymap org-mouse-map 'mouse-face 'highlight
            'font-lock-multiline t 'help-echo help
            'htmlize-link `(:uri ,hl))))
         (Vp (list 'keymap org-mouse-map 'mouse-face 'highlight
               'font-lock-multiline t 'help-echo help
               'htmlize-link `(:uri ,hl))))
    ;; We need to remove the invisible property here.  Table narrowing
    ;; may have made some of this invisible.
    (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0))
    (remove-text-properties (match-beginning 0) (match-end 0)
                '(invisible nil))
    (if (match-end 3)
        (progn
          (add-text-properties (match-beginning 0) (match-beginning 3) ip)
          (org-rear-nonsticky-at (match-beginning 3))
          (add-text-properties (match-beginning 3) (match-end 3) vp)
          (org-rear-nonsticky-at (match-end 3))
          (add-text-properties (match-end 3) (match-end 0) ip)
          (org-rear-nonsticky-at (match-end 0)))
      (add-text-properties (match-beginning 0) (match-beginning 1) ip)
      (org-rear-nonsticky-at (match-beginning 1))
      (add-text-properties (match-beginning 1) (match-end 1) vp)
      (org-rear-nonsticky-at (match-end 1))
      (add-text-properties (match-end 1) (match-end 0) ip)
      (org-rear-nonsticky-at (match-end 0)))
    t)))

Clearly, this is an ugly hack. But it works for me and might work for you. I still recommend filing a bug report.

2
  • Thank you for the effort (+1 for that!), but this solution is not working for me. Rather than propertizing [[~/somefile.txt][link label]] as link label (where italics indicate the standard face for the link), it becomes link label]] (with no change in face). I'll file a bug report.
    – Dan
    Oct 29, 2014 at 12:03
  • Hmm, strange. The only change in my definition of org-activate-bracket-links is replacing 'invisible non-nil with 'display "", so it should still apply the link face as before. It certainly works for me in Emacs 24.4, but I guess energy is better spent on the bug report rather than trying to make my hack work... :)
    – user2005
    Oct 29, 2014 at 14:05

Your Answer

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

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