Relocate is_combining_char() from gui-entry.c to src/core/utf8.c
as suggested in code review. This better organizes the codebase by
placing UTF-8 character classification logic with other UTF-8
utilities in the core module.
The function now uses is_utf8() instead of checking term_type
directly, which is the appropriate method for core layer code.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Addresses issue where emoji with variation selectors (like ❣️, ♥️) were
incorrectly calculated as width 1 instead of width 2, causing display
overflow in modern terminals.
- Add variation selector detection (0xFE0F) in string_advance_with_grapheme_support()
- Apply special width handling for base emoji + variation selector combinations
- Ensures consistent width calculation with input field processing
- Fixes chat window overflow issues in terminals like Ghostty
This brings string_advance logic in line with unichar_array_advance_cluster
which already had proper variation selector handling.
This commit addresses critical UTF-8 display and input issues, particularly
with emoji variations and complex Unicode sequences that caused rendering
problems in modern terminals like Ghostty.
Key improvements:
- Enhanced utf8.c with grapheme cluster detection using utf8proc
- Fixed input field handling to preserve emoji variation selectors
- Improved text measurement and cursor positioning for complex Unicode
- Added smart paste processing for multi-codepoint characters
- Bypassed problematic TRANSLIT when not needed for UTF-8 content
- Enhanced GUI entry and readline to handle grapheme clusters properly
The implementation maintains full backward compatibility while providing
proper support for modern Unicode standards, fixing display corruption
that occurred with emoji sequences containing variation selectors.
Tested with emoji sequences like: 💕💋😘🥰💞❣️💓♥️♥️💓❣️🥰🥰😘💋💋
This adds a i_wcwidth() function that replaces mk_wcwidth(), and a
'wcwidth_implementation' setting to pick which one it wraps.
Values:
- old: uses our local mk_wcwidth() which implements unicode 5.0
- system: uses the libc-provided wcwidth(), which may be better or worse
than ours depending on how up to date the system is.
- auto: tests the system one against two characters that became
fullwidth in unicode 5.2 and 9.0 respectively. If either of them pass,
pick the system implementation, otherwise pick ours.
It defaults to auto.
mk_wcwidth() is still preferable in some cases, since the way it uses
ranges for fullwidth characters means most CJK blocks are covered even
if their characters didn't exist back then.
The "system" implementation is also wrapped to never return -1, but to
assume those unknown characters use one cell. Quoting the code:
/* Treat all unknown characters as taking one cell. This is
* the reason mk_wcwidth and other outdated implementations
* mostly worked with newer unicode, while glibc's wcwidth
* needs updating to recognize new characters.
*
* Instead of relying on that, we keep the behavior of assuming
* one cell even for glibc's implementation, which is still
* highly accurate and less of a headache overall.
*/