mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-189709: implement/override tooltip for gutter icons
This commit is contained in:
+12
@@ -36,14 +36,26 @@ public class GutterIconTooltipHelper {
|
||||
private GutterIconTooltipHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated by com.intellij.codeInsight.daemon.impl.GutterTooltipHelper
|
||||
*/
|
||||
@Deprecated
|
||||
public static String composeText(@NotNull PsiElement[] elements, @NotNull String start, @NotNull String pattern) {
|
||||
return composeText(Arrays.asList(elements), start, pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated by com.intellij.codeInsight.daemon.impl.GutterTooltipHelper
|
||||
*/
|
||||
@Deprecated
|
||||
public static String composeText(@NotNull Iterable<? extends PsiElement> elements, @NotNull String start, @NotNull String pattern) {
|
||||
return composeText(elements, start, pattern, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated by com.intellij.codeInsight.daemon.impl.GutterTooltipHelper
|
||||
*/
|
||||
@Deprecated
|
||||
static String composeText(@NotNull Iterable<? extends PsiElement> elements,
|
||||
@NotNull String start,
|
||||
@NotNull String pattern,
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2000-2018 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.daemon.impl;
|
||||
|
||||
import com.intellij.ide.actions.QualifiedNameProvider;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.ui.JBColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.intellij.openapi.keymap.KeymapUtil.getPreferredShortcutText;
|
||||
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
|
||||
import static com.intellij.psi.util.PsiTreeUtil.getStubOrPsiParentOfType;
|
||||
import static com.intellij.psi.util.PsiUtilCore.getVirtualFile;
|
||||
import static com.intellij.ui.ColorUtil.toHex;
|
||||
|
||||
public final class GutterTooltipHelper {
|
||||
private static final JBColor CONTEXT_HELP_FOREGROUND
|
||||
= JBColor.namedColor("GutterTooltip.ContextHelp.foreground", new JBColor(0x787878, 0x878787));
|
||||
|
||||
private GutterTooltipHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param elements a collection of elements to create a formatted tooltip text
|
||||
* @param prefix a text to insert before all elements
|
||||
* @param skipFirstMember {@code true} to skip a method (or field) name in the link to element
|
||||
* @param actionId an action identifier to generate context help or {@code null} if not applicable
|
||||
*/
|
||||
@NotNull
|
||||
public static <E extends PsiElement> String getTooltipText(@NotNull Collection<E> elements,
|
||||
@NotNull String prefix,
|
||||
boolean skipFirstMember,
|
||||
@Nullable String actionId) {
|
||||
String elementPrefix = 1 < elements.size() ? "<br> " : " ";
|
||||
return getTooltipText(prefix, elements, e -> elementPrefix, e -> skipFirstMember, actionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param elements a collection of elements to create a formatted tooltip text
|
||||
* @param function a function that returns a text to insert before the current element
|
||||
* @param predicate a function that returns {@code true} to skip a method (or field) name for the current element
|
||||
* @param actionId an action identifier to generate context help or {@code null} if not applicable
|
||||
*/
|
||||
@NotNull
|
||||
public static <E extends PsiElement> String getTooltipText(@NotNull Collection<E> elements,
|
||||
@NotNull Function<E, String> function,
|
||||
@NotNull Predicate<E> predicate,
|
||||
@Nullable String actionId) {
|
||||
return getTooltipText(null, elements, function, predicate, actionId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <E extends PsiElement> String getTooltipText(@Nullable String prefix,
|
||||
@NotNull Collection<E> elements,
|
||||
@NotNull Function<E, String> function,
|
||||
@NotNull Predicate<E> predicate,
|
||||
@Nullable String actionId) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (prefix != null) sb.append(prefix);
|
||||
for (E element : elements) {
|
||||
String elementPrefix = function.apply(element);
|
||||
if (elementPrefix != null) sb.append(elementPrefix);
|
||||
appendElement(sb, element, predicate.test(element));
|
||||
}
|
||||
appendContextHelp(sb, actionId);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void appendElement(@NotNull StringBuilder sb, @NotNull PsiElement element, boolean skip) {
|
||||
boolean useSingleLink = Registry.is("gutter.tooltip.single.link");
|
||||
String packageName = null;
|
||||
boolean addedSingleLink = useSingleLink && appendLink(sb, element);
|
||||
PsiElement skipped = null;
|
||||
if (skip && (element instanceof PsiMethod || element instanceof PsiField)) {
|
||||
skipped = element; // use skipped member as first separate link
|
||||
element = getContainingElement(element);
|
||||
}
|
||||
while (element != null) {
|
||||
String name = getPresentableName(element);
|
||||
if (name != null) {
|
||||
boolean addedLink = !useSingleLink && appendLink(sb, skipped != null ? skipped : element);
|
||||
// Swing uses simple HTML processing and paints a link incorrectly if it contains different fonts.
|
||||
// This is the reason why I use monospaced font not only for element name, but for a whole link.
|
||||
// By the same reason I have to comment out support for deprecated elements.
|
||||
//
|
||||
// boolean deprecated = RefJavaUtil.isDeprecated(element);
|
||||
// if (deprecated) sb.append("<strike>");
|
||||
// sb.append("<code>");
|
||||
sb.append(name);
|
||||
// sb.append("</code>");
|
||||
// if (deprecated) sb.append("</strike>");
|
||||
if (addedLink) sb.append("</code></a>");
|
||||
}
|
||||
if (element instanceof PsiFile) break;
|
||||
PsiElement parent = getContainingElement(element);
|
||||
if (parent == null || parent instanceof PsiFile) {
|
||||
if (element instanceof PsiClass && !(element instanceof PsiAnonymousClass)) {
|
||||
String qualifiedName = ((PsiClass)element).getQualifiedName();
|
||||
if (qualifiedName != null) packageName = StringUtil.getPackageName(qualifiedName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parent != null) sb.append(" in ");
|
||||
element = parent;
|
||||
skipped = null;
|
||||
}
|
||||
if (addedSingleLink) sb.append("</code></a>");
|
||||
appendPackageName(sb, packageName);
|
||||
}
|
||||
|
||||
private static void appendPackageName(@NotNull StringBuilder sb, @Nullable String name) {
|
||||
if (StringUtil.isEmpty(name)) return; // no package name
|
||||
sb.append(" <font color=").append(toHex(CONTEXT_HELP_FOREGROUND));
|
||||
sb.append("><code>(").append(name).append(")</code></font>");
|
||||
}
|
||||
|
||||
private static void appendContextHelp(@NotNull StringBuilder sb, @Nullable String actionId) {
|
||||
if (actionId == null) return; // action id is not set
|
||||
AnAction action = ActionManager.getInstance().getAction(actionId);
|
||||
if (action == null) return; // action is not exist
|
||||
String text = getPreferredShortcutText(action.getShortcutSet().getShortcuts());
|
||||
if (StringUtil.isEmpty(text)) return; // action have no shortcuts
|
||||
sb.append("<br><div style='margin-top: 5px'><font size='2' color='");
|
||||
sb.append(toHex(CONTEXT_HELP_FOREGROUND));
|
||||
sb.append("'>Press ").append(text).append(" to navigate</font></div>");
|
||||
}
|
||||
|
||||
private static boolean appendLink(@NotNull StringBuilder sb, @NotNull PsiElement element) {
|
||||
try {
|
||||
String name = getQualifiedName(element);
|
||||
if (!StringUtil.isEmpty(name)) {
|
||||
sb.append("<a href=\"#element/").append(name).append("\"><code>");
|
||||
return true;
|
||||
}
|
||||
VirtualFile file = getVirtualFile(element);
|
||||
if (file == null) return false;
|
||||
|
||||
int offset = element.getTextOffset();
|
||||
sb.append("<a href=\"#navigation/");
|
||||
sb.append(toSystemIndependentName(file.getPath()));
|
||||
sb.append(":").append(offset).append("\"><code>");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getQualifiedName(@NotNull PsiElement element) {
|
||||
PsiClass psiClass = element instanceof PsiClass ? (PsiClass)element : getStubOrPsiParentOfType(element, PsiClass.class);
|
||||
if (psiClass instanceof PsiAnonymousClass) return null;
|
||||
for (QualifiedNameProvider provider : QualifiedNameProvider.EP_NAME.getExtensionList()) {
|
||||
String name = provider.getQualifiedName(element);
|
||||
if (name != null) return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement getContainingElement(@NotNull PsiElement element) {
|
||||
PsiMember member = getStubOrPsiParentOfType(element, PsiMember.class);
|
||||
return member != null ? member : element.getContainingFile();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getPresentableName(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiEnumConstantInitializer) {
|
||||
PsiEnumConstantInitializer initializer = (PsiEnumConstantInitializer)element;
|
||||
return initializer.getEnumConstant().getName();
|
||||
}
|
||||
if (element instanceof PsiAnonymousClass) {
|
||||
return "Anonymous";
|
||||
}
|
||||
if (element instanceof PsiNamedElement) {
|
||||
PsiNamedElement named = (PsiNamedElement)element;
|
||||
return named.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -10,17 +10,13 @@ import com.intellij.codeInsight.daemon.DaemonBundle;
|
||||
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
|
||||
import com.intellij.codeInsight.navigation.BackgroundUpdaterTask;
|
||||
import com.intellij.ide.util.*;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.actionSystem.Shortcut;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.FindSuperElementsHelper;
|
||||
import com.intellij.psi.presentation.java.ClassPresentationUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiElementProcessor;
|
||||
import com.intellij.psi.search.PsiElementProcessorAdapter;
|
||||
@@ -35,14 +31,13 @@ import com.intellij.util.Function;
|
||||
import com.intellij.util.NullableFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class MarkerType {
|
||||
private final GutterIconNavigationHandler<PsiElement> handler;
|
||||
@@ -115,44 +110,44 @@ public class MarkerType {
|
||||
PsiMethod[] superMethods = composeSuperMethods(method, acceptSelf);
|
||||
if (superMethods.length == 0) return null;
|
||||
|
||||
PsiMethod superMethod = superMethods[0];
|
||||
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
boolean isSuperAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
|
||||
final boolean sameSignature = superMethod.getSignature(PsiSubstitutor.EMPTY).equals(method.getSignature(PsiSubstitutor.EMPTY));
|
||||
@NonNls final String key;
|
||||
if (isSuperAbstract && !isAbstract){
|
||||
key = sameSignature ? "method.implements" : "method.implements.in";
|
||||
}
|
||||
else{
|
||||
key = sameSignature ? "method.overrides" : "method.overrides.in";
|
||||
}
|
||||
return composeText(superMethods, "", DaemonBundle.message(key), IdeActions.ACTION_GOTO_SUPER);
|
||||
AtomicBoolean first = new AtomicBoolean(true);
|
||||
return GutterTooltipHelper.getTooltipText(
|
||||
Arrays.asList(superMethods),
|
||||
superMethod -> getTooltipPrefix(method, superMethod, first.getAndSet(false)),
|
||||
superMethod -> isSameSignature(method, superMethod),
|
||||
IdeActions.ACTION_GOTO_SUPER);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String calculateOverridingSiblingMethodTooltip(@NotNull PsiMethod method) {
|
||||
FindSuperElementsHelper.SiblingInfo pair = FindSuperElementsHelper.getSiblingInfoInheritedViaSubClass(method);
|
||||
if (pair == null) return null;
|
||||
PsiMethod superMethod = pair.superMethod;
|
||||
PsiClass subClass = pair.subClass;
|
||||
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
boolean isSuperAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
|
||||
String postfix = MessageFormat.format(" via sub-class <a href=\"#javaClass/{0}\">{0}</a>", ClassPresentationUtil.getNameForClass(subClass, true));
|
||||
@NonNls String pattern = DaemonBundle.message(isSuperAbstract && !isAbstract ?
|
||||
"method.implements" :
|
||||
"method.overrides") + postfix;
|
||||
return composeText(new PsiElement[]{superMethod}, "", pattern, IdeActions.ACTION_GOTO_SUPER);
|
||||
return GutterTooltipHelper.getTooltipText(
|
||||
Arrays.asList(pair.superMethod, pair.subClass),
|
||||
element -> element instanceof PsiMethod ? getTooltipPrefix(method, (PsiMethod)element, true) : " via sub-class ",
|
||||
element -> element instanceof PsiMethod && isSameSignature(method, (PsiMethod)element),
|
||||
IdeActions.ACTION_GOTO_SUPER);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String composeText(@NotNull PsiElement[] methods, @NotNull String start, @NotNull String pattern, @NotNull String actionId) {
|
||||
Shortcut[] shortcuts = ActionManager.getInstance().getAction(actionId).getShortcutSet().getShortcuts();
|
||||
Shortcut shortcut = ArrayUtil.getFirstElement(shortcuts);
|
||||
String postfix = "<br><div style='margin-top: 5px'><font size='2'>Click";
|
||||
if (shortcut != null) postfix += " or press " + KeymapUtil.getShortcutText(shortcut);
|
||||
postfix += " to navigate</font></div>";
|
||||
return GutterIconTooltipHelper.composeText(Arrays.asList(methods), start, pattern, postfix);
|
||||
private static String getTooltipPrefix(@NotNull PsiMethod method, @NotNull PsiMethod superMethod, boolean first) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!first) sb.append("<br>");
|
||||
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
boolean isSuperAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
sb.append(isSuperAbstract && !isAbstract ? "Implements method " : "Overrides method ");
|
||||
if (isSameSignature(method, superMethod)) sb.append("in ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean isSameSignature(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
|
||||
return method.getSignature(PsiSubstitutor.EMPTY).equals(superMethod.getSignature(PsiSubstitutor.EMPTY));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getImplementationTooltip(@NotNull String prefix, @NotNull PsiElement... elements) {
|
||||
return GutterTooltipHelper.getTooltipText(Arrays.asList(elements), prefix, true, IdeActions.ACTION_GOTO_IMPLEMENTATION);
|
||||
}
|
||||
|
||||
private static void navigateToOverridingMethod(MouseEvent e, @NotNull PsiMethod method, boolean acceptSelf) {
|
||||
@@ -164,6 +159,7 @@ public class MarkerType {
|
||||
DaemonBundle.message("navigation.findUsages.title.super.method", method.getName()),
|
||||
new MethodCellRenderer(showMethodNames));
|
||||
}
|
||||
|
||||
private static void navigateToSiblingOverridingMethod(MouseEvent e, @NotNull PsiMethod method) {
|
||||
PsiMethod superMethod = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method);
|
||||
if (superMethod == null) return;
|
||||
@@ -203,14 +199,13 @@ public class MarkerType {
|
||||
PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiMethod)) return;
|
||||
navigateToOverriddenMethod(e, (PsiMethod)parent);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
private static String getOverriddenMethodTooltip(@NotNull PsiMethod method) {
|
||||
final PsiClass aClass = method.getContainingClass();
|
||||
if (aClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) {
|
||||
return DaemonBundle.message("method.is.implemented.too.many");
|
||||
return getImplementationTooltip("Has implementations");
|
||||
}
|
||||
|
||||
PsiElementProcessor.CollectElementsWithLimit<PsiMethod> processor = new PsiElementProcessor.CollectElementsWithLimit<>(5);
|
||||
@@ -220,13 +215,13 @@ public class MarkerType {
|
||||
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
|
||||
if (processor.isOverflow()){
|
||||
return DaemonBundle.message(isAbstract ? "method.is.implemented.too.many" : "method.is.overridden.too.many");
|
||||
return getImplementationTooltip(isAbstract ? "Has implementations" : "Is overridden in subclasses");
|
||||
}
|
||||
|
||||
PsiMethod[] overridings = processor.toArray(PsiMethod.EMPTY_ARRAY);
|
||||
if (overridings.length == 0) {
|
||||
if (aClass != null && isAbstract && FunctionalExpressionSearch.search(aClass).findFirst() != null) {
|
||||
return "Has functional implementations";
|
||||
return getImplementationTooltip("Has functional implementations");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -234,9 +229,7 @@ public class MarkerType {
|
||||
Comparator<PsiMethod> comparator = new MethodCellRenderer(false).getComparator();
|
||||
Arrays.sort(overridings, comparator);
|
||||
|
||||
String start = DaemonBundle.message(isAbstract ? "method.is.implemented.header" : "method.is.overriden.header");
|
||||
@NonNls String pattern = " <a href=\"#javaClass/{1}\">{1}</a>";
|
||||
return composeText(overridings, start, pattern, IdeActions.ACTION_GOTO_IMPLEMENTATION);
|
||||
return getImplementationTooltip(isAbstract ? "Is implemented in" : "Is overridden in", overridings);
|
||||
}
|
||||
|
||||
private static void navigateToOverriddenMethod(MouseEvent e, @NotNull final PsiMethod method) {
|
||||
@@ -298,7 +291,7 @@ public class MarkerType {
|
||||
ClassInheritorsSearch.search(aClass).forEach(new PsiElementProcessorAdapter<>(processor));
|
||||
|
||||
if (processor.isOverflow()) {
|
||||
return DaemonBundle.message(aClass.isInterface() ? "interface.is.implemented.too.many" : "class.is.subclassed.too.many");
|
||||
return getImplementationTooltip(aClass.isInterface() ? "Has implementations" : "Has subclasses");
|
||||
}
|
||||
|
||||
PsiClass[] subclasses = processor.toArray(PsiClass.EMPTY_ARRAY);
|
||||
@@ -307,7 +300,7 @@ public class MarkerType {
|
||||
new PsiElementProcessor.CollectElementsWithLimit<>(2, new THashSet<>());
|
||||
FunctionalExpressionSearch.search(aClass).forEach(new PsiElementProcessorAdapter<>(functionalImplementations));
|
||||
if (!functionalImplementations.getCollection().isEmpty()) {
|
||||
return "Has functional implementations";
|
||||
return getImplementationTooltip("Has functional implementations");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -315,9 +308,7 @@ public class MarkerType {
|
||||
Comparator<PsiClass> comparator = new PsiClassListCellRenderer().getComparator();
|
||||
Arrays.sort(subclasses, comparator);
|
||||
|
||||
String start = DaemonBundle.message(aClass.isInterface() ? "interface.is.implemented.by.header" : "class.is.subclassed.by.header");
|
||||
@NonNls String pattern = " <a href=\"#javaClass/{0}\">{0}</a>";
|
||||
return composeText(subclasses, start, pattern, IdeActions.ACTION_GOTO_IMPLEMENTATION);
|
||||
return getImplementationTooltip(aClass.isInterface() ? "Is implemented by" : "Is subclassed by", subclasses);
|
||||
}
|
||||
|
||||
// Used in Kotlin, please don't make private
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2018 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.hint;
|
||||
|
||||
import com.intellij.codeInsight.highlighting.TooltipLinkHandler;
|
||||
import com.intellij.ide.actions.QualifiedNameProvider;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Handles tooltip links in format {@code #element/qualified.name}.
|
||||
* On a click opens specified element in an editor and positions caret to the corresponding offset.
|
||||
*/
|
||||
public final class ElementLinkHandler extends TooltipLinkHandler {
|
||||
@Override
|
||||
public boolean handleLink(@NotNull String name, @NotNull Editor editor) {
|
||||
Project project = editor.getProject();
|
||||
if (project != null) {
|
||||
PsiElement element = qualifiedNameToElement(name, project);
|
||||
if (element instanceof Navigatable) {
|
||||
Navigatable navigatable = (Navigatable)element;
|
||||
if (navigatable.canNavigate()) {
|
||||
navigatable.navigate(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement qualifiedNameToElement(@NotNull String name, @NotNull Project project) {
|
||||
for (QualifiedNameProvider provider : QualifiedNameProvider.EP_NAME.getExtensionList()) {
|
||||
PsiElement element = provider.qualifiedNameToElement(name, project);
|
||||
if (element != null) return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -700,6 +700,7 @@
|
||||
|
||||
<codeInsight.linkHandler prefix="#inspection/" handlerClass="com.intellij.codeInsight.hint.InspectionDescriptionLinkHandler"/>
|
||||
<codeInsight.linkHandler prefix="#navigation/" handlerClass="com.intellij.codeInsight.hint.NavigationLinkHandler"/>
|
||||
<codeInsight.linkHandler prefix="#element/" handlerClass="com.intellij.codeInsight.hint.ElementLinkHandler"/>
|
||||
<codeInsight.lineMarkerProvider language="" implementationClass="com.intellij.ui.ColorLineMarkerProvider"/>
|
||||
|
||||
<codeFoldingOptionsProvider instance="com.intellij.application.options.editor.BaseCodeFoldingOptionsProvider" order="first"/>
|
||||
|
||||
@@ -277,6 +277,9 @@ ide.balloon.shadow.size=15
|
||||
ide.tooltip.show.with.actions=false
|
||||
ide.tooltip.show.with.actions.description=Show new error tooltips with fixes
|
||||
|
||||
gutter.tooltip.single.link=true
|
||||
gutter.tooltip.single.link.description=Shows a single link for all classes in a chain
|
||||
|
||||
ide.helptooltip.enabled=false
|
||||
ide.helptooltip.enabled.restartRequired=true
|
||||
ide.helptooltip.enabled.description=Enables new style help context tooltips
|
||||
|
||||
Reference in New Issue
Block a user