Add simple parser for use with just quotes/replies

This commit is contained in:
David Langley 2021-12-14 20:40:44 +00:00
parent 96062b7daa
commit bef238f851
3 changed files with 32 additions and 10 deletions

View file

@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import org.commonmark.node.BlockQuote
import org.commonmark.parser.Parser import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer import org.commonmark.renderer.html.HtmlRenderer
import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.file.FileService
@ -98,6 +99,14 @@ import org.matrix.android.sdk.internal.session.room.version.DefaultRoomVersionUp
import org.matrix.android.sdk.internal.session.room.version.RoomVersionUpgradeTask import org.matrix.android.sdk.internal.session.room.version.RoomVersionUpgradeTask
import org.matrix.android.sdk.internal.session.space.DefaultSpaceService import org.matrix.android.sdk.internal.session.space.DefaultSpaceService
import retrofit2.Retrofit import retrofit2.Retrofit
import javax.inject.Qualifier
/**
* Used to inject the simple commonmark Parser
*/
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SimpleCommonmarkParser
@Module @Module
internal abstract class RoomModule { internal abstract class RoomModule {
@ -124,6 +133,17 @@ internal abstract class RoomModule {
return Parser.builder().build() return Parser.builder().build()
} }
@Provides
@SimpleCommonmarkParser
@JvmStatic
fun providesSimpleParser(): Parser {
// The simple parser disables all blocks but quotes.
// Inline parsing(bold, italic, etc) is also enabled and is not easy to disable in commonmark currently.
return Parser.builder()
.enabledBlockTypes(setOf(BlockQuote::class.java))
.build()
}
@Provides @Provides
@JvmStatic @JvmStatic
fun providesHtmlRenderer(): HtmlRenderer { fun providesHtmlRenderer(): HtmlRenderer {

View file

@ -91,9 +91,9 @@ internal class LocalEchoEventFactory @Inject constructor(
return createMessageEvent(roomId, content) return createMessageEvent(roomId, content)
} }
private fun createTextContent(text: CharSequence, autoMarkdown: Boolean, forceMarkdownParse: Boolean = false): TextContent { private fun createTextContent(text: CharSequence, autoMarkdown: Boolean): TextContent {
if (autoMarkdown) { if (autoMarkdown) {
return markdownParser.parse(text, force = forceMarkdownParse) return markdownParser.parse(text)
} else { } else {
// Try to detect pills // Try to detect pills
textPillsUtils.processSpecialSpansToHtml(text)?.let { textPillsUtils.processSpecialSpansToHtml(text)?.let {
@ -206,9 +206,9 @@ internal class LocalEchoEventFactory @Inject constructor(
val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply())
// As we always supply formatted body for replies we should force the MarkdownParser to produce html. // As we always supply formatted body for replies we should force the MarkdownParser to produce html.
val newBodyFormatted = createTextContent(newBodyText, true, forceMarkdownParse = true).takeFormatted() val newBodyFormatted = markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted()
// Body of the original message may not have formatted version, so may also have to convert to html. // Body of the original message may not have formatted version, so may also have to convert to html.
val bodyFormatted = body.formattedText ?: createTextContent(body.text, true, forceMarkdownParse = true).takeFormatted() val bodyFormatted = body.formattedText ?: markdownParser.parse(newBodyText, force = true, advanced = false).takeFormatted()
val replyFormatted = REPLY_PATTERN.format( val replyFormatted = REPLY_PATTERN.format(
permalink, permalink,
userLink, userLink,
@ -396,9 +396,9 @@ internal class LocalEchoEventFactory @Inject constructor(
val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply())
// As we always supply formatted body for replies we should force the MarkdownParser to produce html. // As we always supply formatted body for replies we should force the MarkdownParser to produce html.
val replyTextFormatted = createTextContent(replyText, true, forceMarkdownParse = true).takeFormatted() val replyTextFormatted = markdownParser.parse(replyText, force = true, advanced = false).takeFormatted()
// Body of the original message may not have formatted version, so may also have to convert to html. // Body of the original message may not have formatted version, so may also have to convert to html.
val bodyFormatted = body.formattedText ?: createTextContent(body.text, true, forceMarkdownParse = true).takeFormatted() val bodyFormatted = body.formattedText ?: markdownParser.parse(replyText, force = true, advanced = false).takeFormatted()
val replyFormatted = REPLY_PATTERN.format( val replyFormatted = REPLY_PATTERN.format(
permalink, permalink,
userLink, userLink,
@ -514,7 +514,7 @@ internal class LocalEchoEventFactory @Inject constructor(
val messageContent = quotedEvent.getLastMessageContent() val messageContent = quotedEvent.getLastMessageContent()
val textMsg = messageContent?.body val textMsg = messageContent?.body
val quoteText = legacyRiotQuoteText(textMsg, text) val quoteText = legacyRiotQuoteText(textMsg, text)
return createFormattedTextEvent(roomId, createTextContent(quoteText, autoMarkdown=true, forceMarkdownParse = true), MessageType.MSGTYPE_TEXT) return createFormattedTextEvent(roomId, markdownParser.parse(quoteText, force = true, advanced = false), MessageType.MSGTYPE_TEXT)
} }
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.send
import org.commonmark.parser.Parser import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer import org.commonmark.renderer.html.HtmlRenderer
import org.matrix.android.sdk.internal.session.room.SimpleCommonmarkParser
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
import javax.inject.Inject import javax.inject.Inject
@ -27,7 +28,8 @@ import javax.inject.Inject
* If any change is required, please add a test covering the problem and make sure all the tests are still passing. * If any change is required, please add a test covering the problem and make sure all the tests are still passing.
*/ */
internal class MarkdownParser @Inject constructor( internal class MarkdownParser @Inject constructor(
private val parser: Parser, private val advancedParser: Parser,
@SimpleCommonmarkParser private val simpleParser: Parser,
private val htmlRenderer: HtmlRenderer, private val htmlRenderer: HtmlRenderer,
private val textPillsUtils: TextPillsUtils private val textPillsUtils: TextPillsUtils
) { ) {
@ -40,7 +42,7 @@ internal class MarkdownParser @Inject constructor(
* @param force Skips the check for detecting if the input contains markdown and always converts to html. * @param force Skips the check for detecting if the input contains markdown and always converts to html.
* @return TextContent containing the plain text and the formatted html if generated. * @return TextContent containing the plain text and the formatted html if generated.
*/ */
fun parse(text: CharSequence, force: Boolean = false): TextContent { fun parse(text: CharSequence, force: Boolean = false, advanced: Boolean = true): TextContent {
val source = textPillsUtils.processSpecialSpansToMarkdown(text) ?: text.toString() val source = textPillsUtils.processSpecialSpansToMarkdown(text) ?: text.toString()
// If no special char are detected, just return plain text // If no special char are detected, just return plain text
@ -48,7 +50,7 @@ internal class MarkdownParser @Inject constructor(
return TextContent(source) return TextContent(source)
} }
val document = parser.parse(source) val document = if (advanced) advancedParser.parse(source) else simpleParser.parse(source)
val htmlText = htmlRenderer.render(document) val htmlText = htmlRenderer.render(document)
// Cleanup extra paragraph // Cleanup extra paragraph