Add font fallback support for missing glyphs - #677
Open
MingqiZhang0 wants to merge 2 commits into
Open
Conversation
Owner
|
Hi, sorry I don't have the capacity to review PRs yet, even though the contribution is clearly valuable. With AI I might never be able to review any PRs going forward because it takes less effort to create them now than it is to review them. |
Author
|
Fair enough, AI really changes GitHub |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds configurable font fallback support to Focus for glyphs that are not available in the selected primary font, covering both editor text and UI text.
Previously, when the configured font did not contain a glyph, Focus could display a replacement glyph such as
�instead. This was especially noticeable with CJK text in source files, filenames, and directory paths when using fonts without CJK coverage.With this change, Focus keeps the configured primary font unchanged and searches an ordered fallback font stack only when a glyph is missing.
Fallbacks can be configured independently for editor and UI fonts through
font_fallbacksandfont_ui_fallbacks.The implementation is generic to missing glyphs and is not limited to CJK text.
Implementation
Simp.Dynamic_Fontnow supports a fallback font stack.When a glyph is requested:
Kerning is only applied when two adjacent glyphs come from the same font face.
The fallback stack is also part of the dynamic-font cache identity, so fonts using different ordered fallback lists do not incorrectly reuse the same cached
Dynamic_Font.Configuration
Editor and UI fallback fonts can be configured independently:
font_fallbackscontrols fallback fonts for editor text.font_ui_fallbackscontrols fallback fonts for UI text.Windows, macOS, and Linux provide platform-specific default fallback candidates.
The default configuration file includes commented examples for both fallback settings.
Since this PR adds user-facing configuration options, the config version is bumped from 25 to 26. Existing configs are not given an explicit empty fallback setting during migration, so existing users retain the platform-default behavior.
Motivation
The issue originally showed up with CJK filenames and directory names.
The underlying paths were valid, but the selected UI font did not contain the required glyphs. Focus uses its own FreeType-based text rendering rather than relying on the operating system's automatic font fallback, so missing characters followed Focus's replacement-glyph path.
The same problem applies to editor text when the selected code font does not contain a requested character.
Handling fallback in the font renderer fixes the problem at the glyph level instead of adding special handling for filenames, paths, editor buffers, or individual languages.
It also preserves the appearance of the selected primary font: fallback fonts are only used for glyphs that the primary font does not contain.
Validation
The fallback implementation was exercised against the original CJK filename/path cases and a broader generated filename stress test.
git diff --checkpassesThe current source changes were also reviewed for editor/UI fallback independence, config override behavior, explicit-empty handling, duplicate detection, fallback limits, cache identity, and cross-face kerning.
I currently do not have access to the Jai compiler, so the latest branch has not been freshly rebuilt locally.
Known limitations
This change provides font fallback, not full text shaping or universal Unicode support.
Characters that are not present in either the primary font or any configured fallback font still use Focus's existing replacement-glyph behavior.
Complex text shaping, combining sequences, emoji rendering, and East Asian display-width handling are outside the scope of this change.
This PR also does not change language tokenizers or identifier rules. Whether a character is considered valid source-code syntax is independent of whether its glyph can be rendered.
Development note
OpenAI Codex was used to assist with implementation, code migration, review, and test automation.
The resulting changes and reported test results were manually reviewed before submission.
Screenshot
Example of mixed CJK filenames and directory names rendered through the fallback font path:
Before
After