using System.Linq; using Content.Client.Guidebook.Richtext; using Pidgin; using Robust.Client.UserInterface; using Robust.Shared.Reflection; using Robust.Shared.Sandboxing; using static Pidgin.Parser; namespace Content.Client.Guidebook; /// /// This manager should be used to convert documents (shitty rich-text / pseudo-xaml) into UI Controls /// public sealed partial class DocumentParsingManager { [Dependency] private readonly IReflectionManager _reflectionManager = default!; [Dependency] private readonly ISandboxHelper _sandboxHelper = default!; private readonly Dictionary> _tagControlParsers = new(); private Parser _tagParser = default!; private Parser _controlParser = default!; public Parser> ControlParser = default!; public void Initialize() { _tagParser = TryOpeningTag .Assert(_tagControlParsers.ContainsKey, tag => $"unknown tag: {tag}") .Bind(tag => _tagControlParsers[tag]); _controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser).Before(SkipWhitespaces); foreach (var typ in _reflectionManager.GetAllChildren()) { _tagControlParsers.Add(typ.Name, CreateTagControlParser(typ.Name, typ, _sandboxHelper)); } ControlParser = SkipWhitespaces.Then(_controlParser.Many()); } public bool TryAddMarkup(Control control, string text, bool log = true) { try { foreach (var child in ControlParser.ParseOrThrow(text)) { control.AddChild(child); } } catch (Exception e) { if (log) Logger.Error($"Encountered error while generating markup controls: {e}"); return false; } return true; } private Parser CreateTagControlParser(string tagId, Type tagType, ISandboxHelper sandbox) => Map( (args, controls) => { var tag = (IDocumentTag) sandbox.CreateInstance(tagType); if (!tag.TryParseTag(args, out var control)) { Logger.Error($"Failed to parse {tagId} args"); return new Control(); } foreach (var child in controls) { control.AddChild(child); } return control; }, ParseTagArgs(tagId), TagContentParser(tagId)).Labelled($"{tagId} control"); // Parse a bunch of controls until we encounter a matching closing tag. private Parser> TagContentParser(string tag) => OneOf( Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty()), TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children")) ); }