Skip to content

Commit

Permalink
Add the ability to override markup named colors
Browse files Browse the repository at this point in the history
  • Loading branch information
sliekens committed Nov 3, 2024
1 parent 9149d90 commit e34f288
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 119 deletions.
3 changes: 2 additions & 1 deletion .markdownlint.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"allowed_elements": [
"details",
"summary",
"span"
"span",
"br"
]
}
}
41 changes: 30 additions & 11 deletions GW2SDK/Features/Markup/MarkupColorName.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,55 @@
using static System.StringComparison;
using System.Collections.ObjectModel;

namespace GuildWars2.Markup;

/// <summary>The color names used in markup.</summary>
[PublicAPI]
public static class MarkupColorName
{
/// <summary>The color for flavor text.</summary>
/// <summary>The color for flavor text. Color used in game: Aqua.</summary>
public static string Flavor => "@flavor";

/// <summary>The color for reminder text.</summary>
/// <summary>The color for reminder text. Color used in game: Gray.</summary>
public static string Reminder => "@reminder";

/// <summary>The color for ability type text.</summary>
/// <summary>The color for ability type text. Color used in game: Light Yellow.</summary>
public static string AbilityType => "@abilitytype";

/// <summary>The color for warning text.</summary>
/// <summary>The color for warning text. Color used in game: Red.</summary>
public static string Warning => "@warning";

/// <summary>The color for task text.</summary>
/// <summary>The color for task text. Color used in game: Gold.</summary>
public static string Task => "@task";

/// <summary>
/// A dictionary that maps color names to their corresponding hex color codes, based on colors picked from the game.
/// </summary>
/// <remarks>
/// The dictionary is case-insensitive and contains the following default mappings:
/// <list type="bullet">
/// <item><term>@abilitytype</term><description>#ffee88 (light yellow)</description></item>
/// <item><term>@flavor</term><description>#99dddd (aqua)</description></item>
/// <item><term>@reminder</term><description>#aaaaaa (gray)</description></item>
/// <item><term>@task</term><description>#ffcc55 (gold)</description></item>
/// <item><term>@warning</term><description>#ff0000 (red)</description></item>
/// </list>
/// </remarks>
public static readonly IReadOnlyDictionary<string, string> DefaultColorMap =
new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
[AbilityType] = "#ffee88",
[Flavor] = "#99dddd",
[Reminder] = "#aaaaaa",
[Task] = "#ffcc55",
[Warning] = "#ff0000"
});

/// <summary>Determines whether the specified color name is defined.</summary>
/// <remarks>The name should include the leading '@' symbol, e.g. '@flavor'.</remarks>
/// <param name="colorName">The name of the color to check.</param>
/// <returns><c>true</c> if the specified color name is defined; otherwise, <c>false</c>.</returns>
public static bool IsDefined(string colorName)
{
return string.Equals(colorName, Flavor, OrdinalIgnoreCase)
|| string.Equals(colorName, Reminder, OrdinalIgnoreCase)
|| string.Equals(colorName, AbilityType, OrdinalIgnoreCase)
|| string.Equals(colorName, Warning, OrdinalIgnoreCase)
|| string.Equals(colorName, Task, OrdinalIgnoreCase);
return DefaultColorMap.ContainsKey(colorName);
}
}
19 changes: 18 additions & 1 deletion GW2SDK/Features/Markup/MarkupConverter.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;

namespace GuildWars2.Markup;

/// <summary>Provides functionality to convert markup strings to other formats.</summary>
Expand Down Expand Up @@ -27,7 +29,7 @@ public static string ToPlainText(string markup)
return TextConverter.Convert(rootNode);
}

/// <summary>Converts a markup string to a string with HTML formatting.</summary>
/// <summary>Converts a markup string to a string with HTML formatting using the <see cref="MarkupColorName.DefaultColorMap"/>.</summary>
/// <param name="markup">The markup string to convert.</param>
/// <returns>The HTML string.</returns>
public static string ToHtml(string markup)
Expand All @@ -42,4 +44,19 @@ public static string ToHtml(string markup)
return HtmlConverter.Convert(rootNode);
}

/// <summary>Converts a markup string to a string with HTML formatting using a custom color map.</summary>
/// <param name="markup">The markup string to convert.</param>
/// <param name="colorMap">A dictionary mapping color names to their corresponding HTML color codes.</param>
/// <returns>The HTML string.</returns>
public static string ToHtml(string markup, IReadOnlyDictionary<string, string> colorMap)
{
if (string.IsNullOrWhiteSpace(markup))
{
return markup;
}

var tokens = Lexer.Tokenize(markup);
var rootNode = Parser.Parse(tokens);
return HtmlConverter.Convert(rootNode, colorMap);
}
}
57 changes: 33 additions & 24 deletions GW2SDK/Features/Markup/MarkupHtmlConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,41 @@ namespace GuildWars2.Markup;
public sealed class MarkupHtmlConverter
{
/// <summary>
/// Converts a <see cref="RootNode"/> to its HTML representation.
/// Converts a <see cref="RootNode"/> to its HTML representation using the <see cref="MarkupColorName.DefaultColorMap"/>.
/// </summary>
/// <param name="root">The root node containing nodes to be converted.</param>
/// <returns>A string representation of the nodes within the root node.</returns>
/// <param name="root">The root node of the markup syntax tree to convert.</param>
/// <returns>A string containing the HTML representation of the markup syntax tree.</returns>
public string Convert(RootNode root)
{
return Convert(root, MarkupColorName.DefaultColorMap);
}

/// <summary>Converts a <see cref="RootNode"/> and its children to an HTML string representation using a custom color map.</summary>
/// <param name="root">The root node of the markup syntax tree to convert.</param>
/// <param name="colorMap">A dictionary mapping color names to their corresponding HTML color codes.</param>
/// <returns>A string containing the HTML representation of the markup syntax tree.</returns>
public string Convert(RootNode root, IReadOnlyDictionary<string, string>? colorMap)
{
if (colorMap == null)
{
colorMap = MarkupColorName.DefaultColorMap;
}
else if (colorMap != MarkupColorName.DefaultColorMap)
{
// Ensure the key comparison is case-insensitive
colorMap = colorMap.ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase);
}

var builder = new StringBuilder();
foreach (var node in root.Children)
{
builder.Append(ConvertNode(node));
builder.Append(ConvertNode(node, colorMap));
}

return builder.ToString();
}

private string ConvertNode(MarkupNode node)
private string ConvertNode(MarkupNode node, IReadOnlyDictionary<string, string> colorMap)
{
switch (node)
{
Expand All @@ -33,30 +52,20 @@ private string ConvertNode(MarkupNode node)
case LineBreakNode:
return "<br>";
case ColoredTextNode coloredText:
var content = string.Concat(coloredText.Children.Select(ConvertNode));
if (coloredText.Color.StartsWith("#"))
var builder = new StringBuilder();
foreach (var child in coloredText.Children)
{
return $"<span style=\"color: {coloredText.Color}\">{content}</span>";
builder.Append(ConvertNode(child, colorMap));
}
else if (string.Equals(coloredText.Color, MarkupColorName.Flavor, StringComparison.OrdinalIgnoreCase))
{
return $"<span style=\"color: #9BE8E4\">{content}</span>";
}
else if (string.Equals(coloredText.Color, MarkupColorName.Reminder, StringComparison.OrdinalIgnoreCase))
{
return $"<span style=\"color: #B0B0B0\">{content}</span>";
}
else if (string.Equals(coloredText.Color, MarkupColorName.AbilityType, StringComparison.OrdinalIgnoreCase))
{
return $"<span style=\"color: #FFEC8C\">{content}</span>";
}
else if (string.Equals(coloredText.Color, MarkupColorName.Warning, StringComparison.OrdinalIgnoreCase))

var content = builder.ToString();
if (coloredText.Color.StartsWith("#"))
{
return $"<span style=\"color: #ED0002\">{content}</span>";
return $"<span style=\"color: {coloredText.Color}\">{content}</span>";
}
else if (string.Equals(coloredText.Color, MarkupColorName.Task, StringComparison.OrdinalIgnoreCase))
else if (colorMap.TryGetValue(coloredText.Color, out var color))
{
return $"<span style=\"color: #FFC957\">{content}</span>";
return $"<span style=\"color: {color}\">{content}</span>";
}
else
{
Expand Down
Loading

0 comments on commit e34f288

Please sign in to comment.