calt.clj 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. (def priorities
  31. {;; <|>
  32. ["less" "bar" "greater"] 0
  33. ;; |||> ||> |> <| <|| <|||
  34. ["bar" "bar" "bar" "greater"] 1
  35. ["bar" "bar" "greater"] 1
  36. ["bar" "greater"] 1
  37. ["less" "bar" "bar" "bar"] 1
  38. ["less" "bar" "bar"] 1
  39. ["less" "bar"] 1
  40. ;; #346 We need << <<< >> >>> || ||| substituted before -- --- == ===
  41. ;; so that `ignore [less greater bar] hyphen hyphen` would not trigger
  42. ["less" "less"] 2
  43. ["less" "less" "less"] 2
  44. ["greater" "greater"] 2
  45. ["greater" "greater" "greater"] 2
  46. ["bar" "bar"] 2
  47. ["bar" "bar" "bar"] 2})
  48. (def ignores
  49. (coll/multimap-by str
  50. ["slash" "asterisk"]
  51. (str
  52. " ignore sub slash' asterisk slash;\n"
  53. " ignore sub asterisk slash' asterisk;\n")
  54. ["asterisk" "slash"]
  55. (str
  56. " ignore sub slash asterisk' slash;\n"
  57. " ignore sub asterisk' slash asterisk;\n")
  58. ["asterisk" "asterisk"]
  59. (str
  60. " ignore sub slash asterisk' asterisk;\n"
  61. " ignore sub asterisk' asterisk slash;\n")
  62. ["asterisk" "asterisk" "asterisk"]
  63. (str
  64. " ignore sub slash asterisk' asterisk asterisk;\n"
  65. " ignore sub asterisk' asterisk asterisk slash;\n")
  66. ;; #1061
  67. ["colon" "colon"]
  68. (str " ignore sub colon' colon [less greater];\n"
  69. " ignore sub [less greater] colon' colon;\n")
  70. ["colon" "colon" "colon"]
  71. (str " ignore sub colon' colon colon [less greater];\n"
  72. " ignore sub [less greater] colon' colon colon;\n")
  73. ;; #621 <||>
  74. ["less" "bar" "bar"]
  75. " ignore sub less' bar bar greater;\n"
  76. ["bar" "bar" "greater"]
  77. " ignore sub less bar' bar greater;\n"
  78. ;; #593 {|}
  79. ["braceleft" "bar"]
  80. " ignore sub braceleft' bar braceright;\n"
  81. ["bar" "braceright"]
  82. " ignore sub braceleft bar' braceright;\n"
  83. ;; #593 [|]
  84. ["bracketleft" "bar"]
  85. " ignore sub bracketleft' bar bracketright;\n"
  86. ["bar" "bracketright"]
  87. " ignore sub bracketleft bar' bracketright;\n"
  88. ;; #410 <*>> <+>> <$>>
  89. ["greater" "greater"]
  90. " ignore sub [asterisk plus dollar] greater' greater;\n"
  91. ;; #410 <*>>> <+>>> <$>>>
  92. ["greater" "greater" "greater"]
  93. " ignore sub [asterisk plus dollar] greater' greater greater;\n"
  94. ;; #410 <<*> <<+> <<$>
  95. ["less" "less"]
  96. " ignore sub less' less [asterisk plus dollar];\n"
  97. ;; #410 <<<*> <<<+> <<<$>
  98. ["less" "less" "less"]
  99. " ignore sub less' less less [asterisk plus dollar];\n"
  100. ;; #948 [==[ ]==]
  101. ;; #968 [== ==]
  102. ["equal" "equal"]
  103. (str " ignore sub bracketleft equal' equal;\n"
  104. " ignore sub equal' equal bracketright;\n")
  105. ;; #948 [===[ ]===]
  106. ;; #968 [=== ===]
  107. ["equal" "equal" "equal"]
  108. (str " ignore sub bracketleft equal' equal equal;\n"
  109. " ignore sub equal' equal equal bracketright;\n")
  110. ;; #346 =:=
  111. ["colon" "equal"]
  112. " ignore sub equal colon' equal;\n"
  113. ;; #346 =!=
  114. ["exclam" "equal"]
  115. " ignore sub equal exclam' equal;\n"
  116. ;; #346 =!==
  117. ["exclam" "equal" "equal"]
  118. " ignore sub equal exclam' equal equal;\n"
  119. ;; #346 =<= <=< <=> <=| <=: <=! <=/
  120. ["less" "equal"]
  121. (str " ignore sub equal less' equal;\n"
  122. " ignore sub less' equal [less greater bar colon exclam slash];\n")
  123. ;; #548 >=<
  124. ;; #346 =>= >=> >=< >=| >=: >=! >=/
  125. ["greater" "equal"]
  126. (str " ignore sub equal greater' equal;\n"
  127. " ignore sub greater' equal [less greater bar colon exclam slash];\n")
  128. ;; #346 >>->> >>=>>
  129. ;; #974 keep >>=
  130. ["greater" "greater"]
  131. (str " ignore sub [hyphen equal] greater' greater;\n"
  132. " ignore sub greater' greater hyphen;\n"
  133. " ignore sub greater' greater equal [equal less greater bar colon exclam slash];\n")
  134. ;; #346 <<-<< <<=<<
  135. ;; #974 keep <<=
  136. ["less" "less"]
  137. (str " ignore sub [hyphen equal] less' less;\n"
  138. " ignore sub less' less hyphen;\n"
  139. " ignore sub less' less equal [equal less greater bar colon exclam slash];\n")
  140. ;; #346 ||-|| ||=||
  141. ;; #974 keep ||=
  142. ["bar" "bar"]
  143. (str " ignore sub [hyphen equal] bar' bar;\n"
  144. " ignore sub bar' bar hyphen;\n"
  145. " ignore sub bar' bar equal [equal less greater bar colon exclam slash];\n")
  146. ;; #816 //=
  147. ["slash" "slash"]
  148. (str " ignore sub equal slash' slash;\n"
  149. " ignore sub slash' slash equal;\n")
  150. ;; #346 <--> >--< |--|
  151. ["hyphen" "hyphen"]
  152. (str " ignore sub [less greater bar] hyphen' hyphen;\n"
  153. " ignore sub hyphen' hyphen [less greater bar];\n")
  154. ;; #346 <==> >==< |==| /==/ =:== =!== ==:= ==!=
  155. ["equal" "equal"]
  156. (str " ignore sub equal [colon exclam] equal' equal;\n"
  157. " ignore sub [less greater bar slash] equal' equal;\n"
  158. " ignore sub equal' equal [less greater bar slash] ;\n"
  159. " ignore sub equal' equal [colon exclam] equal;\n")
  160. ;; #346 <===> >===< |===| /===/ =:=== =!=== ===:= ===!=
  161. ["equal" "equal" "equal"]
  162. (str " ignore sub equal [colon exclam] equal' equal equal;\n"
  163. " ignore sub [less greater bar slash] equal' equal equal;\n"
  164. " ignore sub equal' equal equal [less greater bar slash];\n"
  165. " ignore sub equal' equal equal [colon exclam] equal;\n")
  166. ))
  167. ;; DO NOT generate ignores at all
  168. (def skip-ignores? #{
  169. ;; #410 <<*>> <<+>> <<$>>
  170. ["less" "asterisk" "greater"]
  171. ["less" "plus" "greater"]
  172. ["less" "dollar" "greater"]
  173. })
  174. ;; DO NOT generate ligature
  175. (def manual? #{
  176. ;; /\ \/
  177. ["slash" "backslash"]
  178. ["backslash" "slash"]
  179. })
  180. (defn liga->rule
  181. "[f f i] => { [LIG LIG i] f_f_i.liga
  182. [LIG f i] LIG
  183. [ f f i] LIG }"
  184. [liga]
  185. (case (count liga)
  186. 2 (let [[a b] liga]
  187. (str/replace
  188. (str
  189. "lookup 1_2 {\n"
  190. (when-not (skip-ignores? liga)
  191. (str " ignore sub 1 1' 2;\n"
  192. " ignore sub 1' 2 2;\n"))
  193. (gen-ignore-prefixes liga)
  194. (get ignores liga)
  195. " sub 1.spacer 2' by 1_2.liga;\n"
  196. " sub 1' 2 by 1.spacer;\n"
  197. ; "sub 1 2 by 1_2.liga;"
  198. "} 1_2;")
  199. #"\d" {"1" a "2" b}))
  200. 3 (let [[a b c] liga]
  201. (str/replace
  202. (str
  203. "lookup 1_2_3 {\n"
  204. (when-not (skip-ignores? liga)
  205. (str " ignore sub 1 1' 2 3;\n"
  206. " ignore sub 1' 2 3 3;\n"))
  207. (gen-ignore-prefixes liga)
  208. (get ignores liga)
  209. " sub 1.spacer 2.spacer 3' by 1_2_3.liga;\n"
  210. " sub 1.spacer 2' 3 by 2.spacer;\n"
  211. " sub 1' 2 3 by 1.spacer;\n"
  212. ; "sub 1 2 3 by 1_2_3.liga;"
  213. "} 1_2_3;")
  214. #"\d" {"1" a "2" b "3" c}))
  215. 4 (let [[a b c d] liga]
  216. (str/replace
  217. (str
  218. "lookup 1_2_3_4 {\n"
  219. (when-not (skip-ignores? liga)
  220. (str " ignore sub 1 1' 2 3 4;\n"
  221. " ignore sub 1' 2 3 4 4;\n"))
  222. (gen-ignore-prefixes liga)
  223. (get ignores liga)
  224. " sub 1.spacer 2.spacer 3.spacer 4' by 1_2_3_4.liga;\n"
  225. " sub 1.spacer 2.spacer 3' 4 by 3.spacer;\n"
  226. " sub 1.spacer 2' 3 4 by 2.spacer;\n"
  227. " sub 1' 2 3 4 by 1.spacer;\n"
  228. ; "sub 1 2 3 4 by 1_2_3_4.liga;"
  229. "} 1_2_3_4;")
  230. #"\d" {"1" a "2" b "3" c "4" d}))
  231. 5 (let [[a b c d e] liga]
  232. (str/replace
  233. (str
  234. "lookup 1_2_3_4_5 {\n"
  235. (when-not (skip-ignores? liga)
  236. (str " ignore sub 1 1' 2 3 4 5;\n"
  237. " ignore sub 1' 2 3 4 4 5;\n"))
  238. (gen-ignore-prefixes liga)
  239. (get ignores liga)
  240. " sub 1.spacer 2.spacer 3.spacer 4.spacer 5' by 1_2_3_4_5.liga;\n"
  241. " sub 1.spacer 2.spacer 3.spacer 4' 5 by 4.spacer;\n"
  242. " sub 1.spacer 2.spacer 3' 4 5 by 3.spacer;\n"
  243. " sub 1.spacer 2' 3 4 5 by 2.spacer;\n"
  244. " sub 1' 2 3 4 5 by 1.spacer;\n"
  245. ; "sub 1 2 3 4 5 by 1_2_3_4_5.liga;"
  246. "} 1_2_3_4_5;")
  247. #"\d" {"1" a "2" b "3" c "4" d "5" e}))
  248. ))
  249. (defn compare-ligas [l1 l2]
  250. (let [p1 (priorities l1 Long/MAX_VALUE)
  251. p2 (priorities l2 Long/MAX_VALUE)
  252. pc (compare p1 p2)
  253. c1 (count l1)
  254. c2 (count l2)
  255. cc (compare c1 c2)]
  256. (cond
  257. (not= 0 pc) pc ;; lower priority first
  258. (not= 0 cc) (- cc) ;; longer first
  259. :else (compare l1 l2)))) ;; alphabetical
  260. (defn replace-calt [font ligas]
  261. (let [ligas' (->> ligas
  262. (remove manual?)
  263. (sort compare-ligas))
  264. calt (->> ligas'
  265. (map liga->rule)
  266. (str/join "\n\n"))
  267. glyphs (map #(str (str/join "_" %) ".liga") ligas')
  268. counts (coll/group-by-to count count ligas')]
  269. (when-some [unused (not-empty (reduce dissoc ignores ligas'))]
  270. (println " WARN Unused ignores" (str/join " " (keys unused))))
  271. (when-some [unused (not-empty (reduce disj skip-ignores? ligas'))]
  272. (println " WARN Unused skip-ignores?" (str/join " " unused)))
  273. (when-some [unused (not-empty (reduce disj manual? ligas))]
  274. (println " WARN Unused manual?" (str/join " " unused)))
  275. (println " generated calt:"
  276. ; (str/join " " glyphs)
  277. (str
  278. #_"(" (get counts 2) " pairs, "
  279. (get counts 3) " triples, "
  280. (get counts 4) " quadruples, "
  281. (count ligas') " total" #_")"))
  282. (glyphs/update-code font :features "calt" (constantly calt))))