supporting nested tags within a paragraph
This commit is contained in:
parent
446d729981
commit
23b004ff02
|
@ -9,7 +9,7 @@ private const val LI_VALUE_CAPTURE = "value=\""
|
||||||
|
|
||||||
internal class HtmlParser {
|
internal class HtmlParser {
|
||||||
|
|
||||||
fun parseHtmlTags(input: String, searchIndex: Int, builder: PartBuilder): SearchIndex = input.findTag(
|
fun parseHtmlTags(input: String, searchIndex: Int, builder: PartBuilder, nestingLevel: Int = 0): SearchIndex = input.findTag(
|
||||||
fromIndex = searchIndex,
|
fromIndex = searchIndex,
|
||||||
onInvalidTag = { builder.appendText(input[it].toString()) },
|
onInvalidTag = { builder.appendText(input[it].toString()) },
|
||||||
onTag = { tagOpen, tagClose ->
|
onTag = { tagOpen, tagClose ->
|
||||||
|
@ -45,7 +45,7 @@ internal class HtmlParser {
|
||||||
else -> {
|
else -> {
|
||||||
appendTextBeforeTag(searchIndex, tagOpen, builder, input)
|
appendTextBeforeTag(searchIndex, tagOpen, builder, input)
|
||||||
val tagContent = input.substring(tagClose + 1, exitIndex)
|
val tagContent = input.substring(tagClose + 1, exitIndex)
|
||||||
handleTagWithContent(input, tagName, wholeTag, builder, tagContent, exitTagCloseIndex)
|
handleTagWithContent(input, tagName, wholeTag, builder, tagContent, exitTagCloseIndex, nestingLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ internal class HtmlParser {
|
||||||
builder: PartBuilder,
|
builder: PartBuilder,
|
||||||
tagContent: String,
|
tagContent: String,
|
||||||
exitTagCloseIndex: Int,
|
exitTagCloseIndex: Int,
|
||||||
|
nestingLevel: Int,
|
||||||
) = when (tagName) {
|
) = when (tagName) {
|
||||||
"a" -> {
|
"a" -> {
|
||||||
val findHrefUrl = wholeTag.substringAfter("href=").replace("\"", "").removeSuffix(">")
|
val findHrefUrl = wholeTag.substringAfter("href=").replace("\"", "").removeSuffix(">")
|
||||||
|
@ -80,7 +81,18 @@ internal class HtmlParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
"p" -> {
|
"p" -> {
|
||||||
builder.appendText(tagContent)
|
if (tagContent.isNotEmpty() && nestingLevel < 2) {
|
||||||
|
var lastIndex = 0
|
||||||
|
iterateIndex(start = 0) { searchIndex ->
|
||||||
|
lastIndex = searchIndex
|
||||||
|
parseHtmlTags(tagContent, searchIndex, builder, nestingLevel = nestingLevel + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastIndex < tagContent.length) {
|
||||||
|
builder.appendText(tagContent.substring(lastIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder.appendNewline()
|
builder.appendNewline()
|
||||||
exitTagCloseIndex
|
exitTagCloseIndex
|
||||||
}
|
}
|
||||||
|
@ -135,10 +147,9 @@ internal class HtmlParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseList(parentTag: String, parentContent: String, builder: PartBuilder) {
|
private fun parseList(parentTag: String, parentContent: String, builder: PartBuilder) {
|
||||||
var nextIndex = 0
|
|
||||||
var index = 1
|
var index = 1
|
||||||
while (nextIndex != END_SEARCH) {
|
iterateIndex(start = 0) { nextIndex ->
|
||||||
nextIndex = singleTagParser(parentContent, "li", nextIndex, builder) { wholeTag, tagContent ->
|
singleTagParser(parentContent, "li", nextIndex, builder) { wholeTag, tagContent ->
|
||||||
val content = when (parentTag) {
|
val content = when (parentTag) {
|
||||||
"ol" -> {
|
"ol" -> {
|
||||||
index = wholeTag.indexOf(LI_VALUE_CAPTURE).let {
|
index = wholeTag.indexOf(LI_VALUE_CAPTURE).let {
|
||||||
|
@ -163,7 +174,6 @@ internal class HtmlParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun singleTagParser(content: String, wantedTagName: String, searchIndex: Int, builder: PartBuilder, onTag: (String, String) -> Unit): SearchIndex {
|
private fun singleTagParser(content: String, wantedTagName: String, searchIndex: Int, builder: PartBuilder, onTag: (String, String) -> Unit): SearchIndex {
|
||||||
return content.findTag(
|
return content.findTag(
|
||||||
fromIndex = searchIndex,
|
fromIndex = searchIndex,
|
||||||
|
@ -195,4 +205,12 @@ internal class HtmlParser {
|
||||||
return intput.indexOf(TAG_OPEN, startingFrom)
|
return intput.indexOf(TAG_OPEN, startingFrom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun iterateIndex(start: SearchIndex, action: (SearchIndex) -> SearchIndex): SearchIndex {
|
||||||
|
var nextIndex = start
|
||||||
|
while (nextIndex != END_SEARCH) {
|
||||||
|
nextIndex = action(nextIndex)
|
||||||
|
}
|
||||||
|
return nextIndex
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -24,6 +24,12 @@ class RichMessageParserTest {
|
||||||
expected = RichText(setOf(Normal("Hello world!\nfoo bar\nafter paragraph")))
|
expected = RichText(setOf(Normal("Hello world!\nfoo bar\nafter paragraph")))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `parses nesting within p tags`() = runParserTest(
|
||||||
|
input = "<p><b>Hello world!</b></p>",
|
||||||
|
expected = RichText(setOf(Bold("Hello world!")))
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `replaces quote entity`() = runParserTest(
|
fun `replaces quote entity`() = runParserTest(
|
||||||
input = "Hello world! "foo bar"",
|
input = "Hello world! "foo bar"",
|
||||||
|
|
Loading…
Reference in New Issue