From 447cea89c5e5617f7c8be0cc2ffbdc25bfbc7c2b Mon Sep 17 00:00:00 2001 From: Milan Doslic Date: Tue, 8 Apr 2025 10:42:46 +0200 Subject: [PATCH] [markdown] IJPL-177111 refactor Markdown components for consistency and clarity GitOrigin-RevId: 531f7c07d9a8be35cf6db5bd56a7aaa66a07488c --- .../resources/intellij.markdown.backend.xml | 4 +- ...er.kt => MarkdownLinkOpenerApiProvider.kt} | 2 +- .../providers/ProjectStructureProvider.kt | 14 --- .../providers/VirtualFileAccessorProvider.kt | 14 +++ ...eApiImpl.kt => VirtualFileAccessorImpl.kt} | 9 +- .../accessor/impl/MarkdownLinkOpenerImpl.kt | 10 +- .../extensions/jcef/ProcessImagesExtension.kt | 38 ++++--- .../markdown/mapper/MarkdownHeaderMapper.java | 6 +- ...ureRemoteApi.kt => VirtualFileAccessor.kt} | 8 +- .../ui/preview/accessor/MarkdownLinkOpener.kt | 1 + .../accessor/MarkdownLinkOpenerUtil.kt | 102 +++++++++--------- 11 files changed, 104 insertions(+), 104 deletions(-) rename plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/{LinkOpenerApiProvider.kt => MarkdownLinkOpenerApiProvider.kt} (88%) delete mode 100644 plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/ProjectStructureProvider.kt create mode 100644 plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/VirtualFileAccessorProvider.kt rename plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/{ProjectStructureRemoteApiImpl.kt => VirtualFileAccessorImpl.kt} (76%) rename plugins/markdown/core/src/org/intellij/plugins/markdown/service/{ProjectStructureRemoteApi.kt => VirtualFileAccessor.kt} (75%) diff --git a/plugins/markdown/core/backend/resources/intellij.markdown.backend.xml b/plugins/markdown/core/backend/resources/intellij.markdown.backend.xml index 960c80349259..69afef589557 100644 --- a/plugins/markdown/core/backend/resources/intellij.markdown.backend.xml +++ b/plugins/markdown/core/backend/resources/intellij.markdown.backend.xml @@ -5,8 +5,8 @@ + implementation="org.intellij.plugins.markdown.backend.providers.MarkdownLinkOpenerApiProvider"/> + implementation="org.intellij.plugins.markdown.backend.providers.VirtualFileAccessorProvider"/> \ No newline at end of file diff --git a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/LinkOpenerApiProvider.kt b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/MarkdownLinkOpenerApiProvider.kt similarity index 88% rename from plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/LinkOpenerApiProvider.kt rename to plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/MarkdownLinkOpenerApiProvider.kt index 9a45f78b8c98..c43086a8f3b9 100644 --- a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/LinkOpenerApiProvider.kt +++ b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/MarkdownLinkOpenerApiProvider.kt @@ -5,7 +5,7 @@ import fleet.rpc.remoteApiDescriptor import org.intellij.plugins.markdown.backend.services.MarkdownLinkOpenerRemoteApiImpl import org.intellij.plugins.markdown.service.MarkdownLinkOpenerRemoteApi -private class LinkOpenerApiProvider : RemoteApiProvider { +private class MarkdownLinkOpenerApiProvider : RemoteApiProvider { override fun RemoteApiProvider.Sink.remoteApis() { remoteApi(remoteApiDescriptor()) { MarkdownLinkOpenerRemoteApiImpl() diff --git a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/ProjectStructureProvider.kt b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/ProjectStructureProvider.kt deleted file mode 100644 index 9d330056ca8b..000000000000 --- a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/ProjectStructureProvider.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.intellij.plugins.markdown.backend.providers - -import com.intellij.platform.rpc.backend.RemoteApiProvider -import fleet.rpc.remoteApiDescriptor -import org.intellij.plugins.markdown.backend.services.ProjectStructureRemoteApiImpl -import org.intellij.plugins.markdown.service.ProjectStructureRemoteApi - -private class ProjectStructureProvider : RemoteApiProvider { - override fun RemoteApiProvider.Sink.remoteApis() { - remoteApi(remoteApiDescriptor()) { - ProjectStructureRemoteApiImpl() - } - } -} \ No newline at end of file diff --git a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/VirtualFileAccessorProvider.kt b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/VirtualFileAccessorProvider.kt new file mode 100644 index 000000000000..9c5696de3612 --- /dev/null +++ b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/providers/VirtualFileAccessorProvider.kt @@ -0,0 +1,14 @@ +package org.intellij.plugins.markdown.backend.providers + +import com.intellij.platform.rpc.backend.RemoteApiProvider +import fleet.rpc.remoteApiDescriptor +import org.intellij.plugins.markdown.backend.services.VirtualFileAccessorImpl +import org.intellij.plugins.markdown.service.VirtualFileAccessor + +private class VirtualFileAccessorProvider : RemoteApiProvider { + override fun RemoteApiProvider.Sink.remoteApis() { + remoteApi(remoteApiDescriptor()) { + VirtualFileAccessorImpl() + } + } +} \ No newline at end of file diff --git a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/ProjectStructureRemoteApiImpl.kt b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/VirtualFileAccessorImpl.kt similarity index 76% rename from plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/ProjectStructureRemoteApiImpl.kt rename to plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/VirtualFileAccessorImpl.kt index 2147918b6d63..f95dfac3eec4 100644 --- a/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/ProjectStructureRemoteApiImpl.kt +++ b/plugins/markdown/core/backend/src/org/intellij/plugins/markdown/backend/services/VirtualFileAccessorImpl.kt @@ -9,11 +9,11 @@ import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.platform.project.ProjectId import com.intellij.platform.project.findProject -import org.intellij.plugins.markdown.service.ProjectStructureRemoteApi +import org.intellij.plugins.markdown.service.VirtualFileAccessor import java.io.File import java.net.URL -class ProjectStructureRemoteApiImpl : ProjectStructureRemoteApi { +class VirtualFileAccessorImpl : VirtualFileAccessor { private fun getBaseDirectory(projectId: ProjectId, virtualFileId: VirtualFileId) : VirtualFile?{ val project = projectId.findProject() val virtualFile = virtualFileId.virtualFile() ?: return null @@ -21,8 +21,7 @@ class ProjectStructureRemoteApiImpl : ProjectStructureRemoteApi { return baseDirectory } - override suspend fun getFileByResourceName(resourceName: String, virtualFileId: VirtualFileId?, projectId: ProjectId?): VirtualFileId? { - if (projectId == null || virtualFileId == null) return null + override suspend fun getFileByResourceName(resourceName: String, virtualFileId: VirtualFileId, projectId: ProjectId): VirtualFileId? { val projectRoot = getBaseDirectory(projectId, virtualFileId) val resource = if (resourceName.startsWith("file:/")) { VfsUtil.findFileByIoFile(File(URL(resourceName).path), true) @@ -33,6 +32,6 @@ class ProjectStructureRemoteApiImpl : ProjectStructureRemoteApi { } companion object { - private val logger: Logger = Logger.getInstance(ProjectStructureRemoteApiImpl::class.java) + private val logger: Logger = Logger.getInstance(VirtualFileAccessorImpl::class.java) } } \ 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 f50955188c59..d3ee6288da07 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 @@ -36,12 +36,13 @@ 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.ui.preview.accessor.MarkdownLinkOpenerUtil.findVirtualFile import org.intellij.plugins.markdown.util.MarkdownDisposable import java.net.URI import java.net.URISyntaxException internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : MarkdownLinkOpener { + @Deprecated("Use openLink(project, link, sourceFile) instead", replaceWith = ReplaceWith("openLink(project, link, sourceFile)")) override fun openLink(project: Project?, link: String) { val uri = createUri(link) ?: return if (tryOpenInEditorDeprecated(project, uri)) { @@ -52,16 +53,15 @@ internal class MarkdownLinkOpenerImpl(val coroutineScope: CoroutineScope) : Mark } } - override fun openLink(project: Project?, link: String, containingFile: VirtualFile?) { + override fun openLink(currentProject: Project?, link: String, containingFile: VirtualFile?) { coroutineScope.launch { val data = MarkdownLinkOpenerRemoteApi.getInstance().fetchLinkNavigationData(link, containingFile?.rpcId()) val uri = createUri(data.uri) ?: return@launch if (uri.scheme != "file") { - openExternalLink(project, uri) + openExternalLink(currentProject, uri) return@launch } - @Suppress("NAME_SHADOWING") - val project = project ?: data.projectId?.findProject() ?: return@launch + val project = currentProject ?: data.projectId?.findProject() ?: return@launch val fileToOpen = data.virtualFileId?.virtualFile() ?: return@launch val anchor = uri.fragment if (anchor == null) { diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessImagesExtension.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessImagesExtension.kt index e1959166211e..795880b43767 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessImagesExtension.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/jcef/ProcessImagesExtension.kt @@ -8,14 +8,14 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.platform.project.projectId import kotlinx.coroutines.runBlocking import org.intellij.plugins.markdown.extensions.MarkdownBrowserPreviewExtension -import org.intellij.plugins.markdown.service.ProjectStructureRemoteApi +import org.intellij.plugins.markdown.service.VirtualFileAccessor import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel import org.intellij.plugins.markdown.ui.preview.ResourceProvider internal class ProcessImagesExtension( private val baseFile: VirtualFile?, - private val project: Project? -): ResourceProvider, MarkdownBrowserPreviewExtension { + private val project: Project?, +) : ResourceProvider, MarkdownBrowserPreviewExtension { override val resourceProvider: ResourceProvider = this override fun canProvide(resourceName: String): Boolean { @@ -23,32 +23,30 @@ internal class ProcessImagesExtension( } override fun loadResource(resourceName: String): ResourceProvider.Resource? { - val baseFileId = baseFile?.rpcId() - val projectId = project?.projectId() + val baseFileId = baseFile?.rpcId() ?: return null + val projectId = project?.projectId() ?: return null val resource = runBlocking { - ProjectStructureRemoteApi.getInstance().getFileByResourceName(resourceName, baseFileId, projectId)?.virtualFile() + VirtualFileAccessor.getInstance().getFileByResourceName(resourceName, baseFileId, projectId)?.virtualFile() } ?: return null return ResourceProvider.loadExternalResource(resource) } override fun dispose() = Unit - companion object{ - fun hasImageExtension(name: String): Boolean { - val lowerName = name.lowercase() - return lowerName.endsWith(".jpeg") || - lowerName.endsWith(".jpg") || - lowerName.endsWith(".png") || - lowerName.endsWith(".gif") || - lowerName.endsWith(".bmp") || - lowerName.endsWith(".svg") || - lowerName.endsWith(".webp") || - lowerName.endsWith(".tiff") || - lowerName.endsWith(".tif") - } + private fun hasImageExtension(name: String): Boolean { + val lowerName = name.lowercase() + return lowerName.endsWith(".jpeg") || + lowerName.endsWith(".jpg") || + lowerName.endsWith(".png") || + lowerName.endsWith(".gif") || + lowerName.endsWith(".bmp") || + lowerName.endsWith(".svg") || + lowerName.endsWith(".webp") || + lowerName.endsWith(".tiff") || + lowerName.endsWith(".tif") } - class Provider: MarkdownBrowserPreviewExtension.Provider { + class Provider : MarkdownBrowserPreviewExtension.Provider { override fun createBrowserExtension(panel: MarkdownHtmlPanel): MarkdownBrowserPreviewExtension { return ProcessImagesExtension(panel.virtualFile, panel.project) } diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/mapper/MarkdownHeaderMapper.java b/plugins/markdown/core/src/org/intellij/plugins/markdown/mapper/MarkdownHeaderMapper.java index 6cfca2890866..98ce072d4f69 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/mapper/MarkdownHeaderMapper.java +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/mapper/MarkdownHeaderMapper.java @@ -2,6 +2,7 @@ package org.intellij.plugins.markdown.mapper; import com.intellij.ide.vfs.VirtualFileId; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; @@ -13,9 +14,12 @@ import com.intellij.ide.vfs.VirtualFileIdKt; * Mapper class for converting a MarkdownHeader into a MarkdownHeaderInfo. */ public final class MarkdownHeaderMapper { + private static final Logger logger = Logger.getInstance(MarkdownHeaderMapper.class); + public static MarkdownHeaderInfo map(MarkdownHeader header) { if (header == null) { - throw new IllegalArgumentException("header cannot be null"); + logger.warn("Header is null, returning null"); + return null; } String headerText = header.getText(); diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/service/ProjectStructureRemoteApi.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/service/VirtualFileAccessor.kt similarity index 75% rename from plugins/markdown/core/src/org/intellij/plugins/markdown/service/ProjectStructureRemoteApi.kt rename to plugins/markdown/core/src/org/intellij/plugins/markdown/service/VirtualFileAccessor.kt index 131ffb401c40..2b50c15056ac 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/service/ProjectStructureRemoteApi.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/service/VirtualFileAccessor.kt @@ -11,13 +11,13 @@ import org.jetbrains.annotations.ApiStatus @ApiStatus.Internal @Rpc -interface ProjectStructureRemoteApi : RemoteApi { - suspend fun getFileByResourceName(resourceName: String, virtualFileId: VirtualFileId?, projectId: ProjectId?): VirtualFileId? +interface VirtualFileAccessor : RemoteApi { + suspend fun getFileByResourceName(resourceName: String, virtualFileId: VirtualFileId, projectId: ProjectId): VirtualFileId? companion object { @JvmStatic - suspend fun getInstance(): ProjectStructureRemoteApi { - return RemoteApiProviderService.resolve(remoteApiDescriptor()) + suspend fun getInstance(): VirtualFileAccessor { + return RemoteApiProviderService.resolve(remoteApiDescriptor()) } } } \ No newline at end of file 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 c0fa7fe686e8..d85d6db3e1cd 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,7 @@ interface MarkdownLinkOpener { * * Note: it is possible to add custom url handler with [com.intellij.ide.browsers.UrlOpener] EP. */ + @Deprecated("this method lacks remote development support", ReplaceWith("openLink(project, link, sourceFile)")) fun openLink(project: Project?, link: String) fun openLink(project: Project?, link: String, sourceFile: VirtualFile?) 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 index 7a2f36b05ced..226173700756 100644 --- 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 @@ -25,63 +25,61 @@ import java.net.URISyntaxException import java.nio.file.Path import com.intellij.openapi.diagnostic.logger -class MarkdownLinkOpenerUtil { - companion object{ - private val logger = logger() +object MarkdownLinkOpenerUtil { + 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 + 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) } - val descriptor = OpenFileDescriptor(project, file, element.getTextOffset()) - manager.openEditor(descriptor, 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 collectHeaders(project: Project, anchor: String, targetFile: VirtualFile): List? { + return runReadAction { + if (DumbService.isDumb(project)) { + return@runReadAction emptyList() } - } - - 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 + 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) } } -} \ No newline at end of file + + 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 + } + } +}