Now that mainstream Monospace Typefaces have support for ligatures—typefaces such as Pragmata Pro, Hasklig, & Fira Code—it now seems worthwhile to add Ligature support to iTerm2 when previously that probably couldn’t be justiable.
An Example of the desired outcome
Here’s a figure of a Monospace font1 w/ Ligatures when ligatures are disabled:
Here’s a figure of a Monospace font w/ Ligatures when ligatures are active:
Nuances
In the spirit of the existing configurability already available, this may be desired to be toggled on & off in the text section of options since this only makes sense to be active for monospace (fixed-width) Typefaces.
Therefore, ligatures being rendered only makes sense for fixed-width (monospace) fonts to not “confuse” everything; accordingly, it makes sense for it to be configurable for it to be active or not for users who’ve decided to eccentrically use a non-monospace typeface with their instances of iTerm.**
This will ensure the ligatures of their non-monospace typeface creating undesirable results to their existing iTerm2 experience.
This will also obviously allow people who have Monospace fonts with ligatures have an option for them to not be used if undesired for whatever reason.
Footmarks
1: The Monospace font demonstrated in this email is Fira Code. It’s an open-source, monospace typeface that can be downloaded on Github here
Designs
Child items
...
Show closed items
Linked items
0
Link issues together to show that they're related.
Learn more.
@gnachman The following image is a better example than the original images posted with this issue showing ligatures being enabled on a monotype type font are exactly as wide as the original chars before ligatures were enabled:
+1 please; many new fonts for development are supporting this and it would be very nice to see it in iTerm2. Hasklig, Monoid, Fira Code, Hack and more, I'm sure.
@davidzchen If you can commit some serious time to it, sure!
The basic idea is to rip out -[iTermTextDrawingHelper constructTextRuns:row:selected:indexRange:backgroundColor:matches:storage:] and replace it with a method that returns a NSAttributedString * (optionally including ligatures), and then modifying -[iTermTextDrawingHelper drawRun:ctx:initialPoint:storage:] to properly draw the attributed string, respecting the character grid. The sharp corners are:
Handling images. I think a custom attribute will need to be stored in the NSAttributedString around a placeholder character so we can figure out which part of the image to draw.
Supporting "fake bold" where text is double-struck. Another custom attribute, I suppose.
Ensuring that each grapheme cluster starts at the proper horizontal offset. I think we'll do something like what -[iTermTextDrawingHelper drawStringWithCombiningMarksInRun:at:] does, but first you'll need to segment the attributed string into grapheme clusters (see enumerateComposedCharacters:, but there might be something in CTLine that can do it too), figure out the width of each, and modify the positions array so each grapheme cluster sits in its own grid-aligned cell.
Handling box-drawing characters, which are special-cased to draw lines instead of using whatever is in the current font.
Finally, make sure performance doesn't take a hit. The logDrawingPerformance advanced preference will help with this.
There is a decent suite of unit tests (PTYTextViewTest has a bunch of tests that draw into a scratch buffer and compare the result to a saved "golden" image. Beware that the comparison is fuzzy to avoid false positives when the OS's antialiasing algo changes; I recommend regenerating the golden images and removing the fuzziness before testing these changes). The tests will need to be extended to test alignment issues that might be introduced.
@jrolfs I updated the donation page with whiskey instructions. But you'd better let that Laphroaig age a little more because there are still many bugs remaining in 3.0.
I'm sure it has issues. Please put it through its paces and let me know what you see. I expect you'll find bugs and performance problems, but please report them. For bugs, a debug log would be helpful (https://iterm2.com/debuglog). For performance issues, a sample is helpful (https://gitlab.com/gnachman/iterm2/wikis/HowToSample).
@gnachman its noticeably slower and laggy when working with vim in tmux. I ended up just rolling back to 3.0.20160713 for now. I'll try messing around with non-ligature fonts and without tmux tomorrow to see if that helps any.
That particular issue is distinct to Menlo, which macOS seems to treat oddly with ligatures. You’ll see that same thing crop up on websites which have ligatures turned on if you end up falling back to Menlo in their font stack for some reason. It’s a problem with the font.
Just a note: I downloaded the latest nightly build (for some reason my older nightly (20160719) was unable to upgrade. Not sure if ligatures are disabled there but Build 3.0.20160720-nightly does not show ligatures for the Menlo for Powerline font.
PragmataPro doesn't show ligatures in Terminal.app, either. It looks like Fira uses a hack to get ligatures that Pragmata does not. I suppose it will require a setting :(
Im kinda curious what changes you did to make some fonts work when others do not. As I understand it there are three modes in NSAttributedString for ligatures.
0 - off
1 - basic support
2 - all ligature support
Interesting. Rather than having a list of fonts and their associated ligature levels embedded in the code, it should probably be a plist or JSON file or other confit that allows the behavior to be tweaked without a new build. But it makes sense now what and why. Thanks tor the link.
I'm waiting to see if a better method comes along before putting it in a plist file. Or maybe letting you override it with user defaults would make more sense, anyway.
@gnachman either way is fine, but even in the interim for developmental purposes, it would allow you to tweak more quickly and get user input. Once a stable list arose, you could codify it internally. However I tend to think having it modifiable and user overridable is going to be a better long term approach. Especially since more and more fonts used for coding are adding ligature support; it being the new craze and what not.
Ah you're right. I was attempting to use a FiraCode patch for NerdFont and it seems that particular font isn't working. Everything works fine when I use plain FiraCode.
Can you attach the version of FiraCode you're using? Mine displays its name as "Fira Code Retina" not "FiraCode-Retina" so I guess there are different versions of it with different names floating around.
I still think the settings, near where you choose the fonts, should simply
have a level of font ligatures to enable for that profile rather than
trying to whitelist a set of fonts. What is the reason for this again?
If you wish to have a list, the list should be for fonts that are known to
perform either well or poorly with a specified level of ligature features.
And these fonts can have a sensible default set to the level of ligature
that works best for it.
This approach requires significantly less hands on manipulation
NSAttributedStrings have only three possible ligature settings to my
knowledge.
The ligature attribute determines what kinds of ligatures should be used
when displaying the string. A value of 0 indicates that only ligatures
essential for proper rendering of text should be used, 1 indicates that
standard ligatures should be used, and 2 indicates that all available
ligatures should be used. Which ligatures are standard depends on the
script and possibly the font. Arabic text, for example, requires ligatures
for many character sequences, but has a rich set of additional ligatures
that combine characters. English text has no essential ligatures, and
typically has only two standard ligatures, those for “fi” and “fl”—all
others being considered more advanced or fancy.
There's a whitelist because most fonts don't offer ligatures so it'd be a do-nothing button most of the time. I wish it were feasible to detect their presence but there's no API for this and parsing the font file itself is prohibitively difficult. I don't want to offer ligature level 1 by default because that implies using Core Text which is incredibly slow compared to Core Graphics (which cannot render ligatures). To make matters worse, FiraCode uses ligatures even at level 0 (they used dirty tricks), which is one more way the button wouldn't work.
A drop down that defaults to 0 would be fine for 90% of the fonts out there. Don't worry if they found a way to enable ligatures with dirty tricks or not. That's not your problem, however a whitelist that lets you track fonts like FiraCode would allow you to put warnings or disclosures up.
I still think the toggle for user control makes more sense. But ultimately it is your product and you have the final say, of course. I have just found that advanced features, no matter how prevalent, are generally best left to toggles that advanced users can tweak.
Remember that in most cases if someone is using a ligature enabled font, at this point in time, they are already looking for the switch or way to enable the feature.
Render with Core Graphics. Fast performance, no ligatures even for FiraCode.
Render with Core Graphics for ASCII and Core Text otherwise, and ligature level 0. Still no ligatures in any font (but some non-Latin scripts will look nicer).
Always use Core Text at ligature level 0. Worst performance. Provides ligatures for FiraCode and similar. Will render RTL properly.
As above, but with ligature level 1.
As you suggested, a whitelist could indicate when a font is known to provide ligatures. All that's left is to figure out how to present this to the user clearly.
It seems that you have a few conflated concerns. One is the text rendering engine which should be separate from ligature level. I believe there is a third ligature level (2) as well, which should be offered to be complete.
In my opinion there's a few ways to do this. One is to keep the interface simple and provide only a ligature level drop down. Perhaps have some text next to the drop down and describe for each selection the side effects of choosing a level. If zero is chosen, the most expedient rendering will be used. When levels 1 and 2 are chosen, a slower but more complete rendering system will be used.
Another way is to have two drop downs. One for the rendering engine and another for ligatures. Selecting core graphics for the rendering engine would disable the ligature drop down. Selecting core text would open the ligature drop down options. This is the preferred approach, I'd think.
As for the whitelist, in option two, simply selecting a known font would preselect the appropriate rendering engine and ligature level.
Hm, I can't reproduce it. I have noticed that there have been many different versions of FiraCode. Can you install the most recent version from https://github.com/tonsky/FiraCode/releases and see if the problem persists? If so I'll give you a build with some extra logging to see if we're doing anything wrong.
@perryprog Please make a debug log using tomorrow's nightly build. In it, create a new tab using a profile whose font is FiraCode. Instructions on creating a debug log are at https://iterm2.com/debuglog
@christian-oudard Tomorrow's nightly build adds Monoid to the whitelist.
@perryprog that vimeo video is kind of blurry but I think I see what's happening: if two characters of a ligature have different colors then they're rendered separately. For example, because of fish shell's syntax highlighting, I get this (expected behavior):
Please don't "fix" that - there's no sane way to figure out which color
should take dominance, and it helps when syntax highlighting in ViM or
whatever other editor purposefully separates characters with colors because
they mean semantically different things.
I'm experiencing an interesting issue with Build 3.1.20170227-nightly on a iMac 5K running macOS 10.12.3 .
The ligatures only show up when the line contains non-ascii characters too.
I'm using a patched FiraCode-Regular and "Use a different font for non-ASCII text" is NOT selected. Adding the same font as a non-ASCII font did not change the behavior.
While at it, would it be possible to enable ligation feature set, e.g. on a per-profile setting? So when I use Iosevka, I get all the calt ligations just fine, but can't get the XML0 or any other ligatures to show up:
None at all? That's odd since I see the ones defined in the calt set just fine in the current beta. I am running my custom built version based on the default style but according to the current release page every variant except for Term should contain the ligatures.
This is the font which makes iTerm 3.1 display => as ligature but not e.g. >>= from the XHS0 feature set. I don't know if this is due to the ligatures missing in the font or an opt-in feature to get more ligatures.
Terminal.app also doesn't render >>= with a ligature. I would bring this up with the Iosevka folks. I'm not a fontographer but I don't see the calt section they claim to have in Glyphs.app.
@gnachman Thanks for looking into this. I found this discussion (with this specific comment) explaining that the font does not have ligatures per se but rather some OTF feature that must be opted in by the renderer. In CSS this can be done with font-feature-settings. In fact, I tested with the font I've sent you and set the font-feature-setting CSS property to "XHS0" in Firefox and it did display the >>= "ligature".
Ah, looks like Iosevka needs ligature level 2 to get >>= to render as as ligation. Usually that's undesirable because you get other ligatures like fi. I don't see those in there, so let me give it a try and see what breaks…it's nice to be in beta :)
I hope this is the right place @gnachman.
Would it be possible to add the patched Nerdfonts which come with ligatures to the whitelist?
Note that some were renamed due to licensing constraints. E.g. Fira Code was renamed to Fura Code, Hasklig to Hasklug.
@gnachman Would it be possible to have the ligature level or set configurable? Sorry to go back, but it turns out Iosevka has quite a some ligatures which are quite annoying if all enabled (for example .-, -., .> etc so I can't even type -.- to express my annoyance). On the other hand, in 1.13.0 they added a feature which allows picking the default ligature set so either the possibility of going back to level 1 or probably better directly picking feature flags like XML0/XHS0 would be good.
I've attached a font where I've set the default ligation to be ml, if this helps you to see what is going on in here:
To Marek's point, I spoke with you (George) about a more configurable
system for this a while back. If I have some time I can draft up a PR and
see what you think. Might be a couple of weeks. But basically, I know
you're whitelisting these things and there are potentially up to three
settings for ligatures in macOS. Off, on and full. This should likely
default to sensible things you've already figured out for known fonts but
it should be configurable as alternate option and extendable by the user.
You don't want to have to issue a new build every single time a new font
comes into existence and always off, on or full for all fonts isn't great
either.
Would be curious if your thoughts on this have changed.
@nyteshade Thanks for the offer! I have recently removed most of the the whitelisting because the number of ligature fonts has grown too large. Currently the following special behaviors are carved out:
Iosevka and IosevkaCC get ligature level 2. All other fonts get ligature level 1 by default.
FiraCode and FuraCodeNerdFont get a hint that even when ligatures are disabled, they still render ligatures.
It seems like the right way to go might be to remove the special setting for Iosevka and make the ligature level user-settable. I'm open to thoughts on this.