Content AND Presentation

2024-07-22 New things in new Emacs

Some time ago I wrote about a few new things in Emacs 29. Well, even though Emacs 30 is not out yet, I use Emacs compiled from source on the master branch, and while I do not compile a new version very often, I still have a version newer than what my distro offers. (Well technically, Arch has the emacs-git package, but I am so accustomed to have my own Emacs sources that I can see no point in using that.)

So, I went through the NEWS.30 in my Emacs Git repo, and – as usual – found a few gems. For example, advice-remove is now a command, it shows a list of functions which have an advice added, and when you select one of them, it shows a list of advices and lets you select one. (Of course, this is because I have ivy installed – with vanilla Emacs it just uses the default completion mechanism. But from what I’ve heard, the default completion is getting better all the time, and it probably can also be configured to show a list etc.)

It turns out that some of my personal customizations may no longer be needed – for example, Emacs now has 'kill-ring-deindent-mode, which mostly supersedes my own copy-snippet-deindented command.

You can now give a regex and a replacement, and instead of applying the replacement interactively like query-replace-regexp does, see a diff of the changes you would have made and – for example – apply individual hunks (M-x replace-regexp-as-diff). This may be very useful to review and selectively apply complex changes.

There is a lot of new functionality for keyboard macros. One area that got improved is keyboard macro counters, which can now be e.g. compared to registers and conditionally incremented if smaller or greater than the registers’ contents, or copied to and from registers. I don’t have (yet?) any idea about how this could be useful, but here’s another great thing: you can now see a list of all keyboard macros and use it to inspect and edit them, their counters etc. This seems an extremely useful thing if you happen to use keyboard macros often!

There are numerous other changes – I strongly advise you to look through your NEWS file every time you upgrade your Emacs – but I saved the one I may like the most until now. I mentioned last time that Emacs 29 introduced the Info-goto-node-web command (bound to G in Info by default), opening the current node in a browser. I mentioned then that
As of now, it lacks two things: it only works in the Emacs manual and the Elisp reference (and it really should work in the Org manual and in Chassell’s An Introduction to Programming in Emacs Lisp!), and it doesn’t do anything special with a prefix argument (and it definitely should put the link on the kill ring then).
and voilà: the former is now corrected, and it works in over 60 manuals! As for the latter, well, it’s not difficult to write a quick-and-dirty solution:

(defun Info-copy-url-to-kill-ring (&rest ignored)
  "Copy the URL of the current node to the kill ring."
  (when current-prefix-arg
    (kill-new (Info-url-for-node
               (format "(%s)%s"
                       (file-name-sans-extension
                        (file-name-nondirectory
                         Info-current-file))
                       Info-current-node)))))

(advice-add 'Info-goto-node-web
            :after
            #'Info-copy-url-to-kill-ring)

That’s it for today!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-07-13 Dedicated windows

There are some features of Emacs which you can read about in the manual and go “huh? what is that about? who’d ever need it?”, and then later turn out to need exactly them.

Recently I learned that dedicated windows are just like that. I generally tend to stay away from configuring display-buffer, which is famously flexible but infamously complex. If I really want my window configuration not to disappear, I store it in a register.

A few days ago, however, I learned a new trick (which is in fact new, in the sense that you need to be on master to use it – it will become available in Emacs 30). You can press C-x w d in a window and that window becomes dedicated to the buffer it is displaying. (Note: dedicated windows have been in Emacs for quite some time IIRC, but there was no UI for making a window dedicated.)

The precise meaning and behavior of deidacted windows seems a bit complex, but the main idea is that Emacs generally won’t reuse a dedicated window for anything unless explicitly told to. Here is my use-case. Imagine you are studying a book (in a pdf file, using pdf-tools, or in and epub file, using nov.el). Since the book is a technical manual or textbook, you want to try out code snippets while reading, so you open two side-by-side windows – one with the book itself and one with an Org file with your notes. You put the code snippets in source blocks – but then, when you run the code snippet and something is wrong, a buffer with the error shows in place of the book, which is not very nice.

Now, if you make the window displaying the book dedicated (and it will be possible with C-x w d in Emacs 30), this won’t happen anymore! Now the error buffer is shown in another window, and you book will be still visible. You can also make a window strongly dedicated, which means Emacs won’t let you change the buffer when inside that window.

This is still not ideal – for example, org-edit-special (C-c ') doesn’t seem to respect the “dedicated” status – but it is a big help. From now on I will try to remember to use C-x w d in this situation (and perhaps a few others), when I want some window to show a given buffer all (or at least most) of the time without Emacs suddenly deciding to show something else there.

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-07-01 Starting Node.js with selected packages preloaded

There are several Node.js packages I use fairly often, and often need to debug some code using them. As any Lisper will tell you, there are few better ways to debug/explore code than a REPL. Node.js comes with a decent enough REPL for simple experiments, but what if I want to play around with Lodash or Ramda, not just vanilla JS?

(Ramda has an online REPL to play around with, and it’s pretty nice, but it has one important drawback: it’s a cloud service. I can’t just paste some code from the app I’m working on if said code contains anything even slightly confidential.)

It turns out that Node.js comes with a --require CLI option, which sounds nice in theory, but is next to useless in this case. Even if I say node --require lodash, nothing is actually put in the global scope (and by the way, how would it know to assign lodash to _ as is customary to do?).

Here is one way to start Node.js with Lodash preloaded, ready for experimenting:

node -i -e "const _ = require('lodash')"

This is nice and often enough, but what if I need more than one package? And what if I don’t want to type this again and again, possibly with various packages?

I searched for a bit and I found several packages which do exactly what I need (or so it seems). Unfortunately, I couldn’t get them to work – so I decided to roll out my own. It is really fairly simple, though it was not obvious to me at first how to spawn an interactive REPL from within a Node.js script. It turns out that child_process.exec() and friends are not really suitable for the job (although I’m still not 100% sure why – I suspect they do not set stdin and stdout of the spawned process correctly), but child_process.spawn works fine.

After a few minutes of fiddling, I came up with this little script.

const fs = require('fs');
const path = require('path');
const child_process = require('child_process');

function main() {
        let directory = process.cwd();
        let package_json;
        while (true) {
                try {
                        package_json = JSON.parse(
                                fs.readFileSync(path.join(directory, 'package.json'), {encoding: 'utf8'}),
                        );
                        break;
                } catch(error) {
                        directory = path.dirname(directory);
                        // Break if at top directory
                        if (directory === path.dirname(directory)) {
                                break;
                        }
                }
        }

        if (!package_json) {
                console.error('package.json invalid or not found');
                process.exit(1);
        }

        const {node_repl} = package_json;
        if (!node_repl) {
                console.error('No `node_repl` property in `package.json`')
                process.exit(2);
        }
        const modules = Object.keys(node_repl?.packages).map(
                key => `const ${key} = require('${node_repl?.packages[key]}')`,
        ).join(';\n')
        child_process.spawn('node', ['--interactive', '--eval', modules], {stdio: 'inherit'});
};

main();

I’m not sure if it will work on Windows – I hope the trick I employed to check if we reached the root directory will work there, but I’ll have to check it when I have access to a Windows machine (this should happen later this week). The reason I do the whole “go up the directory tree until you find package.json​” thing is that I need to somehow tell my script what modules you want preloaded and under what names (for example, you’d probably want Lodash to be _ and Ramda to be R, etc.). Introducing another config file just for that seems redundant – especially that you need them in node_modules anyway, so package.json seems fitting for the purpose. And you might want to start your REPL in some subdirectory, so making it work only in the project’s root directory may be a bit too restrictive.

(Initially, I used path.resolve('directory', '..') instead of path.dirname(directory), but they work the same.)

By the way, normally Node.js can do all that directory tree traversing for you, and you can just say const package_json = require('./package.json') (even if you’re deeper in the directory tree). This would not work here, since it would load the package.json file of my script, not the one of the project I’m in!

I hope someone finds it useful. I certainly do, and I put it on npm for you to check out! It is a bit of a mess – it is my first published npm package and I learned as I went through several iterations. That’s why it’s at version 1.0.3, but there are no earlier versions – I fixed a few errors and used git rebase to keep the history clean, but you can’t publish a new version unless you bump the version number. Technically, it’s probably against semver rules to bump only the “patch” portion of the version number while introducing breaking changes, but since it wasn’t advertised at all, and it didn’t even have a proper readme, I guess it should be ok.

Check it out! PRs are welcome. One feature which is definitely missing is support for ESM modules – I might add it one day, but for now only CJS modules are supported.

CategoryEnglish, CategoryBlog, CategoryJavaScript

Comments on this page

More...

CategoryEnglish, CategoryBlog