You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
6.1KB

  1. ;;; fira-code.el --- Summary -*- lexical-binding: t -*-
  2. ;;; Commentary:
  3. ;;;
  4. ;;; Emacs mode for displaying Fira Code ligatures using modified
  5. ;;; version of Fira Code called Fira Emacs.
  6. ;;;
  7. ;;; Originally derived from code posted by Emmanuel Touzery
  8. ;;; at <https://emacs.stackexchange.com/a/26824>.
  9. ;;;
  10. ;;; Code:
  11. (eval-when-compile
  12. (require 'cl))
  13. (require 'dash)
  14. (load "fira-code-data")
  15. (defconst fira-code--word-ligatures
  16. (-keep
  17. (-lambda ([glyph input-string])
  18. (and (string-match-p "\\.liga$" glyph)
  19. (string-match-p "^[[:alpha:]]+$" input-string)
  20. input-string))
  21. fira-code--data)
  22. "List of ligatures that should be recognized when the occur
  23. within a word.")
  24. (defvar fira-code-enable-substitution-predicate
  25. 'fira-code--default-enable-substitution-predicate
  26. "Predicate to decide whether to enable a substitution from
  27. `fira-code--data'. The arguments are the name of the glyph and
  28. the characters to be replaced.
  29. This predicate is evaluated once for each possible substitution
  30. whenever `fira-code-mode' is activated. This predicate is
  31. generally less useful than `fira-code-compose-predicate', but it
  32. is needed in situations where Fira Code provides multiple glyphs
  33. that can be subsituted for a particular input sequence, and it
  34. can be used to optimize screen refreshes by excluding
  35. substitutions that are never desired in any context.
  36. If this function returns a string, that string will be replaced
  37. instead of the default string takenf rom `fira-code--data' This
  38. could be used, for example, to replace \"*\" rather than \"x\"
  39. with the x.multiply glyph.")
  40. (defvar fira-code-compose-predicate
  41. 'fira-code--default-compose-predicate
  42. "Predicate to decide whether a particular sequence of
  43. characters should be replaced with a prettier alternative. The
  44. arguments are the start and end positions of the characters to be
  45. replaced, plus a string containing the characters themselves.
  46. This predicate is evaluated before each string of characters is
  47. replaced with a glyph while `fira-code-mode' is active. See also
  48. `fira-code-enable-substitution-predicate'.")
  49. (defun fira-code--default-enable-substitution-predicate
  50. (name input-string)
  51. (let ((default-enabled
  52. (or
  53. ;; Enable most ligatures.
  54. (when (string-match-p ".*\\.liga$" name)
  55. (not (member name '("less_equal.liga"
  56. "greater_equal.liga"))))
  57. ;; Turn on certain alternative glyphs.
  58. (member name '("at.ss06"
  59. "less_equal.ss02"
  60. "geter_equal.ss02")))))
  61. (cond
  62. ;; Haskell-specific settings:
  63. ((derived-mode-p 'haskell-mode)
  64. (cl-case input-string
  65. ("$" t) ; use alterantive $
  66. ("/=" "!=") ; "not equal" is /=
  67. ("!=" nil) ; != is not special
  68. (t default-enabled)))
  69. (t default-enabled))))
  70. (defun fira-code--default-compose-predicate
  71. (start end input-string)
  72. (condition-case nil
  73. (and
  74. ;; Turn off composition in strings.
  75. (not (nth 3 (syntax-ppss)))
  76. ;; Prevent portions of words from being transformed. This can
  77. ;; happen with, for example, the default transformations in
  78. ;; python-mode, which replace "or" with "∨". Without this
  79. ;; check, "for" would be rendered as "f∨". As a special case,
  80. ;; input strings in fira-code--word-ligatures are allowed, since
  81. ;; they are intended to appear as parts of words.
  82. (or (not (string-match-p "^[[:alnum:]_]+$" input-string))
  83. (member input-string fira-code--word-ligatures)
  84. (condition-case nil
  85. (and (not (string-match-p
  86. "[[:alnum:]_]"
  87. (buffer-substring (1- start) start)))
  88. (not (string-match-p
  89. "[[:alnum:]_]"
  90. (buffer-substring end (1+ end)))))
  91. (args-out-of-range nil)))
  92. ;; Prevent long sequences of repeating characters from being
  93. ;; turned into a weird combination of ligatures, such as when a
  94. ;; long line of = characters appears in a comment.
  95. (condition-case nil
  96. (not (or (equal input-string
  97. (buffer-substring (1- start) (1- end)))
  98. (equal input-string
  99. (buffer-substring (1+ start) (1+ end)))))
  100. (args-out-of-range t)))))
  101. (defun fira-code--make-alist (list)
  102. "Generate prettify-symbols alist from LIST."
  103. (-keep
  104. (-lambda ([name input-string output-string])
  105. (let ((pred-result
  106. (funcall fira-code-enable-substitution-predicate
  107. name input-string)))
  108. (when pred-result
  109. (when (stringp pred-result)
  110. (setq input-string pred-result))
  111. (cons input-string
  112. (append '(?\s (Br . Br))
  113. (cl-loop for n
  114. from 2
  115. to (string-width input-string)
  116. append '(?\s (Br . Bl)))
  117. (list (aref output-string 0)))))))
  118. list))
  119. (defvar-local fira-code--old-prettify-alist nil)
  120. (defun fira-code--prettify-symbols-compose-predicate (start end input-string)
  121. (funcall fira-code-compose-predicate start end input-string))
  122. (defun fira-code--enable ()
  123. "Enable Fira Code ligatures in current buffer."
  124. (setq-local fira-code--old-prettify-alist prettify-symbols-alist)
  125. (setq-local prettify-symbols-alist (append (fira-code--make-alist fira-code--data) fira-code--old-prettify-alist))
  126. (setq-local prettify-symbols-compose-predicate
  127. 'fira-code--prettify-symbols-compose-predicate)
  128. (prettify-symbols-mode t))
  129. (defun fira-code--disable ()
  130. "Disable Fira Code ligatures in current buffer."
  131. (setq-local prettify-symbols-alist fira-code--old-prettify-alist)
  132. (kill-local-variable 'prettify-symbols-compose-predicate)
  133. (prettify-symbols-mode -1))
  134. (define-minor-mode fira-code-mode
  135. "Fira Code ligatures minor mode"
  136. :lighter " "
  137. (setq-local prettify-symbols-unprettify-at-point 'right-edge)
  138. (if fira-code-mode
  139. (fira-code--enable)
  140. (fira-code--disable)))
  141. (provide 'fira-code)