[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
This commit is contained in:
Milan Doslic
2025-04-15 07:21:53 +00:00
committed by intellij-monorepo-bot
parent 8cd04eb35c
commit be919d5f08
5 changed files with 99 additions and 148 deletions
@@ -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<MarkdownHeaderInfo>?){
private suspend fun processHeaders(anchor: String, project: Project, headers: List<MarkdownHeaderInfo>?){
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)
}
}
}
@@ -186,9 +186,6 @@
<applicationService serviceInterface="org.intellij.plugins.markdown.extensions.ExtensionsExternalFilesPathManager"
serviceImplementation="org.intellij.plugins.markdown.extensions.ExtensionsExternalFilesPathManager$Impl"/>
<applicationService serviceInterface="org.intellij.plugins.markdown.service.MarkdownFrontendService"
serviceImplementation="org.intellij.plugins.markdown.service.MarkdownFrontendServiceImpl"/>
<search.optionContributor implementation="org.intellij.plugins.markdown.extensions.ExtensionSearchableOptionContributor"/>
<projectConfigurable id="Settings.Markdown"
@@ -1,22 +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.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.platform.project.ProjectId;
import org.intellij.plugins.markdown.dto.MarkdownHeaderInfo;
import org.jetbrains.annotations.NotNull;
import java.net.URI;
import java.util.Collection;
public interface MarkdownFrontendService {
void openFile(ProjectId projectId, @NotNull URI uri);
Collection<MarkdownHeaderInfo> 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);
}
}
@@ -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<MarkdownHeaderInfo> 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<MarkdownHeader> 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<MarkdownEditorWithPreview> 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;
}
}
}
@@ -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<MarkdownLinkOpenerUtil>()
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<MarkdownEditorWithPreview?> { 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<MarkdownHeaderInfo>? {
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
}
}
}
}