<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>Breathe</title><id>https://breatheoutbreathe.in/feeds/tags/emacs.xml</id><subtitle>Tag: emacs</subtitle><updated>2026-07-23T21:34:52Z</updated><link href="https://breatheoutbreathe.in/feeds/tags/emacs.xml" rel="self" /><link href="https://breatheoutbreathe.in" /><entry><title>Follow Up on emails with mu4e and org-capture</title><id>https://breatheoutbreathe.in/posts/follow-up-on-emails-with-mu4e-and-org-capture.html</id><author><name>Joseph Turner</name><email>contact@breatheoutbreathe.in</email></author><updated>2025-02-03T12:00:00Z</updated><link href="https://breatheoutbreathe.in/posts/follow-up-on-emails-with-mu4e-and-org-capture.html" rel="alternate" /><content type="html">&lt;p&gt;When I send an email, I often want my agenda to remind me to follow up
a few days later.  Thanks to &lt;a href=&quot;https://www.djcbsoftware.nl/&quot;&gt;Dirk-Jan C. Binnema&lt;/a&gt;'s help, I was able to
set this up with &lt;code&gt;mu4e&lt;/code&gt; and &lt;code&gt;org-capture&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;&lt;code&gt;mu4e&lt;/code&gt; + &lt;code&gt;org-capture&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Here's the code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  (add-to-list
   'org-capture-templates
   (&amp;quot;efu&amp;quot; &amp;quot;Follow Up&amp;quot; entry (file+olp &amp;quot;todo/todo.org&amp;quot; &amp;quot;Email&amp;quot; &amp;quot;Follow Up&amp;quot;)
    &amp;quot;* TODO Follow up with %(eval sent-message-to) on [[mu4e:msgid:%(eval sent-message-id)][%(eval sent-subject)]]
SCHEDULED: %(org-insert-time-stamp (org-read-date nil t \&amp;quot;+3d\&amp;quot;))
%i&amp;quot;))

  (add-hook 'message-sent-hook #'my/org-capture-sent-mail)

  (defun my/org-capture-sent-mail ()
    &amp;quot;Prepare to capture sent mail after window configuration is reset.&amp;quot;
    (let* ((sent-message-id
          (replace-regexp-in-string
           &amp;quot;[&amp;lt;&amp;gt;]&amp;quot; &amp;quot;&amp;quot; (message-fetch-field &amp;quot;Message-Id&amp;quot;)))
           (sent-message-to
            (replace-regexp-in-string &amp;quot; &amp;lt;.*&amp;gt;&amp;quot; &amp;quot;&amp;quot; (message-fetch-field &amp;quot;To&amp;quot;)))
         (sent-subject (or (message-fetch-field &amp;quot;Subject&amp;quot;) &amp;quot;No subject&amp;quot;)))
      (org-capture nil &amp;quot;efu&amp;quot;)
      (add-hook 'mu4e-compose-post-hook #'my/pop-to-buffer-org-capture-mail 99)))

  (defun my/pop-to-buffer-org-capture-mail ()
    (pop-to-buffer
     (car (match-buffers
           (lambda (buffer)
             (equal &amp;quot;efu&amp;quot;
                    (plist-get
                     (buffer-local-value
                      'org-capture-current-plist buffer)
                     :key))))))
    (remove-hook 'mu4e-compose-post-hook #'my/pop-to-buffer-org-capture-mail))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After I send an email, I see the &lt;code&gt;org-capture&lt;/code&gt; pop-up window
pre-filled from the template:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*** TODO Follow up with John Doe on [[mu4e:msgid:1234576890][Beautiful Snowy Morning]]
SCHEDULED: &amp;lt;2025-02-06 Wed&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I run &lt;code&gt;org-capture-finalize&lt;/code&gt; (&lt;code&gt;C-c C-c&lt;/code&gt;), then I'll be reminded in
three days to follow up on the email.  Before capturing the reminder,
I can add more information or edit the date if I want to have more or
less time before it shows up on my agenda.  Alternatively, I can run
&lt;code&gt;org-capture-kill&lt;/code&gt; (&lt;code&gt;C-c C-k&lt;/code&gt;) if I don't want to be reminded at all.&lt;/p&gt;
&lt;h2&gt;Technical Details&lt;/h2&gt;
&lt;p&gt;After closing a composition buffer, &lt;code&gt;mu4e&lt;/code&gt; restores your window
configuration in &lt;code&gt;mu4e-compose-post-hook&lt;/code&gt;, a feature which I want to
keep.  However, this means that the &lt;code&gt;org-capture&lt;/code&gt; buffer gets hidden
when the windows are restored.1 The solution above is to add a
self-removing hook function &lt;code&gt;my/pop-to-buffer-org-capture-mail&lt;/code&gt; to
&lt;code&gt;mu4e-compose-post-hook&lt;/code&gt; which pops back to the &lt;code&gt;org-capture&lt;/code&gt; buffer
after the window configuration is restored.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/djcb/mu/issues/2809&quot;&gt;Issue in mu4e tracker with djcb's comments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~breatheoutbreathein/dotfiles/tree/master/item/.emacs.d/init.el&quot;&gt;My Emacs configuration&lt;/a&gt; (search for &lt;code&gt;my/org-capture-sent-mail&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Footnotes&lt;/h1&gt;
&lt;h4&gt;1&lt;/h4&gt;
&lt;p&gt;I want to interact with the org capture buffer each time I send
an email, so I can edit or cancel the reminder while capturing it.  If
I didn't want to interact with the org capture buffer, I could put
&lt;code&gt;:immediate-finish&lt;/code&gt; in the &lt;code&gt;org-capture&lt;/code&gt; template and get rid of
&lt;code&gt;my/pop-to-buffer-org-capture-mail&lt;/code&gt;.&lt;/p&gt;
</content></entry><entry><title>Query Regexp Replace Lisp Replacement</title><id>https://breatheoutbreathe.in/posts/query-regexp-replace-lisp-replacement.html</id><author><name>Joseph Turner</name><email>contact@breatheoutbreathe.in</email></author><updated>2024-12-19T12:00:00Z</updated><link href="https://breatheoutbreathe.in/posts/query-regexp-replace-lisp-replacement.html" rel="alternate" /><content type="html">&lt;p&gt;I rarely use &lt;code&gt;query-replace-regexp&lt;/code&gt;'s lisp expression replacement
feature, but I came upon a good use for it today!&lt;/p&gt;
&lt;p&gt;Recently, I've been testing out &lt;a href=&quot;https://github.com/bohonghuang/org-srs/&quot;&gt;org-srs&lt;/a&gt; for practicing Mandarin vocab.
So far, it works very well!  When I asked to truncate the serialized
data which gets written to the Org files to reduce diff noise and to
make the &lt;code&gt;state&lt;/code&gt; column fit on my small laptop screen, &lt;a href=&quot;https://github.com/bohonghuang/org-srs/issues/8#issuecomment-2551418929&quot;&gt;the package
author kindly offered a workaround&lt;/a&gt;1, which causes this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Without workaround:

#+NAME: srsitem:2d51efcd-8523-4432-b074-50d1ba4c45b3
| ! | timestamp            | rating |          stability |        difficulty | state     |
|---+----------------------+--------+--------------------+-------------------+-----------|
|   | 2024-12-11T18:37:19Z |        |                0.0 |               0.0 | :new      |
|   | 2024-12-11T18:38:23Z | :again |             0.4072 |            7.2102 | :learning |
|   | 2024-12-11T18:39:05Z | :easy  | 0.9375660842587888 | 6.078123501986166 | :review   |
|   | 2024-12-14T07:07:08Z | :easy  | 16.133939701409084 | 4.972537594025855 | :review   |
| * | 2024-12-30T07:07:08Z |        |                    |                   |           |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to become this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# With workaround:

#+NAME: srsitem:2d51efcd-8523-4432-b074-50d1ba4c45b3
| ! | timestamp            | rating | stability | difficulty | state     |
|---+----------------------+--------+-----------+------------+-----------|
|   | 2024-12-11T18:37:19Z |        |       0.0 |        0.0 | :new      |
|   | 2024-12-11T18:38:23Z | :again |      0.41 |       7.21 | :learning |
|   | 2024-12-11T18:39:05Z | :easy  |      0.94 |       6.08 | :review   |
|   | 2024-12-14T07:07:08Z | :easy  |     16.13 |       4.97 | :review   |
| * | 2024-12-30T07:07:08Z |        |           |            |           |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, I already had some existing data in my &lt;code&gt;chinese.org&lt;/code&gt; file.
To truncate that data, I used this lisp expression replacement with
&lt;code&gt;query-replace-regexp&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[0-9]+\.\([0-9]+\) → \,(if (length&amp;gt; \1 2) (format &amp;quot;%0.02f&amp;quot; \#0) \0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The regexp matches the numbers in the &lt;code&gt;stability&lt;/code&gt; and &lt;code&gt;difficulty&lt;/code&gt;
columns.  My &lt;code&gt;chinese.org&lt;/code&gt; file conveniently contains no other numbers
in decimal-point notation.  The lisp expression checks if the part
after the decimal point contains more than two numbers, since I don't
want &lt;code&gt;0.0&lt;/code&gt; to become &lt;code&gt;0.00&lt;/code&gt;.  If it does, then the &lt;code&gt;format&lt;/code&gt; expression
rounds it to two decimal places.  Otherwise, the match is left as-is.
Note that &lt;code&gt;\#0&lt;/code&gt; in the replacement text converts the match string to a
number, which is required by the &lt;code&gt;%f&lt;/code&gt; format type specifier.&lt;/p&gt;
&lt;p&gt;After replacing all of the numbers in the buffer, I ran a quick
keyboard macro to align all of the &lt;code&gt;srsitem&lt;/code&gt; tables to fit the
narrower contents, but I'll leave that as an exercise to the reader.&lt;/p&gt;
&lt;p&gt;I hope this helps you.  Feel free to &lt;a href=&quot;../contact.md&quot;&gt;contact me&lt;/a&gt; with suggestions,
corrections, or questions!&lt;/p&gt;
&lt;h1&gt;Footnotes&lt;/h1&gt;
&lt;h4&gt;1&lt;/h4&gt;
&lt;p&gt;Workaround to truncate the data:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(cl-defmethod org-srs-algorithm-repeat :around ((_ fsrs-scheduler) _)
  (let ((output (cl-call-next-method)))
    (setf (alist-get 'stability output) (format &amp;quot;%.02f&amp;quot; (alist-get 'stability output))
          (alist-get 'difficulty output) (format &amp;quot;%.02f&amp;quot; (alist-get 'difficulty output)))
    output))
&lt;/code&gt;&lt;/pre&gt;
</content></entry><entry><title>Mirror Worg to a Hyperdrive</title><id>https://breatheoutbreathe.in/posts/mirror-worg-to-a-hyperdrive.html</id><author><name>Joseph Turner</name><email>contact@breatheoutbreathe.in</email></author><updated>2024-12-16T12:00:00Z</updated><link href="https://breatheoutbreathe.in/posts/mirror-worg-to-a-hyperdrive.html" rel="alternate" /><content type="html">&lt;p&gt;During the Q&amp;amp;A session of the &lt;a href=&quot;https://emacsconf.org/2024/talks/hyperdrive/&quot;&gt;EmacsConf 2024 hyperdrive.el talk&lt;/a&gt;, Ihor
Radchenko suggested using hyperdrive to distribute &lt;a href=&quot;https://git.sr.ht/~bzg/worg/&quot;&gt;worg&lt;/a&gt;, the
community-driven Org documentation written in Org format.&lt;/p&gt;
&lt;p&gt;Thanks, Ihor!&lt;/p&gt;
&lt;h1&gt;Mirror &lt;code&gt;worg&lt;/code&gt; to a hyperdrive&lt;/h1&gt;
&lt;p&gt;You can run these commands on a local or remote machine.  I chose to
mirror &lt;code&gt;worg&lt;/code&gt; to a hyperdrive on a remote Debian 12 VPS so that the
content stays available when I turn off my computer.&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;dtach&lt;/code&gt; and &lt;a href=&quot;https://github.com/holepunchto/drives/&quot;&gt;drives cli&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install git dtach npm
npm install -g drives
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clone &lt;code&gt;worg&lt;/code&gt;, then create a new corestore and hyperdrive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://git.sr.ht/~bzg/worg/
cd worg
drives init
drives touch # Copy hyperdrive public key from output
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mirror the &lt;code&gt;worg&lt;/code&gt; repository into the newly created hyperdrive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# drives mirror --dry-run . bpb1bq6sdfajw4bok7k9tdmrq153pbwzcyfkrqrfxw3hhihyddhy # Dry run first
dtach -n /tmp/drives-worg-session drives mirror --live . bpb1bq6sdfajw4bok7k9tdmrq153pbwzcyfkrqrfxw3hhihyddhy # From output of `drives touch`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;a href=&quot;https://github.com/holepunchto/drives/?tab=readme-ov-file#mirror-any-drive-into-another&quot;&gt;&lt;code&gt;drives&lt;/code&gt; ignores the &lt;code&gt;.git&lt;/code&gt; folder&lt;/a&gt; out of the box.  Running
the command inside a &lt;code&gt;dtach&lt;/code&gt; session is a quick and dirty way to make
the process persist after we've disconnected from the VPS.  A better
approach would be to write a service file to register the &lt;code&gt;drives&lt;/code&gt;
process with the init system (Debian 12 uses &lt;code&gt;systemd&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;To automatically pull the latest changes from the remote &lt;code&gt;worg&lt;/code&gt;
repository, add a cronjob with &lt;code&gt;crontab -e&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;30 0 * * * git -C ~/worg pull
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilá!  &lt;code&gt;worg&lt;/code&gt; is now continuously mirrored to a hyperdrive, which can
be explored, linked to, transcluded, and more using &lt;a href=&quot;https://ushin.org/hyperdrive/hyperdrive-manual.html&quot;&gt;hyperdrive.el&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hyper://bpb1bq6sdfajw4bok7k9tdmrq153pbwzcyfkrqrfxw3hhihyddhy/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you're viewing this file in Emacs with &lt;code&gt;hyperdrive.el&lt;/code&gt; installed,
follow this link to view the &amp;quot;Introduction to Org-Mode and Worg&amp;quot; in
the hyperdrive mirror:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hyper://bpb1bq6sdfajw4bok7k9tdmrq153pbwzcyfkrqrfxw3hhihyddhy/index.org#%3A%3A%2AIntroduction%20to%20Org-Mode%20and%20Worg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this helps you.  Feel free to &lt;a href=&quot;../contact.md&quot;&gt;contact me&lt;/a&gt; with suggestions,
corrections, or questions!&lt;/p&gt;
</content></entry><entry><title>Sourcehut Org Mode README</title><id>https://breatheoutbreathe.in/posts/sourcehut-org-mode-readme.html</id><author><name>Joseph Turner</name><email>contact@breatheoutbreathe.in</email></author><updated>2024-12-10T12:00:00Z</updated><link href="https://breatheoutbreathe.in/posts/sourcehut-org-mode-readme.html" rel="alternate" /><content type="html">&lt;p&gt;Out of the box, Sourcehut only renders &lt;code&gt;README&lt;/code&gt;, &lt;code&gt;README.md&lt;/code&gt;, and
&lt;code&gt;README.markdown&lt;/code&gt;.1&lt;/p&gt;
&lt;h1&gt;Workarounds to render &lt;code&gt;README.org&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;One proposed workaround is to export &lt;code&gt;README.org&lt;/code&gt; to &lt;code&gt;README.md&lt;/code&gt; (in a
git hook), and then commit &lt;code&gt;README.md&lt;/code&gt;, which will be rendered as HTML
on &lt;code&gt;git.sr.ht&lt;/code&gt;.  Another option is to &lt;a href=&quot;https://man.sr.ht/git.sr.ht/#setting-a-custom-readme&quot;&gt;set a custom README&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's how we render the &lt;a href=&quot;https://git.sr.ht/~ushin/hyperdrive.el&quot;&gt;README for hyperdrive.el on git.sr.ht&lt;/a&gt; by
setting a custom README:&lt;/p&gt;
&lt;h2&gt;Makefile + build script&lt;/h2&gt;
&lt;p&gt;The Elisp for exporting &lt;code&gt;README.org&lt;/code&gt; to HTML goes in a Makefile, which
simplifies the packaging process, since there's no extra &lt;code&gt;.el&lt;/code&gt; file
for linters and compilers to complain about.  Credit for &lt;a href=&quot;https://git.sr.ht/~ushin/hyperdrive.el/commit/727f1b4b145c777dc5080312b08dc2c8d019a591&quot;&gt;the idea to
use a Makefile&lt;/a&gt; goes to &lt;a href=&quot;https://emacsair.me/&quot;&gt;Jonas Bernoulli&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Add a file called &lt;code&gt;Makefile&lt;/code&gt; at the root of your project
(&lt;a href=&quot;https://git.sr.ht/~ushin/hyperdrive.el/tree/master/item/Makefile&quot;&gt;hyperdrive.el Makefile&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EMACS := emacs
EMACSQ := $(EMACS) -Q
BATCH := $(EMACSQ) --batch \
          --eval '(setq vc-handled-backends nil)' \
          --eval '(setq org-startup-folded nil)' \
          --eval '(setq org-element-cache-persistent nil)' \
          --eval '(setq gc-cons-threshold (* 50 1000 1000))'

README.html: README.org
        $(BATCH) --find-file $&amp;lt; --eval &amp;quot;(require 'ox-html)&amp;quot; \
          --eval '(org-html-export-to-html nil nil nil t)'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The build script installs &lt;code&gt;hut&lt;/code&gt; and &lt;code&gt;emacs&lt;/code&gt;, clones your repo, exports
&lt;code&gt;README.html&lt;/code&gt;, and sets the custom README.  Add a file called
&lt;code&gt;readme.yml&lt;/code&gt; in the &lt;code&gt;.builds/&lt;/code&gt; subdirectory of your project
(&lt;a href=&quot;https://git.sr.ht/~ushin/hyperdrive.el/tree/master/item/.builds/readme.yml&quot;&gt;hyperdrive.el .builds/readme.yml&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;image: archlinux
oauth: git.sr.ht/REPOSITORIES:RW git.sr.ht/PROFILE:RO
packages:
  - hut
  - emacs-nox
sources:
  - https://git.sr.ht/~ushin/hyperdrive.el
tasks:
  - update-readme: |
      cd hyperdrive.el
      make README.html
      hut git update --readme README.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this helps you.  Feel free to &lt;a href=&quot;../contact.md&quot;&gt;contact me&lt;/a&gt; with suggestions,
corrections, or questions!&lt;/p&gt;
&lt;h1&gt;Acknowledgements&lt;/h1&gt;
&lt;p&gt;Thank you to Thorben Günther, for &lt;a href=&quot;https://git.xenrox.net/~xenrox/custom-readme&quot;&gt;this example git repo&lt;/a&gt; which shows
how to set a custom README.&lt;/p&gt;
&lt;h1&gt;Footnotes&lt;/h1&gt;
&lt;h4&gt;1&lt;/h4&gt;
&lt;p&gt;Requests for Org-mode README rendering support on git.sr.ht:
&lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3Cfe7aa296-9c90-463d-b4e6-50eeb7e57428@localhost%3E&quot;&gt;Mar 2020&lt;/a&gt; &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3C5a7e210f869589d6abd0c0b165192b32c6325068.camel@zaclys.net%3E&quot;&gt;Feb 2021&lt;/a&gt; &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3C8830212A-2DDD-4BCA-AB0C-534D9B6B1EC7@traduction-libre.org%3E&quot;&gt;Oct 2021&lt;/a&gt; &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3C87fsp4szx5.fsf@complete.org%3E#%3C87fsp4szx5.fsf@complete.org%3E&quot;&gt;Jan 2022&lt;/a&gt; &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3Cm2wndqpgdf.fsf@New-Vulcan.localdomain%3E#%3Cm2wndqpgdf.fsf@New-Vulcan.localdomain%3E&quot;&gt;June 2022&lt;/a&gt; &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3C87sez21jhj.fsf@gilgamesh.mail-host-address-is-not-set%3E#%3CD0Z8J5N4PLG0.14FO1M1WUQO7C@cepl.eu%3E&quot;&gt;May 2024&lt;/a&gt; &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3C87cyib79ad.fsf@%3E&quot;&gt;Dec 2024&lt;/a&gt;&lt;/p&gt;
</content></entry></feed>