1
0

DocumentParsingManager.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. using System.Linq;
  2. using Content.Client.Guidebook.Controls;
  3. using Content.Client.Guidebook.Richtext;
  4. using Content.Shared.Guidebook;
  5. using Pidgin;
  6. using Robust.Client.UserInterface;
  7. using Robust.Shared.ContentPack;
  8. using Robust.Shared.Prototypes;
  9. using Robust.Shared.Reflection;
  10. using Robust.Shared.Sandboxing;
  11. using Robust.Shared.Utility;
  12. using static Pidgin.Parser;
  13. namespace Content.Client.Guidebook;
  14. /// <summary>
  15. /// This manager should be used to convert documents (shitty rich-text / pseudo-xaml) into UI Controls
  16. /// </summary>
  17. public sealed partial class DocumentParsingManager
  18. {
  19. [Dependency] private readonly IPrototypeManager _prototype = default!;
  20. [Dependency] private readonly IReflectionManager _reflectionManager = default!;
  21. [Dependency] private readonly IResourceManager _resourceManager = default!;
  22. [Dependency] private readonly ISandboxHelper _sandboxHelper = default!;
  23. private readonly Dictionary<string, Parser<char, Control>> _tagControlParsers = new();
  24. private Parser<char, Control> _controlParser = default!;
  25. private ISawmill _sawmill = default!;
  26. private Parser<char, Control> _tagParser = default!;
  27. public Parser<char, IEnumerable<Control>> ControlParser = default!;
  28. public void Initialize()
  29. {
  30. _tagParser = TryOpeningTag
  31. .Assert(_tagControlParsers.ContainsKey, tag => $"unknown tag: {tag}")
  32. .Bind(tag => _tagControlParsers[tag]);
  33. _controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser)
  34. .Before(SkipWhitespaces);
  35. foreach (var typ in _reflectionManager.GetAllChildren<IDocumentTag>())
  36. {
  37. _tagControlParsers.Add(typ.Name, CreateTagControlParser(typ.Name, typ, _sandboxHelper));
  38. }
  39. ControlParser = SkipWhitespaces.Then(_controlParser.Many());
  40. _sawmill = Logger.GetSawmill("Guidebook");
  41. }
  42. public bool TryAddMarkup(Control control, ProtoId<GuideEntryPrototype> entryId, bool log = true)
  43. {
  44. if (!_prototype.TryIndex(entryId, out var entry))
  45. return false;
  46. using var file = _resourceManager.ContentFileReadText(entry.Text);
  47. return TryAddMarkup(control, file.ReadToEnd(), log);
  48. }
  49. public bool TryAddMarkup(Control control, GuideEntry entry, bool log = true)
  50. {
  51. using var file = _resourceManager.ContentFileReadText(entry.Text);
  52. return TryAddMarkup(control, file.ReadToEnd(), log);
  53. }
  54. public bool TryAddMarkup(Control control, string text, bool log = true)
  55. {
  56. try
  57. {
  58. foreach (var child in ControlParser.ParseOrThrow(text))
  59. {
  60. control.AddChild(child);
  61. }
  62. }
  63. catch (Exception e)
  64. {
  65. _sawmill.Error($"Encountered error while generating markup controls: {e}");
  66. control.AddChild(new GuidebookError(text, e.ToStringBetter()));
  67. return false;
  68. }
  69. return true;
  70. }
  71. private Parser<char, Control> CreateTagControlParser(string tagId, Type tagType, ISandboxHelper sandbox)
  72. {
  73. return Map(
  74. (args, controls) =>
  75. {
  76. try
  77. {
  78. var tag = (IDocumentTag) sandbox.CreateInstance(tagType);
  79. if (!tag.TryParseTag(args, out var control))
  80. {
  81. _sawmill.Error($"Failed to parse {tagId} args");
  82. return new GuidebookError(args.ToString() ?? tagId, $"Failed to parse {tagId} args");
  83. }
  84. foreach (var child in controls)
  85. {
  86. control.AddChild(child);
  87. }
  88. return control;
  89. }
  90. catch (Exception e)
  91. {
  92. var output = args.Aggregate(string.Empty,
  93. (current, pair) => current + $"{pair.Key}=\"{pair.Value}\" ");
  94. _sawmill.Error($"Tag: {tagId} \n Arguments: {output}/>");
  95. return new GuidebookError($"Tag: {tagId}\nArguments: {output}", e.ToString());
  96. }
  97. },
  98. ParseTagArgs(tagId),
  99. TagContentParser(tagId))
  100. .Labelled($"{tagId} control");
  101. }
  102. // Parse a bunch of controls until we encounter a matching closing tag.
  103. private Parser<char, IEnumerable<Control>> TagContentParser(string tag)
  104. {
  105. return OneOf(
  106. Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty<Control>()),
  107. TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children"))
  108. );
  109. }
  110. }