alacritty/alacritty

Support for ligatures

fuine opened this issue Β· 142 comments

fuine commented

I would love to see the support for ligatures (an example of their use might be found in https://github.com/tonsky/FiraCode). Please note that i have almost no idea how do they work under the hood, and so i don't know how hard is it to implement support for them, I'm just throwing an idea here.

Anyhow i think this is a great project, congratulation! :)

jwilm commented

I would really like to support this! It falls into the categories of things Alacritty cares about which include quality font rendering and performance.

Anyhow i think this is a great project, congratulation! :)

Thanks :)

Support for ligatures would be indeed super nice. One terminal emulator that handles them quite well in my experience is Pangoterm, though I have not looked at the code to see whether it does something explicitly or it just delegates the task to Pango β€” anyway, leaving the link here as reference, in case it's useful to gather inspiration from its code.

For reference, the following screenshot shows an interesting behaviour of Pangoterm: ligatures are not handled in the lines/cells where used input is done, but only in the ones where output is shows. I suppose that it's easier to handle because it's not needed to backtrack and redraw the line where the cursor is to have ligatures applied during input:

screenshot from 2017-01-23 12-04-35

(Pangoterm on top, Alacritty on bottom. Of course, if the same line used for input receives output from a program e.g. Vim, then ligatures β€œappear”.)

Edit: BTW, Alacritty is awesome! It reminds me of GLterm, which was my go-to terminal emulator back in the early 2000s (mentioned here, the official site at http://www.pollet.net/GLterm/ seems dead). Alacritty is the first terminal which feels as fast and snappy as GLterm back in the day πŸ˜‰

I use qterminal, which has full support for ligatures and seems to have a more recent implementation of a terminal. I think it's a nice example of how to handle ligature (and unicode in general).

Qterminal and Konsole share the same implementation, AFAIK, and correctly render ligatures in input lines, excepting when under the cursor.
For further reference:
cropped ligatures in konsole

@3goliad I use konsole and always considered this behaviour to be intended. It would be nice to have a similar implementation in alacritty.

I'd love this too!

zovt commented

Anyone currently working on this?

What would need to change in order to support ligatures? And I assume ligature support would extend not just to programming fonts in ASCII, but any unicode script, correct?

I had a cursory look and it seems alacritty renders one glyph at a time and then caches it. For ligatures, an entire line needs to be rendered at a time, and this should be delegated to the platform-native text renderer. The alternative would be checking character combinations and rendering their ligatures as a new glyph, but then we're doing work that's already done by the text layout engine. I didn't continue researching further because I don't know Rust or OpenGL well enough to implement this.

tbodt commented

Not an entire line, an entire part of a line with the same formatting (since it doesn't make much sense for ligatures to cross formatting boundaries.

Probably the best way would be to read the ligature info from the font and use that to search for ligature sequences in a formatting group, then render those sequences and cache that.

Other complexities might include how to render ligatures when the characters are broken over multiple lines.

tbodt commented

It doesn't really make sense to do that IMO.

Other complexities might include how to render ligatures when the characters are broken over multiple lines.

It doesn't really make sense to do that IMO.

I would say it does: e.g. if you know you have a -> ligature and you see -\n>, it's going to look like you've entered - > accidentally.

@OJFord a ligature is supposed to be one glyph.. so how could it be broken into 2 lines?

a ligature is supposed to be one glyph.. so how could it be broken into 2 lines?

Just break it, or see how double-width characters are dealt with.

@pvaibhav Adding to my previous comment:

a ligature is supposed to be one glyph..

Maybe not. How to distinguish ff (ff) from -> (β†’)? In practice there should not be a ligature for "ff" in a monospace font? ;-)

This font supports unlimited-length ligation: https://be5invis.github.io/Iosevka/
Rendering in the browser:

========== <========> <=========> <========================> <=========>

@pvaibhav I meant that if the constituent characters are line-broken, the ligature should still be rendered (on either one of the lines) - since if you know your font ligates ->, and you see:

$ long long long long line that ends after -
>

you could easily mistake that for having an erroneous space (or linebreak) - at least, I know I would be constantly hitting backspace-backspace-("oh")---> to check.

@OJFord I don't think the ligature should be rendered on that case, wouldn't that just sometimes make your terminal to have less/more width? The behaviour of qterminal on that seems just fine (terminal on the left)
2017-07-07-1499451590_3200x1800_scrot

kxzk commented

Any update on this effort?

Collecting information on this. Here's the commit which introduced support in iTerm. Should be worth looking at for the macOS side. This commit is also relevant

I'm afraid iTerm2's code won't do you much good as all the layout happens in macOS's dreadfully slow and AFAICT single-threaded layout engine. I am of the belief that using an open source layout engine is your best betβ€”you can get at the opentype tables from macOS, but making this work goes way beyond parsing LIGA tables as there's a great deal of complexity around glyph reordering. A few other tables whose names escape me are involved. Look at FiraCode to see the madness.

Perhaps it would be useful to look at the approach https://github.com/daa84/neovim-gtk takes to ligatures (as it's also written in Rust)? I haven't isolated the commit where this support was added, however - I think it might be this: daa84/neovim-gtk@dc8d6d5

tbodt commented

That won't help, it's doing pretty much the same thing as iTerm 2.

Kitty has partial support for ligatures (char supstitution like in the case of Fira Code) but fails for Iosevka. Related discussion kovidgoyal/kitty#297

tbodt commented

After a bit of research it seems like the best way to do this would be to use HarfBuzz. That's a library where you can give it a unicode string and a font and it returns a list of glyphs. Each glyph can then be looked up in the cache and rendered in the appropriate place. The only hard part then would be deciding how many terminal cells to use for rendering the character. You could count the codepoints, or maybe round up the glyph width that HarfBuzz gives you.

The idea was to use HarfBuzz, but before switching to a HarfBuzz solution that is able to render ligatures, just render every character with HarfBuzz.

Then once that is switched to HarfBuzz we could look into expanding on it in an attempt to render multiple characters. The issue with rendering multiple characters right now is our glyph cache, so it would probably require some fundamental changes to get HarfBuzz working with ligatures.

You could count the codepoints, or maybe round up the glyph width that HarfBuzz gives you.

I think the ideal thing would be using the glyph width that HarfBuzz gives. Emojis are a decent case where codepoints would be really misleading/break things - you can combine a silly number of emojis with a ZWJ into a single rendered emoji (I realize emojis don't show up in a terminal very often, if ever, but they're still a pretty interesting test case...). The problems I can see are:

  • do you choose to center the glyph across the n cells, or do you just left-align?
  • Handling where you place the cursor also seems like it'll be weird, when you have things like arrows and emojis that behave differently.

It would be awesome to support context alternates for things like https://github.com/aftertheflood/sparks

Moved from the unicode issue.

I realize emojis don't show up in a terminal very often, if ever

FYI, yarn, Facebook's node package manager uses them liberally, as do a number of npm packages. They're definitely a thing I'm seeing more often.

Something I do myself is reading and writing e-mails using a console editor. Often I find myself wanting to write as they a nicer alternative to traditional ASCII-based emoticons πŸ˜‰

Where does this stand in the priority list, now that scrollback is out? I'm simply curious. People seem to be very interested in this feature, if the number of comments/emoji reactions on this issue is anything to go by.

@ArniDagur Because of the high complexity of this issue and because it's a very 'advanced' feature, I don't think there's any rush to get this in.

The focus right now is more to smooth out the existing features and make sure everything works properly.

just reading up on the debate here while trying to get fira code working in alacritty. seems like the general consensus is that ligatures is doable but hard, while emojis are kind of a problem. if i could make a suggestion, why not just ignore ZWJs while implementing full unicode support, until someone has time to properly address that issue? that's an explicitly supported use case from the unicode consortium.

(apologies if this isn't a super-helpful comment, this is my first foss project.)

@arthropodSeven Even if ZWJs are ignored, this is still a non-trivial issue unfortunately. So the focus lies on more fundamental problems right now.

Though an incremental approach is definitely not discouraged.

zacps commented

Kitty's discussion here is probably the most relevant for us kovidgoyal/kitty#50.

Digging through the relevant commits it looks like Kitty chose to use a fixed maximum ligature length.

I don't think it's possible to have perfect ligature support without impacting performance. Having perfect ligature support requires getting rid of the glyph cache entirely and rasterizing the entire line if it changes.

jwilm commented

Having perfect ligature support requires getting rid of the glyph cache entirely

I don't think this is true? We can still cache output of Freetype in texture atlas, and harfbuzz is used for font/glyph selection and positioning.

The skribo project that was just announced seems like good fit for alacritty. They are just starting off but it would be good to keep an eye on it.

@jwilm currently working on this. I'm splitting the Vec<RenderableCell> into a Vec<Vec<RenderableCell>> based on their line number. Is it okay to shape rows with harfbuzz, or is there something I'm missing? (btw working on it only for freetype backend, because harfbuzz readily supports that)

That approach will likely introduce a significant performance penalty.

Pretty much anything that alters Alacritty's current backing structure significantly will probably create issues.

What likely would be a better approach is to combine and shape only the specific glyph in a way where non-ligature performance is completely unaffected, making this just a small performance penalty for cells which contain ligatures.

Yes, but wouldn't figuring out which characters need to be shaped in the first place be expensive?

That is correct. This certainly is not a simple issue (which is why it has the hard tag), which is why it hasn't been addressed yet. I don't think I've heard any solution yet which would be guaranteed to work.

At the end of the day I think performance is more important than ligatures unfortunately. So we should definitely try to figure something out that doesn't hit non-ligature performance.

But using harfbuzz to shape the rows would be a major architectural improvement; it opens the way for bidi/Arabic text, proper CJK, etc.

As I have said, performance outweighs these. For most people that need support for things like bidirectional text, it's likely a better choice to use a terminal emulator explicitly made to support that anyways.

Performance is still the primary goal of Alacritty, everything else comes after that. Trade-offs sometimes have to be made to get something usable of course, but ditching the primary goal for secondary features doesn't seem like a wise decision.

If you can make this work without tanking Alacritty's performance, I'm sure everyone would absolutely love it. However that's not straight-forward.

Ok πŸ‘ I'll see how harfbuzz affects Alacritty's performance, and maybe start a base from which it could be optimized

Thanks for looking into this. Playing around with things and actually getting performance metrics to compare different approaches is never a bad thing! I just wanted to make it absolutely clear that this is not a simple issue and will likely cause a lot of frustration.

@chrisduerr Implementation question: How do I position a glyph on an exact position in the screen? Also, how do I convert from (line, col) -> (x, y)?

edit: can't deal with bold and italic at the same time... grr...

You can multiply line/col by cell width/height and add the x/y padding.

Gotten really far with this. It almost renders ;)

I've made a WIP pr here: #2181

174n commented

Any progress on this issue?

I am not exactly sure how you want ligatures to work, but they work fine with Alacritty for my use. My needs might be different that others. Specifically:

  • I want ligatures only in source code, and only for lines that I am not editing. I do not want to cat a file to the console and see the ASCII converted to something else.
  • I want the ability to control what source code types uses ligatures and what do not.

So for this I setup Alacritty to use Fira Code font. I let vim control the ligatures.

Example:
Here is a Haskell source file in Alacritty, opened with vim, where ligatures are controlled by the enomsg/vim-haskellConcealPlus plugin.

image

Notice that the line with the cursor is rendered using the ASCII, as stored in the source file. The other lines have been transformed to use special characters.

You can also see that I have the comments rendering in italics. If this fits your needs, and you want more detail on how to setup it up, I have it written out here.

PHILL commented

Personally for me, I like seeing the ligatures everywhere. I'm using Kitty currently while I wait for Alacritty to gain support.

Ooof, Im going to swap to Kitty while I wait as well, I've really been missing ligatures and this doesn't seem to be going anywhere atm, thanks for sharing!

I would also love it!

Although obviously not the optimal solution, would be it be reasonable to add initial (opt-in) Mac ligature support using the Core Text API as opposed to parsing/interpreting the font tables from scratch?

It's great. I don't know much but in some languages like Arabic or Persian some alphabets joins togother. may it's implemented by using this feature of font render, and also Konsole is the only terminal emulator renders Persian correctly.

hosxy commented

if support this feature,Could you please provide a option to disable this feature?

if support this feature,Could you please provide a option to disable this feature?

This is only a support for using some kind of fonts, if you don't use such a font, this feature won't used.

The config option for opt-out will be presented, that's for sure. It's already implemented in related Linux RP though.

@mmynsted from the looks of that, it's not actually using the ligatures from fira code, but is instead inserting equivalent unicode symbols in it's place. The plugin you mentioned doesn't actually do anything with ligatures (from their README):

The feature is used to display unicode operators in Haskell code without changing the underlying file.

Yes, it's a vim feature whereby patterns can be replaced by some other (not longer) string in representation only, indentation guides are quite a common example - i.e. replace every four spaces with whatever vertical bar like character and three spaces, for example. But nothing to do with ligatures.

(You wouldn't really want to use it to approximate ligatures, since e.g. two characters -> would become one unicode narrow arrow, and so, assuming you don't always have it enabled (because it's distracting/confusing while editing) the width of the line will be jumping around.)

allsorts was recently released, and is a PURE RUST font shaping engine. It could be useful!

But it is partially incomplete (one notable feature missing is emoji), and requires a lot more legwork as it's a lower-level API (originally designed for PDF font shaping).

wezm commented

I'm one of the Allsorts developers. Feel free to send any questions my way should using it be pursued for this.

I recently did some work on Kitty. I'd recommend that if you're going to provide this feature, you enable OpenType in general and let users pick OpenType tags as is now possible in Kitty master and will be in next Kitty version.

Why? Not every fonts puts their ligatures in the same place, and some fonts, yes, even monospaced fonts, have ffi etc ligatures.

See kovidgoyal/kitty#2248 (my PR)
And ctrlcctrlv/TT2020#3 (inspiration)

Is there an ETA?

Would love to see this, is someone working on/planning to work on this feature?

Why were the comments marked as spam? We both asked legitimate questions. There's been no activity in this issue in 4 months, so asking about the progress of the development is perfectly appropriate.

No it's not. There is not going to be any progress until someone works on it. If you want to see progress, contribute. If nobody contributes, this will never be implemented. Doesn't matter if you wait a day or 10 years, things don't just magically pop up in Alacritty because you've waited for 4 months.

I realize that changes can't be made without somebody working on them, but it makes way more sense to just say "nobody's working on this feature, so don't expect it to be implemented any time soon" rather than marking the comments as spam. I wasn't insisting that there be work done on this feature I was simply asking for more info.

b0o commented

This seems to be a difficult feature to implement for a minor aesthetic improvement. Understandably, it will be very low priority. Still, a lot of people are interested in it.

Maybe this would be a good fit for a bounty on a platform like BountySource? I would chip in a few dollars towards it, I’m sure a number of others would as well.

(The BountySource site seems to be down right now, coincidentally. I think there are other similar platforms?)

@b0o Currently this issue is the highest sorted by πŸ‘'s, and third highest in the number of comments, so how it can be low-priority is beyond me. I mean, I get how aesthetic improvements take a back seat to bugs and errors, but at 646 πŸ‘'s and counting, this issue should definitely be high up on the list of priorities.

b0o commented

@pianocomposer321 What people want isn’t always what people need. The maintainers have made it clear they don’t have the bandwidth for this, so it’s up to a volunteer to implement it.

Code isn’t manifested through sheer desire, it’s written with human effort, which isn’t free. Someone either has to donate their effort towards an aesthetic improvement or people need to come together to make it financially viable 😊

With so many people πŸ‘β€˜ing this issue as you point out, I think it’s very likely we could source a pool of money large enough to make it happen!

@b0o not sure I understand how money is going to help this issue along. In case you haven't noticed, this is GitHub, this project is open source. If the authors/maintainers were looking for money, they would have made the project commercial, not open source.

b0o commented

In case you haven't noticed, this is GitHub, this project is open source. If the authors/maintainers were looking for money, they would have made the project commercial, not open source.

The two aren’t mutually exclusive. A lot of open source work is subsidized, crowd-funded, or sponsored to make it more viable. Additionally, this would encourage other individuals to step forward to contribute the code, not just the primary project maintainers, who may not have the bandwidth regardless of any sort of bounty.

I feel that given the difficulty of the feature, its purely aesthetic nature, and the duration this feature request has been active, the fact that we haven’t seen any real progress yet indicates we may not see anyone step forward to work on it any time soon without additional incentive.

For all those who are asking about the progress of the feature - there is a WIP implementation in #2677 which, as far as I know, the original author has disappeared. If you are interested in seeing this feature, it would be excellent if you have the willing to rebase that work on current master and open a new PR to see this feature completed and merged!

If you are interested in seeing this feature, it would be excellent if you have the willing to rebase that work on current master and open a new PR to see this feature completed and merged!

Since there already have been two PRs that attempted this, I'd like to point out that just rebasing this PR is not going to help unless you're actually interested in seeing this through. And implementing it in a way that does not significantly degrade performance is not easy, which is why this is marked as hard. There's a lot of work left to do with the original PR, so please be aware of that.

@b0o ok, I've just never heard of raising money for a specific feature request before. If you really think it'll help then go for it!

We could use https://www.bountysource.com/issues/40633065-support-for-ligatures for that, however I don't really like BountySource's concept of collecting funds and keeping them for an unlimited time no matter what happens to the bountysourced task. They do have an option to have bounties expire, but they won't refund the money to your payment account (which seems to be PayPal only), but instead keep it as credit inside your bountysource account. So maybe the maintainers should set up a Liberapay account or something for the whole repo instead?

I wonder if the amount of money people are willing to contribute is commensurate with the amount of work required. If it's like a week of work at typical developer salaries; that's quite a bit. The personal value of such a feature to me is maybe $100. I'm reasonably happy with kitty though it would be good to have an alternative.

The personal value of such a feature to me is maybe $100.

Don't forget, there's 654 upvotes so far. If 100 of us pitched in $50, that $5000 would likely be worthwhile for someone to pick up the task. πŸ˜‰

I'm reasonably happy with kitty though it would be good to have an alternative.

KiTTY's okay but at times oddly opinionated. I don't like that I have to maintain a personal fork to make its clipboard handling behave in a standard way. I'd also prefer something compiled, like Alacritty.

since I can't actually contribute w/ code in this project, I'd be willing to contribute financially, definitely :-)

I wonder if the amount of money people are willing to contribute is commensurate with the amount of work required. If it's like a week of work at typical developer salaries; that's quite a bit. The personal value of such a feature to me is maybe $100. I'm reasonably happy with kitty though it would be good to have an alternative.

I'd really like this feature and my experience is that neovim in tmux on a decent sized monitor is just painfully slow on any other terminal. Getting ligatures in alacritty with minimal performance impact is something I'd chip in $100 for as well.

as @vphantom pointed out there's a lot of interest expressed here. There might actually be enough to pay for someone's time.

I'd probably chip in too. I switched to Kitty basically solely for ligatures.

@hjdivad what's a decent sized monitor? For me on a MbP 16" attached to an external 27" 4k monitor with Neovim, tmux, and Kitty it's fast/normal for me.

Also shout-out out to @b0o for a great example of a community interaction.

kuon commented

I also switched to kitty because of ligature support. I use vim in terminal and this is a nice addition.

Kitty is performing well, but has some issues Alacritty doesn't have, Alacritty is also simpler to configure in my opinion.

I'm also OK with the concept of monetary contributions. I have no experience with platforms like bounty source but I think it is really important that this is done properly, we don't want this to end in a "kickstarter" scenario. The funding should be transparent and secured.

For those desperately in need (like me) of an alacritty version with ligatures on linux:
I'm occasionally rebasing #2677 (which was initially rebased by #3456) onto master:

https://github.com/Philipp-M/alacritty/tree/continue-font-ligatures

Though since I've no overview over the alacritty codebase nor the original Pull-Request, this should be seen as experimental and could potentially break with your config, so no guarantees. For me it's working greatly though.

While the offer of monetary support is greatly appreciated, I'd advice against trying to set something up independently. If there ever is infrastructure to handle such contributions, it'll be public on the Alacritty project page on github.

However I will take this offer into account both for evaluation of the priority of this issue and evaluation for better methods of contributing to Alacritty for non-developers who cannot realistically support it by contributing the solutions themselves.

While the offer of monetary support is greatly appreciated, I'd advice against trying to set something up independently. If there ever is infrastructure to handle such contributions, it'll be public on the Alacritty project page on github.

However I will take this offer into account both for evaluation of the priority of this issue and evaluation for better methods of contributing to Alacritty for non-developers who cannot realistically support it by contributing the solutions themselves.

@b0o This is exactly what I was talking about. I've heard of financial support for a project set up by the project owner/maintainers, but never financial support for a specific feature set up by the people who want it implemented.

Just to clarify, Alacritty has used BountySource for the implementation of scrollback before. Though while that did work out okay, I'm not a huge fan of the process. So the suggestion certainly makes some sense.

b0o commented

@pianocomposer321 I haven’t personally used BountySource, but my understanding is that the award is not issued until a PR is successfully merged by the project, which allows people to basically crowd-fund issues they care about without necessarily needing to go through the project owner while still ensuring the work was actually done and approved of by the project.

It’s not like someone random would set up a direct donation campaign saying β€œgive me money I’ll and I’ll give it to whoever implements the feature”, I agree that would be sketchy.

All that said, I do think the best way to go about this would absolutely be for the project to officially set up a way to send financial support as @chrisduerr alluded to. If we’re gonna start throwing around money, I think the main project owners/contributors should definitely have a say and a cut of it.

Btw @pianocomposer321 sorry you kinda got brigaded with emojis, I didn’t intend for that to happen!

@b0o I think I understood about what BountySource was meant to do. I also was not (and am not) completely against the idea. I just seemed at the time to be completely contrary to the idea of open source software.

Btw @pianocomposer321 sorry you kinda got brigaded with emojis, I didn’t intend for that to happen!

Ok. Good to know. I guess I was a little more Ad hominem than I needed to be, and I apologize for that.

Is it worth continuing the discussion of various incentives for contribution / focus in a separate GitHub Issue?

I'd say so.

Or we can just use bountysource and get over it. I just pledged 10$.

Just be patient. Rushing in to do things yourself trying to be helpful is likely just going to cause additional maintenance overhead. Which is time maintainers cannot spend working on implementing things like ligatures.

kuon commented

While the offer of monetary support is greatly appreciated, I'd advice against trying to set something up independently. If there ever is infrastructure to handle such contributions, it'll be public on the Alacritty project page on github.

However I will take this offer into account both for evaluation of the priority of this issue and evaluation for better methods of contributing to Alacritty for non-developers who cannot realistically support it by contributing the solutions themselves.

This is also what i meant. If the Alacritty project is starting to accept funding, it should be set up properly project wise, this means:

  • Advertising the donations on the "website" (which is the github README at present)
  • Having proper legal structure to manage the fundings (by using third party service or foundations, I don't think this project is large enough to justify its own structure)
  • Keeping a logbook of all contribution and spending

It's a bit formal, but I think this is needed for proper handling of monetary contributions.

No offense but this seems overly complicated and a waste of time. Why not use bountysource ? Maybe not perfect but simple and ready.

By throwing money into monitor you won't change our priorities. Like we don't need money at all, and I don't want myself to set a price for some issues, it's not up to me to decide on such things, and especially knowing that most of them will be fixed at some point(otherwise they'll be already closed). However setting up reward for some specific issues could help folks to contribute and try, but I don't want to waste my time in reviewing subpar solutions, but I want to keep that away from alacritty.

That particular issue will be solved either by us ( I kind of want to try that myself at some point during my work on our internal font library) or by contributors. But in any case, right now most of the work is focused on 'practical features'/bug fixes and not eye candy.

A counterpoint to the above - as an occasional contributor a bounty might be a way to motivate me to give up a weekend or two to get this feature implemented.

I'm not saying I necessarily will if a bounty is raised. But the thought did cross my mind that if there was a $5000 pot, it'd be worth a few of my weekends.

(Personally I don't do much with the terminal where ligatures would be relevant, so I haven't had any motivation to pick this up. I've also got no working Linux desktop setup at the moment so I'd have to go through a bit of faff to get started - unless delivering ligature support for Alacritty on Windows is enough to claim the bounty πŸ˜„ !)

@Philipp-M's branch is working really well for me as well, I'll keep testing it and report if there are any issues!