Blog

For the English part of the blog, see Content AND Presentation.

2024-10-19 substitute-command-keys

Today I have a short tip for everyone that codes Elisp (for other people or even only for themselves), inspired by something I wrote recently (and will blog about soon, too). If you want to tell the user things like “press C-c C-c to finish what you are doing”, don’t hardcode the keybindings in the message string. You may bind the command to finish whatever the user is doing to C-c C-c, true, but the user could rebind it to <f10> or even M-s s-a or whatever key they like, or even unbind it completely. Instead, use the following syntax: \[command-name] (of course, you need to double the slash if you put it in an Elisp string), and process the string with substitute-command-keys. This way, Emacs will convert your command-name to the textual representation of the actual binding of your command. If the command has more than one binding, you can set the “preferred one” by setting the :advertised-binding property of the function name using put (or preferably function-put). If that property is not set, the binding used is selected according to where-is-preferred-modifier, which see; note that the docstring of that variable is apparently wrong, since it should say where-is-internal, not where-is. Even better, the resulting string will be fontified so that the binding is in the help-key-binding face.

For example, in emacs -Q, (substitute-command-keys "\\[undo]") yields C-x u, and (substitute-command-keys "\\[describe-face]") yields M-x describe-face (since this one is not bound to any key by default).

In fact, substitute-command-keys can do a lot more – see the relevant section of the documentation in the Elisp reference.

As usual, the self-documenting nature of Emacs shows itself – the support for something as trivial as displaying the binding of a command is superb, and if you want your code to be as user-friendly as possible when showing those bindings, you only need to make use of what Emacs already gives you.

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-10-14 mrr-replace-mode

I have to admit that this is a bit embarrassing. A long time ago I announced a future post (and promised to release my code) for performing multiple regex replacements in a buffer, possibly in an interactive way. A few months later I started my first programming job (yay!) and promptly forgot about it…

Good thing is, I was reminded about this recently, so I have a chance to revisit the topic. Without further ado, let’s go!

As I said in that previous post, I also wrote a minor mode for selectively (and interactively) performing replacements of some regexen. It proved immensely useful when I worked for Wiadomości Matematyczne as a copyeditor and proofreader. In order to use it, you need to configure it first. There is just one option to do that: mrr-interactive-substitutions. It is a list of lists of strings. Every sublist starts with a regex to replace, followed by an optional list of key-value pairs, followed by a list of possible replacements.

As of now, there are two options: :hint (a string, displayed in the echo area when a string matching the regex is found) and :test (a symbol, which should be a predicate function accepting no arguments; the replacement is skipped if that function returns nil).

The default value for mrr-interactive-substitutions – which serves as an example – looks like this:

'(("\\(\\b\\| +\\)-\\{1,3\\}\\(\\b\\| +\\)" :hint "TeX dashes" "-" "--" "~-- ")
  ("d\\([xts]\\)" :hint "Differential operator" :test texmathp "\\\\mathrm{d}\\1"))

Let’s analyze this. The first entry matches a string of one to three hyphens, preceeded (and followed) either by at least one space or a word boundary. The suggested replacements are a hyphen, double hyphen and a double hyphen surrounded by spaces (an unbreakable one on the left and a normal one on the right). The rationale is that many authors do not care about hyphens, en-dashes and em-dashes, so we checked every occurrence of such things and possibly fixed them. (It is disputable whether you really should use an em-dash or just an en-dash surrounded by spaces; we followed Polish typographical customs here, and apparently Robert Bringhurst agrees.)

The second entry is the letter d followed by one of the letters x, t or s, which are the most common differentials (in integrals, for example). Those are potentially replaced by the same thing, but with an upright d. Note the use of a capturing group in the regex and \1 in the replacement. Also, I used the :test keyword here – these strings can appear in words (take “redshift”, for example). Old-time readers of my blog might remember that the function texmathp returns true if the point is in TeX math mode.

Now, if I say M-x mrr-replace-mode, Emacs searches for the first match for any of the given regexen, highlights it with an overlay (using the same face as isearch does) and shows the “hint” in the echo area. You have three options then. You can press C-g to quit mrr-replace-mode, RET to move to the next match (or quit if there are no matches), and TAB to cycle between possible replacements (including the original text – after all, you might decide you want to leave it as it is).

A cool (and rather Emacs’y) thing is that it is a minor mode. This means that you can normally edit the surrounding text while doing the replacements, or do something in a different buffer and come back later. You might even notice that some useful pattern is missing from mrr-interactive-substitutions, move to your init.el, add it, evaluate the setq, get back to the original buffer, move point to the right place and press RET to look for the new list of patterns – all without even leaving the minor mode! You can also manually edit the text under replacement, but be warned that pressing TAB then will remove your edits.

I wrote this code almost a decade ago, and it shows. There is at least one stupid bug left (the “hint” is suggested as one of the replacements (how could I have not noticed that?), and the format of the mrr-interactive-substitutions option is very terse which makes it almost impossible to describe properly in defcustom. I intend to review the code and fix both issues, but it might take a while. Still, the code is usable as-is (assuming you don’t use the :hint option), and I can assure you that it helps tremendously with copyediting. (When I worked for “Wiadomości Matematyczne”, I had almost 50 entries in mrr-interactive-substitutions!) It is not on any Emacs package repository (I am going to finally learn how to add a package to Melpa, and this is going to be one of the packages I am going to put there), but you can find it on Gitlab.

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-10-07 Autoloads

I’ve known about the Emacs autoload feature for a long time, but I never bothered to read about it. I decided to fix that and finally read the relevant part of the Emacs Lisp manual.

It turns out that autoloads are a pretty simple concept. They basically serve as a way of deferring loading a package until it is really needed. If you have a lot of packages, loading them on Emacs startup may take considerable time and memory. (Of course, nowadays we usually don’t care too much for the latter, since computers now have insane amounts of RAM. My first computer had 64 kilobytes of RAM, and my first PC had 4 megabytes, and GNU Emacs was written in times when 4 megabytes was a lot!) Autoloads are a way to tell Emacs when the package should be loaded by defining some functions as “entry points” to the package. (Actually, it is a bit more sophisticated than that, but that’s the main idea.)

If you write a package, you should mark the entry point(s) with the “autoload comment”. That means putting the string ;;;###autoload on the line before the relevant defun (or perhaps other form defining something, like defvar or cl-defun). When the package is installed, Emacs scans it for these comments and generates a special file containing information about these functions, variables etc. During startup, Emacs loads that special file (which is much, much smaller than the main package file or files, and hence faster to load). This means that all autoloaded functions become actually callable – but calling any of them causes Emacs to load the package in question first. This means that autoloaded functions are loaded “on demand” – when they are first called.

That is (of course!) not the whole story. For example, you can make Emacs put basically arbitrary code into the autoloads file by putting it after the autoload comment but on the same line. This way, that piece of code is not evaluated when loading the package (since it is commented out), but on Emacs startup (because it gets copied to the autoloads file). This may be useful if the package uses non-standard ways to define things.

I intend to put this knowledge into practice soon – I’d like to take some of my Elisp code and submit it as a package to Melpa, both to make it more discoverable and hence useful to other people and also to learn the process. Of course, when I do it, I will write about it here!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

More...