IDEA-66333 Quick documentation lookup on mouse hover

1. Preserve old 'mouse hover' text, just use doc links there;
2. Limit 'mouse hover' text by the header width;
3. Test data is updated;
This commit is contained in:
Denis.Zhdanov
2012-07-13 15:47:25 +04:00
parent 8d401251b8
commit d5ccdf5b6f
5 changed files with 305 additions and 223 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,10 @@ class Foo {{
}}
'''
def ref = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset)
assert CtrlMouseHandler.getInfo(ref.resolve(), ref.element) == """Bar
java.util.List<java.lang.String> foo (java.lang.String param)"""
assertEquals (
'<a href="psi_element://Bar">Bar</a><br/> <a href="psi_element://java.util.List">List</a>&lt;java.lang.String&gt; foo (java.lang.String param)',
CtrlMouseHandler.getInfo(ref.resolve(), ref.element)
)
}
public void testGenericField() {
@@ -45,8 +47,9 @@ class Foo {{
}}
'''
def ref = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset)
assert CtrlMouseHandler.getInfo(ref.resolve(), ref.element) == """Bar
java.lang.Integer field"""
assertEquals(
'<a href="psi_element://Bar">Bar</a><br/> java.lang.Integer field',
CtrlMouseHandler.getInfo(ref.resolve(), ref.element)
)
}
}
@@ -85,8 +85,7 @@ import java.util.List;
public class CtrlMouseHandler extends AbstractProjectComponent {
private static final int ourQuickDocRowsNumber = getIntProperty("quick.doc.desired.rows.number", 2);
private static final int ourQuickDocSymbolsInRowNumber = getIntProperty("quick.doc.desired.symbols.in.row.number", 80);
private static final int ourQuickDocRowsNumber = getIntProperty("quick.doc.desired.rows.number", 1);
private final TextAttributes ourReferenceAttributes;
private HighlightersSet myHighlighter;
@@ -264,7 +263,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
if (result != null) {
String fullText = documentationProvider.generateDoc(element, atPointer);
String qName = element instanceof PsiQualifiedNamedElement ? ((PsiQualifiedNamedElement)element).getQualifiedName() : null;
String text = DocPreviewUtil.buildPreview(result, qName, fullText, ourQuickDocRowsNumber, ourQuickDocSymbolsInRowNumber);
String text = DocPreviewUtil.buildPreview(result, qName, fullText, ourQuickDocRowsNumber);
return new DocInfo(text, documentationProvider, atPointer);
}
return DocInfo.EMPTY;
@@ -18,12 +18,16 @@ package com.intellij.codeInsight.navigation;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.Stack;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Provides utility methods for building documentation preview.
@@ -35,15 +39,26 @@ import java.util.Set;
*/
public class DocPreviewUtil {
private static final Set<String> TAGS_TO_ADD_LF = new HashSet<String>(Arrays.asList("p", "blockquote", "pre"));
private static final Set<String> TAGS_TO_IGNORE = new HashSet<String>(Arrays.asList("style", "b", "small"));
private static final Set<String> TAGS_TO_ADD_LF = new HashSet<String>();
private static final Set<String> TAGS_TO_IGNORE = new HashSet<String>();
static {
for (String tag : new String[] {"p", "blockquote", "pre"}) {
TAGS_TO_ADD_LF.add(tag);
TAGS_TO_ADD_LF.add(tag.toUpperCase());
}
for (String tag : new String[] {"style", "b", "small"}) {
TAGS_TO_IGNORE.add(tag);
TAGS_TO_IGNORE.add(tag.toUpperCase());
}
}
private DocPreviewUtil() {
}
/**
* Allows to build a documentation preview from the given arguments. Basically, takes given 'full documentation', wraps it according
* to the given 'desired rows and columns per-row' arguments and returns a result.
* Allows to build a documentation preview from the given arguments. Basically, takes given 'full documentation', cuts it according
* to the given 'desired rows' argument and returns a result.
*
* @param header target documentation header. Is expected to be a result of the
* {@link DocumentationProvider#getQuickNavigateInfo(PsiElement, PsiElement)} call
@@ -52,16 +67,10 @@ public class DocPreviewUtil {
* element with the given qualified name is added to the preview's end if the qName is provided then
* @param fullText full documentation text (if available)
* @param desiredRowsNumber maximum number of rows to use at the preview's body ('header' text is not count here)
* @param desiredSymbolsInRowNumber desired max number of columns per row
* @return preview text to use for the given arguments
*/
@NotNull
public static String buildPreview(@NotNull String header,
@Nullable String qName,
@Nullable String fullText,
int desiredRowsNumber,
int desiredSymbolsInRowNumber)
{
public static String buildPreview(@NotNull String header, @Nullable String qName, @Nullable String fullText, int desiredRowsNumber) {
if (fullText == null) {
return header;
}
@@ -76,237 +85,283 @@ public class DocPreviewUtil {
if (bodyEnd < 0) {
return header;
}
String body = fullText.substring(bodyStart, bodyEnd);
int headerEnd = fullText.indexOf("</PRE>");
if (headerEnd < 0) {
return header;
}
String headerWithLinks = fullText.substring(bodyStart, headerEnd);
String docText = fullText.substring(headerEnd + "</PRE>".length(), bodyEnd);
// The algorithm is:
// 1. Process full text body as follows:
// 1.1. Count non-markup symbols until desired row symbols number is exceeded;
// 1.2. Insert <br> after that to start a new row;
// 1.3. Stop processing as soon as the desired rows number is reached or the text is finished;
// 2. Add closing tags for all non-matched open tags;
// 1. Get given header text and replace meaningful symbols with the links to those symbols;
// 2. Calculate max symbols per-row to use for the result as a number of non-markup symbols at the longest header line;
// 3. Process full text body as follows:
// 2.1. Count non-markup symbols until desired row symbols number is exceeded;
// 2.2. Insert <br> after that to start a new row (if max rows number is not reached);
// 3.3. Stop processing as soon as the desired rows number is reached or the text is finished;
// 4. Add a link to the full documentation if it's not placed inside the resulted text;
// 5. Add closing tags for all non-matched open tags;
final Context context = new Context(desiredRowsNumber, desiredSymbolsInRowNumber);
int startParseOffset = 0;
//region 1. Prepare header to use
final Context context = new Context(desiredRowsNumber);
// Include information about the library/module location.
int bracket = header.indexOf(']');
int lf = header.indexOf('\n');
if (bracket > 0 && (lf < 0 || bracket < lf)) {
context.buffer.append(header.substring(0, bracket + 1)).append(" ");
}
// Include information that is available at the given header (it's not count to the given rows/columns arguments).
startParseOffset = process(body, startParseOffset, body.length(), getHeaderParser(context, header));
//endregion
//region Parse body
startParseOffset = process(body, startParseOffset, body.length(), getBodyParser(context));
//endregion
if (qName != null && startParseOffset < body.length()) {
context.buffer.append(String.format("<a href='psi_element://%s'>&lt;more&gt;</a>", qName));
}
int columnsPerRow = parseHeader(header, headerWithLinks, context);
process(docText, new BodyCallback(context, qName, columnsPerRow));
//region Add closing tags
while (!context.openTags.isEmpty()) {
context.buffer.append("</").append(context.openTags.pop()).append('>');
}
//endregion
return context.buffer.toString();
}
private static int parseHeader(@NotNull String headerTemplate, @NotNull String headerWithLinks, @NotNull Context context) {
//region Build links info.
Map<String/*qName*/, String/*address*/> links = new HashMap<String, String>();
process(headerWithLinks, new LinksCollector(links));
//endregion
//region Apply links info to the header template.
String headerToUse = headerTemplate.replace("\n", "<br/>");
for (Map.Entry<String, String> entry : links.entrySet()) {
String visibleName = entry.getKey();
int i = visibleName.lastIndexOf('.');
if (i > 0 && i < visibleName.length() - 1) {
visibleName = visibleName.substring(i + 1);
}
headerToUse = headerToUse.replace(entry.getKey(), String.format("<a href=\"%s\">%s</a>", entry.getValue(), visibleName));
}
context.buffer.append(headerToUse);
//endregion
//region Update 'columns-per-row' if the header is wide.
MaxColumnCalculator calculator = new MaxColumnCalculator();
process(headerToUse, calculator);
return calculator.maxColumn;
//endregion
}
private enum State {TEXT, INSIDE_OPEN_TAG, INSIDE_CLOSE_TAG}
@SuppressWarnings("AssignmentToForLoopParameter")
private static int process(@NotNull String text, int start, int end, @NotNull Callback callback) {
private static int process(@NotNull String text, @NotNull Callback callback) {
State state = State.TEXT;
int dataStartOffset = start;
int tagNameStartOffset = start;
int dataStartOffset = 0;
int tagNameStartOffset = 0;
String tagName = null;
for (; start < end; start++) {
char c = text.charAt(start);
int i = 0;
for (; i < text.length(); i++) {
char c = text.charAt(i);
switch (state) {
case TEXT:
if (c == '<') {
if (start > dataStartOffset) {
if (!callback.onText(text.substring(dataStartOffset, start).replace("&nbsp;", " "))) {
if (i > dataStartOffset) {
if (!callback.onText(text.substring(dataStartOffset, i).replace("&nbsp;", " "))) {
return dataStartOffset;
}
}
dataStartOffset = start;
if (start < text.length() - 1 && text.charAt(start + 1) == '/') {
dataStartOffset = i;
if (i < text.length() - 1 && text.charAt(i + 1) == '/') {
state = State.INSIDE_CLOSE_TAG;
tagNameStartOffset = ++start + 1;
tagNameStartOffset = ++i + 1;
}
else {
state = State.INSIDE_OPEN_TAG;
tagNameStartOffset = start + 1;
tagNameStartOffset = i + 1;
}
}
break;
case INSIDE_OPEN_TAG:
if (c == ' ') {
tagName = text.substring(tagNameStartOffset, start);
tagName = text.substring(tagNameStartOffset, i);
}
else if (c == '/') {
if (start < text.length() - 1 && text.charAt(start + 1) == '>') {
if (i < text.length() - 1 && text.charAt(i + 1) == '>') {
if (tagName == null) {
tagName = text.substring(tagNameStartOffset, start);
tagName = text.substring(tagNameStartOffset, i);
}
if (!callback.onStandaloneTag(tagName, text.substring(dataStartOffset, start + 2))) {
if (!callback.onStandaloneTag(tagName, text.substring(dataStartOffset, i + 2))) {
return dataStartOffset;
}
tagName = null;
state = State.TEXT;
dataStartOffset = ++start + 1;
dataStartOffset = ++i + 1;
break;
}
}
else if (c == '>') {
if (tagName == null) {
tagName = text.substring(tagNameStartOffset, start);
tagName = text.substring(tagNameStartOffset, i);
}
if (!callback.onOpenTag(tagName, text.substring(dataStartOffset, start + 1))) {
if (!callback.onOpenTag(tagName, text.substring(dataStartOffset, i + 1))) {
return dataStartOffset;
}
tagName = null;
state = State.TEXT;
dataStartOffset = start + 1;
dataStartOffset = i + 1;
}
break;
case INSIDE_CLOSE_TAG:
if (c == '>') {
if (tagName == null) {
tagName = text.substring(tagNameStartOffset, start);
tagName = text.substring(tagNameStartOffset, i);
}
if (!callback.onCloseTag(tagName, text.substring(dataStartOffset, start + 1))) {
if (!callback.onCloseTag(tagName, text.substring(dataStartOffset, i + 1))) {
return dataStartOffset;
}
tagName = null;
state = State.TEXT;
dataStartOffset = start + 1;
dataStartOffset = i + 1;
}
}
}
return start;
}
@NotNull
private static Callback getHeaderParser(@NotNull Context context, @NotNull final String header) {
return new AbstractCallback(context, false) {
private boolean myStop;
@Override
public boolean onOpenTag(@NotNull String name, @NotNull String text) {
return !myStop && super.onOpenTag(name, text);
}
@Override
public boolean onText(@NotNull String text) {
boolean addLf = false;
for (String s : text.split("\n")) {
if (addLf) {
newLine();
}
else {
addLf = true;
}
if (s.length() <= 0) {
continue;
}
if (!header.contains(s) && s.startsWith("java.lang.")) {
s = s.substring("java.lang.".length());
}
if (myStop || !header.contains(s)) {
return false;
}
if (header.endsWith(s)) {
myStop = true;
}
addText(s);
}
if (text.endsWith("\n")) {
newLine();
}
return true;
}
@Override
protected boolean canBreakBeforeText(@NotNull String text) {
// Don't allow line break before the closing type parameter bracket.
return !text.startsWith("&gt;") && !text.startsWith(",");
}
};
}
@NotNull
private static Callback getBodyParser(@NotNull final Context context) {
return new AbstractCallback(context, true) {
@Override
public boolean onText(@NotNull String text) {
return addText(text);
}
};
}
private static class Context {
if (dataStartOffset < text.length()) {
callback.onText(text.substring(dataStartOffset, text.length()).replace("&nbsp;", " "));
}
return i;
}
private static class Context {
@NotNull public final Stack<String> openTags = new Stack<String>();
@NotNull public final StringBuilder buffer = new StringBuilder();
public final int rows;
public final int columnsPerRow;
public int currentRow;
public int currentColumn;
public Context(int rows, int columnsPerRow) {
public Context(int rows) {
this.rows = rows;
this.columnsPerRow = columnsPerRow;
}
}
private interface Callback {
boolean onOpenTag(@NotNull String name, @NotNull String text);
boolean onCloseTag(@NotNull String name, @NotNull String text);
boolean onStandaloneTag(@NotNull String name, @NotNull String text);
boolean onText(@NotNull String text);
}
private static abstract class AbstractCallback implements Callback {
@NotNull protected final Context myContext;
private final boolean myCountRows;
private boolean myScheduleNewLine;
private boolean myInsidePre;
private static class LinksCollector implements Callback {
protected AbstractCallback(@NotNull Context context, boolean countRows) {
myContext = context;
myCountRows = countRows;
private static final Pattern HREF_PATTERN = Pattern.compile("href=[\"']([^\"']+)");
@NotNull private final Map<String, String> myLinks;
private String myHref;
LinksCollector(@NotNull Map<String, String> links) {
myLinks = links;
}
@Override
public boolean onOpenTag(@NotNull String name, @NotNull String text) {
if (!"a".equals(name)) {
return true;
}
Matcher matcher = HREF_PATTERN.matcher(text);
if (matcher.find()) {
myHref = matcher.group(1);
}
return true;
}
@Override
public boolean onCloseTag(@NotNull String name, @NotNull String text) {
if ("a".equals(name)) {
myHref = null;
}
return true;
}
@Override
public boolean onStandaloneTag(@NotNull String name, @NotNull String text) {
return true;
}
@Override
public boolean onText(@NotNull String text) {
if (myHref != null) {
myLinks.put(text, myHref);
myHref = null;
}
return true;
}
}
private static class MaxColumnCalculator implements Callback {
private static final TObjectIntHashMap<String> SUBSTITUTIONS = new TObjectIntHashMap<String>();
static {
SUBSTITUTIONS.put("&lt;", 1);
SUBSTITUTIONS.put("&gt;", 1);
SUBSTITUTIONS.put("&nbsp;", 1);
}
public int maxColumn;
private int myCurrentColumn;
@Override
public boolean onOpenTag(@NotNull String name, @NotNull String text) {
if ("br".equals(name)) {
myCurrentColumn = 0;
}
return true;
}
@Override
public boolean onCloseTag(@NotNull String name, @NotNull String text) {
return true;
}
@Override
public boolean onStandaloneTag(@NotNull String name, @NotNull String text) {
return onOpenTag(name, text);
}
@Override
public boolean onText(@NotNull String text) {
int length = text.length();
if (SUBSTITUTIONS.containsKey(text)) {
length = SUBSTITUTIONS.get(text);
}
myCurrentColumn += length;
maxColumn = Math.max(maxColumn, myCurrentColumn);
return true;
}
}
private static class BodyCallback implements Callback {
@NotNull protected final Context myContext;
private boolean myScheduleNewLine;
private boolean myInsidePre;
private boolean myDocAdded;
private int myCurrentRow;
private int myCurrentColumn;
@Nullable private String myQName;
private int myColumnsPerRow;
protected BodyCallback(@NotNull Context context, @Nullable String qName, final int columnsPerRow) {
myContext = context;
myQName = qName;
myColumnsPerRow = columnsPerRow;
}
public boolean onOpenTag(@NotNull String name, @NotNull String text) {
if ("pre".equals(name)) {
myInsidePre = true;
}
if (!processDelayedLfTag(name)) {
return myContext.currentRow < myContext.rows;
return myCurrentRow < myContext.rows;
}
if (!TAGS_TO_IGNORE.contains(name)) {
myContext.buffer.append(text);
addText(text, false);
myContext.openTags.push(name);
}
return true;
@@ -324,120 +379,123 @@ public class DocPreviewUtil {
return false;
}
@Override
public boolean onCloseTag(@NotNull String name, @NotNull String text) {
if ("pre".equals(name)) {
myInsidePre = false;
}
if (!processDelayedLfTag(name)) {
return myContext.currentRow < myContext.rows;
return myCurrentRow < myContext.rows;
}
if (!TAGS_TO_IGNORE.contains(name)) {
myContext.buffer.append(text);
addText(text, false);
myContext.openTags.remove(name);
}
return true;
}
@Override
public boolean onStandaloneTag(@NotNull String name, @NotNull String text) {
if (!processDelayedLfTag(name)) {
return true;
}
if (!TAGS_TO_IGNORE.contains(name)) {
myContext.buffer.append(text);
addText(text, false);
}
return true;
}
@Override
public boolean onText(@NotNull String text) {
myContext.buffer.append(text);
return true;
}
protected boolean canBreakBeforeText(@NotNull String text) {
return true;
}
protected boolean addText(@NotNull String text) {
boolean addSpace = false;
if (!text.isEmpty() && (text.startsWith(" ") || text.startsWith("\t"))) {
myContext.buffer.append(text.charAt(0));
myContext.currentColumn++;
if (!text.isEmpty() && (text.startsWith(" ") || text.startsWith("\t")) && !addText(String.valueOf(text.charAt(0)), true)) {
return false;
}
String tailText = (!text.isEmpty() && (text.endsWith(" ") || text.endsWith("\t"))) ? text.substring(text.length() - 1) : null;
String tailText = (!text.isEmpty() && (text.endsWith(" ") || text.endsWith("\t"))) ? text.substring(text.length() - 1) : null;
text = text.trim();
if (myInsidePre && text.contains("\n")) {
boolean addLf = false;
for (String s : text.split("\n")) {
if (addLf) {
newLine();
if (myContext.currentRow >= myContext.rows) {
if (!newLine()) {
return false;
}
}
else {
addLf = true;
}
addText(s);
if (myContext.currentRow >= myContext.rows) {
return false;
}
if (!onText(s)) return false;
}
return myContext.currentRow < myContext.rows;
return true;
}
for (String s : text.split(" ")) {
s = s.trim();
if (s.length() <= 0) {
continue;
}
if (myScheduleNewLine && canBreakBeforeText(s)) {
newLine();
if (!newLine()) return false;
addSpace = false;
if (myContext.currentRow >= myContext.rows) {
return false;
}
}
if (addSpace) {
myContext.buffer.append(" ");
myContext.currentColumn++;
if (!addText(" ", true)) return false;
}
else {
addSpace = true;
}
myContext.currentColumn += s.length();
myContext.buffer.append(s);
if (myContext.currentColumn < myContext.columnsPerRow) {
continue;
if (!addText(s, true)) return false;
}
return !(tailText != null && !addText(tailText, true));
}
private static boolean canBreakBeforeText(@NotNull String text) {
return !text.startsWith("&gt;") && !text.startsWith(",");
}
private boolean newLine() {
if (onLastRow()) {
if (myQName != null) {
myContext.buffer.append(String.format(" <a href='psi_element://%s'>...</a>", myQName));
}
myScheduleNewLine = true;
}
if (tailText != null) {
myContext.buffer.append(tailText);
myContext.currentColumn += tailText.length();
return false;
}
addText("<br/>", false);
myCurrentColumn = 0;
myScheduleNewLine = false;
myCurrentRow++;
return true;
}
protected void newLine() {
myContext.buffer.append("<br/>");
myContext.currentColumn = 0;
myScheduleNewLine = false;
if (myCountRows) {
myContext.currentRow++;
private boolean addText(@NotNull String text, boolean countColumns) {
if (!myDocAdded) {
myContext.buffer.append("<br/>");
myDocAdded = true;
}
if (!countColumns) {
myContext.buffer.append(text);
return true;
}
int remainingColumns = myColumnsPerRow - myCurrentColumn;
if (onLastRow()) {
remainingColumns -= " ...".length();
}
if (remainingColumns < text.length() && !newLine()) return false;
myContext.buffer.append(text);
myCurrentColumn += text.length();
return true;
}
private boolean onLastRow() {
return myCurrentRow >= myContext.rows - 1;
}
}
private enum State {TEXT, INSIDE_OPEN_TAG, INSIDE_CLOSE_TAG}
}
@@ -19,6 +19,7 @@ package com.intellij.codeInsight.navigation;
import org.junit.Test
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertTrue
/**
* @author Denis Zhdanov
@@ -103,10 +104,28 @@ implements <a href="psi_element://java.io.Serializable"><code>java.io.Serializab
'''
def expected = '''\
[&lt; 1.7 &gt;] java.lang<PRE>public final class java.lang.String<br/>extends <a href="psi_element://java.lang.Object"><code>Object</code></a><br/>implements <a href="psi_element://java.io.Serializable"><code>java.io.Serializable</code></a>, <a href="psi_element://java.lang.Comparable"><code>java.lang.Comparable</code></a>&lt;<a href="psi_element://java.lang.String"><code>java.lang.String<br/></code></a>&gt;, <a href="psi_element://java.lang.CharSequence"><code>java.lang.CharSequence</code></a></PRE><br/>The <code>String</code> class represents character strings. All string literals<br/>in Java programs, such as <code>"abc"</code>, are implemented as instances<br/><a href='psi_element://java.lang.String'>&lt;more&gt;</a>\
java.lang<br/> public final class <a href="psi_element://java.lang.String">String</a> extends Object<br/> implements <a href="psi_element://java.io.Serializable">Serializable</a>, <a href="psi_element://java.lang.Comparable">Comparable</a>&lt;<a href="psi_element://java.lang.String">String</a>&gt;, <a href="psi_element://java.lang.CharSequence">CharSequence</a><br/>The <code>String</code> class represents character strings. All string <br/>literals in Java programs, such as <code>"abc"</code>, are implemented as <br/>instances of this class. <br/>Strings are constant; their values cannot be changed after <br/>they are created. String buffers support mutable strings. <br/>Because String objects are immutable they can be shared. For <br/>example: <br/>String str = "abc"; <br/>is equivalent to: <br/>char data[] = {'a', 'b', 'c'}; <a href='psi_element://java.lang.String'>...</a>\
'''
def actual = DocPreviewUtil.buildPreview(header, "java.lang.String", fullText, 2, 60)
def actual = DocPreviewUtil.buildPreview(header, "java.lang.String", fullText, 10)
assertTrue(actual.endsWith(expected)) // Can't check for equals() because jdk name might differ on different machines.
}
@Test
void fieldTypeSubstitution() {
def header = '''\
Bar
java.util.List&lt;java.lang.String&gt; foo (java.lang.String param)\
'''
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://Bar"><code>Bar</code></a></b></small><PRE><a href="psi_element://java.util.List"><code>java.util.List</code></a>&lt;T&gt;&nbsp;<b>foo</b>(T&nbsp;param)</PRE></body></html>\
'''
def expected = '''\
<a href="psi_element://Bar">Bar</a><br/> <a href="psi_element://java.util.List">List</a>&lt;java.lang.String&gt; foo (java.lang.String param)\
'''
def actual = DocPreviewUtil.buildPreview(header, "java.lang.String", fullText, 2)
assertEquals(expected, actual)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,8 +29,10 @@ class Bar<T> { java.util.List<T> foo(T param); }
new Bar<String>().f<caret>oo();
'''
def ref = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset)
assert CtrlMouseHandler.getInfo(ref.resolve(), ref.element) == """Bar
java.util.List&lt;java.lang.String&gt; foo (java.lang.String param)"""
assertEquals(
"""<a href="psi_element://Bar">Bar</a><br/>java.util.<a href="psi_element://java.util.List">List</a>&lt;java.lang.String&gt; foo (java.lang.String param)""",
CtrlMouseHandler.getInfo(ref.resolve(), ref.element)
)
}
public void testGenericField() {
@@ -39,8 +41,9 @@ class Bar<T> { T field; }
new Bar<Integer>().fi<caret>eld
'''
def ref = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset)
assert CtrlMouseHandler.getInfo(ref.resolve(), ref.element) == """Bar
java.lang.Integer getField ()"""
assertEquals(
"""<a href="psi_element://Bar">Bar</a><br/>java.lang.Integer getField ()""",
CtrlMouseHandler.getInfo(ref.resolve(), ref.element)
)
}
}