Files
tbd-station-14/Content.Client/Guidebook/DocumentParsingManager.cs
Leon Friedrich 22d72f56b5 Guidebook Revival (#13320)
* Fix some bugs in stations and do a little cleanup.

* Begin backporting the guidebook.

* wow that's a lot of work.

* More work, gives the monkey some more interactions.

* disco monkye.

* monky

* jobs entry.

* more writing.

* disco

* im being harassed

* fix spacing.

* i hate writing.

* Update Resources/Prototypes/Entities/Mobs/NPCs/animals.yml

Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>

* builds again

* a

* pilfer changes from AL

* fix and remove unused code

* pilfer actual guide changes from AL

* localization

* more error logs & safety checks

* replace controls button with command

* add test

* todos

* pidgin parsing

* remove old parser

* Move files and change tree sorting

* add localization and public methods.

* Add help component/verb

* rename ITag to IDocumentTag

* Fix yml and tweak tooltips

* autoclose tooltip

* Split container

* Fancier-tree

* Hover color

* txt to xml

* oops

* Curse you hidden merge conflicts

* Rename parsing manager

* Stricter arg parsing

tag args must now be of the form key="value"

* Change default args

* Moar tests

* nullable enable

* Even fancier tree

* extremely fancy trees

* better indent icons

* stricter xml and subheadings

* tweak embed margin

* Fix parsing bugs

* quick fixes.

* spain.

* ogh

* hn bmvdsyc

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
2023-01-16 02:42:22 -06:00

85 lines
2.9 KiB
C#

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;
/// <summary>
/// This manager should be used to convert documents (shitty rich-text / pseudo-xaml) into UI Controls
/// </summary>
public sealed partial class DocumentParsingManager
{
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly ISandboxHelper _sandboxHelper = default!;
private readonly Dictionary<string, Parser<char, Control>> _tagControlParsers = new();
private Parser<char, Control> _tagParser = default!;
private Parser<char, Control> _controlParser = default!;
public Parser<char, IEnumerable<Control>> 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<IDocumentTag>())
{
_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<char, Control> 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<char, IEnumerable<Control>> TagContentParser(string tag) =>
OneOf(
Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty<Control>()),
TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children"))
);
}