doom-emacs/modules/ui/ligatures
Gerry Agbobada ad6caabfb8
fix(ligatures): proper resetting of font-ligatures
resetting font-ligatures means passing `nil` as the second argument
of `(set-font-ligatures!)`

Using `ligatures-ignored-major-modes` to cancel ligatures is too
brittle, because "resetting ligatures for `prog-mode`" would have bad
semantics with all its derived modes. The user probably just wants to
remove the default ligatures and then configure `foo-mode`, but pushing
`prog-mode` to `ignored-major-modes` might just disable ligatures across
all modes _derived from_ `prog-mode`.

This commit changes things in 2 ways:
- resetting ligatures now directly manipulates ligature.el internal
  state (the `ligature-composition-table` alist)
- in order to work, Doom must maintain an extra invariant on that alist,
  multi-modes keys (`'(foo-mode bar-mode)`), cannot be used, only single
  modes.

That mostly means that users should _not_ use
`ligature-set-ligatures` themselves in private config, but instead
always rely on `set-font-ligatures!` which does splicing behind
curtains. Failing to do so would be mostly harmless though (it would
just make "resetting ligatures" only partially remove set ligatures).

Fix: #7433
2024-05-21 17:16:14 +02:00
..
autoload
config.el
doctor.el
packages.el
README.org

:ui ligatures

Description   unfold

Installation

Enable this module in your doom! block.

This module requires one of three setups for ligatures to work:

  • A recent enough version of Emacs which will compose ligatures automatically (Emacs 28 with Harfbuzz support), or
  • Mitsuharu's emacs-mac build on macOS (available on Homebrew), or
  • A patched font for Doom's fallback ligature support. This module does not have specific installation instructions doom doctor will tell you if the module is incompatible with your current Emacs version, and what you can do to remediate.

Usage

󱌣 This module's usage documentation is incomplete. Complete it?

Mathematical symbols replacement

If you want to set symbol replacements for modules that don't have them by default you can use the set-ligatures! function in your config.el file

(after! PACKAGE
  (set-ligatures! 'MAJOR-MODE
    :symbol "keyword"))

E.g.

(after! go-mode ; in this case the major mode and package named the same thing
  (set-ligatures! 'go-mode
    :def "func" ; function keyword
    :true "true" :false "false"
    ; this will replace not only definitions
    ; but coresponding functions aswell
    :int "int" :str "string"
    :float "float" :bool "bool"
    :for "for"
    :return "return" :yield "yield"))

You can set these symbols out of the box:

(set-ligatures! 'MAJOR-MODE
    ;; Functional
    :lambda        "lambda keyword"
    :def           "function keyword"
    :composition   "composition"
    :map           "map/dictionary keyword"
    ;; Types
    :null          "null type"
    :true          "true keyword"
    :false         "false keyword"
    :int           "int keyword"
    :float         "float keyword"
    :str           "string keyword"
    :bool          "boolean keyword"
    :list          "list keyword"
    ;; Flow
    :not           "not operator"
    :in            "in operator"
    :not-in        "not in operator"
    :and           "and keyword"
    :or            "or keyword"
    :for           "for keyword"
    :some          "some keyword"
    :return        "return"
    :yield         "yeild"
    ;; Other
    :union         "Union keyword"
    :intersect     "Intersect keyword"
    :diff          "diff keyword"
    :tuple         "Tuple Keyword "
    :pipe          "Pipe Keyword"
    :dot           "Dot operator")

If you have multiple versions of the same keyword you can set the symbol twice:

(set-ligatures! scala-mode
  :null "none"
  :null "None")

Coding ligatures

This module includes configuration to compose combinations like -> or :: into prettier glyphs (called a ligature), specific for your font, or specific for the major modes that you want to use.

As these ligatures come from the font itself instead of elisp symbols, we use set-font-ligatures!

(set-font-ligatures! '(haskell-mode clojure-mode) ">>=" ">>-")

Details

Ligatures are implemented using a composition-function-table method: regexps are used to match all the usual sequences which are composed into ligatures. These regexps are passed to emacs directly, which asks Harfbuzz to shape it. Ligatures are obtained automatically depending on the capabilities of the font, and no font-specific configuration is necessary.

Emacs-mac port implements the same method natively in its code, nothing is necessary on Doom side; otherwise, Doom uses the ligature.el package that implements this method for Emacs 28+ built with Harfbuzz support. Therefore, the module will not work with Emacs 27 or previous.

Even though harfbuzz has been included in emacs 27, there is currently a bug (#40864) which prevents a safe usage of the composition-function-table method in Emacs 27.

Configuration

Symbol replacements (λ for "lambda"…)

if you don't like the symbols chosen you can change them by using:

;; you don't need to include all of them you can pick and mix
(plist-put! +ligatures-extra-symbols
  ;; org
  :name          "»"
  :src_block     "»"
  :src_block_end "«"
  :quote         "“"
  :quote_end     "”"
  ;; Functional
  :lambda        "λ"
  :def           "ƒ"
  :composition   "∘"
  :map           "↦"
  ;; Types
  :null          "∅"
  :true          "𝕋"
  :false         "𝔽"
  :int           ""
  :float         ""
  :str           "𝕊"
  :bool          "𝔹"
  :list          "𝕃"
  ;; Flow
  :not           "¬"
  :in            "∈"
  :not-in        "∉"
  :and           "∧"
  :or            ""
  :for           "∀"
  :some          "∃"
  :return        "⟼"
  :yield         "⟻"
  ;; Other
  :union         ""
  :intersect     "∩"
  :diff          ""
  :tuple         "⨂"
  :pipe          ""
  :dot           "•")  ;; you could also add your own if you want

Font ligatures (turning "=>" into an arrow…)

Setting ligatures for specific font or major mode

As the README for ligature.el states, you can manipulate the ligatures that you want to enable, specific for your font, or specific for the major modes that you want to use. set-font-ligatures! is a thin wrapper around ligature.el to control these.

(set-font-ligatures! '(haskell-mode clojure-mode) ">>=" ">>-")

This call will:

  • overwrite all preceding calls to set-font-ligatures! for haskell-mode and clojure-mode specifically, but
  • keep the inheritance to ligatures set for all modes, or parent modes like prog-mode

Overwriting all default ligatures

If you want to "start from scratch" and get control over all ligatures that happen in all modes, you can use

;; Set all your custom ligatures for all prog-modes here
;; This section is *out of* the after! block
;; Example: only get ligatures for "==" and "===" in programming modes
;; by default, and get only "www" in all buffers by default.
(setq +ligatures-prog-mode-list '("==" "===")
      +ligatures-all-modes-list '("www"))
;; Set any of those variables to nil to wipe all defaults.

  ;; Set all your additional custom ligatures for other major modes here.
  ;; Example: enable traditional ligature support in eww-mode, if the
  ;; `variable-pitch' face supports it
(set-font-ligatures! 'eww-mode "ff" "fi" "ffi")

Troubleshooting

Report an issue?

Some symbols are not rendering correctly

This can usually be fixed by doing one of the following:

  • Make sure Symbola (the font) is installed on your system.
  • Otherwise, change var:doom-unicode-font (set to Symbola by default).
  • Disable the doom-module::ui unicode module. It not only overrides var:doom-unicode-font, but should only be used as a last resort.

Frequently asked questions

This module has no FAQs yet. Ask one?

TODO Appendix

󱌣 This module has no appendix yet. Write one?