calt.clj 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. (ns fira-code.calt
  2. (:require
  3. [clojure.string :as str]
  4. [fira-code.coll :as coll]
  5. [fira-code.glyphs :as glyphs]
  6. [fira-code.time :as time]
  7. [flatland.ordered.map :refer [ordered-map]]))
  8. ;; No ligature should follow those sequences
  9. (def ignore-prefixes
  10. [["parenleft" "question" "colon"]
  11. ;; #578 #624 Regexp lookahead/lookbehind
  12. ["parenleft" "question" "equal"]
  13. ["parenleft" "question" "less" "equal"]
  14. ["parenleft" "question" "exclam"]
  15. ["parenleft" "question" "less" "exclam"]
  16. ;; #850 PHP <?=
  17. ["less" "question" "equal"]
  18. ])
  19. (defn gen-ignore-prefixes [liga]
  20. (str/join
  21. (for [prefix ignore-prefixes
  22. ;; try to match last N glyphs in `prefix` with N first in `liga`
  23. N (range (count liga) 0 -1)
  24. :when (= (take-last N prefix) (take N liga))]
  25. (str " ignore sub"
  26. " " (str/join " " (drop-last N prefix))
  27. " " (first liga) "'"
  28. " " (str/join " " (drop 1 liga))
  29. ";\n"))))
  30. ;; #346 We need << <<< >> >>> || ||| substituted before -- --- == ===
  31. ;; so that `ignore [less greater bar] hyphen hyphen` would not trigger
  32. (def priority?
  33. #{["less" "less"]
  34. ["less" "less" "less"]
  35. ["greater" "greater"]
  36. ["greater" "greater" "greater"]
  37. ["bar" "bar"]
  38. ["bar" "bar" "bar"]})
  39. (def ignores
  40. (coll/multimap-by str
  41. ["slash" "asterisk"]
  42. (str
  43. " ignore sub slash' asterisk slash;\n"
  44. " ignore sub asterisk slash' asterisk;\n")
  45. ["asterisk" "slash"]
  46. (str
  47. " ignore sub slash asterisk' slash;\n"
  48. " ignore sub asterisk' slash asterisk;\n")
  49. ["asterisk" "asterisk"]
  50. (str
  51. " ignore sub slash asterisk' asterisk;\n"
  52. " ignore sub asterisk' asterisk slash;\n")
  53. ["asterisk" "asterisk" "asterisk"]
  54. (str
  55. " ignore sub slash asterisk' asterisk asterisk;\n"
  56. " ignore sub asterisk' asterisk asterisk slash;\n")
  57. ;; #621 <||>
  58. ["less" "bar" "bar"]
  59. " ignore sub less' bar bar greater;\n"
  60. ["bar" "bar" "greater"]
  61. " ignore sub less bar' bar greater;\n"
  62. ;; #574 :>=
  63. ["colon" "greater"]
  64. " ignore sub colon' greater equal;\n"
  65. ;; #593 {|}
  66. ["braceleft" "bar"]
  67. " ignore sub braceleft' bar braceright;\n"
  68. ["bar" "braceright"]
  69. " ignore sub braceleft bar' braceright;\n"
  70. ;; #593 [|]
  71. ["bracketleft" "bar"]
  72. " ignore sub bracketleft' bar bracketright;\n"
  73. ["bar" "bracketright"]
  74. " ignore sub bracketleft bar' bracketright;\n"
  75. ;; #410 <*>> <+>> <$>>
  76. ["greater" "greater"]
  77. " ignore sub [asterisk plus dollar] greater' greater;\n"
  78. ;; #410 <*>>> <+>>> <$>>>
  79. ["greater" "greater" "greater"]
  80. " ignore sub [asterisk plus dollar] greater' greater greater;\n"
  81. ;; #410 <<*> <<+> <<$>
  82. ["less" "less"]
  83. " ignore sub less' less [asterisk plus dollar];\n"
  84. ;; #410 <<<*> <<<+> <<<$>
  85. ["less" "less" "less"]
  86. " ignore sub less' less less [asterisk plus dollar];\n"
  87. ;; #968 [== ==]
  88. ["equal" "equal"]
  89. (str " ignore sub bracketleft equal' equal;\n"
  90. " ignore sub equal' equal bracketright;\n")
  91. ;; #968 [=== ===]
  92. ["equal" "equal" "equal"]
  93. (str " ignore sub bracketleft equal' equal equal;\n"
  94. " ignore sub equal' equal equal bracketright;\n")
  95. ;; #346 =:=
  96. ["colon" "equal"]
  97. " ignore sub equal colon' equal;\n"
  98. ;; #346 =!=
  99. ["exclam" "equal"]
  100. " ignore sub equal exclam' equal;\n"
  101. ;; #346 =!==
  102. ["exclam" "equal" "equal"]
  103. " ignore sub equal exclam' equal equal;\n"
  104. ;; #346 =<= <=< <=> <=| <=: <=! <=/
  105. ["less" "equal"]
  106. (str " ignore sub equal less' equal;\n"
  107. " ignore sub less' equal [less greater bar colon exclam slash];\n")
  108. ;; #548 >=<
  109. ;; #346 =>= >=> >=< >=| >=: >=! >=/
  110. ["greater" "equal"]
  111. (str " ignore sub equal greater' equal;\n"
  112. " ignore sub greater' equal [less greater bar colon exclam slash];\n")
  113. ;; #346 >>->> >>=>>
  114. ["greater" "greater"]
  115. (str " ignore sub [hyphen equal] greater' greater;\n"
  116. " ignore sub greater' greater [hyphen equal];\n")
  117. ;; #346 <<-<< <<=<<
  118. ["less" "less"]
  119. (str " ignore sub [hyphen equal] less' less;\n"
  120. " ignore sub less' less [hyphen equal];\n")
  121. ;; #346 ||-|| ||=||
  122. ["bar" "bar"]
  123. (str " ignore sub [hyphen equal] bar' bar;\n"
  124. " ignore sub bar' bar [hyphen equal];\n")
  125. ;; #346 <--> >--< |--|
  126. ["hyphen" "hyphen"]
  127. (str " ignore sub [less greater bar] hyphen' hyphen;\n"
  128. " ignore sub hyphen' hyphen [less greater bar];\n")
  129. ;; #346 <---> >---< |---|
  130. ["hyphen" "hyphen" "hyphen"]
  131. (str " ignore sub [less greater bar] hyphen' hyphen hyphen;\n"
  132. " ignore sub hyphen' hyphen hyphen [less greater bar];\n")
  133. ;; #346 <==> >==< |==| /==/ =:== =!== ==:= ==!=
  134. ["equal" "equal"]
  135. (str " ignore sub equal [colon exclam] equal' equal;\n"
  136. " ignore sub [less greater bar slash] equal' equal;\n"
  137. " ignore sub equal' equal [less greater bar slash] ;\n"
  138. " ignore sub equal' equal [colon exclam] equal;\n")
  139. ;; #346 <===> >===< |===| /===/ =:=== =!=== ===:= ===!=
  140. ["equal" "equal" "equal"]
  141. (str " ignore sub equal [colon exclam] equal' equal equal;\n"
  142. " ignore sub [less greater bar slash] equal' equal equal;\n"
  143. " ignore sub equal' equal equal [less greater bar slash];\n"
  144. " ignore sub equal' equal equal [colon exclam] equal;\n")
  145. ))
  146. ;; DO NOT generate ignores at all
  147. (def skip-ignores? #{
  148. ;; #410 <<*>> <<+>> <<$>>
  149. ["less" "asterisk" "greater"]
  150. ["less" "plus" "greater"]
  151. ["less" "dollar" "greater"]
  152. })
  153. ;; DO NOT generate ligature
  154. (def manual? #{
  155. ;; /\ \/
  156. ["slash" "backslash"]
  157. ["backslash" "slash"]
  158. })
  159. (defn liga->rule
  160. "[f f i] => { [LIG LIG i] f_f_i.liga
  161. [LIG f i] LIG
  162. [ f f i] LIG }"
  163. [liga]
  164. (case (count liga)
  165. 2 (let [[a b] liga]
  166. (str/replace
  167. (str
  168. "lookup 1_2 {\n"
  169. (when-not (skip-ignores? liga)
  170. (str " ignore sub 1 1' 2;\n"
  171. " ignore sub 1' 2 2;\n"))
  172. (gen-ignore-prefixes liga)
  173. (get ignores liga)
  174. " sub 1.spacer 2' by 1_2.liga;\n"
  175. " sub 1' 2 by 1.spacer;\n"
  176. ; "sub 1 2 by 1_2.liga;"
  177. "} 1_2;")
  178. #"\d" {"1" a "2" b}))
  179. 3 (let [[a b c] liga]
  180. (str/replace
  181. (str
  182. "lookup 1_2_3 {\n"
  183. (when-not (skip-ignores? liga)
  184. (str " ignore sub 1 1' 2 3;\n"
  185. " ignore sub 1' 2 3 3;\n"))
  186. (gen-ignore-prefixes liga)
  187. (get ignores liga)
  188. " sub 1.spacer 2.spacer 3' by 1_2_3.liga;\n"
  189. " sub 1.spacer 2' 3 by 2.spacer;\n"
  190. " sub 1' 2 3 by 1.spacer;\n"
  191. ; "sub 1 2 3 by 1_2_3.liga;"
  192. "} 1_2_3;")
  193. #"\d" {"1" a "2" b "3" c}))
  194. 4 (let [[a b c d] liga]
  195. (str/replace
  196. (str
  197. "lookup 1_2_3_4 {\n"
  198. (when-not (skip-ignores? liga)
  199. (str " ignore sub 1 1' 2 3 4;\n"
  200. " ignore sub 1' 2 3 4 4;\n"))
  201. (gen-ignore-prefixes liga)
  202. (get ignores liga)
  203. " sub 1.spacer 2.spacer 3.spacer 4' by 1_2_3_4.liga;\n"
  204. " sub 1.spacer 2.spacer 3' 4 by 3.spacer;\n"
  205. " sub 1.spacer 2' 3 4 by 2.spacer;\n"
  206. " sub 1' 2 3 4 by 1.spacer;\n"
  207. ; "sub 1 2 3 4 by 1_2_3_4.liga;"
  208. "} 1_2_3_4;")
  209. #"\d" {"1" a "2" b "3" c "4" d}))
  210. 5 (let [[a b c d e] liga]
  211. (str/replace
  212. (str
  213. "lookup 1_2_3_4_5 {\n"
  214. (when-not (skip-ignores? liga)
  215. (str " ignore sub 1 1' 2 3 4 5;\n"
  216. " ignore sub 1' 2 3 4 4 5;\n"))
  217. (gen-ignore-prefixes liga)
  218. (get ignores liga)
  219. " sub 1.spacer 2.spacer 3.spacer 4.spacer 5' by 1_2_3_4_5.liga;\n"
  220. " sub 1.spacer 2.spacer 3.spacer 4' 5 by 4.spacer;\n"
  221. " sub 1.spacer 2.spacer 3' 4 5 by 3.spacer;\n"
  222. " sub 1.spacer 2' 3 4 5 by 2.spacer;\n"
  223. " sub 1' 2 3 4 5 by 1.spacer;\n"
  224. ; "sub 1 2 3 4 5 by 1_2_3_4_5.liga;"
  225. "} 1_2_3_4_5;")
  226. #"\d" {"1" a "2" b "3" c "4" d "5" e}))
  227. ))
  228. (defn compare-ligas [l1 l2]
  229. (cond
  230. (and (priority? l1) (not (priority? l2))) -1
  231. (and (not (priority? l1)) (priority? l2)) 1
  232. (> (count l1) (count l2)) -1
  233. (< (count l1) (count l2)) 1
  234. :else (compare l1 l2)))
  235. (defn replace-calt [font ligas]
  236. (let [ligas' (->> ligas
  237. (remove manual?)
  238. (sort compare-ligas))
  239. calt (->> ligas'
  240. (map liga->rule)
  241. (str/join "\n\n"))
  242. glyphs (map #(str (str/join "_" %) ".liga") ligas')
  243. counts (coll/group-by-to count count ligas')]
  244. (when-some [unused (not-empty (reduce dissoc ignores ligas'))]
  245. (println " WARN Unused ignores" (str/join " " (keys unused))))
  246. (when-some [unused (not-empty (reduce disj skip-ignores? ligas'))]
  247. (println " WARN Unused skip-ignores?" (str/join " " unused)))
  248. (when-some [unused (not-empty (reduce disj manual? ligas))]
  249. (println " WARN Unused manual?" (str/join " " unused)))
  250. (println " generated calt:"
  251. ; (str/join " " glyphs)
  252. (str
  253. #_"(" (get counts 2) " pairs, "
  254. (get counts 3) " triples, "
  255. (get counts 4) " quadruples, "
  256. (count ligas') " total" #_")"))
  257. (glyphs/update-code font :features "calt" (constantly calt))))