From f7ecb8a5f705e3c0026f7a3fdb5cb148ee955eee Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Tue, 19 May 2015 13:22:06 +0300 Subject: [PATCH] IDEA-138063 Support HTML links with fragment references in quick doc --- .../javadoc/JavaDocInfoGenerator.java | 14 ++++++- .../javadocIG/htmlLinkWithRef.html | 5 +++ .../javadocIG/htmlLinkWithRef.java | 6 +++ .../javadoc/JavaDocInfoGeneratorTest.java | 4 ++ .../DocumentationManagerProtocol.java | 6 +++ .../documentation/DocumentationComponent.java | 19 ++++++--- .../documentation/DocumentationManager.java | 42 +++++++++++++++++-- 7 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.java diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 543087fac6d0..8eaf6f3593ea 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -252,10 +252,17 @@ public class JavaDocInfoGenerator { protected String convertReference(@NonNls String href) { final String originalReference = href; + String fragment = null; int hashPosition = href.indexOf('#'); if (hashPosition >= 0) { + fragment = href.substring(hashPosition + 1); href = href.substring(0, hashPosition); } + if (href.isEmpty()) { + PsiElement containingClass = myElement instanceof PsiMember ? ((PsiMember)myElement).getContainingClass() : null; + PsiElement rootElement = containingClass == null ? myElement : containingClass; + return createLinkWithRef(rootElement, fragment); + } if (!href.toLowerCase().endsWith(".htm") && !href.toLowerCase().endsWith(".html")) { return originalReference; } @@ -286,7 +293,12 @@ public class JavaDocInfoGenerator { PsiClass target = JavaPsiFacade.getInstance(myProject).findClass(qualifiedName, myElement.getResolveScope()); if (target == null) return originalReference; - return DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL + JavaDocUtil.getReferenceText(myProject, target); + return createLinkWithRef(target, fragment); + } + + private String createLinkWithRef(PsiElement psiElement, String ref) { + return DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL + JavaDocUtil.getReferenceText(myProject, psiElement) + + (ref == null ? "" : DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL_REF_SEPARATOR + ref); } public boolean generateDocInfoCore (final StringBuilder buffer, final boolean generatePrologueAndEpilogue) { diff --git a/java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.html b/java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.html new file mode 100644 index 000000000000..8a3e3d767677 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.html @@ -0,0 +1,5 @@ +
class Test
+extends Object
+ section one + text + link up \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.java b/java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.java new file mode 100644 index 000000000000..65b79385acfc --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/htmlLinkWithRef.java @@ -0,0 +1,6 @@ +/** + * section one + * text + * link up + */ +class Test {} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java index 82223836c223..8f62e29635e2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java @@ -254,6 +254,10 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase { assertEquals(StringUtil.convertLineSeparators(new String(htmlFile.getVirtualFile().contentsToByteArray()).trim()), replaceEnvironmentDependentContent(doc)); } + + public void testHtmlLinkWithRef() throws Exception { + verifyJavaDoc(getTestClass()); + } @Override protected String getTestDataPath() { diff --git a/platform/core-api/src/com/intellij/codeInsight/documentation/DocumentationManagerProtocol.java b/platform/core-api/src/com/intellij/codeInsight/documentation/DocumentationManagerProtocol.java index 69ee80332cbd..23f61701af0a 100644 --- a/platform/core-api/src/com/intellij/codeInsight/documentation/DocumentationManagerProtocol.java +++ b/platform/core-api/src/com/intellij/codeInsight/documentation/DocumentationManagerProtocol.java @@ -27,6 +27,12 @@ public interface DocumentationManagerProtocol { * @see DocumentationManagerUtil */ @NonNls String PSI_ELEMENT_PROTOCOL = "psi_element://"; + + /** + * Separator between PSI element link and a reference to specific text fragment, which should be scrolled to on navigation. Can be used + * with {@link #PSI_ELEMENT_PROTOCOL} links, full link should look like {@code psi_element://link###ref}. + */ + @NonNls String PSI_ELEMENT_PROTOCOL_REF_SEPARATOR = "###"; /** * @deprecated Link with such prefix will open documentation for current element in browser, regardless of full link content. Http links diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index 36e8f4005070..e326651ab797 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -524,6 +524,10 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr } public void setData(PsiElement _element, String text, final boolean clearHistory, String effectiveExternalUrl) { + setData(_element, text, clearHistory, effectiveExternalUrl, null); + } + + public void setData(PsiElement _element, String text, final boolean clearHistory, String effectiveExternalUrl, String ref) { myEffectiveExternalUrl = effectiveExternalUrl; if (myElement != null) { myBackStack.push(saveContext()); @@ -540,16 +544,16 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr myIsEmpty = false; updateControlState(); - setDataInternal(element, text, new Rectangle(0, 0)); + setDataInternal(element, text, new Rectangle(0, 0), ref); if (clearHistory) clearHistory(); } - private void setDataInternal(SmartPsiElementPointer element, String text, final Rectangle viewRect) { - setDataInternal(element, text, viewRect, false); + private void setDataInternal(SmartPsiElementPointer element, String text, final Rectangle viewRect, String ref) { + setDataInternal(element, text, viewRect, ref, false); } - private void setDataInternal(SmartPsiElementPointer element, String text, final Rectangle viewRect, boolean skip) { + private void setDataInternal(SmartPsiElementPointer element, String text, final Rectangle viewRect, final String ref, boolean skip) { setElement(element); myEditorPane.setText(text); @@ -568,7 +572,10 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - myEditorPane.scrollRectToVisible(viewRect); + myEditorPane.scrollRectToVisible(viewRect); // if ref is defined but is not found in document, this provides a default location + if (ref != null) { + myEditorPane.scrollToReference(ref); + } } }); } @@ -618,7 +625,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr } private void restoreContext(Context context) { - setDataInternal(context.element, context.text, context.viewRect); + setDataInternal(context.element, context.text, context.viewRect, null); if (myNavigateCallback != null) { final PsiElement element = context.element.getElement(); if (element != null) { 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 89ca7ac45fcd..104d915c7964 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -641,6 +641,10 @@ public class DocumentationManager extends DockablePopupManager= 0) { + ref = refText.substring(separatorPos + DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL_REF_SEPARATOR.length()); + refText = refText.substring(0, separatorPos); + } DocumentationProvider provider = getProviderFromElement(psiElement); PsiElement targetElement = provider.getDocumentationElementForLink(manager, refText, psiElement); if (targetElement == null) { @@ -877,7 +887,7 @@ public class DocumentationManager extends DockablePopupManager