Skip to content

Commit 9c666ef

Browse files
committed
Fix Spectre markup color name mismatch in EscapeInvalidMarkupTags
The standardColors set in IsKnownColorName contained underscore-separated names (dark_cyan, dark_blue, etc.) that Spectre.Console's ColorTable does not recognize — it uses concatenated names (darkcyan, darkblue). This caused EscapeInvalidMarkupTags to preserve tags like [dark_cyan] as valid, but Spectre's Markup class would then throw, triggering the catch-block fallback that double-escaped the entire content. Fix: update standardColors to match Spectre's actual names, normalize input by stripping underscores/spaces before lookup, and normalize tag content in EscapeInvalidMarkupTags so [dark_cyan] is emitted as [darkcyan].
1 parent 1977999 commit 9c666ef

1 file changed

Lines changed: 41 additions & 33 deletions

File tree

SharpConsoleUI/Helpers/AnsiConsoleHelper.cs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,17 @@ public static string EscapeInvalidMarkupTags(string input)
289289
string tagContent = input.Substring(start + 1, j - start - 1);
290290
if (IsValidTagContent(tagContent))
291291
{
292-
// Append the entire tag as is
293-
result.Append(input, start, j - start + 1);
292+
// Normalize underscores in color names for Spectre compatibility.
293+
// Spectre.Console's ColorTable uses concatenated names (e.g. "darkcyan")
294+
// but callers may write "dark_cyan". Style keywords (bold, italic, etc.)
295+
// and structural tokens (/, on) don't contain underscores, so stripping
296+
// them only affects color name segments.
297+
string normalized = tagContent.Contains('_')
298+
? tagContent.Replace("_", "")
299+
: tagContent;
300+
result.Append('[');
301+
result.Append(normalized);
302+
result.Append(']');
294303
i = j + 1;
295304
continue;
296305
}
@@ -866,47 +875,46 @@ private static bool IsKnownColorName(string colorName)
866875
if (string.IsNullOrWhiteSpace(colorName))
867876
return false;
868877

869-
// Basic set of standard colors supported by Spectre.Console
878+
// Spectre.Console color names — uses concatenated lowercase with NO underscores.
879+
// The lookup is case-insensitive. This set covers the basic 16 colors, their
880+
// common aliases, and frequently-used extended 256-color palette names.
870881
var standardColors = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
871882
{
872-
"default", "black", "blue", "cyan", "dark_blue", "dark_cyan",
873-
"dark_green", "dark_grey", "dark_magenta", "dark_red", "dark_yellow",
874-
"grey", "gray", "green", "magenta", "maroon", "navy", "purple",
875-
"red", "silver", "teal", "white", "yellow",
876-
"brightblack", "bright_black", "brightblue", "bright_blue",
877-
"brightcyan", "bright_cyan", "brightgreen", "bright_green",
878-
"brightmagenta", "bright_magenta", "brightred", "bright_red",
879-
"brightwhite", "bright_white", "brightyellow", "bright_yellow",
880-
"steelblue", "steel_blue", "darkorange", "dark_orange",
881-
"lime", "olive", "aqua", "fuchsia", "darkgrey", "dark_grey",
882-
"lightgrey", "light_grey", "lightblue", "light_blue",
883-
"lightgreen", "light_green", "lightcyan", "light_cyan",
884-
"lightred", "light_red", "lightmagenta", "light_magenta",
885-
"lightyellow", "light_yellow", "cornflowerblue", "cornflower_blue",
886-
"hotpink", "hot_pink", "pink", "deeppink", "deep_pink"
883+
// Basic 16 colors
884+
"default", "black", "maroon", "green", "olive", "navy", "purple",
885+
"teal", "silver", "grey", "red", "lime", "yellow", "blue",
886+
"fuchsia", "aqua", "white",
887+
// Aliases for basic colors
888+
"gray", "magenta", "cyan",
889+
// Common extended palette names (no underscores — matches Spectre's ColorTable)
890+
"darkblue", "darkgreen", "darkcyan", "darkred", "darkmagenta",
891+
"darkviolet", "darkorange", "darkturquoise", "darkolivegreen",
892+
"darkgoldenrod", "darkseagreen", "darkslategray", "darkkhaki",
893+
"deeppink", "deepskyblue",
894+
"lightblue", "lightcyan", "lightgreen", "lightcoral",
895+
"lightgoldenrodyellow", "lightpink", "lightsalmon", "lightseagreen",
896+
"lightskyblue", "lightslategray", "lightsteelblue", "lightyellow",
897+
"mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue",
898+
"mediumspringgreen", "mediumturquoise", "mediumvioletred",
899+
"hotpink", "cornflowerblue", "steelblue", "cadetblue", "rosybrown",
900+
"plum", "orchid", "violet", "thistle", "indianred",
901+
"palegreen", "paleturquoise", "palevioletred",
902+
"springgreen", "chartreuse", "blueviolet", "greenyellow",
903+
"navyblue", "navajowhite", "mistyrose", "honeydew", "khaki",
904+
"wheat", "salmon", "sandybrown", "tan", "pink",
887905
};
888906

889-
// Check for basic color names first
890-
if (standardColors.Contains(colorName))
891-
return true;
907+
// Normalize: strip underscores and spaces so callers can use
908+
// "dark_cyan", "dark cyan", or "darkcyan" interchangeably.
909+
string normalizedName = colorName.Replace("_", "").Replace(" ", "").ToLowerInvariant();
892910

893-
// Check for web colors with underscores or camelCase (e.g., "dark_blue" or "darkblue")
894-
string normalizedName = colorName.Replace("_", "").ToLowerInvariant();
895911
if (standardColors.Contains(normalizedName))
896912
return true;
897913

898-
// Check for numbered color variants (e.g., "grey46", "orange3")
899-
if (Regex.IsMatch(colorName, @"^[a-z]+\d+$", RegexOptions.IgnoreCase))
914+
// Numbered color variants (e.g., "grey46", "orange3", "dodgerblue2")
915+
if (Regex.IsMatch(normalizedName, @"^[a-z]+\d+$", RegexOptions.IgnoreCase))
900916
return true;
901917

902-
// Check for color names with spaces (e.g., "hot pink", "light blue")
903-
if (colorName.Contains(" "))
904-
{
905-
string spacelessName = colorName.Replace(" ", "").ToLowerInvariant();
906-
if (standardColors.Contains(spacelessName))
907-
return true;
908-
}
909-
910918
return false;
911919
}
912920

0 commit comments

Comments
 (0)