From 5d1817d628ae73eecbdf3e8e19c990d99fc66eca Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 5 Feb 2019 18:19:07 +0300 Subject: [PATCH] [platform] add new go to declaration handler using GotoDeclarationProvider API (IDEA-198180) --- .../actions/GotoDeclarationOnlyAction.kt | 5 +- .../actions/GotoDeclarationOnlyHandler2.kt | 76 +++++++++++++++++++ .../src/com/intellij/navigation/chooser.kt | 38 ++++++++++ .../util/resources/misc/registry.properties | 2 + 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyHandler2.kt create mode 100644 platform/lang-impl/src/com/intellij/navigation/chooser.kt diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyAction.kt b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyAction.kt index 393d3f64e82e..7c69988caae9 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyAction.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyAction.kt @@ -2,11 +2,14 @@ package com.intellij.codeInsight.navigation.actions import com.intellij.codeInsight.CodeInsightActionHandler +import com.intellij.openapi.util.registry.Registry /** * Go To Declaration which doesn't invoke Show Usages if there are no declarations to go */ class GotoDeclarationOnlyAction : GotoDeclarationAction() { - override fun getHandler(): CodeInsightActionHandler = GotoDeclarationOnlyHandler + override fun getHandler(): CodeInsightActionHandler { + return if (Registry.`is`("ide.goto.target")) GotoDeclarationOnlyHandler2 else GotoDeclarationOnlyHandler + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyHandler2.kt b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyHandler2.kt new file mode 100644 index 000000000000..2f8ab1d36815 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoDeclarationOnlyHandler2.kt @@ -0,0 +1,76 @@ +// Copyright 2000-2019 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. +package com.intellij.codeInsight.navigation.actions + +import com.intellij.codeInsight.CodeInsightActionHandler +import com.intellij.codeInsight.CodeInsightBundle +import com.intellij.codeInsight.findAllTargets +import com.intellij.codeInsight.hint.HintManager +import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction.isKeywordUnderCaret +import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction.underModalProgress +import com.intellij.featureStatistics.FeatureUsageTracker +import com.intellij.navigation.NavigationTarget +import com.intellij.navigation.chooseTarget +import com.intellij.openapi.command.executeCommand +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.OpenFileDescriptor +import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.IndexNotReadyException +import com.intellij.openapi.project.Project +import com.intellij.pom.Navigatable +import com.intellij.psi.PsiFile + +object GotoDeclarationOnlyHandler2 : CodeInsightActionHandler { + + override fun startInWriteAction(): Boolean = false + + override fun invoke(project: Project, editor: Editor, file: PsiFile) { + FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration") + + val targets = try { + underModalProgress(project, "Resolving Reference...") { + findAllTargets(project, editor, file) + } + } + catch (e: IndexNotReadyException) { + DumbService.getInstance(project).showDumbModeNotification("Navigation is not available here during index update") + return + } + + if (targets.isEmpty()) { + notifyCantGoAnywhere(project, editor, file) + } + else { + chooseTarget(project, editor, CodeInsightBundle.message("declaration.navigation.title"), targets.toList()) { + gotoTarget(project, editor, file, it) + } + } + } + + private fun notifyCantGoAnywhere(project: Project, editor: Editor, file: PsiFile) { + if (!isKeywordUnderCaret(project, file, editor.caretModel.offset)) { + HintManager.getInstance().showErrorHint(editor, "Cannot find declaration to go to") + } + } + + private fun gotoTarget(project: Project, editor: Editor, file: PsiFile, target: NavigationTarget) { + val navigatable = target.navigatable ?: return + if (navigateInCurrentEditor(project, editor, file, navigatable)) { + return + } + if (navigatable.canNavigate()) { + navigatable.navigate(true) + } + } + + private fun navigateInCurrentEditor(project: Project, editor: Editor, file: PsiFile, target: Navigatable): Boolean { + if (!editor.isDisposed && target is OpenFileDescriptor && target.file == file.virtualFile) { + executeCommand { + IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation() + target.navigateIn(editor) + } + return true + } + return false + } +} diff --git a/platform/lang-impl/src/com/intellij/navigation/chooser.kt b/platform/lang-impl/src/com/intellij/navigation/chooser.kt new file mode 100644 index 000000000000..727d36fcdc88 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/navigation/chooser.kt @@ -0,0 +1,38 @@ +// Copyright 2000-2019 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. +@file:Experimental + +package com.intellij.navigation + +import com.intellij.ide.ui.createTargetPresentationRenderer +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.ex.util.EditorUtil +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.popup.JBPopupFactory +import com.intellij.util.Consumer +import org.jetbrains.annotations.ApiStatus.Experimental + +fun chooseTarget(project: Project, editor: Editor, title: String, targets: List, handler: (NavigationTarget) -> Unit) { + chooseTarget(project, editor, title, targets, Consumer(handler)) +} + +fun chooseTarget(project: Project, editor: Editor, title: String, targets: List, consumer: Consumer) { + targets.singleOrNull()?.let { + consumer.consume(it) + return + } + val renderer = createTargetPresentationRenderer(project, NavigationTarget::getPresentationIfValid) + return JBPopupFactory.getInstance() + .createPopupChooserBuilder(targets) + .setRenderer(renderer) + .setNamerForFiltering(renderer::getItemSearchString) + .setFont(EditorUtil.getEditorFont()) + .setTitle(title) + .setItemChosenCallback(consumer) + .withHintUpdateSupply() + .createPopup() + .showInBestPositionFor(editor) +} + +private fun NavigationTarget.getPresentationIfValid(): TargetPresentation? { + return if (isValid) targetPresentation else null +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index e21b1001ab61..557b2312295d 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -1684,6 +1684,8 @@ ide.jvm.run.marker=false ide.jvm.run.marker.description=Enable JVM-lang based run line markers ide.create.field.enable.shortening=true ide.create.field.enable.shortening.description=Always enable shortening of FQNs in templates in Create Field fixes +ide.goto.target=false +ide.goto.target.description=Use NavigationTarget/Symbol navigation instead of PsiElement navigation use.prebuilt.indices=true use.prebuilt.indices.description=Use bundled prebuilt stubs if they are available for better indexing performance