This package maps ordinary graphemes (characters) to fancy ligatures, if both your version of Emacs and the font supports it.
With this package you can control where Emacs must display ligatures. That is useful if you only want a subset of the ligatures in certain major modes, for instance, or if you want to ensure that some modes have no ligatures at all.
If you know what you're doing, you can skip to the end for an example
that works with Cascadia Code
(and most probably other fonts, too).
Support for this feature is new. You must meet a number of requirements to ensure the package works correctly:
You must use Emacs 28 or later, or backport a fix to Emacs 27.x;
You can check by typing
M-x emacs-version
.NOTE: There are critical issues in Emacs 27.1 and 27.2. Ideally, if at all possible, you should attempt to use a build of Emacs that includes this fix. See below for details.
Your Emacs must be built with Harfbuzz enabled -- this is the default as of Emacs 27.1, but obscure platforms may not support it;
You can check by typing
C-h v system-configuration-features
. Search for the wordHARFBUZZ
.You must have a font that supports the particular typographical ligature you wish to display. Emacs should skip the ones it does not recognize, however;
Common programming fonts include Cascadia Code, Fira Code, Iosevka, and JetBrains Mono.
For variable width fonts, the world is your oyster.
Ideally, your Emacs is built with Cairo support. Without it, you may experience issues;
You can check by typing
C-h v cairo-version-string
. If you cannot find it, you probably don't have it built: you can double check by looking atsystem-configuration-features
-- see above.Older versions of Cairo apparently have some issues.
cairo-version-string
should say "1.16.0" or later.See above. It may work perfectly fine with a lower version, however.
If you are using a release build of Emacs 27.x then you may experience hangs or crashes with the following message:
Attempt to shape unibyte text
The source of the fix is this commit, but it did not make it into Emacs 27.1 or 27.2, unfortunately.
However, if you built Emacs off the master
branch then you most certainly have the fix already.
Unlike almost all text editors that support ligatures, you are free to choose which ligatures you want and which modes they apply to. That is rather important as you may only want some ligatures in certain modes, and perhaps none at all in other modes. With this package you can freely pick and choose.
You can copy and paste the example snippet near the end. It'll give you basic support for Cascadia Code, but many of the ligations are similar across fonts. You will most likely have to amend this sample if you want fancier features. Check the Wiki for other font configurations.
Fonts are often built with stylistic sets. They're also called character variants among other names. Typically, a stylistic set is a set of changes to apply to a font --- such as ligatures. They can also be things like alternative characters (such as a slashed zero.) Usually, they're known by their cryptic shorthand names: ss01, ss03, etc.
Emacs does not support stylistic sets. If you have a feature that you want to enable, and it's gated behind a stylistic set, then you're (probably) out of luck. You cannot manipulate, from the elisp side anyway, the stylistic sets to use in the harfbuzz / Emacs display engine. It works for ligatures as this package works around that limitation by exploiting a requirement that some east-Asian languages rely on ligature-like grapheme clusters to render text. So, if you want other font features enabled, you probably can't.
One workaround is to rebuild the font with the feature(s) you want enabled by default. It's quick and painless. The Font Tools OpenType Feature Freezer can do just that. Give that a try if you want specific stylistic sets enabled by default.
This is very easy to do, but do check if someone's done the work for you first on the Wiki. You can create ligations with the function ligature-set-ligatures
.
You must also enable M-x ligature-mode
in the mode(s) you want it to apply to. The ligations are disabled if you turn off this minor mode, and you can enable it globally with M-x global-ligature-mode
.
You must have the list of ligations you want Emacs to ligate. For instance, ==>
to turn into an arrow, for example. You also need the list of major mode(s) you want it apply to; or, you can tell the ligation engine to apply it everywhere. You can make as many calls to ligature-set-ligatures
as you like.
Here is a very simple example that enables simple HTML ligations for web-related major modes using the string notation to create ligations
(ligature-set-ligatures '(html-mode nxml-mode web-mode) '("<!--" "-->" "</>" "</" "/>" "://"))
When you evaluate the form the change should take effect immediately in html-mode
, nxml-mode
, and web-mode
. Occasionally, you may have to "reload" the configuration in a major mode. This is usually only required if you are experimenting. Simply toggle M-x ligature-mode
or M-x global-ligature-mode
.
You can also supply t
in lieu of a list of major modes. Any ligations registered with t
will have their ligations applied everywhere in Emacs.
Some fonts support variable-length ligations, such as headings or arrows. The usual string notation used above is not always enough. You can build your own regular expressions and the ligation engine will try -- emphasis try -- to combine your custom regular expressions with any existing string notations that may already exist.
To use the regular expression syntax you can add forms of (STR-CHAR . REGEXP)
, like so
(ligature-set-ligatures 'markdown-mode '(("=" (rx (+ "=") (? (| ">" "<"))))
("-" (rx (+ "-")))))
This creates two ligation mappings: one for ligations beginning with =
and the other for -
. You must give the starting character of a ligation so Emacs's composition engine knows how to compose the beginning of a ligature. The second part of the form is an rx
macro call that defines the regular expression. In this case it will match any length of =
followed by an optional <
or >
to add arrow support.
NOTE: You can find complete examples for Fira and Cascadia Code in the Wiki.
If you are experimenting and you want to clear all existing compositions, you can do so with this command
(setq ligature-composition-table nil)
You can also view the variable's contents if you want to see how the ligation tool works. You can even edit it manually (see Technical Details
below.)
Each buffer you want the ligatures to apply to require a call to ligature-generate-ligatures
. That command will check against a table of registered ligatures if the current buffer's major mode has any associated ligatures and, if it does, what they are. The command will check against anything that may be considered a valid parent of your buffer's major mode: for instance, a lot of programming major modes inherit from prog-mode
, so assigning ligatures to that major mode is a good way to ensure they work in most programming modes.
To create a ligature mapping you can either update the alist ligature-composition-table
directly or use the helper function ligature-set-ligatures
. I recommend you start with the latter helper function and only modify the table if you have complex requirements.
The package is available here or through MELPA.
These example snippets enables all ligatures for prog-mode
and any
major mode that derives from that mode; that is usually most
programming-related modes.
You can find more font configurations in the Wiki
If you are looking for a complete set of ligatures for both Fira or Cascadia Code, then visit the Wiki for a complete example.
If you use another font, you may find that many of the ligatures still work.
This snippet is designed for the Cascadia Code font; you may find it won't work 100% if you use a different one.
Please visit the Wiki for complete examples for many other popular fonts.
;; This assumes you've installed the package via MELPA.
(use-package ligature
:config
;; Enable the "www" ligature in every possible major mode
(ligature-set-ligatures 't '("www"))
;; Enable traditional ligature support in eww-mode, if the
;; `variable-pitch' face supports it
(ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
;; Enable all Cascadia Code ligatures in programming modes
(ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
"\\\\" "://"))
;; Enables ligature checks globally in all buffers. You can also do it
;; per mode with `ligature-mode'.
(global-ligature-mode t))
NOTE: :load-path "/path/to/ligature.el/" is required if you're installing the package using git directly.
I'm glad you asked. Yes, please. If you want to configure ligatures for common programming fonts not already listed here, please raise a github issue, but do check if the Wiki has what you need already.