using System; using System.Collections; using System.Collections.Generic; using System.Text; using JetBrains.Annotations; using Robust.Shared.Maths; using Robust.Shared.Serialization; namespace Robust.Shared.Utility { /// /// Represents a formatted message in the form of a list of "tags". /// Does not do any concrete formatting, simply useful as an API surface. /// [PublicAPI] [Serializable, NetSerializable] public sealed partial class FormattedMessage { public TagList Tags => new(_tags); private readonly List _tags; public bool IsEmpty => _tags.Count == 0; public FormattedMessage() { _tags = new List(); } public FormattedMessage(int capacity) { _tags = new List(capacity); } public static FormattedMessage FromMarkup(string markup) { var msg = new FormattedMessage(); msg.AddMarkup(markup); return msg; } public static FormattedMessage FromMarkupPermissive(string markup) { var msg = new FormattedMessage(); msg.AddMarkupPermissive(markup); return msg; } /// /// Escape a string of text to be able to be formatted into markup. /// public static string EscapeText(string text) { return text.Replace("\\", "\\\\").Replace("[", "\\["); } /// /// Remove all markup, leaving only the basic text content behind. /// public static string RemoveMarkup(string text) { return FromMarkup(text).ToString(); } /// /// Create a new FormattedMessage by copying another one. /// /// The message to copy. public FormattedMessage(FormattedMessage toCopy) { _tags = toCopy._tags.ShallowClone(); } public void AddText(string text) { _tags.Add(new TagText(text)); } public void PushColor(Color color) { _tags.Add(new TagColor(color)); } public void PushNewline() { AddText("\n"); } public void Pop() { _tags.Add(new TagPop()); } public void AddMessage(FormattedMessage other) { _tags.AddRange(other.Tags); } public void Clear() { _tags.Clear(); } /// The string without markup tags. public override string ToString() { var builder = new StringBuilder(); foreach (var tag in _tags) { if (tag is not TagText text) { continue; } builder.Append(text.Text); } return builder.ToString(); } /// The string without filtering out markup tags. public string ToMarkup() { var builder = new StringBuilder(); foreach (var tag in _tags) { builder.Append(tag); } return builder.ToString(); } [Serializable, NetSerializable] public abstract record Tag { } [Serializable, NetSerializable] public sealed record TagText(string Text) : Tag { public override string ToString() { return Text; } } [Serializable, NetSerializable] public sealed record TagColor(Color Color) : Tag { public override string ToString() { return $"[color={Color.ToHex()}]"; } } [Serializable, NetSerializable] public sealed record TagPop : Tag { public static readonly TagPop Instance = new(); public override string ToString() { return $"[/color]"; } } public readonly struct TagList : IReadOnlyList { private readonly List _tags; public TagList(List tags) { _tags = tags; } public List.Enumerator GetEnumerator() { return _tags.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _tags.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _tags.GetEnumerator(); } public int Count => _tags.Count; public Tag this[int index] => _tags[index]; } } }