From be919d5f084dccf5fd45467d898cc1d243547bf9 Mon Sep 17 00:00:00 2001 From: Milan Doslic Date: Mon, 7 Apr 2025 17:24:30 +0200 Subject: [PATCH] [markdown] IJPL-177111 remove MarkdownFrontendService The deprecated MarkdownFrontendService has been removed, along with the associated application service. Functionality previously handled by the service is now migrated to MarkdownLinkOpenerUtil. GitOrigin-RevId: 80f8c549a4dd1614aff883b1010f197ce56b11bb --- .../accessor/impl/MarkdownLinkOpenerImpl.kt | 23 ++-- .../core/resources/META-INF/plugin.xml | 3 - .../service/MarkdownFrontendService.java | 22 ---- .../service/MarkdownFrontendServiceImpl.java | 112 ------------------ .../accessor/MarkdownLinkOpenerUtil.kt | 87 ++++++++++++++ 5 files changed, 99 insertions(+), 148 deletions(-) delete mode 100644 plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendService.java delete mode 100644 plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendServiceImpl.java create mode 100644 plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpenerUtil.kt 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 79eda043b326..f50955188c59 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 @@ -10,6 +10,7 @@ import com.intellij.openapi.diagnostic.logger 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 @@ -21,7 +22,6 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.wm.WindowManager import com.intellij.platform.project.findProject -import com.intellij.platform.project.projectId import com.intellij.ui.awt.RelativePoint import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.io.isLocalHost @@ -31,11 +31,12 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.intellij.plugins.markdown.MarkdownBundle import org.intellij.plugins.markdown.dto.MarkdownHeaderInfo -import org.intellij.plugins.markdown.service.MarkdownFrontendService import org.intellij.plugins.markdown.service.MarkdownLinkOpenerRemoteApi import org.intellij.plugins.markdown.settings.DocumentLinksSafeState import org.intellij.plugins.markdown.ui.MarkdownNotifications import org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpener +import org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpenerUtil +import org.intellij.plugins.markdown.ui.preview.accessor.MarkdownLinkOpenerUtil.Companion.findVirtualFile import org.intellij.plugins.markdown.util.MarkdownDisposable import java.net.URI import java.net.URISyntaxException @@ -71,11 +72,11 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark } return@launch } - processHeaders(fileToOpen, anchor, project, data.headers) + processHeaders(anchor, project, data.headers) } } - private suspend fun processHeaders(file: VirtualFile, anchor: String, project: Project, headers: List?){ + private suspend fun processHeaders(anchor: String, project: Project, headers: List?){ if (headers == null) { DumbService.getInstance(project).showDumbModeNotificationForFunctionality( message = MarkdownBundle.message("markdown.dumb.mode.navigation.is.not.available.notification.text"), @@ -86,7 +87,7 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark if (headers.size == 1) { withContext(Dispatchers.EDT) { runReadAction { - MarkdownFrontendService.getInstance().navigateToHeader(project.projectId(), headers.first()) + MarkdownLinkOpenerUtil.navigateToHeader(project, headers.first()) } } return @@ -183,17 +184,17 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark } private fun actuallyOpenInEditorDeprecated(project: Project?, uri: URI): Boolean { - val service = MarkdownFrontendService.getInstance() + val targetFile = uri.findVirtualFile() ?: return false @Suppress("NAME_SHADOWING") - val project = project ?: service.guessProjectForUri(uri) ?: return false + val project = project ?: guessProjectForFile(targetFile) ?: return false val anchor = uri.fragment if (anchor == null){ coroutineScope.launch(Dispatchers.EDT) { - service.openFile(project.projectId(), uri) + OpenFileAction.openFile(targetFile, project) } return true } - val headers = service.collectHeaders(project.projectId(), uri) + val headers = MarkdownLinkOpenerUtil.collectHeaders(project, anchor, targetFile) if (headers == null) { coroutineScope.launch { DumbService.getInstance(project).showDumbModeNotificationForFunctionality( @@ -206,7 +207,7 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark } if (headers.size == 1) { coroutineScope.launch(Dispatchers.EDT) { - service.navigateToHeader(project.projectId(), headers.first()) + MarkdownLinkOpenerUtil.navigateToHeader(project, headers.first()) } return true } @@ -277,7 +278,7 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark override fun onChosen(selectedValue: MarkdownHeaderInfo, finalChoice: Boolean): PopupStep<*> { return doFinalStep { - MarkdownFrontendService.getInstance().navigateToHeader(project.projectId(), selectedValue) + MarkdownLinkOpenerUtil.navigateToHeader(project, selectedValue) } } } diff --git a/plugins/markdown/core/resources/META-INF/plugin.xml b/plugins/markdown/core/resources/META-INF/plugin.xml index 395de9aa3672..d85c2bb5a796 100644 --- a/plugins/markdown/core/resources/META-INF/plugin.xml +++ b/plugins/markdown/core/resources/META-INF/plugin.xml @@ -186,9 +186,6 @@ - - collectHeaders(ProjectId projectId, @NotNull URI uri); - Project guessProjectForUri(URI uri); - void navigateToHeader(ProjectId projectId, MarkdownHeaderInfo headerInfo); - - static MarkdownFrontendService getInstance() { - return ApplicationManager.getApplication().getService(MarkdownFrontendService.class); - } -} 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 deleted file mode 100644 index 9a4e1882973c..000000000000 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/service/MarkdownFrontendServiceImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.intellij.plugins.markdown.service; - -import com.intellij.ide.actions.OpenFileAction; -import com.intellij.ide.vfs.VirtualFileIdKt; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectUtil; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.platform.project.ProjectId; -import com.intellij.platform.project.ProjectIdKt; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.PsiUtilCore; -import com.intellij.util.PsiNavigateUtil; -import com.intellij.util.UriUtil; -import com.intellij.util.containers.ContainerUtil; -import org.intellij.plugins.markdown.dto.MarkdownHeaderInfo; -import org.intellij.plugins.markdown.lang.index.HeaderAnchorIndex; -import org.intellij.plugins.markdown.lang.psi.impl.MarkdownHeader; -import org.intellij.plugins.markdown.mapper.MarkdownHeaderMapper; -import org.intellij.plugins.markdown.ui.preview.MarkdownEditorWithPreview; -import org.jetbrains.annotations.NotNull; - -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.util.Collection; -import java.util.List; - -// md TODO: organize the code better -public final class MarkdownFrontendServiceImpl implements MarkdownFrontendService { - private static final Logger logger = Logger.getInstance(MarkdownFrontendServiceImpl.class); - - @Override - public void openFile(ProjectId projectId, @NotNull URI uri) { - Project project = ProjectIdKt.findProject(projectId); - OpenFileAction.openFile(uri.getPath(), project); - } - - @Override - public Collection collectHeaders(ProjectId projectId, @NotNull URI uri) { - Project project = ProjectIdKt.findProject(projectId); - VirtualFile targetFile = findVirtualFile(uri); - String anchor = uri.getFragment(); - - 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); - Collection headers = HeaderAnchorIndex.Companion.collectHeaders(project, scope, anchor); - return ContainerUtil.map(headers, MarkdownHeaderMapper::map); - }); - } - - @Override - public Project guessProjectForUri(URI uri) { - VirtualFile file = findVirtualFile(uri); - return ProjectUtil.guessProjectForFile(file); - } - - @Override - public void navigateToHeader(ProjectId projectId, MarkdownHeaderInfo headerInfo) { - URI uri = createFileUri(headerInfo.getFilePath()); - if (uri == null) return; - VirtualFile file = VirtualFileIdKt.virtualFile(headerInfo.getVirtualFileId()); - if (file == null) return; - Project project = ProjectIdKt.findProject(projectId); - FileEditorManager manager = FileEditorManager.getInstance(project); - List openedEditors = manager.getEditorList(file).stream() - .filter(editor -> editor instanceof MarkdownEditorWithPreview) - .map(editor -> (MarkdownEditorWithPreview)editor) - .toList(); - PsiElement element = PsiUtilCore.getPsiFile(project, file).findElementAt(headerInfo.getTextOffset()); - if (element == null) return; - if (!openedEditors.isEmpty()) { - for (MarkdownEditorWithPreview editor : openedEditors) { - PsiUtilCore.getElementAtOffset(PsiUtilCore.getPsiFile(project, file), element.getTextOffset()); - PsiNavigateUtil.navigate(element, true); - } - return; - } - OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file, element.getTextOffset()); - manager.openEditor(descriptor, true); - } - - private static VirtualFile findVirtualFile(URI uri){ - String uriPath = uri.getPath(); - if (SystemInfo.isWindows) uriPath = UriUtil.trimLeadingSlashes(uriPath); - Path path = Path.of(uriPath); - return VfsUtil.findFile(path, true); - } - - // md TODO: might be an extension function - private static URI createFileUri(String link) { - try { - return new URI("file", null, link, null); - } - catch (URISyntaxException exception) { - logger.warn(exception); - return null; - } - } -} diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpenerUtil.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpenerUtil.kt new file mode 100644 index 000000000000..7a2f36b05ced --- /dev/null +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/accessor/MarkdownLinkOpenerUtil.kt @@ -0,0 +1,87 @@ +// Copyright 2000-2025 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 + +import com.intellij.ide.vfs.virtualFile +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.fileEditor.FileEditor +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.OpenFileDescriptor +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.SystemInfo +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiManager +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiUtilCore +import com.intellij.util.PsiNavigateUtil +import com.intellij.util.UriUtil +import org.intellij.plugins.markdown.dto.MarkdownHeaderInfo +import org.intellij.plugins.markdown.lang.index.HeaderAnchorIndex +import org.intellij.plugins.markdown.mapper.MarkdownHeaderMapper +import org.intellij.plugins.markdown.ui.preview.MarkdownEditorWithPreview +import java.net.URI +import java.net.URISyntaxException +import java.nio.file.Path +import com.intellij.openapi.diagnostic.logger + +class MarkdownLinkOpenerUtil { + companion object{ + private val logger = logger() + + fun navigateToHeader(project: Project, headerInfo: MarkdownHeaderInfo) { + val uri = createFileUri(headerInfo.filePath) + if (uri == null) return + val file = headerInfo.virtualFileId.virtualFile() + if (file == null) return + val manager = FileEditorManager.getInstance(project) + val openedEditors = manager.getEditorList(file).stream() + .filter { editor: FileEditor? -> editor is MarkdownEditorWithPreview } + .map { editor: FileEditor? -> editor as MarkdownEditorWithPreview } + .toList() + val element = PsiUtilCore.getPsiFile(project, file).findElementAt(headerInfo.textOffset) + if (element == null) return + if (!openedEditors.isEmpty()) { + for (editor in openedEditors) { + PsiUtilCore.getElementAtOffset(PsiUtilCore.getPsiFile(project, file), element.getTextOffset()) + PsiNavigateUtil.navigate(element, true) + } + return + } + val descriptor = OpenFileDescriptor(project, file, element.getTextOffset()) + manager.openEditor(descriptor, true) + } + + fun collectHeaders(project: Project, anchor: String, targetFile: VirtualFile): List? { + return runReadAction { + if (DumbService.isDumb(project)) { + return@runReadAction emptyList() + } + 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).map(MarkdownHeaderMapper::map) + } + } + + 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 createFileUri(link: String?): URI? { + try { + return URI("file", null, link, null) + } + catch (exception: URISyntaxException) { + logger.warn(exception) + return null + } + } + } +} \ No newline at end of file