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.
+ *
+ * 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; null 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.
+ *
+ * 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); null 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++) {
diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/navigation/DocPreviewUtilTest.groovy b/platform/lang-impl/testSources/com/intellij/codeInsight/navigation/DocPreviewUtilTest.groovy
index a7799d0b61a0..31b235dcd1ca 100644
--- a/platform/lang-impl/testSources/com/intellij/codeInsight/navigation/DocPreviewUtilTest.groovy
+++ b/platform/lang-impl/testSources/com/intellij/codeInsight/navigation/DocPreviewUtilTest.groovy
@@ -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 = '''\
+ com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl@Nullable public static Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterConstraint(PsiTypeParameter typeParam,
+ PsiType param,
+ PsiType arg,
+ boolean isContraVariantPosition,
+ LanguageLevel languageLevel)
'''
+
+ def expected = '''\
+PsiResolveHelperImpl
public static Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterConstraint (PsiTypeParameter typeParam, PsiType param, PsiType arg, boolean isContraVariantPosition, LanguageLevel languageLevel)'''
+
+ assertEquals(expected, DocPreviewUtil.buildPreview(header, null, fullText))
+ }
}