How to correctly call NavigatablePsiElement.navigate()

My current way is

cs.launch(Dispatchers.Default) {
    smartReadAction(project, process) {
        val element = findNavigatablePsiElement()
        invokeLater {
            element.navigate(true)
        }
    }
}

It generates an exception(only in non-production releases, such as EAP, but anyway)

java.lang.Throwable: Slow operations are prohibited on EDT. See SlowOperations.assertSlowOperationsAreAllowed javadoc.
at com.intellij.openapi.diagnostic.Logger.error(Logger.java:375)
at com.intellij.util.SlowOperations.assertSlowOperationsAreAllowed(SlowOperations.java:113)
at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.ensureIsUpToDate(WorkspaceFileIndexDataImpl.kt:160)
at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.getFileInfo(WorkspaceFileIndexDataImpl.kt:105)
at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexImpl.getFileInfo(WorkspaceFileIndexImpl.kt:307)
at com.intellij.openapi.roots.impl.ProjectFileIndexImpl.isUnderIgnored(ProjectFileIndexImpl.java:86)
at com.intellij.openapi.roots.impl.ProjectFileIndexFacade.isUnderIgnored(ProjectFileIndexFacade.java:81)
at com.intellij.psi.impl.file.impl.FileManagerImpl.isExcludedOrIgnored(FileManagerImpl.java:577)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectoryImpl(FileManagerImpl.java:563)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectoryImpl(FileManagerImpl.java:567)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectoryImpl(FileManagerImpl.java:567)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectory(FileManagerImpl.java:556)
at com.intellij.psi.impl.PsiManagerImpl.findDirectory(PsiManagerImpl.java:187)
at com.intellij.psi.AbstractFileViewProvider.shouldCreatePsi(AbstractFileViewProvider.java:91)
at com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider.getPsiInner(MultiplePsiFilesPerDocumentFileViewProvider.java:69)
at com.intellij.psi.AbstractFileViewProvider.getPsi(AbstractFileViewProvider.java:188)
at com.intellij.psi.impl.file.impl.FileManagerImpl.areViewProvidersEquivalent(FileManagerImpl.java:698)
at com.intellij.psi.impl.file.impl.FileManagerImpl.shouldResurrect(FileManagerImpl.java:803)
at com.intellij.psi.impl.file.impl.FileManagerImpl.evaluateValidity(FileManagerImpl.java:767)
at com.intellij.psi.impl.file.impl.FileManagerImpl.evaluateValidity(FileManagerImpl.java:750)
at com.intellij.psi.impl.source.PsiFileImpl.isValid(PsiFileImpl.java:173)
at com.intellij.psi.impl.source.SubstrateRef$StubRef.isValid(SubstrateRef.java:101)
at com.intellij.extapi.psi.StubBasedPsiElementBase.isValid(StubBasedPsiElementBase.java:261)
at com.intellij.ide.util.EditSourceUtil.canNavigate(EditSourceUtil.java:62)
at com.intellij.ide.util.EditSourceUtil.getDescriptor(EditSourceUtil.java:33)
at com.intellij.ide.util.PsiNavigationSupportImpl.getDescriptor(PsiNavigationSupportImpl.java:24)
at com.intellij.psi.impl.PsiElementBase.navigate(PsiElementBase.java:192)

Side note: please do not use in coroutines, switch to EDT using withContext(Dispatchers.EDT) { } outside of read actions

1 Like

I think in your case the simplest option is com.intellij.util.OpenSourceUtil#navigate(com.intellij.pom.Navigatable...)

@Service(Service.Level.PROJECT)
class SearchService(private val project: Project, private val cs: CoroutineScope) {
    fun searchForString() {
        cs.launch {
            val psiClass = smartReadAction {
                JavaPsiFacade.getInstance(project).findClass("java.lang.String", allScope(project))
            }
            if (psiClass != null) {
                withContext(Dispatchers.EDT) {
                    OpenSourceUtil.navigate(psiClass)
                }
            }
        }
    }
}

Ideally, you would use NavigationService but it will become stable only in 2026.1:

val request: NavigationRequest? = smartReadAction {
    JavaPsiFacade.getInstance(project).findClass("java.lang.String", allScope(project))
        ?.navigationRequest()
}
if (request != null) {
    withContext(Dispatchers.EDT) {
        NavigationService.getInstance(project).navigate(request)
    }
}

The key difference is that NavigationService does not use any kind of modal dialogs (usually they are invisible anyway because navigation is shorter than 250ms). While OpenSourceUtil uses a modal progress and supports legacy use cases with Navigatables implementing custom navigate.

1 Like

Hi, Yuriy. Thanks a lot for your answer. I already changed everything in my plugin after your last comment. And my solution looks almost the same as yours. The only difference that I use simple NavigatableElement.navigate(true). Anything wrong with this? Works fine…

Navigatable.navigate() call is not really recommended because it may access model and lead to visible delays/freezes on EDT.

Both options from my answer always access model on background threads.

E.g. plain navigate may trigger decompiler in EDT for Java .class, we had a lot of similar freezes in the past. Currently the platform mostly works through NavigationService.

2 Likes

Slow operations are prohibited on EDT. See SlowOperations.assertSlowOperationsAreAllowed javadoc.

Also want to highlight the fact that SlowOperations assert is not an exception, it is logged error in development/EAP to prevent new incorrect code. It is an important safety measure - it shows that here UI freeze is possible.