A new section for my awesome website. Migrates an old blogpost from the github repository. Change-Id: I5fd0c2b2679a1367015fa098e3e787bbc0cdd973 Reviewed-on: https://cl.tvl.fyi/c/depot/+/3293 Tested-by: BuildkiteCI Reviewed-by: Profpatsch <mail@profpatsch.de>
		
			
				
	
	
		
			123 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
title: Ligature Emulation in Emacs
 | 
						||
date: 2017-05-04
 | 
						||
 | 
						||
Monday was (yet another)
 | 
						||
[NixOS hackathon][hackathon] at [OpenLab Augsburg][ola].
 | 
						||
[Maximilian][mhuber] was there and to my amazement
 | 
						||
he got working ligatures in his Haskell files in Emacs! Ever since Hasklig
 | 
						||
updated its format to use ligatures and private Unicode code points a while ago,
 | 
						||
the hack I had used in my config stopped working.
 | 
						||
 | 
						||
Encouraged by that I decided to take a look on Tuesday. Long story short, I was
 | 
						||
able to [get it working in a pretty satisfying way][done].
 | 
						||
 | 
						||
[hackathon]: https://www.meetup.com/Munich-NixOS-Meetup/events/239077247/
 | 
						||
[mhuber]: https://github.com/maximilianhuber
 | 
						||
[ola]: https://openlab-augsburg.de
 | 
						||
[done]: https://github.com/i-tu/Hasklig/issues/84#issuecomment-298803495
 | 
						||
 | 
						||
What’s left to do is package it into a module and push to melpa.
 | 
						||
 | 
						||
 | 
						||
### elisp still sucks, but it’s bearable, sometimes
 | 
						||
 | 
						||
I’m the kind of person who, when trying to fix something elisp related, normally
 | 
						||
gives up two hours later and three macro calls deep. Yes, homoiconic,
 | 
						||
non-lexically-scoped, self-rewriting code is not exactly my fetish.
 | 
						||
This time the task and the library (`prettify-symbols-mode`) were simple enough
 | 
						||
for that to not happen.
 | 
						||
 | 
						||
Some interesting technical trivia:
 | 
						||
 | 
						||
- elisp literal character syntax is `?c`. `?\t` is the tab character
 | 
						||
- You join characters by `(string c1 c2 c3 ...)`
 | 
						||
- [dash.el][dash] is pretty awesome and does what a functional programmer
 | 
						||
  expects. Also, Rainbow Dash.
 | 
						||
- Hasklig and FiraCode multi-column symbols actually [only occupy one column, on
 | 
						||
  the far right of the glyph][glyph]. `my-correct-symbol-bounds` fixes emacs’
 | 
						||
  rendering in that case.
 | 
						||
 | 
						||
 | 
						||
[dash]: https://github.com/magnars/dash.el
 | 
						||
[glyph]: https://github.com/tonsky/FiraCode/issues/211#issuecomment-239082368
 | 
						||
 | 
						||
 | 
						||
## Appendix A
 | 
						||
 | 
						||
For reference, here’s the complete code as it stands now. Feel free to paste
 | 
						||
into your config; let’s make it [MIT][mit]. Maybe link to this site, in case there are
 | 
						||
updates.
 | 
						||
 | 
						||
[mit]: https://opensource.org/licenses/MIT
 | 
						||
 | 
						||
```elisp
 | 
						||
 (defun my-correct-symbol-bounds (pretty-alist)
 | 
						||
    "Prepend a TAB character to each symbol in this alist,
 | 
						||
this way compose-region called by prettify-symbols-mode
 | 
						||
will use the correct width of the symbols
 | 
						||
instead of the width measured by char-width."
 | 
						||
    (mapcar (lambda (el)
 | 
						||
              (setcdr el (string ?\t (cdr el)))
 | 
						||
              el)
 | 
						||
            pretty-alist))
 | 
						||
 | 
						||
  (defun my-ligature-list (ligatures codepoint-start)
 | 
						||
    "Create an alist of strings to replace with
 | 
						||
codepoints starting from codepoint-start."
 | 
						||
    (let ((codepoints (-iterate '1+ codepoint-start (length ligatures))))
 | 
						||
      (-zip-pair ligatures codepoints)))
 | 
						||
 | 
						||
  ; list can be found at https://github.com/i-tu/Hasklig/blob/master/GlyphOrderAndAliasDB#L1588
 | 
						||
  (setq my-hasklig-ligatures
 | 
						||
    (let* ((ligs '("&&" "***" "*>" "\\\\" "||" "|>" "::"
 | 
						||
                   "==" "===" "==>" "=>" "=<<" "!!" ">>"
 | 
						||
                   ">>=" ">>>" ">>-" ">-" "->" "-<" "-<<"
 | 
						||
                   "<*" "<*>" "<|" "<|>" "<$>" "<>" "<-"
 | 
						||
                   "<<" "<<<" "<+>" ".." "..." "++" "+++"
 | 
						||
                   "/=" ":::" ">=>" "->>" "<=>" "<=<" "<->")))
 | 
						||
      (my-correct-symbol-bounds (my-ligature-list ligs #Xe100))))
 | 
						||
 | 
						||
  ;; nice glyphs for haskell with hasklig
 | 
						||
  (defun my-set-hasklig-ligatures ()
 | 
						||
    "Add hasklig ligatures for use with prettify-symbols-mode."
 | 
						||
    (setq prettify-symbols-alist
 | 
						||
          (append my-hasklig-ligatures prettify-symbols-alist))
 | 
						||
    (prettify-symbols-mode))
 | 
						||
 | 
						||
  (add-hook 'haskell-mode-hook 'my-set-hasklig-ligatures)
 | 
						||
```
 | 
						||
 | 
						||
## Appendix B (Update 1): FiraCode integration
 | 
						||
 | 
						||
I also created a mapping for [FiraCode][fira]. You need to grab the [additional
 | 
						||
symbol font][symbol] that adds (most) ligatures to the unicode private use area.
 | 
						||
Consult your system documentation on how to add it to your font cache.
 | 
						||
Next add `"Fira Code"` and `"Fira Code Symbol"` to your font preferences. Symbol
 | 
						||
only contains the additional characters, so you need both.
 | 
						||
 | 
						||
If you are on NixOS, the font package should be on the main branch shortly, [I
 | 
						||
added a package][symbol-pkg].
 | 
						||
 | 
						||
[fira]: https://github.com/tonsky/FiraCode/
 | 
						||
[symbol]: https://github.com/tonsky/FiraCode/issues/211#issuecomment-239058632
 | 
						||
[symbol-pkg]: https://github.com/NixOS/nixpkgs/pull/25517
 | 
						||
 | 
						||
Here’s the mapping adjusted for FiraCode:
 | 
						||
 | 
						||
```elisp
 | 
						||
  (setq my-fira-code-ligatures
 | 
						||
    (let* ((ligs '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\"
 | 
						||
                  "{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}"
 | 
						||
                  "--" "---" "-->" "->" "->>" "-<" "-<<" "-~"
 | 
						||
                  "#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_("
 | 
						||
                  ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*"
 | 
						||
                  "/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||="
 | 
						||
                  "|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "=="
 | 
						||
                  "===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">="
 | 
						||
                  ">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>"
 | 
						||
                  "<$" "<$>" "<!--" "<-" "<--" "<->" "<+" "<+>" "<="
 | 
						||
                  "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" "<~"
 | 
						||
                  "<~~" "</" "</>" "~@" "~-" "~=" "~>" "~~" "~~>" "%%"
 | 
						||
                  "x" ":" "+" "+" "*")))
 | 
						||
      (my-correct-symbol-bounds (my-ligature-list ligs #Xe100))))
 | 
						||
```
 |