emacs/var/elfeed/db/data/61/61a30e08172b18b2dbd79a8787430aa5255c2581
2022-01-03 12:49:32 -06:00

257 lines
16 KiB
Plaintext

<div class="outline-2" id="outline-container-org20f7efc">
<h2 id="org20f7efc">Zettelwhat?</h2>
<div class="outline-text-2" id="text-org20f7efc">
<p>
A couple of years ago, I was trying to improve my note taking abilities and did some research. I discovered the Zettelkasten method and read the book <i>How to take smart notes</i> by Sönke Ahrens which describes this approach invented by Niklas Luhmann, a German sociologist.
</p>
<p>
For a quick introduction to the method, I find this <a href="https://zettelkasten.de/posts/overview/">web site</a> very well done. If you get interested in Zettelkasten, before jumping to the last shiny app or Emacs package, I think it's better to read Ahrens' book.
</p>
<p>
Anyway, in a few words, in Zettelkasten, you create notes with small bits of knowledge that are meant to be self contained. These notes may contain links to other notes with related content. The idea is that the knowledge is not organized hierarchically, but in a graph where notes point to other notes.
</p>
<p>
The Zettelkasten is a living thing where notes are regularly added and most importantly, the notes are frequently read and improved, either by reformulating the content, adding links to other notes, etc.
</p>
<p>
The Zettelkasten is meant to be personal, edited by a single person. It's like a second brain.
</p>
<p>
Niklas Luhmann did everything by hand and his Zettelkasten was made of paper cards. He had to invent a clever indexing method and used special <i>structure notes</i> to create tables of contents for different subjects. It is a pleasure to browse <a href="https://niklas-luhmann-archiv.de/bestand/zettelkasten/suche">the original Zettels</a>.
</p>
<p>
In order to implement a digital Zettelkasten, we only need a note taking application with the ability to create links between notes. A nice bonus is adding tags to the notes to simplify search and generation of sets of related notes.
</p>
<p>
There are lots of applications on the proprietary software market that support the creation and management of a Zettelkasten. There are also free software counterparts.
</p>
<p>
Emacs offers several alternatives in terms of packages:
</p>
<ul class="org-ul">
<li><a href="https://github.com/felko/neuron-mode">Neuron mode</a>, which uses markdown;</li>
<li><a href="https://github.com/ymherklotz/emacs-zettelkasten">org-zettelkasten</a>, which is a set of functions on top of org-mode;</li>
<li><a href="https://efls.github.io/zetteldeft/">Zetteldeft</a>, which uses <a href="https://jblevins.org/projects/deft/">deft</a>;</li>
<li><a href="https://github.com/org-roam/org-roam">org-roam</a>, based on org-mode and inspired by a commercial application.</li>
</ul>
<p>
There are probably others that I am not aware of. Org-roam seems to be the most popular one.
</p>
<p>
After reading Ahrens' book, I decided that I wanted to try the approach. I did not think want to choose one of the available Emacs packages for several reasons. The first one is that I did not understand why I needed anything else than plain org-mode. The second one was that I did not want to commit to any particular implementation before understanding how and if the approach would be useful for me.
</p>
</div>
</div>
<div class="outline-2" id="outline-container-org9995d75">
<h2 id="org9995d75">How I do Zettelkasten</h2>
<div class="outline-text-2" id="text-org9995d75">
<p>
I have a big org-mode file called <code>zettels.org</code> with to top-level headings, one for <i>structure notes</i> and another one for standard zettels. Each note is a second level heading with a title, possibly some org-mode tags, a property drawer and the note content.
</p>
<p>
The property drawer contains at least the <code>DATE_CREATED</code> property with an inactive org-mode timestamp for the day and time when the note was created.
</p>
<p>
The <i>structure notes</i> are created by hand. That means that I create a heading, write the note and add the <code>DATE_CREATED</code> property. I do not create many structure notes, so a manual workflow is OK.
</p>
<p>
For the standard zettels, I use org-capture. The capture template automatically inserts the <code>DATE_CREATED</code> property, but also a <code>FROM</code> property with an org-mode link to the place I was in Emacs when I run org-capture. This link can therefore point to another Zettel (for which an org-id will be created), any org-mode heading if I am in an org-mode file, but this can also be an e-mail in Gnus, a pdf document, an EPUB file, a web page, etc. This is possible because I do <b>everything</b> in Emacs. Storing where I was when I created the note gives interesting context.
</p>
<p>
I have 2 org-capture templates for Zettelkasten, one which does what I described above, and another one which is used for quotes. The latter will copy the marked region in the current buffer into a quote org-mode block.
</p>
<p>
So a typical captured zettel may look like this:
</p>
<pre class="example" id="orge5e90bc">** The title of the note :tag1:tag2:
:PROPERTIES:
:DATE_CREATED: [2021-08-06 Fri 22:43]
:FROM: [[nov:/home/garjola/Calibre Library/abook.epub::26:7069][EPUB file at /home/garjola/Calibre Library/abook.epub]]
:END:
My ideas on the subject. Etc.
#+begin_quote
Some text that was marked in the EPUB I was reading.
#+end_quote
- See also [[id:e2b5839d-d7ef-4151-8676-17dacd261e86][Another note linked with org-id.]]
</pre>
<p>
This way, while I am reading interesting things, I can capture an idea with all its context. I will of course come later to this note to improve it.
</p>
<p>
This is done in my regular <i>gardening sessions</i>. During these sessions, I browse the Zettelkasten, read notes, add tags and links to other notes, rewrite things, etc.
</p>
<p>
For this tasks, I use a couple of functions. The first one jumps to a random Zettel, so I am sure that I regularly explore forgotten parts of the Zettelkasten. The second one finds <i>back-links</i>, that is notes having links that point to the current note. This is useful for a bi-directional browsing of the content.
</p>
</div>
</div>
<div class="outline-2" id="outline-container-org3d2fa7b">
<h2 id="org3d2fa7b">My custom Zettelkasten setup</h2>
<div class="outline-text-2" id="text-org3d2fa7b">
</div>
<div class="outline-3" id="outline-container-orgdab062e">
<h3 id="orgdab062e">Capture templates for Zettelkasten</h3>
<div class="outline-text-3" id="text-orgdab062e">
<p>
The first component of the setup is the capture templates. They look like this:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">setq</span> org-capture-templates
(append org-capture-templates
(<span class="org-keyword">quote</span> ((<span class="org-string">"z"</span> <span class="org-string">"Zettelkasten"</span>)
(<span class="org-string">"zz"</span> <span class="org-string">"Zettel"</span> entry
(file+headline <span class="org-string">"~/org/zettels.org"</span> <span class="org-string">"Zettels"</span>)
(<span class="org-keyword">function</span> my/zettel-template)
<span class="org-builtin">:empty-lines</span> 1)
(<span class="org-string">"zq"</span> <span class="org-string">"Quote"</span> entry
(file+headline <span class="org-string">"~/org/zettels.org"</span> <span class="org-string">"Zettels"</span>)
(<span class="org-keyword">function</span> my/zettel-quote-template)
<span class="org-builtin">:empty-lines</span> 1)))))
</pre>
</div>
<p>
There are 2 templates, one for notes without quotes (called with <code>zz</code>) and another for notes where I want to insert the marked region in the current buffer as a quote (called with <code>zq</code>). Both templates insert the note in the <code>zettels.org</code> file under the <code>Zettels</code> heading. Instead of writing the template in this code, I prefer using a function to generate it. I find this more readable.
</p>
<p>
The 2 functions are here:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my/zettel-template</span> ()
<span class="org-doc">"* %?\n:PROPERTIES:\n:DATE_CREATED: %U\n:FROM: %a\n:END:\n%i\n"</span>)
(<span class="org-keyword">defun</span> <span class="org-function-name">my/zettel-quote-template</span> ()
<span class="org-doc">"* %?\n:PROPERTIES:\n:DATE_CREATED: %U\n:FROM: %a\n:END:\n#+begin_quote\n%i\n#+end_quote"</span>)
</pre>
</div>
<p>
They are straightforward. The cursor is placed in the heading (with the <code>%?</code> org-expansion) so I can write the title. The property drawer will contain the time stamp and a link to the place Emacs was when org-capture was called. In the case of the quote, the marked region is copied inside the org-mode quote block.
</p>
<p>
And that's it!
</p>
</div>
</div>
<div class="outline-3" id="outline-container-org02de5bb">
<h3 id="org02de5bb">Back-links to zettels</h3>
<div class="outline-text-3" id="text-org02de5bb">
<p>
Back-links can be tricky. The package <a href="https://github.com/alphapapa/org-sidebar">org-sidebar</a> provides a function for that. But for some reason that I don't remember, I didn't like the way it worked (or more probably, my incompetence did not allow me to make it work). So I searched a bit and found <a href="https://www.reddit.com/r/orgmode/comments/fyr2ai/orgsuperlinks_new_package_for_auto_adding/">a bit of elisp</a> that does what I need.
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">require</span> '<span class="org-constant">org-ql</span>)
(<span class="org-keyword">require</span> '<span class="org-constant">org-ql-view</span>)
(<span class="org-keyword">defun</span> <span class="org-function-name">my/zettel-backlinks</span> ()
(<span class="org-keyword">interactive</span>)
(<span class="org-keyword">let*</span> ((id (org-entry-get (point) <span class="org-string">"ID"</span>))
(custom-id (org-entry-get (point) <span class="org-string">"CUSTOM_ID"</span>))
(query (<span class="org-keyword">cond</span> ((<span class="org-keyword">and</span> id custom-id)
<span class="org-comment-delimiter">;; </span><span class="org-comment">This will be slow because it isn't optimized to a single regexp. :(</span>
(<span class="org-warning">warn</span> <span class="org-string">"Entry has both ID and CUSTOM_ID set; query will be slow"</span>)
`(<span class="org-keyword">or</span> (link <span class="org-builtin">:target</span> ,(concat <span class="org-string">"id:"</span> id))
(link <span class="org-builtin">:target</span> ,(concat <span class="org-string">"id:"</span> custom-id))))
((<span class="org-keyword">or</span> id custom-id)
`(link <span class="org-builtin">:target</span> ,(concat <span class="org-string">"id:"</span> (<span class="org-keyword">or</span> id custom-id))))
(t (<span class="org-warning">error</span> <span class="org-string">"Entry has no ID nor CUSTOM_ID property"</span>))))
(title (concat <span class="org-string">"Links to: "</span> (org-get-heading t t)))
(org-agenda-tag-filter nil))
(org-ql-search (<span class="org-keyword">quote</span> (<span class="org-string">"~/org/zettels.org"</span>)) query <span class="org-builtin">:title</span> title)))
</pre>
</div>
<p>
It uses the wonderful <a href="https://github.com/alphapapa/org-ql">org-ql</a> package to search all org headings with a link matching the org-id of the current note. The search is limited to my <code>zettels.org</code> file.
</p>
</div>
</div>
<div class="outline-3" id="outline-container-org51b60f0">
<h3 id="org51b60f0">Jump to a random zettel</h3>
<div class="outline-text-3" id="text-org51b60f0">
<p>
The last bit I needed for serendipity is jumping to a random note. For this, I use the <a href="http://github.com/mwfogleman/org-randomnote">org-randomnote</a> package. This package uses the <code>org-randomnote-candidates</code> variable to store the list of files that will be searched for random headings. It is initialized to the value of <code>org-agenda-files</code>. I just wrote a couple of functions to temporary change the list of candidate files so that I can limit the random jump to my zettels file:
</p>
<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my/random-note</span> (candidates)
<span class="org-doc">"Jump to a random org heading in one of the `CANDIDATES` files"</span>
(<span class="org-keyword">let</span> ((old-randomnote-candidates org-randomnote-candidates))
(<span class="org-keyword">setq</span> org-randomnote-candidates candidates)
(org-randomnote <span class="org-string">"zettels"</span>)
(<span class="org-keyword">setq</span> org-randomnote-candidates old-randomnote-candidates)))
(<span class="org-keyword">defun</span> <span class="org-function-name">my/random-zettel</span> ()
<span class="org-doc">"Jump to a random zettel"</span>
(<span class="org-keyword">interactive</span>)
(my/random-note '(<span class="org-string">"~/org/zettels.org"</span>)))
</pre>
</div>
</div>
</div>
</div>
<div class="outline-2" id="outline-container-orgcb163a0">
<h2 id="orgcb163a0">Moving to org-roam (or maybe not)</h2>
<div class="outline-text-2" id="text-orgcb163a0">
<p>
My first Zettel dates back to December 30 2019. I've been using this system since then and I am very happy with it. A year ago, I started seeing a big buzz about org-roam and I looked into it. It seemed very nice, with functionalities that I don't have in my system and I started wondering whether I should use it. Since at that time I understood that there were some breaking changes planned for version 2, I decided to wait for org-roam v2 to be released and re-evaluate the situation.
</p>
<p>
In the meantime, my Zettelkasten has continued growing. Today, it contains 1345 zettels. After org-roam v2 was released, David Wilson at System Crafters did a <a href="https://www.youtube.com/playlist?list=PLEoMzSkcN8oN3x3XaZQ-AXFKv52LZzjqD">series of videos</a> showing the power of org-roam. As always, David's videos are of great help to get started and he has the rare ability of guiding the interested <i>crafter</i> so that he or she is able to customize thing as wanted.
</p>
<p>
I therefore installed org-roam and even wrote the code to migrate my <i>Zettelkustom</i> to org-roam. I was an interesting exercise that allowed me increase my knowledge of Emacs Lisp and get familiar with the <code>org-element</code> API.
</p>
<p>
However, I am not sure that I want to do the migration. Although org-roam is very rich and has a large community of users, I have the feeling that it does to many things that I don't need. I guess that, if I was starting with org-mode, org-roam would be the good choice, but I have been using org-mode for personal organization (with GTD), note taking, writing scientific papers and reports, doing literate programming, and may more things.
</p>
<p>
And I have my personal preferences and habits that I will likely not change. For instance, for me, mixing project management and Zettelkasten does not make sense. I also don't need a special integration between Zettelkasten and my bibliographic database (<a href="https://github.com/jkitchin/org-ref">org-ref</a> and <a href="https://github.com/tmalsburg/helm-bibtex/blob/master/README.ivy-bibtex.org">ivy-bibtex</a> are all I need).
</p>
<p>
Therefore, by now, I'll stick to my 2 capture templates and 2 simple functions that I understand well.
</p>
<p>
I any case, all this shows the power of org-mode and its ecosystem.
</p>
</div>
</div>
<div class="taglist"><a href="https://dindi.garjola.net/tags.html">Tags</a>: <a href="https://dindi.garjola.net/tag-emacs.html">emacs</a> <a href="https://dindi.garjola.net/tag-zettelkasten.html">zettelkasten</a> <a href="https://dindi.garjola.net/tag-org-mode.html">org-mode</a> </div>