Fix #297799: color picker in private fields#307902
Fix #297799: color picker in private fields#307902martacbraga wants to merge 1 commit intomicrosoft:mainfrom
Conversation
Update regex to avoid matching hex colors outside strings. Prevent false positives for private fields (#foo). Preserve valid cases like "#fff".
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
This PR adjusts the editor’s default document color detection to avoid false positives where JavaScript/TypeScript private fields (e.g. #facade) were being detected as hex colors, and updates the associated unit tests.
Changes:
- Tighten the early-validation regex for hex colors to reduce matches outside quoted strings.
- Remove an outdated test that asserted hex detection after leading whitespace.
- Add regression coverage for JS/TS private fields that resemble hex colors.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/vs/editor/common/languages/defaultDocumentColorsComputer.ts |
Updates the initial color-detection regex to avoid matching #... in more non-string contexts. |
src/vs/editor/test/common/languages/defaultDocumentColorsComputer.test.ts |
Updates tests to reflect the new matching behavior and adds a regression test for private fields. |
| // Early validation for RGB and HSL (including CSS Level 4 syntax with / separator) | ||
| const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%\/]*\))|^(#)([A-Fa-f0-9]{3})\b|^(#)([A-Fa-f0-9]{4})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b|(?<=['"\s])(#)([A-Fa-f0-9]{3})\b|(?<=['"\s])(#)([A-Fa-f0-9]{4})\b|(?<=['"\s])(#)([A-Fa-f0-9]{6})\b|(?<=['"\s])(#)([A-Fa-f0-9]{8})\b/gm; | ||
| const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%\/]*\))|^(#)([A-Fa-f0-9]{3})\b|^(#)([A-Fa-f0-9]{4})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{3})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{4})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{6})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{8})\b/gm; | ||
| const initialValidationMatches = _findMatches(model, initialValidationRegex); |
There was a problem hiding this comment.
The new lookbehind (?<=['"][ ]*) is variable-length because of *, which is not supported by the JS RegExp engine and will throw a SyntaxError when this module is parsed. Please rewrite this part of the regex without variable-length lookbehind (e.g. match the quote/whitespace in the overall match and adjust the computed range accordingly, or use only fixed-length lookbehinds).
| const result: IColorInformation[] = []; | ||
| // Early validation for RGB and HSL (including CSS Level 4 syntax with / separator) | ||
| const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%\/]*\))|^(#)([A-Fa-f0-9]{3})\b|^(#)([A-Fa-f0-9]{4})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b|(?<=['"\s])(#)([A-Fa-f0-9]{3})\b|(?<=['"\s])(#)([A-Fa-f0-9]{4})\b|(?<=['"\s])(#)([A-Fa-f0-9]{6})\b|(?<=['"\s])(#)([A-Fa-f0-9]{8})\b/gm; | ||
| const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%\/]*\))|^(#)([A-Fa-f0-9]{3})\b|^(#)([A-Fa-f0-9]{4})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{3})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{4})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{6})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{8})\b/gm; |
There was a problem hiding this comment.
This regex still matches bare hex colors at the start of a line via the ^(#)([A-Fa-f0-9]{...}) alternatives. That means a private field written without indentation (e.g. class Foo {\n#facade;\n}) would still be detected as a color, so the false positive isn’t fully eliminated. Consider removing the start-of-line hex alternatives (to align with the PR description of only matching inside strings), or otherwise tightening the non-string matching so it can’t match valid private field syntax.
| const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%\/]*\))|^(#)([A-Fa-f0-9]{3})\b|^(#)([A-Fa-f0-9]{4})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{3})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{4})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{6})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{8})\b/gm; | |
| const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%\/]*\))|(?<=['"][ ]*)(#)([A-Fa-f0-9]{3})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{4})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{6})\b|(?<=['"][ ]*)(#)([A-Fa-f0-9]{8})\b/gm; |
| // Test case from issue #297799 | ||
| const testCases = [ | ||
| { content: 'class Foo {\n\t#facade\n}', name: 'private field #facade should not be a color' }, | ||
| { content: 'class Component {\n\treadonly #facade = inject(Facade);\n}', name: 'private field #facade with injection should not be a color' }, |
There was a problem hiding this comment.
The added regression test only covers a 6-hex-digit private field name (#facade). Since the matcher has separate branches for 3/4/6/8-digit hex, it would be good to add a couple of additional cases (e.g. #fed, #fedc, #fedbca0/#fedbcaff) and also a no-indentation variant to ensure private fields aren’t detected regardless of formatting.
| { content: 'class Component {\n\treadonly #facade = inject(Facade);\n}', name: 'private field #facade with injection should not be a color' }, | |
| { content: 'class Component {\n\treadonly #facade = inject(Facade);\n}', name: 'private field #facade with injection should not be a color' }, | |
| { content: 'class Foo {\n\t#fed\n}', name: 'private field #fed should not be a color' }, | |
| { content: 'class Foo {\n\t#fedc\n}', name: 'private field #fedc should not be a color' }, | |
| { content: 'class Foo {\n\t#fedbca0\n}', name: 'private field #fedbca0 should not be a color' }, | |
| { content: 'class Foo {\n\t#fedbcaff\n}', name: 'private field #fedbcaff should not be a color' }, | |
| { content: 'class Foo {\n#fed\n}', name: 'private field #fed without indentation should not be a color' }, |
Fix false positives in default color detection regex
Fixes #297799
This PR updates the default color detection regex to avoid incorrectly matching JavaScript/TypeScript private fields (e.g.,
#foo) as hex colors.The default color provider applies to all languages, which caused incorrect matches in JS/TS. Since short hex colors like
#fffare mainly relevant for CSS (which has its own color picker), removing this behavior improves accuracy without affecting common use cases like"#000000"in strings.Changes
{ content: ' #ff0000', name: 'hex with whitespace before' })How to test
#foo→ not detected"#000000"→ detected