Emacs Config Gems - Part 6
I came to realize that attempting to wrap up the “Wordcraft” section (as I call it) in Emacs is a fool’s errand. Emacs is Wordcraft. It’s what it’s all about. By the time I finish a draft, a new config or setting changes what I have. This here is an attempt to stay true to my conclusion in the last section.
Abbrevs
In my opinion, Abbrevs in Emacs is one of its fundamental features. It’s one of those things I’d show someone coming from other word-processing programs to show them the magic of Emacs. In essence, it’s a simple tool: you type two or more letters, and Emacs expands it to a phrase or even a whole paragraph. For example, when you type “hw” it will expand to “hello world” as soon as you hit space after the “w”.
Of course, you can achieve more with this useful function. We will get there. First, let’s discuss general usage:
-
To add a new abbrev, stand with the marker after the abbreviation, and then use: C-x a g for a global abbrev (which applies everywhere in Emacs), or C-x a i l for the specific major mode of the buffer you’re in (so, if you’re working in shell inside Emacs, it will add an abbrev there specifically.)
-
Once added, Emacs has a smart (and when you get it, helpful) way of remembering your lower-case/upper-case usage, depending on how you recorded the abbrev. Assume we’ve added “rnd” for “Research and Development”. This means:
- “rnd” automatically expands to Research and Development
- “Rnd” expands to Research and development (only the first word is capitalized!)
- “RND” expands to RESEARCH AND DEVELOPMENT
-
You can escape abbrevs with C-q after typing the abbrev. If “rnd” expands to “Research and Development” but you actually want to type “rnd” in the buffer, type rnd followed by C-q1.
-
If you want to add a prefix to an abbrev, for example, you want the end result to be “non-Research and Development”, type: non- and then M-’ (this will result in a hyphen showing, which is good) and then rnd to produce the abbrev.
-
Finally, don’t forget about
edit-abbrevswhich lets you edit your abbrev file directly. This is good when you want to add a bulk of abbreviations (I did this when I wanted to add usernames: I copy-pasted a bunch of those from Outlook one day during lunch).
Alright, with the basics out of the way, let’s dig into the config itself.
First, we want to turn on Emacs’ abbrevs. In my case, I want to put the abbrev file in a folder I sync between my computers so these shortcuts are available on both of them. To do this, I need to set the path for the abbrev file:
(setq-default abbrev-mode t)
(setq abbrev-file-name "~/Sync/Personal/.abbrev_defs")
Now something a bit more advanced. I posted about this before, but I didn’t explain my use case, and I think its usefulness is a bit lost as a result. Here’s the general idea.
At work, we have user IDs (usernames) that I need to use with ServiceNow, our ticketing system. It’s also useful when I want to write an email, and there are five people named “John.” My brain solved this problem years ago with nicknames: the John who has tattoos and a beard looks like a motorcyclist, so I call him Harley (Davidson); the other John loves tea from Georgia, so I call him George, etc.
When I want to dig out Harley’s username, I prefix it with @, and Emacs spits out “jd1234” for his username. This way I don’t hijack the word “Harley” to always be associated with jd1234, only when I really want to use it2.
The post I linked above explains how this works and gives proper credit to Prot, who helped me build this up using regex:
(abbrev-table-put global-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[@].*\\|.*\\)")
(abbrev-table-put org-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[@].*\\|.*\\)")
Grammar Checkers
When it comes to grammar, I explored two options in Emacs. I first started with LanguageTool, then discovered Harper, which I use today. But as these Emacs posts go, I need to take a step back and talk about spelling and grammar checkers first.
With a spell-checking tool, we just need a dictionary: a list of words the program checks against what we typed and identifies the closest match in its list. The mechanism behind it is a mathematical principle of edit distance: it is a fuzzy search for words that are the closest to the misspelled one3. This can be complemented with a personal dictionary of additional unofficial words that we tend to use, and that’s mostly it; we’re good to go.
A grammar checker is a different animal, or actually, animals. And before we start talking about these, it’s worth mentioning that grammar, or what is generally considered “correct” language, is a moving target4. Language is a grey area, where what’s correct can be — at least to a degree — subjective.
The complex mix of tools used in what we cram together today under the term “grammar checkers” is a big underestimation of the history and development of the last 50 years or so of progress and the different tools that we use today. With modern-day LLMs, the confusion extends further, as these usually don’t use any of those tools, relying only on transformers and weights to predict what seems correct and what isn’t5.
Harper
Many Emacs folks, myself included, use Harper these days. It’s no wonder, given its speed and minimal install size, especially compared with LanguageTool, the other free alternative. But this lightness comes at a cost.
Harper provides:
- a basic spelling checker (dictionary-based)
- core grammar rule engine
… and that’s it. Indeed, in my first post about Harper, I noted a couple of basic errors it missed, and I still think it’s far from “the private alternative to Grammarly,” as it states on its homepage. I actually barely use it because it’s the competition that does a much better job, grammar-wise. If it were up to me, the tagline would be “a private, fast, customizable grammar checker for when you don’t want a grammar nanny looking over your shoulder.”
Harper is available on homebrew (it seems the folks behind Harper made a dedicated dmg since I last installed it), but for Ubuntu and its kin, it requires Cargo. This also means that if Harper is the only program you have from Cargo, you also need to remember to update it6. I’ve written and explained my Harper setup a few times, but it evolved over time to accommodate both my Mac and my Linux desktop, so I’m including the whole thing here. Note that Harper works through Eglot on Emacs, as I explained in the first post linked above.
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(org-mode . ("harper-ls" "--stdio")))) ; This is the eglot-related integration
(setq-default eglot-workspace-configuration
'(:harper-ls (:dialect "American" :linters (:LongSentences :json-false :AvoidCurses :json-false))))
;; These are options for Harper. I chose American dialect as the language, I also want to ignore long sentence errors (the main issue is that it hides other errors nested in those) and I also want Harper not to tell me when it thinks something is offensive. The full list of these options is in https://writewithharper.com/docs/rules. It needs to be nested inside the :linters option.
(when (eq system-type 'gnu/linux)
(add-to-list 'exec-path (expand-file-name "~/.cargo/bin"))
(add-to-list 'exec-path "/snap/bin"))
;; On Linux, harper-ls lives in ~/.cargo/bin (installed via Cargo/Rust).
;; /snap/bin is added for Vale (installed via snap - that's another program I might discuss, ignore it for now) and any future snap packages.
;; On Mac, Harper is installed via Homebrew which handles PATH automatically.
LanguageTool
LanguageTool provides:
- A spelling checker
- Core rule Grammar engine
- n-gram data set (optional as local install for free)7
- LLM (Paid, online only)
In addition, as I was researching more into LanguageTool, I found lesser-known layers and additions that are not fully documented. For example, there’s a neural network for confusing pairs included. I don’t know how important those other tools are, but if you use the full product online to get a taste, you will see the difference when comparing it to Harper immediately.
While it sounds like I’m favoring LanguageTool over Harper, I don’t have it installed anymore, and I haven’t used it in a while. The reason for this is that it requires Java and over 10GB (yep) if you want to get the n-gram running with the other additions. Below is my old config, based on version 6.1. You will need to install langtool.el to run it, without the full layers discussed above (by the time I found out about these options, I stopped using it):
(require 'langtool)
(setq langtool-language-tool-server-jar "~/Path/To/Current/LanguageTool-#.#/languagetool-server.jar")
(setq langtool-default-language "en-US")
All in all, I do not think Emacs is the best place for grammar checkers. YMMV, but since I have access to Grammarly Pro’s features, I prefer it. I also use AI to go through my posts, and it does a few very useful things, grammar being just one of them if I need it to. If I didn’t have Grammarly, I’d consider LanguageTool’s pro package, which comes with additional perks that are not available for a local install.
My logic here is simple: if I write a post for my blog, which is going to be public anyway, I don’t mind using online tools or LLMs. For more sensitive things, like my journal, I use Harper if I want to, but most of the time it’s an overkill for my own mind’s rambling anyway. This is why I said above that I barely use Harper, as I don’t really have a use case for it.
-
As I’m writing this, I realize another simpler trick is Emacs’ undo. Simply use C-/ (or whatever you have it bound to) and it will cancel the expansion. ↩︎
-
Now that I use corfu, which also contains my abbrevs, typing “george” will bring up a menu allowing me to choose george from a drop down instead of specifically typing @george. ↩︎
-
The mathematical concept behind this is called Levenshtein distance, after Vladimir Levenshtein, who came up with this in 1965. The basic idea in spelling programs is to measure how many “jumps” are required to correct a mistyped word. For example, “helo” to “help” is one jump, as well as “helo” to “hello”; “helo” to “hallow” though substitutes e with a, inserts an l, and then inserts the letter w — 3 jumps. This means that pure Levenshtein spell checkers will rank the first two corrections higher on the list (modern spell checkers introduce a few more tweaks). Wikipedia gives us an interesting history lesson here: Unix got Ispell, which is based on SPELL, and GNU built upon that with Aspell, which is what we have in Emacs today. The front end in Emacs,
ispell.el, is named after the original program it once used. ↩︎ -
Brennan wrote a good post about this recently with plenty of links, which I’m stripping here because I want you to go read his whole thing, not just my footnote: “To ask someone to write properly is to assume there is a way to write proper. There isn’t. There is no universal, static version of English. Or any other language, for that matter. Every dialect of English, including regional accents and non-standard forms, follows complex, systematic grammatical rules.” ↩︎
-
While most popular LLMs today are generic and don’t rely on more traditional grammar tools, specific grammar products like Grammarly and Langtool do use specific traditional tools as well as an LLM on another layer, making the box of grammar checkers as a category even more vague. ↩︎
-
After realizing how often I forget to update Harper on Linux (I don’t get reminders as I do with other packages), I searched for a solution and discovered topgrade. It’s a Rust tool that probes your system, figures out which packages you use as well as your hardware, and offers you all the updates in one go: apt, flatpak, cargo, snap, and even updates for your boot, if you have a Linux machine from a brand that supports it, like System76. ↩︎
-
n-gram is a sequence of symbols, or in our case, words, where n is the number of words. So “a nice sunset” is a 3-gram sequence and “they are wearing hats” is a 4-gram one. Grammar checkers look at many of these sequences in a large body of text and evaluate whether what we type has errors. For example, if I write “they are wearing hat,” the checker will run this against several examples and will find that “they are wearing hats” (with an s at the end) is much more common than the first example, which means the first example is probably wrong and should be corrected to what is found in the text. As you can imagine, the checker needs to look at a lot of text to make good corrections, which is why that body of text is huge — hence the size of a few hefty gigabytes. LanguageTool explains n-grams and how they use them, which is helpful. If this sounds similar to LLMs and how they work, it’s because the connection exists: the basic idea of looking through a large body of text is the same, and in a way, LLMs today are built on a similar concept. However, LLMs take this a step further and compute what would make sense using weights and other settings in real time, while n-grams are essentially static lists of words the computer just compares against. There’s no “thinking” involved. It helped me to think about it this way: in terms of hardware, your hard drive reading speed is way more important for n-grams than your GPU. ↩︎