4

The problem is that when there is one line in between folded headings with source blocks the text properties of the #end-src line bleeds out. You can see the lines bleeding on the right in the image.

Observe Lines Bleeding out on the right

Update Progress So Far

step 1

To do this I have taken two steps.

The first is to unfontify the end of the last line of a subtree when hiding it if that line is the end of a source block. That's the line responsible for the bleeding. This will ensure that the font face doesn't bleed out when a block is hidden with outline-hide-subtree.

(defun dwim-unfontify-last-line-of-subtree (&rest _)
  "Unfontify last line of subtree if it's a source block."
  (save-excursion
    (org-end-of-subtree)
    (beginning-of-line)
    (when (looking-at-p (rx "#+end_src"))
      (font-lock-unfontify-region
       (line-end-position) (1+ (line-end-position))))))

(advice-add #'outline-hide-subtree :after #'dwim-unfontify-last-line-of-subtree)

step 2

The second step is the hard one. I determined that outline-show-heading is the fundamental function for showing headings. This function checks if the heading being shown has

(defun dwim-fontify-last-line-of-block (&rest _)
  "Do what I mean: fontify last line of source block.
When the heading has a source block as the last item (in the subtree) do one of the
  following:
If the source block is now visible, fontify the end its last line.
If it's invisible, unfontify its last line."
  (let (font-lock-fn invisible-p heading-name)
    (save-excursion
      ;; Debugging
      ;; (save-match-data
      ;;   (beginning-of-line)
      ;;   (looking-at
      ;;    (rx bol (+ "*") "\s" (submatch (+ (not (any "\n"))))))
      ;;   (setq heading-name (match-string-np 1)))
      (org-end-of-subtree)
      (beginning-of-line)
      (when (looking-at-p (rx "#+end_src"))
        (setq invisible-p (outline-invisible-p (line-end-position)))
        (setq font-lock-fn
              (if invisible-p
                  #'font-lock-unfontify-region
                #'font-lock-fontify-region))
        ;; Debugging
        ;; (message "-%s-’s end_src is %svisible"
        ;;          heading-name
        ;;          (if invisible-p "in" ""))
        (funcall font-lock-fn
                 (line-end-position)
                 (1+ (line-end-position)))))))

(advice-add #'outline-show-heading :after #'dwim-fontify-last-line-of-block)

problem

This is extremely close to working. If you use outline-toggle-children to display the children of a subtree and every child is a subtree (in other words each is displayed as a folded headline), the last source block of every headline should be invisible. That's because all the headings are folded. But (outline-invisible-p (line-end-position)) says the last child of that subtree remains visible.

The picture below shows what I mean. You can see the last child of the Top level headings is bleeding out. And the last child of the level Completion heading is bleeding. This is what happens when I displayed them with outline-toggle-fold.

last child]

  • In general, messing with the text-properties (expressly setting the text properties) when font-lock is active is just asking for trouble. The best practice, in my opinion, is to use one or the other. Since flat-text files in org-mode use font-lock and org-agenda-mode uses text-properties, I would suggest any changes you make be limited as such. The easiest solution is to not use a background face, except for an active region ... There is a lengthy recent discussion on the Emacs Devel list spanning several days about face attributes that extend to the end of the line ... – lawlist Aug 24 '19 at 18:22
  • Thanks, I did not know this. I'll investigate how to do the same thing with font-lock. – Aquaactress Aug 24 '19 at 21:47
  • Here is a link to the opening thread I referred to above where the Emacs developers discuss face properties that extend to the end of the display line (e.g., end of the text area / window body width): lists.gnu.org/archive/html/emacs-devel/2019-08/msg00132.html In a couple of those posts, they compare/contrast terminal and GUI builds where certain face attributes extend to the end of the display line .... Inasmuch as these things are handled internally by the display engine, Lisp workarounds may include stop highlighting before reaching EOL; or, fold to next line without face ... – lawlist Aug 24 '19 at 22:35
  • Thanks for this link. I tried folding to the next line without face by setting the second argument of outline-flag-region to the start of the next line. However I like having a blank line in between fold. Also doing this results in end_src lines looking like this: #+end_src.... Right now, I'm thinking the best workaround my be, as you mentioned, "stop highlighting before reaching EOL". However I would want to do this conditionally: only just before I fold a heading. And to highlight normally just before unfolding. – Aquaactress Aug 25 '19 at 0:09
  • Years ago, I stopped using the background face for everything (except region) because of problems like this ... I also stopped using bold ... but I forget the reason at the moment for that decision. – lawlist Aug 25 '19 at 0:14
0

May not be the answer you were looking for, but the simplest way might be to change the org-block-end-line variable face to that of the default background, thus ensuring that there's nothing to leak in the first place. Also, end_src line generally seems useless in that all the code-block arguments belong in the begin_line, or better yet an entirely separate #+header:.

| improve this answer | |
  • Thanks you for your answer. I had noticed this when I used themes (like avk-daylight) whose background was the same as the src block line. Actually, the line bleeding bothered me so much that I tend to mostly use such themes. – Aquaactress Aug 29 '19 at 3:04
  • Like I said, no need to bother with themes, all it takes is addind the '(org-block-end-line ((t (:background "#")))) to the (custom-set-faces) inside the dot file, where # is your background hex. – causenetsofv Aug 29 '19 at 5:15
  • You say no need to bother with themes but that I need to specify the specific background hex. If I did this what would happen if I changed themes using load-theme? Would I need to change the background hex? If this is the case, this would involve the current theme. This being said, I'm sure there is away to programatically update this value via an advice to load-theme. However, even still, I do not feel comfortable accepting such an answer because it introduces a visible side-effect: namely changing the face of #+end_src lines. – Aquaactress Aug 30 '19 at 14:30
  • If you're asking whether a given theme takes priorty over manually set properties, the answer is it doesn't. Once you explicitly define a face setting it gets rendered above the the load-theme properties. – causenetsofv Aug 31 '19 at 3:37
0

First attempt. Doesn't work, disregard. See below:

I just ran into the same issue and, inspired by @Aquaactress's attempt, added a simple advice to org-flag-region:

(defun org-flag-region-hide-last (from to flag spec)
  "Unfontify last char of hidden region when folding, fontify it
when showing.

This avoids the bleeding of `org-block-end-line' when block or
parent heading is folded."
  (unless (= (point-max) to)
      (save-excursion
        (if flag
            (font-lock-unfontify-region to (1+ to))
          (font-lock-fontify-region to (1+ to))))))

(advice-add 'org-flag-region :after #'org-flag-region-hide-last)

It seems to work well!

(Edit: no it doesn't. It doesn't work well with org-separator-lines and it would leave the last char unfontified in some situations.)

Second attempt. Seems to work:

The above code is still useful (after some adaptations) to deal with hiding the block itself. But, when cycling the outline, we need to hook into org-cycle-hook:

(defun org-fix-bleed-end-line-block (from to flag spec)
  "Toggle fontification of last char of block end lines when cycling.

This avoids the bleeding of `org-block-end-line' when block is
folded."
  (when (and (eq spec 'org-hide-block)
             (/= (point-max) to))
    (save-excursion
      (if flag
          (font-lock-unfontify-region to (1+ to))
        (font-lock-flush to (1+ to))))))

(advice-add 'org-flag-region :after #'org-fix-bleed-end-line-block)

(defun org-fix-bleed-end-line-cycle (state)
  "Toggle fontification of last char of block lines when cycling.

This avoids the bleeding of `org-block-end-line' when outline is
folded."
  (save-excursion
    (when org-fontify-whole-block-delimiter-line
      (let ((case-fold-search t)
            beg end)
        (cond ((memq state '(overview contents all))
               (setq beg (point-min)
                     end (point-max)))
              ((memq state '(children folded subtree))
               (setq beg (point)
                 end (org-end-of-subtree t t))))
        (when beg           ; should always be true, but haven't tested enough
          (goto-char beg)
          (while (search-forward "#+end" end t)
            (end-of-line)
            (unless (= (point) (point-max))
              (if (org-invisible-p (1- (point)))
                  (font-lock-unfontify-region (point) (1+ (point)))
                (font-lock-flush (point) (1+ (point)))))))))))

(add-hook 'org-cycle-hook #'org-fix-bleed-end-line-cycle)

If this doesn't work, try to remove the check for org-fontify-whole-block-delimiter-line. That is org-mode's own mechanism for dealing with whole line fontification; maybe some themes define their own methods for achieving whole line fontification, I don't know.

Last (?) issue

There is still ONE issue I couldn't resolve... When org-startup-folded is t or contents, the lines still bleed when the file first opens up. You can manually cycle the visibility and they'll behave again, but I could not figure out how to do that automatically at startup.

I tried creating a hook:

(defun org-fix-bleed-startup ()
  (when (memq org-startup-folded '(t content))
    (org-fix-bleed-end-line-cycle 'all)))

(add-hook 'org-mode-hook #'org-fix-bleed-startup)
;; also tried:
(remove-hook 'after-change-major-mode-hook #'org-fix-bleed-startup)

But for some reason it doesn't work. I guess some other functions run after org is loaded which also change the visibility of some parts of the buffer? I don't know.

Please test this and let me know if it works!

| improve this answer | |
  • It did not work for me. I evaluated this code and tried out the spacemacs theme but I still see the bleeding. Which theme did you try it on? And what org version? – Aquaactress Nov 13 at 21:36
  • Really? Strange... The theme is my own. The face is set with something like (set-face-attribute 'org-block-end-line nil :foreground "gray90" :background "gray40" :overline t :underline t). (That's not really the face I use, but I decided to turn lots of features on to see if any bleeding would show up. It doesn't, everything seems to work.) Org version is 9.3.7, built from source. I actually updated it just now to test this. Don't use spacemacs, sorry. – cksk Nov 14 at 1:09
  • OHHH, wait a minute. Just noticed that in your screenshot org-cycle-separator-lines is set to 1. I tried it and, with that setting, the bleeding DOES show up. I didn't see it because I normally use 0 separator lines... Let me see if I can fix this. – cksk Nov 14 at 1:12
  • Yesh. Sorry to spam the comment section, but I just want to say that this is way more finicky and involved than I thought. My original solution didn't really work, even with separator-lines set to 0... When global-cycling after locally hiding a subtree the last char of the end line would still be left unfontified. What a mess; please disregard, and I will keep trying, because this problem annoys me a lot, too. – cksk Nov 14 at 2:36
  • Just to be clear, when I say I "try" one of your solutions I mean that I evaluate your code an enable a theme I know has bleeding, such as the spacemacs-theme or the built-in leuven-theme. Trying your latest solution I still encounter bleeding. In fact, with the leuven theme in particular all visible headlines bleed. I am using org version 9.4. Ah, but the reason for this might be that I use evil-folds instead of org cycle. I'll investigate into this and report back. – Aquaactress Nov 15 at 0:31

Your Answer

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

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