[markdown] IJPL-177111 add a Registry flag for Markdown link opening fallback

Introduced a registry flag (`markdown.open.link.fallback`) for controlling the fallback to the previous implementation of Markdown link handling. Also includes refactoring and code adjustments for improved clarity.

GitOrigin-RevId: 2cab3173b9d85f751a17c96e8f8444be6519b22f
This commit is contained in:
Milan Doslic
2025-04-15 07:21:53 +00:00
committed by intellij-monorepo-bot
parent 9ce83874aa
commit c896bc7e89
8 changed files with 52 additions and 9 deletions
@@ -50,5 +50,6 @@
<orderEntry type="module" module-name="intellij.driver.sdk" />
<orderEntry type="module" module-name="intellij.platform.lang" />
<orderEntry type="module" module-name="intellij.platform.ide.core.impl" />
<orderEntry type="module" module-name="intellij.platform.ide.rpc" />
</component>
</module>
@@ -42,5 +42,7 @@
<orderEntry type="module" module-name="intellij.platform.ide.util.netty" />
<orderEntry type="module" module-name="intellij.platform.util.ui" />
<orderEntry type="module" module-name="intellij.platform.kernel" />
<orderEntry type="module" module-name="intellij.platform.ide.rpc" />
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
</component>
</module>
@@ -5,5 +5,8 @@
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceInterface="org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpener"
serviceImplementation="org.intellij.plugins.markdown.frontend.preview.accessor.impl.MarkdownLinkOpenerImpl"/>
<registryKey key="markdown.open.link.fallback"
defaultValue="true"
description="Should fallback to Markdown link opening implementation before the split."/>
</extensions>
</idea-plugin>
@@ -18,7 +18,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.wm.WindowManager
import com.intellij.platform.project.findProject
@@ -43,6 +42,16 @@ import java.net.URI
import java.net.URISyntaxException
internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : MarkdownLinkOpener {
override fun openLink(project: Project?, link: String) {
val uri = createUri(link) ?: return
if (tryOpenInEditorDeprecated(project, uri)) {
return
}
coroutineScope.launch {
openExternalLink(project, uri)
}
}
override fun openLink(project: Project?, link: String, containingFile: VirtualFile?) {
val uri = createUri(link, containingFile) ?: return
if (tryOpenInEditor(project, uri)) {
@@ -52,8 +61,9 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark
openExternalLink(project, uri)
}
}
override fun isSafeLink(project: Project?, link: String): Boolean {
val uri = createUri(link, null) ?: return false
val uri = createUri(link) ?: return false
return isSafeUri(project, uri)
}
@@ -132,7 +142,16 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark
}
}
private fun actuallyOpenInEditor(project: Project?, uri: URI): Boolean {
private fun tryOpenInEditorDeprecated(project: Project?, uri: URI): Boolean {
if (uri.scheme != "file") {
return false
}
return runReadAction {
actuallyOpenInEditorDeprecated(project, uri)
}
}
private fun actuallyOpenInEditorDeprecated(project: Project?, uri: URI): Boolean {
val service = MarkdownFrontendService.getInstance()
@Suppress("NAME_SHADOWING")
val project = project ?: service.guessProjectForUri(uri) ?: return false
@@ -229,7 +248,7 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark
return try {
if (BrowserUtil.isAbsoluteURL(link)) return URI(link)
else {
if (!Registry.`is`("markdown.open.link.fallback") && PlatformUtils.isJetBrainsClient()){
if (PlatformUtils.isJetBrainsClient()){
val scheme = runBlockingCancellable {
withContext(Dispatchers.IO) {
MarkdownLinkOpenerRemoteApi.getInstance().resolveLinkAsFilePath(link, containingFile?.rpcId())
@@ -239,6 +258,7 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark
return URI(scheme)
}
}
return URI("http://$link")
}
} catch (exception: URISyntaxException) {
@@ -250,6 +270,18 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark
companion object {
private val logger = logger<MarkdownLinkOpenerImpl>()
fun createUri(link: String): URI? {
return try {
when {
BrowserUtil.isAbsoluteURL(link) -> URI(link)
else -> URI("http://$link")
}
} catch (exception: URISyntaxException) {
logger.warn(exception)
null
}
}
private fun isLocalHost(hostName: String?): Boolean {
return hostName == null ||
hostName.startsWith("127.") ||
@@ -288,7 +320,7 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark
override fun onChosen(selectedValue: MarkdownHeaderInfo, finalChoice: Boolean): PopupStep<*> {
return doFinalStep {
MarkdownFrontendService.getInstance().navigateToHeader(project.projectId(), selectedValue);
MarkdownFrontendService.getInstance().navigateToHeader(project.projectId(), selectedValue)
}
}
}
+2 -1
View File
@@ -74,5 +74,6 @@
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
<orderEntry type="module" module-name="intellij.platform.kernel" />
<orderEntry type="module" module-name="intellij.platform.ide.rpc" />
</component>
</module>
</module>
@@ -41,7 +41,10 @@ internal class ProcessLinksExtension(private val panel: MarkdownHtmlPanel): Mark
return false
}
}
MarkdownLinkOpener.getInstance().openLink(panel.project, link, panel.virtualFile)
if (Registry.`is`("markdown.open.link.fallback"))
MarkdownLinkOpener.getInstance().openLink(panel.project, link)
else
MarkdownLinkOpener.getInstance().openLink(panel.project, link, panel.virtualFile)
return false
}
@@ -3,7 +3,6 @@ package org.intellij.plugins.markdown.service;
import com.intellij.ide.actions.OpenFileAction;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
@@ -55,7 +54,7 @@ public final class MarkdownFrontendServiceImpl implements MarkdownFrontendServic
return ReadAction.compute(() -> {
if (DumbService.isDumb(project)) return null;
PsiFile file = PsiManager.getInstance(project).findFile(targetFile);
GlobalSearchScope scope = (file == null) ? GlobalSearchScope.EMPTY_SCOPE : GlobalSearchScope.fileScope(file);;
GlobalSearchScope scope = (file == null) ? GlobalSearchScope.EMPTY_SCOPE : GlobalSearchScope.fileScope(file);
Collection<MarkdownHeader> headers = HeaderAnchorIndex.Companion.collectHeaders(project, scope, anchor);
return ContainerUtil.map(headers, MarkdownHeaderMapper::map);
});
@@ -22,6 +22,8 @@ interface MarkdownLinkOpener {
*
* Note: it is possible to add custom url handler with [com.intellij.ide.browsers.UrlOpener] EP.
*/
fun openLink(project: Project?, link: String)
fun openLink(project: Project?, link: String, sourceFile: VirtualFile?)
fun isSafeLink(project: Project?, link: String): Boolean