mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Revert "[markdown] IJPL-177111 add a Registry flag for falling back to the old implementation of link opening"
This reverts commit e17374dd72c71d821ba15b2756a13d001d46150c. GitOrigin-RevId: 748622f699497dc41c35e9676d57928f87f38dc5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
80847857de
commit
d5dd6ad7de
@@ -4,7 +4,6 @@
|
||||
</dependencies>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService serviceInterface="org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpener"
|
||||
serviceImplementation="org.intellij.plugins.markdown.frontend.preview.accessor.DelegatingMarkdownLinkOpener"/>
|
||||
<applicationService serviceImplementation="org.intellij.plugins.markdown.frontend.preview.accessor.impl.MarkdownLinkOpenerImpl"/>
|
||||
serviceImplementation="org.intellij.plugins.markdown.frontend.preview.accessor.impl.MarkdownLinkOpenerImpl"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package org.intellij.plugins.markdown.frontend.preview.accessor
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpener
|
||||
import org.intellij.plugins.markdown.ui.preview.accessor.impl.MarkdownLinkOpenerImpl
|
||||
|
||||
class DelegatingMarkdownLinkOpener : MarkdownLinkOpener {
|
||||
private val oldImplementation: MarkdownLinkOpenerImpl by lazy {
|
||||
service<MarkdownLinkOpenerImpl>()
|
||||
}
|
||||
private val newImplementation: org.intellij.plugins.markdown.frontend.preview.accessor.impl.MarkdownLinkOpenerImpl by lazy {
|
||||
service<org.intellij.plugins.markdown.frontend.preview.accessor.impl.MarkdownLinkOpenerImpl>()
|
||||
}
|
||||
|
||||
private val useFallbackLinkOpener: Boolean = Registry.`is`("markdown.use.fallback.link.opener", false)
|
||||
|
||||
override fun openLink(project: Project?, link: String, virtualFile: VirtualFile?) {
|
||||
if (useFallbackLinkOpener) {
|
||||
oldImplementation.openLink(project, link, virtualFile)
|
||||
} else {
|
||||
newImplementation.openLink(project, link, virtualFile)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSafeLink(project: Project?, link: String): Boolean {
|
||||
return if (useFallbackLinkOpener) {
|
||||
oldImplementation.isSafeLink(project, link)
|
||||
} else {
|
||||
newImplementation.isSafeLink(project, link)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -1,7 +1,6 @@
|
||||
package org.intellij.plugins.markdown.frontend.preview.accessor.impl
|
||||
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.ide.vfs.rpcId
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
@@ -16,7 +15,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.vfs.VirtualFile
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.platform.project.projectId
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
@@ -40,7 +38,7 @@ internal class MarkdownLinkOpenerImpl
|
||||
private val coroutineScope: CoroutineScope
|
||||
)
|
||||
: MarkdownLinkOpener {
|
||||
override fun openLink(project: Project?, link: String, virtualFile: VirtualFile?) {
|
||||
override fun openLink(project: Project?, link: String) {
|
||||
val uri = createUri(link) ?: return
|
||||
if (tryOpenInEditor(project, uri)) {
|
||||
return
|
||||
|
||||
@@ -364,10 +364,6 @@
|
||||
<registryKey key="markdown.experimental.allow.external.requests"
|
||||
defaultValue="true"
|
||||
description="Enables requests for external resources from the preview (e.g. non-local images)."/>
|
||||
|
||||
<registryKey key="markdown.use.fallback.link.opener"
|
||||
defaultValue="false"
|
||||
description="Use the old implementation of the link opener."/>
|
||||
<!--endregion-->
|
||||
|
||||
<advancedSetting id="markdown.hide.floating.toolbar" default="false" groupKey="markdown.settings.name"/>
|
||||
|
||||
-272
@@ -1,272 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.intellij.plugins.markdown.ui.preview.accessor.impl
|
||||
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.ide.actions.OpenFileAction
|
||||
import com.intellij.openapi.application.invokeLater
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.project.DumbModeBlockedFunctionality
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessProjectForFile
|
||||
import com.intellij.openapi.ui.DoNotAskOption
|
||||
import com.intellij.openapi.ui.MessageDialogBuilder
|
||||
import com.intellij.openapi.ui.MessageType
|
||||
import com.intellij.openapi.ui.popup.Balloon
|
||||
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.SystemInfo
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.util.PsiNavigateUtil
|
||||
import com.intellij.util.UriUtil
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.io.isLocalHost
|
||||
import org.intellij.plugins.markdown.MarkdownBundle
|
||||
import org.intellij.plugins.markdown.lang.index.HeaderAnchorIndex
|
||||
import org.intellij.plugins.markdown.settings.DocumentLinksSafeState
|
||||
import org.intellij.plugins.markdown.ui.MarkdownNotifications
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownEditorWithPreview
|
||||
import org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpener
|
||||
import org.intellij.plugins.markdown.util.MarkdownDisposable
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.nio.file.Path
|
||||
|
||||
@Service
|
||||
@ApiStatus.Internal
|
||||
class MarkdownLinkOpenerImpl : MarkdownLinkOpener {
|
||||
override fun openLink(project: Project?, link: String, virtualFile: VirtualFile?) {
|
||||
val uri = createUri(link) ?: return
|
||||
if (tryOpenInEditor(project, uri)) {
|
||||
return
|
||||
}
|
||||
invokeLater {
|
||||
openExternalLink(project, uri)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSafeLink(project: Project?, link: String): Boolean {
|
||||
val uri = createUri(link)?: return false
|
||||
return isSafeUri(project, uri)
|
||||
}
|
||||
|
||||
private fun isSafeUri(project: Project?, uri: URI): Boolean {
|
||||
val protocol = uri.scheme ?: return false
|
||||
if (project != null) {
|
||||
val safeLinksState = DocumentLinksSafeState.getInstance(project)
|
||||
return safeLinksState.isProtocolAllowed(protocol)
|
||||
}
|
||||
return DocumentLinksSafeState.isHttpScheme(protocol) && isLocalHost(uri.host)
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
private fun openExternalLink(project: Project?, uri: URI) {
|
||||
if (isSafeUri(project, uri)) {
|
||||
actuallyBrowseExternalLink(project, uri)
|
||||
return
|
||||
}
|
||||
if (showDialog(project, uri)) {
|
||||
actuallyBrowseExternalLink(project, uri)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
private fun showDialog(project: Project?, uri: URI): Boolean {
|
||||
val dialog = MessageDialogBuilder.yesNo(
|
||||
title = MarkdownBundle.message("markdown.browse.external.link.open.confirmation.dialog.title"),
|
||||
message = MarkdownBundle.message("markdown.browse.external.link.open.confirmation.dialog.text", uri)
|
||||
).doNotAsk(createDoNotAskOption(project, uri))
|
||||
return dialog.ask(project)
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
private fun actuallyBrowseExternalLink(project: Project?, uri: URI) {
|
||||
try {
|
||||
BrowserUtil.browse(uri)
|
||||
} catch (exception: Throwable) {
|
||||
logger.warn("Failed to browse external link!", exception)
|
||||
MarkdownNotifications.showWarning(
|
||||
project,
|
||||
id = "markdown.links.external.open.failed",
|
||||
title = MarkdownBundle.message("markdown.browse.external.link.failed.notification.title"),
|
||||
message = MarkdownBundle.message("markdown.browse.external.link.failed.notification.content", uri),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDoNotAskOption(project: Project?, uri: URI): DoNotAskOption? {
|
||||
if (project == null) {
|
||||
return null
|
||||
}
|
||||
val protocol = uri.scheme
|
||||
if (protocol == null) {
|
||||
logger.error("Failed to obtain protocol for link: $uri")
|
||||
return null
|
||||
}
|
||||
return object: DoNotAskOption.Adapter() {
|
||||
override fun rememberChoice(isSelected: Boolean, exitCode: Int) {
|
||||
if (isSelected) {
|
||||
DocumentLinksSafeState.getInstance(project).allowProtocol(protocol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDoNotShowMessage(): String {
|
||||
return MarkdownBundle.message("markdown.browse.external.link.open.confirmation.dialog.do.not.ask.again.text", protocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.") ||
|
||||
hostName.endsWith(":1") ||
|
||||
isLocalHost(hostName, false, false)
|
||||
}
|
||||
|
||||
private fun tryOpenInEditor(project: Project?, uri: URI): Boolean {
|
||||
if (uri.scheme != "file") {
|
||||
return false
|
||||
}
|
||||
return runReadAction {
|
||||
actuallyOpenInEditor(project, uri)
|
||||
}
|
||||
}
|
||||
|
||||
private fun URI.findVirtualFile(): VirtualFile? {
|
||||
val actualPath = when {
|
||||
SystemInfo.isWindows -> UriUtil.trimLeadingSlashes(path)
|
||||
else -> path
|
||||
}
|
||||
val path = Path.of(actualPath)
|
||||
return VfsUtil.findFile(path, true)
|
||||
}
|
||||
|
||||
private fun actuallyOpenInEditor(project: Project?, uri: URI): Boolean {
|
||||
val anchor = uri.fragment
|
||||
val targetFile = uri.findVirtualFile() ?: return false
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val project = project ?: guessProjectForFile(targetFile) ?: return false
|
||||
if (anchor == null) {
|
||||
invokeLater {
|
||||
OpenFileAction.openFile(targetFile, project)
|
||||
}
|
||||
return true
|
||||
}
|
||||
val point = obtainHeadersPopupPosition(project)
|
||||
if (point == null) {
|
||||
logger.warn("Failed to obtain screen point for showing popup")
|
||||
return false
|
||||
}
|
||||
val headers = runReadAction {
|
||||
if (DumbService.isDumb(project)) {
|
||||
return@runReadAction null
|
||||
}
|
||||
val scope = when (val file = PsiManager.getInstance(project).findFile(targetFile)) {
|
||||
null -> GlobalSearchScope.EMPTY_SCOPE
|
||||
else -> GlobalSearchScope.fileScope(file)
|
||||
}
|
||||
return@runReadAction HeaderAnchorIndex.collectHeaders(project, scope, anchor)
|
||||
}
|
||||
if (headers == null) {
|
||||
invokeLater {
|
||||
DumbService.getInstance(project).showDumbModeNotificationForFunctionality(
|
||||
message = MarkdownBundle.message("markdown.dumb.mode.navigation.is.not.available.notification.text"),
|
||||
functionality = DumbModeBlockedFunctionality.ActionWithoutId
|
||||
)
|
||||
}
|
||||
// Return true to prevent external navigation from happening
|
||||
return true
|
||||
}
|
||||
invokeLater {
|
||||
when {
|
||||
headers.isEmpty() -> showCannotNavigateNotification(project, anchor, point)
|
||||
headers.size == 1 -> navigateToHeader(project, targetFile, headers.first())
|
||||
else -> showHeadersPopup(project, headers, point)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun obtainHeadersPopupPosition(project: Project?): RelativePoint? {
|
||||
val frame = WindowManager.getInstance().getFrame(project)
|
||||
val mousePosition = frame?.mousePosition ?: return null
|
||||
return RelativePoint(frame, mousePosition)
|
||||
}
|
||||
|
||||
private fun showCannotNavigateNotification(project: Project, anchor: String, point: RelativePoint) {
|
||||
val balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(
|
||||
MarkdownBundle.message("markdown.navigate.to.header.no.headers", anchor),
|
||||
MessageType.WARNING,
|
||||
null
|
||||
)
|
||||
val balloon = balloonBuilder.createBalloon()
|
||||
Disposer.register(MarkdownDisposable.getInstance(project), balloon)
|
||||
balloon.show(point, Balloon.Position.below)
|
||||
}
|
||||
|
||||
private fun showHeadersPopup(project: Project, headers: Collection<PsiElement>, point: RelativePoint) {
|
||||
JBPopupFactory.getInstance().createListPopup(HeadersPopup(project, headers.toList())).show(point)
|
||||
}
|
||||
|
||||
private class HeadersPopup(
|
||||
private val project: Project,
|
||||
headers: List<PsiElement>
|
||||
): BaseListPopupStep<PsiElement>(MarkdownBundle.message("markdown.navigate.to.header"), headers) {
|
||||
override fun getTextFor(value: PsiElement): String {
|
||||
val document = FileDocumentManager.getInstance().getDocument(value.containingFile.virtualFile)
|
||||
requireNotNull(document)
|
||||
val name = value.containingFile.virtualFile.name
|
||||
val line = document.getLineNumber(value.textOffset) + 1
|
||||
return "${value.text} ($name:$line)"
|
||||
}
|
||||
|
||||
override fun onChosen(selectedValue: PsiElement, finalChoice: Boolean): PopupStep<*> {
|
||||
return doFinalStep {
|
||||
navigateToHeader(project, selectedValue.containingFile.virtualFile, selectedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToHeader(project: Project, file: VirtualFile, element: PsiElement) {
|
||||
val manager = FileEditorManager.getInstance(project)
|
||||
val openedEditors = manager.getEditorList(file).filterIsInstance<MarkdownEditorWithPreview>()
|
||||
if (openedEditors.isNotEmpty()) {
|
||||
for (editor in openedEditors) {
|
||||
PsiNavigateUtil.navigate(element, true)
|
||||
}
|
||||
return
|
||||
}
|
||||
val descriptor = OpenFileDescriptor(project, file, element.textOffset)
|
||||
manager.openEditor(descriptor, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user