diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java index 455f6511eb..63ffbe774a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java @@ -91,7 +91,7 @@ private String computeJavadocHover(IJavaElement element) throws CoreException { } else if (element instanceof IMember) { member= (IMember) element; } else if (element instanceof IPackageFragment) { - Reader r = JavadocContentAccess.getHTMLContentReader((IPackageFragment) element, true); + Reader r = JavadocContentAccess.getMarkdownContentReader((IPackageFragment) element, true); if(r == null ) { return null; } @@ -109,7 +109,7 @@ private String computeJavadocHover(IJavaElement element) throws CoreException { if(javadocRange == null ) { return null; } - Reader r = JavadocContentAccess.getHTMLContentReader(member,true,true); + Reader r = JavadocContentAccess.getMarkdownContentReader(member); if(r == null ) { return null; } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionResolveHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionResolveHandler.java index a7f998d2e5..95cec64372 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionResolveHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionResolveHandler.java @@ -122,7 +122,7 @@ public CompletionItem resolve(CompletionItem param) { try { final IMember curMember = member; javadoc = new SimpleTimeLimiter().callWithTimeout(() -> { - Reader reader = JavadocContentAccess.getHTMLContentReader(curMember, true, true); + Reader reader = JavadocContentAccess.getPlainTextContentReader(curMember); return reader == null? null:CharStreams.toString(reader); }, 500, TimeUnit.MILLISECONDS, true); } catch (UncheckedTimeoutException tooSlow) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/AbstractJavaDocConverter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/AbstractJavaDocConverter.java new file mode 100644 index 0000000000..0307dffbfe --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/AbstractJavaDocConverter.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2017 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.javadoc; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +/** + * Converts JavaDoc tags into an output format. + * + * @author Fred Bricon + */ +abstract class AbstractJavaDocConverter { + + private JavaDoc2HTMLTextReader reader; + + private boolean read; + + private String result; + + public AbstractJavaDocConverter(Reader reader) { + setJavaDoc2HTMLTextReader(reader); + } + + public AbstractJavaDocConverter(String javadoc) { + setJavaDoc2HTMLTextReader(javadoc == null ? null : new StringReader(javadoc)); + } + + private void setJavaDoc2HTMLTextReader(Reader reader) { + if (reader == null || reader instanceof JavaDoc2HTMLTextReader) { + this.reader = (JavaDoc2HTMLTextReader) reader; + } else { + this.reader = new JavaDoc2HTMLTextReader(reader); + } + } + + public String getAsString() throws IOException { + if (!read && reader != null) { + String rawHtml = reader.getString(); + result = convert(rawHtml); + } + return result; + } + + public Reader getAsReader() throws IOException { + String m = getAsString(); + return m == null ? null : new StringReader(m); + } + + abstract String convert(String rawHtml); +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/HtmlToPlainText.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/HtmlToPlainText.java new file mode 100644 index 0000000000..7f51d28a18 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/HtmlToPlainText.java @@ -0,0 +1,114 @@ +/** + * Copyright © 2009-2017, Jonathan Hedley + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.eclipse.jdt.ls.core.internal.javadoc; + +import org.jsoup.helper.StringUtil; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.NodeTraversor; +import org.jsoup.select.NodeVisitor; + +/** + * HTML to plain-text. Uses jsoup to convert HTML input to lightly-formatted + * plain-text. This is a fork of Jsoup's HtmlToPlainText + * + * @author Jonathan Hedley, jonathan@hedley.net + */ +public class HtmlToPlainText { + + /** + * Format an Element to plain-text + * @param element the root element to format + * @return formatted text + */ + public String getPlainText(Element element) { + FormattingVisitor formatter = new FormattingVisitor(); + NodeTraversor traversor = new NodeTraversor(formatter); + traversor.traverse(element); // walk the DOM, and call .head() and .tail() for each node + + return formatter.toString(); + } + + // the formatting rules, implemented in a breadth-first DOM traverse + private class FormattingVisitor implements NodeVisitor { + private StringBuilder accum = new StringBuilder(); // holds the accumulated text + private int listNesting; + // hit when the node is first seen + @Override + public void head(Node node, int depth) { + String name = node.nodeName(); + if (node instanceof TextNode) { + append(((TextNode) node).text()); // TextNodes carry all user-readable text in the DOM. + } else if (name.equals("ul")) { + listNesting++; + } else if (name.equals("li")) { + append("\n "); + for (int i = 1; i < listNesting; i++) { + append(" "); + } + if (listNesting == 1) { + append("* "); + } else { + append("- "); + } + } else if (name.equals("dt")) { + append(" "); + } else if (StringUtil.in(name, "p", "h1", "h2", "h3", "h4", "h5", "tr")) { + append("\n"); + } + } + + // hit when all of the node's children (if any) have been visited + @Override + public void tail(Node node, int depth) { + String name = node.nodeName(); + if (StringUtil.in(name, "br", "dd", "dt", "p", "h1", "h2", "h3", "h4", "h5")) { + append("\n"); + } else if (StringUtil.in(name, "th", "td")) { + append(" "); + } else if (name.equals("a")) { + append(String.format(" <%s>", node.absUrl("href"))); + } else if (name.equals("ul")) { + listNesting--; + } + } + + // appends text to the string builder with a simple word wrap method + private void append(String text) { + if (text.equals(" ") && + (accum.length() == 0 || StringUtil.in(accum.substring(accum.length() - 1), " ", "\n"))) + { + return; // don't accumulate long runs of empty spaces + } + accum.append(text); + } + + @Override + public String toString() { + return accum.toString(); + } + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2MarkdownConverter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2MarkdownConverter.java index 0d8ddd1ff1..7008d54af2 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2MarkdownConverter.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2MarkdownConverter.java @@ -10,9 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.ls.core.internal.javadoc; -import java.io.IOException; import java.io.Reader; -import java.io.StringReader; import com.overzealous.remark.Options; import com.overzealous.remark.Options.Tables; @@ -23,12 +21,10 @@ * * @author Fred Bricon */ -public class JavaDoc2MarkdownConverter { +public class JavaDoc2MarkdownConverter extends AbstractJavaDocConverter { private static Remark remark; - private String markDown; - static { Options options = new Options(); options.tables = Tables.CONVERT_TO_CODE_BLOCK; @@ -38,38 +34,17 @@ public class JavaDoc2MarkdownConverter { options.reverseHtmlSmartPunctuation = true; remark = new Remark(options); } - - private JavaDoc2HTMLTextReader reader; - - private boolean read; - public JavaDoc2MarkdownConverter(Reader reader) { - setJavaDoc2HTMLTextReader(reader); + super(reader); } public JavaDoc2MarkdownConverter(String javadoc) { - setJavaDoc2HTMLTextReader(javadoc== null? null:new StringReader(javadoc)); - } - - private void setJavaDoc2HTMLTextReader(Reader reader) { - if (reader == null || reader instanceof JavaDoc2HTMLTextReader) { - this.reader = (JavaDoc2HTMLTextReader) reader; - } else { - this.reader = new JavaDoc2HTMLTextReader(reader); - } - } - - public String getAsString() throws IOException { - if (!read && reader != null) { - String rawHtml = reader.getString(); - markDown = remark.convert(rawHtml); - } - return markDown; + super(javadoc); } - public Reader getAsReader() throws IOException { - String m = getAsString(); - return m == null ? null : new StringReader(m); + @Override + String convert(String rawHtml) { + return remark.convert(rawHtml); } } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2PlainTextConverter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2PlainTextConverter.java new file mode 100644 index 0000000000..0adc7f9b0f --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavaDoc2PlainTextConverter.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2016-2017 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.javadoc; + +import java.io.Reader; + +import org.jsoup.Jsoup; + +/** + * Converts JavaDoc tags into plain text equivalent. + * + * @author Fred Bricon + */ +public class JavaDoc2PlainTextConverter extends AbstractJavaDocConverter { + + public JavaDoc2PlainTextConverter(Reader reader) { + super(reader); + } + + public JavaDoc2PlainTextConverter(String javadoc) { + super(javadoc); + } + + @Override + String convert(String rawHtml) { + HtmlToPlainText formatter = new HtmlToPlainText(); + return formatter.getPlainText(Jsoup.parse(rawHtml)); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess.java index a1bb201e09..34883b3c22 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/javadoc/JavadocContentAccess.java @@ -63,25 +63,6 @@ private JavadocContentAccess() { // do not instantiate } - /** - * Gets a reader for an IMember's Javadoc comment content from the source attachment. - * The content does contain only the text from the comment without the Javadoc leading star characters. - * Returns null if the member does not contain a Javadoc comment or if no source is available. - * @param member The member to get the Javadoc of. - * @param allowInherited For methods with no (Javadoc) comment, the comment of the overridden class - * is returned if allowInherited is true. - * @return Returns a reader for the Javadoc comment content or null if the member - * does not contain a Javadoc comment or if no source is available - * @throws JavaModelException is thrown when the elements javadoc can not be accessed - */ - public static Reader getContentReader(IMember member, boolean allowInherited) throws JavaModelException { - Reader contentReader= internalGetContentReader(member); - if (contentReader != null || !(allowInherited && (member.getElementType() == IJavaElement.METHOD))) { - return contentReader; - } - return findDocInHierarchy((IMethod) member, false, false); - } - /** * Gets a reader for an IMember's Javadoc comment content from the source attachment. * The content does contain only the text from the comment without the Javadoc leading star characters. @@ -191,22 +172,21 @@ private static boolean containsOnlyInheritDoc(Reader reader, int length) { } /** - * Gets a reader for an IMember's Javadoc comment content from the source attachment. - * and renders the tags in HTML. - * Returns null if the member does not contain a Javadoc comment or if no source is available. + * Gets a reader for an IMember's Javadoc comment content from the source + * attachment. and renders the tags in Markdown. Returns null + * if the member does not contain a Javadoc comment or if no source is + * available. * - * @param member the member to get the Javadoc of. - * @param allowInherited for methods with no (Javadoc) comment, the comment of the overridden - * class is returned if allowInherited is true - * @param useAttachedJavadoc if true Javadoc will be extracted from attached Javadoc - * if there's no source - * @return a reader for the Javadoc comment content in HTML or null if the member - * does not contain a Javadoc comment or if no source is available - * @throws JavaModelException is thrown when the elements Javadoc can not be accessed - * @since 3.2 + * @param member + * the member to get the Javadoc of. + * @return a reader for the Javadoc comment content in Markdown or + * null if the member does not contain a Javadoc + * comment or if no source is available + * @throws JavaModelException + * is thrown when the elements Javadoc can not be accessed */ - public static Reader getHTMLContentReader(IMember member, boolean allowInherited, boolean useAttachedJavadoc) throws JavaModelException { - Reader contentReader= internalGetContentReader(member); + public static Reader getMarkdownContentReader(IMember member) throws JavaModelException { + Reader contentReader = getHTMLContentReader(member, true, true); if (contentReader != null) { try { return new JavaDoc2MarkdownConverter(contentReader).getAsReader(); @@ -214,6 +194,62 @@ public static Reader getHTMLContentReader(IMember member, boolean allowInherited throw new JavaModelException(e, IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT); } } + return null; + } + + /** + * Gets a reader for an IMember's Javadoc comment content from the source + * attachment. and renders the tags in plain text. Returns null if + * the member does not contain a Javadoc comment or if no source is available. + * + * @param member + * the member to get the Javadoc of. + * @return a reader for the Javadoc comment content in plain text or + * null if the member does not contain a Javadoc comment or + * if no source is available + * @throws JavaModelException + * is thrown when the elements Javadoc can not be accessed + */ + public static Reader getPlainTextContentReader(IMember member) throws JavaModelException { + Reader contentReader = getHTMLContentReader(member, true, true); + if (contentReader != null) { + try { + return new JavaDoc2PlainTextConverter(contentReader).getAsReader(); + } catch (IOException e) { + throw new JavaModelException(e, IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT); + } + } + return null; + } + + /** + * Gets a reader for an IMember's Javadoc comment content from the source + * attachment. and renders the tags in HTML. Returns null if + * the member does not contain a Javadoc comment or if no source is + * available. + * + * @param member + * the member to get the Javadoc of. + * @param allowInherited + * for methods with no (Javadoc) comment, the comment of the + * overridden class is returned if allowInherited is + * true + * @param useAttachedJavadoc + * if true Javadoc will be extracted from attached + * Javadoc if there's no source + * @return a reader for the Javadoc comment content in HTML or + * null if the member does not contain a Javadoc + * comment or if no source is available + * @throws JavaModelException + * is thrown when the elements Javadoc can not be accessed + * @since 3.2 + */ + static Reader getHTMLContentReader(IMember member, boolean allowInherited, boolean useAttachedJavadoc) + throws JavaModelException { + Reader contentReader= internalGetContentReader(member); + if (contentReader != null) { + return contentReader; + } if (useAttachedJavadoc && member.getOpenable().getBuffer() == null) { // only if no source available String s= member.getAttachedJavadoc(null); @@ -223,35 +259,33 @@ public static Reader getHTMLContentReader(IMember member, boolean allowInherited } if (allowInherited && (member.getElementType() == IJavaElement.METHOD)) { - return findDocInHierarchy((IMethod) member, true, useAttachedJavadoc); + return findDocInHierarchy((IMethod) member, useAttachedJavadoc); } return null; } /** - * Gets a reader for a package fragment's Javadoc comment content from the source attachment. - * and renders the tags in HTML. - * Returns null if the package fragment does not contain a Javadoc comment or if no source is available. + * Gets a reader for a package fragment's Javadoc comment content from the + * source attachment. and renders the tags in Markdown. Returns + * null if the package fragment does not contain a Javadoc + * comment or if no source is available. * - * @param fragment the package fragment to get the Javadoc of. - * @param useAttachedJavadoc if true Javadoc will be extracted from attached Javadoc - * if there's no source - * @return a reader for the Javadoc comment content in HTML or null if the package fragment - * does not contain a Javadoc comment or if no source is available - * @throws JavaModelException is thrown when the package fragment's Javadoc can not be accessed - * @since 3.2 + * @param fragment + * the package fragment to get the Javadoc of. + * @param useAttachedJavadoc + * if true Javadoc will be extracted from attached + * Javadoc if there's no source + * @return a reader for the Javadoc comment content in Markdown or + * null if the package fragment does not contain a + * Javadoc comment or if no source is available + * @throws JavaModelException + * is thrown when the package fragment's Javadoc can not be + * accessed */ - public static Reader getHTMLContentReader(IPackageFragment fragment, boolean useAttachedJavadoc) throws JavaModelException { + public static Reader getMarkdownContentReader(IPackageFragment fragment, boolean useAttachedJavadoc) throws JavaModelException { Reader contentReader= internalGetContentReader(fragment); - if (contentReader != null) { - try { - return new JavaDoc2MarkdownConverter(contentReader).getAsReader(); - } catch (IOException e) { - throw new JavaModelException(e, IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT); - } - } - if (useAttachedJavadoc) { + if (contentReader == null && useAttachedJavadoc) { // only if no source available // check parent IPackageFragmentRoot root= (IPackageFragmentRoot) fragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); @@ -261,18 +295,21 @@ public static Reader getHTMLContentReader(IPackageFragment fragment, boolean use if (isBinary) { String s= fragment.getAttachedJavadoc(null); if (s != null) { - try { - return new JavaDoc2MarkdownConverter(new StringReader(s)).getAsReader(); - } catch (IOException e) { - throw new JavaModelException(e, IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT); - } + contentReader = new StringReader(s); } } } + if (contentReader != null) { + try { + return new JavaDoc2MarkdownConverter(contentReader).getAsReader(); + } catch (IOException e) { + throw new JavaModelException(e, IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT); + } + } return null; } - private static Reader findDocInHierarchy(IMethod method, boolean isHTML, boolean useAttachedJavadoc) throws JavaModelException { + private static Reader findDocInHierarchy(IMethod method, boolean useAttachedJavadoc) throws JavaModelException { /* * Catch ExternalJavaProject in which case * no hierarchy can be built. @@ -287,16 +324,10 @@ private static Reader findDocInHierarchy(IMethod method, boolean isHTML, boolean MethodOverrideTester tester= new MethodOverrideTester(type, hierarchy); IType[] superTypes= hierarchy.getAllSupertypes(type); - for (int i= 0; i < superTypes.length; i++) { - IType curr= superTypes[i]; + for (IType curr : superTypes) { IMethod overridden= tester.findOverriddenMethodInType(curr, method); if (overridden != null) { - Reader reader; - if (isHTML) { - reader= getHTMLContentReader(overridden, false, useAttachedJavadoc); - } else { - reader= getContentReader(overridden, false); - } + Reader reader = getHTMLContentReader(overridden, false, useAttachedJavadoc); if (reader != null) { return reader; } diff --git a/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/Baz.java b/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/Baz.java new file mode 100644 index 0000000000..e0056bd001 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/Baz.java @@ -0,0 +1,6 @@ +package java; + +import java.internal.InternalBar; + +public class Baz extends InternalBar { +} \ No newline at end of file diff --git a/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/internal/InternalBar.java b/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/internal/InternalBar.java new file mode 100644 index 0000000000..35ccd1be55 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/internal/InternalBar.java @@ -0,0 +1,10 @@ +package java.internal; + +import org.springframework.core.SpringVersion; + +public class InternalBar { + + public static void main(String[] args) { + System.err.println(SpringVersion.getVersion()); + } +} diff --git a/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/internal/package-info.java b/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/internal/package-info.java new file mode 100644 index 0000000000..3a33005085 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/projects/eclipse/hello/src/java/internal/package-info.java @@ -0,0 +1,4 @@ +/** + * this is a bold package! + */ +package java.internal; \ No newline at end of file diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java index 7f41983feb..2844f9ff23 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandlerTest.java @@ -18,6 +18,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collections; @@ -35,10 +38,10 @@ import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; +import org.junit.Before; import org.junit.ComparisonFailure; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; /** @@ -64,6 +67,11 @@ public class CompletionHandlerTest extends AbstractCompletionBasedTest { " \"jsonrpc\": \"2.0\"\n" + "}"; + @Before + public void setUp() { + mockLSP3Client(); + } + //FIXME Something very fishy here: when run from command line as part of the whole test suite, //no completions are returned maybe 80% of the time if this method runs first in this class, //i.e. if this method is named testCompletion_1. It seems to fail in the IDE too but *very* @@ -107,10 +115,6 @@ public void testCompletion_object() throws Exception{ @Test public void testCompletion_constructor() throws Exception{ - ClientPreferences mockCapabilies = Mockito.mock(ClientPreferences.class); - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - Mockito.when(mockCapabilies.isCompletionSnippetsSupported()).thenReturn(Boolean.TRUE); - ICompilationUnit unit = getWorkingCopy( "src/java/Foo.java", "public class Foo {\n"+ @@ -129,8 +133,6 @@ public void testCompletion_constructor() throws Exception{ CompletionItem ctor = items.get(0); assertEquals("Object()", ctor.getLabel()); - - CompletionItem resolvedItem = server.resolveCompletionItem(ctor).join(); assertNotNull(resolvedItem); TextEdit te = resolvedItem.getTextEdit(); @@ -142,16 +144,11 @@ public void testCompletion_constructor() throws Exception{ assertEquals(17, range.getStart().getCharacter()); assertEquals(2, range.getEnd().getLine()); assertEquals(18, range.getEnd().getCharacter()); - - } @Test public void testCompletion_import_package() throws JavaModelException{ - ClientPreferences mockCapabilies = Mockito.mock(ClientPreferences.class); - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - ICompilationUnit unit = getWorkingCopy( "src/java/Foo.java", "import java.sq \n" + @@ -190,12 +187,7 @@ public void testCompletion_import_package() throws JavaModelException{ @Test public void testCompletion_method_withLSPV2() throws JavaModelException{ - - ClientPreferences mockCapabilies = Mockito.mock(ClientPreferences.class); - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - Mockito.when(mockCapabilies.isCompletionSnippetsSupported()).thenReturn(Boolean.FALSE); - Mockito.when(mockCapabilies.isSignatureHelpSupported()).thenReturn(Boolean.FALSE); - + mockLSP2Client(); ICompilationUnit unit = getWorkingCopy( "src/java/Foo.java", @@ -210,7 +202,6 @@ public void testCompletion_method_withLSPV2() throws JavaModelException{ int[] loc = findCompletionLocation(unit, "map.pu"); - CompletionList list = server.completion(JsonMessageHelper.getParams(createCompletionRequest(unit, loc[0], loc[1]))).join().getRight(); assertNotNull(list); CompletionItem ci = list.getItems().stream() @@ -233,14 +224,6 @@ public void testCompletion_method_withLSPV2() throws JavaModelException{ @Test public void testCompletion_method_withLSPV3() throws JavaModelException{ - - //Mock the preference manager to use LSP v3 support. - ClientPreferences mockCapabilies = Mockito.mock(ClientPreferences.class); - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - Mockito.when(mockCapabilies.isCompletionSnippetsSupported()).thenReturn(Boolean.TRUE); - Mockito.when(mockCapabilies.isSignatureHelpSupported()).thenReturn(Boolean.TRUE); - - ICompilationUnit unit = getWorkingCopy( "src/java/Foo.java", "public class Foo {\n"+ @@ -254,7 +237,6 @@ public void testCompletion_method_withLSPV3() throws JavaModelException{ int[] loc = findCompletionLocation(unit, "map.pu"); - CompletionList list = server.completion(JsonMessageHelper.getParams(createCompletionRequest(unit, loc[0], loc[1]))).join().getRight(); assertNotNull(list); CompletionItem ci = list.getItems().stream() @@ -282,9 +264,6 @@ public void testCompletion_method_withLSPV3() throws JavaModelException{ @Test public void testCompletion_field() throws JavaModelException{ - ClientPreferences mockCapabilies = Mockito.mock(ClientPreferences.class); - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - ICompilationUnit unit = getWorkingCopy( "src/java/Foo.java", "import java.sq \n" + @@ -315,15 +294,6 @@ public void testCompletion_field() throws JavaModelException{ @Test public void testCompletion_import_type() throws JavaModelException{ - ClientPreferences mockCapabilies = Mockito.mock(ClientPreferences.class); - //Mock the preference manager to use LSP v3 support. - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - Mockito.when(mockCapabilies.isCompletionSnippetsSupported()).thenReturn(Boolean.TRUE); - Mockito.when(mockCapabilies.isSignatureHelpSupported()).thenReturn(Boolean.TRUE); - - - Mockito.when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); - ICompilationUnit unit = getWorkingCopy( "src/java/Foo.java", "import java.sq \n" + @@ -366,10 +336,52 @@ public void testCompletion_noPackage() throws Exception{ } + @Test + public void testCompletion_plainTextDoc() throws Exception{ + ICompilationUnit unit = getWorkingCopy( + "src/java/Foo.java", + "import java.sq \n" + + "public class Foo {\n"+ + " void foo() {\n"+ + " zz\n"+ + " }\n\" }\\n\"+"+ + "\n"+ + "/** This should be plain.*/\n" + + " void zzz() {}\n"+ + "}\n"); + int[] loc = findCompletionLocation(unit, " zz"); + CompletionList list = server.completion(JsonMessageHelper.getParams(createCompletionRequest(unit, loc[0], loc[1]))).join().getRight(); + assertNotNull(list); + assertFalse("No proposals were found", list.getItems().isEmpty()); + CompletionItem item = list.getItems().get(0); + assertEquals("zzz() : void", item.getLabel()); + + CompletionItem resolvedItem = server.resolveCompletionItem(item).join(); + assertEquals("This should be plain.", resolvedItem.getDocumentation()); + } + private String createCompletionRequest(ICompilationUnit unit, int line, int kar) { return COMPLETION_TEMPLATE.replace("${file}", JDTUtils.getFileURI(unit)) .replace("${line}", String.valueOf(line)) .replace("${char}", String.valueOf(kar)); } + + private void mockLSP3Client() { + mockLSPClient(true, true); + } + + private void mockLSP2Client() { + mockLSPClient(false, false); + } + + private void mockLSPClient(boolean isSnippetSupported, boolean isSignatureHelpSuported) { + reset(preferenceManager); + ClientPreferences mockCapabilies = mock(ClientPreferences.class); + // Mock the preference manager to use LSP v3 support. + when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); + when(mockCapabilies.isCompletionSnippetsSupported()).thenReturn(isSnippetSupported); + when(mockCapabilies.isSignatureHelpSupported()).thenReturn(isSignatureHelpSuported); + when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies); + } } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java index 0348e47045..4ab8383074 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/HoverHandlerTest.java @@ -91,6 +91,22 @@ public void testHoverStandalone() throws Exception { assertEquals("Unexpected hover "+result, "This is foo", result); } + @Test + public void testHoverPackage() throws Exception { + // given + // Hovers on the java.internal package + String payload = createHoverRequest("src/java/Baz.java", 2, 16); + TextDocumentPositionParams position = getParams(payload); + + // when + Hover hover = handler.hover(position).get(); + + // then + assertNotNull(hover); + String result = hover.getContents().get(0).getLeft();// + assertEquals("Unexpected hover ", "this is a **bold** package!", result); + } + @Test public void testEmptyHover() throws Exception { //given diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/javadoc/AbstractJavadocConverterTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/javadoc/AbstractJavadocConverterTest.java new file mode 100644 index 0000000000..43fcdefc3e --- /dev/null +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/javadoc/AbstractJavadocConverterTest.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2017 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.javadoc; + +import java.io.IOException; + +public abstract class AbstractJavadocConverterTest { + + /** + * This Javadoc contains some code , a link to + * {@link IOException} and a table + * + * + * + * + * + * + * + * + * + * + * + * + *
header 1header 2
data 1data 2
+ *
+ * {@literal literal} and now a list: + * + *