Include ligature.el in a new set-font-ligatures! function, so that "normal" (read: "font-based") ligatures can also be controlled on a per-major mode basis from a user function in configuration. This commit also drops support for Emacs 27 to reduce the maintenance burden. BREAKING CHANGE: font ligatures for Harfbuzz/Coretext composition table-based ligations are no longer controlled with `+ligatures-composition-alist`, but is handled with `+ligatures-prog-mode-list` and `+ligatures-all-modes-list` for most common cases. See the README for the mode-specific methods BREAKING CHANGE: the `:ui ligatures` module will not work anymore with Emacs 27 or older. Also, there is no need to keep patched fonts (for Fira, Hasklig, Iosevka) if you use the module. Update Emacs if you want to keep using ligatures, or disable the module (`doom doctor` will tell you if your current version of Emacs stopped working with the module)
180 lines
6.9 KiB
EmacsLisp
180 lines
6.9 KiB
EmacsLisp
;;; ui/ligatures/config.el -*- lexical-binding: t; -*-
|
||
|
||
(defvar +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 "" ;; FIXME: find a non-private char
|
||
:dot "•")
|
||
"Maps identifiers to symbols, recognized by `set-ligatures'.
|
||
|
||
This should not contain any symbols from the Unicode Private Area! There is no
|
||
universal way of getting the correct symbol as that area varies from font to
|
||
font.")
|
||
|
||
(defvar +ligatures-extra-alist '((t))
|
||
"A map of major modes to symbol lists (for `prettify-symbols-alist').")
|
||
|
||
(defvar +ligatures-prog-mode-list
|
||
'("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
|
||
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
|
||
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
|
||
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
|
||
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
|
||
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
|
||
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
|
||
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
|
||
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
|
||
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
|
||
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
|
||
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
|
||
"\\\\" "://")
|
||
"A list of ligatures to enable in all `prog-mode' buffers.")
|
||
|
||
(defvar +ligatures-all-modes-list
|
||
'()
|
||
"A list of ligatures to enable in all buffers.")
|
||
|
||
(defvar +ligatures-in-modes
|
||
'(not special-mode comint-mode eshell-mode term-mode vterm-mode Info-mode
|
||
elfeed-search-mode elfeed-show-mode)
|
||
"List of major modes where ligatures should be enabled.
|
||
|
||
If t, enable it everywhere (except `fundamental-mode').
|
||
If the first element is 'not, enable it in any mode besides what is listed.
|
||
If nil, don't enable ligatures anywhere.")
|
||
|
||
(defvar +ligatures-extras-in-modes t
|
||
"List of major modes where extra ligatures should be enabled.
|
||
|
||
Extra ligatures are mode-specific substituions, defined in
|
||
`+ligatures-extra-symbols' and assigned with `set-ligatures!'. This variable
|
||
controls where these are enabled.
|
||
|
||
If t, enable it everywhere (except `fundamental-mode').
|
||
If the first element is 'not, enable it in any mode besides what is listed.
|
||
If nil, don't enable these extra ligatures anywhere (though it's more
|
||
efficient to remove the `+extra' flag from the :ui ligatures module instead).")
|
||
|
||
(defvar +ligatures--init-font-hook nil)
|
||
|
||
(defun +ligatures--correct-symbol-bounds (ligature-alist)
|
||
"Prepend non-breaking spaces to a ligature.
|
||
|
||
This way `compose-region' (called by `prettify-symbols-mode') will use the
|
||
correct width of the symbols instead of the width measured by `char-width'."
|
||
(let ((len (length (car ligature-alist)))
|
||
(acc (list (cdr ligature-alist))))
|
||
(while (> len 1)
|
||
(setq acc (cons #X00a0 (cons '(Br . Bl) acc))
|
||
len (1- len)))
|
||
(cons (car ligature-alist) acc)))
|
||
|
||
(defun +ligatures--enable-p (modes)
|
||
"Return t if ligatures should be enabled in this buffer depending on MODES."
|
||
(unless (eq major-mode 'fundamental-mode)
|
||
(or (eq modes t)
|
||
(if (eq (car modes) 'not)
|
||
(not (apply #'derived-mode-p (cdr modes)))
|
||
(apply #'derived-mode-p modes)))))
|
||
|
||
(defun +ligatures-init-buffer-h ()
|
||
"Set up ligatures for the current buffer.
|
||
|
||
Extra ligatures are mode-specific substituions, defined in
|
||
`+ligatures-extra-symbols', assigned with `set-ligatures!', and made possible
|
||
with `prettify-symbols-mode'. This variable controls where these are enabled.
|
||
See `+ligatures-extras-in-modes' to control what major modes this function can
|
||
and cannot run in."
|
||
(when after-init-time
|
||
(let ((in-mode-p
|
||
(+ligatures--enable-p +ligatures-in-modes))
|
||
(in-mode-extras-p
|
||
(and (modulep! +extra)
|
||
(+ligatures--enable-p +ligatures-extras-in-modes))))
|
||
(when in-mode-p
|
||
;; If ligature-mode has been installed, there's no
|
||
;; need to do anything, we activate global-ligature-mode
|
||
;; later and handle all settings from `set-ligatures!' later.
|
||
(unless (fboundp #'ligature-mode-turn-on)
|
||
(run-hooks '+ligatures--init-font-hook)
|
||
(setq +ligatures--init-font-hook nil)))
|
||
(when in-mode-extras-p
|
||
(prependq! prettify-symbols-alist
|
||
(alist-get major-mode +ligatures-extra-alist)))
|
||
(when (and (or in-mode-p in-mode-extras-p)
|
||
prettify-symbols-alist)
|
||
(when prettify-symbols-mode
|
||
(prettify-symbols-mode -1))
|
||
(prettify-symbols-mode +1)))))
|
||
|
||
|
||
;;
|
||
;;; Bootstrap
|
||
|
||
;;;###package prettify-symbols
|
||
;; When you get to the right edge, it goes back to how it normally prints
|
||
(setq prettify-symbols-unprettify-at-point 'right-edge)
|
||
|
||
(add-hook! 'doom-init-ui-hook :append
|
||
(defun +ligatures-init-h ()
|
||
(add-hook 'after-change-major-mode-hook #'+ligatures-init-buffer-h)))
|
||
|
||
(cond
|
||
;; The emacs-mac build of Emacs appears to have built-in support for ligatures,
|
||
;; using the same composition-function-table method
|
||
;; https://bitbucket.org/mituharu/emacs-mac/src/26c8fd9920db9d34ae8f78bceaec714230824dac/lisp/term/mac-win.el?at=master#lines-345:805
|
||
;; so use that instead if this module is enabled.
|
||
((and IS-MAC (fboundp 'mac-auto-operator-composition-mode))
|
||
(add-hook 'doom-init-ui-hook #'mac-auto-operator-composition-mode 'append))
|
||
|
||
;; NOTE: the module does not support Emacs 27 and less, but if we still try to enable ligatures,
|
||
;; it will end up in catastrophic work-loss errors, so we leave the check here for safety.
|
||
((and (> emacs-major-version 27)
|
||
(or (featurep 'ns)
|
||
(string-match-p "HARFBUZZ" system-configuration-features))
|
||
(featurep 'composite)) ; Emacs loads `composite' at startup
|
||
|
||
(use-package! ligature
|
||
:config
|
||
;; Enable all `+ligatures-prog-mode-list' ligatures in programming modes
|
||
(ligature-set-ligatures 'prog-mode +ligatures-prog-mode-list)
|
||
(ligature-set-ligatures 't +ligatures-all-modes-list))
|
||
|
||
(add-hook! 'doom-init-ui-hook :append
|
||
(defun +ligature-enable-globally-h ()
|
||
"Enables ligature checks globally in all buffers.
|
||
You can also do it per mode with `ligature-mode'."
|
||
(global-ligature-mode t)))))
|