spacemacs
spacemacs copied to clipboard
FiraCode “Ligature” integration
FiraCode is a nice derivation from Fira Mono that adds many ligatures for a nice code look. It uses Unicode ligatures, but Emacs sadly doesn’t support them (yet). I’d like to add a layer to Spacemacs to support that.
There is a page about integration into Emacs via font-lock, and the second example is also how Hasklig can be made to work with Emacs. I noticed integrating too many ligatures that way makes font rendering very slow.
Is there a reason why prettify-symbols-mode is not being used for this?
Probably not, no. But it looks awesome.
I'm not sure if that's the right solution here, as it looks like prettify-symbols-mode alters the number of characters per row, while the ligature rendering preserves that property. For instance, using FiraCode in Konsole would make "->" become an arrow but still take up two characters. Similarly, "::", ">>=", et al. conserve that many characters, keeping the spacing correct. However, if prettify-symbols-mode can keep the character count, then that works too.
So to install and use Fira code with spacemacs, does one follow the instructions for emacs from the fira code project site [1], or there is already some mechanism in spacemacs that can use fira code so that the user only needs to "helm xselect font" the Fira Code Retina? I would very much like to use Fira Code in Spacemacs, but am trying not to break anything. I am just coming from Vim, and know almost nothing about Lisp except what is taught in Wikipedia. I tried putting the first snippet provided in that page to my .spacemacs file within the user-config() function, but after restarting, when I restart spacemacs and type "=>", nothing changes (I do not see a ligature).
[1] https://github.com/tonsky/FiraCode/wiki/Setting-up-Emacs
I haven’t yet looked at it any further, maybe the regex-method works, but I suspect it to severely slow down every kind of text display. (It’s essentially running a couple dozen matches on every substring in each buffer…)
News! https://ekaschalk.github.io/post/prettify-mode/
It seems to work with firacode now. If anyone wants to take a look: go for it!
Another update: Hasklig seems to have moved to the FiraCode mechanism with 1.0 as well. So getting this to work would give us two fonts to choose from.
Unfortunately that method still does not allow multiple blocks to be taken up by a single ligature (from the website @Profpatsch linked to). This is problematic for coding:
For ligatures, the number of visual points composing the replacement is the same as its composing characters. For instance, the ligature for -> occupies two spaces.
But this is not the case for prettify-symbols or pretty-mode. Both alpha and not in are reduced to one character.
So the line width you see may not be the same as its actual width.
This has two effects:
A line could then exceed 80 characters with prettify-mode disabled. Indentation is performed using the Unicode replacements, not actual spacing.
Oh no. Maybe it can be patched?
I think that prettify would have to support adding in the lost space.
Well, these fonts make the ligatures exactly as wide as the normal combinations, so it must support multi-width characters as well.
Hi I wrote the blog on pretty-mode linked earlier.
The options as I see it are:
- Don't use any symbols with disagreeing number of spaces.
- Use the visual spacing - your indentation won't agree with the unfontified buffer.
- Use the true spacing - your visuals will have extraneous spaces. (Possible with: https://github.com/Ilazki/prettify-utils.el)
- Use the visual spacing with true indentation - your visuals will have wrong indentation.
- Pre/post-save (or commit/etc..) hook that unfontifies and runs tab on the buffer then refontifies and tabs again.
- Use the visual spacing and visually modify indentation, ie. the true indentation is font-locked.
I took a shot at the hook:
(defun test-fontlock-fix-before ()
(font-lock-mode -1)
(spacemacs/indent-region-or-buffer))
(defun test-fontlock-fix-after ()
(font-lock-mode 1)
(sit-for 1) ; Without the sleep it doesnt reindent.
(spacemacs/indent-region-or-buffer))
(add-hook 'before-save-hook 'test-fontlock-fix-before)
(add-hook 'after-save-hook 'test-fontlock-fix-after)
This works in all cases that (spacemacs/indent-region-or-buffer) does, which I found it is not everytime.
The glaring flaws are the sleep and that save-file cannot tell anymore when your file hasn't changed, especially annoying with eg magit.
Number 6 is the only true solution I see to this problem but it seems quite involved.
Any other thoughts on how spacing could be handled?
I implemented the visual spacing solution: https://github.com/Profpatsch/blog/blob/master/posts/ligature-emulation-in-emacs/post.md
I only afterwards noticed that it’s not perfect, but it works fine for me right now with Hasklig.
My idea would be to switch back to non-visual and tag every symbol with the number of spaces it occupies, then prefix them with spaces accordingly. I think I’m gonna try that next.
When I use Fira Code in Spacemacs even without ligature support sometimes the character alignment is off, specifically where line 1 uses bold for some characters, and line 2 doesn't have bold, then they don't line up vertically. What could be causing this?
@Profpatsch did you considered making a MELPA package from your code?
@root42 No, not really. But if you want one, go ahead. You have my blessing. :) It’s MIT-licensed anyway.
@Profpatsch How can I plug that into my .spacemacs?
@honza Just copy the code verbatim, then Spc f e R or Spc r R if that does not work.
Putting this code:
(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)))
(setq my-fira-code-ligatures
(let* ((ligs '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\"
"{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}"
"--" "---" "-->" "->" "->>" "-<" "-<<" "-~"
"#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_("
".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*"
"/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||="
"|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "=="
"===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">="
">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>"
"<$" "<$>" "<!--" "<-" "<--" "<->" "<+" "<+>" "<="
"<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" "<~"
"<~~" "</" "</>" "~@" "~-" "~=" "~>" "~~" "~~>" "%%"
"x" ":" "+" "+" "*")))
(my-correct-symbol-bounds (my-ligature-list ligs #Xe100))))
to the (defun dotspacemacs/user-config ()) section, and restarting Emacs has no effect. I also set the font to Fira Code.
You forgot the code that adds it to the hooks that are called when you enable a mode.
;; 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)
For FiraCode change accordingly.
@Profpatsch Ah, thanks! I was hoping to enable it globally.
I can confirm that works wonderfully. Thanks @Profpatsch <3
Afaik inherits every mode from fundamental-mode and every programming language mode from prog-mode. So (maybe?) hooking it into one of these makes it work globally.
I've developed a proof of concept fix to ligature indentation that you all might find interesting: http://www.modernemacs.com/post/lig-spacing/
http://www.modernemacs.com/post/lig-spacing/
Won’t that require support from language modes, who can tell you how many lines down spaces should be collapsed?
The ligature code in this issue is all based on the assumption that ligatures have exactly the same width as the naïve code, which is true of all ligatures in FiraCode or Hasklig.
Won’t that require support from language modes
It's major mode agnostic.
which is true of all ligatures in FiraCode or Hasklig.
FiraCode and Hasklig aren't the only fonts to implement ligatures - see PragmataPro.
Spaced ligatures do not imply correct visual indentation/grouping. For example, start a form with the .- ligature or use the +++ ligature and check an org-table.
This is as much a post about the more general prettify-mode which not only has incorrect visual, but also incorrect true indentation. If the restriction surrounding incorrect true indentation is removed, then ligature/symbol-based programming becomes more reasonable.
I tried this for both Fira Code and Hasklig, but my output is definitely unexpected:


I've placed the code from here as-is into my user-config, and my font settings look like:
dotspacemacs-default-font '("Hasklig"
:size 13
:weight normal
:width normal
:powerline-scale 1.75)
Any ideas?
Interestingly, if I switch my font back to Source Code Pro but leave in the custom elisp, the weird replacements from above still show. So, I know that Spacemacs can see Hasklig, since changing it to say Hasklig Foo and restarting throws warnings, but just Hasklig does not. I installed Hasklig from an Arch Linux AUR package. I also know that the ligature replacement mechanism is in place, but for some reason it's not finding the right ligatures.
@fosskers which version of Hasklig do you have installed? They changed the way ligatures worked after 1.0 I think, currently I use 1.1.
I've got 1.1 from this package.