Skip to content

Glob ? and [...] never match an emoji or other non-BMP character: "icon?.png" misses "icon😀.png", and "x[😀🎉]" misses "x😀" #176

Open

Description

@matt-edmondson

What's wrong

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

Suggested fix

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.

Activity

  1. matt-edmondson commented on Oct 31, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions