[parsing] Add strike html tag support, keep italic/bold/strike in html tag form when reserializing
This commit is contained in:
parent
cd84b480a6
commit
2ef78e3f41
3 changed files with 147 additions and 36 deletions
|
@ -35,11 +35,13 @@ public static class MfmSerializer
|
|||
result.Append(" [search]");
|
||||
break;
|
||||
}
|
||||
case MfmBoldNode:
|
||||
case MfmBoldNode mfmBoldNode:
|
||||
{
|
||||
result.Append("**");
|
||||
var start = mfmBoldNode.Type.IsSymbol ? "**" : "<b>";
|
||||
var end = mfmBoldNode.Type.IsSymbol ? "**" : "</b>";
|
||||
result.Append(start);
|
||||
result.Append(SerializeInternal(node.Children));
|
||||
result.Append("**");
|
||||
result.Append(end);
|
||||
break;
|
||||
}
|
||||
case MfmCenterNode:
|
||||
|
@ -80,11 +82,13 @@ public static class MfmSerializer
|
|||
result.Append($"`{mfmInlineCodeNode.Code}`");
|
||||
break;
|
||||
}
|
||||
case MfmItalicNode:
|
||||
case MfmItalicNode mfmItalicNode:
|
||||
{
|
||||
result.Append('*');
|
||||
var start = mfmItalicNode.Type.IsSymbol ? "*" : "<i>";
|
||||
var end = mfmItalicNode.Type.IsSymbol ? "*" : "</i>";
|
||||
result.Append(start);
|
||||
result.Append(SerializeInternal(node.Children));
|
||||
result.Append('*');
|
||||
result.Append(end);
|
||||
break;
|
||||
}
|
||||
case MfmLinkNode mfmLinkNode:
|
||||
|
@ -125,11 +129,13 @@ public static class MfmSerializer
|
|||
result.Append("</small>");
|
||||
break;
|
||||
}
|
||||
case MfmStrikeNode:
|
||||
case MfmStrikeNode mfmStrikeNode:
|
||||
{
|
||||
result.Append("~~");
|
||||
var start = mfmStrikeNode.Type.IsSymbol ? "~~" : "<s>";
|
||||
var end = mfmStrikeNode.Type.IsSymbol ? "~~" : "</s>";
|
||||
result.Append(start);
|
||||
result.Append(SerializeInternal(node.Children));
|
||||
result.Append("~~");
|
||||
result.Append(end);
|
||||
break;
|
||||
}
|
||||
case MfmTextNode mfmTextNode:
|
||||
|
|
|
@ -19,18 +19,25 @@ module MfmNodeTypes =
|
|||
inherit MfmNode()
|
||||
do base.Children <- c |> List.map (fun x -> x :> MfmNode)
|
||||
|
||||
type InlineNodeType =
|
||||
| Symbol
|
||||
| HtmlTag
|
||||
|
||||
type MfmTextNode(v: string) =
|
||||
inherit MfmInlineNode([])
|
||||
member val Text = v
|
||||
|
||||
type MfmItalicNode(c) =
|
||||
type MfmItalicNode(c, t) =
|
||||
inherit MfmInlineNode(c)
|
||||
member val Type: InlineNodeType = t
|
||||
|
||||
type MfmBoldNode(c) =
|
||||
type MfmBoldNode(c, t) =
|
||||
inherit MfmInlineNode(c)
|
||||
member val Type: InlineNodeType = t
|
||||
|
||||
type MfmStrikeNode(c) =
|
||||
type MfmStrikeNode(c, t) =
|
||||
inherit MfmInlineNode(c)
|
||||
member val Type: InlineNodeType = t
|
||||
|
||||
type MfmInlineCodeNode(v: string) =
|
||||
inherit MfmInlineNode([])
|
||||
|
@ -240,7 +247,7 @@ module private MfmParser =
|
|||
>>. pushLine
|
||||
>>. manyTill inlineNode italicPatternAsterisk
|
||||
.>> assertLine
|
||||
|>> fun c -> MfmItalicNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmItalicNode(aggregateTextInline c, Symbol) :> MfmNode
|
||||
|
||||
let italicUnderscoreNode =
|
||||
previousCharSatisfiesNot isNotWhitespace
|
||||
|
@ -248,11 +255,11 @@ module private MfmParser =
|
|||
>>. pushLine
|
||||
>>. manyTill inlineNode italicPatternUnderscore
|
||||
.>> assertLine
|
||||
|>> fun c -> MfmItalicNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmItalicNode(aggregateTextInline c, Symbol) :> MfmNode
|
||||
|
||||
let italicTagNode =
|
||||
skipString "<i>" >>. manyTill inlineNode (skipString "</i>")
|
||||
|>> fun c -> MfmItalicNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmItalicNode(aggregateTextInline c, HtmlTag) :> MfmNode
|
||||
|
||||
let boldAsteriskNode =
|
||||
previousCharSatisfiesNot isNotWhitespace
|
||||
|
@ -260,7 +267,7 @@ module private MfmParser =
|
|||
>>. pushLine
|
||||
>>. manyTill inlineNode (skipString "**")
|
||||
.>> assertLine
|
||||
|>> fun c -> MfmBoldNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmBoldNode(aggregateTextInline c, Symbol) :> MfmNode
|
||||
|
||||
let boldUnderscoreNode =
|
||||
previousCharSatisfiesNot isNotWhitespace
|
||||
|
@ -268,16 +275,20 @@ module private MfmParser =
|
|||
>>. pushLine
|
||||
>>. manyTill inlineNode (skipString "__")
|
||||
.>> assertLine
|
||||
|>> fun c -> MfmBoldNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmBoldNode(aggregateTextInline c, Symbol) :> MfmNode
|
||||
|
||||
let boldTagNode =
|
||||
skipString "<b>" >>. manyTill inlineNode (skipString "</b>")
|
||||
|>> fun c -> MfmBoldNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmBoldNode(aggregateTextInline c, HtmlTag) :> MfmNode
|
||||
|
||||
let strikeNode =
|
||||
skipString "~~" >>. pushLine >>. manyTill inlineNode (skipString "~~")
|
||||
.>> assertLine
|
||||
|>> fun c -> MfmStrikeNode(aggregateTextInline c) :> MfmNode
|
||||
|>> fun c -> MfmStrikeNode(aggregateTextInline c, Symbol) :> MfmNode
|
||||
|
||||
let strikeTagNode =
|
||||
skipString "<s>" >>. manyTill inlineNode (skipString "</s>")
|
||||
|>> fun c -> MfmStrikeNode(aggregateTextInline c, HtmlTag) :> MfmNode
|
||||
|
||||
let codeNode =
|
||||
codePattern >>. pushLine >>. manyCharsTill anyChar codePattern .>> assertLine
|
||||
|
@ -430,6 +441,14 @@ module private MfmParser =
|
|||
| Simple
|
||||
|
||||
let parseNode (m: ParseMode) =
|
||||
let inlineTagNodes =
|
||||
[ plainNode
|
||||
smallNode
|
||||
italicTagNode
|
||||
boldTagNode
|
||||
strikeTagNode
|
||||
urlNodeBrackets ]
|
||||
|
||||
let prefixedNode (m: ParseMode) : Parser<MfmNode, UserState> =
|
||||
fun (stream: CharStream<_>) ->
|
||||
match (stream.Peek(), m) with
|
||||
|
@ -450,7 +469,7 @@ module private MfmParser =
|
|||
| ':', (Full | Inline | Simple) -> emojiCodeNode
|
||||
| '~', (Full | Inline) when stream.Match "~~" -> strikeNode
|
||||
| '[', (Full | Inline) -> linkNode
|
||||
| '<', (Full | Inline) -> choice [ plainNode; smallNode; italicTagNode; boldTagNode; urlNodeBrackets ]
|
||||
| '<', (Full | Inline) -> choice inlineTagNodes
|
||||
| '<', Simple when stream.Match "<plain>" -> plainNode
|
||||
| '\\', (Full | Inline) when stream.Match "\\(" -> mathNode
|
||||
| '$', (Full | Inline) when stream.Match "$[" -> fnNode
|
||||
|
|
|
@ -14,30 +14,73 @@ public class MfmTests
|
|||
[TestMethod]
|
||||
public void TestParseBoldItalic()
|
||||
{
|
||||
List<MfmNode> expected =
|
||||
// @formatter:off
|
||||
List<MfmNode> expected123 =
|
||||
[
|
||||
new MfmItalicNode(ListModule.OfSeq<MfmInlineNode>([
|
||||
new MfmTextNode("italic "),
|
||||
new MfmBoldNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("bold")])),
|
||||
new MfmBoldNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("bold")]), InlineNodeType.Symbol),
|
||||
new MfmTextNode(" italic")
|
||||
]))
|
||||
]), InlineNodeType.Symbol)
|
||||
];
|
||||
|
||||
List<MfmNode> expected4 =
|
||||
[
|
||||
new MfmItalicNode(ListModule.OfSeq<MfmInlineNode>([
|
||||
new MfmTextNode("italic "),
|
||||
new MfmBoldNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("bold")]), InlineNodeType.HtmlTag),
|
||||
new MfmTextNode(" italic")
|
||||
]), InlineNodeType.HtmlTag)
|
||||
];
|
||||
|
||||
List<MfmNode> expected5 =
|
||||
[
|
||||
new MfmItalicNode(ListModule.OfSeq<MfmInlineNode>([
|
||||
new MfmTextNode("italic "),
|
||||
new MfmBoldNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("bold")]), InlineNodeType.Symbol),
|
||||
new MfmTextNode(" italic")
|
||||
]), InlineNodeType.HtmlTag)
|
||||
];
|
||||
|
||||
List<MfmNode> expected6 =
|
||||
[
|
||||
new MfmItalicNode(ListModule.OfSeq<MfmInlineNode>([
|
||||
new MfmTextNode("italic "),
|
||||
new MfmBoldNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("bold")]), InlineNodeType.HtmlTag),
|
||||
new MfmTextNode(" italic")
|
||||
]), InlineNodeType.Symbol)
|
||||
];
|
||||
// @formatter:on
|
||||
|
||||
var res = Mfm.parse("*italic **bold** italic*").ToList();
|
||||
var resAlt = Mfm.parse("_italic **bold** italic_").ToList();
|
||||
var resAlt2 = Mfm.parse("_italic __bold__ italic_").ToList();
|
||||
var resAlt3 = Mfm.parse("<i>italic <b>bold</b> italic</i>").ToList();
|
||||
var resMixed = Mfm.parse("<i>italic **bold** italic</i>").ToList();
|
||||
var resMixedAlt = Mfm.parse("*italic <b>bold</b> italic*").ToList();
|
||||
const string input = "*italic **bold** italic*";
|
||||
const string input2 = "_italic **bold** italic_";
|
||||
const string input3 = "_italic __bold__ italic_";
|
||||
const string input4 = "<i>italic <b>bold</b> italic</i>";
|
||||
const string input5 = "<i>italic **bold** italic</i>";
|
||||
const string input6 = "*italic <b>bold</b> italic*";
|
||||
|
||||
var res = Mfm.parse(input).ToList();
|
||||
var res2 = Mfm.parse(input2).ToList();
|
||||
var res3 = Mfm.parse(input3).ToList();
|
||||
var res4 = Mfm.parse(input4).ToList();
|
||||
var res5 = Mfm.parse(input5).ToList();
|
||||
var res6 = Mfm.parse(input6).ToList();
|
||||
|
||||
AssertionOptions.FormattingOptions.MaxDepth = 100;
|
||||
|
||||
res.Should().Equal(expected, MfmNodeEqual);
|
||||
resAlt.Should().Equal(expected, MfmNodeEqual);
|
||||
resAlt2.Should().Equal(expected, MfmNodeEqual);
|
||||
resAlt3.Should().Equal(expected, MfmNodeEqual);
|
||||
resMixed.Should().Equal(expected, MfmNodeEqual);
|
||||
resMixedAlt.Should().Equal(expected, MfmNodeEqual);
|
||||
res.Should().Equal(expected123, MfmNodeEqual);
|
||||
res2.Should().Equal(expected123, MfmNodeEqual);
|
||||
res3.Should().Equal(expected123, MfmNodeEqual);
|
||||
res4.Should().Equal(expected4, MfmNodeEqual);
|
||||
res5.Should().Equal(expected5, MfmNodeEqual);
|
||||
res6.Should().Equal(expected6, MfmNodeEqual);
|
||||
|
||||
MfmSerializer.Serialize(res).Should().BeEquivalentTo(input);
|
||||
MfmSerializer.Serialize(res2).Should().BeEquivalentTo(input);
|
||||
MfmSerializer.Serialize(res3).Should().BeEquivalentTo(input);
|
||||
MfmSerializer.Serialize(res4).Should().BeEquivalentTo(input4);
|
||||
MfmSerializer.Serialize(res5).Should().BeEquivalentTo(input5);
|
||||
MfmSerializer.Serialize(res6).Should().BeEquivalentTo(input6);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -58,7 +101,7 @@ public class MfmTests
|
|||
expected =
|
||||
[
|
||||
new MfmTextNode("test "),
|
||||
new MfmItalicNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("test")])),
|
||||
new MfmItalicNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("test")]), InlineNodeType.Symbol),
|
||||
new MfmTextNode("test")
|
||||
];
|
||||
|
||||
|
@ -66,6 +109,31 @@ public class MfmTests
|
|||
Mfm.parse("test _test_test").ToList().Should().Equal(expected, MfmNodeEqual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestStrike()
|
||||
{
|
||||
const string input = "~~test~~";
|
||||
const string input2 = "<s>test</s>";
|
||||
List<MfmNode> expected =
|
||||
[
|
||||
new MfmStrikeNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("test")]), InlineNodeType.Symbol)
|
||||
];
|
||||
|
||||
List<MfmNode> expected2 =
|
||||
[
|
||||
new MfmStrikeNode(ListModule.OfSeq<MfmInlineNode>([new MfmTextNode("test")]), InlineNodeType.HtmlTag)
|
||||
];
|
||||
|
||||
var res = Mfm.parse(input).ToList();
|
||||
var res2 = Mfm.parse(input2).ToList();
|
||||
|
||||
res.Should().Equal(expected, MfmNodeEqual);
|
||||
res2.Should().Equal(expected2, MfmNodeEqual);
|
||||
|
||||
MfmSerializer.Serialize(res).Should().BeEquivalentTo(input);
|
||||
MfmSerializer.Serialize(res2).Should().BeEquivalentTo(input2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestParseList()
|
||||
{
|
||||
|
@ -511,6 +579,24 @@ public class MfmTests
|
|||
{
|
||||
case MfmTextNode textNode when ((MfmTextNode)b).Text != textNode.Text:
|
||||
return false;
|
||||
case MfmItalicNode ax:
|
||||
{
|
||||
var bx = (MfmItalicNode)b;
|
||||
if (!bx.Type.Equals(ax.Type)) return false;
|
||||
break;
|
||||
}
|
||||
case MfmBoldNode ax:
|
||||
{
|
||||
var bx = (MfmBoldNode)b;
|
||||
if (!bx.Type.Equals(ax.Type)) return false;
|
||||
break;
|
||||
}
|
||||
case MfmStrikeNode ax:
|
||||
{
|
||||
var bx = (MfmStrikeNode)b;
|
||||
if (!bx.Type.Equals(ax.Type)) return false;
|
||||
break;
|
||||
}
|
||||
case MfmMentionNode ax:
|
||||
{
|
||||
var bx = (MfmMentionNode)b;
|
||||
|
|
Loading…
Add table
Reference in a new issue