calt.clj 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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. ["greater" "greater"]
  130. (str " ignore sub [hyphen equal] greater' greater;\n"
  131. " ignore sub greater' greater [hyphen equal];\n")
  132. ;; #346 <<-<< <<=<<
  133. ["less" "less"]
  134. (str " ignore sub [hyphen equal] less' less;\n"
  135. " ignore sub less' less [hyphen equal];\n")
  136. ;; #346 ||-|| ||=||
  137. ["bar" "bar"]
  138. (str " ignore sub [hyphen equal] bar' bar;\n"
  139. " ignore sub bar' bar [hyphen equal];\n")
  140. ;; #816 //=
  141. ["slash" "slash"]
  142. (str " ignore sub equal slash' slash;\n"
  143. " ignore sub slash' slash equal;\n")
  144. ;; #346 <--> >--< |--|
  145. ["hyphen" "hyphen"]
  146. (str " ignore sub [less greater bar] hyphen' hyphen;\n"
  147. " ignore sub hyphen' hyphen [less greater bar];\n")
  148. ;; #346 <---> >---< |---|
  149. ["hyphen" "hyphen" "hyphen"]
  150. (str " ignore sub [less greater bar] hyphen' hyphen hyphen;\n"
  151. " ignore sub hyphen' hyphen hyphen [less greater bar];\n")
  152. ;; #346 <==> >==< |==| /==/ =:== =!== ==:= ==!=
  153. ["equal" "equal"]
  154. (str " ignore sub equal [colon exclam] equal' equal;\n"
  155. " ignore sub [less greater bar slash] equal' equal;\n"
  156. " ignore sub equal' equal [less greater bar slash] ;\n"
  157. " ignore sub equal' equal [colon exclam] equal;\n")
  158. ;; #346 <===> >===< |===| /===/ =:=== =!=== ===:= ===!=
  159. ["equal" "equal" "equal"]
  160. (str " ignore sub equal [colon exclam] equal' equal equal;\n"
  161. " ignore sub [less greater bar slash] equal' equal equal;\n"
  162. " ignore sub equal' equal equal [less greater bar slash];\n"
  163. " ignore sub equal' equal equal [colon exclam] equal;\n")
  164. ))
  165. ;; DO NOT generate ignores at all
  166. (def skip-ignores? #{
  167. ;; #410 <<*>> <<+>> <<$>>
  168. ["less" "asterisk" "greater"]
  169. ["less" "plus" "greater"]
  170. ["less" "dollar" "greater"]
  171. })
  172. ;; DO NOT generate ligature
  173. (def manual? #{
  174. ;; /\ \/
  175. ["slash" "backslash"]
  176. ["backslash" "slash"]
  177. })
  178. (defn liga->rule
  179. "[f f i] => { [LIG LIG i] f_f_i.liga
  180. [LIG f i] LIG
  181. [ f f i] LIG }"
  182. [liga]
  183. (case (count liga)
  184. 2 (let [[a b] liga]
  185. (str/replace
  186. (str
  187. "lookup 1_2 {\n"
  188. (when-not (skip-ignores? liga)
  189. (str " ignore sub 1 1' 2;\n"
  190. " ignore sub 1' 2 2;\n"))
  191. (gen-ignore-prefixes liga)
  192. (get ignores liga)
  193. " sub 1.spacer 2' by 1_2.liga;\n"
  194. " sub 1' 2 by 1.spacer;\n"
  195. ; "sub 1 2 by 1_2.liga;"
  196. "} 1_2;")
  197. #"\d" {"1" a "2" b}))
  198. 3 (let [[a b c] liga]
  199. (str/replace
  200. (str
  201. "lookup 1_2_3 {\n"
  202. (when-not (skip-ignores? liga)
  203. (str " ignore sub 1 1' 2 3;\n"
  204. " ignore sub 1' 2 3 3;\n"))
  205. (gen-ignore-prefixes liga)
  206. (get ignores liga)
  207. " sub 1.spacer 2.spacer 3' by 1_2_3.liga;\n"
  208. " sub 1.spacer 2' 3 by 2.spacer;\n"
  209. " sub 1' 2 3 by 1.spacer;\n"
  210. ; "sub 1 2 3 by 1_2_3.liga;"
  211. "} 1_2_3;")
  212. #"\d" {"1" a "2" b "3" c}))
  213. 4 (let [[a b c d] liga]
  214. (str/replace
  215. (str
  216. "lookup 1_2_3_4 {\n"
  217. (when-not (skip-ignores? liga)
  218. (str " ignore sub 1 1' 2 3 4;\n"
  219. " ignore sub 1' 2 3 4 4;\n"))
  220. (gen-ignore-prefixes liga)
  221. (get ignores liga)
  222. " sub 1.spacer 2.spacer 3.spacer 4' by 1_2_3_4.liga;\n"
  223. " sub 1.spacer 2.spacer 3' 4 by 3.spacer;\n"
  224. " sub 1.spacer 2' 3 4 by 2.spacer;\n"
  225. " sub 1' 2 3 4 by 1.spacer;\n"
  226. ; "sub 1 2 3 4 by 1_2_3_4.liga;"
  227. "} 1_2_3_4;")
  228. #"\d" {"1" a "2" b "3" c "4" d}))
  229. 5 (let [[a b c d e] liga]
  230. (str/replace
  231. (str
  232. "lookup 1_2_3_4_5 {\n"
  233. (when-not (skip-ignores? liga)
  234. (str " ignore sub 1 1' 2 3 4 5;\n"
  235. " ignore sub 1' 2 3 4 4 5;\n"))
  236. (gen-ignore-prefixes liga)
  237. (get ignores liga)
  238. " sub 1.spacer 2.spacer 3.spacer 4.spacer 5' by 1_2_3_4_5.liga;\n"
  239. " sub 1.spacer 2.spacer 3.spacer 4' 5 by 4.spacer;\n"
  240. " sub 1.spacer 2.spacer 3' 4 5 by 3.spacer;\n"
  241. " sub 1.spacer 2' 3 4 5 by 2.spacer;\n"
  242. " sub 1' 2 3 4 5 by 1.spacer;\n"
  243. ; "sub 1 2 3 4 5 by 1_2_3_4_5.liga;"
  244. "} 1_2_3_4_5;")
  245. #"\d" {"1" a "2" b "3" c "4" d "5" e}))
  246. ))
  247. (defn compare-ligas [l1 l2]
  248. (let [p1 (priorities l1 Long/MAX_VALUE)
  249. p2 (priorities l2 Long/MAX_VALUE)
  250. pc (compare p1 p2)
  251. c1 (count l1)
  252. c2 (count l2)
  253. cc (compare c1 c2)]
  254. (cond
  255. (not= 0 pc) pc ;; lower priority first
  256. (not= 0 cc) (- cc) ;; longer first
  257. :else (compare l1 l2)))) ;; alphabetical
  258. (defn replace-calt [font ligas]
  259. (let [ligas' (->> ligas
  260. (remove manual?)
  261. (sort compare-ligas))
  262. calt (->> ligas'
  263. (map liga->rule)
  264. (str/join "\n\n"))
  265. glyphs (map #(str (str/join "_" %) ".liga") ligas')
  266. counts (coll/group-by-to count count ligas')]
  267. (when-some [unused (not-empty (reduce dissoc ignores ligas'))]
  268. (println " WARN Unused ignores" (str/join " " (keys unused))))
  269. (when-some [unused (not-empty (reduce disj skip-ignores? ligas'))]
  270. (println " WARN Unused skip-ignores?" (str/join " " unused)))
  271. (when-some [unused (not-empty (reduce disj manual? ligas))]
  272. (println " WARN Unused manual?" (str/join " " unused)))
  273. (println " generated calt:"
  274. ; (str/join " " glyphs)
  275. (str
  276. #_"(" (get counts 2) " pairs, "
  277. (get counts 3) " triples, "
  278. (get counts 4) " quadruples, "
  279. (count ligas') " total" #_")"))
  280. (glyphs/update-code font :features "calt" (constantly calt))))