mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-139946 support HTML links in generated Quick Docs
This commit is contained in:
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager;
|
||||
import com.intellij.codeInsight.InferredAnnotationsManager;
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerProtocol;
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.LangBundle;
|
||||
@@ -60,6 +61,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JavaDocInfoGenerator {
|
||||
@@ -67,6 +69,8 @@ public class JavaDocInfoGenerator {
|
||||
|
||||
@NonNls private static final Pattern ourNotDot = Pattern.compile("[^.]");
|
||||
@NonNls private static final Pattern ourWhitespaces = Pattern.compile("[ \\n\\r\\t]+");
|
||||
@NonNls private static final Pattern ourRelativeHtmlLinks = Pattern.compile("<A.*?HREF=\"([^\":]*)\"",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
||||
|
||||
@NonNls private static final String THROWS_KEYWORD = "throws";
|
||||
@NonNls private static final String BR_TAG = "<br>";
|
||||
@@ -211,11 +215,14 @@ public class JavaDocInfoGenerator {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String fixupDoc(@NotNull final StringBuilder buffer) {
|
||||
private String fixupDoc(@NotNull final StringBuilder buffer) {
|
||||
String text = buffer.toString();
|
||||
if (text.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
text = convertHtmlLinks(text);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Generated JavaDoc:");
|
||||
LOG.debug(text);
|
||||
@@ -225,6 +232,63 @@ public class JavaDocInfoGenerator {
|
||||
return StringUtil.replace(text, "/>", ">");
|
||||
}
|
||||
|
||||
private String convertHtmlLinks(String text) {
|
||||
if (myElement == null) return text; // we are resolving links in a context, without context, don't change links
|
||||
StringBuilder result = new StringBuilder();
|
||||
int prev = 0;
|
||||
Matcher matcher = ourRelativeHtmlLinks.matcher(text);
|
||||
while (matcher.find()) {
|
||||
int groupStart = matcher.start(1);
|
||||
int groupEnd = matcher.end(1);
|
||||
result.append(text, prev, groupStart);
|
||||
result.append(convertReference(text.substring(groupStart, groupEnd)));
|
||||
prev = groupEnd;
|
||||
}
|
||||
if (result.length() == 0) return text; // don't copy text over, if there are no matches
|
||||
result.append(text, prev, text.length());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
protected String convertReference(@NonNls String href) {
|
||||
final String originalReference = href;
|
||||
|
||||
int hashPosition = href.indexOf('#');
|
||||
if (hashPosition >= 0) {
|
||||
href = href.substring(0, hashPosition);
|
||||
}
|
||||
if (!href.toLowerCase().endsWith(".htm") && !href.toLowerCase().endsWith(".html")) {
|
||||
return originalReference;
|
||||
}
|
||||
href = href.substring(0, href.lastIndexOf('.'));
|
||||
|
||||
String packageName = null;
|
||||
if (myElement instanceof PsiPackage) {
|
||||
packageName = ((PsiPackage)myElement).getQualifiedName();
|
||||
}
|
||||
else {
|
||||
PsiFile file = myElement.getContainingFile();
|
||||
if (file instanceof PsiClassOwner) {
|
||||
packageName = ((PsiClassOwner)file).getPackageName();
|
||||
}
|
||||
}
|
||||
if (packageName == null) return originalReference;
|
||||
|
||||
while (href.startsWith("../")) {
|
||||
if (packageName.isEmpty()) return originalReference;
|
||||
int dotPos = packageName.lastIndexOf('.');
|
||||
packageName = dotPos < 0 ? "" : packageName.substring(0, dotPos);
|
||||
href = href.substring(3);
|
||||
}
|
||||
|
||||
href = href.replace('/', '.');
|
||||
|
||||
String qualifiedName = packageName.isEmpty() ? href : packageName + "." + href;
|
||||
PsiClass target = JavaPsiFacade.getInstance(myProject).findClass(qualifiedName, myElement.getResolveScope());
|
||||
if (target == null) return originalReference;
|
||||
|
||||
return DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL + JavaDocUtil.getReferenceText(myProject, target);
|
||||
}
|
||||
|
||||
public boolean generateDocInfoCore (final StringBuilder buffer, final boolean generatePrologueAndEpilogue) {
|
||||
if (myElement instanceof PsiClass) {
|
||||
generateClassJavaDoc(buffer, (PsiClass)myElement, generatePrologueAndEpilogue);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><PRE>class <b>htmlLink</b>
|
||||
extends <a href="psi_element://java.lang.Object"><code>Object</code></a></PRE> <a href="psi_element://other">link</a></body></html>
|
||||
@@ -0,0 +1,2 @@
|
||||
/** <a href="other.html">link</a> */
|
||||
class htmlLink {}
|
||||
@@ -0,0 +1 @@
|
||||
class other {}
|
||||
@@ -0,0 +1,2 @@
|
||||
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b>pack</b></small><PRE>class <b>htmlLinkDeep</b>
|
||||
extends <a href="psi_element://java.lang.Object"><code>Object</code></a></PRE> <a href="psi_element://other">link</a></body></html>
|
||||
@@ -0,0 +1,4 @@
|
||||
package pack;
|
||||
|
||||
/** <a href="../other.html">link</a> */
|
||||
class htmlLinkDeep {}
|
||||
+20
@@ -21,6 +21,7 @@ import com.intellij.lang.java.JavaDocumentationProvider;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
|
||||
@@ -234,6 +235,25 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase {
|
||||
PsiParameter parameter = innerClass.getMethods()[0].getParameterList().getParameters()[0];
|
||||
verifyJavaDoc(parameter);
|
||||
}
|
||||
|
||||
public void testHtmlLink() throws Exception {
|
||||
PsiTestUtil.createTestProjectStructure(myProject, myModule,
|
||||
getTestDataPath() + "/codeInsight/javadocIG/htmlLinkProject", myFilesToDelete);
|
||||
verifyJavadocFor("htmlLink");
|
||||
verifyJavadocFor("pack.htmlLinkDeep");
|
||||
}
|
||||
|
||||
private void verifyJavadocFor(String className) throws IOException {
|
||||
PsiClass psiClass = JavaPsiFacade.getInstance(myProject).findClass(className, GlobalSearchScope.allScope(myProject));
|
||||
assertNotNull(psiClass);
|
||||
String doc = new JavaDocInfoGenerator(myProject, psiClass).generateDocInfo(null);
|
||||
assertNotNull(doc);
|
||||
PsiDirectory dir = (PsiDirectory)psiClass.getParent().getParent();
|
||||
PsiFile htmlFile = dir.findFile(psiClass.getName() + ".html");
|
||||
assertNotNull(htmlFile);
|
||||
assertEquals(StringUtil.convertLineSeparators(new String(htmlFile.getVirtualFile().contentsToByteArray()).trim()),
|
||||
replaceEnvironmentDependentContent(doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
|
||||
Reference in New Issue
Block a user