JetBrains / markdown

Markdown parser written in kotlin
Apache License 2.0
682 stars 75 forks source link

Mixed markdown marks cause AssertionError from intellij-markdown parser #98

Closed abarsov closed 2 years ago

abarsov commented 2 years ago

Content of the following code block (just content without code block itself) being parsed causes AssertionError **~~b**~~

Expected behaviour: Parser returns a tree with bolded ~~bb followed directly by plain text ~~ Actual result:

java.lang.AssertionError 
    at org.intellij.markdown.parser.TreeBuilder.buildTree(TreeBuilder.kt:125)
    at org.intellij.markdown.parser.MarkdownParser.parseInline(MarkdownParser.kt:60)
    at org.intellij.markdown.parser.MarkdownParser$InlineExpandingASTNodeBuilder.createLeafNodes(MarkdownParser.kt:70)
    at org.intellij.markdown.parser.MyRawBuilder.createASTNodeOnClosingEvent(MyRawBuilder.kt:21)
    at org.intellij.markdown.parser.TreeBuilder.buildTree(TreeBuilder.kt:38)
    at org.intellij.markdown.parser.MarkdownParser.parse(MarkdownParser.kt:47)
    at org.intellij.markdown.parser.MarkdownParser.buildMarkdownTreeFromString(MarkdownParser.kt:17)
    at circlet.collab.model.PmFromIntellijMarkdown.readJson(PmFromIntellijMarkdown.kt:20)
    at circlet.collab.model.PmFromMarkdown.readJson(PmFromMarkdown.kt:6)
    at circlet.collab.model.PmFromMarkdown.readJson$default(PmFromMarkdown.kt:6)
    at circlet.collab.server.model.DocumentFormatMigrationKt.convertModel(documentFormatMigration.kt:88)
    at circlet.collab.server.model.DocumentFormatMigrationKt.doMigrateDocument(documentFormatMigration.kt:44)
    at circlet.collab.server.model.DocumentFormatMigrationKt.access$doMigrateDocument(documentFormatMigration.kt:1)
    at circlet.collab.server.model.DocumentFormatMigrationKt$migrateDocument$2.invoke(documentFormatMigration.kt:35)
    at circlet.collab.server.model.DocumentFormatMigrationKt$migrateDocument$2.invoke(documentFormatMigration.kt:35)
    at circlet.collab.server.service.CollabComponentKt.runWithDocumentLock(CollabComponent.kt:704)
    at circlet.collab.server.model.DocumentFormatMigrationKt.migrateDocument(documentFormatMigration.kt:35)
    at circlet.collab.server.migration.DocumentTablesMigration.applyChunk(DocumentTablesMigration.kt:54)
    at circlet.platform.server.services.migrations.DBMigrator$applyBackgroundMigrations$2$3$2$duration$1$1.invoke(DBMigrator.kt:98)
    at circlet.platform.server.services.migrations.DBMigrator$applyBackgroundMigrations$2$3$2$duration$1$1.invoke(DBMigrator.kt:94)
    at circlet.platform.server.core.ClusterSingletonTransactionImpl$lockedTx$2$1$1$1.invoke(ClusterSingletonTransaction.kt:435)
    at circlet.server.db.OrgDbContext$tx$2.invoke(OrgDbContext.kt:22)
    at circlet.platform.server.db.TxKt$txImpl$2$1$1$1$1.invoke(Tx.kt:149)
    at circlet.platform.server.db.TxKt$txImpl$2$1$1$1$1.invoke(Tx.kt:139)
    at circlet.platform.server.db.TxKt$performTopLevelTransaction$res$1$2.invoke(Tx.kt:341)
    at circlet.platform.server.dispatcher.JobsKt.scheduleJobsLater(Jobs.kt:208)
    at circlet.platform.server.db.TxKt$performTopLevelTransaction$res$1.invoke(Tx.kt:331)
    at circlet.platform.server.db.TxKt$performTopLevelTransaction$res$1.invoke(Tx.kt:294)
    at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:198)
    at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.access$inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:1)
    at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$inTopLevelTransaction$1.invoke(ThreadLocalTransactionManager.kt:221)
    at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:229)
    at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction(ThreadLocalTransactionManager.kt:220)
    at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction$default(ThreadLocalTransactionManager.kt:179)
    at circlet.platform.server.db.TxKt.performTopLevelTransaction(Tx.kt:294)
    at circlet.platform.server.db.TxKt.access$performTopLevelTransaction(Tx.kt:1)
    at circlet.platform.server.db.TxKt$txImpl$2$1$1$1.invoke(Tx.kt:139)
    at circlet.platform.server.db.TxKt$txImpl$2$1$1$1.invoke(Tx.kt:138)
    at libraries.coroutines.extra.CoroutineSessionElementKt.withMetricsSessionScopeBlocking(CoroutineSessionElement.kt:23)
    at circlet.platform.server.db.TxKt$txImpl$2$1.invoke(Tx.kt:138)
    at circlet.platform.server.db.TxKt$txImpl$2$1.invoke(Tx.kt:127)
    at circlet.platform.server.db.TxPool$LimitedTxQuota$run$2$1.invoke(TxPool.kt:88)
    at circlet.platform.server.db.TxPool$LimitedTxQuota$run$2$1.invoke(TxPool.kt:87)
    at circlet.platform.server.db.TxPool$ThreadedTxQuota.runChecked(TxPool.kt:161)
    at circlet.platform.server.db.TxPool$ThreadedTxQuota.access$runChecked(TxPool.kt:93)
    at circlet.platform.server.db.TxPool$ThreadedTxQuota$run$2$task$1$1.invokeSuspend(TxPool.kt:120)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at circlet.platform.server.db.TxPool$ThreadedTxQuota.run$lambda-4$lambda-2(TxPool.kt:118)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)
valich commented 2 years ago

Fixed in #100

abarsov commented 2 years ago

Parsing of the string **~~b**~~ still fails with another exception though:

"MarkdownParsingException: Intersecting parsed nodes detected: Node(range=2..9, type=Markdown:STRIKETHROUGH) vs Node(range=0..7, type=Markdown:STRONG)
    at Object.captureStack (http://localhost:8000/index.js:63163:15)
    at MarkdownParsingException.Exception [as constructor] (http://localhost:8000/index.js:63496:14)
    at MarkdownParsingException.RuntimeException [as constructor] (http://localhost:8000/index.js:63522:17)
    at MarkdownParsingException.IllegalStateException [as constructor] (http://localhost:8000/index.js:63562:24)
    at IllegalStateException_init_0 (http://localhost:8000/index.js:63573:29)
    at new MarkdownParsingException (http://localhost:8000/index.js:194111:5)
    at InlineBuilder.TreeBuilder.buildTree_3cbhjl$ (http://localhost:8000/index.js:199350:19)
    at MarkdownParser.doParseInline_0 (http://localhost:8000/index.js:199028:71)
    at MarkdownParser.parseInline_6auev$ (http://localhost:8000/index.js:198985:19)
    at MarkdownParser$InlineExpandingASTNodeBuilder.createLeafNodes_1ffi4f$ (http://localhost:8000/index.js:199043:33)"

cc @FirstTimeInForever

abarsov commented 2 years ago

It appeared to be a misuse of deprecated API.

Since 0.3.0 one should define the only composite parser in the overridden function MarkdownFlavourDescriptor.sequentialParserManager of a flavour descriptor:

valich commented 2 years ago

@FirstTimeInForever Do you think it might be better to provide a different (deprecated) fallback for StrikeThroughParser? E.g. use EmphasisLikeParser(EmphStrongDelimiterParser()), I think that should alleviate the issue

FirstTimeInForever commented 2 years ago

@valich Unfortunately, I don't see a way of providing a fully working fallbacks for old parsers :(. The problem is, that both parsers for strikethroughs and empasises require a single instance of EmphasisLikeParser. So, making a EmphasisLikeParser(EmphStrongDelimiterParser()) fallback for old emphasis parser and EmphasisLikeParser(StrikeThroughDelimiterParser()) fallback for strikethrough parser would not work, since they both will be wrapped with different instances of EmphasisLikeParser.