mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-66333 Quick documentation lookup on mouse hover
Corrected links appliance
This commit is contained in:
@@ -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++) {
|
||||
|
||||
+19
@@ -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<com.intellij.psi.PsiType, com.intellij.psi.ConstraintType> 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> public static <a href="psi_element://com.intellij.openapi.util.Pair"><code>Pair</code></a><<a href="psi_element://com.intellij.psi.PsiType"><code>PsiType</code></a>, <a href="psi_element://com.intellij.psi.ConstraintType"><code>ConstraintType</code></a>> <b>getSubstitutionForTypeParameterConstraint</b>(<a href="psi_element://com.intellij.psi.PsiTypeParameter"><code>PsiTypeParameter</code></a> typeParam,
|
||||
<a href="psi_element://com.intellij.psi.PsiType"><code>PsiType</code></a> param,
|
||||
<a href="psi_element://com.intellij.psi.PsiType"><code>PsiType</code></a> arg,
|
||||
boolean isContraVariantPosition,
|
||||
<a href="psi_element://com.intellij.pom.java.LanguageLevel"><code>LanguageLevel</code></a> 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><<a href="psi_element://com.intellij.psi.PsiType">PsiType</a>, <a href="psi_element://com.intellij.psi.ConstraintType">ConstraintType</a>> 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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user