diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/JupyterMarkdownCompatibilityChecker.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/JupyterMarkdownCompatibilityChecker.kt
index 4a6db506f40a..e5e215085439 100644
--- a/notebooks/visualization/src/com/intellij/notebooks/visualization/JupyterMarkdownCompatibilityChecker.kt
+++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/JupyterMarkdownCompatibilityChecker.kt
@@ -5,6 +5,9 @@ import com.intellij.lang.Language
import com.intellij.notebooks.jupyter.core.jupyter.CellType
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiFile
import org.intellij.plugins.markdown.lang.MarkdownCompatibilityChecker
class JupyterMarkdownCompatibilityChecker : MarkdownCompatibilityChecker {
@@ -18,4 +21,15 @@ class JupyterMarkdownCompatibilityChecker : MarkdownCompatibilityChecker {
val cell = NotebookCellLines.get(editor).getCellByLineNumber(lineNumber) ?: return true
return cell.type == CellType.MARKDOWN
}
+
+ override fun isSupportedRange(file: PsiFile, range: TextRange): Boolean {
+ if (!isSupportedLanguage(file.language) || range.isEmpty) return false
+ val document = PsiDocumentManager.getInstance(file.project).getDocument(file) ?: return false
+ val startLine = document.getLineNumber(range.startOffset)
+ val endLine = document.getLineNumber(range.endOffset - 1)
+ val cellLines = NotebookCellLines.get(document)
+ val startCell = cellLines.getCellByLineNumber(startLine) ?: return false
+ val endCell = cellLines.getCellByLineNumber(endLine) ?: return false
+ return startCell == endCell && startCell.type == CellType.MARKDOWN
+ }
}
\ No newline at end of file
diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessor.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessor.kt
index 8594395c63af..e1c2b4b3822f 100644
--- a/plugins/markdown/core/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessor.kt
+++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessor.kt
@@ -5,7 +5,13 @@ import com.intellij.codeInsight.editorActions.CopyPastePreProcessor
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RawText
import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
+import com.intellij.psi.util.elementType
+import com.intellij.psi.util.parents
+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.supportsMarkdown
internal class MarkdownLinkPastePreProcessor : CopyPastePreProcessor {
@@ -16,7 +22,8 @@ internal class MarkdownLinkPastePreProcessor : CopyPastePreProcessor {
override fun preprocessOnPaste(project: Project, file: PsiFile, editor: Editor, text: String, rawText: RawText?): String {
val linkText = editor.selectionModel.selectedText ?: return text
val linkDestination = MarkdownLinkEditingUtil.getLinkDestination(text) ?: return text
- if (linkText.isBlank() || !file.language.supportsMarkdown()) {
+ val selectionRange = TextRange(editor.selectionModel.selectionStart, editor.selectionModel.selectionEnd)
+ if (linkText.isBlank() || !file.supportsMarkdown(selectionRange) || isInIgnoredContext(file, selectionRange)) {
return text
}
return MarkdownLinkEditingUtil.createInlineLink(linkText, linkDestination)
@@ -26,7 +33,31 @@ internal class MarkdownLinkPastePreProcessor : CopyPastePreProcessor {
return false
}
- override fun requiresAllDocumentsToBeCommitted(editor: Editor, project: Project): Boolean {
- return false
+ private fun isInIgnoredContext(file: PsiFile, selectionRange: TextRange): Boolean {
+ val contextFile = file.viewProvider.getPsi(MarkdownLanguage.INSTANCE) ?: file
+ val startElement = contextFile.findElementAt(selectionRange.startOffset) ?: return false
+ val endElement = contextFile.findElementAt(selectionRange.endOffset - 1) ?: return false
+ return sequenceOf(startElement, endElement).any { element ->
+ // Markdown PSI can return an embedded HTML element at the requested offset.
+ !element.supportsMarkdown() || element.parents(withSelf = true).any { it.elementType in ignoredLinkPasteContextTypes }
+ }
}
}
+
+private val ignoredLinkPasteContextTypes = setOf(
+ MarkdownElementTypes.CODE_FENCE,
+ MarkdownElementTypes.CODE_BLOCK,
+ MarkdownElementTypes.CODE_SPAN,
+ MarkdownElementTypes.HTML_BLOCK,
+ MarkdownElementTypes.LINK_TEXT,
+ MarkdownElementTypes.LINK_DESTINATION,
+ MarkdownElementTypes.LINK_DEFINITION,
+ MarkdownElementTypes.INLINE_LINK,
+ MarkdownElementTypes.FULL_REFERENCE_LINK,
+ MarkdownElementTypes.SHORT_REFERENCE_LINK,
+ MarkdownElementTypes.IMAGE,
+ MarkdownElementTypes.TEST_LINK,
+ MarkdownElementTypes.AUTOLINK,
+ MarkdownElementTypes.FRONT_MATTER_HEADER,
+ MarkdownTokenTypes.GFM_AUTOLINK,
+)
diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownCompatibilityChecker.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownCompatibilityChecker.kt
index fcd19a1be95a..791e52c73240 100644
--- a/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownCompatibilityChecker.kt
+++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownCompatibilityChecker.kt
@@ -4,6 +4,9 @@ package org.intellij.plugins.markdown.lang
import com.intellij.lang.Language
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.extensions.ExtensionPointName
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
/**
* Provides support for languages that either are Markdown or are compatible with Markdown-like features.
@@ -20,6 +23,14 @@ interface MarkdownCompatibilityChecker {
}
fun isSupportedLanguage(language: Language): Boolean
+
+ fun isSupportedElement(element: PsiElement): Boolean {
+ return isSupportedLanguage(element.containingFile.language)
+ }
+
+ fun isSupportedRange(file: PsiFile, range: TextRange): Boolean {
+ return isSupportedLanguage(file.language)
+ }
fun isSupportedContext(language: Language, dataContext: DataContext?): Boolean {
return isSupportedLanguage(language)
diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownLanguageUtils.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownLanguageUtils.kt
index 4f2ad5f0dd62..6d24000b9b2d 100644
--- a/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownLanguageUtils.kt
+++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/lang/MarkdownLanguageUtils.kt
@@ -4,7 +4,10 @@ import com.intellij.lang.Language
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
+import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
fun Language.isMarkdownLanguage(): Boolean {
return this == MarkdownLanguage.INSTANCE
@@ -14,6 +17,14 @@ fun Language.supportsMarkdown(dataContext: DataContext? = null): Boolean {
return MarkdownCompatibilityChecker.EP_NAME.extensionList.any { it.isSupportedContext(this, dataContext) }
}
+fun PsiElement.supportsMarkdown(): Boolean {
+ return MarkdownCompatibilityChecker.EP_NAME.extensionList.any { it.isSupportedElement(this) }
+}
+
+fun PsiFile.supportsMarkdown(range: TextRange): Boolean {
+ return MarkdownCompatibilityChecker.EP_NAME.extensionList.any { it.isSupportedRange(this, range) }
+}
+
fun FileType.isMarkdownType(): Boolean {
return this == MarkdownFileType.INSTANCE
}
diff --git a/plugins/markdown/test/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessorTest.kt b/plugins/markdown/test/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessorTest.kt
new file mode 100644
index 000000000000..700153394b4f
--- /dev/null
+++ b/plugins/markdown/test/src/org/intellij/plugins/markdown/editor/MarkdownLinkPastePreProcessorTest.kt
@@ -0,0 +1,87 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.intellij.plugins.markdown.editor
+
+import com.intellij.codeInsight.editorActions.CopyPastePreProcessor
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixture4TestCase
+import org.junit.Test
+
+class MarkdownLinkPastePreProcessorTest : LightPlatformCodeInsightFixture4TestCase() {
+ @Test
+ fun `creates link in Markdown text`() {
+ assertPreprocessedText("Some