Hung-Yi's LogoHung-Yi’s Journal

Emacs Lets You Calculate While You Write

What happens when you're in the middle of a sentence and you need to insert a precisely calculated number in amongst your prose? Emacs let's you do just that using its built-in calculator.

Let’s say you’re writing a recipe but you want to scale the ingredients down by one third. The recipe calls for 280g of flour. And you want to round to the nearest 10g.

I applaud you if you can easily do that in your head—because I certainly can’t. And yes, it would be easy just to run your system’s calculator app to get the answer, but why leave Emacs if you can help it? Doing it this way also saves you a bunch of manual copying and pasting between apps.

Here’s how:

  1. Type in the expression you want to calculate; in this case it’ll be round(280*2/3, -1)1. Your buffer might look like this:

    Ingredients:
    - 2 eggs
    - round(280*2/3, -1)g flour
    - 10g baking powder
    
  2. Mark/select the expression by hitting C-SPC (just v in Doom Emacs or evil mode) and moving the point/cursor until the whole expression is neatly marked.
  3. Call calc-grab-region either manually via M-x calc-grab-region or by pressing C-x * g. In Doom Emacs, the G has to be capital for some reason.

    This will grab the expression, shove it into Emacs’ calc mode and evaluate it. You should see [190] on the “stack”.

  4. Call calc-unpack either manually using M-x or by pressing v u to unpack the vector (i.e. get rid of the square brackets that we don’t need)
  5. To get the result back in your buffer, press C-u y (that would be SPC u p P in Doom Emacs2 or evil mode). This calls calc-copy-to-buffer.

    This calls calc-copy-to-buffer on the most recent result. The prefix C-u makes it overwrite the marked region in your original buffer and conveniently quit calc mode. Without the prefix, it would insert the result at the point in your buffer, but leaving the original expression text intact and also let you hang out in calc mode in case you want to do more math.

  6. The result will now look like this:

    Ingredients:
    - 2 eggs
    - 190g flour
    - 10g baking powder
    

Summary

Assuming you have your expression marked, your keystrokes in one line would be: C-x * g then v u and finally C-u y.

For Doom Emacs and evil mode users, that would be C-x * G, v u and SPC u p P.2

A word on calc-dispatch

C-x * actually calls the calc-dispatch command, which you can also call manually. In this mode you can press ? to get a list of all the calc options that are available to you. However, in this tutorial we just used g for calc-grab-region.

In Doom Emacs, most common operations are mapped to the SPC leader, so pressing C-x felt foreign to me. Personally I went ahead and mapped calc-dispatch to SPC = = by putting this in my config.el:

(map! :leader
      (:prefix-map ("=" . "calc")
       "=" #'calc-dispatch

       ;; Some other shortcuts
       "c" #'calc
       "q" #'quick-calc
       "g" #'calc-grab-region))

And as you can see, there’s room for more personal shortcuts for whatever calc commands you use regularly. In my case:

  • SPC = c to just launch calc mode without doing anything clever
  • SPC = q to do quick calculations in the minibuffer
  • SPC = g as a shortcut to grab a region without going through calc-dispatch

Footnotes:

1

The round formula does as you’d expect. The second parameter specifies the precision that you want to round to. Usually this is the number of decimal places, and if you leave it out it’ll default to 0, which would round to the nearest integer. Putting -1 means that it’ll round one place to the left of the decimal point, i.e. to the nearest 10.

2

Doom Emacs uses evil-collection which remaps some of the keys in calc mode to be more aligned with evil mode. One of the changes is to remap y to pP for calc-copy-to-buffer. That’s why the Doom Emacs key sequence is a little weird.

The way I remember it is that I want to ’put’ or ’paste’ the result, so I press p, then I remember that I want to put it in the buffer above, so I elevate the next ’p’ to P with the shift key.