Warn that an incomplete color scheme is incomplete, not missing (#11457) - #20428
Warn that an incomplete color scheme is incomplete, not missing (#11457)#20428drvoss wants to merge 4 commits into
Conversation
…osoft#11457) When a color scheme is missing one or more of its 16 colors, ColorScheme::FromJson rejects it and it is dropped, so a profile that references it later reports `UnknownColorScheme` — i.e. "that scheme doesn't exist" — even though the scheme is present, just incomplete. Collect the names of schemes that were rejected only for being incomplete (a "name" is present but the color set is short) and, when a profile references one, raise a new `IncompleteColorScheme` warning with an accurate message instead of the misleading "unknown scheme" one. Incomplete schemes are still ignored (unchanged behavior); only the diagnostic changes. ColorScheme parsing/merging/serialization is untouched.
|
Thanks! I'm thinking about this a bit. Even though #11457 mentions fragments, I think it is very important that Terminal not display errors for Fragment JSON files. They can be installed in locations that the user cannot write to, a broken Fragment file in a system folder will cause a warning message the user can never escape from. |
_validateAllSchemesExist() had no origin filtering, so a fragment-authored profile referencing a broken fragment scheme produced a settings warning the user could never dismiss -- the fragment may live in a system folder they cannot write to. Resolve the origin of the layer that actually set the colorScheme value (via the appearance's override source, as ResolveMediaResources() does for icons) and only warn for User/ProfilesDefaults references. profile.Origin() cannot be used here: fragment profiles are merged into user-owned children that FinalizeLayering() stamps OriginTag::User. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks for taking a look — you're right, and digging into it I found the problem is a bit deeper than the PR as written. First, a correction to my own PR: suppressing the fragment-origin collection isn't sufficient. Second, and more importantly: this inescapable warning already exists on So I'd like to fix the underlying issue properly, following the precedent already set by The key refinement is what we key the suppression on. I think it should be the origin of the layer that actually set the
Note the second row is the original #11457 repro, so it needs to keep warning. One trap worth calling out: I've pushed that, along with a regression test that asserts zero warnings for a fragment-authored profile pointing at a broken scheme (it fails both on Separately: |
This comment has been minimized.
This comment has been minimized.
4b5e828 to
a122255
Compare
This comment has been minimized.
This comment has been minimized.
e48b3d0 introduced _originOfColorSchemeName() to suppress UnknownColorScheme/IncompleteColorScheme warnings for references the user cannot edit (fragments, generators, defaults.json). It relied on the public <NAME>OverrideSource() accessor, which only walks *parents* and never inspects the leaf itself. When both a leaf (user settings.json) and a parent (e.g. an InBox profile) set colorScheme, the effective value is the leaf's (via _get<NAME>Impl(), which checks the leaf first), but OverrideSource() still returned the parent -- so a user's own typo could be misattributed to the InBox layer and silently swallowed. This is the opposite of what the original PR intended. Fix _originOfColorSchemeName() to check Has<NAME>() first: if the leaf set its own value, attribute origin to the leaf regardless of what any parent has. Only fall back to the walked-parents OverrideSource() when the leaf is silent, and fall back to the leaf itself when neither leaf nor any parent set a value (preserving the existing behavior relied on by ProfileTests.cpp's and TerminalSettingsTests.cpp's ==2 warning-count assertions). Also corrects an inaccurate comment: the media-resource path (AppearanceConfig::ResolveMediaResources()) does not use the public OverrideSource() accessor; it uses the private _get<NAME>OverrideSourceAndValueImpl(), which already checks the leaf first. Adds a regression test (UserOverrideOfInboxColorSchemeTypoWarns) that reproduces the leaf-vs-parent scenario via matching profile GUIDs, verified to fail against the pre-fix code and pass after (_addUserProfileParent()'s !inserted branch in CascadiaSettingsSerialization.cpp merges the InBox profile as a parent, and the pre-fix code trusted that non-null parent OverrideSource() and suppressed the user's own typo). Also asserts AllProfiles().Size() == 1 so a future guid-matching regression can't silently degrade this into an unrelated single-profile test. BrokenSchemeReferencedByFragmentProfileIsSilent is left as a pure negative control (fragment-derived profiles produce zero warnings of any kind); an earlier revision of this change also folded a positive control into it via a second, user-owned broken reference, but _validateAllSchemesExist() accumulates UnknownColorScheme into a single bool across all profiles, so that would have made the fragment-suppression assertion indistinguishable from a regression that lets a broken fragment reference through. The positive control is independently covered by UserOverrideOfInboxColorSchemeTypoWarns instead. Reformatted with clang-format (22.1.3); adding the hasOwnValue parameter to _originOfColorSchemeName() and _noteMissingColorScheme() shifted their continuation-line alignment. Fixes two check-spelling failures: rewords "typo'd" to "misspelled" in a test comment, and allow-lists "Cambell", the deliberate misspelling used as test fixture data in UserOverrideOfInboxColorSchemeTypoWarns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
a122255 to
e34d8c8
Compare
Replace the free function _originOfColorSchemeName() in CascadiaSettings.cpp with AppearanceConfig::ColorSchemeNameOrigin(), following the same pattern as ResolveMediaResources(): it delegates to the existing private _get<Name>ColorSchemeNameOverrideSourceImpl(), which already resolves the leaf-first inheritance walk in one call, instead of manually recombining Has<Name>() + <Name>OverrideSource(). This removes the hand-written leaf-vs-parent logic that e34d8c8 had to patch by hand. Also: - Correct the _noteMissingColorScheme() comment: the suppression allow-list covers InBox, Fragment, and Generated (dynamic profile generator) origins, not just fragments, and note the latent ProfilesDefaults ambiguity between user profiles.defaults and inbox defaults.json (both are stamped OriginTag::ProfilesDefaults by CascadiaSettingsSerialization.cpp's _parse()). - Remove review-history references ("see DHowett on microsoft#20428") from permanent test comments in favor of behavior-based descriptions. - Change the ColorSchemeTests fixture literal from "Cambell" to "NoSuchScheme" so the misspelling isn't mistaken for a real typo, and drop the now- unnecessary "Cambell" entry from the spelling dictionary exceptions. Verified via SettingsModel.Unit.Tests.dll: all color scheme tests pass, including UserOverrideOfInboxColorSchemeTypoWarns and BrokenSchemeReferencedByFragmentProfileIsSilent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@DHowett -- following up on your concern that fragment-referenced schemes must never warn. The revision addresses it directly. Root cause. What changed.
Tests. Known limitation, out of scope here. Inbox defaults.json and user profiles.defaults share the I've also split the separate |
|
Pushed a small follow-up cleanup that moves effective color-scheme origin resolution into |
Summary of the Pull Request
Color schemes that are missing some of their colors are rejected during parsing and
dropped. A profile that references such a scheme then produces the
UnknownColorSchemewarning ("...the value matches the name of a color scheme..."), which misleads the user
into thinking the scheme name is wrong when the scheme actually exists but is incomplete.
This adds a distinct
IncompleteColorSchemewarning so the message is accurate.References that come from a layer the user cannot edit (fragments, dynamic profile
generators,
defaults.json) are suppressed instead of warned, since a broken built-inreference would otherwise be inescapable for the user.
References and Relevant Issues
Closes #11457. Related: #11456 (added
magenta/brightMagentaaliases; note theoriginal repro no longer reproduces because of it — a genuinely missing color such as
cyanis needed).Detailed Description of the Pull Request / Additional comments
ColorScheme::FromJsononly fails when a scheme has nonameor fewer than 16 colors.When it returns null but the JSON has a non-empty
name, the scheme exists but isincomplete.
SettingsLoadernow collects those names (both user and fragment parsepaths) into
incompleteColorSchemes.CascadiaSettings::_validateAllSchemesExistraisesIncompleteColorSchemewhen aprofile references a name in that set, and
UnknownColorScheme(as before) otherwise.ColorSchemeparsing/merging/serialization is intentionally not modified, so schemelayering and round-tripping are unchanged; incomplete schemes are still ignored.
SettingsLoadWarningsenum (existing ordinalsunchanged) with an
en-USresource string.copy), an incomplete override is silently ignored without a warning — unchanged from
today. A scheme that reaches 16 colors via duplicate aliases while omitting a real slot
is likewise not flagged (pre-existing
ColorSchemecounting behavior, out of scope).defaults.jsonare suppressed via_originOfColorSchemeName(), which walks the profile's inheritance graph to find whichlayer actually set the
colorSchemevalue (fragment-contributed profiles don't appearin
_allProfileson their own —SettingsLoader::_addUserProfileParent()merges theminto a user-owned child, so
Origin()alone can't distinguish "fragment set this" from"user set this").
_originOfColorSchemeName()used only the public<NAME>OverrideSource()accessor,which walks parents but never inspects the leaf itself. When both the leaf (the
user's own
settings.json) and a parent (e.g. an in-box profile) setcolorScheme,the effective value is the leaf's — but
OverrideSource()still returned the parent,so the user's own typo was misattributed to the in-box layer and silently swallowed.
This is a realistic scenario:
defaults.jsonsetscolorSchemedirectly on theWindows PowerShell and Command Prompt profiles, so any user who overrides
colorSchemeon those same (GUID-matched) profiles hits this path. Fixed by checking
Has<NAME>()first: if the leaf set its own value, origin is attributed to the leaf regardless of
what any parent has; the walked-parent
OverrideSource()is only consulted when theleaf itself is silent. Added a regression test
(
UserOverrideOfInboxColorSchemeTypoWarns) that reproduces this via matching profileGUIDs.
Validation Steps Performed
DeserializationTests::TestIncompleteColorSchemeName(user settings path) andColorSchemeTests::IncompleteColorSchemeInFragmentWarns(fragment path).ColorSchemeTests::BrokenSchemeReferencedByFragmentProfileIsSilent(fragment /in-box references are suppressed, not warned) and
ColorSchemeTests::UserOverrideOfInboxColorSchemeTypoWarns(regression test for theleaf-vs-parent origin bug described above).
TestInvalidColorSchemeName(genuinely missing name →UnknownColorScheme) still passes.SettingsModel.Unit.Testssuite: 161/161 passing locally.clang-format(22.1.3, same binary as CI) run over all touched files with no diff.PR Checklist
How the origin of a broken reference is determined
A broken color-scheme reference now produces a warning only when the reference
originates from a layer the user can edit (
UserorProfilesDefaults). References thatoriginate from a fragment, a dynamic profile generator, or defaults.json are suppressed,
so a broken reference the user cannot remove never surfaces an inescapable warning
(per @DHowett's note in #11457).
Origin is resolved by a file-local helper,
_originOfColorSchemeName()inCascadiaSettings.cpp-- not fromprofile.Origin(). Fragment-contributed profilesare merged into a user-owned child profile and stamped
OriginTag::User, so theprofile's own origin would misattribute the reference to the user. The helper instead
walks the inheritance graph, checking the leaf (
Has*ColorSchemeName()) before trusting*ColorSchemeNameOverrideSource()(which only inspects parents)._noteMissingColorScheme()applies the
User/ProfilesDefaultsallow-list.Note: a scheme defined by a fragment but referenced by the user is still warned --
only fragment-originated references are suppressed.
Known limitation (out of scope): inbox defaults.json and user profiles.defaults share the
ProfilesDefaultsorigin tag, so both are treated as user-actionable.Split out separately: #20462 (
Clear*ColorSchemeNamedoes not clear fragment-inherited values).