I need something like abbrev, just without any extra spaces, including spaces which trigger the replacement. I experimented with keyboard shortcuts, like this:

(global-set-key (kbd "H H") "---~")
(global-set-key (kbd "H H J") "~---")
(global-set-key (kbd "J J") "~---~")

It replaces every H, whereas I need double H (HH, uppercase only) as a trigger. I tried different shortcut descriptions; unfortunately, this is my best result. Ideally, I want also a timeout (if the second H is lagging, business as usual), and the replacement to be enabled for *.tex files only. Is there a way?

Actually I use Doom Emacs with evil mode.


This one is unusable in a different way:

 "H H" "---~"
 "H J" "~---"
 "J J" "~---~"

It firmly locks down and waits for the second key, which isn't what I need.

As a matter of fact, this is the vim script code I'm struggling to recreate:

inoremap HH ---~
inoremap HJ ~---
inoremap JJ ~---~
  • You could just use "H J" for the middle combination, to avoid a common prefix. Dec 20, 2022 at 13:47
  • Yes, but I have to make it work first, just for two characters. At present it fires right from the first character. Dec 20, 2022 at 14:09
  • Have you made H a prefix key?
    – NickD
    Dec 20, 2022 at 16:24
  • I'm sorry, I haven't yet learned the ropes. Dec 20, 2022 at 16:36
  • Well, that's probably what map! did and you didn't want that, so no loss!
    – NickD
    Dec 20, 2022 at 17:42

3 Answers 3


This might do what you're after:

(defun abbrev-or-insert ()
  (self-insert-command 1)

(define-global-abbrev "HH" "---~")
(define-global-abbrev "JJ" "~---~")
(define-global-abbrev "JH" "~---")

(global-set-key (kbd "H") 'abbrev-or-insert)
(global-set-key (kbd "J") 'abbrev-or-insert)

With this approach, you can't use three letter combos that repeat a two-letter combo. i.e., if you define HH to be one abbrev, you can't also use HHJ as a different abbrev`.

  • Thanks! There are two kinks, so far: (1) it takes a workaround, like entering hh and then uppercase it manually, to insert HH, for instance, if you really need actual HH for some reason, because it remembers the first H indefinitely; (2) it does not respect non-English characters. For me it's essential to keep the abbreviation across different layouts. Dec 21, 2022 at 14:59
  • 1) you could also to H C-Q H, to enter HH without forcing an expansion. But yes, in general you'll want to use combos that don't get used often or ever. 2) I don't know what this means, I only use an English keyboard. You should be able to add bindings for different characters in different layouts? I'm not sure how that works though
    – Tyler
    Dec 21, 2022 at 15:57
  • As a matter of fact, your abbrev solution accepts non-english characters. It's the key-chord approach, otherwise very satisfactory, that's stubborn :) Dec 21, 2022 at 16:22
  • There's a wrinkle: ---~---~aaaHH ---~---~aaaHH ---~. After any input it takes a space to put things back to rights. Dec 22, 2022 at 8:58
  • Will you please have a look at the above comment? Dec 24, 2022 at 6:15

This solution meets all my requirements. The discussion on reddit. Special thanks to Tyler, who prompted me in the right direction.

;; Instant abbreviations for LaTeX,
;; English and Cyrillic.
;; In case no expansion is needed, example: (H C-S-q H) results in HH.
(defun abbrev-or-insert ()
  (self-insert-command 1)

(add-hook 'post-self-insert-hook #'expand-abbrev nil t)

(define-abbrev-table 'global-abbrev-table
    ("HH" "---~" nil :case-fixed nil :count 0)
    ("РР" "---~" nil :case-fixed nil :count 0)
    ("HJ" "~---" nil :case-fixed nil :count 0)
    ("РО" "~---" nil :case-fixed nil :count 0)
    ("JJ" "~---~" nil :case-fixed nil :count 0)
    ("ОО" "~---~" nil :case-fixed nil :count 0)
    ("LL" "\\ldots{}" nil :case-fixed t :count 0)
    ("ДД" "\\ldots{}" nil :case-fixed t :count 0)
  :regexp (regexp-opt '("HH" "РР" "HJ" "РО" "JJ" "ОО" "LL" "ДД") t))

(global-set-key (kbd "H") 'abbrev-or-insert)
(global-set-key (kbd "Р") 'abbrev-or-insert)
(global-set-key (kbd "J") 'abbrev-or-insert)
(global-set-key (kbd "О") 'abbrev-or-insert)
(global-set-key (kbd "L") 'abbrev-or-insert)
(global-set-key (kbd "Д") 'abbrev-or-insert)
  • Actually it works without (add-hook 'post-self-insert-hook #'expand-abbrev nil t) just fine. Maybe the innards of Doom Emacs supply it. Anybody in the know is welcome :) Dec 30, 2022 at 7:42
  • If you use abbrev-or-insert, you need the global-set-key code, but you don't need the post-self-insert-hook. On the other hand, if you use post-self-insert-hook you shouldn't need abbrev-or-insert or global-set-key
    – Tyler
    Dec 31, 2022 at 20:52
  • @Tyler Could you be more specific? It's very interesting, but hopelessly cryptic. Jan 11, 2023 at 8:57
  • You solution uses two different answers, either of which should work on its own. There's the code that comes from my answer, which should be all you need (although I don't fully understand your comment under my answer with a problem). The other approach should only require the single line (add-hook 'post-self-insert-hook #'expand-abbrev nil t), as well as the (define-abbrev-table ..). If you use the post-self-insert-hook, you shouldn't need my function or the global-set-key code.
    – Tyler
    Jan 11, 2023 at 18:19

The thing I so badly wanted is the Key Chord. The motion even has its proper name, come to think of it.

;; Mapping a pair of simultaneously pressed keys to a command or
;; mapping the same key being pressed twice in quick
;; succession to a command. Such bindings are called "key chords".
(key-chord-mode 1)
(key-chord-define-global "HH" "---~")
(key-chord-define-global "HJ" "~---")
(key-chord-define-global "JJ" "~---~")
  • Most unfortunately, it won't work with non-English characters. Dec 22, 2022 at 9:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.