I’m old enough to remember Norton Commander for DOS. Despite that, I never used Midnight Commander nor Sunrise Commander – Dired is still my go-to file manager these days. In fact, Dired has a feature which seems to be inspired by NC: when there are two Dired windows, the default destination for copying, moving and symlinking is “the other” window.
Surprisingly, another feature which would be natural in an orthodox file manager is absent from Dired: TAB is not bound by its keymap. This means that pressing it results in a “Buffer is read-only” error. I figured that making TAB switch to the other Dired window would be pretty cool – and definitely more useful than just erroring out.
(defun dired-cycle-dired-windows ()
"Switch to the next Dired window in the selected frame."
(interactive)
(select-window
(cadr (seq-filter
(lambda (window)
(eq (buffer-local-value
'major-mode (window-buffer window))
'dired-mode))
(window-list)))))
(keymap-set dired-mode-map "TAB" #'dired-cycle-dired-windows)
And that’s it! From now on, pressing TAB in a Dired buffer will cycle through all Dired buffer in the currently selected frame.
Now this command could be made more elaborate. For example, it could go in another direction with a prefix argument. (No point for me, though – very rarely do I have more than two Dired windows open at the same time.) It could cycle through all Emacs frames (doable, but much more work for little gain). It could switch to another Dired buffer in the same window if there’s only one (clever, but I’m not sure if useful). That’s not the point – the point I’m making over and over again is that creating little but useful commands like this is a breeze in Emacs. In fact, creating this one took me maybe 10 minutes – and it would be under 5 minutes if not for the fact that I had to look up a few things, like how to get the buffer given the window, how to get major mode given a buffer etc.
As usual, I recommend the late Robert J. Chassell’s Introduction to Programming in Emacs Lisp if you want to learn to write commands like this and mold Emacs to your needs – and my book about Emacs Lisp if you want to see a few more (and more advanced) examples.
That’s it for today, until next time!
Christ has risen, rejoice! I have a bit of a rough time right now, but we have Good News – Jesus is greater than our troubles!
Like in previous years, rest assured that I will pray a decade of the Rosary for all of you, my dear readers.
Hallelujah!
The more I work with Node.js, the more I appreciate its built-in features. Supply chain attacks are a real thing and the more I can do without pulling in megabytes (or even kilobytes) of dependencies, the better.
For example, one of the very common patterns in Node applications are configs in .env files. The traditional way to digest them is to use dotenv, which admittedly has zero dependencies – but nowadays even that is not necessary. You can just run Node with --env-file .env, and the variables from .env will be loaded into process.env. (There is also --env-file-if-exists, which silently fails instead of throwing if the given file does not exist.) This works much like the dotenv package – if something is already in process.env, the value from the .env file is discarded, you can use comments (starting with #) in the .env file, etc. Even better, you can use --env-file more than one time, and a few .env files will be read in, with values in later files overriding values from the earlier ones. That way, you can have a general .env file and an .env.dev file with local overrides (although whether it’s a good practice is another question). You can also use this feature in a dotenv-like style where instead of a command-line option you call process.loadEnvFile at the beginning of your script. There is even util.parseEnv, which accepts a string and returns an object containing the key-value pairs resulting from its parsing.
One feature that sets this apart from dotenv is that you can use this to set variables that configure Node itself. For example, you can say
NODE_OPTIONS=--title=some-node-app
in .env, and when you run node --env-file .env your-app.js, ps
aux will show you some-node-app instead of node --env-file .env
your-app.js. As far as I know, this is something dotenv (which operates on a different level) cannot do.
This behavior has some quirks, though. Apparently parsing CLI arguments is not entirely consistent. I created this file:
console.log('process.argv:', process.argv)
console.log('NODE_OPTIONS:', process.env.NODE_OPTIONS)
console.log('SOME_OPTION:', process.env.SOME_OPTION)
setTimeout(() => {}, 10 * 1000)
and this .env fle:
SOME_OPTION=some option NODE_OPTIONS=--title=some-node-app
and said node --env-file .env test.js. Unsurprisingly, this is what I’ve got:
process.argv: [ '/usr/bin/node', '/.../test.js' ] NODE_OPTIONS: --title=some-node-app SOME_OPTION: some option
and $ pgrep -a node showed
1225363 some-node-app
So far, so good. But watch this:
$ node test.js --env-file .env process.argv: [ '/usr/bin/node', '/.../test.js', '--env-file', '.env' ] NODE_OPTIONS: undefined SOME_OPTION: undefined
So, Node treated –env-file .env as parameters to my script and not to Node itself (correctly) – but somehow still used it:
$ pgrep -a node 1226282 some-node-app
Why this happens I have no idea. I suspect it’s a weird (and hopefully harmless) bug in Node itself I plan to report.
Anyway, as you can see, you might not need dotenv anymore. In fact, thanks to this, my most recent project (which I will certainly blog about soon!) is a Node CLI tool with zero dependencies.
While at that, let me also mention another cool feature which landed in Node v25.0.0 (so it will be available in the next LTS version in a month or two). Prior to Node v25, you could console.log a regex and see this:
Since v25, the output has the regex colored:
How cool is that!?