kaorahi / howm

note-taking tool on Emacs
GNU General Public License v2.0
147 stars 11 forks source link

How to use org-mode formatting in *howmC* buffer? #29

Closed rhstanton closed 2 weeks ago

rhstanton commented 3 months ago

A long-time org-mode user, I've just started using howm and love it. I've set it to use org as the file extension and almost everything works great, except for one thing - when I run howm-list-recent, the buffer showing each note seems to have no visual formatting, while I'd like it to be formatted as if it were an org file, in particular using org-modern-mode so that instead of rows of asterisks before section (sub)headers, I see nicer-looking icons. How can I achieve this?

Here's what I've tried:

1) If I try and execute "org-modern-mode" in the howmC buffer, I'm told:

org-modern-mode: ‘org-modern-mode’ should be enabled only in ‘org-mode’

2) I can manually turn on org-mode in this buffer, and all is fine (for this particular note, but I have to do it every time I change notes) . But if I try and automate this with the following in my init.el,

(add-hook 'howm-view-contents-mode-hook 'org-mode)

then when I run howm-list-recent I get an error like

Error in post-command-hook (riffle-post-command): (error "Can’t detect type of ((/Users/stanton/howm/2025/01/2025-01-01-172754.org * To Do List 1 nil nil nil)) for riffle-contents-item.")

Thanks for any suggestions

mmarshall540 commented 3 months ago

I was curious about this also.

Tried changing the definition of riffle-mode so that it derived from org-mode instead of text-mode, but it didn't seem to have any effect. Haven't gone any further than that.

Another approach (if you want to handle this in your config) could be to bind a key in howm-view-summary-mode-map to something like this:

(defun my/howm-view-summary-open-stay ()
  "Open note without changing window."
  (interactive)
  (save-selected-window
    (howm-view-summary-open)))

I would suggest using "C-o" as the key, since it is already used in Dired and Ibuffer for essentially the same thing (open selected buffer/file in other-window without selecting that window).

This will open each file you use it on, so be aware of that.

kaorahi commented 3 months ago

@rhstanton This is not supported at present. I'll consider it as a long-term request. Current random thoughts:

nicolaisingh commented 3 months ago

There is a function in org-mode (org-set-font-lock-defaults) that loads font locking for an org-mode buffer; this got me curious so I did a quick test by including it in a minor mode, and then loading it in howm-view-contents-mode-hook.

Fontification is applied but I'm not sure if this covers the entire org-mode font locking. I'm thinking this is not the complete set, but at least you get some colors :)

Screenshot_20250104_211820

(define-minor-mode org-font-lock-minor-mode
  "Minor mode to apply org-mode font locking to a buffer."
  :lighter " orgFL"
  (if org-font-lock-minor-mode
      (progn
        (org-set-font-lock-defaults)
        (font-lock-add-keywords nil org-font-lock-keywords))
    (font-lock-remove-keywords nil org-font-lock-keywords))
  (font-lock-flush)
  (font-lock-ensure))

(add-hook 'howm-view-contents-mode-hook #'org-font-lock-minor-mode)

One problem I immediately saw was that the hook does not fire when you change the note selection, so some additional work might be needed on that end.

rhstanton commented 3 months ago

Nice! And on my machine this does continue to work when I change notes.

I've also made some progress with getting org-modern-mode to work. It doesn't like to install if it's not running under org-mode, so I just redefined org-modern-mode, commenting out two lines to remove the test for org-mode:

(define-minor-mode org-modern-mode
  "Modern looks for Org."
  :global nil
  :group 'org-modern
  ; (unless (derived-mode-p 'org-mode)
  ; (error "`org-modern-mode' should be enabled only in `org-mode'"))
  (cond
   (org-modern-mode
    (add-to-invisibility-spec 'org-modern)
    (setq
     org-modern--folded-star-cache
     (and (eq org-modern-star 'fold)
          (vconcat (mapcar #'org-modern--symbol (mapcar #'car org-modern-fold-stars))))
     org-modern--expanded-star-cache
     (and org-modern-star
          (vconcat (mapcar #'org-modern--symbol (if (eq org-modern-star 'fold)
                                                    (mapcar #'cdr org-modern-fold-stars)
                                                  org-modern-replace-stars))))
     org-modern--hide-stars-cache
     (and (char-or-string-p org-modern-hide-stars)
          (list (org-modern--symbol org-modern-hide-stars)
                (org-modern--symbol org-modern-hide-stars)))
     org-modern--checkbox-cache
     (mapcar (pcase-lambda (`(,k . ,v)) (cons k (org-modern--symbol v)))
             org-modern-checkbox)
     org-modern--font-lock-keywords
     (append (remove '(org-fontify-meta-lines-and-blocks) org-font-lock-keywords)
             (org-modern--make-font-lock-keywords)))
    (font-lock-remove-keywords nil org-font-lock-keywords)
    (font-lock-add-keywords nil org-modern--font-lock-keywords)
    (setq-local font-lock-unfontify-region-function #'org-modern--unfontify)
    (add-hook 'pre-redisplay-functions #'org-modern--pre-redisplay nil 'local)
    (add-hook 'org-after-promote-entry-hook #'org-modern--unfontify-line nil 'local)
    (add-hook 'org-after-demote-entry-hook #'org-modern--unfontify-line nil 'local)
    (when (eq org-modern-star 'fold)
      (add-hook 'org-cycle-hook #'org-modern--cycle nil 'local))
    (org-modern--update-label-face)
    (org-modern--update-fringe-bitmaps))
   (t
    (remove-from-invisibility-spec 'org-modern)
    (font-lock-remove-keywords nil org-modern--font-lock-keywords)
    (font-lock-add-keywords nil org-font-lock-keywords)
    (setq-local font-lock-unfontify-region-function #'org-unfontify-region)
    (remove-hook 'pre-redisplay-functions #'org-modern--pre-redisplay 'local)
    (remove-hook 'org-after-promote-entry-hook #'org-modern--unfontify-line 'local)
    (remove-hook 'org-after-demote-entry-hook #'org-modern--unfontify-line 'local)
    (when (eq org-modern-star 'fold)
      (remove-hook 'org-cycle-hook #'org-modern--cycle 'local))))
  (without-restriction
    (with-silent-modifications
      (org-modern--unfontify (point-min) (point-max)))
    (font-lock-flush)))

If I put this into my *scratch* buffer, evaluate it, then run org-modern-mode while looking at one of the notes in the howm summary, the stars now get very nicely formatted with fancy icons, just as I wanted. Moreover, this survives moving to a new note.

So we're getting very close!

The only problem is that if I put this definition into my init.el rather than running it manually, then when I run org-modern-mode I get the error message

Symbol’s value as variable is void: org-modern-star

And actually, when I open an org file, I get a similar error:

File mode specification error: (void-variable org-modern-star)

Any thoughts on how to get over this last hump? It's a bit odd because I made sure to put my redefinition after installing org-modern, which I do using

(use-package org-modern
  :ensure t
  :custom
  (org-modern-hide-stars 'leading)
  (org-modern-table nil)
  :hook
  (org-mode . org-modern-mode)
  (org-agenda-finalize . org-modern-agenda))
rhstanton commented 3 months ago

Solved!

I put the same code as above after :config in the use-package command for org-modern, and also added

:hook
  (howm-view-contents-mode . org-modern-mode)

It now works as desired without my needing to do anything manually. Used in conjunction with Nicolai's code from above, here's an example of what I see in the *howmC* buffer:

Screenshot 2025-01-04 at 2 30 18 PM

rhstanton commented 3 months ago

Just one problem - howm links don't work with this setup. They do not show up as underlined, and when I press Return I just get a line break (in the middle of a link). Any suggestions for getting this working in conjunction with org highlighting?

Thanks.

mmarshall540 commented 3 months ago

It's working for me, but I'm pressing "RET" in the Summary buffer, which then opens the note from its file and moves point to it.

rhstanton commented 3 months ago

Aha! I'd added the following to init.el, intended to turn on org mode:

(add-hook 'howm-mode-hook
          (lambda ()
            (unless (derived-mode-p 'howm-menu-mode)
              (org-mode))))

But actually this doesn't seem necessary, and commenting it out gets links working.

kaorahi commented 2 months ago

Has this issue been resolved? Is it okay to close it?

mmarshall540 commented 2 months ago

I hope @rhstanton will correct me if wrong. But they did write "Solved!" at the top of one of their comments. Later they concluded that an additional problem regarding links was caused by their config. So I believe the issue is resolved.

However, I think @nicolaisingh's solution is broadly useful to those who exclusively use Org-mode files. Maybe it should be added to the "Quick start" section of the readme? Or maybe a more general solution could be crafted that lets users apply either Org-mode or Markdown-mode font-locking to the content buffers.

But I'm just thinking aloud. Please don't feel you need to keep the issue open on account of this secondary idea.

rhstanton commented 2 months ago

Yes, we can close this. Thanks!

kaorahi commented 2 months ago

Does the "solution" seem OK in the concatenated view of the contents buffer (toggled by @ key)? If so, I welcome a PR to update README.md.

It might also help readers if the conclusion of this issue is explicitly written out, making it easy to copy and paste.

jackbaty commented 2 months ago

Something I noticed with @nicolaisingh's solution is that, while it does fontify the org buffers, for some reason it disables the font locking in other buffers, such as Mu4e or Elfeed. Could just be something in my config, but thought I'd mention it, in case it's a broader issue. I'm afraid I don't know how to fix it, though.

mmarshall540 commented 2 months ago

Does the "solution" seem OK in the concatenated view of the contents buffer (toggled by @ key)?

Personally, I think it works great. Like @rhstanton, I'm also a user of the "org-modern" package. However, I don't enable org-modern in the contents buffer, as I like for it to look slightly different from Org files that are open.

But I see that @jackbaty just posted this:

while it does fontify the org buffers, for some reason it disables the font locking in other buffers, such as Mu4e or Elfeed.

And I can confirm that I see no font-locking if I open Elfeed or Notmuch. It's a bit hard to remember which buffers had colorful faces before, but I do believe those did.

rhstanton commented 2 months ago

I agree it looks great. And I also see no colors in notmuch, but I can't remember if it used to or not.

kaorahi commented 2 months ago

Could I confirm if I'm understanding correctly?

jackbaty commented 2 months ago

Yes. I'm using the same code from that comment.

Yes, the font issue happens only with the new code. I don't have any font locking issues when I don't include the "solution's" code. It happens in Elfeed, Notmuch, and Mu4e, although it only happens in notmuch-search mode, not in notmuch-hello mode.

These steps:

Without the "solution"

Image

With the "solution"

Image

kaorahi commented 2 months ago

thx. strange... @nicolaisingh, would you have any suggestions?

rhstanton commented 2 months ago

I did a little exploration using notmuch. When I run it before running howm, the message summary window is fontified nicely. Looking at a character in the date field and typing C-u C-x = to get some font info generates (among other things):

There are text properties here:
  face                 (notmuch-tree-match-date-face notmuch-tree-match-face)

Then I pressed C-c , a to see the howm message list and reran notmuch. Now the buffer had no color. Examining the fonts again, now I see something a bit different:

There are text properties here:
  fontified            t

Does this give any suggestions for where to look?

kaorahi commented 2 months ago

The above C-u C-x = results suggest that font-lock is wrongly applied to buffers that handle their own text coloring independently of font-lock. Is org-font-lock-minor-mode unexpectedly called in mailer buffers? Really??

Could you confirm?

(define-minor-mode org-font-lock-minor-mode
  "Minor mode to apply org-mode font locking to a buffer."
  :lighter " orgFL"
  (message "orgFL is called in %s" (buffer-name))  ;; for debug
  (if org-font-lock-minor-mode
      (progn
        (org-set-font-lock-defaults)
        (font-lock-add-keywords nil org-font-lock-keywords))
    (font-lock-remove-keywords nil org-font-lock-keywords))
  (font-lock-flush)
  (font-lock-ensure))

(add-hook 'howm-view-contents-mode-hook #'org-font-lock-minor-mode)
rhstanton commented 2 months ago

A) org-font-lock-minor-mode returns nil.

Part B later...

rhstanton commented 2 months ago

B) I see (expected) messages like

orgFL is called in *howmC*

but no unexpected messages.

nicolaisingh commented 2 months ago

I am able to reproduce it by the ff. steps:

When I check variable font-lock-keywords, I see that it is (t nil).

Further checking, when I run only howm-list-recent and check variable font-lock-keywords, it seems that the org-mode font-locking was applied globally when the hook (add-hook 'howm-view-contents-mode-hook #'org-font-lock-minor-mode) was triggered. I am not sure why though.. Looks like this little experiment needs more work than initially thought.

kaorahi commented 2 months ago

thx! My guess was wrong.

Now I'm worried that old and dirty hacks in cheat-font-lock.el might be doing something bad...

mmarshall540 commented 2 months ago

Now I'm worried that old and dirty hacks in cheat-font-lock.el might be doing something bad...

I don't think that's the cause of this, as I'm able to reproduce the problem running Emacs with no config and without loading Howm.

Here, I'll use EWW to test it. Note that we can observe the problem in EWW in addition to Mu4e, notmuch, Elfeed, and Gnus. I use EWW to reproduce it, because EWW is a built-in package that doesn't require any configuration.

  1. Open Emacs without configuration "emacs -Q"
  2. Run "M-x eww RET lite.cnn.com RET"
  3. Note that a variable-pitch font is used and that links are colored in blue and underlined.
  4. Close Emacs "C-x C-c"
  5. Reopen without configuration "emacs -Q"
  6. In the "*scratch*" buffer, evaluate (require 'org) and (org-set-font-lock-defaults) (from the definition of the custom org-font-lock-minor-mode)
  7. Now open the web page again "M-x eww RET lite.cnn.com RET"
  8. Note that the font is no longer variable-pitch, and links are not distinguished at all.

So it seems this is caused by the call to org-set-font-lock-defaults when the new minor-mode is enabled. And it has that same effect even if Howm hasn't loaded at all.

Is this a bug in Org-mode? Not sure, because I don't know how that function is intended to be used.

mmarshall540 commented 2 months ago

So, after searching the "lisp/org" directory of the Emacs source code, it appears that org-set-font-lock-defaults is only ever used once, in the definition of org-mode.

I think it was written with the assumption that it would only be used as part of enabling Org-mode.

When used outside of that context, it changes the global value of font-lock-keywords (and possibly more, but I haven't checked).

mmarshall540 commented 2 months ago

It's caused by this line near the bottom of the definition of org-set-font-lock-defaults.

(kill-local-variable 'font-lock-keywords)

From the docstring of kill-local-variable (whose definition comes from Emacs's C source code).

Make VARIABLE no longer have a separate value in the current buffer.

So running that line of code should only affect the current buffer. But it has other effects. Try this.

  1. "emacs -Q"
  2. "C-h v font-lock-keywords RET M-\<end>" (see that global value is nil)
  3. "M-x eww RET lite.cnn.com RET" (looks good)
  4. "q" (back in "*scratch*")
  5. "C-h v font-lock-keywords RET M-\<end>" (see that global value is nil)
  6. "C-x C-c"
  7. "emacs -Q"
  8. In "*scratch*", type "(kill-local-variable 'font-lock-keywords) C-x C-e"
  9. "C-h v font-lock-keywords RET M-\<end>" (see that global value remains nil)
  10. "M-x eww RET lite.cnn.com RET" (faces are gone again)
  11. "C-h v font-lock-keywords RET M-\<end>" (see that global value has changed to (t nil)!)

This seems very strange. Running EWW shouldn't affect the global value of font-lock-keywords, and normally, it doesn't.

But when we evaluate (kill-local-variable 'font-lock-keywords) before running EWW, then the global value of font-lock-keywords is altered when we run EWW.

Am I just misunderstanding what kill-local-variable does? Maybe it changes the nature of the variable and not just the buffer-local value of a variable?

mmarshall540 commented 2 months ago

Got it! Apologies for the long chain of comments, but I believe I've just fixed it.

font-lock-keywords is not an automatically buffer-local variable. Like all Emacs variables, it can be given a local binding if you explicitly set one using setq-local. But if you remove its local binding (which kill-local-variable does), the next time some package updates it using setq, the variable's global binding will be altered.

So here's the workaround.

All we have to do is add one line to @nicolaisingh's original solution, after the line that calls org-set-font-lock-defaults.

(define-minor-mode org-font-lock-minor-mode
  "Minor mode to apply org-mode font locking to a buffer."
  :lighter " orgFL"
  (if org-font-lock-minor-mode
      (progn
        (org-set-font-lock-defaults)
        (setq-local font-lock-keywords nil)
        (font-lock-add-keywords nil org-font-lock-keywords))
    (font-lock-remove-keywords nil org-font-lock-keywords))
  (font-lock-flush)
  (font-lock-ensure))

(add-hook 'howm-view-contents-mode-hook #'org-font-lock-minor-mode)
rhstanton commented 2 months ago

Got it! Apologies for the long chain of comments, but I believe I've just fixed it.

font-lock-keywords is not an automatically buffer-local variable. Like all Emacs variables, it can be given a local binding if you explicitly set one using setq-local. But if you remove its local binding (which kill-local-variable does), the next time some package updates it using setq, the variable's global binding will be altered.

So here's the workaround.

All we have to do is add one line to @nicolaisingh's original solution, after the line that calls org-set-font-lock-defaults.

(define-minor-mode org-font-lock-minor-mode "Minor mode to apply org-mode font locking to a buffer." :lighter " orgFL" (if org-font-lock-minor-mode (progn (org-set-font-lock-defaults) (setq-local font-lock-keywords nil) (font-lock-add-keywords nil org-font-lock-keywords)) (font-lock-remove-keywords nil org-font-lock-keywords)) (font-lock-flush) (font-lock-ensure))

(add-hook 'howm-view-contents-mode-hook #'org-font-lock-minor-mode)

Yes!

mmarshall540 commented 2 months ago

For @rhstanton, here's a way to get org-modern-mode working without having to copy its definition into your config.

;; Make Org-modern work in Howm content buffers.
(defun my/org-modern-mode-advice (oldfunc &rest r)
  "OLDFUNC is `org-modern-mode' and R is its arguments."
  (cl-letf (((symbol-function 'derived-mode-p)
             (lambda (&rest _) t)))
    (apply oldfunc r)))

(advice-add 'org-modern-mode :around 'my/org-modern-mode-advice)
rhstanton commented 2 months ago

Much more elegant!

kaorahi commented 2 months ago

Congrats!

I just noticed there are some variables for customization. I should've remembered them earlier.

;; example

;; Highlight FOO and [BAR] in all howm buffers.
(setq howm-user-font-lock-keywords
  '(
    ("FOO" . (0 'highlight prepend))
    ("\\[BAR\\]" . (0 'font-lock-doc-face prepend))
    ))

;; Use the coloring rules of rd-mode in howm's contents buffers.
(setq howm-view-contents-font-lock-keywords rd-font-lock-keywords)

I'm not sure if these are usable in the present case. Sorry that they are documented only in JP. https://kaorahi.github.io/howm/README-j.html

kaorahi commented 2 months ago

https://github.com/kaorahi/howm/issues/38#issuecomment-2626978631

@jabirali, my thoughts are here.

nicolaisingh commented 2 months ago

glad you found a fix for it @mmarshall540 !

kaorahi commented 1 month ago

I've merged some related commits. I'll close this issue soon if they seem OK. Thanks again for your comments.

Please let me know if there's anything else.

rhstanton commented 1 month ago

I've noticed that org links don't format using this definition of org-font-lock-minor-mode. For example, if I put this link into my file:

[[https://www.gnu.org/software/emacs/][GNU Emacs]]

it displays just as shown above (though it is clickable). Is there a way to have it display just the text, like in an org buffer, GNU Emacs?

kaorahi commented 1 month ago

Thanks for the report. It looks like it's working in the latest GitHub master branch.

  1. Add (require 'howm-org) before (require 'howm) in sample/dot.emacs.
  2. emacs -Q -l sample/dot.emacs
  3. C-c ; c
  4. Add [[https://www.gnu.org/software/emacs/][GNU Emacs]] at the bottom.
  5. C-x C-s and C-c ; a.
  6. Ensure that only GNU Emacs is displayed in the *howmC* buffer, properly colored and underlined.

Are you using howm-org as described in the current README.md? If it's set up correctly, M-x describe-variable RET howm-wiki-regexp RET should show Its value is nil.

(ref.) https://github.com/kaorahi/howm/issues/50#issuecomment-2663378208 https://github.com/kaorahi/howm/commit/a708f7b44fc8a9432464b6318c4c0ff2ad4ed953

rhstanton commented 3 weeks ago

Thanks! I wasn't using howm-org. Easy enough to change...

However, I note that with the steps above, while the link shows up correctly formatted in the *howmC* buffer, it is not formatted correctly in the *howmS* buffer if I put the link into the title of the note. Instead, it shows up verbatim, e.g.,

* [[https://www.gnu.org/software/emacs/][GNU Emacs]]
rhstanton commented 3 weeks ago

One more thing: if I enable org-modern-mode in the *howmC* buffer, the link display goes back to being literal. Any suggestions for getting this to work?

kaorahi commented 2 weeks ago

Does this work?

(add-hook 'howm-view-summary-mode-hook #'howm-org-font-lock-minor-mode)
rhstanton commented 2 weeks ago

It does. Thanks!

kaorahi commented 2 weeks ago

I've opened another issue #61 for org-modern.