<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Emacs on Wai Hon's Blog</title><link>https://whhone.com/tags/emacs/</link><description>Recent content in Emacs on Wai Hon's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>whhone@gmail.com (Wai Hon Law)</managingEditor><webMaster>whhone@gmail.com (Wai Hon Law)</webMaster><lastBuildDate>Fri, 03 Jul 2026 20:00:00 -0700</lastBuildDate><atom:link href="https://whhone.com/tags/emacs/index.xml" rel="self" type="application/rss+xml"/><item><title>One-Line-Per-Day Diary in Org-mode</title><link>https://whhone.com/posts/one-line-per-day-diary-in-org-mode/</link><pubDate>Fri, 03 Jul 2026 20:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/one-line-per-day-diary-in-org-mode/</guid><description>&lt;p&gt;Recently, I came across these plain text systems and concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://georgecoghill.wordpress.com/2018/01/01/the-one-line-per-day-one-page-plain-text-daily-journal/"&gt;The One-Line-Per-Day, One-Page Plain Text Daily Journal&lt;/a&gt;&lt;/strong&gt;: A single, reverse-chronological plain text file to record only the most significant event of each day in a single line.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.google.com/search?q=obtf+one+big+text+file"&gt;OBTF (One Big Text File)&lt;/a&gt;&lt;/strong&gt;: Putting everything in a big text file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They resonated me because they match very closely with how I keep my daily diary in Org-mode!&lt;/p&gt;
&lt;h2 id="the-one-line-per-day-format"&gt;The One-Line-Per-Day Format&lt;/h2&gt;
&lt;p&gt;I keep my diary in a one-line-per-day format. Under this system, each day is represented by a single line using Org-mode&amp;rsquo;s description list syntax:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Level-1 heading:&lt;/strong&gt; The Year (e.g., &lt;code&gt;* 2026&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Level-2 heading:&lt;/strong&gt; The Week, containing the week number, date range, and a brief weekly highlight (e.g., &lt;code&gt;** W25 (06-15 - 06-21) - Weekly highlight&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;List items:&lt;/strong&gt; One line per day, starting with an inactive timestamp and a description separator (&lt;code&gt;::&lt;/code&gt;) followed by the day&amp;rsquo;s events.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is an example illustrating a complete recent week:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-org" data-lang="org"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;*&lt;/span&gt;&lt;span class="gs"&gt; 2026&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;**&lt;/span&gt; W26 (06-22 - 06-28) - Run 3 times. Mele X1! CPU PL1/PL2/Multpilers. TPM: LUKS + SSH.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-22 Mon] :: Team dinner.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-23 Tue] :: Running. Daycare fire false alarm. Primes day Mini PC research.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-24 Wed] :: Buy Mele X1.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-25 Thu] :: Running. Benchmark Mele X1. Good single core performance. Emacs startup time ~0.4s.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-26 Fri] :: Bike to pick up. Dinner: YAYOI.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-27 Sat] :: TIL: Performance Copilot (pcp). Arch: migrate to systemd &lt;span class="gd"&gt;+ sd-encrypt. TPM: LUKS unlock +&lt;/span&gt; SSH Agent.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- &lt;/span&gt;[2026-06-28 Sun] :: Running. Order Square Paper. Termux workaround with non-local network.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="daily-entries"&gt;Daily Entries&lt;/h2&gt;
&lt;p&gt;At the end of every day, I go to the top of &lt;code&gt;diary.org&lt;/code&gt;, add a line for today with the things that worth remembering.&lt;/p&gt;
&lt;p&gt;To make writing daily entries frictionless, I use a simple Emacs Lisp helper. I use the following function to insert today&amp;rsquo;s date formatted as an inactive timestamp:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/insert-current-date&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Insert today&amp;#39;s date in Org inactive format.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;format-time-string&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;[%Y-%m-%d %a]&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I bind this command to a key combination for quick access:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;keymap-global-set&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;C-c s d&amp;#34;&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;my/insert-current-date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this helper, starting a daily log is as simple as typing &lt;code&gt;-&lt;/code&gt; (or using &lt;code&gt;M-RET&lt;/code&gt; to start a list item), calling the function, typing &lt;code&gt;::&lt;/code&gt;, and entering the daily highlight.&lt;/p&gt;
&lt;h2 id="weekly-highlight"&gt;Weekly Highlight&lt;/h2&gt;
&lt;p&gt;At the end of each week (Sunday), I pick the things worth highlighting from my daily entries and put them in the weekly heading. This allows me to look back at my past weeks, know what happened, and feel like my days count.&lt;/p&gt;
&lt;h2 id="why-this-works-well"&gt;Why This Works Well&lt;/h2&gt;
&lt;p&gt;This format has greatly improved both writing and searching:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Self-Contained Search Results:&lt;/strong&gt; Since the date and the content of the entry reside on the same line, searching with &lt;code&gt;consult-line&lt;/code&gt; or &lt;code&gt;ripgrep&lt;/code&gt; returns the full context directly. A search for a keyword like &amp;ldquo;sourdough&amp;rdquo; immediately displays the date it happened without needing to check parent headings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compact File Structure:&lt;/strong&gt; The outline remains clean and fast to fold and navigate, with only weeks and years as headings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lower Friction:&lt;/strong&gt; Writing a single line per day lowers the cognitive barrier to journaling. Even on busy days, it is easy to jot down a quick summary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am also using a &lt;code&gt;diary-work.org&lt;/code&gt; to track my daily work similarly.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This layout is a lightweight and sustainable way to keep a daily log. By using Org-mode&amp;rsquo;s description lists and automating timestamp insertions, the diary remains clean, highly readable, and easily searchable for years to come.&lt;/p&gt;
&lt;h2 id="appendix-screenshot"&gt;Appendix: Screenshot&lt;/h2&gt;
&lt;p&gt;This is how the &lt;code&gt;diary.org&lt;/code&gt; looks like:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-diary.png"&gt;
&lt;/figure&gt;</description></item><item><title>Refining Org-mode Deadlines</title><link>https://whhone.com/posts/refining-org-mode-deadlines/</link><pubDate>Sun, 24 May 2026 22:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/refining-org-mode-deadlines/</guid><description>&lt;p&gt;For a long time, I didn&amp;rsquo;t use deadlines in Org-mode at all. However, over the past year (or perhaps even longer, though I forget exactly when), I have found myself relying on them more and more. Deadlines have quietly become the backbone of my day-to-day productivity.&lt;/p&gt;
&lt;h2 id="a-new-prioritization-hierarchy"&gt;A New Prioritization Hierarchy&lt;/h2&gt;
&lt;p&gt;In my earlier post on &lt;a href="https://whhone.com/posts/org-mode-task-management/"&gt;task management workflow&lt;/a&gt;, my day-to-day focus was heavily centered around scheduled dates and TODO states. Today, my prioritization hierarchy has fundamentally evolved to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deadline + Priority&lt;/strong&gt; &amp;gt; &lt;strong&gt;Schedule&lt;/strong&gt; &amp;gt; &lt;strong&gt;TODO States&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Deadlines + Priorities&lt;/strong&gt; represent my primary focus. Deadlines show what must be delivered and when, while priorities help determine which deadline to tackle first. Together, they serve as the main driver for my planning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheduled&lt;/strong&gt; dates are reserved for habits, recurring chores, or temporary &amp;ldquo;snoozing&amp;rdquo; (hiding tasks until they need to be revisited).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TODO States&lt;/strong&gt; (like &lt;code&gt;NEXT&lt;/code&gt; or &lt;code&gt;PROG&lt;/code&gt;) act as a backlog of next actions. If my deadline and prioritized items are blocked or completed, I look here to see what to pick up next.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="default-org-agenda-view"&gt;Default Org Agenda View&lt;/h3&gt;
&lt;p&gt;However, the default deadline behavior in Org-mode didn&amp;rsquo;t really work for me. Out of the box, deadlines look identical to scheduled tasks, and the short warning window makes them hard to plan around.&lt;/p&gt;
&lt;p&gt;Here is the default Org agenda view:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-deadline-before.png"&gt;
&lt;/figure&gt;
&lt;h3 id="customized-org-agenda-view"&gt;Customized Org Agenda View&lt;/h3&gt;
&lt;p&gt;To support this deadline-first workflow, here are two tricks I used to turn Org-mode deadlines into a powerful planning tool.&lt;/p&gt;
&lt;p&gt;Here is the customized Org agenda view:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-deadline-after.png"&gt;
&lt;/figure&gt;
&lt;h2 id="trick-1-visual-deadlines-with-custom-leaders-and-sorting"&gt;Trick 1: Visual Deadlines with Custom Leaders and Sorting&lt;/h2&gt;
&lt;p&gt;In the Org agenda view, distinguishing scheduled tasks from deadlines at a glance is surprisingly difficult. Because they share a similar layout and color palette, a looming deadline can easily get lost in a sea of routine scheduled chores.&lt;/p&gt;
&lt;p&gt;To solve this, I customized the &amp;ldquo;leaders&amp;rdquo; (the prefix text shown in the agenda) by prepending a bright red circle emoji (🔴) to all deadline items. This makes them immediately pop out.&lt;/p&gt;
&lt;p&gt;Also, sort deadeline at the very top lines by customizing the &lt;code&gt;org-agenda-sorting-strategy&lt;/code&gt; to clearly separate &amp;ldquo;deadline&amp;rdquo; and &amp;ldquo;scheduled&amp;rdquo; items.&lt;/p&gt;
&lt;p&gt;Here is the Elisp configuration I use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Shorten the leaders to reserve spaces for the repeater.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-scheduled-leaders&lt;/span&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sched&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;S.%2dx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-deadline-leaders&lt;/span&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;🔴 0d&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;🔴%2dd&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;🔴-%1dd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/org-agenda-repeater&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;The repeater shown in org-agenda-prefix for agenda.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;pom&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-get-at-bol&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-marker&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-get-scheduled-time&lt;/span&gt; &lt;span class="nv"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-get-deadline-time&lt;/span&gt; &lt;span class="nv"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%5s: &amp;#34;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-get-repeat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;------------&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Add repeater and effort to the agenda prefixes.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-prefix-format&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;agenda&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34; %i %-12:c%?-12t%s%(my/org-agenda-repeater)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Add deadline-up to agenda to separate schedule and deadline.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-sorting-strategy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;agenda&lt;/span&gt; &lt;span class="nv"&gt;deadline-up&lt;/span&gt; &lt;span class="nv"&gt;habit-down&lt;/span&gt; &lt;span class="nv"&gt;time-up&lt;/span&gt; &lt;span class="nv"&gt;priority-down&lt;/span&gt; &lt;span class="nv"&gt;category-keep&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is an extension of my previous post on &lt;a href="https://whhone.com/posts/org-agenda-repeated-tasks/"&gt;distinguishing repeated tasks&lt;/a&gt;. By shortening the scheduled leaders, we free up horizontal space for the repeater interval. The addition of the 🔴 emoji ensures that any item with an active deadline immediately commands attention.&lt;/p&gt;
&lt;h2 id="trick-2-expanding-the-warning-window-to-a-month"&gt;Trick 2: Expanding the Warning Window to a Month&lt;/h2&gt;
&lt;p&gt;By default, Org-mode starts warning you about a deadline 7 days in advance. While a week sounds reasonable, in practice, it is often too short.&lt;/p&gt;
&lt;p&gt;When a deadline suddenly appears on your agenda with only 7 days to go, it can easily disrupt your cadence. Since 7 days technically translates to only 5 working days, you might already be fully committed to other urgent tasks. Instead of helping you plan, a short warning window simply induces stress.&lt;/p&gt;
&lt;p&gt;To fix this, I increased the default warning period from a week to a month (35 days, or 5 weeks):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-deadline-warning-days&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With a 35-day warning window, upcoming major deadlines are visible on the agenda far in advance. This gives me ample time to adjust my weekly plan and budget resources before the deadline becomes urgent.&lt;/p&gt;
&lt;p&gt;Of course, not every task warrants a month-long warning. For minor tasks, you can override the default warning period on a per-item basis. For instance, you can use the &lt;code&gt;-3d&lt;/code&gt; syntax to only trigger a warning 3 days prior:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-org" data-lang="org"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;DEADLINE: &lt;/span&gt;&lt;span class="cs"&gt;&amp;lt;2026-06-19 Fri ++28d -3d&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I used to find schedules and deadlines somewhat overlapping in Org-mode. However, separating them into distinct roles, by using custom leaders for urgency and a longer warning window for planning, has made my agenda a lot easier to parse. It is a simple config tweak, but it has made my planning a bit smoother.&lt;/p&gt;</description></item><item><title>Introducing markdown-indent-mode</title><link>https://whhone.com/posts/markdown-indent-mode/</link><pubDate>Sun, 08 Mar 2026 16:30:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/markdown-indent-mode/</guid><description>&lt;h2 id="the-itch"&gt;The Itch&lt;/h2&gt;
&lt;p&gt;One thing I particularly love about &lt;code&gt;org-mode&lt;/code&gt; is &lt;code&gt;org-indent-mode&lt;/code&gt;, which
visually indents content under each heading based on its level. It makes long
org files much easier to read and navigate.&lt;/p&gt;
&lt;p&gt;Occasionally, I need to edit Markdown files — and every time I do, I miss that
clean visual hierarchy. So I vibe-coded a first version over a weekend.&lt;/p&gt;
&lt;h2 id="vibe-coding"&gt;Vibe-Coding&lt;/h2&gt;
&lt;p&gt;The idea translates naturally from &lt;code&gt;org-indent.el&lt;/code&gt;, which ships with Emacs.
Headings marked with &lt;code&gt;#&lt;/code&gt; instead of &lt;code&gt;*&lt;/code&gt;, same concept.&lt;/p&gt;
&lt;p&gt;The first version worked partially, but was full of edge
cases: code fences confusing the heading parser, list items indenting wrong,
wrapped lines losing alignment. I kept using it day-to-day, tweaking it when
something looked off, and simplifying whenever I found a cleaner approach.
Eventually it reached a state I was genuinely happy with.&lt;/p&gt;
&lt;p&gt;At that point I thought it might be useful to others too, so I decided to share it.&lt;/p&gt;
&lt;h2 id="first-attempt-merging-into-markdown-mode"&gt;First Attempt: Merging into markdown-mode&lt;/h2&gt;
&lt;p&gt;My first instinct was to contribute this directly to &lt;a href="https://github.com/jrblevin/markdown-mode"&gt;markdown-mode&lt;/a&gt;, the de-facto
Markdown package for Emacs, so all users could get it without installing
anything extra. It turns out this is a long-anticipated feature — there have
been open issues requesting it for years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jrblevin/markdown-mode/issues/330"&gt;Issue #330&lt;/a&gt; (opened in 2018)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jrblevin/markdown-mode/issues/679"&gt;Issue #679&lt;/a&gt; (opened in 2021)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I opened &lt;a href="https://github.com/jrblevin/markdown-mode/pull/935"&gt;a pull request&lt;/a&gt; to add the feature directly into markdown-mode.&lt;/p&gt;
&lt;h2 id="going-standalone-package"&gt;Going Standalone Package&lt;/h2&gt;
&lt;p&gt;The markdown-mode maintainer reviewed the PR and suggested that this would be
better as a standalone package since it would be a burden for them to maintain
the new feature.&lt;/p&gt;
&lt;p&gt;I took the suggestion, refactored the code into its own package:
&lt;code&gt;markdown-indent-mode&lt;/code&gt;, and submitted it to &lt;a href="https://github.com/melpa/melpa"&gt;MELPA&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-melpa-journey"&gt;The MELPA Journey&lt;/h2&gt;
&lt;p&gt;Submitting to MELPA was a learning experience in itself. I picked up a few tools
along the way for checking Elisp package quality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;checkdoc&lt;/code&gt; — checks that docstrings follow Emacs conventions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package-lint&lt;/code&gt; — catches common packaging issues&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/riscy/melpazoid"&gt;&lt;code&gt;melpazoid&lt;/code&gt;&lt;/a&gt; — an automated checker specifically designed for MELPA submissions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The MELPA maintainers are volunteers who typically review PRs on weekends, so
the process took a few days.&lt;/p&gt;
&lt;h2 id="what-markdown-indent-mode-does"&gt;What markdown-indent-mode Does&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what it does — nothing fancy, just the basics done cleanly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uses &lt;code&gt;line-prefix&lt;/code&gt; and &lt;code&gt;wrap-prefix&lt;/code&gt; text properties for visual indentation&lt;/li&gt;
&lt;li&gt;Content under a heading is indented according to the heading level&lt;/li&gt;
&lt;li&gt;Leading &lt;code&gt;#&lt;/code&gt; symbols are hidden&lt;/li&gt;
&lt;li&gt;List items are indented to align with their content&lt;/li&gt;
&lt;li&gt;Everything is purely visual: no buffer content is ever modified&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;This package is available on MELPA. You can use &lt;code&gt;use-package&lt;/code&gt; to install it and
add hook to enable it for &lt;code&gt;markdown-mode&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;markdown-indent-mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:hook&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;markdown-mode&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;markdown-indent-mode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or toggle it on demand with &lt;code&gt;M-x markdown-indent-mode&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://whhone.com/ox-hugo/markdown-indent-mode-off.png" alt=""&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://whhone.com/ox-hugo/markdown-indent-mode-on.png" alt=""&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The source is available at &lt;a href="https://github.com/whhone/markdown-indent-mode"&gt;https://github.com/whhone/markdown-indent-mode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you ever find yourself editing Markdown in Emacs, give it a try and let me
know what you think!&lt;/p&gt;</description></item><item><title>Distinguish Repeated Tasks in Org Agenda</title><link>https://whhone.com/posts/org-agenda-repeated-tasks/</link><pubDate>Fri, 03 Jan 2025 18:00:00 -0800</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/org-agenda-repeated-tasks/</guid><description>&lt;h2 id="problem-repeated-or-not"&gt;Problem: Repeated or Not?&lt;/h2&gt;
&lt;p&gt;In the Org agenda view, &lt;a href="https://orgmode.org/manual/Repeated-tasks.html"&gt;repeated&lt;/a&gt; and non-repeated tasks look identical. This similarity makes me hesitant to mark a task as DONE; if a task that should be recurring is missing its repeater (e.g., &lt;code&gt;++1w&lt;/code&gt;), it will disappear permanently from the agenda instead of being rescheduled.&lt;/p&gt;
&lt;p&gt;It would be helpful if repeated tasks were visually distinct in the Org agenda, for example, by displaying the repeater interval (&lt;code&gt;++1w&lt;/code&gt;) as part of the entry.&lt;/p&gt;
&lt;h2 id="solution-setting-the-prefix-format"&gt;Solution: Setting the Prefix Format&lt;/h2&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-agenda-repeat-prefix-compare.png"&gt;
&lt;/figure&gt;
&lt;p&gt;Thanks to a &lt;a href="https://fosstodon.org/@yantar92/113769144726128359"&gt;tip&lt;/a&gt; from yantar92 (the current Org maintainer), I was able to add the repeater to the Org agenda using &lt;code&gt;org-agenda-prefix-format&lt;/code&gt;. The process involves three steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shorten the scheduled leaders to make space for the repeater.&lt;/li&gt;
&lt;li&gt;Add a function to retrieve the formatted repeater.&lt;/li&gt;
&lt;li&gt;Include the new function in &lt;code&gt;org-agenda-prefix-format&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This requires only a few lines of Elisp:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Shorten leaders to reserve space for the repeater.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-scheduled-leaders&lt;/span&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sched.&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;S.%2dx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-deadline-leaders&lt;/span&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Deadl.&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;In%2dd&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;D.%2dx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/org-agenda-repeater&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Return the repeater string for the current agenda entry.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-before-first-heading-p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;-------&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;; Align with the time grid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%5s: &amp;#34;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-get-repeat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Add `my/org-agenda-repeater&amp;#39; to the agenda prefix format.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setcdr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;assoc&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;agenda&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-prefix-format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34; %i %-12:c%?-12t%s%(my/org-agenda-repeater)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="alternatives"&gt;Alternatives&lt;/h2&gt;
&lt;h3 id="using-the-agenda-finalize-hook--hacky"&gt;Using the Agenda Finalize Hook (Hacky)&lt;/h3&gt;
&lt;p&gt;Before learning about yantar92&amp;rsquo;s tip, I used a hackier solution (&lt;a href="https://gist.github.com/whhone/214fa34d57ec6eebc09f3c45bc153378"&gt;gist&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fetch the repeater using &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Text-Properties.html"&gt;text properties&lt;/a&gt; associated with the agenda entry.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;org-agenda-finalize-hook&lt;/code&gt;, which runs after the agenda view is built but before it is displayed, to insert the repeater string.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="using-column-view--cluttered"&gt;Using Column View (Cluttered)&lt;/h3&gt;
&lt;p&gt;Prior to that, I tried another solution using column view (&lt;code&gt;M-x org-agenda-columns&lt;/code&gt;). This method involves setting the &lt;code&gt;:COLUMNS:&lt;/code&gt; property to display the &lt;code&gt;SCHEDULED&lt;/code&gt; date as a separate column.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-agenda-columns-with-scheduled.png"&gt;
&lt;/figure&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-columns-default-format-for-agenda&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;%TODO %PRIORITY(Pri) %60ITEM(Task) %SCHEDULED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While this approach works and is built-in, the resulting display is verbose and difficult to parse visually. This led me to seek a better solution with custom Elisp.&lt;/p&gt;</description></item><item><title>Using Emacs in a Terminal</title><link>https://whhone.com/posts/emacs-in-a-terminal/</link><pubDate>Fri, 23 Aug 2024 20:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/emacs-in-a-terminal/</guid><description>&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/24-bit-color.png"&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;Fun fact: a coworker thought I was using a GUI application when they saw my terminal Emacs.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;At my workplace, most coding tasks must be performed on a remote workstation (or a browser-based IDE) due to policies prohibiting local storage of source code and the limited development tools available on company laptops.&lt;/p&gt;
&lt;p&gt;Coding with Emacs on a remote workstation requires access via SSH or a remote desktop connection. I have experimented with various solutions, including Tramp, Chrome Remote Desktop, &lt;a href="https://docs.gtk.org/gtk3/broadway.html"&gt;GTK Broadway&lt;/a&gt;, &lt;a href="https://github.com/Xpra-org/xpra"&gt;xpra&lt;/a&gt;, and &lt;a href="https://github.com/wayland-transpositor/wprs"&gt;wprs&lt;/a&gt;. However, these proved buggy, resource-intensive, or slow. Consequently, I settled on the reliable terminal-based approach using SSH and Tmux.&lt;/p&gt;
&lt;h2 id="issues"&gt;Issues&lt;/h2&gt;
&lt;p&gt;Initially, I encountered several issues using Emacs in the terminal, as things were not working out-of-the-box:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Emacs was no longer colorful.&lt;/li&gt;
&lt;li&gt;Some keybindings did not work.&lt;/li&gt;
&lt;li&gt;I could not copy text to the native clipboard.&lt;/li&gt;
&lt;li&gt;Version control diff highlighting was not visible.&lt;/li&gt;
&lt;li&gt;Opening links in a local browser was not easy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over time, I discovered solutions and workarounds for these issues and began to enjoy some extra benefits I had not expected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the same Emacs instance across multiple computers.&lt;/li&gt;
&lt;li&gt;Eliminating Crostini on my Chromebook.&lt;/li&gt;
&lt;li&gt;Reducing the need to synchronize files and resolve conflicts.&lt;/li&gt;
&lt;li&gt;Remote file editing is faster than Tramp.&lt;/li&gt;
&lt;li&gt;Providing a more native terminal experience via Tmux multiplexing (compared to &lt;code&gt;vterm&lt;/code&gt;, &lt;code&gt;shell&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;No font size scaling issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="solutions"&gt;Solutions&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s how I configured Emacs for comfortable terminal use.&lt;/p&gt;
&lt;h3 id="pick-a-good-terminal-emulator"&gt;Pick a Good Terminal Emulator&lt;/h3&gt;
&lt;p&gt;First, pick a terminal emulator that supports at least:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;true color, and&lt;/li&gt;
&lt;li&gt;OSC 52 (for system clipboard integration).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ideally, it should have fewer default keybindings or allow customization to avoid conflicts with Emacs.&lt;/p&gt;
&lt;p&gt;The default Chrome OS terminal (&lt;a href="https://hterm.org/"&gt;hterm&lt;/a&gt;) is what I currently use, as it meets all three requirements. I do not use Linux, macOS, or Windows frequently enough nowadays to offer recommendations for those platforms.&lt;/p&gt;
&lt;p&gt;Some terminals can even display images! E.g., the &lt;a href="https://sw.kovidgoyal.net/kitty/graphics-protocol/"&gt;terminal graphics protocol&lt;/a&gt; by Kitty, the &lt;a href="https://iterm2.com/documentation-images.html"&gt;inline image protocol&lt;/a&gt; by iTerm2, and the &lt;a href="https://en.wikipedia.org/wiki/Sixel"&gt;Sixel protocol&lt;/a&gt;. It would be nice if there were Emacs integration with them!&lt;/p&gt;
&lt;h3 id="use-emacs-in-a-tmux-session"&gt;Use Emacs in a Tmux Session&lt;/h3&gt;
&lt;p&gt;I use Emacs inside Tmux because it provides a persistent session that can be reattached from any computer or after losing an SSH connection. I also automatically reattach to the Tmux session upon SSH connection with this SSH config:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-ssh-config" data-lang="ssh-config"&gt;Host myhost
RemoteCommand tmux -u new -A -D -s main
RequestTTY yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alternative ssh command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -t myhost tmux -u new -A -D -s main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternative shell script approach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .bashrc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Attach to the main Tmux session if it is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - in SSH session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - not inside Tmux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - not inside Emacs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SSH_CLIENT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMUX&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$INSIDE_EMACS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# &amp;#34;-A&amp;#34; : reattach if session-name already exists&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# &amp;#34;-D&amp;#34; : detach other clients (ensure $SSH_TTY is always correct)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tmux new -A -D -s main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tmux also allows me to have multiple workspaces using Tmux windows (or sessions).&lt;/p&gt;
&lt;p&gt;If I need extra terminals, I split new panes or new windows. I find the Tmux terminal better than Emacs&amp;rsquo;s &lt;code&gt;term&lt;/code&gt;, &lt;code&gt;shell&lt;/code&gt;, &lt;code&gt;vterm&lt;/code&gt;, or &lt;code&gt;eat&lt;/code&gt;, because it is more native, faster, and easily distinguishable from an Emacs buffer.&lt;/p&gt;
&lt;p&gt;Also, I can add information like the system status (CPU, RAM, Disk, etc.) and the current time to the Tmux status bar.&lt;/p&gt;
&lt;p&gt;Optionally, change the default Tmux leader to reserve &lt;code&gt;C-b&lt;/code&gt; for moving the cursor.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-tmux" data-lang="tmux"&gt;set-option -g prefix M-\
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="make-emacs-colorful"&gt;Make Emacs Colorful&lt;/h3&gt;
&lt;p&gt;The screenshots below show the difference with and without true color (24-bit color) enabled.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without true color, low contrast&lt;/th&gt;
&lt;th&gt;With true color, high contrast&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://whhone.com/ox-hugo/emacs-without-truecolor.png" alt=""&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://whhone.com/ox-hugo/emacs-with-truecolor.png" alt=""&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There are a few ways to get true color in a terminal Emacs session:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a &lt;code&gt;TERM&lt;/code&gt; that supports true color, e.g., &lt;code&gt;xterm-direct&lt;/code&gt;. (This won&amp;rsquo;t work inside Tmux, which changes the term to &lt;code&gt;tmux-256color&lt;/code&gt;.)&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;COLORTERM&lt;/code&gt; environment variable to &lt;code&gt;truecolor&lt;/code&gt;. (&lt;strong&gt;preferred&lt;/strong&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;!--listend--&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .bashrc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;COLORTERM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;truecolor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are a few ways to get true color support in Tmux:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a &lt;code&gt;TERM&lt;/code&gt; that supports true color, e.g., &lt;code&gt;xterm-direct&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;Start tmux with the RGB feature, e.g., &lt;code&gt;tmux -T RGB&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;Override the features of your terminal in &lt;code&gt;.tmux.conf&lt;/code&gt;, e.g.:&lt;/li&gt;
&lt;/ol&gt;
&lt;!--listend--&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .tmux.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# See https://github.com/tmux/tmux/wiki/FAQ#how-do-i-use-rgb-colour.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -as terminal-overrides &lt;span class="s2"&gt;&amp;#34;,xterm-256color:RGB&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tip: Use &lt;a href="https://gist.github.com/whhone/6852e485e8ccf51fd3631ba29ab7fad2"&gt;this script&lt;/a&gt; to test for true color support.&lt;/p&gt;
&lt;h3 id="copy-text-to-native-clipboard--osc-52"&gt;Copy Text to Native Clipboard (OSC 52)&lt;/h3&gt;
&lt;p&gt;OSC 52 works by printing an unreadable sequence, &lt;code&gt;\033]52;c;[base64 data]\a&lt;/code&gt;, which instructs the terminal to alter the system clipboard with the base64-encoded data. To verify that OSC 52 is working for your terminal setup, run &lt;code&gt;printf &amp;quot;\033]52;c;$(printf \&amp;quot;Hello, world\&amp;quot; | base64)\a&amp;quot;&lt;/code&gt; from the terminal. It should put &amp;ldquo;Hello, world&amp;rdquo; into the system clipboard.&lt;/p&gt;
&lt;p&gt;In the Emacs config, install the &lt;a href="https://github.com/spudlyo/clipetty"&gt;Clipetty&lt;/a&gt; package, which sends text that you kill in Emacs to the native clipboard.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; init.el&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;clipetty&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:hook&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;after-init&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;global-clipetty-mode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To enable clipboard support in Tmux, add these lines to &lt;code&gt;.tmux.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# .tmux.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -g set-clipboard on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Required to make Clipetty work better on re-attach by appending&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;SSH_TTY&amp;#34; to &amp;#34;update-environment&amp;#34;. See&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# https://github.com/spudlyo/clipetty?tab=readme-ov-file#dealing-with-a-stale-ssh_tty-environment-variable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -ag update-environment &lt;span class="s2"&gt;&amp;#34;SSH_TTY&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it still does not work, try running the &lt;code&gt;printf&lt;/code&gt; verification command above and check &lt;a href="https://github.com/tmux/tmux/wiki/Clipboard"&gt;the Tmux clipboard wiki&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="open-link-in-local-browser"&gt;Open Link in Local Browser&lt;/h3&gt;
&lt;p&gt;I have two workarounds for opening a web link from a remote Emacs session in a local browser:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a mouse click if the terminal emulator can recognize links and open them locally.&lt;/li&gt;
&lt;li&gt;Copy the link to the local clipboard and paste it into the browser.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For (2), I added an advice to &lt;code&gt;browse-url&lt;/code&gt; to place the URL into the native clipboard via OSC 52. I then paste it into a local browser. I also added key and mouse bindings for the function to make copying more seamless.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;define-advice&lt;/span&gt; &lt;span class="nv"&gt;browse-url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;:around&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;orig-fun&lt;/span&gt; &lt;span class="kp"&gt;&amp;amp;rest&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;copy-url-if-terminal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;display-graphic-p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="nv"&gt;orig-fun&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;nth&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Clipetty link: %s&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;clipetty--emit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;clipetty--osc&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See &lt;a href="https://whhone.com/emacs-config/"&gt;my Emacs configuration&lt;/a&gt; for the full and updated version.&lt;/p&gt;
&lt;h3 id="tweak-for-xterm-paste"&gt;Tweak for xterm-paste&lt;/h3&gt;
&lt;p&gt;When pasting from the native clipboard, I would like to delete the region if one is active.&lt;/p&gt;
&lt;p&gt;This is like &lt;code&gt;(delete-selection-mode)&lt;/code&gt; for &lt;code&gt;xterm-paste&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;define-advice&lt;/span&gt; &lt;span class="nv"&gt;xterm-paste&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;:before&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;delete-active-region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Delete the selected text first before pasting from xterm.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;use-region-p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;delete-active-region&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="update-key-maps-that-works-in-terminal"&gt;Update Key Maps that Works in Terminal&lt;/h3&gt;
&lt;p&gt;The idea is to have a set of keybindings that the terminal can respond to.&lt;/p&gt;
&lt;p&gt;This part varies from person to person. For example, I have remapped:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C-x C-;&lt;/code&gt; to &lt;code&gt;C-x ;&lt;/code&gt; for &lt;code&gt;comment-line&lt;/code&gt; (&lt;code&gt;C-;&lt;/code&gt; is unsupported).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-c C-,&lt;/code&gt; to &lt;code&gt;C-c ,&lt;/code&gt; for &lt;code&gt;org-insert-structure-template&lt;/code&gt; (&lt;code&gt;C-,&lt;/code&gt; is unsupported).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can use &lt;code&gt;key-translation-map&lt;/code&gt; for the mapping.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; e.g., comment-line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;define-key&lt;/span&gt; &lt;span class="nv"&gt;key-translation-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;C-x ;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;C-x C-;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; e.g., org-insert-structure-template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;define-key&lt;/span&gt; &lt;span class="nv"&gt;key-translation-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;C-c ,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;C-c C-,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatively, if your terminal supports the &lt;a href="https://sw.kovidgoyal.net/kitty/keyboard-protocol/"&gt;Kitty Keyboard Protocol&lt;/a&gt; (KKP), you can use &lt;a href="https://github.com/benotn/kkp"&gt;kkp.el&lt;/a&gt; to make terminal Emacs handle those &amp;ldquo;impossible&amp;rdquo; keybindings (thanks to sned@ for the tip).&lt;/p&gt;
&lt;h3 id="show-diff-highlighting-with-margin"&gt;Show Diff Highlighting with Margin&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;diff-hl&lt;/code&gt; does not work in the terminal by default, and this issue had annoyed me for a while until I searched for a solution. It turns out that &lt;code&gt;diff-hl&lt;/code&gt; already has &lt;a href="https://github.com/dgutov/diff-hl/issues/155"&gt;a solution&lt;/a&gt;: using the &amp;ldquo;margin&amp;rdquo; to show the diff.&lt;/p&gt;
&lt;p&gt;I added this Elisp config to turn on &lt;code&gt;diff-hl-margin-mode&lt;/code&gt; whenever I am inside a terminal.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; init.el&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-hook&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;diff-hl-mode-on-hook&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;display-graphic-p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;diff-hl-margin-local-mode&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/diff-hl-margin-mode.png"&gt;
&lt;/figure&gt;
&lt;h3 id="enable-mouse-support"&gt;Enable Mouse Support&lt;/h3&gt;
&lt;p&gt;Don&amp;rsquo;t forget to enable mouse support.&lt;/p&gt;
&lt;p&gt;Note that there was a conflict where any mouse movement over Emacs would deactivate the Tmux prefix key (this is fixed in Tmux 3.5!). See &lt;a href="https://github.com/tmux/tmux/issues/4111"&gt;https://github.com/tmux/tmux/issues/4111&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; init.el&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;xterm-mouse-mode&lt;/span&gt; &lt;span class="mi"&gt;+1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-tmux" data-lang="tmux"&gt;# .tmux.conf
# Enables mouse support in Tmux (switching windows and panes,
# resizing panes, etc.). Setting mouse on or off does not disable
# Emacs&amp;#39;s xterm-mouse-mode.
set -g mouse on
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;For most Emacsers, GUI Emacs is still a better choice because it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has no network latency.&lt;/li&gt;
&lt;li&gt;Has better multimedia support (images, PDFs, etc.).&lt;/li&gt;
&lt;li&gt;Can handle all keybindings.&lt;/li&gt;
&lt;li&gt;Can interact with the clipboard natively.&lt;/li&gt;
&lt;li&gt;Can open links in a local browser easily.&lt;/li&gt;
&lt;li&gt;Is more customizable (font size per buffer, tooltips, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, if you, like me, need to work remotely or want to use the same Emacs instance across multiple computers via SSH and Tmux, I hope the tricks above can improve your setup, even at the expense of the aforementioned GUI features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update on 2025-09&lt;/strong&gt;: also check out this YouTube video &lt;a href="https://youtu.be/2_6OxZDoWvw"&gt;nobody knows that using Emacs in the terminal is so great&lt;/a&gt; by Jake B!&lt;/p&gt;</description></item><item><title>Text Replacement Tools for Large Codebases</title><link>https://whhone.com/posts/large-scale-replace-string/</link><pubDate>Wed, 25 Oct 2023 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/large-scale-replace-string/</guid><description>&lt;p&gt;10 years ago, when I became a software engineer, my mentor showed me how to replace all occurrences of a string (or a regex) under a large codebase effectively with a tool called &lt;a href="https://github.com/facebookarchive/codemod"&gt;codemod&lt;/a&gt;. It turned out to be one of the most useful text-editing techniques in my next 10 years, saving me many hours of &amp;ldquo;find and replace&amp;rdquo; manually.&lt;/p&gt;
&lt;p&gt;Over time, I discovered additional options for different use cases. Here they are.&lt;/p&gt;
&lt;h2 id="for-simple-replacement-sed"&gt;For Simple Replacement - &lt;code&gt;sed&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sed&lt;/code&gt; is my favorite for simple cases because it comes pre-installed in most operating systems.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replace all instances of `foo` (regex) with `bar` (regex) in a file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sed -i.bak &lt;span class="s2"&gt;&amp;#34;s/foo/bar/g&amp;#34;&lt;/span&gt; path/to/file
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replace all instances of `foo` (regex) with `bar` (regex) found by `find`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -name &lt;span class="s1"&gt;&amp;#39;*.cpp&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs sed -i.bak &lt;span class="s2"&gt;&amp;#34;s/foo/bar/g&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replace all instances of `foo` (regex) with `bar` (regex) in modified files under git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git diff --name-only &lt;span class="p"&gt;|&lt;/span&gt; xargs sed -i.bak &lt;span class="s2"&gt;&amp;#34;s/foo/bar/g&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replace all instances of `foo` (regex) with `bar` (regex) in modified files under hg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hg status --rev .^ -n &lt;span class="p"&gt;|&lt;/span&gt; xargs sed -i.bak &lt;span class="s2"&gt;&amp;#34;s/foo/bar/g&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If I encounter a performance issue, I will switch to &lt;code&gt;fastmod&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="for-very-large-code-base-fastmod"&gt;For Very Large Code Base - &lt;code&gt;fastmod&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/facebookincubator/fastmod"&gt;fastmod&lt;/a&gt; is a fast partial replacement for &lt;a href="https://github.com/facebookarchive/codemod"&gt;codemod&lt;/a&gt;. It is written in Rust and is designed for large-scale codebase refactoring.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replace a regex pattern in specific files or directories:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fastmod &lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt; -- &lt;span class="o"&gt;{{&lt;/span&gt;path/to/file path/to/directory ...&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To install, run &lt;code&gt;cargo install fastmod&lt;/code&gt;, assume you have Rust&amp;rsquo;s Cargo installed.&lt;/p&gt;
&lt;p&gt;For more use cases, see &lt;a href="https://tldr.inbrowser.app/pages/common/fastmod"&gt;tldr:fastmod&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="for-simple-replacement-in-emacs-dired"&gt;For Simple Replacement in Emacs - &lt;code&gt;dired&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;As an Emacser, I must also provide a way to achieve this in Emacs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;M-x dired&lt;/li&gt;
&lt;li&gt;Mark the required files and directories (&lt;code&gt;m&lt;/code&gt; to select, &lt;code&gt;t&lt;/code&gt; to toggle all)&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Q&lt;/code&gt; (&lt;code&gt;dired-do-find-regexp-and-replace&lt;/code&gt;) to replace string by regex
&lt;ol&gt;
&lt;li&gt;Press &lt;code&gt;y&lt;/code&gt; to accept.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;!&lt;/code&gt; to accept all.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="for-interactive-replacement-in-emacs-wgrep"&gt;For Interactive Replacement in Emacs - &lt;code&gt;wgrep&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wgrep&lt;/code&gt; stands for &amp;ldquo;Writable Grep Buffer&amp;rdquo;. It is a very flexible interactive replacement in Emacs. It gathers all occurrences in a buffer and then you can use whatever you want edit them.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search with &lt;code&gt;counsel&lt;/code&gt;, e.g., &lt;code&gt;counsel-rg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;C-c C-o (&lt;code&gt;ivy-occur&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;C-x C-q (&lt;code&gt;ivy-wgrep-change-to-wgrep-mode&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Edit (e.g., &lt;code&gt;replace-regex&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;C-c C-c (commit the changes)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more details on this method, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sam217pa.github.io/2016/09/11/nuclear-power-editing-via-ivy-and-ag/"&gt;Nuclear weapon multi-editing via Ivy and Ag · Samuel Barreto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://emacs-china.org/t/emacs/16537"&gt;Emacs 核弹级的功能与应用 - Emacs China&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="for-interactive-replacement-in-emacs-project-project-query-replace-regex"&gt;For Interactive Replacement in Emacs Project - &lt;code&gt;project-query-replace-regex&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;project.el&lt;/code&gt; provides a command called &lt;code&gt;project-query-replace-regex&lt;/code&gt; (default keybinding: &lt;code&gt;C-x p r&lt;/code&gt;) that allows you to interactively replace a regex pattern within a project.&lt;/p&gt;
&lt;h2 id="epilogue"&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;In most cases, I prefer &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;fastmod&lt;/code&gt; since they allow me to do it in a single command, which makes it easier to iterate the desired command (execute and revert for the right &amp;ldquo;foo&amp;rdquo; and &amp;ldquo;bar&amp;rdquo;).&lt;/p&gt;</description></item><item><title>Implementing the PARA Method in Org-mode</title><link>https://whhone.com/posts/para-org-mode/</link><pubDate>Sat, 31 Dec 2022 00:00:00 -0800</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/para-org-mode/</guid><description>&lt;p&gt;This post first talks about the PARA method and then my Org-mode implementation for organizing tasks and notes.&lt;/p&gt;
&lt;h2 id="the-para-method"&gt;The PARA Method&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://fortelabs.com/blog/para/"&gt;The PARA Method&lt;/a&gt; is a modern system for organizing digital information. It stands for Projects-Areas-Resources-Archives, the four categories of all digital information.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project&lt;/strong&gt;: a series of tasks linked to a goal, with a deadline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Area&lt;/strong&gt;: a sphere of activity with a standard to be maintained over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource&lt;/strong&gt;: a topic or theme of ongoing interest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Archive&lt;/strong&gt;: inactive items from the other 3 categories.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id="project-vs-area-vs-resource"&gt;Project vs Area vs Resource&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;Projects&amp;rdquo; and &amp;ldquo;Areas&amp;rdquo; are inherited from GTD. &amp;ldquo;Distinguish Project and Area&amp;rdquo; is a common challenge for GTD and PARA adopters, including me. I once had many unimportant projects without deadlines that cluttered my agenda. There are many discussions and articles on it&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and the model answer is to check (1) the nature of the task and (2) if there is a deadline.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;A project has (1) a specific outcome/goal and (2) a deadline.&lt;/li&gt;
&lt;li&gt;An area has (1) a standard to be maintained that (2) is continuous over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;In addition to the model answer, I have my interpretation,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Projects contain the tasks I should focus on&lt;/strong&gt; (because of the importance, urgency and desirability). They have higher priority over the other categories. I work on them first, especially when there is a chunk of uninterrupted time or my energy level is high. If a &amp;ldquo;project&amp;rdquo; does not come with a deadline, I set one for it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Areas contain the tasks I have to do.&lt;/strong&gt; I reserve some time for these tasks every day to keep areas maintained. I can also skip them for a while when the current projects are too demanding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resources contain the tasks I am interested to do.&lt;/strong&gt; When there is no pressure from projects or areas, I can choose to work on these tasks or simply take a rest.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Need my focus&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Have to do&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Maybe&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Want to do&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Maybe&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Maybe&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priority&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These three questions help decide which PARA categories a task should go to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Does it require my focus within a timeframe?&lt;/strong&gt; If yes, make it a &lt;strong&gt;project&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is it something I have to do?&lt;/strong&gt; If yes, move it to an &lt;strong&gt;area&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is it something I want to do?&lt;/strong&gt; If yes, move it to a &lt;strong&gt;resource&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="para-is-pragmatic"&gt;PARA is Pragmatic&lt;/h3&gt;
&lt;p&gt;On one hand, I organize my tasks. On the other hand, I know it is not necessary to get it perfect.&lt;/p&gt;
&lt;p&gt;Firstly, it takes a lot of effort. I move forward and do the actual work when the tasks are by-and-large in the right places that help to prioritize. The end goal should be the actual outcomes instead of organizing.&lt;/p&gt;
&lt;p&gt;Secondly, it is impossible. PARA is a system of single classification. Everything is either a project, an area, or a resource. In practice, a task or note can fall into multiple projects, areas, or interests. For example, &amp;ldquo;planning a trip for the 10-th anniversary&amp;rdquo; could arguably belong to either area &amp;ldquo;spouse&amp;rdquo; or area &amp;ldquo;travel&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Organizing should be forgiving. It is okay to organize wrongly as long as it works. PARA is not a system for perfectionism but for pragmatism.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s talk about my implementation!&lt;/p&gt;
&lt;h2 id="org-mode-implementation"&gt;Org-mode Implementation&lt;/h2&gt;
&lt;p&gt;I organize my tasks with PARA using a single &lt;code&gt;todo.org&lt;/code&gt; file.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/para-todo-org.png"&gt;
&lt;/figure&gt;
&lt;h3 id="tags-and-categories"&gt;Tags and Categories&lt;/h3&gt;
&lt;p&gt;I use &lt;strong&gt;tags&lt;/strong&gt; for the actual areas and resources. I assign each of them a specific tag to the corresponding subheadings. This gives me a set of &lt;a href="https://en.wikipedia.org/wiki/Controlled_vocabulary"&gt;controlled vocabularies&lt;/a&gt; for tagging projects and notes. For example, I know I should use &lt;code&gt;writing&lt;/code&gt; instead of &lt;code&gt;write&lt;/code&gt; or &lt;code&gt;blogging&lt;/code&gt; for area &amp;ldquo;writing&amp;rdquo;. I don&amp;rsquo;t create tags for projects (which are short-term and grow over time) to keep my tag set small and useful&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I use &lt;strong&gt;categories&lt;/strong&gt; for the PARA top categories, Project-Area-Resource. When looking at my org agenda view, I immediately know which tasks are my top focuses (Project), which are things I need to do (Area) or interested to do (Resource). I also know which exact area or resource they belong to with the tags on the right.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/para-agenda.png"&gt;
&lt;/figure&gt;
&lt;p&gt;Tip: I can filter tasks by category and tag by pressing &lt;code&gt;/&lt;/code&gt; (&lt;code&gt;M-x org-agenda-filter&lt;/code&gt;) and clear filters with &lt;code&gt;|&lt;/code&gt; (&lt;code&gt;M-x org-agenda-filter-remove-all&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="organizing-my-notes"&gt;Organizing my Notes&lt;/h3&gt;
&lt;p&gt;For project notes, I add or link them directly under the corresponding org subtree in &lt;code&gt;todo.org&lt;/code&gt; as I avoid adding project tags. When a project is completed or canceled, I convert the reusable part into the area or resource notes, and archive the project subtree to &lt;code&gt;todo.org_archive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I place the areas and resources notes under &lt;code&gt;~/note&lt;/code&gt;. I tag them with the tags defined in &lt;code&gt;todo.org&lt;/code&gt; and reserve the directory hierarchy&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; for the note types. This gives me the flexibility to have multiple tags for a note.&lt;/p&gt;
&lt;p&gt;Here is a simplified version of my org directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├─ todo.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└─ note
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ 20221125T211904--org-attach__emacs.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ meta
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └─ 20200202T222222--using-org-mode__emacs.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ reference
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ article
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├─ ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └─ 20221226T150449--creating-family-rules__parenting.org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ book
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ 20220907T134123--building-a-second-brain-tiago-forte__pkm.org
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;note/20221125T211904--org-attach__emacs.org&lt;/code&gt;&lt;/strong&gt; is a regular note.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;note/meta/20200202T222222--using-org-mode__emacs.org&lt;/code&gt;&lt;/strong&gt; is a meta note that connects other notes, like the &lt;code&gt;org-attach__eamcs.org&lt;/code&gt; above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;note/reference/article/20221226T150449--creating-family-rules__parenting.org&lt;/code&gt;&lt;/strong&gt; is a reference note for the article &amp;ldquo;&lt;a href="https://www.cdc.gov/parents/essentials/structure/rules.html"&gt;Creating Family Rules&lt;/a&gt;&amp;rdquo;, tagged with my resource &amp;ldquo;parenting&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;note/reference/book/20220907T134123--building-a-second-brain-tiago-forte__pkm.org&lt;/code&gt;&lt;/strong&gt; is a new book note for the book &amp;ldquo;Building a Second Brain&amp;rdquo; by Tiago Forte.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far, I don&amp;rsquo;t archive notes under &lt;code&gt;~/note&lt;/code&gt;. If I need to, I might use the tag &lt;code&gt;ARCHIVED&lt;/code&gt; and write some elisp to filter them.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Defining how I am going to organize my digital information reduces the cognitive load when working with tasks and notes. I can add or retrieve the information without thinking too much.&lt;/p&gt;
&lt;p&gt;I love to learn how other people works and I hope this post could be interesting and helpful to you, too. If you like it, you probably also check out these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tasshin.com/blog/implementing-a-second-brain-in-emacs-and-org-mode/"&gt;Tassin&amp;rsquo;s implementation&lt;/a&gt; which combines Org-mode and Evernote for tasks and notes respectively.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://renatgalimov.github.io/org-basb-code/#orgb77d8dc"&gt;Emacs CODE implementation&lt;/a&gt; which focuses on its note taking workflow with lots of details.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Search them yourself or read this great article: &lt;a href="https://fortelabs.com/blog/project-people-vs-area-people-are-you-running-a-sprint-or-a-marathon/"&gt;Project People vs. Area People - Forte Lab&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;It is &lt;a href="https://karl-voit.at/2022/01/29/How-to-Use-Tags/"&gt;suggested&lt;/a&gt; to &lt;em&gt;use a self-defined set of tags&lt;/em&gt; and &lt;em&gt;keep them as few as possible&lt;/em&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;That is why &lt;a href="https://whhone.com/posts/denote-with-subdirectories/"&gt;using denote with subdirectories&lt;/a&gt; is important for me.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Using Denote with Subdirectories</title><link>https://whhone.com/posts/denote-with-subdirectories/</link><pubDate>Thu, 08 Dec 2022 00:00:00 -0800</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/denote-with-subdirectories/</guid><description>&lt;p&gt;My &lt;a href="https://whhone.com/posts/denote-vs-org-roam/"&gt;last post&lt;/a&gt; mentioned the out-of-the-box experience of &lt;a href="https://protesilaos.com/emacs/denote"&gt;Denote&lt;/a&gt; is not so great, especially when having notes across many subdirectories. By default, Denote searches and creates notes from the root &lt;code&gt;denote-directory&lt;/code&gt;. I need to know the exact file location before searching for a note. I need to move note from the root directory to subdirectories after creating the note.&lt;/p&gt;
&lt;p&gt;The good news is that, these issues are now fixed and this post talks about how.&lt;/p&gt;
&lt;h2 id="1-search-by-filename-in-subdirectories"&gt;#1 Search by Filename in Subdirectories&lt;/h2&gt;
&lt;p&gt;By default, Denote searches only in the &lt;code&gt;denote-directory&lt;/code&gt;. To find a note hidden inside a subdirectory, the user needs to know the path to that file and navigate there. Needing to know the exact directory to be able to search for a file is counter-intuitive.&lt;/p&gt;
&lt;p&gt;However, thanks to &lt;a href="https://git.sr.ht/~protesilaos/denote/commit/6bb6da088211324cdd1ad35396c2988ccb2eccb7"&gt;a recent (2022-11-18) refactoring&lt;/a&gt; by nobiot@, it is no longer the case! The commit makes notes inside subdirectories searchable with common Denote commands like &lt;code&gt;denote-open-or-create&lt;/code&gt; and &lt;code&gt;denote-link-*&lt;/code&gt;. This greatly improves the out-of-the-box experience of Denote. Update Denote to a newer version including this new change!&lt;/p&gt;
&lt;h2 id="2-full-text-search-in-subdirectories"&gt;#2 Full-Text Search in Subdirectories&lt;/h2&gt;
&lt;p&gt;By default, Denote does not come with any command for full-text search.&lt;/p&gt;
&lt;p&gt;However, it could be done easily with &lt;code&gt;consult-ripgrep&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;consult-denote-ripgrep&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Search with ‘rg’ for files in denote-directory where the content matches a regexp.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;consult-ripgrep&lt;/span&gt; &lt;span class="nv"&gt;denote-directory&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="3-create-notes-in-subdirectories"&gt;#3 Create Notes in Subdirectories&lt;/h2&gt;
&lt;p&gt;By default, Denote creates notes in the root &lt;code&gt;denote-directory&lt;/code&gt;. Every time after creating a note, I need to move them manually to the right subdirectory. This is cumbersome.&lt;/p&gt;
&lt;p&gt;However, it turned out to be my fault of not reading the official documentation, which has already &lt;a href="https://protesilaos.com/emacs/denote#h:887bdced-9686-4e80-906f-789e407f2e8f"&gt;suggested&lt;/a&gt; two ways to select a specific directory when creating a new note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;includes &lt;code&gt;subdirectory&lt;/code&gt; to &lt;code&gt;denote-prompts&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;uses &lt;code&gt;M-x denote-subdirectory&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I go with (1) because it works with all Denote commands that could create a note, like &lt;code&gt;denote-open-or-create&lt;/code&gt; and &lt;code&gt;denote-link-or-create&lt;/code&gt;. It also allows other customizations to &lt;code&gt;denote-prompts&lt;/code&gt;. For example, I prefer setting &amp;ldquo;subdirectory&amp;rdquo; before &amp;ldquo;title&amp;rdquo; (reorder &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;subdirectory&lt;/code&gt;) and updating keyword later with &lt;code&gt;denote-keyword-*&lt;/code&gt; (remove &lt;code&gt;keyword&lt;/code&gt; from &lt;code&gt;denote-prompts&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;denote-prompts&lt;/span&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;subdirectory&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have too much subdirectories, use &lt;code&gt;denote-excluded-directories-regexp&lt;/code&gt; to exclude some of them.&lt;/p&gt;
&lt;h2 id="comparing-with-org-roam-again"&gt;Comparing with Org-roam Again&lt;/h2&gt;
&lt;p&gt;With these tweaks, I can now use Denote as my primary note-taking package.&lt;/p&gt;
&lt;p&gt;I mentioned a concern with the link type &lt;code&gt;denote:&lt;/code&gt; in my &lt;a href="https://whhone.com/posts/denote-vs-org-roam/"&gt;last post&lt;/a&gt;. It turns out to be not concerning because it is easy to migrate to or away from this link type with some elisp. I don&amp;rsquo;t use other features like org-roam-daily, org-roam-bibtex, or org-roam-ui at all. The last step before removing Org-roam is probably migrating the existing links.&lt;/p&gt;</description></item><item><title>Denote vs Org-roam</title><link>https://whhone.com/posts/denote-vs-org-roam/</link><pubDate>Sun, 27 Nov 2022 00:00:00 -0800</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/denote-vs-org-roam/</guid><description>&lt;p&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2022-12-08: Check out the followup post &lt;a href="https://whhone.com/posts/denote-with-subdirectories/"&gt;Using Denote with Subdirectories&lt;/a&gt; to improve the out-of-the-box experience of Denote.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://protesilaos.com/emacs/denote"&gt;Denote&lt;/a&gt; is getting popular in the Emacs community recently. I decided to give it a shot, learn how it works and see if I should migrate from Org-roam to it. I am writing this blog post to compares Denote with Org-roam.&lt;/p&gt;
&lt;p&gt;In essence, Denote is a note-taking package for Emacs based on its file-naming scheme, which works for all kinds of files. Org-roam is a note-taking package for Org-mode, utilizing a database indexing org files.&lt;/p&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;h3 id="identifier-and-linking"&gt;Identifier and Linking&lt;/h3&gt;
&lt;p&gt;Denote uses the timestamp in its file-naming scheme as the identifier and uses its link type &lt;code&gt;denote:&lt;/code&gt; for linking. This approach allows notes to be taken in other formats (&lt;code&gt;.md&lt;/code&gt; and &lt;code&gt;.txt&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Org-roam uses the &lt;code&gt;ID&lt;/code&gt; properties as the identifier and the built-in link type &lt;code&gt;id:&lt;/code&gt; for linking. It sticks with the Org-mode ecosystem. It utilizes an external database to indexes note metadata, like titles, tags, and locations and searching notes efficiently without parsing any org files. When working on multiple computers, the user might need to re-index the org files.&lt;/p&gt;
&lt;p&gt;This is the biggest architectural difference between Denote and Org-roam. If all notes are written in Org-mode, I find Org-roam provides cleaner org files because it does not introduce a new link type.&lt;/p&gt;
&lt;h3 id="supported-formats"&gt;Supported Formats&lt;/h3&gt;
&lt;p&gt;Denote works for all files. Not only can the user take notes in Org-mode, Markdown, or Txt, but also name a PDF file so that it could be identified, linked, searched and tagged.&lt;/p&gt;
&lt;p&gt;Org-roam works for only Org-mode files. It leverages the mature Org-mode ecosystem. For example, the user can use &lt;code&gt;org-attach&lt;/code&gt; to associate a PDF file with a note.&lt;/p&gt;
&lt;p&gt;Frankly speaking, Denote is designed for Emacs users. Org-roam is designed for Org-mode users.&lt;/p&gt;
&lt;h3 id="file-naming-scheme"&gt;File-naming Scheme&lt;/h3&gt;
&lt;p&gt;In a file-based note system, it is inevitable to have coupling between the filename and the content (e.g., keeping &lt;code&gt;#+TITLE:&lt;/code&gt; and file name in Org-mode).&lt;/p&gt;
&lt;p&gt;Denote answers with its file-naming scheme and need to keep filename consistent and update-to-date so that search results remain valid after changing title or tags. Maintaining the naming scheme could become a burden without any automation. Denote provides &lt;code&gt;denote-rename-*&lt;/code&gt; and &lt;code&gt;denote-dired-rename-*&lt;/code&gt; for the job.&lt;/p&gt;
&lt;p&gt;Org-roam does not enforce any file-naming scheme. The default &lt;code&gt;org-roam-capture-templates&lt;/code&gt; uses the creation timestamp and title slug as the filename, and that is all.&lt;/p&gt;
&lt;p&gt;Note that the Denote file-naming scheme has a limitation: cannot define a tag with multiple words. For example, the tag &amp;ldquo;note-taking&amp;rdquo; becomes two tags &amp;ldquo;note&amp;rdquo; and &amp;ldquo;taking&amp;rdquo; unless combining them to &amp;ldquo;notetaking&amp;rdquo;.&lt;/p&gt;
&lt;h3 id="searching-mechanism"&gt;Searching Mechanism&lt;/h3&gt;
&lt;p&gt;Utilizing its file-naming scheme, Denote can efficiently search for a note by filename without parsing the content of the file. Users can search notes with titles and tags by prefix &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;_&lt;/code&gt; respectively. The out-of-the-box experience does not fit my need. Users need to write some emacs lisp or learn Dired to search efficiently.&lt;/p&gt;
&lt;p&gt;Org-roam provides better out-of-the-box searching experience. With &lt;a href="https://whhone.com/emacs-config/#taking-note-with-org-roam"&gt;a little customization&lt;/a&gt;, I can search notes by folder name, title, and tags, recursively across sub-folders, sorted by the last modification time. This suits most of my needs.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-roam-node-find.png"&gt;
&lt;/figure&gt;
&lt;p&gt;It is nice to eradicate the dependency on a database with Denote. However, I found it hard to replicate the Org-roam searching experience with Denote, especially sorting notes by last modification time within subdirectories. Note that full-text search could be achieved by &lt;code&gt;counsel-rg&lt;/code&gt; for both.&lt;/p&gt;
&lt;h2 id="denote-comparison-to-org-roam"&gt;Denote Comparison to Org-roam&lt;/h2&gt;
&lt;h3 id="pros"&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;No database is needed. Seamless across multiple machines.&lt;/li&gt;
&lt;li&gt;A simple but powerful file-naming scheme that works for all files.&lt;/li&gt;
&lt;li&gt;Allow notes in other formats (txt, md). (Not for me. All my notes are in org.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cons"&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Uses non-standard link type &lt;code&gt;denote:&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Worse out-of-the-box searching experience. (I believe it is hackable)&lt;/li&gt;
&lt;li&gt;Tag with multiple words like &amp;ldquo;note-taking&amp;rdquo; becomes &amp;ldquo;note&amp;rdquo; and &amp;ldquo;taking&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-choice"&gt;My Choice&lt;/h2&gt;
&lt;p&gt;It is tempting to eradicate the dependency on a database with a simple file-naming scheme. However, I am hesitant to introduce &lt;code&gt;denote:&lt;/code&gt; to my org files and want to develop a searching experience that on is par with Org-roam first.&lt;/p&gt;
&lt;p&gt;I decided to stick with Org-roam but keep exploring Denote in the next couple of weeks. Stay tune :-)&lt;/p&gt;
&lt;h2 id="appendix"&gt;Appendix&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://whhone.com/emacs-config/#note-taking-with-denote"&gt;My Denote configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title> Ignore "save-buffer" Unless Visiting a File</title><link>https://whhone.com/posts/my-save-buffer/</link><pubDate>Sat, 09 Apr 2022 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/my-save-buffer/</guid><description>&lt;p&gt;I have a habit of hitting the save shortcut (&lt;code&gt;C-x C-s&lt;/code&gt;) occasionally. However, if the buffer does not have a visited file (like treemacs, vterm, scratch, help, etc), Emacs asks me where to save the file and I need to cancel with triple &lt;code&gt;ESC&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, hitting &lt;code&gt;C-x C-s&lt;/code&gt; within the &lt;code&gt;*scratch*&lt;/code&gt; buffer.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/save-buffer.png"&gt;
&lt;/figure&gt;
&lt;p&gt;This is annoying. To fix it, I remap &lt;code&gt;save-buffer&lt;/code&gt; to the function below&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; which does nothing for non-file-visiting buffer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/save-buffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;optional&lt;/span&gt; &lt;span class="nv"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Like &lt;/span&gt;&lt;span class="ss"&gt;`save-buffer&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;, but does nothing if buffer is not visiting a file.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;interactive&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buffer-file-name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; regular buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buffer-file-name&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buffer-base-buffer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="c1"&gt;; indirect buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;user-error&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Use &amp;#39;M-x save-buffer&amp;#39; explicitly to save this buffer with no visited file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;save-buffer&lt;/span&gt; &lt;span class="nv"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;global-set-key&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;remap&lt;/span&gt; &lt;span class="nv"&gt;save-buffer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;my/save-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, I get the message below and do not need to cancel the save operation.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/my-save-buffer.png"&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/em&gt;: My friend AhLeung has an alternative using &lt;code&gt;save-all-buffers&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/save-all-buffers&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Save all motified file-visiting buffers without asking.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;save-some-buffers&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;global-set-key&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;remap&lt;/span&gt; &lt;span class="nv"&gt;save-buffer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;my/save-all-buffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This elisp is updated with &lt;a href="https://www.reddit.com/r/emacs/comments/u09yf9/comment/i4522jb/"&gt;the suggestion&lt;/a&gt; from Phil-Hudson. Thanks!&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Write with Emacs 2: Synonyms</title><link>https://whhone.com/posts/write-with-emacs-2/</link><pubDate>Fri, 05 Nov 2021 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/write-with-emacs-2/</guid><description>&lt;blockquote&gt;
&lt;p&gt;The limits of language are the limits of one&amp;rsquo;s world. ~Ludwig Wittgenstein&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My limited vocabulary should be a limit of my writing.&lt;/p&gt;
&lt;h2 id="looking-up-synonyms-with-power-thesaurus"&gt;Looking up Synonyms with Power Thesaurus&lt;/h2&gt;
&lt;p&gt;I find looking up synonyms is a way to extend one&amp;rsquo;s &lt;a href="https://en.wiktionary.org/wiki/active_vocabulary"&gt;active vocabulary&lt;/a&gt;. For instance, I can replace &lt;strong&gt;beautiful&lt;/strong&gt; (active vocabulary) with something more specific and accurate, like &lt;em&gt;attractive&lt;/em&gt;, &lt;em&gt;lovely&lt;/em&gt;, &lt;em&gt;gorgeous&lt;/em&gt;, &lt;em&gt;charming&lt;/em&gt;, &lt;em&gt;splendid&lt;/em&gt;, &lt;em&gt;appealing&lt;/em&gt; (passive vocabulary).&lt;/p&gt;
&lt;p&gt;In Emacs, &lt;a href="https://github.com/SavchenkoValeriy/emacs-powerthesaurus"&gt;emacs-powerthesaures&lt;/a&gt; is a package to look up synonyms by fetching data from &lt;a href="https://www.powerthesaurus.org/"&gt;Power Thesaurus&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is a mini demo:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/powerthesaurus.gif"&gt;
&lt;/figure&gt;
&lt;h2 id="configuration"&gt;Configuration&lt;/h2&gt;
&lt;p&gt;The package defines three interactive functions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;powerthesaurus-lookup-word&lt;/code&gt;: look up synonyms of a provided word&lt;/li&gt;
&lt;li&gt;&lt;code&gt;powerthesaurus-lookup-word-at-point&lt;/code&gt;: look up synonyms of the word at point&lt;/li&gt;
&lt;li&gt;&lt;code&gt;powerthesaurus-lookup-word-dwim&lt;/code&gt;: combine the above two,
&lt;ul&gt;
&lt;li&gt;if the current point (cursor) has a word, then calls (2)&lt;/li&gt;
&lt;li&gt;otherwise, calls (1)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is my configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;powerthesaurus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:bind&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;M-`&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;powerthesaurus-lookup-word-dwim&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="caveat"&gt;Caveat&lt;/h2&gt;
&lt;p&gt;Use words that you know. Otherwise, check the meaning and learn that new word first!&lt;/p&gt;</description></item><item><title>Write with Emacs 1: Spell-Checking</title><link>https://whhone.com/posts/write-with-emacs-1/</link><pubDate>Fri, 29 Oct 2021 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/write-with-emacs-1/</guid><description>&lt;p&gt;I am bad at writing, especially in English, which is not my native language. I misspell words, have limited vocabulary, and misuse grammar without being aware of it.&lt;/p&gt;
&lt;p&gt;On the other hand, I write and need to write a lot. Emacs is my main writing tool and I use it to take notes, compose commit messages and blog posts, etc. Over the years, I have learned some tricks to write better with this awesome tool. I am going to share them in the next couple of posts,&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the basic &amp;ndash; spell-checking.&lt;/p&gt;
&lt;h2 id="check-and-correct-spelling-with-flyspell"&gt;Check and Correct Spelling with Flyspell&lt;/h2&gt;
&lt;p&gt;I use &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Spelling.html"&gt;Flyspell&lt;/a&gt;, an Emacs built-in minor mode, for performing on-the-fly spelling checking and correcting.&lt;/p&gt;
&lt;p&gt;When enabled, misspelled words will be highlighted. I can use &lt;code&gt;(flyspell-auto-correct-word)&lt;/code&gt; to correct the word at the point. Most of the time, the first candidate is the correct one. It is smart enough to me.&lt;/p&gt;
&lt;p&gt;I also use the context menu a lot (by middle-clicking the misspelled word). I can choose the right word from all candidates or save the word, so that it does not complain anymore.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/flyspell-mouse.gif"&gt;
&lt;/figure&gt;
&lt;p&gt;If you don&amp;rsquo;t like touching the mouse, &lt;a href="https://github.com/d12frosted/flyspell-correct"&gt;flyspell-correct&lt;/a&gt; is a package to correct misspelled words with other interfaces, like helm and ivy, all inside the keyboard.&lt;/p&gt;
&lt;p&gt;Here is a demo for &lt;code&gt;(flyspell-correct-wrapper)&lt;/code&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/flyspell-ivy.gif"&gt;
&lt;/figure&gt;
&lt;h2 id="configuration"&gt;Configuration&lt;/h2&gt;
&lt;p&gt;Flyspell provides two minor modes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;flyspell-mode&lt;/code&gt; for writing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flyspell-prog-mode&lt;/code&gt; for programming (only text inside comments and strings is checked)ema&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is how I enable them respectively and set up the packages mentioned above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-hook&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;text-mode-hook&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;flyspell-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-hook&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;prog-mode-hook&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;flyspell-prog-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;flyspell-correct&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:after&lt;/span&gt; &lt;span class="nv"&gt;flyspell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:bind&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;:map&lt;/span&gt; &lt;span class="nv"&gt;flyspell-mode-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;C-;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;flyspell-correct-wrapper&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Replace with flyspell-correct-helm if you are a helm person.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;flyspell-correct-ivy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:after&lt;/span&gt; &lt;span class="nv"&gt;flyspell-correct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="caveat"&gt;Caveat&lt;/h2&gt;
&lt;p&gt;This trick does not work if the misspelled word is valid. For example, when misspelling &amp;ldquo;summit&amp;rdquo; as &amp;ldquo;submit&amp;rdquo;. So, you should still check the spelling even with tool like this.&lt;/p&gt;</description></item><item><title>Another Emacs i3 Integration</title><link>https://whhone.com/posts/emacs-i3-integration/</link><pubDate>Fri, 15 Oct 2021 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/emacs-i3-integration/</guid><description>&lt;p&gt;Pavel Korytov wrote an inspiring &lt;a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/"&gt;blog post&lt;/a&gt; to get a consistent set of keybindings between i3 and Emacs. It caught my attention because I use exactly these two tools heavily. Before the integration, I have to define a different keybinding for switching window inside Emacs and i3. After the integration, I can use the same keybinding everywhere. Check out the video in the original &lt;a href="https://sqrtminusone.xyz/posts/2021-10-04-emacs-i3/"&gt;blog post&lt;/a&gt; to see how cool it is!&lt;/p&gt;
&lt;p&gt;I followed the blog post and implemented it immediately. It worked and I loved that! However, the proposed (i3 -&amp;gt; Emacs -&amp;gt; i3) solution introduced a coupling between i3 and Emacs. i3 has to know Emacs, and Emacs has to know i3. It does not fit into the &lt;a href="https://en.wikipedia.org/wiki/Law_of_Demeter"&gt;Law of Demeter&lt;/a&gt;. It makes the solution harder to scale to other tiling window managers or applications.&lt;/p&gt;
&lt;p&gt;Hence, I implemented another (i3 -&amp;gt; Emacs) solution with less coupling (only the &lt;code&gt;focus&lt;/code&gt; command for now).&lt;/p&gt;
&lt;h2 id="1-add-an-elisp-function-my-wm-integration"&gt;1: Add an Elisp function &lt;code&gt;my/wm-integration&lt;/code&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-elisp" data-lang="elisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;windmove&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/wm-integration&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;pcase&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;rx&lt;/span&gt; &lt;span class="nv"&gt;bos&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;focus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;windmove-do-window-select&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;intern&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;elt&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;split-string&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;error&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;my/wm-integration&lt;/code&gt; handles the command from the window manager.&lt;/p&gt;
&lt;p&gt;Emacs does not need to know what the window manager is, and do not need to call &lt;code&gt;i3-msg&lt;/code&gt;. It return a non-zero code if the command is not handled, or a zero code if handled.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;nit. Emacs still use the protocol defined by i3, like the &lt;code&gt;focus&lt;/code&gt; command. Well&amp;hellip; we have to use a protocol anyway. Let&amp;rsquo;s pick the i3 protocol in this post.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="2-create-a-script-i3-msg-proxy"&gt;2: Create a Script &lt;code&gt;i3-msg-proxy&lt;/code&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Proxy the `i3-msg` command to the focused window.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Proxy to Emacs if it is the active window&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;xdotool getactivewindow getwindowclassname&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Emacs&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;(my/wm-integration \&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; emacsclient -e &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# fallback to i3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;i3-msg &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This script proxies the &lt;code&gt;i3-msg&lt;/code&gt; command to the focused window. If the focused window has handled it (return zero), then exit. Otherwise (return non-zero), fallback to i3.&lt;/p&gt;
&lt;p&gt;This mechanism (using the return code) can work with other applications, like Tmux.&lt;/p&gt;
&lt;h2 id="3-update-the-i3-config"&gt;3: Update the i3 config&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-nil" data-lang="nil"&gt;bindsym $mod+Left exec i3-msg-proxy focus left
bindsym $mod+Down exec i3-msg-proxy focus down
bindsym $mod+Up exec i3-msg-proxy focus up
bindsym $mod+Right exec i3-msg-proxy focus right
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, update the i3 config to proxy the command to the focused window. Now, I can move my focus between i3 and Emacs!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The key difference between this and the original solution is how Emacs calls i3 back when it failed to handle the command. The original solution calls &lt;code&gt;i3-msg&lt;/code&gt; inside Emacs. This solution returns non-zero from Emacs.&lt;/p&gt;</description></item><item><title>An Org-mode Workflow for Task Management</title><link>https://whhone.com/posts/org-mode-task-management/</link><pubDate>Sat, 31 Oct 2020 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/org-mode-task-management/</guid><description>&lt;p&gt;As mentioned in the &lt;a href="https://whhone.com/posts/from-evernote-to-org-mode/"&gt;last post&lt;/a&gt;, I switched to Org-mode. I kept adjusting my workflow with this new tool and it has been stabilized for a month. I think it is time to talk about the new workflow for task/time management with Org-mode. This blog post consists of four parts: the principles, the definitions, the workflows, and finally the implementations.&lt;/p&gt;
&lt;h2 id="1-the-principles"&gt;1 The Principles&lt;/h2&gt;
&lt;p&gt;Principles remain valid no matter what the tool is.&lt;/p&gt;
&lt;h3 id="1-dot-1-do-not-add-tasks-indiscriminately"&gt;1.1 Do Not Add Tasks Indiscriminately&lt;/h3&gt;
&lt;p&gt;Not every task should go into the system. Avoid filling the system with bullshits and diluting the things that matter. I only add tasks that I really want or need to do.&lt;/p&gt;
&lt;p&gt;To clarify&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, the task management system described below is not the &amp;ldquo;inbox&amp;rdquo; in GTD. I still capture things into my inbox but not all of them will be converted to a task in the task management system (org agenda files) eventually.&lt;/p&gt;
&lt;h3 id="1-dot-2-not-all-tasks-have-to-be-done"&gt;1.2 Not All Tasks Have To Be Done&lt;/h3&gt;
&lt;p&gt;There are two reasons for this. First, tasks could be deprioritized or even unnecessary. Second, we have limited time and cannot do everything. We should have an opinion on the priority.&lt;/p&gt;
&lt;h3 id="1-dot-3-reduce-the-number-of-open-loops"&gt;1.3 Reduce The Number Of Open Loops&lt;/h3&gt;
&lt;p&gt;Open loops are tasks that have been started but not finished. They stay in our minds and occupy some of our limited working memory so that we cannot focus on another task we are working on.&lt;/p&gt;
&lt;p&gt;Also, open loops reduce agility, according to &lt;a href="https://en.wikipedia.org/wiki/Little%27s_law"&gt;Little&amp;rsquo;s Law&lt;/a&gt;. The more the open loops, the longer time to finish each of them on average.&lt;/p&gt;
&lt;h3 id="1-dot-4-reduce-decision-making-of-what-to-do-next"&gt;1.4 Reduce Decision Making Of What To Do Next&lt;/h3&gt;
&lt;p&gt;The system should suggest to the user what to do next so that the user can reserve the will power to the real task. This also avoids skipping hard tasks with easy tasks unconsciously.&lt;/p&gt;
&lt;h2 id="2-the-definitions"&gt;2 The Definitions&lt;/h2&gt;
&lt;p&gt;Each task in Org-mode has a &lt;a href="https://orgmode.org/manual/Workflow-states.html"&gt;TODO keyword&lt;/a&gt;, optionally &lt;a href="https://orgmode.org/manual/Deadlines-and-Scheduling.html"&gt;a scheduled date, and a deadline&lt;/a&gt;. For example,&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-org" data-lang="org"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;*&lt;/span&gt;&lt;span class="gs"&gt; PROG Write a blog post on task management with Org-mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DEADLINE: &amp;lt;&lt;span class="s"&gt;2020-11-07 Sat&lt;/span&gt;&amp;gt; SCHEDULED: &amp;lt;&lt;span class="s"&gt;2020-10-31 Sat&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each Org-mode user could define their own set of TODO keywords and use scheduled dates and deadlines differently. For example, some people use only two TODO keywords, &amp;ldquo;TODO&amp;rdquo; and &amp;ldquo;DONE&amp;rdquo;, while some use more. Some people set &amp;ldquo;scheduled dates&amp;rdquo; to all the tasks while some people set it to some of the tasks. These nuances could result in a hugely different workflow, although they are using the same Org-mode. Let’s take a look at how I use them.&lt;/p&gt;
&lt;h3 id="2-dot-1-todo-keywords"&gt;2.1 TODO Keywords&lt;/h3&gt;
&lt;p&gt;I use as few TODO keywords as possible but not too few. For example, it is common to use only two states (&amp;ldquo;TODO&amp;rdquo; and &amp;ldquo;DONE&amp;rdquo;) but this does not align with the principles I mentioned above. I need a state for &amp;ldquo;open loops&amp;rdquo; so that I can keep the number of them small. I also need to distinguish a smaller set of &amp;ldquo;next actions&amp;rdquo; from all tasks.&lt;/p&gt;
&lt;p&gt;So far, I defined these five keywords:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TODO Keyword&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TODO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tasks that are not started and not planned.&lt;/strong&gt; They could be the backlogs or the GTD’s someday/maybe. These tasks could be converted to &lt;code&gt;NEXT&lt;/code&gt; during a review.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NEXT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tasks that are not started but planned to do as soon as I can.&lt;/strong&gt; When there is no actionable &lt;code&gt;PROG&lt;/code&gt; (e.g., blocked), I start one of those and convert it to &lt;code&gt;PROG&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tasks that are working in progress (open loops).&lt;/strong&gt; I work on these tasks before starting another &lt;code&gt;NEXT&lt;/code&gt; task to avoid too many open loops at any moment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INTR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tasks that are interruptions.&lt;/strong&gt; They are urgent things that I should drop everything else and work on it. For example, production issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DONE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;The tasks that are completed.&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This diagram illustrates the transition of those states.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-nil" data-lang="nil"&gt; +------+
| INTR |
+------+
|
v
+------+ +------+ +------+ +------+
| TODO |--&amp;gt;| NEXT |--&amp;gt;| PROG |--&amp;gt;| DONE |
+------+ +------+ +------+ +------+
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2-dot-2-scheduled-and-deadline"&gt;2.2 Scheduled and Deadline&lt;/h3&gt;
&lt;p&gt;In the past, I tended to set a date for all tasks. If I want to do A, B, and C on Monday, then I schedule them for Monday. This sounds intuitive but, in reality, I ended up rescheduling many incompleted tasks at the end of every day. It was not only wasting time but also depressing.&lt;/p&gt;
&lt;p&gt;Later, I changed to rely more on the TODO keywords. For example, if a task is still in progress, I keep the state unchanged as &lt;code&gt;PROG&lt;/code&gt; instead of rescheduling it every day until it is done. I am now using the &amp;ldquo;scheduled date&amp;rdquo; to hide a task until the date I should look at it again. Similar to the snooze feature in Gmail.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SCHEDULED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide the task until the scheduled date.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEADLINE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The deadline of the task.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For example, when a &lt;code&gt;PROG&lt;/code&gt; task is being blocked, I set the &lt;code&gt;SCHEDULED&lt;/code&gt; date to hide it until the date I want to revisit. On the scheduled date, if the task is unblocked, I will remove the &lt;code&gt;SCHEDULED&lt;/code&gt; date. If the task is still blocked, I reschedule it again. It acts as the &lt;a href="https://hamberg.no/gtd#the-waiting-for-list"&gt;waiting for list&lt;/a&gt; in GTD.&lt;/p&gt;
&lt;h2 id="3-the-workflow"&gt;3 The Workflow&lt;/h2&gt;
&lt;p&gt;I customize my org agenda view to drive my daily workflow. The customized agenda view has four sections. From the top to bottom, they are the tasks scheduled today, the &lt;code&gt;INTR&lt;/code&gt; tasks, the &lt;code&gt;PROG&lt;/code&gt; tasks, and finally the &lt;code&gt;NEXT&lt;/code&gt; tasks.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://whhone.com/ox-hugo/org-agenda.png"&gt;
&lt;/figure&gt;
&lt;p&gt;My daily workflow goes from the top to the bottom.&lt;/p&gt;
&lt;h3 id="3-dot-1-update-tasks-scheduled-today"&gt;3.1 Update Tasks Scheduled Today&lt;/h3&gt;
&lt;p&gt;At the beginning of the day, I review the tasks that are scheduled for today. The goal here is not to finish them, but to update or remove the scheduled date so that there is nothing left.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the task is still blocked, reschedule it&lt;/li&gt;
&lt;li&gt;If the task could be done in a few minutes, then do it and mark it as &lt;code&gt;DONE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Otherwise, remove the scheduled date and optionally update the &lt;code&gt;TODO&lt;/code&gt; keywords.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Removing the scheduled date is the best outcome. It indicates the previous estimation was correct, at least not too early. Rescheduling indicates the previous estimation is inaccurate. I would avoid rescheduling the task to tomorrow indiscriminately and try to make a good estimation to reduce the number of rescheduling.&lt;/p&gt;
&lt;h3 id="3-dot-2-find-the-next-task-to-work-on"&gt;3.2 Find the Next Task to Work On&lt;/h3&gt;
&lt;p&gt;After reviewing all tasks scheduled for today, it is time to pick a task and do some real works. This step is straight-forward with the customized agenda view above.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick an &lt;code&gt;INTR&lt;/code&gt; task if there is any.&lt;/li&gt;
&lt;li&gt;If there is no &lt;code&gt;INTR&lt;/code&gt; task, then pick a &lt;code&gt;PROG&lt;/code&gt; task and work on it. If that task is blocked, set a &lt;code&gt;SCHEDULED&lt;/code&gt; date to hide it.&lt;/li&gt;
&lt;li&gt;If there is no &lt;code&gt;INTR&lt;/code&gt; and &lt;code&gt;PROG&lt;/code&gt; task, then start a &lt;code&gt;NEXT&lt;/code&gt; task.&lt;/li&gt;
&lt;li&gt;If there is no task in the agenda view, then review the &lt;code&gt;TODO&lt;/code&gt; tasks and convert some to &lt;code&gt;NEXT&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="3-dot-3-review-the-system"&gt;3.3 Review the System&lt;/h3&gt;
&lt;p&gt;The secret of having a system that works in the long-term is regular maintenance. I do it at least once a week. For examples,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Promote some tasks from &lt;code&gt;TODO&lt;/code&gt; to &lt;code&gt;NEXT&lt;/code&gt;. Demote or even delete deprioritized tasks.&lt;/li&gt;
&lt;li&gt;Review the &lt;a href="https://whhone.com/posts/daily-journal/"&gt;journal&lt;/a&gt; and add &lt;code&gt;TODO&lt;/code&gt; if something needs follow-up.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://orgmode.org/manual/Archiving.html"&gt;Archive&lt;/a&gt; completed tasks and extract to permanent notes&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="4-the-configuration"&gt;4 The Configuration&lt;/h2&gt;
&lt;p&gt;Finally, here is the configuration for the above workflow.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; TODO keywords.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-todo-keywords&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;sequence&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;TODO(t)&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;NEXT(n)&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;PROG(p)&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;INTR(i)&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;DONE(d)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Show the daily agenda by default.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-span&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Hide tasks that are scheduled in the future.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-todo-ignore-scheduled&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;future&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Use &amp;#34;second&amp;#34; instead of &amp;#34;day&amp;#34; for time comparison.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; It hides tasks with a scheduled time like &amp;#34;&amp;lt;2020-11-15 Sun 11:30&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-todo-ignore-time-comparison-use-seconds&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Hide the deadline prewarning prior to scheduled date.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-skip-deadline-prewarning-if-scheduled&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;pre-scheduled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;;; Customized view for the daily workflow. (Command: &amp;#34;C-c a n&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-agenda-custom-commands&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;n&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Agenda / INTR / PROG / NEXT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;agenda&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;todo&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;INTR&amp;#34;&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;todo&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;PROG&amp;#34;&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;todo&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;NEXT&amp;#34;&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Thanks for &lt;a href="https://www.reddit.com/r/orgmode/comments/jmf8dw/an_orgmode_workflow_for_task_management/gavkv1r/?context=3"&gt;this comment&lt;/a&gt; in Reddit.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;There will be another post for Org-mode note-taking workflow.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>From Evernote to Org-mode</title><link>https://whhone.com/posts/from-evernote-to-org-mode/</link><pubDate>Thu, 03 Sep 2020 00:00:00 -0700</pubDate><author>whhone@gmail.com (Wai Hon Law)</author><guid>https://whhone.com/posts/from-evernote-to-org-mode/</guid><description>&lt;p&gt;Since 2017, Evernote has been my &lt;a href="https://whhone.com/posts/productivity-system/"&gt;productivity system&lt;/a&gt; for note-taking and task management. I thought it was my ultimate system to stick with. It has served me so well and I have even became a paid user. However, mentioned in the &lt;a href="https://whhone.com/posts/arch-linux-full-disk-encryption/"&gt;last post&lt;/a&gt;, I am pursuing more privacy in my digital life. &lt;strong&gt;I want to take my notes with a peace of mind, without worrying they are being watched or leaked.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This post talks about the privacy concern, my research on the alternatives, and the switch to Org-mode.&lt;/p&gt;
&lt;h2 id="1-dot-the-privacy-concern-of-evernote"&gt;1. The Privacy Concern of Evernote&lt;/h2&gt;
&lt;p&gt;The user data in Evernote is &lt;a href="https://evernote.com/security"&gt;encrypted &amp;ldquo;during transport&amp;rdquo; and &amp;ldquo;at-rest&amp;rdquo;&lt;/a&gt;. This sounds secure to readers but actually not enough because Evernote can still see the content. Technically, it is still server-side encryption instead the client-side encryption (a.k.a. end-to-end encryption). The encryption key of the note content is held by the server instead of the client. If the server is hacked, or there is an evil employee, or Evernote needs to comply with legal obligations (i.e., the government asks for user data, and &lt;a href="https://evernote.com/privacy/transparency-report"&gt;it does&lt;/a&gt;), those notes could be leaked.&lt;/p&gt;
&lt;h2 id="2-dot-researching-the-alternatives"&gt;2. Researching the Alternatives&lt;/h2&gt;
&lt;p&gt;I started researching the alternatives which do not compromise privacy. That means end-to-end encryption is required if the data is hosted on a third-party. This requirement actually simplifies my curation because many solutions are ruled out, including the popular Notion and Roam (o:&lt;/p&gt;
&lt;p&gt;My final shortlist included &lt;a href="https://orgmode.org/"&gt;&lt;strong&gt;Org-mode&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://joplinapp.org/"&gt;&lt;strong&gt;Joplin&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://standardnotes.org/"&gt;&lt;strong&gt;Standard Note&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://taskwarrior.org/"&gt;&lt;strong&gt;Taskwarrior&lt;/strong&gt;&lt;/a&gt;, &lt;a href="http://todotxt.org/"&gt;&lt;strong&gt;TODO.txt&lt;/strong&gt;&lt;/a&gt;, and &lt;a href="https://tiddlywiki.com/"&gt;&lt;strong&gt;TiddlyWiki&lt;/strong&gt;&lt;/a&gt;. I gave each of them a try, reviewed and scored them with my requirements below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Privacy&lt;/strong&gt; Can I seclude my data?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Basic Task Management&lt;/strong&gt;: Can I set a date for a task and then view all tasks for today?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Task Context&lt;/strong&gt;: Can I add references (instruction, link) to the task?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linux Usability&lt;/strong&gt;: How usable is it on Linux?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android Usability&lt;/strong&gt;: How usable is it on Android?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Synchronization&lt;/strong&gt;: How hard is it to synchronize data between Linux and Android?&lt;/li&gt;
&lt;/ol&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Evernote&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Org-mode&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Joplin&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Standard Note&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Taskwarrior&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;TODO.txt&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;TiddlyWiki&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Privacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linux Usability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Android Usability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Synchronization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="review-evernote--24"&gt;Review: Evernote (24)&lt;/h3&gt;
&lt;p&gt;Evernote has a reminder feature for simple task management, which is already enough for me. Since it is a note-taking app, I can add rich context like images or even audio seamlessly. Tusk makes it usabe on Linux and the official Android app is simply awesome. Synchronization works out-of-the-box.&lt;/p&gt;
&lt;p&gt;However, it fails on the privacy bit.&lt;/p&gt;
&lt;h3 id="review-org-mode--26"&gt;Review: Org-mode (26)&lt;/h3&gt;
&lt;p&gt;Org-mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system. It is absolutely capable.&lt;/p&gt;
&lt;p&gt;However, the learning curve of Org-mode is steep and even steeper for non-Emacs users. Users are expected to read documentation and practice. Once familiarized with Emacs, its usability on Linux will jump from 0 to 5 (or even infinity). The Android client Orgzly is usable but could be better. Synchronization works with SyncThing.&lt;/p&gt;
&lt;h3 id="review-joplin--22--and-standard-note--24"&gt;Review: Joplin (22) and Standard Note (24)&lt;/h3&gt;
&lt;p&gt;Joplin and Standard Note are both strong note-taking alternatives to Evernote. However, they suck in task management because they simply cannot show all tasks scheduled for today. They both have a decent desktop app written in Electron. Synchronization works natively and users can turn on end-to-end encryption for privacy/security.&lt;/p&gt;
&lt;p&gt;If task management is not a requirement, both Joplin and Standard Note are very good alternatives.&lt;/p&gt;
&lt;h3 id="review-taskwarrior--21--and-todo-dot-txt--20"&gt;Review: Taskwarrior (21) and TODO.txt (20)&lt;/h3&gt;
&lt;p&gt;Taskwarrior and TODO.txt are both FOSS for task management. However, they both allow only one line per task, which makes adding extra context to the task very hard. Their clients are barely usable to me (or I have not learned enough). Secure synchronization works with a self-hosted server or with SyncThing for Taskwarrior and TODO.txt respectively.&lt;/p&gt;
&lt;h3 id="review-tiddlywiki"&gt;Review: TiddlyWiki (?)&lt;/h3&gt;
&lt;p&gt;TiddlyWiki is a &amp;ldquo;non-linear personal web notebook&amp;rdquo;. It looks fancy but I haven&amp;rsquo;t tried it intensively because Org-mode seems working for me already at this point. I will leave it as a question mark.&lt;/p&gt;
&lt;h2 id="3-dot-switching-to-org-mode"&gt;3. Switching to Org-mode&lt;/h2&gt;
&lt;p&gt;After all these explorations, Org-mode looks promising to me and I started the Org-mode/Emacs 30-day challenge &amp;ndash; keep using Org-mode/Emacs for a month. Note that I was still using Evernote during the transition but gradually reducing the dependency on it.&lt;/p&gt;
&lt;p&gt;I have now finished the challenge and can claim myself an Org-mode user. I am happy with Org-mode and the new workflow. Since the org files are in plain text, I manage my notes with Git, review, and polish all the changes before committing it. It helps to improve the quality of my notes!&lt;/p&gt;
&lt;p&gt;Beyond Org-mode, I also started seeing the beauty of Emacs and why people use it instead of Vim or other modern editors. Emacs is ostensibly an editor but actually a powerful Lisp platform/runtime, which makes it super extensible and capable. Users can customize Emacs and make it their own systems for their own purposes.&lt;/p&gt;
&lt;p&gt;I am now using Emacs even more. For example, I replace Visual Studio Code, my previous editor/git porcelain, with &lt;a href="https://magit.vc/"&gt;Magit&lt;/a&gt;. This blog post is written in Emacs with &lt;a href="https://ox-hugo.scripter.co/"&gt;ox-hugo&lt;/a&gt;. I probably will blog on these topics sometime in the future.&lt;/p&gt;
&lt;p&gt;All in all, I am very happy with the privacy gain and the learning from this switch!&lt;/p&gt;</description></item></channel></rss>