<?xml version="1.0" encoding="utf-8"?><rss version="2.0"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:media="http://search.yahoo.com/mrss/"
    xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
    xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
  <title>Srijan Choudhary, all posts tagged: emacs</title>
  <link>https://srijan.ch/feed/all/tag:emacs</link>
  <lastBuildDate>Wed, 18 Mar 2026 15:10:00 +0000</lastBuildDate>
  <image>
    <url>https://srijan.ch/assets/favicon/favicon-32x32.png</url>
    <title>Srijan Choudhary, all posts tagged: emacs</title>
    <link>https://srijan.ch/feed/all/tag:emacs</link>
  </image>
  <sy:updatePeriod>daily</sy:updatePeriod>
  <sy:updateFrequency>1</sy:updateFrequency>
  <generator>Kirby</generator>
  <atom:link href="https://srijan.ch/feed/all.xml/tag:emacs" rel="self" type="application/rss+xml" />
  <description>Srijan Choudhary&#039;s Articles and Notes Feed for tag: emacs</description>
  <item>
    <title>2026-03-18-001</title>
    <description><![CDATA[A small #Emacs #OrgMode quality-of-life tweak. I often need to replace an org heading's title while preserving the original text in the body. The problem is that pressing enter on a heading inserts a line above the properties drawer, which breaks things. Here's a function that moves the heading title into the body (below the properties drawer and metadata), and binds it to S-RET: (defun …]]></description>
    <link>https://srijan.ch/notes/2026-03-18-001</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2026-03-18-001</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[orgmode]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Wed, 18 Mar 2026 15:10:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>A small <a class="p-category" href="https://srijan.ch/tags/emacs">#Emacs</a> <a class="p-category" href="https://srijan.ch/tags/orgmode">#OrgMode</a> quality-of-life tweak. I often need to replace an org heading's title while preserving the original text in the body. The problem is that pressing enter on a heading inserts a line above the properties drawer, which breaks things.</p>
<p>Here's a function that moves the heading title into the body (below the properties drawer and metadata), and binds it to S-RET:</p>
<pre><code>(defun my-org-demote-title-to-body ()
  "Move the current heading's title into the body, below the metadata.
  Point returns to the heading for editing."
  (interactive)
  (org-back-to-heading t)
  (let* ((element (org-element-at-point))
         (title (org-element-property :raw-value element)))
    (org-edit-headline "")
    (save-excursion
      (org-end-of-meta-data t)
      (insert title "\n"))
    (org-beginning-of-line)))

(defun my-org-shift-return ()
  "On a heading, demote title to body. In a table, copy down."
  (interactive)
  (cond
   ((org-at-heading-p) (my-org-demote-title-to-body))
   ((org-at-table-p) (org-table-copy-down 1))
   (t (org-return))))
  (define-key org-mode-map (kbd "S-&lt;return&gt;") #'my-org-shift-return)</code></pre>]]></content:encoded>
    <comments>https://srijan.ch/notes/2026-03-18-001#comments</comments>
    <slash:comments>4</slash:comments>
  </item><item>
    <title>2025-12-15-001</title>
    <description><![CDATA[A small elisp snippet that I found useful. I often switch between terminals and #Emacs, and they have slightly different behaviors for C-w. This makes it behave the same in Emacs as it does in bash/zsh/fish etc - deletes the last word. It retains the kill-region behavior if a region is actually selected. (defun kill-region-or-backward-word () "If the region is active and non-empty, call …]]></description>
    <link>https://srijan.ch/notes/2025-12-15-001</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2025-12-15-001</guid>
    <category><![CDATA[emacs]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Mon, 15 Dec 2025 06:55:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>A small elisp snippet that I found useful. I often switch between terminals and <a href="https://srijan.ch/tags/emacs" class="p-category">#Emacs</a>, and they have slightly different behaviors for <code>C-w</code>. This makes it behave the same in Emacs as it does in bash/zsh/fish etc - deletes the last word. It retains the <code>kill-region</code> behavior if a region is actually selected.</p>
<pre><code class="language-emacs-lisp">(defun kill-region-or-backward-word ()
  "If the region is active and non-empty, call `kill-region'.
Otherwise, call `backward-kill-word'."
  (interactive)
  (call-interactively
   (if (use-region-p) 'kill-region 'backward-kill-word)))
(global-set-key (kbd "C-w") 'kill-region-or-backward-word)</code></pre>
<p>Ref: <a href="https://stackoverflow.com/questions/13844453/how-do-i-make-c-w-behave-the-same-as-bash">https://stackoverflow.com/questions/13844453/how-do-i-make-c-w-behave-the-same-as-bash</a></p>]]></content:encoded>
    <comments>https://srijan.ch/notes/2025-12-15-001#comments</comments>
    <slash:comments>4</slash:comments>
  </item><item>
    <title>2025-12-09-002</title>
    <description><![CDATA[tramp-hlo looks interesting. Anything that can make tramp on #Emacs snappier is a good thing in my books.]]></description>
    <link>https://srijan.ch/notes/2025-12-09-002</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2025-12-09-002</guid>
    <category><![CDATA[emacs]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Tue, 09 Dec 2025 22:50:00 +0000</pubDate>
    <content:encoded><![CDATA[<p><a href="https://github.com/jsadusk/tramp-hlo">tramp-hlo</a> looks interesting. Anything that can make tramp on  <a href="https://srijan.ch/tags/emacs" class="p-category">#Emacs</a> snappier is a good thing in my books.</p>]]></content:encoded>
    <comments>https://srijan.ch/notes/2025-12-09-002#comments</comments>
    <slash:comments>1</slash:comments>
  </item><item>
    <title>2025-06-11-001</title>
    <description><![CDATA[Quick note for me to generate #Emacs TAGS file for an #Erlang project: find {src,apps,_build/default,$(dirname $(which erl))/../lib} -name "*.[he]rl" | xargs realpath --relative-to="$(pwd)" | etags.emacs -o TAGS - The relative path ensures that this works over tramp as well.]]></description>
    <link>https://srijan.ch/notes/2025-06-11-001</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2025-06-11-001</guid>
    <category><![CDATA[erlang]]></category>
    <category><![CDATA[emacs]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Wed, 11 Jun 2025 02:35:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>Quick note for me to generate <a href="/tags/emacs" class="p-category">#Emacs</a> TAGS file for an <a href="/tags/erlang" class="p-category">#Erlang</a> project:</p>
<pre><code>find {src,apps,_build/default,$(dirname $(which erl))/../lib} -name "*.[he]rl" | xargs realpath --relative-to="$(pwd)" | etags.emacs -o TAGS -</code></pre>
<p>The relative path ensures that this works over tramp as well.</p>]]></content:encoded>
    <comments>https://srijan.ch/notes/2025-06-11-001#comments</comments>
    <slash:comments>4</slash:comments>
  </item><item>
    <title>2025-03-24-002</title>
    <description><![CDATA[Read Jeremy&#039;s post on quickly switching the default browser. I had a shell script to do this as well. Doing it from Emacs makes more sense because I can have a completion UI. So, here's my modified version for Linux: (defun sj/default-browser (&amp;optional name) "Set the default browser based on the given NAME." (interactive (list (completing-read "Browser: " (split-string …]]></description>
    <link>https://srijan.ch/notes/2025-03-24-002</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2025-03-24-002</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[linux]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Mon, 24 Mar 2025 20:55:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>Read <a href="https://takeonrules.com/2025/02/05/quick-switch-default-browser/">Jeremy&#039;s post on quickly switching the default browser</a>.</p>
<p>I had a shell script to do this as well. Doing it from Emacs makes more sense because I can have a completion UI.</p>
<p>So, here's my modified version for Linux:</p>
<pre><code class="language-elisp">(defun sj/default-browser (&amp;optional name)
  "Set the default browser based on the given NAME."
  (interactive
   (list
    (completing-read
     "Browser: "
     (split-string
      (shell-command-to-string
       "find /usr/share/applications ~/.local/share/applications -name \"*.desktop\" -exec grep -l \"Categories=.*WebBrowser\" {} \\;")
      "\n" t))))
  (let ((browser-desktop (file-name-nondirectory name)))
    (shell-command (format "xdg-mime default %s text/html" browser-desktop))
    (shell-command (format "xdg-mime default %s application/xhtml+xml" browser-desktop))
    (shell-command (format "xdg-mime default %s application/x-extension-html" browser-desktop))
    (shell-command (format "xdg-settings set default-web-browser %s" browser-desktop))))</code></pre>
<p>As a plus, it automatically lists the installed browsers based on <code>.desktop</code> files on your system.</p>]]></content:encoded>
    <comments>https://srijan.ch/notes/2025-03-24-002#comments</comments>
    <slash:comments>2</slash:comments>
  </item><item>
    <title>2025-01-15-001</title>
    <description><![CDATA[I had been facing an issue in #Emacs on my work Mac system: C-S-&lt;tab&gt; was somehow being translated to C-&lt;tab&gt;. I tried to look into key-translation-map to figure out the issue, but could not find anything. Finally, turned out that I had bound C-&lt;tab&gt; to tab-line-switch-to-next-tab and C-&lt;iso-lefttab&gt; to tab-line-switch-to-prev-tab, but the actual C-S-&lt;tab&gt; was …]]></description>
    <link>https://srijan.ch/notes/2025-01-15-001</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2025-01-15-001</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[TIL]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Wed, 15 Jan 2025 02:20:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>I had been facing an issue in <a href="https://srijan.ch/tags/emacs" class="p-category">#Emacs</a> on my work Mac system: <code>C-S-&lt;tab&gt;</code> was somehow being translated to <code>C-&lt;tab&gt;</code>. I tried to look into <code>key-translation-map</code> to figure out the issue, but could not find anything.</p>
<p>Finally, turned out that I had bound <code>C-&lt;tab&gt;</code> to <code>tab-line-switch-to-next-tab</code> and <code>C-&lt;iso-lefttab&gt;</code> to <code>tab-line-switch-to-prev-tab</code>, but the actual <code>C-S-&lt;tab&gt;</code> was unbound. <code>C-&lt;iso-lefttab&gt;</code> only works on linux: something to do with how X11 sends the event to the application (and probably some compatibility mode due to which wayland was doing the same).</p>
<p>On Mac, once I explicitly bound <code>C-S-&lt;tab&gt;</code> in my Emacs config, it started working correctly.</p>]]></content:encoded>
    <comments>https://srijan.ch/notes/2025-01-15-001#comments</comments>
    <slash:comments>3</slash:comments>
  </item><item>
    <title>Triggering Orgzly sync on Android when Org file changes</title>
    <description><![CDATA[Event based orgzly sync using tasker to prevent conflicts]]></description>
    <link>https://srijan.ch/triggering-orgzly-sync-on-android-when-org-file-changes</link>
    <guid isPermaLink="false">tag:srijan.ch:/triggering-orgzly-sync-on-android-when-org-file-changes</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[orgmode]]></category>
    <category><![CDATA[android]]></category>
    <category><![CDATA[tasker]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Mon, 13 Jan 2025 18:20:00 +0000</pubDate>
    <content:encoded><![CDATA[<h3>Introduction</h3>
<p>To use my org GTD system on the go, I use the excellent <a href="https://www.orgzlyrevived.com/">Orgzly Revived</a> mobile app for Android. To sync the org files between my laptop and my phone, I use <a href="https://syncthing.net/">syncthing</a> (specifically, the <a href="https://github.com/Catfriend1/syncthing-android">Syncthing Android Fork by Catfriend1</a>).</p>
<p>This allows me to quickly capture things from my phone, get reminder notifications of time-sensitive tasks, and mark tasks complete from my phone. The widgets are also useful for quickly looking at some contexts like errands or calls.</p>
<h3>Sync Issues</h3>
<p>However, Orgzly works by contructing a copy of the contents in it's own database and synchronizing it against the files periodically or when some action is taken in the app. Right now, it does not support synchronizing the data when the file changes.</p>
<p>For me, this has sometimes lead to a conflict between the Orgzly database and the actual files in the org folder. This only happens if the org file is edited on my laptop and something is also edited in the Orgzly app before syncing.</p>
<p>But, the Orgzly app <a href="https://github.com/orgzly-revived/documentation/blob/master/android/public-receiver.org">supports intents</a> that can be used from Tasker, Automate, etc to trigger an event-based sync.</p>
<h3>Tasker Profile</h3>
<p>So, I created a tasker profile to do this. It was surprisingly easy (I've used tasker before, though not too much). It can be found here: <a href="https://taskernet.com/?public&amp;tags=orgzly&amp;time=AllTime">https://taskernet.com/?public&amp;tags=orgzly&amp;time=AllTime</a> called "Run Orgzly Sync When Org Files Change".</p>
<p>Here's the basic flow of the profile:</p>
<pre><code class="language-less">Profile: Run Orgzly Sync When Org Files Change
Settings: Notification: no
Variables: [ %orgfolder:has value ]
    Event: File Modified [ File:%orgfolder Event:* ]



Enter Task: Orgzly Sync
Settings: Run Both Together

A1: If [ %evtprm1 ~ *.org &amp; %evtprm2 ~ ClosedWrite/MovedTo ]

    A2: Send Intent [
         Action: com.orgzly.intent.action.SYNC_START
         Cat: None
         Package: com.orgzlyrevived
         Class: com.orgzly.android.ActionReceiver
         Target: Broadcast Receiver ]

A3: End If</code></pre>
<h3>How it works</h3>
<ol>
<li>When importing this profile, it will ask for your org folder in the local Android filesystem.</li>
<li>Once selected and the profile is activated, it will start monitoring this folder for inotify events.</li>
<li>When any file is modified (or created or moved to/from the folder or deleted), this profile is triggered. It received the parameters: full path of the affected file, and the actual event.</li>
<li>Then, it checks if the affected file is an org file (ending in <code>.org</code>) AND if the event is one of ClosedWrite or MovedTo. I filtered to these events because they are usually the last event received for an edit.</li>
<li>If yes, then Orgzly sync is triggered using an intent.</li>
</ol>
<p>I've been using this for the last several months, and it works well as long as both devices are online when making edits. Conflicts can still happen if, for example, I make some edits on my laptop and subsequently on my phone but the phone is not online or the syncthing app is not running due to data saver or battery saver active. In those cases, eventually syncthing creates a conflicted file that I can manually resolve.</p>
<h3>Limitations</h3>
<ol>
<li>It does not support recursive files inside the folder. Tasker right now does not support recursively watching a folder, but if it's added this can be a good edition; specially because Orgzly Revived supports that.</li>
<li>Since this watches the files in the folder, it also triggers a sync if Orgzly app was used to change the file. Not sure if this can be filtered somehow. Maybe based on foreground app? But it seems flakey.</li>
</ol>
<h3>Alternatives</h3>
<ol>
<li>Orgzly Revived has a git sync backend in beta. This might work better with auto-commit &amp; push.</li>
<li>Using Emacs on Android instead of Orgzly is also an option, but I felt it did not work very well without an external keyboard. Also, it does not have widgets.</li>
</ol>]]></content:encoded>
    <comments>https://srijan.ch/triggering-orgzly-sync-on-android-when-org-file-changes#comments</comments>
    <slash:comments>21</slash:comments>
  </item><item>
    <title>Capturing slack messages directly into Emacs orgmode inbox</title>
    <description><![CDATA[Learn how to seamlessly capture Slack messages into your Emacs Orgmode (GTD) inbox using a custom browser userscript.]]></description>
    <link>https://srijan.ch/capturing-slack-messages-directly-into-emacs-orgmode-inbox</link>
    <guid isPermaLink="false">tag:srijan.ch:/capturing-slack-messages-directly-into-emacs-orgmode-inbox</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[orgmode]]></category>
    <category><![CDATA[slack]]></category>
    <category><![CDATA[gtd]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Fri, 27 Dec 2024 11:50:00 +0000</pubDate>
    <media:content url="https://srijan.ch/media/pages/blog/capturing-slack-messages-directly-into-emacs-orgmode-inbox/d0e1ffec00-1735275760/screenshot-2024-12-27-at-12.01.31am.png" medium="image" />
    <content:encoded><![CDATA[<figure data-ratio="auto">
    <img src="https://srijan.ch/media/pages/blog/capturing-slack-messages-directly-into-emacs-orgmode-inbox/d0e1ffec00-1735275760/screenshot-2024-12-27-at-12.01.31am.png" alt="Screenshot of a custom org capture button in Slack message popup">
  
    <figcaption class="text-center">
    Org capture button in Slack message popup  </figcaption>
  </figure>
<p>Ever since switching to orgmode as my GTD system, I've been trying to (slowly) optimize my capture system for things coming to me from different places. One of the things that I was not satisfied with was capturing and processing inputs from Slack.</p>
<p>Slack messages can easily get lost in the fast-paced stream of conversations. Manually copying and pasting messages introduces too much friction, and also removes some context (backlinks), unless a link to the original message is also manually copied. There are ways to save messages in Slack itself, but it just makes it another Inbox to check, and I'm trying to reduce that.</p>
<p>I started by using the saved messages feature to save messages in a single place, and later either shifting them to my org inbox manually, or directly working on them and completing them. Then, I shifted to using the <a href="https://todoist.com/integrations/apps/slack">Slack Todoist integration</a> to save slack messages to Todoist, and <a href="https://srijan.ch/todoist-cloud-inbox-for-gtd-in-emacs-orgmode">pulling them from Todoist into my org Inbox</a>.</p>
<p>I've now found a better mechanism that allows me to seamlessly capture Slack message with it's context into orgmode. Here's a demo:</p><figure>
  <video controls muted preload="auto"><source src="https://srijan.ch/media/pages/blog/capturing-slack-messages-directly-into-emacs-orgmode-inbox/4ee876ac11-1728494126/slack-to-org-demo.webm" type="video/webm"></video>    <figcaption>Demo video showing a custom slack button to trigger org capture with the selected message's contents and a link back to the message</figcaption>
  </figure>
<h2>Demo breakdown</h2>
<p>This method uses userscripts to add a custom button to the hover menu that comes up for any message in slack, and triggers org protocol capture with the message details when the button is clicked. The message details include the sender, message text, and a direct link back to the message. I've set up this protocol handler to ask me to enter the heading of the capured item, but it can be as easily set up to directly capture the message without user input.</p>
<h2>Setup and Implementation</h2>
<h3>Prerequisites</h3>
<ol>
<li>Slack running in a browser (instead of a desktop app)</li>
<li>Browser extention for userscripts (Tampermonkey, Violentmonkey, Greasemonkey, etc)</li>
<li>Emacs with orgmode installed</li>
<li>Org protocol setup</li>
</ol>
<p>I didn't find a good way to inject userscripts into the Slack destop app, so for now, this method requires using Slack in a browser. It also works when Slack is installed using the "Install Page as App" feature of the browser.</p>
<p>Update: I got a comment that using this in the Slack app is also possible, though I've not evaluated this yet. You can <a href="#komment_6503f91cf9943cf408e47743f863bc12">check this comment for details</a>.</p>
<h3>Setting up Org Protocol Capture</h3>
<p>Setting up org protocol capture involves two steps: configuring your OS to use Emacs to open <code>org-protocol://</code> links, and configuring Emacs to save the captured data as you want.</p>
<p>For OS setup, please check the guides for your OS here: <a href="https://orgmode.org/worg/org-contrib/org-protocol.html#orge00964c">https://orgmode.org/worg/org-contrib/org-protocol.html#orge00964c</a></p>
<p>On Emacs side, this is the minimal config required:</p>
<pre><code class="language-emacs-lisp">(server-start)
(setq-default org-agenda-files '("~/org"))
(setq-default my-org-inbox
    (expand-file-name "inbox.org" "~/org"))
(setq-default org-capture-templates
      '(    
    ("i" "Inbox" entry (file my-org-inbox)
         "* %?\n%i\n%U"
         :kill-buffer t)
    ("l" "Inbox with link" entry (file my-org-inbox)
         "* %?\n%i\n%a\n%U"
         :kill-buffer t)))
(setq-default org-protocol-default-template-key "l")
(require 'org-protocol)</code></pre>
<p>An optional enhancement that can be seen in the demo is: open a new emacs frame to capture this message, then close it automatically after the capture has been done. For this, I use the config snippet from prot's excellent post: <a href="https://protesilaos.com/codelog/2024-09-19-emacs-command-popup-frame-emacsclient/">https://protesilaos.com/codelog/2024-09-19-emacs-command-popup-frame-emacsclient/</a></p>
<p>To run emacsclient with the popup frame parameter, I use:</p>
<pre><code class="language-shell-session">emacsclient --create-frame -F \
    '((prot-window-popup-frame . t) (name . "org-protocol-capture") (width . 80))' \
    -- %u</code></pre>
<p>Emacsclient's args can be set in the desktop entry file (linux) or org-protocol.app file (OSX) when setting up org-protocol.</p>
<h3>The userscript</h3>
<p>For the userscript, I searched for an existing published userscript for Slack that <a href="https://greasyfork.org/en/scripts/500127-slack-quick-edit-button">adds a button</a>, then tweaked it a bit to configure the button according to my needs.</p>
<p>I've published the userscript here: <a href="https://greasyfork.org/en/scripts/521908-slack-org-protocol-capture">https://greasyfork.org/en/scripts/521908-slack-org-protocol-capture</a></p>
<p>From this, I learned about an interesting API called <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver">MutationObserver</a> that seems very useful for userscripts.</p>
<h2>Notes</h2>
<h3>Using emacs-slack</h3>
<p>Another simple approach for this can be to use the <a href="https://github.com/emacs-slack/emacs-slack">emacs-slack</a> package to directly use Slack from Emacs. I tried this, and it does not work very well for me because:</p>
<ol>
<li>My org limits Slack session authentication to 1 week, and authenticating in emacs-slack is a little cumbersome.</li>
<li>We also use Slack huddles, and it does not work well with emacs-slack.</li>
</ol>
<h3>Possible Improvements</h3>
<ol>
<li>Make org capture template configurable</li>
<li>Add tooltip to the button</li>
<li>Pass even more metadata like message timestamp, channel name, emojis, etc.</li>
<li>Maybe some keyboard shortcuts to trigger the capture?</li>
</ol>
<h3>Limitations / Drawbacks</h3>
<p>Since this is dependent on the HTML structure of the slack web app, it can be fragile and can break easily if there are changes in the app.</p>
<p>This userscript uses the MutationObserver API to observe DOM changes in the whole slack workspace. So, it has some impact on performance. However, I've been using this daily for the last several months and I have not noticed any issues.</p>]]></content:encoded>
    <comments>https://srijan.ch/capturing-slack-messages-directly-into-emacs-orgmode-inbox#comments</comments>
    <slash:comments>20</slash:comments>
  </item><item>
    <title>2024-09-24-001</title>
    <description><![CDATA[#Emacs #TIL : I learned about save-interprogram-paste-before-kill - which saves the existing system clipboard text into the kill ring before replacing it. This ensures that Emacs kill operations do not irrevocably overwrite existing clipboard text. A common workflow for me is to copy some text from a different application and paste it inside Emacs. But, if I want to first delete a word or region …]]></description>
    <link>https://srijan.ch/notes/2024-09-24-001</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2024-09-24-001</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[TIL]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Tue, 24 Sep 2024 17:20:00 +0000</pubDate>
    <content:encoded><![CDATA[<p><a href="https://srijan.ch/tags/emacs" class="p-category">#Emacs</a> <a href="https://srijan.ch/tags/TIL" class="p-category">#TIL</a> : I learned about <code>save-interprogram-paste-before-kill</code> - which saves the existing system clipboard text into the kill ring before replacing it. This ensures that Emacs kill operations do not irrevocably overwrite existing clipboard text.</p>
<p>A common workflow for me is to copy some text from a different application and paste it inside Emacs. But, if I want to first delete a word or region to replace, the deleted word or region goes to the system clipboard and replaces my copied text. This config saves the previous entry in the system clipboard so I can do a <code>C-p</code> after paste to choose the previous paste.</p><p>Syndicated to:</p><ul><li><a href="https://bsky.app/profile/srijan4.bsky.social/post/3l4w53avhjw2b">https://bsky.app/profile/srijan4.bsky.social/post/3l4w53avhjw2b</a></li></ul>]]></content:encoded>
    <comments>https://srijan.ch/notes/2024-09-24-001#comments</comments>
    <slash:comments>17</slash:comments>
  </item><item>
    <title>Emacs 30.1 highlight - intuitive tab line</title>
    <description><![CDATA[Tabs in Emacs 30.1 behave similarly to other common desktop applications]]></description>
    <link>https://srijan.ch/emacs-30-1-highlight-intuitive-tab-line</link>
    <guid isPermaLink="false">tag:srijan.ch:/emacs-30-1-highlight-intuitive-tab-line</guid>
    <category><![CDATA[emacs]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Sun, 15 Sep 2024 04:45:00 +0000</pubDate>
    <media:content url="https://srijan.ch/media/pages/blog/emacs-30-1-highlight-intuitive-tab-line/50039cbaae-1726378038/screenshot_20240915_012612.png" medium="image" />
    <content:encoded><![CDATA[<figure data-ratio="auto">
    <img src="https://srijan.ch/media/pages/blog/emacs-30-1-highlight-intuitive-tab-line/50039cbaae-1726378038/screenshot_20240915_012612.png" alt="Emacs NEWS file for 30.1">
  
    <figcaption class="text-center">
    Emacs NEWS file for 30.1  </figcaption>
  </figure>
<p>The first Emacs 30.0.91 pretest for what will be the 30.1 release of Emacs was <a href="https://lists.gnu.org/archive/html/emacs-devel/2024-09/msg00305.html">announced on the mailing list</a> a few days ago. I was going through <a href="https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS?h=emacs-30">the NEWS file</a>, and found something that I've wanted in Emacs for a while now.</p>
<p>One of the niggles I had with Emacs was its tab behavior. It worked differently from how other applications that I'm used to work (firefox, kitty, etc).</p>
<p>In emacs, tab-per-buffer can be achieved by using the <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Line.html">tab-line-mode</a>. But, before now, it had the following problems:</p>
<ol>
<li>Since tab-line-mode listed buffers sorted by recently visited, so the order or tabs kept changing</li>
<li>There was no wrap-around when trying to go to the next tab from the last tab</li>
</ol>
<p>Here's a video showing the old behavior:</p><figure>
  <video controls muted preload="auto"><source src="https://srijan.ch/media/pages/blog/emacs-30-1-highlight-intuitive-tab-line/f1188c76da-1726375650/screencast_20240915_002940.webm" type="video/webm"></video>    <figcaption>A video showing tab-line-mode behavior when switching to next/prev tabs. The tab selection does not wrap around and behaves in an unexpected manner. It also starts showing buffers that have not previously been shown in this window.</figcaption>
  </figure>
<p>To solve this, there is package called <a href="https://github.com/thread314/intuitive-tab-line-mode">intuitive-tab-line-mode</a> that solves the above two problems. But, I had problems with it because it did not work well with the <a href="https://protesilaos.com/emacs/beframe">beframe</a> package that I also use.</p>
<p>Now, with Emacs 30.1, this behavior is what comes out-of-the-box with Emacs. The only config needed is to enable <code>global-tab-line-mode</code>. And because it's built-in, it works with other modes like beframe.</p><figure>
  <video controls muted preload="auto"><source src="https://srijan.ch/media/pages/blog/emacs-30-1-highlight-intuitive-tab-line/1fdc9c1be4-1726375650/screencast_20240915_003650.webm" type="video/webm"></video>    <figcaption>A video showing an intuitive tab-line-mode behavior. Tab selection wraps around and only cycles between buffers already showing in current window.</figcaption>
  </figure>
<p>Here's my config for minimal intuitive per-buffer tabs in Emacs &gt;= 30.0.91:</p>
<pre><code class="language-emacs-lisp">(use-package tab-line
  :demand t
  :bind
  (("C-&lt;iso-lefttab&gt;" . tab-line-switch-to-prev-tab)
   ("C-&lt;tab&gt;" . tab-line-switch-to-next-tab))
  :config
  (global-tab-line-mode 1)
  (setq
   tab-line-new-button-show nil
   tab-line-close-button-show nil))</code></pre>
<p>Here, I've also added keybindings to switch to next/prev tab using <code>C-TAB</code> and <code>C-S-TAB</code> keys.</p>
<p>More details about this change can be found in <a href="https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69993">it&#039;s bug report mail thread</a>.</p>
<h3>Aside: why show buffers as tabs</h3>
<p>One of the common questions is the need to show buffers as tabs at all. After all, in a long running Emacs session, there might be hundreds of buffers, and showing all of them as tabs becomes useless.</p>
<p>For me, I find that I usually work on a 2 to 5 buffers for a "purpose" at a time. These might be some files on a project I'm working on, or org-agenda + a couple of org files, or mu4e-main + headers + one or two emails and a reply. When in this mode, I think looking at all of these buffers in the tab bar gives me a good understanding of where I am in the project and makes it easy to switch to the next or previous buffer easily.</p>
<p>I also use frames and beframe to make sure that only a single "project" is showing in a frame at a time. So, even if there are hundreds of buffers in my Emacs session, only a handful are shown as tabs. This makes it useful to me without overwhelming me with too many tabs.</p>]]></content:encoded>
    <comments>https://srijan.ch/emacs-30-1-highlight-intuitive-tab-line#comments</comments>
    <slash:comments>22</slash:comments>
  </item><item>
    <title>2024-09-01-002</title>
    <description><![CDATA[My small #emacs #orgmode #gtd customization of the day: org-edna is a plugin that can be used to setup auto triggers (and blockers) when completing a task. org-gtd uses it to auto-forward the next TODO item in a project to NEXT when a task in the project is marked as DONE. The #orgedna trigger it uses is: relatives(forward-no-wrap todo-only 1 no-sort) todo!(NEXT). This works okay for me, but also …]]></description>
    <link>https://srijan.ch/notes/2024-09-01-002</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2024-09-01-002</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[orgmode]]></category>
    <category><![CDATA[gtd]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Sun, 01 Sep 2024 21:45:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>My small <a href="https://srijan.ch/tags/emacs" class="p-category">#emacs</a> <a href="https://srijan.ch/tags/orgmode" class="p-category">#orgmode</a> <a href="https://srijan.ch/tags/gtd" class="p-category">#gtd</a> customization of the day:</p>
<p><a href="https://www.nongnu.org/org-edna-el/">org-edna</a> is a plugin that can be used to setup auto triggers (and blockers) when completing a task. <a href="https://github.com/Trevoke/org-gtd.el">org-gtd</a> uses it to auto-forward the next TODO item in a project to NEXT when a task in the project is marked as DONE. The #orgedna trigger it uses is: <code>relatives(forward-no-wrap todo-only 1 no-sort) todo!(NEXT)</code>.</p>
<p>This works okay for me, but also results in tickler tasks configured as repeated tasks to go to NEXT state instead of TODO state when they are completed. This results in them showing up in the org agenda even before they are due.</p>
<p>To fix this, I had to add this property to the top-level headings of the tickler file:</p>
<pre><code class="language-org">:PROPERTIES:
:TRIGGER: self todo!(TODO)
:END:</code></pre>
<p>This overrides the global triggers configured by org-gtd for these org subtrees.</p><p>Syndicated to:</p><ul><li><a href="https://bsky.app/profile/srijan4.bsky.social/post/3l35iccfebi2o">https://bsky.app/profile/srijan4.bsky.social/post/3l35iccfebi2o</a></li></ul>]]></content:encoded>
    <comments>https://srijan.ch/notes/2024-09-01-002#comments</comments>
    <slash:comments>5</slash:comments>
  </item><item>
    <title>Using Todoist as a cloud inbox for GTD in Emacs orgmode</title>
    <description><![CDATA[Using todoist as a cloud inbox for GTD in Emacs orgmode for better integration with services like Slack and Google Assistant]]></description>
    <link>https://srijan.ch/todoist-cloud-inbox-for-gtd-in-emacs-orgmode</link>
    <guid isPermaLink="false">tag:srijan.ch:/todoist-cloud-inbox-for-gtd-in-emacs-orgmode</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[orgmode]]></category>
    <category><![CDATA[gtd]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Tue, 16 Jan 2024 12:10:00 +0000</pubDate>
    <media:content url="https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/5e26d0c015-1705406577/austin-distel-guij0yszpig-unsplash.jpg" medium="image" />
    <content:encoded><![CDATA[<figure data-ratio="auto">
    <img src="https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/5e26d0c015-1705406577/austin-distel-guij0yszpig-unsplash.jpg" alt="Person using phone and laptop">
  
    <figcaption class="text-center">
    Photo by <a href="https://unsplash.com/@austindistel">Austin Distel</a> on <a href="https://unsplash.com/">Unsplash</a>  </figcaption>
  </figure>
<p>I've been using <a href="https://www.gnu.org/software/emacs/">Emacs</a>' <a href="https://orgmode.org/">orgmode</a> as my <a href="https://gettingthingsdone.com/">GTD</a> system for the last several months. I migrated from <a href="https://todoist.com/">Todoist</a>, and one of the things I missed was integration with other services that make things easy to capture tasks into the inbox.</p>
<p>There are ways to <a href="https://srijan.ch/notes/2023-11-30-001">capture org data via email</a>, and this can be a good enough alternative, because most (though not all) services allow some kind of email forward to capture tasks. But, this depends on a complex email setup and would probably work on a single machine.</p>
<p>The main integrations/features I wanted to use were:</p>
<ol>
<li><a href="https://slack.com/">Slack</a>: Todoist has a native app for Slack using which any Slack message can be captured into Todoist as a task.</li>
<li><a href="https://todoist.com/help/articles/how-to-use-todoist-for-google-assistant-qD7srG0c">Google Assistant</a>: Todoist has integration with Google Assistant which can be used to capture tasks by talking to the google assistant.</li>
<li><a href="https://todoist.com/help/articles/task-quick-add-va4Lhpzz">Todoist quick entry</a> on mobile with date recognition: The Todoist apps have a more polished mobile capture system that can be triggered from a widget and can recognize dates using natural language entry.</li>
</ol>
<p>The workflow would look something like this:</p>
<figure><picture><source sizes="(min-width: 768px) 704px, calc(93.6vw - 45px)" srcset="https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-300x.avif 300w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-600x.avif 600w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-704x.avif 704w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-900x.avif 900w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-1200x.avif 1200w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-1800x.avif 1800w" type="image/avif"><source sizes="(min-width: 768px) 704px, calc(93.6vw - 45px)" srcset="https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-300x.webp 300w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-600x.webp 600w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-704x.webp 704w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-900x.webp 900w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-1200x.webp 1200w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-1800x.webp 1800w" type="image/webp"><img alt="" height="502" sizes="(min-width: 768px) 704px, calc(93.6vw - 45px)" src="https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-704x.png" srcset="https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-300x.png 300w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-600x.png 600w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-704x.png 704w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-900x.png 900w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-1200x.png 1200w, https://srijan.ch/media/pages/blog/todoist-cloud-inbox-for-gtd-in-emacs-orgmode/3cf2fe7e71-1705405025/todoist-emacs-inbox-1800x.png 1800w" title="" width="322"></picture></figure>
<p>I found an <a href="https://github.com/abrochard/emacs-todoist">existing Todoist integration for emacs</a>, but it's more suitable for using Todoist as the source of truth for tasks, and keeping a local buffer for operations on it in Emacs.</p>
<p>But, I was able to use it's functions to achieve what I wanted. Here's my elisp:</p>
<pre><code class="language-elisp">(use-package todoist)

(defun fetch-todoist-inbox ()
  (interactive)
  (let ((tasks (todoist--query "GET" "/tasks?project_id=&lt;project_id&gt;")))
    (mapcar (lambda (task)
              (todoist--insert-task task 1 t)
              (todoist--query
                "DELETE"
                (format "/tasks/%s" (todoist--task-id task))))
            tasks)))</code></pre>
<p>Here, the <code>project_id</code> is the Todoist project id of the project from which tasks have to be imported. This can be found by opening the project in the todoist web app - the project id is the last part of the URL.</p>
<p>The elisp function <code>fetch-todoist-inbox</code> can be called when in any org buffer (or any buffer actually). It will fetch all tasks in the specified project, insert then into the current buffer, and delete them from Todoist. It can be bound to any keybinding for easy access. Note that it requires setting the todoist token using elisp or an environment variable.</p>
<h2>Improvements Ideas</h2>
<ul>
<li>Show number of un-fetched items in status bar</li>
<li>Fetch comments and attachments</li>
<li>Fetch task labels and show as orgmode tags</li>
</ul>
<p><a class="p-category" href="https://srijan.ch/tags/gtd">#GTD</a> <a class="p-category" href="https://srijan.ch/tags/emacs">#Emacs</a> <a class="p-category" href="https://srijan.ch/tags/orgmode">#OrgMode</a></p>]]></content:encoded>
    <comments>https://srijan.ch/todoist-cloud-inbox-for-gtd-in-emacs-orgmode#comments</comments>
    <slash:comments>8</slash:comments>
  </item><item>
    <title>2023-11-30-001</title>
    <description><![CDATA[Found Samuel's nice post on capturing data for org via email. This is very close to what I was looking for to be able to do GTD capture on-the-go either from phone apps like Braintoss or from any email app. One addition I would like to make is handling attachments in the email by downloading them and attaching to the org entry. This would be useful for voice notes from Braintoss - it does …]]></description>
    <link>https://srijan.ch/notes/2023-11-30-001</link>
    <guid isPermaLink="false">tag:srijan.ch:/notes/2023-11-30-001</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[gtd]]></category>
    <category><![CDATA[orgmode]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Thu, 30 Nov 2023 18:10:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>Found Samuel's nice post on <a href="http://web.archive.org/web/20240101143355/https://samuelwflint.com/posts/2017/03/13/capturing-data-for-org-via-email/">capturing data for org via email</a>.</p>
<p>This is very close to what I was looking for to be able to do GTD capture on-the-go either from phone apps like <a href="https://braintoss.com/">Braintoss</a> or from any email app.</p>
<p>One addition I would like to make is handling attachments in the email by downloading them and attaching to the org entry.<br />
This would be useful for voice notes from Braintoss - it does transcription of the audio and adds it to the email body, but sometimes it doesn't work so well and I have to fall back to listening to the audio. It will also be useful for forwarded emails containing attachments.</p>
<p><a class="p-category" href="https://srijan.ch/tags/gtd">#GTD</a> <a class="p-category" href="https://srijan.ch/tags/emacs">#Emacs</a> <a class="p-category" href="https://srijan.ch/tags/orgmode">#OrgMode</a></p>]]></content:encoded>
    <comments>https://srijan.ch/notes/2023-11-30-001#comments</comments>
    <slash:comments>2</slash:comments>
  </item><item>
    <title>Exploring conflicting oneshot services in systemd</title>
    <description><![CDATA[Exploring ways to make two systemd services using a shared resource work with each other]]></description>
    <link>https://srijan.ch/exploring-conflicting-oneshot-services-in-systemd</link>
    <guid isPermaLink="false">64807b30f6b0810001fa0d01</guid>
    <category><![CDATA[linux]]></category>
    <category><![CDATA[devops]]></category>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[systemd]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Thu, 08 Jun 2023 19:20:00 +0000</pubDate>
    <media:content url="https://srijan.ch/media/pages/blog/exploring-conflicting-oneshot-services-in-systemd/0c15993753-1699621096/systemd-conflicts-01.png" medium="image" />
    <content:encoded><![CDATA[<figure data-ratio="auto">
    <img src="https://srijan.ch/media/pages/blog/exploring-conflicting-oneshot-services-in-systemd/0c15993753-1699621096/systemd-conflicts-01.png" alt="Exploring conflicting oneshot services in systemd">
  
    <figcaption class="text-center">
    Midjourney: two systemd services fighting over who will start first  </figcaption>
  </figure>
<h2>Background</h2>
<p>I use <a href="https://isync.sourceforge.io/mbsync.html" rel="noreferrer">mbsync</a> to sync my mailbox from my online provider (<a href="https://ref.fm/u12054901" rel="noreferrer">FastMail</a> - referer link) to my local system to eventually use with <a href="https://djcbsoftware.nl/code/mu/mu4e.html" rel="noreferrer">mu4e</a> (on Emacs).</p> <p>For periodic sync, I have a systemd service file called <code>mbsync.service</code> defining a oneshot service and a timer file called <code>mbsync.timer</code> that runs this service periodically. I can also activate the same service using a keybinding from inside mu4e.</p><figure>
  <pre><code class="language-ini">[Unit]
Description=Mailbox synchronization service
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/mbsync fastmail-all
ExecStartPost=bash -c &quot;emacsclient -s srijan -n -e &#039;(mu4e-update-index)&#039; || mu index&quot;

[Install]
WantedBy=default.target</code></pre>
    <figcaption class="text-center">mbsync.service</figcaption>
  </figure>
<figure>
  <pre><code class="language-ini">[Unit]
Description=Mailbox synchronization timer
BindsTo=graphical-session.target
After=graphical-session.target

[Timer]
OnBootSec=2m
OnUnitActiveSec=5m
Unit=mbsync.service

[Install]
WantedBy=graphical-session.target</code></pre>
    <figcaption class="text-center">mbsync.timer</figcaption>
  </figure>
<p>Also, for instant download of new mail, I have another service called <a href="https://gitlab.com/shackra/goimapnotify" rel="noreferrer">goimapnotify</a> configured that listens for new/updated/deleted messages on the remote mailbox using IMAP IDLE, and calls the above <code>mbsync.service</code> when there are changes.</p><p>This has worked well for me for several years.</p><h2>The Problem</h2>
<p>I
 recently split my (huge) archive folder into yearly archives so that I 
can keep/sync only the recent years on my phone. [ Aside: <a href="https://fedi.srijan.dev/notice/AVGV5TuD1cOEWQ8iQa" rel="noreferrer">yearly refile in mu4e snippet</a>
 ]. This lead to an increase in the number of folders that mbsync has to
 sync, and this increased the time taken to sync because it syncs the 
folders one by one.</p> <p>It does have the feature to sync a subset of folders, so I created a second systemd service called <code>mbsync-quick.service</code>
 and only synced my Inbox from this service. Then I updated the 
goimapnotify config to trigger this quick service instead of the full 
service when it detects changes.</p> <p>But, this caused a problem: these
 two services can run at the same time, and hence can cause corruption 
or sync conflicts in the mail files. So, I wanted a way to make sure 
that these two services don't run at the same time.</p> <p>Ideally,
 whenever any of these services are triggered and the other service is 
already running, then it should wait for the other service to stop 
before starting, essentially forming a queue.</p><h2>Solution 1: Using systemd features</h2>
<p>Systemd has a <a href="https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=" rel="noreferrer">way to specify conflicts</a> in the unit section. From the docs:</p><blockquote>
  If a unit has a<code>Conflicts=</code>setting on another unit, starting the former will stop the latter and vice versa.<br>[...] to ensure that the conflicting unit is stopped before the other unit is started, an<code>After=</code>or<code>Before=</code>dependency must be declared.  </blockquote>
<p>This
 is different from our requirement that the conflicting service should 
be allowed to finish before the triggered service starts, but maybe a 
good enough way to at least prevent both running at the same time.</p> <p>To test this, I added <code>Conflicts=</code>
 in both the services with the other service as the conflicting service,
 and it works. The only problem is that when a service is triggered, the
 other service is <code>SIGTERM</code>ed. This itself might not cause a 
corruption issue, but if this happens with the mbsync-quick service, 
then there might be a delay getting the mail.</p> <p>This is the best way
 I found that uses built-in systemd features without any workarounds or 
hacks. Other solutions below involve some workarounds.</p><h2>Solution 2: Conflict + stop after sync complete</h2>
<p>This
 is a variation on solution 1 - add a wrapper script to trap the SIGTERM
 and only exit when the sync is complete. This also worked.</p> <p>But, 
the drawback with this method is that anyone calling stop on these 
services (like the system shutting down) will have to wait for this to 
finish (or till timeout of 90s). This can cause slowdowns in system 
shutdown that are hard to debug. So, I don't prefer this solution.</p><h2>Solution 3: Delay start until the other service is finished</h2>
<p>This is also a hacky solution - use <code>ExecStartPre</code> to check if the other service is running, and busywait for it to stop before starting ourselves.</p><figure>
  <pre><code class="language-ini">[Unit]
Description=Mailbox synchronization service (quick)
After=network-online.target

[Service]
Type=oneshot
ExecStartPre=/bin/sh -c &#039;while systemctl --user is-active mbsync.service | grep -q activating; do sleep 0.5; done&#039;
ExecStart=/usr/bin/mbsync fastmail-inbox
ExecStartPost=bash -c &quot;emacsclient -s srijan -n -e &#039;(mu4e-update-index)&#039; || mu index&quot;</code></pre>
    <figcaption class="text-center">mbsync-quick.service</figcaption>
  </figure>
<p>Here, we use <code>systemctl is-active</code> to query the status of the other service, and wait until the other service is not in <code>activating</code> state anymore. The state is called <code>activating</code> instead of <code>active</code> because these are oneshot services that go from <code>inactive</code> to <code>activating</code> to <code>inactive</code> without ever reaching <code>active</code>.</p><p>To not make this an actual busywait on the CPU, I added a sleep of 0.5s.</p><p>This worked the best for my use case. When one of the services is triggered, it checks if the other service is running and waits for it to stop before running itself. It also does not have the drawback of solution 2 of trapping exits and delaying a stop command.</p><p>But, after using it for a day, I found there is a race condition (!) that can cause a deadlock between these two services and none of them are able to start.</p><p>The reason for the race condition was:</p><ul><li>A service is marked as <code>activating</code> when it's <code>ExecStartPre</code> command starts</li><li>I added a sleep of 0.5 seconds</li></ul><p>So, if the other service is triggered again in between those 0.5 seconds, both services will be marked as <code>activating</code> and they will indefinitely wait for each other to complete. This is what I get for using workarounds.</p><h2>Solution 4: One-way conflict, other way delay</h2>
<p>So,
 the final good-enough solution I came up with was to break this cyclic 
dependency by doing a hybrid of Solution 1 and Solution 3. I was okay 
with the <code>mbsync.service</code> being stopped for the (higher priority) <code>mbsync-quick.service</code>.</p> <p>So, I added <code>mbsync.service</code> in Conflicts section of <code>mbsync-quick.service</code>, and used the <code>ExecStartPre</code> method in <code>mbsync.service</code>.</p> <p>💡Let me know if you know a better way to achieve this.</p><h2>References</h2>
<ul><li><a href="https://unix.stackexchange.com/questions/503719/how-to-set-a-conflict-in-systemd-in-one-direction-only" rel="noreferrer">https://unix.stackexchange.com/questions/503719/how-to-set-a-conflict-in-systemd-in-one-direction-only</a></li><li><a href="https://unix.stackexchange.com/questions/465794/is-it-possible-to-make-a-systemd-unit-wait-until-all-its-conflicts-are-stopped/562959" rel="noreferrer">https://unix.stackexchange.com/questions/465794/is-it-possible-to-make-a-systemd-unit-wait-until-all-its-conflicts-are-stopped/562959</a></li></ul>]]></content:encoded>
    <comments>https://srijan.ch/exploring-conflicting-oneshot-services-in-systemd#comments</comments>
    <slash:comments>0</slash:comments>
  </item><item>
    <title>Running multiple emacs daemons</title>
    <description><![CDATA[Run multiple emacs daemons for different purposes and set different themes/config based on daemon name]]></description>
    <link>https://srijan.ch/running-multiple-emacs-daemons</link>
    <guid isPermaLink="false">60671113b1237c000188bd2e</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[development]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Fri, 02 Apr 2021 14:00:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>I have been using <a href="https://www.gnu.org/software/emacs/" rel="noreferrer">Emacs</a> for several years, and these days I'm using it both for writing code and for working with my email (another post on that soon).</p> <p>As
 commonly suggested, I run Emacs in daemon-mode to keep things fast and 
snappy, with an alias to auto-start the daemon if it's not started, and 
connect to it if started:</p><figure>
  <pre><code class="language-shell">alias e=&#039;emacsclient -a &quot;&quot; -c&#039;</code></pre>
    <figcaption class="text-center">Config for single daemon</figcaption>
  </figure>
<p>But, this has some problems:</p><ol><li>The buffers for email and code projects get mixed together</li><li>Restarting the emacs server for code (for example) kills the open mail buffers as well</li><li>Emacs themes are global – they cannot be set per frame. For code, I prefer a dark theme (most of the time), but for email, a light theme works better for me (specially for HTML email).</li></ol><p>To
 solve this, I searched for a way to run multiple emacs daemons, 
selecting which one to connect to using shell aliases, and automatically
 setting the theme based on the daemon name. Here's my setup to achieve 
this:</p><h3>Custom run_emacs function in zshrc:</h3>
<figure>
  <pre><code class="language-shell">run_emacs() {
  if [ &quot;$1&quot; != &quot;&quot; ];
  then
    server_name=&quot;${1}&quot;
    args=&quot;${@:2}&quot;
  else
    server_name=&quot;default&quot;
    args=&quot;&quot;
  fi

  if ! emacsclient -s ${server_name} &quot;${@:2}&quot;;
  then
    emacs --daemon=${server_name}
    echo &quot;&gt;&gt; Server should have started. Trying to connect...&quot;
    emacsclient -s ${server_name} &quot;${@:2}&quot;
  fi
}</code></pre>
  </figure>
<p>This function takes an optional argument – the name to be used for the daemon. If not provided, it uses <code>default</code>
 as the name. Then, it tries to connect to a running daemon with the 
name. And if it's not running, it starts the daemon and then connects to
 it. It also passes any additional arguments to <code>emacsclient</code>.</p><h3>Custom aliases in zshrc:</h3>
<figure>
  <pre><code class="language-shell"># Create a new frame in the default daemon
alias e=&#039;run_emacs default -n -c&#039;

# Create a new terminal (TTY) frame in the default daemon
alias en=&#039;run_emacs default -t&#039;

# Open a file to edit using sudo
es() {
    e &quot;/sudo:root@localhost:$@&quot;
}

# Open a new frame in the `mail` daemon, and start notmuch in the frame
alias em=&quot;run_emacs mail -n -c -e &#039;(notmuch-hello)&#039;&quot;</code></pre>
  </figure>
<p>The first 3 aliases use the <code>default</code> daemon. The last one creates a new frame in the <code>mail</code> daemon and also uses <code>emacsclient</code>'s <code>-e</code> flag to start notmuch (the email package I use in Emacs).</p><h3>Emacs config:</h3>
<figure>
  <pre><code class="language-elisp">(cond
 ((string= &quot;mail&quot; (daemonp))
  (setq doom-theme &#039;modus-operandi)
 )
 (t
  (setq doom-theme &#039;modus-vivendi)
 )
)</code></pre>
  </figure>
<p>This checks the name of the daemon passed during 
startup, and sets the doom theme accordingly. The same pattern can be 
used to set any config based on the daemon name.</p> <p>Note that I'm using <a href="https://github.com/hlissner/doom-emacs" rel="noreferrer">doom emacs</a>, but the above method should work with or without any framework for Emacs. Tested with Emacs 27 and 28.</p>]]></content:encoded>
    <comments>https://srijan.ch/running-multiple-emacs-daemons#comments</comments>
    <slash:comments>2</slash:comments>
  </item><item>
    <title>Trying Emacs</title>
    <description><![CDATA[Bare bones emacs configuration from when I first started using Emacs]]></description>
    <link>https://srijan.ch/trying-emacs</link>
    <guid isPermaLink="false">6030d3dab5e0920001f557d8</guid>
    <category><![CDATA[emacs]]></category>
    <category><![CDATA[development]]></category>
    <dc:creator>Srijan Choudhary</dc:creator>
    <pubDate>Fri, 16 Aug 2013 00:00:00 +0000</pubDate>
    <content:encoded><![CDATA[<p>I have been using <a href="http://www.vim.org/">Vim</a> as my text editor for the last few years, and have been very happy with it. But lately, some features of <a href="http://www.gnu.org/software/emacs/">Emacs</a> have got me interested (especially <a href="http://orgmode.org/">org-mode</a>),
 and I wanted to try it out. After all, I won't know the difference 
until I actually try it, and opinions on text editors vary widely on the
 internet.</p> <p>So, I decided to give it a try. First I went through the built-in 
Emacs Tutorial, and it seemed easy enough. I got used to the basic 
commands fairly quickly. I guess the real benefits will start to show a 
little later, when I try to optimize some ways of doing things.</p> <p>For now, I just wanted to do some basic configuration so that I could
 start using emacs right now. So, I did the following changes (scroll to
 the bottom of this page for the full <code>init.el</code> file):</p><ul><li><p>Hide the menu, tool, and scroll bars</p></li><li><p>Add line numbers</p></li><li><p>Hide splash screen and banner</p></li><li><p>Setup <a href="http://marmalade-repo.org/">Marmalade</a><br />
Marmalade is a package archive for emacs, which makes it easier to install non-official packages.</p></li><li><p>Maximize emacs window on startup<br />
My emacs was not starting up maximized, and I did not want to maximize it manually every time I started it. I found <a href="http://www.emacswiki.org/emacs/FullScreen">this page</a> addressing this issue, and tried out one of the <a href="http://www.emacswiki.org/emacs/FullScreen#toc20">solutions for linux</a>, and it worked great.</p></li></ul><p>For now, it all looks good, and I can start using it with only this small configuration.</p> <p>For example, for writing this post, I installed <a href="http://jblevins.org/projects/markdown-mode/">markdown-mode</a> using marmalade, and I got syntax highlighting and stuff.</p> <p>I will keep using this, and adding to my setup as required, for a few
 weeks, and then evaluate whether I should switch completely.</p><h3>Complete ~/.emacs.d/init.el file:</h3>
<figure>
  <pre><code class="language-elisp">; init.el

; Remove GUI extras
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)

; Add line numbers
(global-linum-mode 1)

; Hide splash screen and banner
(setq
 inhibit-startup-message t
 inhibit-startup-echo-area-message t)
(define-key global-map (kbd &quot;RET&quot;) &#039;newline-and-indent)

; Set up marmalade
(require &#039;package)
(add-to-list &#039;package-archives 
    &#039;(&quot;marmalade&quot; .
      &quot;http://marmalade-repo.org/packages/&quot;))
(package-initialize)

; Make window maximized
(shell-command &quot;wmctrl -r :ACTIVE: -btoggle,maximized_vert,maximized_horz&quot;)</code></pre>
  </figure>
]]></content:encoded>
    <comments>https://srijan.ch/trying-emacs#comments</comments>
    <slash:comments>0</slash:comments>
  </item></channel>
</rss>
