IDEA-66333 Quick documentation lookup on mouse hover

Corrected links appliance
This commit is contained in:
Denis.Zhdanov
2012-07-24 12:43:25 +04:00
parent 42eebc6127
commit 9799b8ce43
2 changed files with 84 additions and 5 deletions
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.documentation.DocumentationManager;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,6 +37,23 @@ import java.util.regex.Pattern;
*/
public class DocPreviewUtil {
private static final TIntHashSet ALLOWED_LINK_SEPARATORS = new TIntHashSet();
static {
ALLOWED_LINK_SEPARATORS.add(',');
ALLOWED_LINK_SEPARATORS.add(' ');
ALLOWED_LINK_SEPARATORS.add('.');
ALLOWED_LINK_SEPARATORS.add(';');
ALLOWED_LINK_SEPARATORS.add('&');
ALLOWED_LINK_SEPARATORS.add('\t');
ALLOWED_LINK_SEPARATORS.add('\n');
ALLOWED_LINK_SEPARATORS.add('[');
ALLOWED_LINK_SEPARATORS.add(']');
ALLOWED_LINK_SEPARATORS.add('(');
ALLOWED_LINK_SEPARATORS.add(')');
ALLOWED_LINK_SEPARATORS.add('<');
ALLOWED_LINK_SEPARATORS.add('>');
}
/**
* We shorten links text from fully qualified name to short names (e.g. from <code>'java.lang.String'</code> to <code>'String'</code>).
* There is a possible situation then that we have two replacements where one key is a simple name and another one is a fully qualified
@@ -90,13 +108,16 @@ public class DocPreviewUtil {
Map<String/*qName*/, String/*address*/> links = new HashMap<String, String>();
process(fullText, new LinksCollector(links));
// Add short names.
// Add derived names.
Map<String, String> toAdd = new HashMap<String, String>();
for (Map.Entry<String, String> entry : links.entrySet()) {
String key = entry.getKey();
int i = key.lastIndexOf('.');
if (i > 0 && i < key.length() - 1) {
toAdd.put(key.substring(i + 1), entry.getValue());
String shortName = parseShortName(entry.getKey());
if (shortName != null) {
toAdd.put(shortName, entry.getValue());
}
String longName = parseLongName(entry.getKey(), entry.getValue());
if (longName != null) {
toAdd.put(longName, entry.getValue());
}
}
links.putAll(toAdd);
@@ -121,6 +142,40 @@ public class DocPreviewUtil {
return buffer.toString();
}
/**
* Tries to build a short name form the given name assuming that it is a full name.
* <p/>
* Example: return {@code 'String'} for a given {@code 'java.lang.String'}.
*
* @param name name to process
* @return short name derived from the given full name if possible; <code>null</code> otherwise
*/
@Nullable
private static String parseShortName(@NotNull String name) {
int i = name.lastIndexOf('.');
return i > 0 && i < name.length() - 1 ? name.substring(i + 1) : null;
}
/**
* Tries to build a long name from the given short name and a link.
* <p/>
* Example: return {@code 'java.lang.String'} for a given pair (name {@code 'String'}; address: {@code 'psi_element://java.lang.String'}.
*
* @param shortName short name to process
* @param address address to process
* @return long name derived from the given arguments (if any); <code>null</code> otherwise
*/
@Nullable
private static String parseLongName(@NotNull String shortName, @NotNull String address) {
String pureAddress = address;
int i = pureAddress.lastIndexOf("//");
if (i > 0 && i < pureAddress.length() - 2) {
pureAddress = pureAddress.substring(i + 2);
}
return (pureAddress.equals(shortName) || !pureAddress.endsWith(shortName)) ? null : pureAddress;
}
private static void replace(@NotNull StringBuilder text,
@NotNull String replaceFrom,
@NotNull String replaceTo,
@@ -131,6 +186,11 @@ public class DocPreviewUtil {
if (intersects(readOnlyChanges, i, end)) {
continue;
}
if (end - i > 1 && end < text.length() && !ALLOWED_LINK_SEPARATORS.contains(text.charAt(end))) {
// Consider a situation when we have, say, replacement from text 'PsiType' and encounter a 'PsiTypeParameter' in the text.
// We don't want to perform the replacement then.
continue;
}
text.replace(i, end, replaceTo);
int diff = replaceTo.length() - replaceFrom.length();
for (int j = 0; j < readOnlyChanges.size(); j++) {
@@ -145,4 +145,23 @@ Qname'''
assertEquals(expected, DocPreviewUtil.buildPreview(header, null, fullText))
}
@Test
void substrings() {
def header = '''\
PsiResolveHelperImpl
public static com.intellij.openapi.util.Pair&lt;com.intellij.psi.PsiType, com.intellij.psi.ConstraintType&gt; getSubstitutionForTypeParameterConstraint (com.intellij.psi.PsiTypeParameter typeParam, com.intellij.psi.PsiType param, com.intellij.psi.PsiType arg, boolean isContraVariantPosition, com.intellij.pom.java.LanguageLevel languageLevel)'''
def fullText = '''\
<html><head> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl"><code>com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl</code></a></b></small><PRE>@<a href="psi_element://org.jetbrains.annotations.Nullable"><code>Nullable</code></a>&nbsp;public static&nbsp;<a href="psi_element://com.intellij.openapi.util.Pair"><code>Pair</code></a>&lt;<a href="psi_element://com.intellij.psi.PsiType"><code>PsiType</code></a>, <a href="psi_element://com.intellij.psi.ConstraintType"><code>ConstraintType</code></a>&gt;&nbsp;<b>getSubstitutionForTypeParameterConstraint</b>(<a href="psi_element://com.intellij.psi.PsiTypeParameter"><code>PsiTypeParameter</code></a>&nbsp;typeParam,
<a href="psi_element://com.intellij.psi.PsiType"><code>PsiType</code></a>&nbsp;param,
<a href="psi_element://com.intellij.psi.PsiType"><code>PsiType</code></a>&nbsp;arg,
boolean&nbsp;isContraVariantPosition,
<a href="psi_element://com.intellij.pom.java.LanguageLevel"><code>LanguageLevel</code></a>&nbsp;languageLevel)</PRE></body></html>'''
def expected = '''\
<a href="psi_element://com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl">PsiResolveHelperImpl</a><br/>public static <a href="psi_element://com.intellij.openapi.util.Pair">Pair</a>&lt;<a href="psi_element://com.intellij.psi.PsiType">PsiType</a>, <a href="psi_element://com.intellij.psi.ConstraintType">ConstraintType</a>&gt; getSubstitutionForTypeParameterConstraint (<a href="psi_element://com.intellij.psi.PsiTypeParameter">PsiTypeParameter</a> typeParam, <a href="psi_element://com.intellij.psi.PsiType">PsiType</a> param, <a href="psi_element://com.intellij.psi.PsiType">PsiType</a> arg, boolean isContraVariantPosition, <a href="psi_element://com.intellij.pom.java.LanguageLevel">LanguageLevel</a> languageLevel)'''
assertEquals(expected, DocPreviewUtil.buildPreview(header, null, fullText))
}
}