* 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>
143 lines
6.1 KiB
C#
143 lines
6.1 KiB
C#
using System.Linq;
|
||
using Pidgin;
|
||
using Robust.Client.UserInterface;
|
||
using Robust.Client.UserInterface.Controls;
|
||
using Robust.Shared.Utility;
|
||
using static Pidgin.Parser;
|
||
using static Pidgin.Parser<char>;
|
||
using static Robust.Client.UserInterface.Control;
|
||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||
|
||
namespace Content.Client.Guidebook;
|
||
|
||
public sealed partial class DocumentParsingManager
|
||
{
|
||
private const string ListBullet = " › ";
|
||
|
||
#region Text Parsing
|
||
#region Basic Text Parsing
|
||
// Try look for an escaped character. If found, skip the escaping slash and return the character.
|
||
private static readonly Parser<char, char> TryEscapedChar = Try(Char('\\').Then(OneOf(
|
||
Try(Char('<')),
|
||
Try(Char('>')),
|
||
Try(Char('\\')),
|
||
Try(Char('-')),
|
||
Try(Char('=')),
|
||
Try(Char('"')),
|
||
Try(Char(' ')),
|
||
Try(Char('n')).ThenReturn('\n'),
|
||
Try(Char('t')).ThenReturn('\t')
|
||
)));
|
||
|
||
private static readonly Parser<char, Unit> SkipNewline = Whitespace.SkipUntil(Char('\n'));
|
||
|
||
private static readonly Parser<char, char> TrySingleNewlineToSpace = Try(SkipNewline).Then(SkipWhitespaces).ThenReturn(' ');
|
||
|
||
private static readonly Parser<char, char> TextChar = OneOf(
|
||
TryEscapedChar, // consume any backslashed being used to escape text
|
||
TrySingleNewlineToSpace, // turn single newlines into spaces
|
||
Any // just return the character.
|
||
);
|
||
|
||
// like TextChar, but not skipping whitespace around newlines
|
||
private static readonly Parser<char, char> QuotedTextChar = OneOf(TryEscapedChar, Any);
|
||
|
||
// Quoted text
|
||
private static readonly Parser<char, string> QuotedText = Char('"').Then(QuotedTextChar.Until(Try(Char('"'))).Select(string.Concat)).Labelled("quoted text");
|
||
#endregion
|
||
|
||
#region rich text-end markers
|
||
private static readonly Parser<char, Unit> TryStartList = Try(SkipNewline.Then(SkipWhitespaces).Then(Char('-'))).Then(SkipWhitespaces);
|
||
private static readonly Parser<char, Unit> TryStartTag = Try(Char('<')).Then(SkipWhitespaces);
|
||
private static readonly Parser<char, Unit> TryStartParagraph = Try(SkipNewline.Then(SkipNewline)).Then(SkipWhitespaces);
|
||
private static readonly Parser<char, Unit> TryLookTextEnd = Lookahead(OneOf(TryStartTag, TryStartList, TryStartParagraph, Try(Whitespace.SkipUntil(End))));
|
||
#endregion
|
||
|
||
// parses text characters until it hits a text-end
|
||
private static readonly Parser<char, string> TextParser = TextChar.AtLeastOnceUntil(TryLookTextEnd).Select(string.Concat);
|
||
|
||
private static readonly Parser<char, Control> TextControlParser = Try(Map(text =>
|
||
{
|
||
var rt = new RichTextLabel()
|
||
{
|
||
HorizontalExpand = true,
|
||
Margin = new Thickness(0, 0, 0, 15.0f),
|
||
};
|
||
|
||
var msg = new FormattedMessage();
|
||
// THANK YOU RICHTEXT VERY COOL
|
||
// (text doesn't default to white).
|
||
msg.PushColor(Color.White);
|
||
msg.AddMarkup(text);
|
||
msg.Pop();
|
||
rt.SetMessage(msg);
|
||
return rt;
|
||
}, TextParser).Cast<Control>()).Labelled("richtext");
|
||
#endregion
|
||
|
||
#region Headers
|
||
private static readonly Parser<char, Control> HeaderControlParser = Try(Char('#')).Then(SkipWhitespaces.Then(Map(text => new Label()
|
||
{
|
||
Text = text,
|
||
StyleClasses = { "LabelHeadingBigger" }
|
||
}, AnyCharExcept('\n').AtLeastOnceString()).Cast<Control>())).Labelled("header");
|
||
|
||
private static readonly Parser<char, Control> SubHeaderControlParser = Try(String("##")).Then(SkipWhitespaces.Then(Map(text => new Label()
|
||
{
|
||
Text = text,
|
||
StyleClasses = { "LabelHeading" }
|
||
}, AnyCharExcept('\n').AtLeastOnceString()).Cast<Control>())).Labelled("subheader");
|
||
|
||
private static readonly Parser<char, Control> TryHeaderControl = OneOf(SubHeaderControlParser, HeaderControlParser);
|
||
#endregion
|
||
|
||
// Parser that consumes a - and then just parses normal rich text with some prefix text (a bullet point).
|
||
private static readonly Parser<char, Control> ListControlParser = Try(Char('-')).Then(SkipWhitespaces).Then(Map(
|
||
control => new BoxContainer()
|
||
{
|
||
Children = { new Label() { Text = ListBullet, VerticalAlignment = VAlignment.Top, }, control },
|
||
Orientation = LayoutOrientation.Horizontal,
|
||
}, TextControlParser).Cast<Control>()).Labelled("list");
|
||
|
||
#region Tag Parsing
|
||
// closing brackets for tags
|
||
private static readonly Parser<char, Unit> TagEnd = Char('>').Then(SkipWhitespaces);
|
||
private static readonly Parser<char, Unit> ImmediateTagEnd = String("/>").Then(SkipWhitespaces);
|
||
|
||
private static readonly Parser<char, Unit> TryLookTagEnd = Lookahead(OneOf(Try(TagEnd), Try(ImmediateTagEnd)));
|
||
|
||
//parse tag argument key. any normal text character up until we hit a "="
|
||
private static readonly Parser<char, string> TagArgKey = LetterOrDigit.Until(Char('=')).Select(string.Concat).Labelled("tag argument key");
|
||
|
||
// parser for a singular tag argument. Note that each TryQuoteOrChar will consume a whole quoted block before the Until() looks for whitespace
|
||
private static readonly Parser<char, (string, string)> TagArgParser = Map((key, value) => (key, value), TagArgKey, QuotedText).Before(SkipWhitespaces);
|
||
|
||
// parser for all tag arguments
|
||
private static readonly Parser<char, IEnumerable<(string, string)>> TagArgsParser = TagArgParser.Until(TryLookTagEnd);
|
||
|
||
// parser for an opening tag.
|
||
private static readonly Parser<char, string> TryOpeningTag =
|
||
Try(Char('<'))
|
||
.Then(SkipWhitespaces)
|
||
.Then(TextChar.Until(OneOf(Whitespace.SkipAtLeastOnce(), TryLookTagEnd)))
|
||
.Select(string.Concat).Labelled($"opening tag");
|
||
|
||
private static Parser<char, Dictionary<string, string>> ParseTagArgs(string tag)
|
||
{
|
||
return TagArgsParser.Labelled($"{tag} arguments")
|
||
.Select(x => x.ToDictionary(y => y.Item1, y => y.Item2))
|
||
.Before(SkipWhitespaces);
|
||
}
|
||
|
||
private static Parser<char, Unit> TryTagTerminator(string tag)
|
||
{
|
||
return Try(String("</"))
|
||
.Then(SkipWhitespaces)
|
||
.Then(String(tag))
|
||
.Then(SkipWhitespaces)
|
||
.Then(TagEnd)
|
||
.Labelled($"closing {tag} tag");
|
||
}
|
||
#endregion
|
||
}
|