tonsky / FiraCode

Free monospaced font with programming ligatures
SIL Open Font License 1.1
77.16k stars 3.1k forks source link

Prettify-symbols config for emacs #312

Closed To1ne closed 5 years ago

To1ne commented 7 years ago

The methods for Emacs currently explained on the wiki are not really working for me. I would like to facilitate emacs prettify-symbols-mode instead (as suggested by @siegebell).

This is what I've got at the moment:

Enable prettify symbols globally:

(global-prettify-symbols-mode 1)

Un-prettify symbols at point for easier editing (one of the reasons why I want to use this method):

(setq prettify-symbols-unprettify-at-point t)

Define prettify symbols:

  (add-hook 'prog-mode-hook
            (lambda ()
              (push '("<=" . ?≤) prettify-symbols-alist)))

For this last piece of config I need your help. I like to have the full list of symbol combo's (e.g. <=) with their ligature (e.g. ). So I would appreciate you would share it with us.

tonsky commented 7 years ago

I’m not sure how best to address this. Ligatures are named glyphs in font file, but they don’t have unicode code or anything. They have names (e.g. greater_greater_hyphen.liga), but I’m not sure to what it is translated in OTF/TTF files.

To1ne commented 7 years ago

Owkey, thanks I did not understand this correctly. Too bad, because the unprettify-at-point is pretty useful. See the screenshot below, when the cursor is on the ligature, it unprettifies it.

screen shot 2016-11-21 at 15 54 25

I'll check and see if I can accomplish this with the FiraCode-Regular-Symbol font posted in #211.

To1ne commented 7 years ago

So how does Pragmata Pro do this? https://gist.github.com/kwf/dea7bc51101083acb95c875140e2a96d

tonsky commented 7 years ago

They map ligatures to Unicode private use area. E.g. !! is mapped to U+E720. Looks like there’s a way to that, actually, https://glyphsapp.com/tutorials/roll-your-own-glyph-data/. I’ll try to do that in the next version

fabianhjr commented 7 years ago

@To1ne this is what I was using, however it currently broken. (used to work, something weird is happening now and that's why I came here; found it somwhere on the web/here)

  (custom-set-variables '(haskell-font-lock-symbols t)
                        '(haskell-font-lock-symbols-alist
                          (and (fboundp 'decode-char)
                               (list (cons "&&" (decode-char 'ucs #XE100))
                                     (cons "***" (decode-char 'ucs #XE101))
                                     (cons "*>" (decode-char 'ucs #XE102))
                                     (cons "\\\\" (decode-char 'ucs #XE103))
                                     (cons "||" (decode-char 'ucs #XE104))
                                     (cons "|>" (decode-char 'ucs #XE105))
                                     (cons "::" (decode-char 'ucs #XE106))
                                     (cons "==" (decode-char 'ucs #XE107))
                                     (cons "===" (decode-char 'ucs #XE108))
                                     (cons "==>" (decode-char 'ucs #XE109))
                                     (cons "=>" (decode-char 'ucs #XE10A))
                                     (cons "=<<" (decode-char 'ucs #XE10B))
                                     (cons "!!" (decode-char 'ucs #XE10C))
                                     (cons ">>" (decode-char 'ucs #XE10D))
                                     (cons ">>=" (decode-char 'ucs #XE10E))
                                     (cons ">>>" (decode-char 'ucs #XE10F))
                                     (cons ">>-" (decode-char 'ucs #XE110))
                                     (cons ">-" (decode-char 'ucs #XE111))
                                     (cons "->" (decode-char 'ucs #XE112))
                                     (cons "-<" (decode-char 'ucs #XE113))
                                     (cons "-<<" (decode-char 'ucs #XE114))
                                     (cons "<*" (decode-char 'ucs #XE115))
                                     (cons "<*>" (decode-char 'ucs #XE116))
                                     (cons "<|" (decode-char 'ucs #XE117))
                                     (cons "<|>" (decode-char 'ucs #XE118))
                                     (cons "<$>" (decode-char 'ucs #XE119))
                                     (cons "<>" (decode-char 'ucs #XE11A))
                                     (cons "<-" (decode-char 'ucs #XE11B))
                                     (cons "<<" (decode-char 'ucs #XE11C))
                                     (cons "<<<" (decode-char 'ucs #XE11D))
                                     (cons "<+>" (decode-char 'ucs #XE11E))
                                     (cons ".." (decode-char 'ucs #XE11F))
                                     (cons "..." (decode-char 'ucs #XE120))
                                     (cons "++" (decode-char 'ucs #XE121))
                                     (cons "+++" (decode-char 'ucs #XE122))
                                     (cons "/=" (decode-char 'ucs #XE123))))))
To1ne commented 7 years ago

@fabianhjr It seems to me you need the FiraCode-Regular-Symbol font, see this comment.

howdoicomputer commented 7 years ago

Ah, this would be pretty neat; the suggested workaround currently breaks helm for me in Emacs.

PhilArmstrong commented 7 years ago

I’m not sure how best to address this. Ligatures are named glyphs in font file, but they don’t have unicode code or anything. They have names (e.g. greater_greater_hyphen.liga), but I’m not sure to what it is translated in OTF/TTF files.

According to https://forum.glyphsapp.com/t/how-to-add-a-glyph-and-assign-it-a-custom-unicode-hex-value-like-font-awesome/900/6

You can give a glyph it’s own unicode number by doing the following:

"you can check "Use custom naming" in Font info > other settings (temporarily). Then the unicode field in the info box in the lower left of the font view will become editable. So that you can add your own values. The you can define the glyph names and the unicodes independently. "

So you could do that, assign the ligatures unicode numbers in the private range (0xE000 upwards) and then emacs users will be able to use the glyphs in the fonts you ship directly by putting appropriate mappings in prettify-symbols-alist.

That still leaves the overlap problem - I’m not quite sure why apps that display the glyphs just fine (like gedit for example) aren’t doing this too. It may a disagreement over where the ligature 0 bound should sit between different applications. I’ve tried changing the ligatures so that the char boundary is at the left end of the glyph & that seems to make emacs behave, but it probably not a change you want to make.

Looking at the code for prettify-symbols, it should be possible to tell it that a sequence of characters is being replaced by a bunch of new ones in sequence, but the format is really weird & at the moment I can’t get it to work at all. If I get it working I’ll let you know.

PhilArmstrong commented 7 years ago

OK. I’ve fixed the printing of the extra spaces in prettify-symbols-alist, but that just leads to more rendering errors in emacs - looks like emacs really doesn’t like the layout of these ligatures.

For reference, you can do something like the following to insert the requisite extra spaces:

(defun pretty-symbols ()
  "make some word or string show as pretty Unicode symbols"
  (setq prettify-symbols-alist
        '(
      ("**" . (?\s (Br . Bl) ?\uE004) )
      ("***" . (?\s (Br . Bl) ?\s (Br . Bl) ?\uE005) )
      )
    )
  (prettify-symbols-mode)
;  (setq prettify-symbols-unprettify-at-point t)
  )

(add-hook 'prog-mode-hook 'pretty-symbols)

(I’ve mapped ** and *** to U+E004 and U+E005 respectively in my quick custom version of FiraCode-Medium)

This works fine as you type the symbol, but as soon as you type anything after the symbol, emacs goes back and overwrites the two characters to the left of the new ligature with extra spaces! It looks like it isn’t taking account of the full width of the ligature when rendering it initially, but then is including it afterwards, so either way you get rendering errors with FiraCode as currently designed :(

PhilArmstrong commented 7 years ago

OK, so editing the font so that the ligature starts at offset 0 (well, a small positive offset in fact) instead of -1000 or so means that emacs seems to work just fine, without any need for the extra spaces. Is there some reason the ligatures have this negative offset? I’ve had a look at the ligatures for things like ffi / ffl in other fonts & they don’t seem to need it.

PhilArmstrong commented 7 years ago

Having had a poke around, I see that both Hasklig & Monoid seem to do the same thing, ie position the ligature so that the 0 point is in between the second & third characters for a three character ligature. This does seem to confuse emacs unfortunately!

Is this behaviour documented anywhere in the ttf standards? What does PragmataPro do, does anyone have a copy they could have a look at?

It would be nice to get this fixed - but either emacs or the fonts are doing it wrong - before I poke the emacs devs it would be good to be able to quote chapter & verse at them from somewhere authoritative.

PhilArmstrong commented 7 years ago

OK. So I borrowed a copy of Pragmata Pro for testing purposes only & have discovered that it’s ligatures are defined with the leftmost part of the ligature starting at a positive offset from 0. ie, no part of the ligature is to the left of the origin.

So the free monospace coding fonts with multi-character ligatures (Hasklig, FiraCode) differ from Pragmata Pro in this respect.

PhilArmstrong commented 7 years ago

(Can confirm that emacs works perfectly with PragmataPro & a custom pretty-symbols-alist btw. I may hack FiraCode & change all the ligatures to have their origin on the left. Will see if I have the tuits.)

tonsky commented 7 years ago

@PhilArmstrong this is done so that editors would let you “step into” ligature. This feels more natural when editing the code. Without it, e.g. <-> would be rendered as a single glyph and you won’t be able to position your cursor inside and e.g. remove the dash

tonsky commented 7 years ago

I’ll leave this picture here (it’s code table from PragmataPro so it won’t get lost)

ligatures

PhilArmstrong commented 7 years ago

@tonsky I can step into the PragmataPro ligatures just fine in the editors I have available to me (not including emacs). Eg I can type **, get the three asterisk ligature, step back one character into the ligature, see the caret displayed between the second & third s, delete the middle * and end up with the two asterisk ligature. (This using geany, which is IIRC a GTK3 based editor).

Which editors require these left shifting ligatures?

Ignoring the placement issue, it would be great if FiraCode assigned unicode private space codepoints to the ligatures!

Thanks for all your work on FiraCode.

PhilArmstrong commented 7 years ago

(You can also mark caret points in ligatures, which should let editors automatically backstep into them in a natural fashion, although poking at the PragmataPro ttf in fontforge it doesn’t look like it’s using that feature. It might be an OpenType-only feature?)

tonsky commented 7 years ago

Atom and CodeMirror require it, IntelliJ IDEA does not. I’ll explore if I can make use of caret points, that would be a great help, actually.

PhilArmstrong commented 7 years ago

IntelliJ is (presumably) using the HarfBuzz layout engine as that’s what (IIRC) all the Java runtimes use & it’s also the backend behind most of the Linux text rendering libraries, so it makes sense that Java & GTK3 / Qt - based editors would share the same behaviour.

PhilArmstrong commented 7 years ago

(It’s also why FiraCode doesn’t work in the Gnome Terminal or any other libvte based program - it doesn’t use a layout engine to paint the glyphs, so no ligatures.)

tonsky commented 7 years ago

Unfortunately, Atom doesn’t seem to understand caret positions. Also, Terminal.app (OS X) doesn’t show normal ligatures (liga) or dlig, but does show calt. So I think I’ll stick with that technique. I’ll see what can be done for Emacs, maybe I’ll just copy same ligatures but fix the offset for duplicates and assign Unicode codes to that

PhilArmstrong commented 7 years ago

After some poking, it turns out that CodeMirror works just fine in firefox, but not in Chrome, which suggests that it’s the Chrome font rendering that’s at fault in Atom. How annoying!

PhilArmstrong commented 7 years ago

(Atom being based on electron which is, IIRC, a packaged up Chrome browser.)

Profpatsch commented 7 years ago

So, after reading documentation for a bit, especially on how compose-region works (which is used by prettify-symbols-mode), I was able to create a full solution for Hasklig code points: https://github.com/i-tu/Hasklig/issues/84#issuecomment-298803495

If https://github.com/tonsky/FiraCode/issues/211 gets implemented and each combined glyph gets a code point, this is easily translated to FiraCode.

Triavanicus commented 5 years ago

So this?

(defun fira-code-mode--make-alist (list)
  "Generate prettify-symbols alist from LIST."
  (let ((idx -1))
    (mapcar
     (lambda (s)
       (setq idx (1+ idx))
       (let* ((code (+ #Xe100 idx))
          (width (string-width s))
          (prefix ())
          (suffix '(?\s (Br . Br)))
          (n 1))
     (while (< n width)
       (setq prefix (append prefix '(?\s (Br . Bl))))
       (setq n (1+ n)))
     (cons s (append prefix suffix (list (decode-char 'ucs code))))))
     list)))

(defconst fira-code-mode--ligatures
  '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\"
    "{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}"
    "--" "---" "-->" "->" "->>" "-<" "-<<" "-~"
    "#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_("
    ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*"
    "/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||="
    "|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "=="
    "===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">="
    ">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>"
    "<$" "<$>" "<!--" "<-" "<--" "<->" "<+" "<+>" "<="
    "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" "<~"
    "<~~" "</" "</>" "~@" "~-" "~=" "~>" "~~" "~~>" "%%"
    "x" ":" "+" "+" "*"))

(defvar fira-code-mode--old-prettify-alist)

(defun fira-code-mode--enable ()
  "Enable Fira Code ligatures in current buffer."
  (setq-local fira-code-mode--old-prettify-alist prettify-symbols-alist)
  (setq-local prettify-symbols-alist (append (fira-code-mode--make-alist fira-code-mode--ligatures) fira-code-mode--old-prettify-alist))
  (prettify-symbols-mode t))

(defun fira-code-mode--disable ()
  "Disable Fira Code ligatures in current buffer."
  (setq-local prettify-symbols-alist fira-code-mode--old-prettify-alist)
  (prettify-symbols-mode -1))

(define-minor-mode fira-code-mode
  "Fira Code ligatures minor mode"
  :lighter " FiraCode"
  (setq-local prettify-symbols-unprettify-at-point 'right-edge)
  (if fira-code-mode
      (fira-code-mode--enable)
    (fira-code-mode--disable)))

(defun fira-code-mode--setup ()
  "Setup Fira Code Symbols"
  (set-fontset-font t '(#Xe100 . #Xe16f) "Fira Code Symbol"))

(provide 'fira-code-mode)

(Updated November 20, 2018 at 6:40 am CST)

radrow commented 5 years ago

@Triavanicus

It did not work for me. Instead I got strange characters and below _screenshot

wedens commented 5 years ago

@radrow Do you have "Fira Code Symbol" font installed (not just plain Fira Code)?

Also, you'll need to set the font for the ligatures codepoint range: (set-fontset-font t '(#Xe100 . #Xe16f) "Fira Code Symbol"). For some reason this is not included in the snippet above.

Triavanicus commented 5 years ago

I updated the snippit, and put it in the (fira-code-mode--setup) function

radrow commented 5 years ago

Strange. It is still buggy, but a bit less when using "Fira Code" as default instead of "Fira Code Symbol" (eg. :: turns into =>). Which version of emacs are you testing this on? I am using emacs 26.1 with spacemacs and putting snippet in user-init (before packages load)

Profpatsch commented 5 years ago

It did not work for me. Instead I got strange characters and below

Have you restarted Emacs lor (||) rebooted? That’s sometimes required to make font caches update.

radrow commented 5 years ago

Rebooted, no difference

To1ne commented 5 years ago

Thanks everyone. Especially @Triavanicus.

I've restructured the wiki page with all findings. It should be easy now for other to find them. So I'm closing this issue.

To1ne commented 5 years ago

@radrow I think the Fira Code Symbol font you have is not good. It seems characters have switched places. I hope we can some time soon have an official font with the symbols in the private Unicode area, but I'm leaving that to https://github.com/tonsky/FiraCode/issues/211.

But at the moment the wiki is suggesting the one from: https://github.com/tonsky/FiraCode/issues/211#issuecomment-239058632 The symbol map is included, so you can see if the error is an offset problem, or actually a swap of symbols?

If you see an offset issue, something odd happens with the generation of the prettify-symbols-alist. Can you please post the content of prettify-symbols-alist here?

wedens commented 5 years ago

Note that Fira Code Symbol conflicts with Material Icons (overlapping codepoints), so you may have ligatures instead of icons where material icons are used via all-the-icons.

radrow commented 5 years ago

Okay, it turned that I didn't have Fira Code Symbol installed correctly. I did not recieve any warning nor error from emacs about this

Triavanicus commented 5 years ago

I have it in my settings.org file which is called by my initial.el then I call the setup function, and add the hook function to prog-mode

On Sun, Dec 23, 2018, 7:21 PM Ethan Miller <notifications@github.com wrote:

@Triavanicus https://github.com/Triavanicus where would the code you provided above be placed?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/tonsky/FiraCode/issues/312#issuecomment-449674383, or mute the thread https://github.com/notifications/unsubscribe-auth/AGzP_9gqbq1-5HOcERvTrrDOcgKwlX46ks5u8CwCgaJpZM4K4RJk .

ezmiller commented 5 years ago

@Triavanicus so far I've just dropped it into my .spacemacs file. I didn't realize that to get this to work you need to activate the mode. I tested this by just doing it in the editor: C-M 'fira-code-mode.

That showed some progress, but when I activated the mode I was getting odd symbols with numbers, e.g.

image

Any idea why this may be happening? Thanks for the help!

Triavanicus commented 5 years ago

Do you have the Fira code Symbols font installed from https://github.com/tonsky/FiraCode/issues/211#issuecomment-239058632

On Sun, Dec 23, 2018, 8:08 PM Ethan Miller <notifications@github.com wrote:

@Triavanicus https://github.com/Triavanicus so far I've just dropped it into my .spacemacs file. I didn't realize that to get this to work you need to activate the mode. I tested this by just doing it in the editor: C-M 'fira-code-mode.

That showed some progress, but when I activated the mode I was getting odd symbols with numbers, e.g.

[image: image] https://user-images.githubusercontent.com/772738/50389305-c21e5500-06f6-11e9-8778-611df120bdfd.png

Any idea why this may be happening? Thanks for the help!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/tonsky/FiraCode/issues/312#issuecomment-449676814, or mute the thread https://github.com/notifications/unsubscribe-auth/AGzP_9Kp7VAP2k6PV5ssfOK1FKjfGjYLks5u8DcFgaJpZM4K4RJk .

ezmiller commented 5 years ago

I downloaded that, installed it using the "Font Book" app on mac, and then added this to the font settings in my .spacemacs file:

   dotspacemacs-default-font '(("Fira Code"
                                :size 16
                                :weight normal
                                :width normal
                                :powerline-scale 1.2)
                               ("Fira Code Symbol"
                                :size 16
                                :weight normal
                                :width normal
                                :powerline-scale 1.2))
Triavanicus commented 5 years ago

In your .spacemacs file do you call the fira-code-mode--setup? Because that will tell emacs to use a different font for the characters inside of the private space of the font, while still using regular Fira code for the rest, also have you tried https://github.com/tonsky/FiraCode/wiki/Emacs-instructions#using-composition-mode-in-emacs-mac-port then clicking the install link on spacemacs.org

On Sun, Dec 23, 2018, 8:16 PM Ethan Miller <notifications@github.com wrote:

I downloaded that, installed it using the "Font Book" app on mac, and then added this to the font settings in my .spacemacs file:

dotspacemacs-default-font '(("Fira Code" :size 16 :weight normal :width normal :powerline-scale 1.2) ("Fira Code Symbol" :size 16 :weight normal :width normal :powerline-scale 1.2))

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/tonsky/FiraCode/issues/312#issuecomment-449677305, or mute the thread https://github.com/notifications/unsubscribe-auth/AGzP_9uV0sYQJKy2PP6mzyzHYncY1cmlks5u8DkYgaJpZM4K4RJk .

ezmiller commented 5 years ago

Hi, I posted the entirety of the snippet above into the .spacemacs file, below everything else. My understanding was that the snippet above enables that mode such that I was able to activate it in the editor.

I haven't tried that method you referenced as I was trying to avoid installing the alternate mac port of emacs.

Triavanicus commented 5 years ago

@ezmiller which, at least for me, it does so long as do the following. Also reading some more into the issue, you might have to reset your fontconfig cache (which if you know how to do, it will save you a reboot, otherwise, you will have to reboot :( )

(fira-code-mode--setup)
(add-hook 'prog-mode-hook 'fira-code-mode)

other than that, I don't know what your issue could be.

ejmg commented 5 years ago

@Triavanicus I really appreciate your solution that you added to the wiki. I think it works a lot better than the previous hack I had used.

I have just one question, if you don't mind?

How can I disable the ligature rendering for "x" from the prettify-symbols list without messing up the character encoding? Based on the way you enumerate the font character codes, if I were to remove "x" from fira-code-mode--ligatures, every ligature after that is offset by -1, meaning : renders as x, + renders as : and so forth.

I don't fully understand what fira-code-mode--make-alist is doing (or how fonts work on emacs, for that matter), and have tried to skip over/ignore adding the encoding for "x" to no avail. Is there any easy/obvious way to "unset" or skip over the "x" ligature provided by siegebell's FiraCode-Regular-Symbol pack?

Triavanicus commented 5 years ago

You should be able to just delete everything after the "x" i.e.

(defconst fira-code-mode--ligatures '("www" "" "" "/" ">" "/" "\\" "\\\" "{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}" "--" "---" "-->" "->" "->>" "-<" "-<<" "-~" "#{" "#[" "##" "###" "####" "#(" "#?" "#" "#(" ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/" "/" "/=" "/==" "/>" "//" "///" "&&" "||" "||=" "|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "==" "===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">=" ">=>" ">>" ">>-" ">>=" ">>>" "<" "<>" "<|" "<|>" "<$" "<$>" "<!--" "<-" "<--" "<->" "<+" "<+>" "<=" "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" "<~" "<" "</" "</>" "~@" "~-" "~=" "~>" "" "~~>" "%%"))

soraros commented 5 years ago

This one is easy. Replace fira-code-mode--make-alist with the following and pass in only the list of symbols you want to enable.

(defun fira-code-mode--make-alist (lat)
  "Generate prettify-symbols alist from LIST."
  (let ((alist (cl-mapcar #'cons
                          fira-code-mode--ligatures
                          (number-sequence 0 (- (length fira-code-mode--ligatures) 1)))))
    (mapcar
     (lambda (s)
       (let* ((idx (cdr (assoc s alist)))
              (code (+ #Xe100 idx))
              (width (string-width s))
              (prefix ())
              (suffix '(?\s (Br . Br)))
              (n 1))
         (while (< n width)
           (setq prefix (append prefix '(?\s (Br . Bl))))
           (setq n (1+ n)))
         (cons s (append prefix suffix (list (decode-char 'ucs code))))))
     lat)))
GaussianWonder commented 5 years ago

The methods for Emacs currently explained on the wiki are not really working for me. I would like to facilitate emacs prettify-symbols-mode instead (as suggested by @siegebell).

This is what I've got at the moment:

Enable prettify symbols globally:

(global-prettify-symbols-mode 1)

Un-prettify symbols at point for easier editing (one of the reasons why I want to use this method):

(setq prettify-symbols-unprettify-at-point t)

Define prettify symbols:

  (add-hook 'prog-mode-hook
            (lambda ()
              (push '("<=" . ?≤) prettify-symbols-alist)))

For this last piece of config I need your help. I like to have the full list of symbol combo's (e.g. <=) with their ligature (e.g. ). So I would appreciate you would share it with us.

i know this is suuuuuper late but this might help https://www.copypastecharacter.com/all-characters. Just copy paste them, seems to work :))

hyiltiz commented 4 years ago

Does the wiki on Emacs support need an update now that HarfBuzz based font rendering is in Emacs?