<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Trapped inside a tölva</title>
    <description>Here I leave messages to the world, myself and our future robot overlords.EOF
</description>
    <link>http://edoput.it/</link>
    <atom:link href="http://edoput.it/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 14 Jan 2026 21:24:04 +0100</pubDate>
    <lastBuildDate>Wed, 14 Jan 2026 21:24:04 +0100</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Managing nginx</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://nginx.org/&quot;&gt;Nginx&lt;/a&gt; can be a bit of a bother to configure, especially
since I want to buy in the configuration that my distribution provides.&lt;/p&gt;

&lt;p&gt;On my &lt;a href=&quot;https://debian.org&quot;&gt;Debian&lt;/a&gt; box, this is what the configuration layout looked like when
I started. In the rest of this post, I will omit parts of the configuration
that are not relevant.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# tree -d /etc/nginx/
/etc/nginx/
├── conf.d
├── modules-available
├── modules-enabled
├── sites-available
└── sites-enabled
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The configuration was split into directories, and I would modify a single file in
a directory. As an example, I added a notfound configuration file for a catch-all
website that would be used for mispellings or mistyped URLs. This prevents
nginx from redirecting a request for edoput.it to somethingelse.edoput.it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# tree  /etc/nginx/
/etc/nginx/
├── sites-available
│   ├── default-server.conf
│   └── notfound.conf
└── sites-enabled
    ├── default-server.conf -&amp;gt; /etc/nginx/sites-available/default-server.conf
    └── notfound.conf -&amp;gt; /etc/nginx/sites-available/notfound.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The interesting bit about this layout is that there is an available folder and
an enabled folder. Configuration files are stored in the available folder and
symlinked from the enabled folder. This makes it easy to enable and disable
a website without actually changing it.
You can either create or delete a symbolic link in the filesystem but the actual
configuration is never touched. This is extremely handy.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# rm /etc/nginx/sites-enabled/default-server.conf # disable default-server
# ln -s /etc/nginx/sites-enabled/default-server.conf /etc/nginx/sites-available/default-server.conf # enable default-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The problem is that these command are a mouthful and could be abstracted.
If you think we should take advantage of the available/enabled split
then you should be interested in &lt;a href=&quot;https://github.com/edoput/nginxctl&quot;&gt;nginxctl&lt;/a&gt;.
It is similar in spirit to &lt;a href=&quot;https://github.com/perusio/nginx_ensite&quot;&gt;nginx_ensite&lt;/a&gt; &lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
but extends to the many kinds of resources that nginx has.&lt;/p&gt;

&lt;p&gt;Once copied to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/nginxctl&lt;/code&gt;, you create a new command to manage the resource you are
interested in-for example, the configuration files in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sites&lt;/code&gt; directory:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ln -s /usr/local/bin/nginxctl /usr/local/bin/sites
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This also means that by replicating the available/enabled pattern for a
resource you automatically get the command to work. As an example, I will show
my current configuration: I have added directories for &lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;maps&lt;/a&gt;,
&lt;a href=&quot;https://nginx.org/en/docs/stream/ngx_stream_core_module.html&quot;&gt;streams&lt;/a&gt; , and
&lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_upstream_module.html&quot;&gt;upstreams&lt;/a&gt; and created a command for each of them.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# tree -d /etc/nginx
/etc/nginx/
├── conf.d
├── map-available
├── map-enabled
├── modules-available
├── modules-enabled
├── sites-available
├── sites-enabled
├── streams-available
├── streams-enabled
├── upstream-available
└── upstream-enabled
# tree /usr/local/bin
/usr/local/bin/
├── nginxctl
├── maps -&amp;gt; /usr/local/bin/nginxctl
├── sites -&amp;gt; /usr/local/bin/nginxctl
├── streams -&amp;gt; /usr/local/bin/nginxctl
└── upstreams -&amp;gt; /usr/local/bin/nginxctl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I found this extremely helpful for managing the complexity I created for myself. Moreover
as you may have intuited by now, these are all commands available to shell
scripts, which means you can build upon them to automate even more.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;And Apache’s a2ensite a2disite &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 01 Jan 2026 00:00:00 +0100</pubDate>
        <link>http://edoput.it/2026/01/01/manage-nginx.html</link>
        <guid isPermaLink="true">http://edoput.it/2026/01/01/manage-nginx.html</guid>
        
        
      </item>
    
      <item>
        <title>Immutable Linux is just right (for me)</title>
        <description>&lt;p&gt;You should spend some time in &lt;em&gt;immutable Linux land&lt;/em&gt;, you might like it. When
starting my PhD I got handed a laptop and told to set it up. At the time I chose
&lt;a href=&quot;https://aeondesktop.github.io/&quot;&gt;OpenSUSE Aeon&lt;/a&gt; as I was familiar with
Tumbleweed. This was a fantastic choice and for the kind of work I do and I
don’t regret it. Removing the burden of system administration is a value I can
get behind and I think it really paid off.&lt;/p&gt;

&lt;p&gt;Each day I get a notification to reboot to update. The system has been updated
automatically and it is waiting for me. Each day I ignore it and I know that
tomorrow I will boot into the updated system and it will just work. Nothing is lost or non functional. Ever.&lt;/p&gt;

&lt;p&gt;Getting software was extremely easy because Flatpak is integrated within the
software portal. Day to day life is uneventful as everything I really need I
have. The apps I need (Emacs, Firefox, Zotero) are packaged and automatically updated. I can
even use my wonky extensions setup without too much hassle.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://distrobox.it/&quot;&gt;Distrobox&lt;/a&gt; has been my workhorse, mostly unnoticed which
is good, and the only time I had to install something on the host system was to get
virtualization to work. I could figure it out before the deadlines for artifact
review went by so another win.&lt;/p&gt;

&lt;p&gt;Other apps I got are &lt;a href=&quot;https://flathub.org/en/apps/com.github.tchx84.Flatseal&quot;&gt;Flatseal&lt;/a&gt;
and &lt;a href=&quot;https://flathub.org/en/apps/io.github.flattool.Warehouse&quot;&gt;Warehouse&lt;/a&gt;
because truly Flatpak is a bit too much to handle sometimes.
If I didn’t care to have a dead-simple, reproducible, can’t-break-it setup I
would have used distrobox’s &lt;a href=&quot;https://github.com/89luca89/distrobox/blob/main/docs/usage/distrobox-export.md&quot;&gt;export
feature&lt;/a&gt;.
Anything installed in a container managed by distrobox is also available from
the host launcher. Truly magical.&lt;/p&gt;

&lt;p&gt;But I did care about dead-simple, reproducible, can’t-break-it setup so I have packaged
even &lt;a href=&quot;https://github.com/edoput/de.tum.in.Isabelle&quot;&gt;more&lt;/a&gt; &lt;a href=&quot;https://github.com/edoput/io.github.visualvm&quot;&gt;apps&lt;/a&gt;
and I am happy with the result. This is the first time that I have a disaster
recovery plan for &lt;em&gt;my system and my data&lt;/em&gt; and I am confident that it will work.&lt;/p&gt;

&lt;p&gt;The most important endorsement I can give to this &lt;em&gt;way of life&lt;/em&gt; was reading &lt;a href=&quot;https://lwn.net/Articles/977987/&quot;&gt;Aeon: openSUSE for the lazy developers&lt;/a&gt; when it came out in June 2024 and thinking “I have used a release-candidate all this time without an issue. I didn’t even notice it .”.&lt;/p&gt;
</description>
        <pubDate>Tue, 11 Nov 2025 00:00:00 +0100</pubDate>
        <link>http://edoput.it/2025/11/11/immutable-linux.html</link>
        <guid isPermaLink="true">http://edoput.it/2025/11/11/immutable-linux.html</guid>
        
        
      </item>
    
      <item>
        <title>Isabelle/ML starting tips</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://isabelle.in.tum.de/&quot;&gt;Isabelle/HOL&lt;/a&gt; is an interative theorem prover. To be extremely precise
Isabelle is a distribution of a the theorem prover and HOL is an object logic embedded in the ambient logic.
If this is confusing don’t sweat about it, it’s only to make the next distinction a little clearer.&lt;/p&gt;

&lt;p&gt;I have recently started a project using Isabelle/ML, the part of the Isabelle distribution where
you write Standard ML code to extend what Isabelle can do on the logic side. These are small tips
that made me more productive because the experience of using Isabelle/ML is really subpar to using
Isabelle and Isabelle/HOL.&lt;/p&gt;

&lt;h2 id=&quot;cant-click-x&quot;&gt;Can’t click X&lt;/h2&gt;

&lt;p&gt;Isabelle has a neat feature where you can &lt;kbd&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;-&lt;kbd&gt;Click &lt;/kbd&gt;&lt;/kbd&gt;
anything and it will bring you to where it is defined. This does not
work as well in Isabelle/ML, especially with the output buffer. This
breaks the flows a lot because I cannot find my way to a type or a
module fast.&lt;/p&gt;

&lt;p&gt;Let’s see an example where we want to see the type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object_Logic.atomize&lt;/code&gt;.
In my theory file I write the following.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_val &amp;lt;
  Object_Logic.atomize
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the output buffer I can then read the following but I cannot click anything.
I can copy this signature to the previous block and click it though.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;val it = fn : Proof.context -&amp;gt; conv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In general though if you know where you want to go there are two ways to get there
fast. For types it is easy to define a newtype and the right-hand-side will be clickable,
for modules it is always possible to open them. In the following snippet both
Proof.context and Proof are targets that you can &lt;kbd&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;-&lt;kbd&gt;Click &lt;/kbd&gt;&lt;/kbd&gt; and
you will land where they are defined.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_val &amp;lt;
  type A = Proof.context;
  open Proof;
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;syntax-and-type-errors&quot;&gt;Syntax and type errors&lt;/h2&gt;

&lt;p&gt;Everybody starts with the &lt;em&gt;embedded ML&lt;/em&gt; because it is easy. The errors though
could be better. The syntax and type errors may mix though making it hard to
understand what is the problem and how do you fix it.&lt;/p&gt;

&lt;p&gt;As always the way out is kind of boring and obvious when you think about it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Always start with a complete ML expression which produces no syntax error
and then start adding stuff.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following snippet is an example of many things that could go
wrong. Am I getting an error because there is a type mismatch or
because the term is not parsable? I cannot tell.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_val &amp;lt;
let
  val an_id : a_type_signature = an_expression
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Small incremental steps instead bring you there. First a parsable term&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_ &amp;lt;
let
in
  ()
end
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then a name and type for your binding.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_ &amp;lt;
let
  val a_name : a_type = error &quot;TODO&quot;
in
  ()
end
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then a body for your binding.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_ &amp;lt;
let
  val a_name : a_type = error &quot;TODO&quot;
in
  body
end
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then a value&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ML_ &amp;lt;
let
  val a_name : a_type = a_value
in
  body
end
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each small step is either a syntax error or a type error and you can always tell.&lt;/p&gt;

&lt;h2 id=&quot;error-is-surprisingly-useful&quot;&gt;Error is surprisingly useful&lt;/h2&gt;

&lt;p&gt;Just like you would use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; in your inner syntax to have a hole you can fill in
later I think &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error&lt;/code&gt; is a fantastic hole for your ML sources.&lt;/p&gt;

&lt;h2 id=&quot;finding-other-stuff&quot;&gt;Finding other stuff&lt;/h2&gt;

&lt;p&gt;We do not have access to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_theorems&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_consts&lt;/code&gt; so the way forward is
textual seach. Lucky us that this is already available.&lt;/p&gt;

&lt;h3 id=&quot;where-to-find-stuff&quot;&gt;Where to find stuff&lt;/h3&gt;

&lt;h4 id=&quot;we-have-stuff-already&quot;&gt;We have stuff already&lt;/h4&gt;

&lt;p&gt;The Isabelle distribution comes with a bunch of stuff already. You most likely know
that Isabelle/HOL is defined in the HOL directory of the Isabelle distribution.
There are other developments there such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FOL&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LCF&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pure&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I like to interactively explore this root directory using the builtin search.&lt;/p&gt;

&lt;p&gt;I always start with filtering out the ML files because I am looking for examples
of how to use the outer syntax. If I am looking for examples of Isabelle/ML I also
start with the theory files because it’s likely that I am reinventing the wheel.&lt;/p&gt;

&lt;p&gt;Then when I’m done with theory files I start searching through the ML sources.&lt;/p&gt;

&lt;h4 id=&quot;the-archive-of-formal-proofs&quot;&gt;The Archive of Formal Proofs&lt;/h4&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.isa-afp.org/&quot;&gt;AFP&lt;/a&gt; is another good source of examples you should comb.
Instead of looking at the web frontend you should download it and use the builtin
search as described before.&lt;/p&gt;
</description>
        <pubDate>Sun, 02 Nov 2025 00:00:00 +0100</pubDate>
        <link>http://edoput.it/2025/11/02/isabelle-ml-starting-tips.html</link>
        <guid isPermaLink="true">http://edoput.it/2025/11/02/isabelle-ml-starting-tips.html</guid>
        
        
      </item>
    
      <item>
        <title>Starred forms</title>
        <description>&lt;p&gt;Bob complains that &lt;a href=&quot;https://bobrubbens.nl/post/starred-latex-commands-suck/&quot;&gt;starred commands in LaTeX can have a better
user-experience&lt;/a&gt;. This
actually sent me &lt;a href=&quot;https://stackoverflow.com/questions/67192322/scheme-names-with-as-suffix&quot;&gt;back in time&lt;/a&gt;
to younger edoput trying to understand why some scheme functions are suffixed
with ‘*’, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;append*&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two instances don’t make a pattern but I’m wondering if there’s a third
programming language where * is also used in identifiers with the same
intent, “like this other thing, but a little bit different”.
Then the next question would be did LaTeX steal this from scheme or vice-versa?
Also is this a LaTeXism or is this already in TeX?&lt;/p&gt;
</description>
        <pubDate>Sat, 25 Oct 2025 00:00:00 +0200</pubDate>
        <link>http://edoput.it/2025/10/25/starred-forms.html</link>
        <guid isPermaLink="true">http://edoput.it/2025/10/25/starred-forms.html</guid>
        
        <category>scheme</category>
        
        
      </item>
    
      <item>
        <title>Better Koreader plugin debugging</title>
        <description>&lt;p&gt;There was one huge pain point during the &lt;a href=&quot;/2024/07/28/koreader-gotobed.html&quot;&gt;development&lt;/a&gt; of &lt;a href=&quot;https://github.com/edoput/gotobed.koplugin&quot;&gt;gotobed&lt;/a&gt;:
I would put in bugs without knowing and things would go bad. A plugin is effectively
a smaller application that is embedded into Koreader but Koreader did very little to point
out what went wrong in case something goes wrong.&lt;/p&gt;

&lt;p&gt;This behavior is actually implemented by the following piece of code. At plugin load time
the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sandboxPluginEventHandlers&lt;/code&gt; would be called with the plugin table.
This table is inspected for key,value pairs that match an event handler: the key
matches the prefix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;on&quot;&lt;/code&gt; and the value is of type function. For each pair that
matches, the event handler is wrapped into an anonymous function that executes
a protected call. Errors are logged as is without further processing.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;c1&quot;&gt;-- koreader/frontend/pluginloader.lua&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sandboxPluginEventHandlers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;function&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pcall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;failed to call event handler&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This implementation works but does not make me, the plugin developer, happy.
Let’s break it down.&lt;/p&gt;

&lt;p&gt;This is the behavior of pcall.&lt;/p&gt;

&lt;blockquote cite=&quot;https://www.lua.org/pil/8.4.html&quot;&gt;
&lt;p&gt;If there are no errors, pcall returns true, plus any values returned by the call. Otherwise, it returns false, plus the error message.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://www.lua.org/pil/8.4.html&quot;&gt;Programming in Lua - Error Handling and Exceptions&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moreover, anything can be an error but it’s most likely an error message. If it’s an internal
error, Lua will generate the error message. As an example consider the following errors.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;n&quot;&gt;Lua&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;Copyright&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2012&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lua&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PUC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rio&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unexpected&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;near&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;1&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;I am a naughty bug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;am&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;naughty&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bug&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;
	stdin:1: in main chunk
	[C]: ?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The first error message is quite simple but the second is much more detailed:
there is a stack-trace that tells me what happened and where, meaning I can
actually debug this.&lt;/p&gt;

&lt;p&gt;If I could get a description for the first error that is as detailed as the second one
I would be happy. Internal errors have been the majority during my delelopment, which
means that having good stack-traces would improve much my experience. Moreover a
short &lt;a href=&quot;https://github.com/search?q=repo%3Akoreader%2Fkoreader+path%3A%2F%5Eplugins%5C%2F%2F++error%28&amp;amp;type=code&quot;&gt;investigation&lt;/a&gt;
reveals that, at the time of writing, there are only 6 instances of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error&lt;/code&gt;
calls in the plugin shipped with Koreader. This means that they are on the same
boat and would benefit from stack-traces on internal errors.&lt;/p&gt;

&lt;p&gt;Because the handlers in the plugin are only invocked in a protected call, errors
raised in the plugin will not bubble up to Koreader. Our problem is the following:&lt;/p&gt;

&lt;blockquote cite=&quot;https://www.lua.org/pil/8.5.html&quot;&gt;
&lt;p&gt;
Frequently, when an error happens, we want more debug information than only the location where the error occurred. At least, we want a traceback, showing the complete stack of calls leading to the error. When pcall returns its error message, it destroys part of the stack (the part that went from it to the error point). Consequently, if we want a traceback, we must build it before pcall returns. 
&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://www.lua.org/pil/8.5.html&quot;&gt;Programming in Lua - Error Messages and Tracebacks&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;So either I, the plugin developer, build the stack-trace and stuff it in the
error message myself or there is no stack-trace. Lucky me that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error&lt;/code&gt; does this
implicitly. Unlucky me that internal Lua errors do not do so.&lt;/p&gt;

&lt;p&gt;Turns out that if we ask nicely Lua will not throw away the stack information.&lt;/p&gt;

&lt;blockquote cite=&quot;https://www.lua.org/pil/8.5.html&quot;&gt;
&lt;p&gt;
To do that, Lua provides the xpcall function. Besides the function to be called, it receives a second argument, an error handler function. In case of errors, Lua calls that error handler before the stack unwinds, so that it can use the debug library to gather any extra information it wants about the error.
&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://www.lua.org/pil/8.5.html&quot;&gt;Programming in Lua - Error Messages and Tracebacks&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;So that’s what I implemented recently in &lt;a href=&quot;https://github.com/koreader/koreader/pull/13677&quot;&gt;plugin handlers tracebacks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have a better debugging experience!&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Oct 2025 00:00:00 +0200</pubDate>
        <link>http://edoput.it/2025/10/07/better-koreader-debugging.html</link>
        <guid isPermaLink="true">http://edoput.it/2025/10/07/better-koreader-debugging.html</guid>
        
        <category>koreader</category>
        
        
      </item>
    
      <item>
        <title>Minibuffer and input method</title>
        <description>&lt;p&gt;I really like typesetting with unicode math symbols. I use them in my notes, sometimes even in LaTeX as we get the unicode-math package.
To input unicode symbols in Emacs I use the unicode-math package and before that my own-grown input method. The input method is bound
to a buffer so when you switch (intentionally or not) to another buffer typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\alpha&lt;/code&gt; will not result in a pretty α being rendered.
I can live with this. Except when switching to the minibuffer!&lt;/p&gt;

&lt;p&gt;The minibuffer better be aware of my current input method! So be it!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;minibuffer-input-method-initialize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Initialize input method from `minibuffer-selected-window&apos;.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;win&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;minibuffer-selected-window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;win&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;window-buffer&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;win&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buf&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;activate-input-method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;buffer-local-value&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;current-input-method&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;use-package&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;emacs&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:hook&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;minibuffer-setup-hook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;minibuffer-input-method-initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sun, 31 Aug 2025 00:00:00 +0200</pubDate>
        <link>http://edoput.it/2025/08/31/minibuffer-input-method.html</link>
        <guid isPermaLink="true">http://edoput.it/2025/08/31/minibuffer-input-method.html</guid>
        
        <category>emacs</category>
        
        
      </item>
    
      <item>
        <title>Emacs: a paradigm shift</title>
        <description>&lt;p&gt;Recently I read &lt;a href=&quot;https://blog.tjll.net/a-beginners-guide-to-extending-emacs/&quot;&gt;this beginners guide to extend Emacs&lt;/a&gt;.
The guide is perfect for starting out with elisp and it shows a lot of care in teaching how to interact with Emacs.&lt;/p&gt;

&lt;p&gt;To me, the most important bit though is this one, from the section aptly named &lt;strong&gt;Emacs Wants You to Extend It&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I haven’t written plugins for other editors extensively, but I can tell you this: emacs doesn’t just make deep customization available, but it actively encourages you to make an absolute customization messes masterpieces. Core editor functions aren’t just documented, but often include tidbits about “you probably want to see this other variable” or “here’s how you should use this”.&lt;/p&gt;

  &lt;p&gt;Not only that, but emacs happily hands you functions shaped like nuclear warheads like advice-add (that let you override any function) that can absolutely obliterate your editor if you hold it the wrong way. Of course, this also grants you unlimited power.&lt;/p&gt;

  &lt;p&gt;Remember that emacs is designed to be torn apart and rearranged.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the core bit of the argument. Emacs, as a system, wants you to
extend it and it gives you all the means to do so. This is in contrast
with systems that can be extended through scripting and instead don’t give
you all the means to do so!&lt;/p&gt;

&lt;p&gt;I think the tutorial is a fantastic example of &lt;em&gt;doing things
right&lt;/em&gt;. There is a well-thought example, a constructive approach where
the solution grows to a full package.&lt;/p&gt;

&lt;p&gt;This is problematic. You may get the impression that extending Emacs
is only possible if you do things right and that is definitely not true.&lt;/p&gt;

&lt;p&gt;To make my point I want to walk you through an example. I will show
you how I used standard Emacs extension-points to extend org-mode to sort my
reading lists automatically.&lt;/p&gt;

&lt;h2 id=&quot;what-do-i-want&quot;&gt;What do I want?&lt;/h2&gt;

&lt;p&gt;The behavior I want is that when I save an org file the entries are
ordered automatically. I keep a timeline of the papers I am reading
and it is annoying to keep them kind of ordered.&lt;/p&gt;

&lt;p&gt;This is the content of an example buffer.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-org&quot; data-lang=&quot;org&quot;&gt;#+TITLE: My thematic reading list

* Paper which is old but not too old
:PROPERTY:
:year: 2002
:END:

* Definitely older paper but unfortunately it&apos;s later in the list
:PROPERTY:
:year: 1998
:END:&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When I add a paper to my reading list I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-entries&lt;/code&gt; and
interactively select to order the entries by the value in the property
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;year&lt;/code&gt;. Initally this was nice to have but now it’s just annoying that
I have to keep doing it. Let’s extend org-mode so that this is done automatically.&lt;/p&gt;

&lt;h2 id=&quot;a-simple-solution&quot;&gt;A simple solution&lt;/h2&gt;

&lt;p&gt;The first step is to automate the interactive part. Lucky for me this is easy
as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-entries&lt;/code&gt; is both a function and a command. I can call it in a
script just as I can run it as a command.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defvar&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;year&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-run&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;derived-mode-p&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;case-sensitive&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sorting-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;getkey-func&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compare-func&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive?&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-sort-entries&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;case-sensitive&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sorting-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;getkey-func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;compare-func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactive?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This solves one part of the problem. Let’s solve the other one, automatically calling
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-run&lt;/code&gt; whenever an org-mode buffer is saved.&lt;/p&gt;

&lt;p&gt;Emacs already has support for this use-case through the use of hooks. We can run
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-run&lt;/code&gt; all the times we want to save a buffer.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;before-save-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-sort-run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;These two together solve the problem but the solution presented is “just more code”.
We tapped into the &lt;em&gt;hook&lt;/em&gt; extension point but this would be possible in any
&lt;em&gt;scriptable system&lt;/em&gt; that exposes well-defined extension points such as hooks and commands.&lt;/p&gt;

&lt;h2 id=&quot;leveraging-emacs-extensibility-to-extend-org-mode&quot;&gt;Leveraging Emacs’ extensibility to extend org-mode&lt;/h2&gt;

&lt;p&gt;I want to show that even if something is not thought with
extensibility in mind Emacs allow us to extend it. Most importantly, while we
want to extend org-mode’s behavior we would like this not to be an
extension to org-mode’s code.&lt;/p&gt;

&lt;p&gt;Here’s the updated problem statement. Have the buffer be automatically
sorted and have the sorting criteria be in the buffer itself. We will
specify the sorting as a &lt;em&gt;in-buffer setting&lt;/em&gt; and use Emacs to
support this never thought before org-mode behavior.&lt;/p&gt;

&lt;p&gt;Our example buffer changes to the following.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt; #+TITLE: My thematic reading list
&lt;span class=&quot;gi&quot;&gt;+#+SORT: year
&lt;/span&gt; 
 * Paper which is old but not too old
 :PROPERTY:
 :year: 2002
 :END:
 
 * Definitely older paper but unfortunately it&apos;s later in the list
 :PROPERTY:
 :year: 1998
 :END:&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;aside&gt;
Do follow along issuing the commands that I leave in the text. I am an
Emacs user, not an expert, and I want to show you how to achieve this
so you can judge for yourself if it&apos;s something you would be able to
do.
&lt;/aside&gt;

&lt;p&gt;The hard part of this is to find how org-mode reads &lt;em&gt;&lt;a href=&quot;https://orgmode.org/manual/In_002dbuffer-Settings.html&quot;&gt;in-buffer
settings&lt;/a&gt;&lt;/em&gt;
from the header. A &lt;kbd&gt;M-x find-library&lt;/kbd&gt; later we are in org’s
sources.&lt;/p&gt;

&lt;p&gt;Searching for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+STARTUP&lt;/code&gt; (&lt;kbd&gt;Ctrl+s +STARTUP&lt;/kbd&gt;), one of the
supported settings, leads us to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-startup-folded&lt;/code&gt; and that in turn
(&lt;kbd&gt;Ctrl+s org-startup-folded&lt;/kbd&gt;) leads us to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-startup-options&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-startup-options&lt;/code&gt; is then used by (again &lt;kbd&gt;Ctrl+s org-startup-option&lt;/kbd&gt;)
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-set-regexps-and-options&lt;/code&gt;.&lt;/p&gt;

&lt;aside&gt;
&lt;kbd&gt;M-x find-function org-set-regexps-and-options&lt;/kbd&gt; for those that have not
followed along.
&lt;/aside&gt;

&lt;p&gt;While the documentation for this function is not very convincing, its code
does make sense for what we are after. I copied it here for reference.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;derived-mode-p&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;alist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-collect-keyword&lt;/span&gt;
		  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FILETAGS&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;TAGS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tags-only&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			       &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ARCHIVE&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CATEGORY&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;COLUMNS&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CONSTANTS&quot;&lt;/span&gt;
				 &lt;span class=&quot;s&quot;&gt;&quot;LINK&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;OPTIONS&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;PRIORITIES&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;PROPERTY&quot;&lt;/span&gt;
				 &lt;span class=&quot;s&quot;&gt;&quot;SEQ_TODO&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;STARTUP&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;TODO&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;TYP_TODO&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
		  &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ARCHIVE&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;CATEGORY&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;COLUMNS&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;PRIORITIES&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;;; Startup options.  Get this early since it does change&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;;; behavior for other options (e.g., tags).&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;startup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cl-mapcan&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;split-string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
				&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;STARTUP&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
	     &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Unfortunately this function calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-collect-keyword&lt;/code&gt; with a list that we cannot
touch. There is no custom variable to set to pass our own keyword.&lt;/p&gt;

&lt;p&gt;If this was a “normal programming environment” we would make our changes
to this function body and forever maintain a fork of org-mode. As this
is elisp instead we have choices.&lt;/p&gt;

&lt;p&gt;I think the best choice is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;advice-add&lt;/code&gt; and have Emacs call our
advice code every time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-set-regexps-and-options&lt;/code&gt; is called. We will copy
what we need from the function body but that will be all.&lt;/p&gt;

&lt;p&gt;This is what I ended up with.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defvar&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-set-option&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;rest&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Read the +SORT: spec value into variable `org-sort-option&apos;.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;derived-mode-p&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;alist&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-collect-keywords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SORT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cdr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SORT&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;alist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sort-spec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read-from-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;setq-local&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sort-spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;advice-add&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-set-regexps-and-options&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:after&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-sort-set-option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-run&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;derived-mode-p&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;org-mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;case-sensitive&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sorting-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;getkey-func&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compare-func&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;org-sort-option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive?&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-sort-entries&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;case-sensitive&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sorting-type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;getkey-func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;compare-func&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;interactive?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&apos;before-save-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#&apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;org-sort-run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We keep a buffer-local variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-option&lt;/code&gt; around to store the
property name read from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#+SORT: property-name&lt;/code&gt;. This variable is initially
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; and will be set from the property name in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#+SORT: property-name&lt;/code&gt;. To do so
we have a function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-set-option&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But when to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-set-option&lt;/code&gt;? The easy way out is to have Emacs call it whenever
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-set-regexps-and-options&lt;/code&gt; is called on a file visit. To achieve this we
tap into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;advice-add&lt;/code&gt; and ask Emacs to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-set-option&lt;/code&gt; after
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;org-sort-regexps-and-options&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have now succesfully interposed ourselves in the control flow of the org-mode library.&lt;/p&gt;

&lt;p&gt;Org-mode did not provide any interposition point for us, there
is no thought ahead extension-point or configuration variable we can
use to achieve our goal an yet here we are with a sorted buffer.&lt;/p&gt;

&lt;p&gt;We succeeded in our effort because &lt;em&gt;Emacs wants you to extend it&lt;/em&gt;
and it gives you all the means to do so.&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;I have made a horrible hack and it works. I have learnt nothing about
how org-mode works or Emacs’ file-visiting extension-points.&lt;/p&gt;
</description>
        <pubDate>Wed, 16 Apr 2025 00:00:00 +0200</pubDate>
        <link>http://edoput.it/2025/04/16/emacs-paradigm-shift.html</link>
        <guid isPermaLink="true">http://edoput.it/2025/04/16/emacs-paradigm-shift.html</guid>
        
        <category>emacs</category>
        
        
      </item>
    
      <item>
        <title>RE: your application environment variables</title>
        <description>&lt;p&gt;Loading values from the environment into variables is a way to
configure processes at startup. Alternatives are to define a
command-line interface or parsing configuration files.&lt;/p&gt;

&lt;p&gt;Each of these methods has its advantage but loading values
from the environment has a shortcoming. As an operator you have
to refer to the documentation of the tool as there is no
interface to query what the process expects.&lt;/p&gt;

&lt;p&gt;This is possible when using a command line interface
as libraries have implemented a default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-h/--help&lt;/code&gt; flag
that will print what is expected. It is also possible to
use a format for configuration files that supports comments
to document the values supported.&lt;/p&gt;

&lt;p&gt;I have published another environment variable parser package that
fills this void as follows.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It is possible to define program variables that will hold the
associated environment values.&lt;/li&gt;
  &lt;li&gt;It is possible to define different sets of environment variables in
the same application. This allows subcommands to have their own
environment variables.&lt;/li&gt;
  &lt;li&gt;It is possible to get a description of the set of environment
variables that includes the name, type, default value, and a
short description.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have adapted the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flag&lt;/code&gt; package for this purpose and it is available
&lt;a href=&quot;https://github.com/edoput/env&quot;&gt;here&lt;/a&gt;. I would like to thank the Go
authors for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flag&lt;/code&gt; package’s design. It was fairly easy to adapt
it and all the errors you may find are my own.&lt;/p&gt;

&lt;p&gt;This is a minimal example to see how the package can be used to
load values from the environment in program variables and get
a description of the variables the program expects.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;github.com/edoput/env&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BLEP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Should we blep?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// do gophers blep?&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the corresponding usage as an operator.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;main &lt;span class=&quot;c&quot;&gt;# no output&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ HELP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; main &lt;span class=&quot;c&quot;&gt;# setting HELP or H will trigger the usage message&lt;/span&gt;
Environment of main:
  BLEP  boolean
    	Should we blep?&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Is this good? Is this bad? I’m not sure but I’m not going back.&lt;/p&gt;

&lt;aside&gt;
If you are an author of a more popular environment variable package please
steal this approach and make my world better.
&lt;/aside&gt;

&lt;h2 id=&quot;previous-art-in-environment-loading&quot;&gt;Previous art in environment loading&lt;/h2&gt;

&lt;p&gt;There are many packages dedicated to parsing values from environment
variables. Here is an incomplete list along with their pros.&lt;/p&gt;

&lt;p&gt;Most of these packages focus on loading environment variables into one
config structure as a program variable. All provide a procedural API
and almost all support some sort of reflection driven by struct tags.
One of these packages introspect on the environment variables the
program declares to do something meaningful.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://pkg.go.dev/github.com/kelseyhightower/envconfig&quot;&gt;kelseyhightowers/envconfig&lt;/a&gt;&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Package envconfig implements decoding of environment variables based on a user defined specification.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This package is by far the most popular I can find. It supports
loading values into a structure, struct tags to make that easy,
and custom decoders. There are many other reimplementations but none
has the same reach.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://pkg.go.dev/github.com/olivere/env&quot;&gt;olivere/env&lt;/a&gt;&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;The env package is to simplify reading environment variables with e.g. the flag package.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This package actually implements a similar API but focuses on using environment
variables as fallbacks for flag variables.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://pkg.go.dev/code.cloudfoundry.org/go-envstruct&quot;&gt;code.cloudfoundry.org/go-envstruct&lt;/a&gt;&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;envstruct is a simple library for populating values on structs from environment variables.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This package is the first that implements the missing feature. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteReport&lt;/code&gt;
function can be used to give a description of the environment variables the
program defines but is not wired up by default.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 18 Nov 2024 00:00:00 +0100</pubDate>
        <link>http://edoput.it/2024/11/18/your-application-environment-variables.html</link>
        <guid isPermaLink="true">http://edoput.it/2024/11/18/your-application-environment-variables.html</guid>
        
        
      </item>
    
      <item>
        <title>Flatpak: how to keep your development tools at runtime</title>
        <description>&lt;p&gt;Running &lt;a href=&quot;https://microos.opensuse.org/&quot;&gt;MicroOS&lt;/a&gt; as my daily on the work laptop
is quite the experience. Nothing ever breaks but at the same time there is very
little that can. This is because the apps I need are not packaged. Sad.&lt;/p&gt;

&lt;p&gt;Over time I have packaged what I need but one of them took some time.
&lt;a href=&quot;https://visualvm.github.io/&quot;&gt;VisualVM&lt;/a&gt; is a GUI to troubleshoot your Java
processes, as it requires the Java SDK and Java SRE at runtime I was not sure
how to package it.&lt;/p&gt;

&lt;p&gt;Development tools and libraries are available at build time but applications
are not shipped with them. Turns out that it was easy but almost undocumented so
here we are. I will give bits and pieces of the manifest I made and explain what
parts where necessary to have access to the Java SDK and Java JRE at runtime so
that others may do the same.&lt;/p&gt;

&lt;p&gt;The Flatpak build process is isolated in a sandbox and this means that we have
access to almost nothing. What we need to build the app must be brough into the
sandbox. This is where the sdk image comes in. The sdk image is a file-system
with lots of development tools that can be used by flatpak at build times.
The problem for us is that it does not have /all the tools/.&lt;/p&gt;

&lt;p&gt;The sdk image does not provide a Java SDK and Java JRE so you cannot build your Java
applications as is. The sanctioned way to do build a Java app is to use a sdk
image extension. Following is the incantation to use the openjdk extension.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;sdk-extensions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;org.freedesktop.Sdk.Extension.openjdk&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The extension provides the (Java) tools and libraries we need at build time but
we also want them available at runtime. As this is a sensible use-case the publishers of
the sdk extension have included a script to install everything in the final
artifact that we produce. Following is the incanation to do so.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;install-openjdk&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;buildsystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;simple&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build-commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/lib/sdk/openjdk/install.sh&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;javac&lt;/code&gt; will be available both at build time and runtime.
Everything we need will be copied in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/app&lt;/code&gt; directory. If the packaged
app requires the tools we can point it there and everything will work as
expected.&lt;/p&gt;

&lt;p&gt;Following is the manifest that I have used to package VisualVM.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;io.github.visualvm&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2.1.10&apos;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;org.freedesktop.Platform&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;runtime-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;24.08&apos;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;sdk&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;org.freedesktop.Sdk&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;sdk-extensions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;org.freedesktop.Sdk.Extension.openjdk&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;finish-args&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# setup JRE and JDK path&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--env=PATH=/app/jre/bin:/usr/bin&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# X11 + XShm access&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--share=ipc&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--socket=x11&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# GPU acceleration if needed&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--device=dri&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Needs to talk to the network:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--share=network&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--persist=.visualvm&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/app/visualvm/bin/visualvm&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;install-openjdk&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;buildsystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;simple&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build-commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/lib/sdk/openjdk/install.sh&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;install-visualvm&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;archive&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://github.com/oracle/visualvm/releases/download/2.1.10/visualvm_2110.zip&apos;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;archive-type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;zip&apos;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;sha256&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;87850af9e9873c690cbb76056627e73dfbfd1e1d95279a9031722a85ac36d999&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;buildsystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;simple&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build-commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mkdir -p ${FLATPAK_DEST}/visualvm&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cp -ar * ${FLATPAK_DEST}/visualvm&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build-options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# debugedit craps otherwise&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;install-desktop-app&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;file&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;io.github.visualvm.desktop&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;buildsystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;simple&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build-commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;install -D --target-directory=${FLATPAK_DEST}/share/applications -m444 ${FLATPAK_ID}.desktop&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Tue, 01 Oct 2024 00:00:00 +0200</pubDate>
        <link>http://edoput.it/2024/10/01/flatpak-runtime-sdk.html</link>
        <guid isPermaLink="true">http://edoput.it/2024/10/01/flatpak-runtime-sdk.html</guid>
        
        
      </item>
    
      <item>
        <title>Redux: Running zotero in background</title>
        <description>&lt;p&gt;For the last years I have been runnning &lt;a href=&quot;https://www.zotero.org/&quot;&gt;Zotero&lt;/a&gt; headlessly. It
starts as a user service when I log in and provides the same features except
for the GUI. Other programs, e.g. browser, word processor, can interact with
it without fault.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;/2021/07/22/background-zotero.html&quot;&gt;previous setup&lt;/a&gt; I
was managing Zotero but today I need to use Flatpak. So let’s update this
setup and have it work as well.&lt;/p&gt;

&lt;p&gt;The major change is that we do not interact with the Zotero process directly
but with wrapper processes, i.e. the bubblewrap sandboxing.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;c&quot;&gt;# ~/.config/systemd/user/zotero.service
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;[Unit]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Run zotero in background&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;[Service]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# flatpak manages the lifecycle of zotero now, got to delegate
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;flatpak run org.zotero.Zotero --headless&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;ExecStop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;flatpak kill org.zotero.Zotero&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;on-failure&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;RestartSec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;30s&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;[Install]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# actually starts this service when I log in
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;default.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Mon, 23 Sep 2024 00:00:00 +0200</pubDate>
        <link>http://edoput.it/2024/09/23/background-zotero-redux.html</link>
        <guid isPermaLink="true">http://edoput.it/2024/09/23/background-zotero-redux.html</guid>
        
        <category>systemd</category>
        
        
      </item>
    
  </channel>
</rss>
