diff --git a/Iceshrimp.MfmSharp/Parsing/MfmParser.cs b/Iceshrimp.MfmSharp/Parsing/MfmParser.cs index d95d14ef..923202be 100644 --- a/Iceshrimp.MfmSharp/Parsing/MfmParser.cs +++ b/Iceshrimp.MfmSharp/Parsing/MfmParser.cs @@ -17,10 +17,19 @@ public static class MfmParser { new AltUrlNodeParser(), new LinkNodeParser(), new SilentLinkNodeParser(), - new InlineCodeNodeParser() + new InlineCodeNodeParser(), + new EmojiCodeNodeParser(), + new MathInlineNodeParser(), + new MathBlockNodeParser(), + new CodeBlockParser() ]; + /// + /// This intentionally doesn't implement the node type UnicodeEmojiNode, both for performance and because it's not + /// needed for backend processing + /// public static IEnumerable Parse(string buffer, int position = 0, int nestLimit = 20) { + if (nestLimit <= 0) return []; var nodes = new List(); while (position < buffer.Length) { var parser = Parsers.FirstOrDefault(p => p.IsValid(buffer, position)); @@ -396,5 +405,91 @@ internal class SilentLinkNodeParser : INodeParser { } } -//TODO: still missing: FnNode, MathInlineNode, EmojiCodeNode, UnicodeEmojiNode, MfmMathBlockNode, MfmCodeBlockNode, MfmSearchNode, MfmQuoteNode +internal class EmojiCodeNodeParser : INodeParser { + private const string Char = ":"; + private static readonly Regex Full = new("^[a-z0-9_+-]+$"); + + public bool IsValid(string buffer, int position) { + if (!buffer[position..].StartsWith(Char)) + return false; + + var (start, end, _) = NodeParserAbstractions.HandlePosition(Char, buffer, position); + return end != buffer.Length && Full.IsMatch(buffer[start..end]); + } + + public (MfmNode node, int chars) Parse(string buffer, int position, int nestLimit) { + var (start, end, chars) = NodeParserAbstractions.HandlePosition(Char, buffer, position); + + var node = new MfmEmojiCodeNode { + Name = buffer[start..end] + }; + + return (node, chars); + } +} + +internal class MathInlineNodeParser : INodeParser { + private const string Pre = @"\("; + private const string Post = @"\)"; + + public bool IsValid(string buffer, int position) { + return buffer[position..].StartsWith(Pre); + } + + public (MfmNode node, int chars) Parse(string buffer, int position, int nestLimit) { + var (start, end, chars) = NodeParserAbstractions.HandlePosition(Pre, Post, buffer, position); + + var node = new MfmMathInlineNode { + Formula = buffer[start..end] + }; + + return (node, chars); + } +} + +internal class MathBlockNodeParser : INodeParser { + private const string Pre = @"\["; + private const string Post = @"\]"; + + public bool IsValid(string buffer, int position) { + return buffer[position..].StartsWith(Pre); + } + + public (MfmNode node, int chars) Parse(string buffer, int position, int nestLimit) { + var (start, end, chars) = NodeParserAbstractions.HandlePosition(Pre, Post, buffer, position); + + var node = new MfmMathBlockNode { + Formula = buffer[start..end] + }; + + return (node, chars); + } +} + +internal class CodeBlockParser : INodeParser { + private const string Char = "```"; + + public bool IsValid(string buffer, int position) { + if (!buffer[position..].StartsWith(Char)) return false; + + var (start, end, _) = NodeParserAbstractions.HandlePosition(Char, buffer, position); + return buffer[start..end].EndsWith('\n'); + } + + public (MfmNode node, int chars) Parse(string buffer, int position, int nestLimit) { + var (start, end, chars) = NodeParserAbstractions.HandlePosition(Char, buffer, position); + var split = buffer[start..end].Split('\n'); + var lang = split[0].Length > 0 ? split[0] : null; + var code = string.Join('\n', split[1..^1]); + + var node = new MfmCodeBlockNode { + Code = code, + Language = lang + }; + + return (node, chars); + } +} + +//TODO: still missing: FnNode, MfmSearchNode, MfmQuoteNode //TODO: "*italic **bold** *" doesn't work yet \ No newline at end of file