|
- ;;; fira-code.el --- Summary -*- lexical-binding: t -*-
- ;;; Commentary:
- ;;;
- ;;; Emacs mode for displaying Fira Code ligatures using modified
- ;;; version of Fira Code called Fira Emacs.
- ;;;
- ;;; Originally derived from code posted by Emmanuel Touzery
- ;;; at <https://emacs.stackexchange.com/a/26824>.
- ;;;
- ;;; Code:
-
- (eval-when-compile
- (require 'cl))
- (require 'dash)
-
- (load "fira-code-data")
-
- (defconst fira-code--word-ligatures
- (-keep
- (-lambda ([glyph input-string])
- (and (string-match-p "\\.liga$" glyph)
- (string-match-p "^[[:alpha:]]+$" input-string)
- input-string))
- fira-code--data)
- "List of ligatures that should be recognized when the occur
- within a word.")
-
- (defvar fira-code-enable-substitution-predicate
- 'fira-code--default-enable-substitution-predicate
- "Predicate to decide whether to enable a substitution from
- `fira-code--data'. The arguments are the name of the glyph and
- the characters to be replaced.
-
- This predicate is evaluated once for each possible substitution
- whenever `fira-code-mode' is activated. This predicate is
- generally less useful than `fira-code-compose-predicate', but it
- is needed in situations where Fira Code provides multiple glyphs
- that can be subsituted for a particular input sequence, and it
- can be used to optimize screen refreshes by excluding
- substitutions that are never desired in any context.
-
- If this function returns a string, that string will be replaced
- instead of the default string takenf rom `fira-code--data' This
- could be used, for example, to replace \"*\" rather than \"x\"
- with the x.multiply glyph.")
-
- (defvar fira-code-compose-predicate
- 'fira-code--default-compose-predicate
- "Predicate to decide whether a particular sequence of
- characters should be replaced with a prettier alternative. The
- arguments are the start and end positions of the characters to be
- replaced, plus a string containing the characters themselves.
-
- This predicate is evaluated before each string of characters is
- replaced with a glyph while `fira-code-mode' is active. See also
- `fira-code-enable-substitution-predicate'.")
-
- (defun fira-code--default-enable-substitution-predicate
- (name input-string)
- (let ((default-enabled
- (or
- ;; Enable most ligatures.
- (when (string-match-p ".*\\.liga$" name)
- (not (member name '("less_equal.liga"
- "greater_equal.liga"))))
-
- ;; Turn on certain alternative glyphs.
- (member name '("at.ss06"
- "less_equal.ss02"
- "geter_equal.ss02")))))
- (cond
- ;; Haskell-specific settings:
- ((derived-mode-p 'haskell-mode)
- (cl-case input-string
- ("$" t) ; use alterantive $
- ("/=" "!=") ; "not equal" is /=
- ("!=" nil) ; != is not special
- (t default-enabled)))
- (t default-enabled))))
-
- (defun fira-code--default-compose-predicate
- (start end input-string)
- (condition-case nil
- (and
- ;; Turn off composition in strings.
- (not (nth 3 (syntax-ppss)))
-
- ;; Prevent portions of words from being transformed. This can
- ;; happen with, for example, the default transformations in
- ;; python-mode, which replace "or" with "∨". Without this
- ;; check, "for" would be rendered as "f∨". As a special case,
- ;; input strings in fira-code--word-ligatures are allowed, since
- ;; they are intended to appear as parts of words.
- (or (not (string-match-p "^[[:alnum:]_]+$" input-string))
- (member input-string fira-code--word-ligatures)
- (condition-case nil
- (and (not (string-match-p
- "[[:alnum:]_]"
- (buffer-substring (1- start) start)))
- (not (string-match-p
- "[[:alnum:]_]"
- (buffer-substring end (1+ end)))))
- (args-out-of-range nil)))
-
- ;; Prevent long sequences of repeating characters from being
- ;; turned into a weird combination of ligatures, such as when a
- ;; long line of = characters appears in a comment.
- (condition-case nil
- (not (or (equal input-string
- (buffer-substring (1- start) (1- end)))
- (equal input-string
- (buffer-substring (1+ start) (1+ end)))))
- (args-out-of-range t)))))
-
- (defun fira-code--make-alist (list)
- "Generate prettify-symbols alist from LIST."
- (-keep
- (-lambda ([name input-string output-string])
- (let ((pred-result
- (funcall fira-code-enable-substitution-predicate
- name input-string)))
- (when pred-result
- (when (stringp pred-result)
- (setq input-string pred-result))
- (cons input-string
- (append '(?\s (Br . Br))
- (cl-loop for n
- from 2
- to (string-width input-string)
- append '(?\s (Br . Bl)))
- (list (aref output-string 0)))))))
- list))
-
- (defvar-local fira-code--old-prettify-alist nil)
-
- (defun fira-code--prettify-symbols-compose-predicate (start end input-string)
- (funcall fira-code-compose-predicate start end input-string))
-
- (defun fira-code--enable ()
- "Enable Fira Code ligatures in current buffer."
- (setq-local fira-code--old-prettify-alist prettify-symbols-alist)
- (setq-local prettify-symbols-alist (append (fira-code--make-alist fira-code--data) fira-code--old-prettify-alist))
- (setq-local prettify-symbols-compose-predicate
- 'fira-code--prettify-symbols-compose-predicate)
- (prettify-symbols-mode t))
-
- (defun fira-code--disable ()
- "Disable Fira Code ligatures in current buffer."
- (setq-local prettify-symbols-alist fira-code--old-prettify-alist)
- (kill-local-variable 'prettify-symbols-compose-predicate)
- (prettify-symbols-mode -1))
-
- (define-minor-mode fira-code-mode
- "Fira Code ligatures minor mode"
- :lighter " "
- (setq-local prettify-symbols-unprettify-at-point 'right-edge)
- (if fira-code-mode
- (fira-code--enable)
- (fira-code--disable)))
-
- (provide 'fira-code)
|