You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Glob ? and [...] never match an emoji or other non-BMP character: "icon?.png" misses "icon😀.png", and "x[😀🎉]" misses "x😀" #176
IsGlobMatch (TextFilter/TextFilter.cs:443-447) and ResolveGlob (TextFilter/TextFilter.cs:637-663) hand the pattern and the text to DotNet.Glob as UTF-16 code units. A character outside the Basic Multilingual Plane (emoji, CJK Extension B such as 𠮷, and so on) is a surrogate pair, so DotNet.Glob sees two characters where the user sees one.
Reproduced at f681291 with TextFilter.IsMatch(text, pattern, TextFilterType.Glob, TextFilterMatchOptions.ByWordAny):
Pattern
Text
Actual
Expected
icon?.png
icon😀.png
false
true
icon??.png
icon😀.png
true
false
??家
𠮷野家
false
true
???家
𠮷野家
true
false
??家
吉野家 (BMP)
true
true
x[😀🎉]
x😀
false
true. The class lists the character literally.
x[!a]
x😀
false
true. 😀 is not a.
Why it matters
Type-ahead filtering over file names or labels that contain emoji or rare CJK characters silently drops matching items, and the user has no way to see why.
A negated class such as [!a] excludes every supplementary character.
Do with surrogate pairs what MaskPathSeparators already does with / and \: map them to private-use characters before DotNet.Glob sees either string.
Build a per-pattern map from each distinct supplementary code point that appears in the pattern to its own private-use BMP character (for example from U+E001 upward, avoiding U+E000, which is the masked separator). Apply the map to both the pattern and the text.
In the text, replace any supplementary code point that is not in the map with one shared private-use placeholder. ?, * and negated classes then count it as one character.
Lone surrogates can be left as they are.
Acceptance criteria
The seven cases in the table above return the expected value.
The existing glob tests, including those for path-separator masking and **/ expansion, still pass.
Next step: once #175 merges, add the per-pattern code-point map and the shared placeholder for unmapped supplementary characters. Cover the seven table cases with tests.
Activity
matt-edmondson commentedon Oct 31, 2026
Triage
[!a]excluding every such character is the most surprising case. The glob path now disagrees with the fuzzy path, which FuzzySearch Bump the ktsu group with 9 updates #71/Bump the ktsu group with 11 updates #73/Bump MSTest.Sdk from 4.4.0 to 4.4.1 #90 already fixed.TextFilter.cs(IsGlobMatch,ResolveGlob). The proposed fix extends the existingMaskPathSeparatorsapproach.1/2also matches1\2, and excluding-*/*also hidesC:\temp:/and\are masked to the same character #132). That PR changes which private-use characters the separator masking uses, and this fix has to allocate around them. Land Mask / and \ to different characters in glob matching [patch] #175 first and build the code-point map on top of its masking scheme, so the two do not collide in the U+E000 range.Next step: once #175 merges, add the per-pattern code-point map and the shared placeholder for unmapped supplementary characters. Cover the seven table cases with tests.
Generated by Claude Code