From c896bc7e89b69ce699d4fdd52710ea788ed1dcd3 Mon Sep 17 00:00:00 2001 From: Milan Doslic Date: Tue, 25 Mar 2025 11:16:40 +0100 Subject: [PATCH] [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 --- .../backend/intellij.markdown.backend.iml | 1 + .../frontend/intellij.markdown.frontend.iml | 2 + .../resources/intellij.markdown.frontend.xml | 3 ++ .../accessor/impl/MarkdownLinkOpenerImpl.kt | 42 ++++++++++++++++--- plugins/markdown/core/intellij.markdown.iml | 3 +- .../extensions/jcef/ProcessLinksExtension.kt | 5 ++- .../service/MarkdownFrontendServiceImpl.java | 3 +- .../ui/preview/accessor/MarkdownLinkOpener.kt | 2 + 8 files changed, 52 insertions(+), 9 deletions(-) diff --git a/plugins/markdown/core/backend/intellij.markdown.backend.iml b/plugins/markdown/core/backend/intellij.markdown.backend.iml index ff2081e18ae9..e510b54d96a8 100644 --- a/plugins/markdown/core/backend/intellij.markdown.backend.iml +++ b/plugins/markdown/core/backend/intellij.markdown.backend.iml @@ -50,5 +50,6 @@ + \ No newline at end of file diff --git a/plugins/markdown/core/frontend/intellij.markdown.frontend.iml b/plugins/markdown/core/frontend/intellij.markdown.frontend.iml index c8740bf78756..a7fc7849ff1f 100644 --- a/plugins/markdown/core/frontend/intellij.markdown.frontend.iml +++ b/plugins/markdown/core/frontend/intellij.markdown.frontend.iml @@ -42,5 +42,7 @@ + + \ No newline at end of file diff --git a/plugins/markdown/core/frontend/resources/intellij.markdown.frontend.xml b/plugins/markdown/core/frontend/resources/intellij.markdown.frontend.xml index e17a79abc145..74aac0a1d33a 100644 --- a/plugins/markdown/core/frontend/resources/intellij.markdown.frontend.xml +++ b/plugins/markdown/core/frontend/resources/intellij.markdown.frontend.xml @@ -5,5 +5,8 @@ + \ No newline at end of file diff --git a/plugins/markdown/core/frontend/src/org/intellij/plugins/markdown/frontend/preview/accessor/impl/MarkdownLinkOpenerImpl.kt b/plugins/markdown/core/frontend/src/org/intellij/plugins/markdown/frontend/preview/accessor/impl/MarkdownLinkOpenerImpl.kt index 0f3e1db30d24..08345e3fc4cf 100644 --- a/plugins/markdown/core/frontend/src/org/intellij/plugins/markdown/frontend/preview/accessor/impl/MarkdownLinkOpenerImpl.kt +++ b/plugins/markdown/core/frontend/src/org/intellij/plugins/markdown/frontend/preview/accessor/impl/MarkdownLinkOpenerImpl.kt @@ -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() + 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) } } } diff --git a/plugins/markdown/core/intellij.markdown.iml b/plugins/markdown/core/intellij.markdown.iml index 7baa00073b8b..ef3323b45cd5 100644 --- a/plugins/markdown/core/intellij.markdown.iml +++ b/plugins/markdown/core/intellij.markdown.iml @@ -74,5 +74,6 @@ + - + \ No newline at end of file diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessLinksExtension.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessLinksExtension.kt index 78e94af794b9..3cb086afbd87 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessLinksExtension.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessLinksExtension.kt @@ -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 } diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendServiceImpl.java b/plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendServiceImpl.java index 1cfa4ced4e7b..f18049b15425 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendServiceImpl.java +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendServiceImpl.java @@ -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 headers = HeaderAnchorIndex.Companion.collectHeaders(project, scope, anchor); return ContainerUtil.map(headers, MarkdownHeaderMapper::map); }); diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpener.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpener.kt index e98bdc2d773e..c0fa7fe686e8 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpener.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpener.kt @@ -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