Re: [PATCH] emoji on TUI emacs
Kai Ma <[email protected]> Sat, 08 Aug 2026 20:48:33 +0200
| Newsgroups | gmane.emacs.devel |
|---|---|
| Message-ID | <[email protected]> |
Eli Zaretskii <[email protected]> writes: >> Date: Wed, 5 Aug 2026 04:45:27 +0200 >> Cc: [email protected] >> From: Kai Ma <[email protected]> >> >> Attached are the three patches that make textual Emacs work with emoji >> on modern terminal emulators. > > Thanks. Please find a few comments to the patch below. Thanks for the review. I have updated the patches accordingly. >> For your enjoyment, a smaller file containing many different kinds of >> emoji sequences is also attached. >> >> First, the test results. So far, there are 3 main issues with Emoji >> support in the textual terminals. >> >> * Issue 1: Keycap sequences like 3️⃣ (#x33 #xfe0f #x20e3) are not >> supported. They are explicitly excluded in emoji-zwj.awk: "FIXME: add >> support for Emoji_Keycap_Sequence once we learn how to respect >> FE0F/VS-16 for ASCII characters." But I'm not very sure what is missing. >> Could someone enlighten me here? > > See the (very long) discussion in bug#39799. The problem here is that > Emacs can only support a composition of a sequence of characters if > all of those characters have a glyph in the same font used for all of > them. But ASCII characters are always displayed by the default face's > font, which is normally not an Emoji font, and so doesn't usually > support VS-16. So compositions that begin with an ASCII codepoint > don't work. Thanks for the background. However, I didn't manage to find an acceptable way to support both tty and graphical displays, since, IIUC, both textual and graphical terminals reuse the same composition table. So I think I'll leave this open in tty terminals as well. > However, this doesn't have to affect TTY frames, see below. > >> (BTW, during testing I find some East_Asian_Neutral characters misalign >> because Emacs marks them as wide. I'm not sure about know the rationale. >> Patch 2 removes them so they get width 1, in accordance to the Unicode >> standard. But perhaps there's some background I'm missing?) > > We generally go by the data in the file EastAsianWidth.txt, but at > least #x2690..#x2692 look almost double-width on my system. Isn't > that what you see? #x2690..#x2692 look narrow here. I do observe some symbols that appear double-width or near double-width, e.g. on WezTerm, #x1fa60 🩠 XIANGQI RED GENERAL But they function as if they were single-width despite the appearance, so IMHO it's correct to treat them as narrow. >> A follow-up question is: should we make string-width recognize emoji >> sequences and return 2 instead of 1? Or at least update org-string-width. > > string-width considers compositions, so if it produces results > different from the actual display, it means composition_gstring_width > needs to be fixed, IMO. Thanks. It seems in lisp_string_width the auto_comp branch is limited to only graphical terminals. I removed #ifdef HAVE_WINDOW_SYSTEM and made some minor changes for tty terminals. string-width returns 2 after the patch for fully-matched composed emoji sequences on tty terminals: (string-width (string #x2665)) ;; ==> 1 (string-width (string #x2665 #xfe0f)) ;; ==> 2 (string-width (string #x1f3f4 #x200d #x2620 #xfe0f)) ;; ==> 2 And I can confirm org-table alignment works properly now. However, the column number change due to C-f, C-b, etc. is still the total sum of CHARACTER_WIDTH (not necessarily 2). Do we want to change it as well? >> +static bool >> +composite_glyph_is_emoji_sequence (struct it *it) >> +{ >> + Lisp_Object gstring; >> + int first, second; >> + >> + /* Skip static compositions. */ >> + if (it->cmp_it.ch < 0) >> + return false; >> + >> + gstring = composition_gstring_from_id (it->cmp_it.id); >> + if (it->cmp_it.from + 1 >= it->cmp_it.to) >> + return false; >> + first = XFIXNUM (LGSTRING_CHAR (gstring, it->cmp_it.from)); >> + second = XFIXNUM (LGSTRING_CHAR (gstring, it->cmp_it.from + 1)); >> + >> + /* Base + VS16/Modifier. */ >> + if (second == 0xFE0F || (0x1F3FB <= second && second <= 0x1F3FF)) >> + { >> + Lisp_Object tail = Vauto_composition_emoji_eligible_codepoints; >> + >> + FOR_EACH_TAIL (tail) >> { >> - glyph->resolved_level = 0; >> - glyph->bidi_type = UNKNOWN_BT; >> + Lisp_Object item = XCAR (tail); >> + if (FIXNUMP (item) && XFIXNUM (item) == first) >> + return true; > > Any reason not to use Fmember here? Fixed. That was my mistake. > Also, I think we should have a separate variable for the TTY case, see > below. Fixed. >> + DEFVAR_BOOL ("tty-display-emoji-force-wide", tty_display_emoji_force_wide, >> + doc: /* Whether TTY frames should always render emoji sequences as >> +wide glyphs. > > The first line of a doc string should always be a single complete > sentence. Also, "render" is not accurate here (it's the terminal that > renders them). So I suggest > > Whether Emoji sequences on TTY frames should always be considered > wide. Fixed. >> +When nil, emoji sequences whose first character's width is 1 may be >> +rendered as a narrow glyph to be compatible with some non-compliant > ^^^^^^^^ > Again, "considered" Fixed. >> + tty_display_emoji_force_wide = 1; > > Please use 'true' here instead of 1. Fixed. > I think we should leave auto-composition-emoji-eligible-codepoints > alone, and introduce a separate variable for TTY frames. The reason > is a bit subtle, and is again related to the way Emacs selects fonts > for non-ASCII characters on GUI frames. Since we want the codepoints > hard-coded above to be shown as their normal non-Emoji glyphs, > preferably using the default face's font, we cannot add them to the > 'emoji' script, thus forcing them to be displayed by fonts used for > Emoji. But OTOH, when they are followed by VS-16, we must force Emacs > to use the Emoji font for them, or else there will be no composition. > The comment above tells you that we have a trick in the code for that. > > But all of this is not relevant for TTY frames, because we don't care > about the fonts, which are the responsibility of the terminal. So I > think we should have a separate variable which lists all of the > codepoints, but used only in term.c, so as not to inadvertently affect > GUI frames. For TTY frames, we should simply have there all the > codepoints in emoji-variation-sequences.txt. Does this make sense? Yes. Added auto-composition-emoji-tty-eligible-codepoints that collects codepoints from emoji-variation-sequences.txt. > >> - print " #'compose-gstring-for-graphic))))" >> + print " #'compose-emoji-gstring))))" > > The name of the function compose-emoji-gstring is somewhat misleading. > I'd prefer something like compose-gstring-and-emoji instead. Fixed. >> +(defvar auto-compose-emoji-for-terminal t >> + "Enable auto composition of Emoji sequences on text terminals. > > Once again, this is not about enabling such composition. This is > about Emacs being in sync with what the terminal actually does with > Emoji sequences of more than one codepoint. A non-nil value makes > Emacs assume the terminal composes them. > > And I think when the user sets this to nil, he/she should also disable > auto-mode, right? Or at least to consider that? Yes. This was also the reason why t was the default value. On a second thought, this option seems to be pointless because there does not appear to be a case where you enable auto-composition-mode but do not want to compose emoji sequences. In the updated patch, I removed this option to simplify things. >> +(defun compose-emoji-gstring (gstring _direction) >> + "Compose Emoji sequences into a grapheme cluster. >> +This function is redirected to `compose-glyph-for-graphic' by >> +`auto-compose-chars' on graphical display." > > There's something wrong with the last sentence. "Redirected to" is > incorrect, I think, and there's no such thing as > compose-glyph-for-graphic. Fixed.