The blog is going through a rewrite of its generator code
Created:
· Updated:
In short:
Longer version: this blog was previously generated with a custom Python-based generator. I wrote it as an excercise - at the time, I was fascinated with functional programming, and I used the blog generator to play with functional features in Python, leveraging mainly funcy library. The code turned out weird, but it worked, and I used it for years. However, the whole workflow was optimized for publishing loooong and complex blog posts, and for every post, setup time was substantial.
I realized that I need to eliminate as many barriers to publishing posts as possible if I ever want to start publishing shorter content more often. I didn't care for a long time, but recently, finding time to finish those looong and complex posts became challenging. So, I decided to take a stab at integrating the generator into my normal workflow. In other words, I decided to rewrite the whole thing in Emacs Lisp.
I researched a bunch of existing solutions, the ones that appeared maintained:
But none of them worked for me out of the box. Plus, my previous custom code did a bunch of smart things that I'm not yet sure how to replicate in Org, and of course none of the packages provide them.
I took inspiration from weblorg, mostly, and in under 100 loc I managed to write something that works just enough to post this information. It will take another weekend or two to bring back all the features I want, and then some more time to convert old posts, but it's going to happen.
Just FYI, if you wondered where the content disappeared to: it will be back!
Replicating the best feature of Sunrise Commander in plain Dired
Created:
Some time ago, Sunrise Commander - a two-pane file manager for Emacs, built on
top of Dired by Drew Adams - stopped working for me. The loss of familiar
bindings was a little painful, but I mostly solved it with Hydra - the
functionality is mostly still there in Dired.
One thing I missed was swithcing to the other Dired window easily. With just two
Dired windows occupying a whole frame just M-x other-window worked well, but
with more windows, I had to fall back to M-x windmove-* and that was less
convenient1.
I decided to fix it at some point, and here's the result:
1: (defunmy-select-other-dired-window() 2: "Select the other dired window, if there is only one." 3: (interactive) 4: (let*((selected-window(selected-window)) 5: (other-windows(cdr (window-list)))) 6: (cl-loopforwin other-windows
7: if(with-current-buffer(window-buffer w) 8: (derived-mode-p 'dired-mode)) 9: collect w intoresult10: finally11: (when(= 1 (length result))12: (select-window (car result))))))13: 14: (keymap-set dired-mode-map "<tab>" #'my-select-other-dired-window)
Now I can select the other Dired buffer by pressing <TAB> in one of them, no
matter how many other windows are there.
The person who originally wrote this code must have been a little strange in the
head, which isn't that unusual in this industry. The reviewer of this code who
agreed to merge it - if they even existed - should probably reflect on how
serious they are about doing their job, but well - it happens to the best of us.
A single fuckup like this is nothing too unusual.
The real tragedy starts after that, though. Code is read much more often than it
is written, so it had to be read by other programmers in the time it existed.
Especially since many different programmers worked on a project, most of them
only for a short time. Now, not one of these people took it upon themselves to
eradicate this monstrosity, even though it would be 5 minutes of work. Here's
how could it have looked like:
I'll start by appealing to authority (because I'm just as lazy as those that
will, no doubt, persecute me with Uncle Bob quotes):
Sometimes, the elegant implementation is just a function. Not a method. Not a
class. Not a framework. Just a function. – John Carmack
I happen to agree, and it seemed like it was the case in this particular
instance, so that's the first change to the code.
Switching to the block body was obvious, since that's the only way to declare
vals in direct function scope, and it looked like giving a name to a
subexpression could be helpful.
Flipping the condition not only highlighted the exact conditions (explicitly) in
which the function does something special, but also allowed the use of just an
if statement and dropping the use of else block. It's now clear that,
no matter what happens, this function will return a File object - there's only
one return statement, and it's explicit, making it really hard to miss.
Simultaneously using more than one Closeable instance is a pretty
common thing to do, and deserves its own helper function. You can find using
implementation on Github - it's boring, schematic code, so writing it yourself
wouldn't be too fun. It's a single file liberally licensed utility that you can
just copy and paste to your utils module.
Finally, since stringly-typed code is an abomination that should be eradicated
from the face of the Earth, we return the File object itself, rather then just
the path to it. The calling code knows better in what form it wants the file to
be: if it's for printing, file path is OK, but if you wanted to read it, you'd
have to create a new File object based on the returned path. It's always
better to leave the representation decisions to the caller, and it pays off to
return the most universal representation you have access to.