GotoDeclarationHandler now can return multiple targets

This commit is contained in:
Eugene Kudelevsky
2011-04-01 20:52:57 +04:00
parent b32135b2b0
commit 6e1f17ce06
7 changed files with 95 additions and 24 deletions
@@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable;
/**
* @author yole
*/
public class GotoBreakContinueHandler implements GotoDeclarationHandler {
public class GotoBreakContinueHandler extends GotoDeclarationHandlerBase {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.navigation.actions.GotoBreakContinueHandler");
@Nullable
@@ -353,6 +353,23 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
}
}
else if (browseMode == BrowseMode.Declaration) {
final PsiElement[] targetElements = GotoDeclarationAction.findTargetElementsNoVS(myProject, editor, offset);
final PsiElement elementAtPointer = file.findElementAt(offset);
if (targetElements != null) {
if (targetElements.length == 0) {
return null;
}
else if (targetElements.length == 1) {
if (elementAtPointer != null && targetElements[0].isPhysical()) {
return new InfoSingle(elementAtPointer, targetElements[0]);
}
}
else {
return elementAtPointer != null ? new InfoMultiple(elementAtPointer) : null;
}
}
PsiReference ref = TargetElementUtilBase.findReference(editor, offset);
if (ref != null) {
PsiElement resolvedElement = resolve(ref);
@@ -360,7 +377,6 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
return new InfoSingle(ref, resolvedElement);
}
}
targetElement = GotoDeclarationAction.findTargetElementNoVS(myProject, editor, offset);
}
else if (browseMode == BrowseMode.Implementation) {
final PsiElement element = TargetElementUtilBase.getInstance().findTargetElement(editor, ImplementationSearcher.getFlags(), offset);
@@ -76,13 +76,16 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
try {
int offset = editor.getCaretModel().getOffset();
PsiElement element = findTargetElement(project, editor, offset);
PsiElement[] elements = findAllTargetElements(project, editor, offset);
FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration");
if (element == null) {
chooseAmbiguousTarget(editor, offset);
if (elements.length != 1) {
chooseAmbiguousTarget(editor, offset, elements);
return;
}
final PsiElement element = elements[0];
PsiElement navElement = element.getNavigationElement();
navElement = TargetElementUtilBase.getInstance().getGotoDeclarationTarget(element, navElement);
@@ -104,7 +107,7 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
}
}
private static void chooseAmbiguousTarget(final Editor editor, int offset) {
private static void chooseAmbiguousTarget(final Editor editor, int offset, PsiElement[] elements) {
PsiElementProcessor<PsiElement> navigateProcessor = new PsiElementProcessor<PsiElement>() {
public boolean execute(final PsiElement element) {
Navigatable navigatable = EditSourceUtil.getDescriptor(element);
@@ -114,28 +117,37 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
return true;
}
};
boolean found = chooseAmbiguousTarget(editor, offset, navigateProcessor, CodeInsightBundle.message("declaration.navigation.title"));
boolean found =
chooseAmbiguousTarget(editor, offset, navigateProcessor, CodeInsightBundle.message("declaration.navigation.title"), elements);
if (!found) {
HintManager.getInstance().showErrorHint(editor, "Cannot find declaration to go to");
}
}
// returns true if processor is run or is going to be run after showing popup
public static boolean chooseAmbiguousTarget(final Editor editor, int offset, PsiElementProcessor<PsiElement> processor, String titlePattern) {
public static boolean chooseAmbiguousTarget(final Editor editor,
int offset,
PsiElementProcessor<PsiElement> processor,
String titlePattern,
PsiElement[] elements) {
if (TargetElementUtilBase.inVirtualSpace(editor, offset)) {
return false;
}
final PsiReference reference = TargetElementUtilBase.findReference(editor, offset);
final Collection<PsiElement> candidates = suggestCandidates(reference);
if (candidates.size() == 1) {
PsiElement element = candidates.iterator().next();
if (elements == null) {
final Collection<PsiElement> candidates = suggestCandidates(reference);
elements = PsiUtilBase.toPsiElementArray(candidates);
}
if (elements.length == 1) {
PsiElement element = elements[0];
LOG.assertTrue(element != null);
processor.execute(element);
return true;
}
if (candidates.size() > 1) {
PsiElement[] elements = PsiUtilBase.toPsiElementArray(candidates);
if (elements.length > 1) {
final TextRange range = reference.getRangeInElement();
final String refText = range.substring(reference.getElement().getText());
String title = MessageFormat.format(titlePattern, refText);
@@ -158,15 +170,22 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
@Nullable
public static PsiElement findTargetElement(Project project, Editor editor, int offset) {
final PsiElement[] targets = findAllTargetElements(project, editor, offset);
return targets.length == 1 ? targets[0] : null;
}
@NotNull
public static PsiElement[] findAllTargetElements(Project project, Editor editor, int offset) {
if (TargetElementUtilBase.inVirtualSpace(editor, offset)) {
return null;
return PsiElement.EMPTY_ARRAY;
}
return findTargetElementNoVS(project, editor, offset);
final PsiElement[] targets = findTargetElementsNoVS(project, editor, offset);
return targets != null ? targets : PsiElement.EMPTY_ARRAY;
}
@Nullable
public static PsiElement findTargetElementNoVS(Project project, Editor editor, int offset) {
public static PsiElement[] findTargetElementsNoVS(Project project, Editor editor, int offset) {
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (file == null) {
return null;
@@ -174,7 +193,7 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
PsiElement elementAt = file.findElementAt(offset);
for (GotoDeclarationHandler handler : Extensions.getExtensions(GotoDeclarationHandler.EP_NAME)) {
PsiElement result = handler.getGotoDeclarationTarget(elementAt);
PsiElement[] result = handler.getGotoDeclarationTargets(elementAt);
if (result != null) {
return result;
}
@@ -182,12 +201,14 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
int flags = TargetElementUtilBase.getInstance().getAllAccepted() & ~TargetElementUtilBase.ELEMENT_NAME_ACCEPTED;
PsiElement element = TargetElementUtilBase.getInstance().findTargetElement(editor, flags, offset);
if (element != null) return element;
if (element != null) {
return new PsiElement[] {element};
}
// if no references found in injected fragment, try outer document
if (editor instanceof EditorWindow) {
EditorWindow window = (EditorWindow)editor;
return findTargetElementNoVS(project, window.getDelegate(), window.getDocument().injectedToHost(offset));
return findTargetElementsNoVS(project, window.getDelegate(), window.getDocument().injectedToHost(offset));
}
return null;
}
@@ -27,5 +27,5 @@ public interface GotoDeclarationHandler {
ExtensionPointName<GotoDeclarationHandler> EP_NAME = ExtensionPointName.create("com.intellij.gotoDeclarationHandler");
@Nullable
PsiElement getGotoDeclarationTarget(PsiElement sourceElement);
PsiElement[] getGotoDeclarationTargets(PsiElement sourceElement);
}
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.navigation.actions;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public abstract class GotoDeclarationHandlerBase implements GotoDeclarationHandler {
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(PsiElement sourceElement) {
final PsiElement target = getGotoDeclarationTarget(sourceElement);
return target != null ? new PsiElement[] {target} : null;
}
@Nullable
public abstract PsiElement getGotoDeclarationTarget(PsiElement sourceElement);
}
@@ -74,7 +74,7 @@ public class FindUsagesAction extends AnAction {
}
else {
int offset = editor.getCaretModel().getOffset();
boolean chosen = GotoDeclarationAction.chooseAmbiguousTarget(editor, offset, processor, FindBundle.message("find.usages.ambiguous.title"));
boolean chosen = GotoDeclarationAction.chooseAmbiguousTarget(editor, offset, processor, FindBundle.message("find.usages.ambiguous.title"), null);
if (!chosen) {
HintManager.getInstance().showErrorHint(editor, FindBundle.message("find.no.usages.at.cursor.error"));
}
@@ -16,10 +16,10 @@
package com.intellij.codeInspection.i18n.folding;
import com.intellij.codeInsight.folding.impl.EditorFoldingInfo;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandlerBase;
import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.CompositeFoldingBuilder;
import com.intellij.lang.folding.FoldingBuilder;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.fileEditor.FileEditorManager;
@@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable;
/**
* @author Konstantin Bulenkov
*/
public class I18nMessageGotoDeclarationHandler implements GotoDeclarationHandler {
public class I18nMessageGotoDeclarationHandler extends GotoDeclarationHandlerBase {
private static final Key<FoldingBuilder> KEY = CompositeFoldingBuilder.FOLDING_BUILDER;
public PsiElement getGotoDeclarationTarget(PsiElement element) {