[markdown] IJPL-250785 Markdown editor: High CPU from run-configuration lookups per code span

Merge-request: IJ-MR-214471
Merged-by: Ilia Permiashkin <ilia.permiashkin@jetbrains.com>

Merge-request: IJ-MR-215424
Merged-by: Ilia Permiashkin <ilia.permiashkin@jetbrains.com>

GitOrigin-RevId: 9b5366e0f6b3878cc5ee04953e659a7162e5a479
This commit is contained in:
Ilia Permiashkin
2026-08-03 13:37:26 +00:00
committed by intellij-monorepo-bot
parent e5b66d2409
commit e2a6d9082d
2 changed files with 30 additions and 16 deletions
@@ -14,6 +14,9 @@ import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.util.nextLeaf
import org.intellij.plugins.markdown.MarkdownBundle
import org.intellij.plugins.markdown.MarkdownUsageCollector.RUNNER_EXECUTED
@@ -23,6 +26,7 @@ import org.intellij.plugins.markdown.extensions.jcef.commandRunner.CommandRunner
import org.intellij.plugins.markdown.extensions.jcef.commandRunner.CommandRunnerExtension.Companion.trimPrompt
import org.intellij.plugins.markdown.injection.aliases.CodeFenceLanguageGuesser
import org.intellij.plugins.markdown.lang.MarkdownElementTypes
import org.intellij.plugins.markdown.lang.MarkdownLanguage
import org.intellij.plugins.markdown.lang.MarkdownTokenTypes
import org.intellij.plugins.markdown.lang.psi.impl.MarkdownCodeFence
import org.intellij.plugins.markdown.lang.psi.util.hasType
@@ -92,21 +96,12 @@ internal class MarkdownRunLineMarkersProvider: RunLineMarkerContributor(), DumbA
val text = elementText.trim()
if (text.isBlank()) return null
val codeSpans = getAllCodeSpansOnLine(element)
if (codeSpans.firstOrNull() != element) return null
val configurations = codeSpans
.asSequence()
.map { getText(it).trim() to it }
.filter { it.first.isNotBlank() }
.distinctBy { it.first }
.flatMap { (text, host) ->
MarkdownCodeSpanConfigurationContextSearcher
.findAllConfigurations(text, host)
.mapNotNull { it.findExisting() ?: it.getConfiguration() }
.distinctBy { it.uniqueID }
}
.toList()
val configurations = CachedValuesManager.getCachedValue(element) {
CachedValueProvider.Result.create(
getConfigurations(element),
PsiModificationTracker.getInstance(element.project).forLanguages { !it.isKindOf(MarkdownLanguage.INSTANCE) }
)
}
if (configurations.isEmpty()) return null
val actions = configurations.map(::RunConfigurationAction).toTypedArray()
@@ -116,6 +111,24 @@ internal class MarkdownRunLineMarkersProvider: RunLineMarkerContributor(), DumbA
) { MarkdownBundle.message("markdown.runner.launch.command", text) }
}
private fun getConfigurations(element: PsiElement): List<RunnerAndConfigurationSettings> {
val codeSpans = getAllCodeSpansOnLine(element)
if (codeSpans.firstOrNull() != element) return emptyList()
return codeSpans
.asSequence()
.map { getText(it).trim() to it }
.filter { it.first.isNotBlank() }
.distinctBy { it.first }
.flatMap { (text, host) ->
MarkdownCodeSpanConfigurationContextSearcher
.findAllConfigurations(text, host)
.mapNotNull { it.getConfiguration() }
.distinctBy { it.uniqueID }
}
.toList()
}
private fun getText(element: PsiElement): @NlsSafe String {
if (element.hasType(MarkdownTokenTypes.CODE_FENCE_CONTENT)) return element.text.trim()
if (element.hasType(MarkdownTokenTypes.BACKTICK)) {
@@ -19,7 +19,8 @@ internal class MarkdownCodeSpanConfigurationRunnerTest : LightJavaCodeInsightFix
assertEquals(1, getPopupActionCount(markers))
}
fun `test code span run marker is not shown for absent Java class`() {
fun `test code span run marker is not shown for non-runnable Java class`() {
myFixture.addClass("public class JavaClass {}")
configureMarkdown("`JavaClass`")
val markers = findRunMarkers()