101 lines
14 KiB
Plaintext
101 lines
14 KiB
Plaintext
<p>After using <a href="https://github.com/toshism/org-super-links">super-links</a> for almost a year, custom IDs and backlinks became an inseparable part of my workflow. It’s the only part I’ve adopted from the whole Zettelkasten/org-roam craze. Org-mode’s built-in custom IDs don’t make sense, so I decided to create better custom IDs and teach myself some Emacs-lisp in the process.</p>
|
||
<p>My idea was to contain the buffer’s name with the current date (ISO formatted) in the ID. For example, if I’m looking at the buffer I’m writing this post in: taonaw.2021-12-19.1730.</p>
|
||
<p>Here’s the process I followed.</p>
|
||
<h2 id="getting-iso-style-time">Getting ISO style time</h2>
|
||
<p>This is how I get the time in the desired format, like 2021-12-19.1730 (17:30 is 5:30 PM).</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">((<span style="color: #a6e22e;">format-time-string</span> <span style="color: #e6db74;">"%Y-%m-%-d.%H%M"</span>(<span style="color: #a6e22e;">current-time</span>))
|
||
</code></pre></div><p>There are two functions here:</p>
|
||
<ol>
|
||
<li><code>current-time</code> gets the current time in Emacs. Kind of. It returns the time in seconds since 01/01/1970 (the <a href="https://en.wikipedia.org/wiki/Unix%5Ftime">Unix Epoch</a>.)</li>
|
||
<li><code>format-time-string</code> formats the current time into something more human-readable, in this case, ISO format. The operators above are common across various programming languages. From the help text on <code>format-time-string</code>:</li>
|
||
</ol>
|
||
<blockquote>
|
||
<p>%Y is the year, %y within the century, %C the century.</p>
|
||
<p>%G is the year corresponding to the ISO week, %g within the century.</p>
|
||
<p>%m is the numeric month.</p>
|
||
<p>%b and %h are the locale’s abbreviated month name, %B the full name.
|
||
(%h is not supported on MS-Windows.)</p>
|
||
<p>%d is the day of the month, zero-padded, %e is blank-padded.</p>
|
||
</blockquote>
|
||
<p>…and so on. %H for hours, %M (capital M) for minutes.</p>
|
||
<h2 id="getting-it-into-a-variable">Getting it into a Variable</h2>
|
||
<p>In order to do something with our date above, we need to assign it a variable. For this we use the function <code>Let</code>.</p>
|
||
<p><code>Let</code> sets variables and their values within the list it creates. The variables do not exist outside of this list. What’s a list? well, think of it as a fence made from parentheses.</p>
|
||
<p>For example: <code>let ((dog)(cat)(mouse))</code> creates three variables. We also assign them a value at the same time: <code>let ((dog woof)(cat meow)(mouse squeak))</code>. <code>Let</code> is usually what we want to use in a function because the values are unique to that function. In our case, that would be the date we created above. This is how it looks like:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(let ((timestring (<span style="color: #a6e22e;">format-time-string</span> <span style="color: #e6db74;">"%Y-%m-%-d.%H%M"</span>(<span style="color: #a6e22e;">current-time</span>)))))
|
||
</code></pre></div><p>we create only one variable, timestring, and then we use the piece of code from above to assign a value to it. Look closely at the parentheses, and the order makes sense:</p>
|
||
<ol>
|
||
<li>Use the function <code>current-time</code>.</li>
|
||
<li>Use <code>format-time-string</code> on the result of this function to format the time into a yyyy-mm-dd.hhmm.</li>
|
||
<li>Store this string we just created (the value) in a variable, “timestring”</li>
|
||
</ol>
|
||
<p>In order to be sure we have the value we want inside timestring, we can ask Emacs to display it in our messages buffer, with <code>message timestring</code>.</p>
|
||
<p>The end result looks like this:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(let ((timestring (<span style="color: #a6e22e;">format-time-string</span> <span style="color: #e6db74;">"%Y-%m-%-d.%H%M"</span>(<span style="color: #a6e22e;">current-time</span>))))
|
||
(<span style="color: #a6e22e;">message</span> timestring))
|
||
</code></pre></div><p>Why do we need double parentheses after the <code>let</code> ? It doesn’t look like it makes any difference…?</p>
|
||
<p>Remember the lists from our example. <code>Let</code> is meant to be used for a couple of items at once. Emacs doesn’t care if we have one item or a hundred: the way we mark them is the same. Since each item in the list is marked with its own parentheses, timestring gets its own pair. Look closely and you will see that timestring’s parentheses end after the <code>format-time-string</code> function; the next one belongs to <code>message timestring</code>.</p>
|
||
<p>Lists are important in Emacs-Lisp. As a matter of fact, That’s what its name stands for: LISt Processing language. It’s right there in <a href="https://www.gnu.org/software/emacs/manual/html%5Fnode/elisp/Lisp-History.html">the manual</a>.</p>
|
||
<p>It took me some time to understand the logic behind it all (with additional extra help), but at the end, you can see it in action.</p>
|
||
<h2 id="getting-the-file--buffer--name">Getting the File (Buffer) Name</h2>
|
||
<p>To add the buffer’s name to the ID we need a different function, <code>buffer-file-name</code>. Since we’ll be working with files, we’ll also need <a href="https://www.gnu.org/software/emacs/manual/html%5Fnode/elisp/File-Name-Components.html">file-name and its components</a>. Before we dive in, it’s important to understand how Emacs “understands” files. From the help text:</p>
|
||
<blockquote>
|
||
<p>Emacs considers a file name as having two main parts: the directory name part, and the nondirectory part (or file name within the directory). Either part may be empty. Concatenating these two parts reproduces the original file name.</p>
|
||
</blockquote>
|
||
<p>Since we’re just interested in the file without the path or the extension, we had to clean it up a bit<sup id="fnref:1"><a class="footnote-ref" href="https://helpdeskheadesk.net/tags/emacs/index.xml#fn:1">1</a></sup>.</p>
|
||
<p>Here’s the code, explanation follows:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(let ((filename (<span style="color: #a6e22e;">file-name-nondirectory</span> (file-name-sans-extension (<span style="color: #a6e22e;">buffer-file-name</span>)))))
|
||
(<span style="color: #a6e22e;">message</span> filename))
|
||
</code></pre></div><p>We already know about <code>let</code>. Here, we’re creating a variable named “filename” and giving it a value through these functions, by the order of the parentheses:</p>
|
||
<ol>
|
||
<li><code>buffer-file-name</code> does exactly what the name implies, gives you the file that belongs to the buffer you’re visiting, complete with a full path.</li>
|
||
<li>First part of “cleaning” the name is on the second level, <code>file-name-sans-extension</code>, which is also pretty straightforward: it gets read of the extension of the file.</li>
|
||
<li>Next, <code>file-name-nondirectory</code> gives out the name of the file without the directory it’s in. This finishes the “cleaning” and leaves us with just the name of the file without its extension and without the directory.</li>
|
||
<li>The name of the file name without the directory and extension is stored in “filename”.</li>
|
||
<li>Like before, to verify we have the right value, we print the variable we created in the messages buffer.</li>
|
||
</ol>
|
||
<h2 id="connecting-the-two-variables">Connecting the two Variables</h2>
|
||
<p>Since we want to get a value that connects the file’s name with the
|
||
date, we need to connect our variables: filename + timestring.</p>
|
||
<p>We already know that <code>let</code> creates lists, so we will build a new <code>let</code> that contains both “filename” and “timestring,” using the code above.</p>
|
||
<p>We then need to connect them together into a new string, the filename + timestring part. We do that with the <code>concat</code> function.</p>
|
||
<p>the code:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(let ((filename (<span style="color: #a6e22e;">file-name-nondirectory</span> (file-name-sans-extension (<span style="color: #a6e22e;">buffer-file-name</span>))))
|
||
(timestring (<span style="color: #a6e22e;">format-time-string</span> <span style="color: #e6db74;">"%Y-%m-%d.%H%M.%S"</span>(<span style="color: #a6e22e;">current-time</span>))))
|
||
(let ((ID (<span style="color: #a6e22e;">concat</span> filename <span style="color: #e6db74;">"."</span> timestring)))
|
||
(<span style="color: #a6e22e;">message</span> ID)))
|
||
</code></pre></div><p>Lots of parentheses which need to be followed carefully. Emacs has a few solutions for this, like <code>show-paran-mode</code>.</p>
|
||
<p>The first <code>let</code> opens up with the instruction above, but instead of closing the <code>let</code> after <code>buffer-file-name</code> like we did before, we open another <code>let</code> inside the existing one. This <code>let</code> creates a new variable, “ID,” which in turn is getting value from the <code>concat</code> function. The concat takes filename, adds a period at the end of it, and then adds timestring at the end. This gets us what we want.</p>
|
||
<p>When I look at the function now and follow the parentheses, an order emerges. Can you see it? It starts with <code>buffer-file-name</code> and <code>current-time</code> simultaneously, and follows an order of procedures all the way to <code>message ID</code>.</p>
|
||
<h2 id="making-it-all-work">Making it All Work</h2>
|
||
<p>Now that we have both parts in place working together, we need to “wrap” the code in a function, so we can call it and use it:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(defun filename_ID()
|
||
(interactive)
|
||
(let ((head (<span style="color: #a6e22e;">file-name-nondirectory</span> (file-name-sans-extension (<span style="color: #a6e22e;">buffer-file-name</span>))))
|
||
(tail (<span style="color: #a6e22e;">format-time-string</span> <span style="color: #e6db74;">"%Y-%m-%d.%H%M.%S"</span>(<span style="color: #a6e22e;">current-time</span>))))
|
||
(let ((filename (<span style="color: #a6e22e;">concat</span> head <span style="color: #e6db74;">"."</span> tail)))
|
||
(<span style="color: #a6e22e;">message</span> filename)))
|
||
)
|
||
</code></pre></div><p>Here’s what was added:</p>
|
||
<p><code>defun</code> is how we define a function in Emacs, followed by a name: filname_ID in this case.</p>
|
||
<p><code>interactive</code> is a special function part (per the help text, this is actually a deceleration. I am not sure yet how this compares to a function, so if you have an idea, feel free to let me know) which allows us to call the function later. In our case, we want to store this function in Emacs' configuration so that we can call it later on when it’s time to create a custom ID.</p>
|
||
<h2 id="using-in-capture-next-steps">Using in Capture: Next Steps</h2>
|
||
<p>When I wanted to use this function inside my capture templates, I encountered a problem: the capture buffer does not have a file associated with it, so the function does not work. Even if we were to use <code>buffer-name</code> (see footnotes), this will still not work well: the capture templates use an “inbox” file, which is a temporary location. I want the ID of the headers to reflect their final position, like my wiki.org or weekly files such as 21_50.org.</p>
|
||
<p>To get around this problem I started exploring the idea of using a function as a target pointer for my capture templates, and found out something interesting: the target in capture templates is usually defined as a path, such as <code>~/Documents/Org-files/</code> or similar. However, you don’t <em>have</em> to. You can put in a name of a variable you assign a value of a path.</p>
|
||
<p>For example, I can set a path using the following <sup id="fnref:2"><a class="footnote-ref" href="https://helpdeskheadesk.net/tags/emacs/index.xml#fn:2">2</a></sup>:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(setq capture-path <span style="color: #e6db74;">"~/Documents/Org-files/tasks.org"</span>)
|
||
</code></pre></div><p>Later, in the capture template, all I have to do is something like this:</p>
|
||
<div class="highlight"><pre><code class="language-emacs-lisp">(<span style="color: #e6db74;">"t"</span> <span style="color: #e6db74;">"task"</span> entry (file+headline capture-path <span style="color: #e6db74;">"Tasks"</span>) <span style="color: #e6db74;">"** TODO %? %^g\n"</span>)
|
||
</code></pre></div><p>to create a task as a secondary header under my “Tasks” heading inside the tasks.org file. In turn, this allows creating another function that could define the target dynamically. In my case, I could use something similar to what we did earlier with timestring to generate a <code>yy_mm</code>.org value for the variable I will use later as a target. Seems like I have some more brainstorming to do.</p>
|
||
<h2 id="footnotes">Footnotes</h2>
|
||
<section class="footnotes">
|
||
<hr />
|
||
<ol>
|
||
<li id="fn:1">
|
||
<p>As I was writing this post, looking into the help documents, I realized there are better functions to use here: <code>buffer-name</code> will return the name of the buffer, which works even if the buffer does not have a file. <code>file-name-base</code> will give out the name of the file without the path and without the extension without the need to “clean it up.” I’m glad I went through the steps above though as a learning experience. <a class="footnote-backref" href="https://helpdeskheadesk.net/tags/emacs/index.xml#fnref:1">↩︎</a></p>
|
||
</li>
|
||
<li id="fn:2">
|
||
<p>Notice that we use <code>setq</code> here. Unlike <code>let</code>, <code>setq</code> sets a variable that remains beyond the immediate expression it was called in. This is necessary since the capture templates are their own functions, and will use this pre-defined variable. <a class="footnote-backref" href="https://helpdeskheadesk.net/tags/emacs/index.xml#fnref:2">↩︎</a></p>
|
||
</li>
|
||
</ol>
|
||
</section> |