Journal

2024-01-22 From the kill ring to a register

I am probably one of the five or six people on this planet who use Emacs registers. I mostly use them for text insertion (especially when I need to insert some boilerplate – and especially more than one piece of it – several times, but it’s not general enough to be put into Yasnippet), but I sometimes store window configurations in them, and I might have used them for other purposes once or twice.

Sometimes, though, I kill some text to yank it elsewhere and only then realize that I’ll need to kill/yank some other fragments, too. Using the kill ring to do this one time is fine (with M-y or with browse-kill-ring); using it more times is rather inconvenient. Hence I decided to write a little command to copy the text from the kill ring to a specified register. And here it is.

(defun shorten-string (string length &optional suffix)
  "Shorten STRING to LENGTH and append SUFFIX if it is longer.
If LENGTH is too short, output the first character of STRING and
SUFFIX."
  (setq suffix (or suffix "..."))
  (if (<= (length string) length)
      string
    (format "%s%s"
            (substring string 0 (max (- length (length suffix))
                                     1))
            suffix)))

(defun copy-kill-to-register (register n)
  "Copy text from the kill ring to REGISTER.
With prefix argument N, use the Nth kill ring entry, but don't
move the last yank pointer to it."
  (interactive (let* ((n (prefix-numeric-value current-prefix-arg)))
                 (list (register-read-with-preview
                        (format
                         "Kill at position %s (%s) to register: "
                         n
                         (shorten-string (current-kill (1- n) t) 40)))
                       n)))
  (set-register register (current-kill (1- n) t)))

As you can see, I decided to be a bit fancy – for example, I display the text to be copied in the prompt, but make it no longer than 40 characters. It took me about half an hour to put all this together, but a working prototype (without the prompt shenanigans) took less than 10 minutes. This is the power of Emacs in action – modifying your Emacs to better meet your needs is a very frictionless experience, you just open some Elisp buffer, write a command, press C-M-x and it just works.

Note that the above code is not DRY – the (current-kill (1- n) t) fragment is repeated twice. The reason is that I wanted to have it in both the interactive clause and in the function body. As far as I know, there is no way to set a temporary variable so that it is available in both places. There are, however, two ways of circumventing this (neither very elegant). One is to declare a global variable for that purpose and setq it within he scope of interactive:

(defvar copy-kill-to-register-text nil
  "A variable set in `copy-kill-to-register'.
This is an internal variable `setq' in the `interactive' clause
of `copy-kill-to-register' so that it can be accessed later, in
the body of that command.")

(defun copy-kill-to-register (register n)
  "Copy text from the kill ring to REGISTER.
With prefix argument N, use the Nth kill ring entry, but don't
move the last yank pointer to it."
  (interactive (let* ((n (prefix-numeric-value current-prefix-arg)))
                 (setq text (current-kill (1- n) t))
                 (list (register-read-with-preview
                        (format
                         "Kill at position %s (%s) to register: "
                         n
                         (shorten-string text 40)))
                       n)))
  (set-register register text))

Definitely not elegant, but at least we don’t repeat ourselves. If we had some more complicated code (or more time-consuming to compute one, like a result of some complex calculations or of a network request, or one with side effects), this may be the way to go.

Another trick would be not to use interactive to get the value of text. We could set register to nil within interactive and then, if it is nil, ask the user for its value in the body of the function:

(defun copy-kill-to-register (register n)
  "Copy text from the kill ring to REGISTER.
With prefix argument N, use the Nth kill ring entry, but don't
move the last yank pointer to it."
  (interactive (list nil (prefix-numeric-value current-prefix-arg)))
  (let ((text (current-kill (1- n) t)))
    (setq register (or register
                       (register-read-with-preview
                        (format
                         "Kill at position %s (%s) to register: "
                         n
                         (shorten-string text 40)))))
    (set-register register text)))

This way, if our function is used in Elisp (and register is a character), it will just copy the n​th entry on the kill ring to the given register, and if used interactively, the interactive clause will set register to nil and register-read-with-preview will be evaluated. Still not beautiful, but at least it works (and we don’t have to type (current-kill (1- n) t) twice). It is up to you which variant to use – personally, I’ll still stick to the first one, and if I really wanted (or needed) to avoid evaluating the same expression twice, I would probably go with the “temporary global variable” variant.

As usual, let me remind you that if you want to learn to write code making your life in Emacs simpler, you should really read Introduction to programming in Emacs Lisp by the late Robert J. Chassell, and you may find my book, Hacking your way around in Emacs, a nice next step. And since I am currently working – slowly but steadily – on improving said book, if you have any suggestions, I might consider writing one more chapter!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-01-08 My plans for 2023 - final report

At the beginning of the last year I wrote about my plans for 2023. As 2023 came to an end, it’s a good moment to look back and reflect on them a bit.

While 2022 was sort of disappointing, 2023 was definitely not. My day job continues pretty uneventfully (which is of course good). I started doing “weekly reviews”, which is still not very easy for me (and I hope to learn to do them better next year). I didn’t yet start with quarterly and yearly reviews (well, this post doesn’t really count, since I only talk here about things I’m doing “in public”), but I hope to learn to do those, too. Last 10 years or so taught me that it is possible (and even not extremely difficult) to instill new habits, so I’m pretty confident this will go fine.

As I wrote (almost) a year ago, I wrote the booklet about personal accounting in Ledger. As expected, it is not exactly “popular”, but Leanpub tells me that 19 people found it worth buying, and even if it is (just a bit) less than I hoped for, it’s still fine with me. And of course big “thank you” to all the readers who trusted me with their money and (hopefully) time they spent reading what I wrote!

My plan to work regularly on the Elisp book was, I have to admit, a complete failure. Well, I really hope it’s going to be better this year. As I mentioned previously, I started to devote my Monday “writing slot” to this, and it seems to work pretty well. The main issue with this is that the changes I’d like to make are going to be more time-consuming than I expected – but I’m not in a big hurry, so that’s ok.

Next comes my “secret project”. Last time I told the story of how I became a Whovian. For the past 3 years I diligently translated subtitles for way over a hundred episodes to Polish (which took me over 400 hours!), improved Emacs Subed mode and later (in July 2023) started my Doctor Who-related blog, called Crimson Eleven Delight Petrichor. The blog took off both very well and very badly. How is that possible? Let’s begin with the bad news: I really hoped for getting some support from readers, both financial or moral (like letting me know that someone is actually reading it). I admit that I didn’t do much “advertising”, but I dropped a link here and there in hope that this is going to be enough. Well, it wasn’t. I did consider spending some cash on Facebook advertising, but it felt slightly wrong to use Facebook to advertise a website which strives to respect the readers’ privacy. I will still try to publicize it, but I have much less hope than I had half a year ago. This also means that I’m not going to write one post every two weeks there – it turned out to take more time than I expected, and this pace is difficult for me to sustain. My current plan is to write at least one Doctor Who post every four weeks and come back to publishing here more frequently, so that I will still write one blog post per week. The only thing to change is the proportion of posts on both blogs.

On the other hand, I am very happy with what I wrote in the last 6 months. I am, of course, aware that I am not a great writer, although I hope that I’m at least a decent one. But rewatching Series 1 of Doctor Who and thinking about it in depth was a fantastic (!) experience, and I’m looking forward to analyzing the later series, too. Quite a few episodes contained even more interesting material to think about than I expected, which was a pleasant surprise. Also, I did a word count (well, more like estimation than count – I write the blog in Org mode, and various markup elements like #+begin_quote block delimiters count as words, too), and it turned out that I wrote well above 30 thousand words. Obviously, quantity does not translate directly to quality (especially in my writing which tends to be rather verbose…), but it is a pleasant thought that what I wrote is an equivalent of a (more or less) 80-page booklet.

Also, the engine I used to write on Crimson Eleven Delight Petrichor, Org Clive, turned out very well. It’s still not 100% feature-complete (there are at least two features I’d like to add: page modification times and exporting only the pages that actually changed), and it has a few rough edges, but overall it turned out to be very nice to work with. If you want to set up a simple website or a blog, controlled from Org mode, give it a try (and make sure to let me know)!

As for the “two new books”, those didn’t work out at all. The booklet documenting the process of my studying documentation about browser extensions was fun to write, but I’m afraid it’s less fun to read than I hoped for. At least, only two people considered it worth their money (thank you both, anonymous readers!) Also, what I learned aling the way about browser extensions makes the other project – an actual book teaching to write them – less appealing than I expected. I am still on a fence – I might try to write that textbook – but even if so, it will have to wait a bit.

You may ask, what about 2024? One year ago I wrote that 2023 was going to be a writing year, and it definitely was. Well, as I said above, I’m not abandoning writing at all (of course!), but I’ve decided that 2024 will be a learning year. There are a few things I’d like to learn. For example, I’d like to make a deeper dive in PostgreSQL, I’d love to learn a bit about some frontend technologies, and some other stuff. Since this is more private-oriented (it’s not going to result in a lot of blog posts, for instance), I won’t write more about my 2024 plans here (nor will I make regular updates) – but maybe I’ll try to do that again in a year. We’ll see!

Anyway, even if not everything went as I hoped it would, I’m still thankful for 2023. My side projects I write about here are not everything for me – this year was pretty good profesionally, and I also had some very good things going on in my private/family life, so I’m overall very happy with it. In fact, I’m looking forward to all the great stuff God has prepared for me in 2024!

CategoryEnglish, CategoryBlog

Comments on this page

2023-12-25 Merry Christmas 2023

As usual at this time of year, let me wish all of you Merry Christmas! And also as usual, I promise to say a decade of the Holy Rosary for everyone reading my blog.

CategoryEnglish, CategoryBlog, CategoryFaith

Comments on this page

More...