mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-7 IDEA-304701 prepare navigation request in BG (parse/decompile), handle it on EDT
GitOrigin-RevId: f75a04052061317c1a0eff0074efca24e5355bef
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b9f4b8a803
commit
3d5155fe07
@@ -34,6 +34,8 @@ interface NavigationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param elementRange is used to determine whether
|
||||
* to preserve the caret if [preserveCaret][com.intellij.platform.ide.navigation.NavigationOptions.preserveCaret] is set
|
||||
* @return a request for the navigation to the [start offset][TextRange.getStartOffset] of [elementRange],
|
||||
* or `null` if the navigation is not possible for any reason
|
||||
*/
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.intellij.ui.popup.list.ListPopupImpl;
|
||||
import com.intellij.ui.popup.list.PopupListElementRenderer;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.TextWithIcon;
|
||||
import com.intellij.util.ui.EDT;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -222,13 +223,15 @@ public final class NavigationUtil {
|
||||
return activateFileIfOpen(project, vFile, element.getTextRange(), searchForOpen, requestFocus);
|
||||
}
|
||||
|
||||
private static boolean activateFileIfOpen(
|
||||
@Internal
|
||||
public static boolean activateFileIfOpen(
|
||||
@NotNull Project project,
|
||||
@NotNull VirtualFile vFile,
|
||||
@Nullable TextRange range,
|
||||
boolean searchForOpen,
|
||||
boolean requestFocus
|
||||
) {
|
||||
EDT.assertIsEdt();
|
||||
if (!EditorHistoryManager.getInstance(project).hasBeenOpen(vFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+15
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package com.intellij.ide.projectView.impl.nodes;
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.FileStatus;
|
||||
import com.intellij.openapi.vcs.FileStatusManager;
|
||||
import com.intellij.openapi.vfs.VFileProperty;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.backend.navigation.NavigationRequest;
|
||||
import com.intellij.pom.StatePreservingNavigatable;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -37,6 +38,8 @@ import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.util.AstLoadingFilter;
|
||||
import com.intellij.util.IconUtil;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
|
||||
import com.intellij.util.concurrency.annotations.RequiresReadLock;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -288,6 +291,17 @@ public abstract class AbstractPsiBasedNode<Value> extends ProjectViewNode<Value>
|
||||
return psiElement instanceof NavigationItem ? (NavigationItem) psiElement : null;
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
@RequiresBackgroundThread
|
||||
@Override
|
||||
public @Nullable NavigationRequest navigationRequest() {
|
||||
PsiElement element = extractPsiFromValue();
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return ((NavigationItem)element).navigationRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void navigate(boolean requestFocus, boolean preserveState) {
|
||||
if (canNavigate()) {
|
||||
|
||||
@@ -8,6 +8,12 @@ import com.intellij.pom.Navigatable
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
/**
|
||||
* @param offsetMarker desired caret position, or `null` to keep the position unchanged
|
||||
* @param elementRangeMarker marker of a range, where the existing caret should remain unchanged
|
||||
* if [com.intellij.platform.ide.navigation.NavigationOptions.preserveCaret] is set,
|
||||
* or `null` to change the caret position according to [offsetMarker]
|
||||
*/
|
||||
@Internal
|
||||
class SourceNavigationRequest internal constructor(
|
||||
val file: VirtualFile,
|
||||
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.navigation.impl
|
||||
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil.activateFileIfOpen
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil.shouldOpenAsNative
|
||||
import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.ide.util.PsiNavigationSupport
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.impl.Utils.isAsyncDataContext
|
||||
import com.intellij.openapi.actionSystem.impl.Utils.wrapToAsyncDataContext
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.progress.blockingContext
|
||||
import com.intellij.openapi.progress.mapWithProgress
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.backend.navigation.NavigationRequest
|
||||
import com.intellij.platform.backend.navigation.impl.DirectoryNavigationRequest
|
||||
import com.intellij.platform.backend.navigation.impl.RawNavigationRequest
|
||||
import com.intellij.platform.backend.navigation.impl.SourceNavigationRequest
|
||||
import com.intellij.platform.ide.navigation.NavigationOptions
|
||||
import com.intellij.platform.ide.navigation.NavigationService
|
||||
import com.intellij.pom.Navigatable
|
||||
import com.intellij.util.OverflowSemaphore
|
||||
import com.intellij.util.ui.EDT
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
@Internal
|
||||
internal class IdeNavigationService(private val project: Project) : NavigationService {
|
||||
|
||||
/**
|
||||
* - `permits = 1` means at any given time only 1 request is being handled.
|
||||
* - [BufferOverflow.DROP_OLDEST] makes each new navigation request cancel the previous one.
|
||||
*/
|
||||
private val semaphore: OverflowSemaphore = OverflowSemaphore(permits = 1, overflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
override suspend fun navigate(ctx: DataContext, options: NavigationOptions) {
|
||||
if (!isAsyncDataContext(ctx)) {
|
||||
LOG.error("Expected async context, got: $ctx")
|
||||
val asyncContext = withContext(Dispatchers.EDT) {
|
||||
// hope that context component is still available
|
||||
wrapToAsyncDataContext(ctx)
|
||||
}
|
||||
navigate(asyncContext, options)
|
||||
}
|
||||
return semaphore.withPermit {
|
||||
val navigatables = readAction {
|
||||
ctx.getData(CommonDataKeys.NAVIGATABLE_ARRAY)
|
||||
}
|
||||
if (!navigatables.isNullOrEmpty()) {
|
||||
doNavigate(navigatables.toList(), options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun navigate(navigatables: List<Navigatable>, options: NavigationOptions): Boolean {
|
||||
return semaphore.withPermit {
|
||||
doNavigate(navigatables, options)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun doNavigate(navigatables: List<Navigatable>, options: NavigationOptions): Boolean {
|
||||
val requests = navigatables.mapWithProgress(concurrent = true) {
|
||||
readAction {
|
||||
it.navigationRequest()
|
||||
}
|
||||
}.filterNotNull()
|
||||
return withContext(Dispatchers.EDT) {
|
||||
blockingContext {
|
||||
navigate(project, requests, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun navigate(navigatable: Navigatable, options: NavigationOptions): Boolean {
|
||||
return semaphore.withPermit {
|
||||
val request = readAction {
|
||||
navigatable.navigationRequest()
|
||||
}
|
||||
if (request == null) {
|
||||
false
|
||||
}
|
||||
else {
|
||||
withContext(Dispatchers.EDT) {
|
||||
blockingContext {
|
||||
navigateToSource(project, request, options as NavigationOptions.Impl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val LOG = Logger.getInstance("#com.intellij.platform.ide.navigation.impl")
|
||||
|
||||
/**
|
||||
* Navigates to all sources from [requests], or navigates to first non-source request.
|
||||
*/
|
||||
private fun navigate(project: Project, requests: List<NavigationRequest>, options: NavigationOptions): Boolean {
|
||||
EDT.assertIsEdt()
|
||||
|
||||
val maxSourceRequests = Registry.intValue("ide.source.file.navigation.limit", 100)
|
||||
var nonSourceRequest: NavigationRequest? = null
|
||||
|
||||
options as NavigationOptions.Impl
|
||||
var navigatedSourcesCounter = 0
|
||||
for (requestFromNavigatable in requests) {
|
||||
if (maxSourceRequests in 1..navigatedSourcesCounter) {
|
||||
break
|
||||
}
|
||||
if (navigateToSource(project, requestFromNavigatable, options)) {
|
||||
navigatedSourcesCounter++
|
||||
}
|
||||
else if (nonSourceRequest == null) {
|
||||
nonSourceRequest = requestFromNavigatable
|
||||
}
|
||||
}
|
||||
if (navigatedSourcesCounter > 0) {
|
||||
return true
|
||||
}
|
||||
if (nonSourceRequest == null) {
|
||||
return false
|
||||
}
|
||||
navigateNonSource(nonSourceRequest, options)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun navigateToSource(project: Project, request: NavigationRequest, options: NavigationOptions.Impl): Boolean {
|
||||
EDT.assertIsEdt()
|
||||
|
||||
when (request) {
|
||||
is SourceNavigationRequest -> {
|
||||
navigateToSource(project, request, options)
|
||||
return true
|
||||
}
|
||||
is DirectoryNavigationRequest -> {
|
||||
return false
|
||||
}
|
||||
is RawNavigationRequest -> {
|
||||
if (request.canNavigateToSource) {
|
||||
request.navigatable.navigate(options.requestFocus)
|
||||
return true
|
||||
}
|
||||
else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
error("Unsupported request: $request")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToSource(project: Project, request: SourceNavigationRequest, options: NavigationOptions.Impl) {
|
||||
EDT.assertIsEdt()
|
||||
|
||||
if (tryActivateOpenFile(project, request, options)) {
|
||||
return
|
||||
}
|
||||
// TODO support pure source request without OpenFileDescriptor
|
||||
val offset = request.elementRangeMarker?.takeIf { it.isValid }?.startOffset ?: -1
|
||||
val openFileDescriptor = OpenFileDescriptor(project, request.file, offset)
|
||||
openFileDescriptor.isUseCurrentWindow = true
|
||||
if (UISettings.getInstance().openInPreviewTabIfPossible && Registry.`is`("editor.preview.tab.navigation")) {
|
||||
openFileDescriptor.isUsePreviewTab = true
|
||||
}
|
||||
openFileDescriptor.navigate(options.requestFocus)
|
||||
}
|
||||
|
||||
private fun tryActivateOpenFile(
|
||||
project: Project,
|
||||
request: SourceNavigationRequest,
|
||||
options: NavigationOptions.Impl,
|
||||
): Boolean {
|
||||
if (!options.preserveCaret && !options.requestFocus) {
|
||||
return false
|
||||
}
|
||||
if (shouldOpenAsNative(request.file)) {
|
||||
return false
|
||||
}
|
||||
val elementRangeMarker = request.elementRangeMarker
|
||||
if (elementRangeMarker == null || !elementRangeMarker.isValid) {
|
||||
return false
|
||||
}
|
||||
val elementRange = elementRangeMarker.textRange
|
||||
return activateFileIfOpen(project, request.file, elementRange, options.requestFocus, options.requestFocus)
|
||||
}
|
||||
|
||||
private fun navigateNonSource(request: NavigationRequest, options: NavigationOptions.Impl) {
|
||||
EDT.assertIsEdt()
|
||||
|
||||
return when (request) {
|
||||
is DirectoryNavigationRequest -> {
|
||||
PsiNavigationSupport.getInstance().navigateToDirectory(request.directory, options.requestFocus)
|
||||
}
|
||||
is RawNavigationRequest -> {
|
||||
check(!request.canNavigateToSource)
|
||||
request.navigatable.navigate(options.requestFocus)
|
||||
}
|
||||
else -> {
|
||||
error("Non-source request expected here, got: $request")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.navigation
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
@Experimental
|
||||
interface NavigationOptions {
|
||||
|
||||
/**
|
||||
* Sets whether to request the focus.
|
||||
*
|
||||
* Default: `true`.
|
||||
*/
|
||||
fun requestFocus(value: Boolean): NavigationOptions
|
||||
|
||||
/**
|
||||
* If the navigation leads to a file, which is already open in some editor,
|
||||
* the editor will be focused, but the caret position will remain unchanged,
|
||||
* if the caret position is within text range of requested PsiElement.
|
||||
*
|
||||
* For example, when requesting navigation to PsiElement, which corresponds to class `C`:
|
||||
* ```
|
||||
* <caret>package com.foo.bar;
|
||||
* class C { }
|
||||
* ```
|
||||
* the caret will be placed here:
|
||||
* ```
|
||||
* package com.foo.bar;
|
||||
* class <caret>C { }
|
||||
* ```
|
||||
* But if the caret was already inside the [element range][com.intellij.platform.backend.navigation.impl.SourceNavigationRequest.elementRangeMarker],
|
||||
* it will remain unchanged:
|
||||
* ```
|
||||
* package com.foo.bar;
|
||||
* class C { <caret> }
|
||||
* ```
|
||||
*
|
||||
* Default: `false`.
|
||||
*/
|
||||
fun preserveCaret(value: Boolean): NavigationOptions
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun defaultOptions(): NavigationOptions = defaultOptions
|
||||
|
||||
private val defaultOptions = Impl(
|
||||
requestFocus = true,
|
||||
preserveCaret = false,
|
||||
)
|
||||
}
|
||||
|
||||
@Internal
|
||||
data class Impl internal constructor(
|
||||
val requestFocus: Boolean,
|
||||
val preserveCaret: Boolean,
|
||||
) : NavigationOptions {
|
||||
override fun requestFocus(value: Boolean): NavigationOptions = copy(requestFocus = value)
|
||||
override fun preserveCaret(value: Boolean): NavigationOptions = copy(preserveCaret = value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.navigation
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.pom.Navigatable
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
@Experimental
|
||||
interface NavigationService {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): NavigationService {
|
||||
return project.service<NavigationService>()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun navigate(ctx: DataContext, options: NavigationOptions)
|
||||
|
||||
@Internal // compatibility function
|
||||
suspend fun navigate(navigatables: List<Navigatable>, options: NavigationOptions): Boolean
|
||||
|
||||
@Internal // compatibility function
|
||||
suspend fun navigate(navigatable: Navigatable, options: NavigationOptions): Boolean
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.util
|
||||
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.AccessToken
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.ui.ClientProperty
|
||||
import com.intellij.ui.DoubleClickListener
|
||||
import com.intellij.ui.treeStructure.treetable.TreeTable
|
||||
@@ -176,7 +178,13 @@ object EditSourceOnDoubleClickHandler {
|
||||
}
|
||||
|
||||
protected open fun processDoubleClick(e: MouseEvent, dataContext: DataContext, treePath: TreePath) {
|
||||
SlowOperations.knownIssue("IDEA-304701, EA-659716").use {
|
||||
val token = if (Registry.`is`("ide.navigation.requests")) {
|
||||
AccessToken.EMPTY_ACCESS_TOKEN
|
||||
}
|
||||
else {
|
||||
SlowOperations.knownIssue("IDEA-304701, EA-659716")
|
||||
}
|
||||
token.use {
|
||||
OpenSourceUtil.openSourcesFrom(dataContext, true)
|
||||
}
|
||||
whenPerformed?.run()
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.util;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.DataProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.pom.StatePreservingNavigatable;
|
||||
@@ -17,6 +18,10 @@ public final class OpenSourceUtil {
|
||||
}
|
||||
|
||||
public static void openSourcesFrom(@NotNull DataContext context, boolean requestFocus) {
|
||||
if (Registry.is("ide.navigation.requests")) {
|
||||
OpenSourceUtilKt.openSourcesFrom(context, requestFocus);
|
||||
return;
|
||||
}
|
||||
navigate(requestFocus, false, CommonDataKeys.NAVIGATABLE_ARRAY.getData(context));
|
||||
}
|
||||
|
||||
@@ -81,6 +86,12 @@ public final class OpenSourceUtil {
|
||||
if (navigatables == null) {
|
||||
return false;
|
||||
}
|
||||
if (Registry.is("ide.navigation.requests")) {
|
||||
Project project = OpenSourceUtilKt.findProject(navigatables);
|
||||
if (project != null) {
|
||||
return OpenSourceUtilKt.navigate(project, requestFocus, tryNotToScroll, navigatables);
|
||||
}
|
||||
}
|
||||
|
||||
Navigatable nonSourceNavigatable = null;
|
||||
|
||||
@@ -139,7 +150,16 @@ public final class OpenSourceUtil {
|
||||
* @return {@code true} if navigation is done, {@code false} otherwise
|
||||
*/
|
||||
public static boolean navigateToSource(boolean requestFocus, boolean tryNotToScroll, @Nullable Navigatable navigatable) {
|
||||
if (navigatable == null || !navigatable.canNavigateToSource()) {
|
||||
if (navigatable == null) {
|
||||
return false;
|
||||
}
|
||||
if (Registry.is("ide.navigation.requests")) {
|
||||
Project project = OpenSourceUtilKt.findProject(navigatable);
|
||||
if (project != null) {
|
||||
return OpenSourceUtilKt.navigateToSource(project, requestFocus, tryNotToScroll, navigatable);
|
||||
}
|
||||
}
|
||||
if (!navigatable.canNavigateToSource()) {
|
||||
return false;
|
||||
}
|
||||
if (tryNotToScroll && navigatable instanceof StatePreservingNavigatable) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Internal
|
||||
|
||||
package com.intellij.util
|
||||
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.ide.ui.IdeUiService
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.progress.runBlockingModal
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.ide.navigation.NavigationOptions
|
||||
import com.intellij.platform.ide.navigation.NavigationService
|
||||
import com.intellij.pom.Navigatable
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
internal fun openSourcesFrom(context: DataContext, requestFocus: Boolean) {
|
||||
val project = context.getData(CommonDataKeys.PROJECT) ?: return
|
||||
val asyncContext = IdeUiService.getInstance().createAsyncDataContext(context)
|
||||
val options = NavigationOptions.defaultOptions().requestFocus(requestFocus)
|
||||
runBlockingModal(project, IdeBundle.message("progress.title.preparing.navigation")) {
|
||||
NavigationService.getInstance(project).navigate(asyncContext, options)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun navigate(project: Project, requestFocus: Boolean, tryNotToScroll: Boolean, navigatables: Iterable<Navigatable?>?): Boolean {
|
||||
if (navigatables == null) {
|
||||
return false
|
||||
}
|
||||
val filteredNavigatables = navigatables.filterNotNull()
|
||||
val options = NavigationOptions.defaultOptions().requestFocus(requestFocus).preserveCaret(tryNotToScroll)
|
||||
return runBlockingModal(project, IdeBundle.message("progress.title.preparing.navigation")) {
|
||||
NavigationService.getInstance(project).navigate(filteredNavigatables, options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to source of the specified navigatable.
|
||||
*
|
||||
* @param requestFocus specifies whether a focus should be requested or not
|
||||
* @param tryNotToScroll specifies whether a corresponding editor should preserve its state if it is possible
|
||||
* @return `true` if navigation is done, `false` otherwise
|
||||
*/
|
||||
internal fun navigateToSource(project: Project, requestFocus: Boolean, tryNotToScroll: Boolean, navigatable: Navigatable): Boolean {
|
||||
val options = NavigationOptions.defaultOptions().requestFocus(requestFocus).preserveCaret(tryNotToScroll)
|
||||
return runBlockingModal(project, IdeBundle.message("progress.title.preparing.navigation")) {
|
||||
NavigationService.getInstance(project).navigate(navigatable, options)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun findProject(navigatables: Iterable<Navigatable>): Project? {
|
||||
return navigatables.firstNotNullOfOrNull {
|
||||
findProject(it)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun findProject(navigatable: Navigatable): Project? {
|
||||
return when (navigatable) {
|
||||
is PsiElement -> navigatable.project
|
||||
is OpenFileDescriptor -> navigatable.project
|
||||
is NodeDescriptor<*> -> navigatable.project
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,8 @@
|
||||
|
||||
<applicationService serviceInterface="com.intellij.platform.backend.navigation.NavigationRequests"
|
||||
serviceImplementation="com.intellij.platform.backend.navigation.impl.NavigationRequestsImpl"/>
|
||||
<projectService serviceInterface="com.intellij.platform.ide.navigation.NavigationService"
|
||||
serviceImplementation="com.intellij.platform.ide.navigation.impl.IdeNavigationService"/>
|
||||
<applicationService serviceInterface="com.intellij.ide.util.PsiNavigationSupport"
|
||||
serviceImplementation="com.intellij.ide.util.PsiNavigationSupportImpl"/>
|
||||
|
||||
|
||||
@@ -2339,5 +2339,7 @@ ide.log.coroutine.pce.description=Log PCEs occurring in coroutines. \
|
||||
ide.colorful.toolbar.force=false
|
||||
ide.colorful.toolbar.force.description=Show 'Colorful toolbar' even if there is one project open (works only if the feature is enabled). \
|
||||
Reopen project or restart IDE to apply the setting
|
||||
ide.navigation.requests=true
|
||||
ide.navigation.requests.description=When navigation works by computing a navigation request on a BG thread.
|
||||
# please leave this note as last line
|
||||
# TODO please use EP com.intellij.registryKey for plugin/product specific keys
|
||||
|
||||
Reference in New Issue
Block a user