diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java index d28cda96e8ed..b2da6749b317 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -78,7 +78,7 @@ public class JavaDocExternalFilter extends AbstractExternalFilter { return (JavaPsiFacade.getInstance(myProject).findClass(classRef, GlobalSearchScope.allScope(myProject)) != null) - ? PSI_ELEMENT_PROTOCOL + elementRef + ? DocumentationManager.PSI_ELEMENT_PROTOCOL + elementRef : DOC_ELEMENT_PROTOCOL + doAnnihilate(nakedRoot + href); } }, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java index ae7e2513784b..504ea5cdf8e6 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -25,10 +25,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.util.io.UrlConnectionUtil; import com.intellij.util.net.HttpConfigurable; @@ -38,7 +35,6 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URL; import java.net.URLConnection; -import java.util.Locale; import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -58,8 +54,6 @@ public abstract class AbstractExternalFilter { protected static @NonNls final Pattern ourHTMLFilesuffix = Pattern.compile("/([^/]*[.][hH][tT][mM][lL]?)$"); private static @NonNls final Pattern ourAnnihilator = Pattern.compile("/[^/^.]*/[.][.]/"); private static @NonNls final Pattern ourIMGselector = Pattern.compile("]*)\"", Pattern.CASE_INSENSITIVE|Pattern.DOTALL); - protected static @NonNls final String DOC_ELEMENT_PROTOCOL = "doc_element://"; - protected static @NonNls final String PSI_ELEMENT_PROTOCOL = "psi_element://"; private static @NonNls final String JAR_PROTOCOL = "jar:"; @NonNls private static final String HR = "
"; @NonNls private static final String P = "

"; @@ -86,7 +80,7 @@ public abstract class AbstractExternalFilter { public String refFilter(final String root, String read) { String toMatch = StringUtilRt.toUpperCase(read); - StringBuffer ready = new StringBuffer(); + StringBuilder ready = new StringBuilder(); int prev = 0; Matcher matcher = mySelector.matcher(toMatch); @@ -115,7 +109,7 @@ public abstract class AbstractExternalFilter { protected final RefConvertor myIMGConvertor = new RefConvertor(ourIMGselector) { protected String convertReference(String root, String href) { if (StringUtil.startsWithChar(href, '#')) { - return DOC_ELEMENT_PROTOCOL + root + href; + return DocumentationManager.DOC_ELEMENT_PROTOCOL + root + href; } if (Comparing.strEqual(VirtualFileManager.extractProtocol(root), LocalFileSystem.PROTOCOL)) { @@ -160,7 +154,7 @@ public abstract class AbstractExternalFilter { return null; } - return new StringReader(VfsUtil.loadText(file)); + return new StringReader(VfsUtilCore.loadText(file)); } URL url = BrowserUtil.getURL(surl); @@ -259,7 +253,7 @@ public abstract class AbstractExternalFilter { } } } - while (read != null && StringUtilRt.toUpperCase(read).indexOf(startSection) == -1); + while (read != null && !StringUtilRt.toUpperCase(read).contains(startSection)); if (input instanceof MyReader && contentEncoding != null) { if (contentEncoding != null && !contentEncoding.equals("UTF-8") && !contentEncoding.equals(((MyReader)input).getEncoding())) { //restart page parsing with correct encoding diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index e9f38615c1e7..2d8469c26136 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -93,7 +93,7 @@ public class DocumentationManager extends DockablePopupManager ORIGINAL_ELEMENT_KEY = Key.create("Original element"); @NonNls public static final String PSI_ELEMENT_PROTOCOL = "psi_element://"; - @NonNls private static final String DOC_ELEMENT_PROTOCOL = "doc_element://"; + @NonNls public static final String DOC_ELEMENT_PROTOCOL = "doc_element://"; private final ActionManagerEx myActionManagerEx; private boolean myCloseOnSneeze; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index 9e32e239595b..e3022dceeb20 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java @@ -24,6 +24,7 @@ import com.intellij.codeInsight.hint.HintManagerImpl; import com.intellij.codeInsight.hint.HintUtil; import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction; import com.intellij.codeInsight.navigation.actions.GotoTypeDeclarationAction; +import com.intellij.ide.IdeTooltipManager; import com.intellij.ide.util.EditSourceUtil; import com.intellij.lang.documentation.DocumentationProvider; import com.intellij.navigation.ItemPresentation; @@ -73,6 +74,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; @@ -250,19 +253,20 @@ public class CtrlMouseHandler extends AbstractProjectComponent { @Nullable @TestOnly public static String getInfo(PsiElement element, PsiElement atPointer) { - return generateInfo(element, atPointer); + return generateInfo(element, atPointer).text; } - @Nullable - private static String generateInfo(PsiElement element, PsiElement atPointer) { + @NotNull + private static DocInfo generateInfo(PsiElement element, PsiElement atPointer) { final DocumentationProvider documentationProvider = DocumentationManager.getProviderFromElement(element, atPointer); String result = doGenerateInfo(element, atPointer, documentationProvider); if (result != null) { String fullText = documentationProvider.generateDoc(element, atPointer); String qName = element instanceof PsiQualifiedNamedElement ? ((PsiQualifiedNamedElement)element).getQualifiedName() : null; - return DocPreviewUtil.buildPreview(result, qName, fullText, ourQuickDocRowsNumber, ourQuickDocSymbolsInRowNumber); + String text = DocPreviewUtil.buildPreview(result, qName, fullText, ourQuickDocRowsNumber, ourQuickDocSymbolsInRowNumber); + return new DocInfo(text, documentationProvider, atPointer); } - return result; + return DocInfo.EMPTY; } @Nullable @@ -334,8 +338,8 @@ public class CtrlMouseHandler extends AbstractProjectComponent { return myRanges; } - @Nullable - public abstract String getInfo(); + @NotNull + public abstract DocInfo getInfo(); public abstract boolean isValid(Document document); @@ -366,8 +370,8 @@ public class CtrlMouseHandler extends AbstractProjectComponent { myTargetElement = targetElement; } - @Nullable - public String getInfo() { + @NotNull + public DocInfo getInfo() { AccessToken token = ReadAction.start(); try { return generateInfo(myTargetElement, myElementAtPointer); @@ -395,9 +399,10 @@ public class CtrlMouseHandler extends AbstractProjectComponent { public InfoMultiple(@NotNull final PsiElement elementAtPointer) { super(elementAtPointer); } - - public String getInfo() { - return CodeInsightBundle.message("multiple.implementations.tooltip"); + + @NotNull + public DocInfo getInfo() { + return new DocInfo(CodeInsightBundle.message("multiple.implementations.tooltip"), null, null); } public boolean isValid(Document document) { @@ -594,21 +599,26 @@ public class CtrlMouseHandler extends AbstractProjectComponent { } } - if (info.isValid(myEditor.getDocument())) { - myHighlighter = installHighlighterSet(info, myEditor); - - String text = info.getInfo(); - - if (text == null) return; - - JComponent label = HintUtil.createInformationLabel(text); - final LightweightHint hint = new LightweightHint(label); - final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); - Point p = HintManagerImpl.getHintPosition(hint, myEditor, myPosition, HintManager.ABOVE); - hintManager.showEditorHint(hint, myEditor, p, - HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, - 0, false, HintManagerImpl.createHintHint(myEditor, p, hint, HintManager.ABOVE).setContentActive(false)); + if (!info.isValid(myEditor.getDocument())) { + return; } + + myHighlighter = installHighlighterSet(info, myEditor); + + DocInfo docInfo = info.getInfo(); + + if (docInfo.text == null) return; + + HyperlinkListener listener = (docInfo.docProvider == null || docInfo.context == null) + ? null + : new QuickDocHyperlinkListener(myProject, docInfo.docProvider, docInfo.context); + JComponent label = HintUtil.createInformationLabel(docInfo.text, listener); + final LightweightHint hint = new LightweightHint(label); + final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); + Point p = HintManagerImpl.getHintPosition(hint, myEditor, myPosition, HintManager.ABOVE); + hintManager.showEditorHint(hint, myEditor, p, + HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, + 0, false, HintManagerImpl.createHintHint(myEditor, p, hint, HintManager.ABOVE).setContentActive(false)); } } @@ -662,4 +672,52 @@ public class CtrlMouseHandler extends AbstractProjectComponent { return myStoredInfo; } } + + private static class DocInfo { + + public static final DocInfo EMPTY = new DocInfo(null, null, null); + + @Nullable public final String text; + @Nullable public final DocumentationProvider docProvider; + @Nullable public final PsiElement context; + + DocInfo(@Nullable String text, @Nullable DocumentationProvider provider, @Nullable PsiElement context) { + this.text = text; + docProvider = provider; + this.context = context; + } + } + + private static class QuickDocHyperlinkListener implements HyperlinkListener { + + @NotNull private final Project myProject; + @NotNull private final DocumentationProvider myProvider; + @NotNull private final PsiElement myContext; + + QuickDocHyperlinkListener(@NotNull Project project, @NotNull DocumentationProvider provider, @NotNull PsiElement context) { + myProject = project; + myProvider = provider; + myContext = context; + } + + @Override + public void hyperlinkUpdate(@NotNull HyperlinkEvent e) { + if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) { + return; + } + + String description = e.getDescription(); + if (StringUtil.isEmpty(description) || !description.startsWith(DocumentationManager.PSI_ELEMENT_PROTOCOL)) { + return; + } + + String elementName = e.getDescription().substring(DocumentationManager.PSI_ELEMENT_PROTOCOL.length()); + + final PsiElement targetElement = myProvider.getDocumentationElementForLink(PsiManager.getInstance(myProject), elementName, myContext); + if (targetElement != null) { + ApplicationManager.getApplication().getComponent(IdeTooltipManager.class).hideCurrentNow(false); + DocumentationManager.getInstance(myProject).showJavaDocInfo(targetElement, myContext, true, null); + } + } + } } diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java index e123688ec328..d048ba4c5f11 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -23,11 +23,15 @@ import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleColoredText; import com.intellij.util.ui.UIUtil; import org.intellij.lang.annotations.JdkConstants; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; import java.awt.*; public class HintUtil { @@ -44,7 +48,11 @@ public class HintUtil { private HintUtil() { } - public static JComponent createInformationLabel(String text) { + public static JComponent createInformationLabel(@NotNull String text) { + return createInformationLabel(text, null); + } + + public static JComponent createInformationLabel(@NotNull String text, @Nullable HyperlinkListener listener) { HintHint hintHint = new HintHint().setTextBg(INFORMATION_COLOR).setTextFg(Color.black).setFont(getBoldFont()).setAwtTooltip(true); HintLabel label = new HintLabel(); @@ -59,6 +67,10 @@ public class HintUtil { label.setOpaque(true); } + if (listener != null) { + label.myPane.addHyperlinkListener(listener); + } + return label; } diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java index 26d92f9320dc..9a80da99d7f0 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java @@ -444,7 +444,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener return true; } - private void hideCurrentNow(boolean animationEnabled) { + public void hideCurrentNow(boolean animationEnabled) { if (myCurrentTipUi != null) { myCurrentTipUi.setAnimationEnabled(animationEnabled); myCurrentTipUi.hide(); diff --git a/platform/util-rt/src/com/intellij/openapi/util/Pair.java b/platform/util-rt/src/com/intellij/openapi/util/Pair.java index ce63e207fcfa..2057525a41cb 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/Pair.java +++ b/platform/util-rt/src/com/intellij/openapi/util/Pair.java @@ -22,7 +22,7 @@ public class Pair { public final A first; public final B second; - public static Pair create(A first, B second) { + public static Pair create(@Nullable A first, @Nullable B second) { return new Pair(first, second); } @@ -52,15 +52,17 @@ public class Pair { return EMPTY; } - public Pair(A first, B second) { + public Pair(@Nullable A first, @Nullable B second) { this.first = first; this.second = second; } - + + @Nullable public final A getFirst() { return first; } + @Nullable public final B getSecond() { return second; }