diff --git a/eclipse-language-servers/org.springframework.tooling.bosh.ls.feature/.project b/eclipse-language-servers/org.springframework.tooling.bosh.ls.feature/.project index 7240fb9e1..d9b05bb3f 100644 --- a/eclipse-language-servers/org.springframework.tooling.bosh.ls.feature/.project +++ b/eclipse-language-servers/org.springframework.tooling.bosh.ls.feature/.project @@ -10,8 +10,14 @@ + + org.eclipse.pde.FeatureBuilder + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.FeatureNature diff --git a/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.feature/.project b/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.feature/.project index b793c0e6d..d0f37da51 100644 --- a/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.feature/.project +++ b/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.feature/.project @@ -10,8 +10,14 @@ + + org.eclipse.pde.FeatureBuilder + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.FeatureNature diff --git a/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.integration.feature/.project b/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.integration.feature/.project index fe091070d..f4c31df09 100644 --- a/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.integration.feature/.project +++ b/eclipse-language-servers/org.springframework.tooling.cloudfoundry.manifest.ls.integration.feature/.project @@ -10,8 +10,14 @@ + + org.eclipse.pde.FeatureBuilder + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.FeatureNature diff --git a/eclipse-language-servers/org.springframework.tooling.concourse.ls.feature/.project b/eclipse-language-servers/org.springframework.tooling.concourse.ls.feature/.project index ed9cc4297..9c1ea2e26 100644 --- a/eclipse-language-servers/org.springframework.tooling.concourse.ls.feature/.project +++ b/eclipse-language-servers/org.springframework.tooling.concourse.ls.feature/.project @@ -10,8 +10,14 @@ + + org.eclipse.pde.FeatureBuilder + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.pde.FeatureNature diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.classpath b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.classpath index eca7bdba8..d215ae19d 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.classpath +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.classpath @@ -2,6 +2,8 @@ - - + + + + diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.gitignore b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.gitignore new file mode 100644 index 000000000..12c18d4ed --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.gitignore @@ -0,0 +1 @@ +/lib/ diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.project b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.project index 8e91c72d0..63058ad80 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.project +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.project @@ -20,8 +20,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF index 312fb2727..17438f583 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/META-INF/MANIFEST.MF @@ -16,9 +16,14 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.8.0", org.eclipse.debug.ui, org.eclipse.ui.console, org.springframework.tooling.jdt.ls.commons, - org.eclipse.ui.ide + org.eclipse.ui.ide, + org.eclipse.jdt.core, + org.eclipse.jdt.ui Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Export-Package: org.springframework.tooling.ls.eclipse.commons, org.springframework.tooling.ls.eclipse.commons.console.preferences Bundle-Activator: org.springframework.tooling.ls.eclipse.commons.LanguageServerCommonsActivator +Bundle-ClassPath: ., + lib/remark-1.0.0.jar, + lib/jsoup-1.9.2.jar diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/build.properties b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/build.properties index 6c480f39f..eec6612ee 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/build.properties +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/build.properties @@ -3,4 +3,6 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ plugin.xml,\ - icons/ + icons/,\ + lib/remark-1.0.0.jar,\ + lib/jsoup-1.9.2.jar diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/pom.xml b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/pom.xml index 137d90143..bc33b4be4 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/pom.xml +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/pom.xml @@ -12,4 +12,58 @@ org.springframework.tooling.ls.eclipse.commons eclipse-plugin + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + + ${basedir}/lib + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + get-libs + + copy + + validate + + + + false + + + com.kotcrab.remark + remark + 1.0.0 + + + org.jsoup + jsoup + 1.9.2 + + + ${basedir}/lib/ + + true + + + + + + diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/JavadocParams.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/JavadocParams.java new file mode 100644 index 000000000..7f6a737b3 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/JavadocParams.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.ls.eclipse.commons; + +public class JavadocParams { + + private String projectUri; + private String bindingKey; + + public JavadocParams(String projectUri, String bindingKey) { + super(); + this.projectUri = projectUri; + this.bindingKey = bindingKey; + } + public String getProjectUri() { + return projectUri; + } + public void setProjectUri(String projectUri) { + this.projectUri = projectUri; + } + public String getBindingKey() { + return bindingKey; + } + public void setBindingKey(String bindingKey) { + this.bindingKey = bindingKey; + } + + @Override + public String toString() { + return "JavadocParams [projectUri=" + projectUri + ", bindingKey=" + bindingKey + "]"; + } + +} diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java index bc4c2592b..727e3d71f 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/LanguageServerCommonsActivator.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.springframework.tooling.ls.eclipse.commons; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; @@ -19,10 +21,10 @@ public class LanguageServerCommonsActivator extends AbstractUIPlugin { public LanguageServerCommonsActivator() { } - + @Override public void start(BundleContext context) throws Exception { - instance = this; + instance = this; super.start(context); } @@ -30,5 +32,7 @@ public class LanguageServerCommonsActivator extends AbstractUIPlugin { return instance; } - + public static void logError(Throwable t, String message) { + instance.getLog().log(new Status(IStatus.ERROR, instance.getBundle().getSymbolicName(), message, t)); + } } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClient.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClient.java index 4b8428974..669f13e02 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClient.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClient.java @@ -15,6 +15,7 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.services.LanguageClient; +import org.springframework.tooling.jdt.ls.commons.javadoc.JavadocResponse; /** * Some 'custom' extensions to standard LSP {@link LanguageClient}. @@ -37,5 +38,8 @@ public interface STS4LanguageClient extends LanguageClient { @JsonRequest("sts/removeClasspathListener") CompletableFuture removeClasspathListener(ClasspathListenerParams classpathListenerParams); - + + @JsonRequest("sts/javadoc") + CompletableFuture javadoc(JavadocParams params); + } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java index fc775285c..796f80056 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/STS4LanguageClientImpl.java @@ -43,6 +43,9 @@ import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.texteditor.AbstractTextEditor; import org.springframework.tooling.jdt.ls.commons.Logger; import org.springframework.tooling.jdt.ls.commons.classpath.ReusableClasspathListenerHandler; +import org.springframework.tooling.jdt.ls.commons.javadoc.JavadocResponse; +import org.springframework.tooling.jdt.ls.commons.javadoc.JavadocUtils; +import org.springframework.tooling.ls.eclipse.commons.javadoc.JavaDoc2MarkdownConverter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -207,4 +210,18 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La public CompletableFuture removeClasspathListener(ClasspathListenerParams params) { return CompletableFuture.completedFuture(classpathService.removeClasspathListener(params.getCallbackCommandId())); } + + @Override + public CompletableFuture javadoc(JavadocParams params) { + JavadocResponse response = new JavadocResponse(); + try { + String content = JavadocUtils.javadoc(JavaDoc2MarkdownConverter::getMarkdownContentReader, + URI.create(params.getProjectUri()), params.getBindingKey()); + response.setContent(content); + } catch (Exception e) { + LanguageServerCommonsActivator.logError(e, "Failed getting javadoc for " + params.toString()); + } + return CompletableFuture.completedFuture(response); + } + } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/javadoc/AbstractJavaDocConverter.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/javadoc/AbstractJavaDocConverter.java new file mode 100644 index 000000000..5a1b0c6e3 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/javadoc/AbstractJavaDocConverter.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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.springframework.tooling.ls.eclipse.commons.javadoc; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import org.eclipse.jdt.internal.ui.text.javadoc.JavaDoc2HTMLTextReader; + +/* Copied from JDT LS */ + +/** + * 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/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/javadoc/JavaDoc2MarkdownConverter.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/javadoc/JavaDoc2MarkdownConverter.java new file mode 100644 index 000000000..73df45b42 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/src/org/springframework/tooling/ls/eclipse/commons/javadoc/JavaDoc2MarkdownConverter.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * 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.springframework.tooling.ls.eclipse.commons.javadoc; + +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Field; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; +import org.jsoup.safety.Cleaner; +import org.jsoup.safety.Whitelist; +import org.springframework.tooling.ls.eclipse.commons.LanguageServerCommonsActivator; + +import com.overzealous.remark.Options; +import com.overzealous.remark.Options.Tables; +import com.overzealous.remark.Remark; + +/* Copied from JDT LS */ +/** + * Converts JavaDoc tags into Markdown equivalent. + * + * @author Fred Bricon + */ +public class JavaDoc2MarkdownConverter extends AbstractJavaDocConverter { + + private static Remark remark; + + static { + Options options = new Options(); + options.tables = Tables.CONVERT_TO_CODE_BLOCK; + options.hardwraps = true; + options.inlineLinks = true; + options.autoLinks = true; + options.reverseHtmlSmartPunctuation = true; + remark = new Remark(options); + //Stop remark from stripping file and jdt protocols in an href + try { + Field cleanerField = Remark.class.getDeclaredField("cleaner"); + cleanerField.setAccessible(true); + + Cleaner c = (Cleaner) cleanerField.get(remark); + + Field whitelistField = Cleaner.class.getDeclaredField("whitelist"); + whitelistField.setAccessible(true); + + Whitelist w = (Whitelist) whitelistField.get(c); + + w.addProtocols("a", "href", "file", "jdt"); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + LanguageServerCommonsActivator.logError(e, "Unable to modify jsoup to include file and jdt protocols"); + } + } + + public JavaDoc2MarkdownConverter(Reader reader) { + super(reader); + } + + public JavaDoc2MarkdownConverter(String javadoc) { + super(javadoc); + } + + @Override + String convert(String rawHtml) { + return remark.convert(rawHtml); + } + + public static Reader getMarkdownContentReader(IJavaElement element) { + + try { + String rawHtml = JavadocContentAccess2.getHTMLContent(element, true); + Reader markdownReader = new JavaDoc2MarkdownConverter(rawHtml).getAsReader(); + return markdownReader; + } catch (IOException | CoreException e) { + + } + + return null; + } + + +} diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java index 323bac78d..6381f0b36 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java @@ -13,7 +13,7 @@ package org.springframework.ide.vscode.commons.gradle; import java.io.File; import java.nio.file.Path; -import org.springframework.ide.vscode.commons.java.AbstractJavaProject; +import org.springframework.ide.vscode.commons.java.LegacyJavaProject; import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; import org.springframework.ide.vscode.commons.java.IClasspath; @@ -26,7 +26,7 @@ import org.springframework.ide.vscode.commons.util.Log; * @author Alex Boyko * */ -public class GradleJavaProject extends AbstractJavaProject { +public class GradleJavaProject extends LegacyJavaProject { private GradleJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File projectDir) { super(fileObserver, projectDir.toURI(), projectDataCache, classpath); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/AnnotationImpl.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/AnnotationImpl.java index bb47e02d4..70aa96da4 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/AnnotationImpl.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/AnnotationImpl.java @@ -20,10 +20,10 @@ import org.springframework.ide.vscode.commons.java.IMemberValuePair; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; public class AnnotationImpl implements IAnnotation { - + private AnnotationInstance annotation; private IJavadocProvider javadocProvider; - + AnnotationImpl(AnnotationInstance annotation, IJavadocProvider javadocProvider) { this.annotation = annotation; this.javadocProvider = javadocProvider; @@ -50,7 +50,7 @@ public class AnnotationImpl implements IAnnotation { return Wrappers.wrap(av); }); } - + @Override public String toString() { return annotation.toString(); @@ -69,4 +69,9 @@ public class AnnotationImpl implements IAnnotation { return super.equals(obj); } + @Override + public String getBindingKey() { + return BindingKeyUtils.getBindingKey(annotation); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/BasicJandexIndex.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/BasicJandexIndex.java new file mode 100644 index 000000000..193b17792 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/BasicJandexIndex.java @@ -0,0 +1,282 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.jandex; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexReader; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Indexer; +import org.jboss.jandex.JarIndexer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.util.FuzzyMatcher; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuple3; +import reactor.util.function.Tuples; + +/** + * Basic Jandex Index using only Reactor Flux and Jandex only constructs + * independent of Javadoc provider logic. Thus this index can be shared via a + * static object + * + * @author Alex Boyko + * + */ +public class BasicJandexIndex { + + private static final Logger log = LoggerFactory.getLogger(BasicJandexIndex.class); + + private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; + + @FunctionalInterface + public static interface IndexFileFinder { + File findIndexFile(File jarFile); + } + + public static File getIndexFolder() { + File folder = new File(System.getProperty(JAVA_IO_TMPDIR), "jandex"); + if (!folder.isDirectory()) { + folder.mkdirs(); + } + return folder; + } + + private Map>> index; + + private Map>>> knownTypes; + + private Map>> knownPackages; + + private BasicJandexIndex[] baseIndex; + + BasicJandexIndex(Collection classpathEntries, IndexFileFinder indexFileFinder, + BasicJandexIndex... baseIndex) { + this.baseIndex = baseIndex; + this.index = new ConcurrentHashMap<>(); + this.knownTypes = new HashMap<>(); + this.knownPackages = new HashMap<>(); + classpathEntries.forEach(file -> { + index.put(file, /*Suppliers.synchronizedSupplier(*/Suppliers.memoize(() -> createIndex(file, indexFileFinder))/*)*/); + knownTypes.put(file, Suppliers.memoize(() -> getKnownTypesStream(file).collect(Collectors.toList()))); + knownPackages.put(file, Suppliers.memoize(() -> getKnownPackages(file).collect(Collectors.toList()))); + }); + } + + private Optional createIndex(File file, IndexFileFinder indexFileFinder) { + if (file != null && file.isFile() && file.getName().endsWith(".jar")) { + return indexJar(file, indexFileFinder); + } else if (file != null && file.isDirectory()) { + return indexFolder(file); + } else { + return Optional.empty(); + } + } + + private static Optional indexFolder(File folder) { + Indexer indexer = new Indexer(); + for (Iterator itr = com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(folder) + .iterator(); itr.hasNext();) { + File file = itr.next(); + if (file.isFile() && file.getName().endsWith(".class")) { + try { + final InputStream stream = new FileInputStream(file); + try { + indexer.index(stream); + } finally { + try { + stream.close(); + } catch (Exception ignore) { + } + } + } catch (Exception e) { + log.error("Failed to index file " + file, e); + } + } + } + return Optional.of(indexer.complete()); + } + + private static Optional indexJar(File file, IndexFileFinder indexFileFinder) { + File indexFile = indexFileFinder.findIndexFile(file); + if (indexFile != null) { + try { + if (!indexFile.getParentFile().exists()) { + indexFile.getParentFile().mkdirs(); + } + if (indexFile.createNewFile()) { + try { + return Optional.of(JarIndexer.createJarIndex(file, new Indexer(), indexFile, false, false, + false, System.out, System.err).getIndex()); + } catch (IOException e) { + log.error("Failed to index '" + file + "'", e); + } + } else { + try { + return Optional.of(new IndexReader(new FileInputStream(indexFile)).read()); + } catch (IOException e) { + log.error("Failed to read index file '" + indexFile + "'. Creating new index file.", e); + if (indexFile.delete()) { + return indexJar(file, indexFileFinder); + } else { + log.error("Failed to read index file '" + indexFile); + } + } + } + } catch (IOException e) { + log.error("Unable to create index file '" + indexFile + "'", e); + } + } else { + try { + return Optional.of(JarIndexer + .createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false) + .getIndex()); + } catch (IOException e) { + log.error("Failed to index '" + file + "'", e); + } + } + return Optional.empty(); + } + + Tuple2 getClassByName(DotName fqName) { + // First look for type in the base index array + return (baseIndex == null ? Stream.>empty() + : Arrays.stream( + baseIndex) + .filter( + jandexIndex -> jandexIndex != null) + .map(jandexIndex -> jandexIndex.getClassByName(fqName))).filter(type -> type != null) + .findFirst() + // If not found look at indices owned by this + // JandexIndex instance + .orElseGet(() -> streamOfIndices() + .map(e -> Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName)))) + .filter(t -> t.getT2().isPresent()) + .map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst() + .orElse(null)); + + } + + private Optional> findMatch(DotName fqName) { + return (baseIndex == null ? Stream.>>empty() + : Arrays.stream( + baseIndex) + .filter( + jandexIndex -> jandexIndex != null) + .map(jandexIndex -> jandexIndex.findMatch(fqName))).filter(o -> o.isPresent()) + .findFirst() + // If not found look at indices owned by this + // JandexIndex instance + .orElseGet(() -> streamOfIndices() + .map(e -> { + IndexView view = e.getT2(); + ClassInfo info = view.getClassByName(fqName); + return Tuples.of(e.getT1(), Optional.ofNullable(info)); +// return Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName))); + }) + .filter(t -> t.getT2().isPresent()) + .map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst()); + } + + public Optional findClasspathResourceForType(String fqName) { + Optional> match = findMatch(DotName.createSimple(fqName)); + return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null); + } + + private Stream> streamOfIndices() { + return index.entrySet().parallelStream().map(e -> Tuples.of(e.getKey(), e.getValue().get())) + .filter(t -> t.getT2().isPresent()).map(t -> Tuples.of(t.getT1(), t.getT2().get())); + } + + private Stream> getKnownTypesStream(File file) { + Optional indexView = index.get(file).get(); + if (indexView.isPresent()) { + return indexView.get().getKnownClasses().parallelStream() + .map(info -> Tuples.of(info.name().toString(), file, info)); + } + return Stream.empty(); + } + + private final Stream getKnownPackages(File file) { + Optional indexView = index.get(file).get(); + if (indexView.isPresent()) { + return indexView.get().getKnownClasses().parallelStream().map(info -> { + String name = info.name().toString(); + return name.substring(0, name.lastIndexOf('.')); + }).distinct(); + } + return Stream.empty(); + } + + Flux> fuzzySearchTypes(String searchTerm) { + Flux> flux = Flux.fromIterable(knownTypes.values()).publishOn(Schedulers.parallel()) + .flatMap(s -> Flux.fromIterable(s.get())) + .map(t -> Tuples.of(t.getT2(), t.getT3(), FuzzyMatcher.matchScore(searchTerm, t.getT1()))) + .filter(t -> t.getT3() != 0.0); + if (baseIndex == null) { + return flux; + } else { + return Flux.merge(flux, + Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchTypes(searchTerm))); + } + } + + public Flux> fuzzySearchPackages(String searchTerm) { + Flux> flux = Flux.fromIterable(knownPackages.values()).publishOn(Schedulers.parallel()) + .flatMap(s -> Flux.fromIterable(s.get())) + .map(pkg -> Tuples.of(pkg, FuzzyMatcher.matchScore(searchTerm, pkg))).filter(t -> t.getT2() != 0.0); + if (baseIndex == null) { + return flux; + } else { + return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchPackages(searchTerm))); + } + } + + Flux> allSubtypesOf(DotName name, boolean isInterface) { + Flux> flux = Flux.fromIterable(index.keySet()).publishOn(Schedulers.parallel()).flatMap(file -> { + Optional optional = index.get(file).get(); + if (optional.isPresent()) { + return Flux + .fromIterable(isInterface ? optional.get().getAllKnownImplementors(name) + : optional.get().getAllKnownSubclasses(name)) + .publishOn(Schedulers.parallel()).map(info -> Tuples.of(file, info)); + } else { + return Flux.empty(); + } + }); + if (baseIndex == null) { + return flux; + } else { + return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.allSubtypesOf(name, isInterface))); + } + } +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/BindingKeyUtils.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/BindingKeyUtils.java new file mode 100644 index 000000000..845539cff --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/BindingKeyUtils.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.jandex; + +import java.util.stream.Collectors; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ArrayType; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassType; +import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.ParameterizedType; +import org.jboss.jandex.PrimitiveType; +import org.jboss.jandex.Type; +import org.jboss.jandex.TypeVariable; +import org.jboss.jandex.UnresolvedTypeVariable; +import org.jboss.jandex.VoidType; +import org.jboss.jandex.WildcardType; + +class BindingKeyUtils { + + public static String getBindingKey(ClassInfo info) { + StringBuilder sb = new StringBuilder(); + sb.append('L'); + sb.append(info.toString().replace('.', '/')); + sb.append(';'); + return sb.toString(); + } + + public static String getBindingKey(AnnotationInstance annotation) { + return annotation.name().toString(); + } + + public static String getBindingKey(FieldInfo field) { + StringBuilder sb = new StringBuilder(); + sb.append(getBindingKey(field.declaringClass())); + sb.append('.'); + sb.append(field.name()); + return sb.toString(); + } + + public static String getBindingKey(MethodInfo method) { + StringBuilder sb = new StringBuilder(); + sb.append(getBindingKey(method.declaringClass())); + sb.append('.'); + sb.append(method.name()); + sb.append('('); + for (Type parameter : method.parameters()) { + sb.append(getGeneralTypeBindingKey(parameter)); + } + sb.append(')'); + sb.append(getGeneralTypeBindingKey(method.returnType())); + return method.name().toString(); + } + + public static String getGeneralTypeBindingKey(Type type) { + switch (type.kind()) { + case ARRAY: + return getBindingKey(type.asArrayType()); + case CLASS: + return getBindingKey(type.asClassType()); + case PARAMETERIZED_TYPE: + return getBindingKey(type.asParameterizedType()); + case PRIMITIVE: + return getBindingKey(type.asPrimitiveType()); + case TYPE_VARIABLE: + return getBindingKey(type.asTypeVariable()); + case UNRESOLVED_TYPE_VARIABLE: + return getBindingKey(type.asUnresolvedTypeVariable()); + case VOID: + return getBindingKey(type.asVoidType()); + case WILDCARD_TYPE: + return getBindingKey(type.asWildcardType()); + default: + break; + } + return ""; + } + + private static String getBindingKey(WildcardType type) { + if (type.extendsBound() != null) { + return "+" + getGeneralTypeBindingKey(type.extendsBound()); + } else if (type.superBound() != null) { + return "-" + getGeneralTypeBindingKey(type.superBound()); + } else { + return "*"; + } + } + + private static String getBindingKey(ParameterizedType type) { + StringBuilder sb = new StringBuilder(); + sb.append(type.owner() == null ? "L" + type.name().toString().replace('.', '/') : getGeneralTypeBindingKey(type.owner())); + sb.append('<'); + for (Type argument : type.arguments()) { + sb.append(getGeneralTypeBindingKey(argument)); + } + sb.append('>'); + return sb.toString(); + } + + private static String getBindingKey(TypeVariable type) { + StringBuilder sb = new StringBuilder(); + sb.append('T'); + sb.append(type.identifier()); + sb.append(type.bounds().stream().map(BindingKeyUtils::getGeneralTypeBindingKey).collect(Collectors.joining(":"))); + sb.append(';'); + return sb.toString(); + } + + private static String getBindingKey(ArrayType type) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < type.dimensions(); i++) { + sb.append('['); + } + sb.append(getGeneralTypeBindingKey(type.component())); + return null; + } + + private static String getBindingKey(ClassType type) { + StringBuilder sb = new StringBuilder(); + sb.append('L'); + sb.append(type.name().toString().replace('.', '/')); + sb.append(';'); + return sb.toString(); + } + + private static String getBindingKey(PrimitiveType primitive) { + if (primitive == PrimitiveType.BYTE) { + return "B"; + } else if (primitive == PrimitiveType.CHAR) { + return "C"; + } else if (primitive == PrimitiveType.DOUBLE) { + return "D"; + } else if (primitive == PrimitiveType.FLOAT) { + return "F"; + } else if (primitive == PrimitiveType.INT) { + return "I"; + } else if (primitive == PrimitiveType.LONG) { + return "J"; + } else if (primitive == PrimitiveType.SHORT) { + return "S"; + } + + // BOOLEAN + return "Z"; + } + + private static String getBindingKey(UnresolvedTypeVariable type) { + return "Q" + type.name().toString(); + } + + private static String getBindingKey(VoidType type) { + return "V"; + } + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/FieldImpl.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/FieldImpl.java index 20059faba..64dfca9c4 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/FieldImpl.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/FieldImpl.java @@ -22,13 +22,13 @@ import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; class FieldImpl implements IField { - - private JandexIndex index; + private FieldInfo field; private IJavadocProvider javadocProvider; - - FieldImpl(JandexIndex index, FieldInfo field, IJavadocProvider javadocProvider) { - this.index = index; + private IType declaringType; + + FieldImpl(IType declaringType, FieldInfo field, IJavadocProvider javadocProvider) { + this.declaringType = declaringType; this.field = field; this.javadocProvider = javadocProvider; } @@ -40,7 +40,7 @@ class FieldImpl implements IField { @Override public IType getDeclaringType() { - return Wrappers.wrap(index, field.declaringClass(), javadocProvider); + return declaringType; } @Override @@ -69,7 +69,7 @@ class FieldImpl implements IField { public boolean isEnumConstant() { return Flags.isEnum(field.flags()); } - + @Override public String toString() { return field.toString(); @@ -87,7 +87,10 @@ class FieldImpl implements IField { } return super.equals(obj); } - - + + @Override + public String getBindingKey() { + return BindingKeyUtils.getBindingKey(field); + } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java index 68f42f005..f75377567 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java @@ -23,12 +23,11 @@ import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.jandex.JandexIndex.JavadocProviderFactory; import org.springframework.ide.vscode.commons.java.ClasspathIndex; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IClasspathUtil; -import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.util.CollectorUtil; @@ -53,8 +52,6 @@ public final class JandexClasspath implements ClasspathIndex { public static final Logger log = LoggerFactory.getLogger(JandexClasspath.class); - public static JavadocProviderTypes providerType = JavadocProviderTypes.HTML; - public enum JavadocProviderTypes { // JAVA_PARSER, //Used to be based on githb java parser. If need something back that can extract docs from source code, we have to implement // based on JDT parser. But at the moment this wasn't being used so just got removed. @@ -64,10 +61,12 @@ public final class JandexClasspath implements ClasspathIndex { private Supplier javaIndex; private final IClasspath classpath; private final FileObserver fileObserver; + private final JavadocProviderFactory javadocProviderFactory; - public JandexClasspath(IClasspath classpath, FileObserver fileObserver) { + public JandexClasspath(IClasspath classpath, FileObserver fileObserver, JavadocProviderFactory javadocProviderFactory) { this.fileObserver = fileObserver; this.classpath = classpath; + this.javadocProviderFactory = javadocProviderFactory; this.javaIndex = Suppliers.synchronizedSupplier(Suppliers.memoize(() -> createIndex())); } @@ -80,16 +79,7 @@ public final class JandexClasspath implements ClasspathIndex { } catch (Exception e) { log.error("Cannot obtain binary root from classpath entries for " + classpath.getName(), e); } - return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), classpathResource -> { - switch (providerType) { -// case JAVA_PARSER: -// return createParserJavadocProvider(classpathResource); - case HTML: - return createHtmlJavdocProvider(classpathResource); - default: - throw new IllegalStateException("Missing switch case?"); - } - }, getBaseIndices()); + return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), javadocProviderFactory, getBaseIndices()); } private Disposable.Composite subscriptions = Disposables.composite(); @@ -115,18 +105,13 @@ public final class JandexClasspath implements ClasspathIndex { } } - private IJavadocProvider createHtmlJavdocProvider(File binaryClasspathRoot) { - CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, binaryClasspathRoot); - return JavaDocProviders.createFor(cpe); - } - @Override public Optional sourceContainer(File binaryClasspathRoot) { CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, binaryClasspathRoot); return cpe == null ? Optional.empty() : Optional.ofNullable(cpe.getSourceContainerUrl()); } - protected JandexIndex[] getBaseIndices() { + protected BasicJandexIndex[] getBaseIndices() { return JandexSystemLibsIndex.getInstance().fromJars(IClasspathUtil.getBinaryRoots(classpath, CPE::isSystem)); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java index f6cd9e815..23e734c01 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java @@ -12,28 +12,12 @@ package org.springframework.ide.vscode.commons.jandex; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; -import org.jboss.jandex.IndexReader; -import org.jboss.jandex.IndexView; -import org.jboss.jandex.Indexer; -import org.jboss.jandex.JarIndexer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.IAnnotation; @@ -42,42 +26,23 @@ import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; -import org.springframework.ide.vscode.commons.util.FuzzyMatcher; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; import reactor.util.function.Tuple2; import reactor.util.function.Tuples; -public class JandexIndex { +public class JandexIndex extends BasicJandexIndex { private static final Logger log = LoggerFactory.getLogger(JandexIndex.class); - private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; - - @FunctionalInterface - public static interface IndexFileFinder { - File findIndexFile(File jarFile); - } - @FunctionalInterface public static interface JavadocProviderFactory { IJavadocProvider createJavadocProvider(File jarContainer); } - public static File getIndexFolder() { - File folder = new File(System.getProperty(JAVA_IO_TMPDIR), "jandex"); - if (!folder.isDirectory()) { - folder.mkdirs(); - } - return folder; - } - private static final IJavadocProvider ABSENT_JAVADOC_PROVIDER = new IJavadocProvider() { @Override @@ -102,18 +67,10 @@ public class JandexIndex { }; - private Map>> index; - private JavadocProviderFactory javadocProviderFactory; - private Map>>> knownTypes; - - private Map>> knownPackages; - private Cache javadocProvidersCache = CacheBuilder.newBuilder().build(); - private JandexIndex[] baseIndex; - public void setJvadocProviderFactory(JavadocProviderFactory sourceContainerProvider) { this.javadocProviderFactory = sourceContainerProvider; } @@ -123,144 +80,19 @@ public class JandexIndex { } public JandexIndex(Collection classpathEntries, IndexFileFinder indexFileFinder, - JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) { - this.baseIndex = baseIndex; - this.index = new ConcurrentHashMap<>(); - this.knownTypes = new HashMap<>(); - this.knownPackages = new HashMap<>(); + JavadocProviderFactory javadocProviderFactory, BasicJandexIndex... baseIndex) { + super(classpathEntries, indexFileFinder, baseIndex); this.javadocProviderFactory = javadocProviderFactory; - classpathEntries.forEach(file -> { - index.put(file, /*Suppliers.synchronizedSupplier(*/Suppliers.memoize(() -> createIndex(file, indexFileFinder))/*)*/); - knownTypes.put(file, Suppliers.memoize(() -> getKnownTypesStream(file).collect(Collectors.toList()))); - knownPackages.put(file, Suppliers.memoize(() -> getKnownPackages(file).collect(Collectors.toList()))); - }); - } - - private Optional createIndex(File file, IndexFileFinder indexFileFinder) { - if (file != null && file.isFile() && file.getName().endsWith(".jar")) { - return indexJar(file, indexFileFinder); - } else if (file != null && file.isDirectory()) { - return indexFolder(file); - } else { - return Optional.empty(); - } - } - - private static Optional indexFolder(File folder) { - Indexer indexer = new Indexer(); - for (Iterator itr = com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(folder) - .iterator(); itr.hasNext();) { - File file = itr.next(); - if (file.isFile() && file.getName().endsWith(".class")) { - try { - final InputStream stream = new FileInputStream(file); - try { - indexer.index(stream); - } finally { - try { - stream.close(); - } catch (Exception ignore) { - } - } - } catch (Exception e) { - log.error("Failed to index file " + file, e); - } - } - } - return Optional.of(indexer.complete()); - } - - private static Optional indexJar(File file, IndexFileFinder indexFileFinder) { - File indexFile = indexFileFinder.findIndexFile(file); - if (indexFile != null) { - try { - if (!indexFile.getParentFile().exists()) { - indexFile.getParentFile().mkdirs(); - } - if (indexFile.createNewFile()) { - try { - return Optional.of(JarIndexer.createJarIndex(file, new Indexer(), indexFile, false, false, - false, System.out, System.err).getIndex()); - } catch (IOException e) { - log.error("Failed to index '" + file + "'", e); - } - } else { - try { - return Optional.of(new IndexReader(new FileInputStream(indexFile)).read()); - } catch (IOException e) { - log.error("Failed to read index file '" + indexFile + "'. Creating new index file.", e); - if (indexFile.delete()) { - return indexJar(file, indexFileFinder); - } else { - log.error("Failed to read index file '" + indexFile); - } - } - } - } catch (IOException e) { - log.error("Unable to create index file '" + indexFile + "'", e); - } - } else { - try { - return Optional.of(JarIndexer - .createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false) - .getIndex()); - } catch (IOException e) { - log.error("Failed to index '" + file + "'", e); - } - } - return Optional.empty(); } public IType findType(String fqName) { - return getClassByName(DotName.createSimple(fqName)); - } - - IType getClassByName(DotName fqName) { - // First look for type in the base index array - return (baseIndex == null ? Stream.empty() - : Arrays.stream( - baseIndex) - .filter( - jandexIndex -> jandexIndex != null) - .map(jandexIndex -> jandexIndex.getClassByName(fqName))).filter(type -> type != null) - .findFirst() - // If not found look at indices owned by this - // JandexIndex instance - .orElseGet(() -> streamOfIndices() - .map(e -> Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName)))) - .filter(t -> t.getT2().isPresent()) - .map(e -> createType(Tuples.of(e.getT1(), e.getT2().get()))).findFirst() - .orElse(null)); - - } - - private Optional> findMatch(DotName fqName) { - return (baseIndex == null ? Stream.>>empty() - : Arrays.stream( - baseIndex) - .filter( - jandexIndex -> jandexIndex != null) - .map(jandexIndex -> jandexIndex.findMatch(fqName))).filter(o -> o.isPresent()) - .findFirst() - // If not found look at indices owned by this - // JandexIndex instance - .orElseGet(() -> streamOfIndices() - .map(e -> { - IndexView view = e.getT2(); - ClassInfo info = view.getClassByName(fqName); - return Tuples.of(e.getT1(), Optional.ofNullable(info)); -// return Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName))); - }) - .filter(t -> t.getT2().isPresent()) - .map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst()); - } - - public Optional findClasspathResourceForType(String fqName) { - Optional> match = findMatch(DotName.createSimple(fqName)); - return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null); + return createType(getClassByName(DotName.createSimple(fqName))); } private IType createType(Tuple2 match) { + if (match == null) { + return null; + } File classpathResource = match.getT1(); IJavadocProvider javadocProvider = null; try { @@ -274,76 +106,18 @@ public class JandexIndex { } catch (ExecutionException e) { log.error("Failed to retrieve javadoc provider for resource " + classpathResource, e); } - return Wrappers.wrap(this, match.getT2(), javadocProvider); - } - - private Stream> streamOfIndices() { - return index.entrySet().parallelStream().map(e -> Tuples.of(e.getKey(), e.getValue().get())) - .filter(t -> t.getT2().isPresent()).map(t -> Tuples.of(t.getT1(), t.getT2().get())); - } - - private Stream> getKnownTypesStream(File file) { - Optional indexView = index.get(file).get(); - if (indexView.isPresent()) { - return indexView.get().getKnownClasses().parallelStream() - .map(info -> Tuples.of(info.name().toString(), createType(Tuples.of(file, info)))); - } - return Stream.empty(); - } - - private Stream getKnownPackages(File file) { - Optional indexView = index.get(file).get(); - if (indexView.isPresent()) { - return indexView.get().getKnownClasses().parallelStream().map(info -> { - String name = info.name().toString(); - return name.substring(0, name.lastIndexOf('.')); - }).distinct(); - } - return Stream.empty(); + return Wrappers.wrap(this, match.getT1(), match.getT2(), javadocProvider); } public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { - Flux> flux = Flux.fromIterable(knownTypes.values()).publishOn(Schedulers.parallel()) - .flatMap(s -> Flux.fromIterable(s.get())).filter(t -> typeFilter == null || typeFilter.test(t.getT2())) - .map(t -> Tuples.of(t.getT2(), FuzzyMatcher.matchScore(searchTerm, t.getT1()))) - .filter(t -> t.getT2() != 0.0); - if (baseIndex == null) { - return flux; - } else { - return Flux.merge(flux, - Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchTypes(searchTerm, typeFilter))); - } - } - - public Flux> fuzzySearchPackages(String searchTerm) { - Flux> flux = Flux.fromIterable(knownPackages.values()).publishOn(Schedulers.parallel()) - .flatMap(s -> Flux.fromIterable(s.get())) - .map(pkg -> Tuples.of(pkg, FuzzyMatcher.matchScore(searchTerm, pkg))).filter(t -> t.getT2() != 0.0); - if (baseIndex == null) { - return flux; - } else { - return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchPackages(searchTerm))); - } + return fuzzySearchTypes(searchTerm) + .map(match -> Tuples.of(createType(Tuples.of(match.getT1(), match.getT2())), match.getT3())) + .filter(t -> typeFilter == null || typeFilter.test(t.getT1())); } public Flux allSubtypesOf(IType type) { DotName name = DotName.createSimple(type.getFullyQualifiedName()); - Flux flux = Flux.fromIterable(index.keySet()).publishOn(Schedulers.parallel()).flatMap(file -> { - Optional optional = index.get(file).get(); - if (optional.isPresent()) { - return Flux - .fromIterable(type.isInterface() ? optional.get().getAllKnownImplementors(name) - : optional.get().getAllKnownSubclasses(name)) - .publishOn(Schedulers.parallel()).map(info -> createType(Tuples.of(file, info))); - } else { - return Flux.empty(); - } - }); - if (baseIndex == null) { - return flux; - } else { - return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.allSubtypesOf(type))); - } + return allSubtypesOf(name, type.isInterface()).map(match -> createType(match)); } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexSystemLibsIndex.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexSystemLibsIndex.java index b418f8afe..fbb9a27a9 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexSystemLibsIndex.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexSystemLibsIndex.java @@ -10,12 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.jandex; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.channels.IllegalSelectorException; import java.nio.file.Files; import java.nio.file.Path; @@ -32,8 +28,6 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider; -import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -58,13 +52,13 @@ public class JandexSystemLibsIndex { private static final Supplier INSTANCE = Suppliers.memoize(() -> new JandexSystemLibsIndex()); - private Cache cache; + private Cache cache; private JandexSystemLibsIndex() { - this.cache = CacheBuilder.newBuilder().build(new CacheLoader() { + this.cache = CacheBuilder.newBuilder().build(new CacheLoader() { @Override - public JandexIndex load(Path key) throws Exception { + public BasicJandexIndex load(Path key) throws Exception { return createIndex(key); } @@ -76,7 +70,7 @@ public class JandexSystemLibsIndex { * @param path the path containing jars * @return Jandex Index of the jars contained in the folder */ - public JandexIndex index(Path path) { + public BasicJandexIndex index(Path path) { try { return cache.get(path, () -> createIndex(path)); } catch (ExecutionException e) { @@ -90,15 +84,15 @@ public class JandexSystemLibsIndex { * @param jars system lib jars * @return Jandex Indexs for jars */ - public JandexIndex[] fromJars(Collection jars) { - return jars.stream().map(jar -> jar.toPath().getParent()).distinct().map(folder -> index(folder)).filter(Objects::nonNull).toArray(JandexIndex[]::new); + public BasicJandexIndex[] fromJars(Collection jars) { + return jars.stream().map(jar -> jar.toPath().getParent()).distinct().map(folder -> index(folder)).filter(Objects::nonNull).toArray(BasicJandexIndex[]::new); } public static JandexSystemLibsIndex getInstance() { return INSTANCE.get(); } - private JandexIndex createIndex(Path path) { + private BasicJandexIndex createIndex(Path path) { List jars = Collections.emptyList(); try { jars = Files.list(path).filter(p -> p.getFileName().toString().endsWith(".jar") && Files.isRegularFile(p)).map(p -> p.toFile()).collect(Collectors.toList()); @@ -106,7 +100,7 @@ public class JandexSystemLibsIndex { // Shouldn't happen - there should at least be one jar file log.error("Cannot list files in folder " + path, e); } - return new JandexIndex(jars, jarFile -> findIndexFile(jarFile), (classpathResource) -> createHtmlJavadocProvider(path)); + return new BasicJandexIndex(jars, jarFile -> findIndexFile(jarFile)); } private File findIndexFile(File jarFile) { @@ -126,46 +120,36 @@ public class JandexSystemLibsIndex { } } - private static HtmlJavadocProvider createHtmlJavadocProvider(Path path) { - try { - String javaVersion = getJavaVersion(path); - URL javadocUrl = new URL("https://docs.oracle.com/javase/" + extractVersionForJavadoc(javaVersion) + "/docs/api/"); - return new HtmlJavadocProvider((type) -> TypeUrlProviderFromContainerUrl.JAVADOC_FOLDER_URL_SUPPLIER.url(javadocUrl, type.getFullyQualifiedName())); - } catch (MalformedURLException e) { - return null; - } - } - - private static String getJavaVersion(Path path) { - // Find valid /bin folder - for (; path != null && !(Files.isDirectory(path.resolve("bin")) && Files.isReadable(path.resolve("bin"))); path = path.getParent()); - // If found, assume it's the java home bin folder - if (path != null) { - Path javaBin = path.resolve("bin"); - try { - Process p = new ProcessBuilder().directory(javaBin.toFile()).command("./java", "-version").start(); - BufferedReader buffer = new BufferedReader(new InputStreamReader(p.getErrorStream())); - int exitCode = p.waitFor(); - if (exitCode == 0) { - return buffer.lines().map(l -> JAVA_VERSION_PATTERN.matcher(l)).filter(m -> m.find()).findFirst().map(m -> m.group(1)).orElse(DEFAULT_JAVA_VERSION); - } else { - log.error("Failed to compute java version in folder: " + javaBin + ". 'java -version' exit code is " + exitCode); - } - } catch (IOException | InterruptedException e) { - log.error("Failed to compute java version in folder: " + javaBin, e); - } - } - return DEFAULT_JAVA_VERSION; - } - - private static String extractVersionForJavadoc(String javaVersion) { - if (javaVersion.startsWith("1.")) { - int idx = javaVersion.indexOf('.', 2); - return idx >= 0 ? javaVersion.substring(2, idx) : javaVersion.substring(2); - } else { - int idx = javaVersion.indexOf('.'); - return idx >= 0 ? javaVersion.substring(0, idx) : javaVersion; - } - } +// private static String getJavaVersion(Path path) { +// // Find valid /bin folder +// for (; path != null && !(Files.isDirectory(path.resolve("bin")) && Files.isReadable(path.resolve("bin"))); path = path.getParent()); +// // If found, assume it's the java home bin folder +// if (path != null) { +// Path javaBin = path.resolve("bin"); +// try { +// Process p = new ProcessBuilder().directory(javaBin.toFile()).command("./java", "-version").start(); +// BufferedReader buffer = new BufferedReader(new InputStreamReader(p.getErrorStream())); +// int exitCode = p.waitFor(); +// if (exitCode == 0) { +// return buffer.lines().map(l -> JAVA_VERSION_PATTERN.matcher(l)).filter(m -> m.find()).findFirst().map(m -> m.group(1)).orElse(DEFAULT_JAVA_VERSION); +// } else { +// log.error("Failed to compute java version in folder: " + javaBin + ". 'java -version' exit code is " + exitCode); +// } +// } catch (IOException | InterruptedException e) { +// log.error("Failed to compute java version in folder: " + javaBin, e); +// } +// } +// return DEFAULT_JAVA_VERSION; +// } +// +// private static String extractVersionForJavadoc(String javaVersion) { +// if (javaVersion.startsWith("1.")) { +// int idx = javaVersion.indexOf('.', 2); +// return idx >= 0 ? javaVersion.substring(2, idx) : javaVersion.substring(2); +// } else { +// int idx = javaVersion.indexOf('.'); +// return idx >= 0 ? javaVersion.substring(0, idx) : javaVersion; +// } +// } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/MethodImpl.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/MethodImpl.java index d4ed6e3bc..f2adcf14f 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/MethodImpl.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/MethodImpl.java @@ -22,16 +22,15 @@ import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; public class MethodImpl implements IMethod { - + private static final String JANDEX_CONTRUCTOR_NAME = ""; - - private JandexIndex index; + private IType declaringType; private MethodInfo method; private IJavadocProvider javadocProvider; - - MethodImpl(JandexIndex index, MethodInfo method, IJavadocProvider javadocProvider) { - this.index = index; + + MethodImpl(IType declaringType, MethodInfo method, IJavadocProvider javadocProvider) { + this.declaringType = declaringType; this.method = method; this.javadocProvider =javadocProvider; } @@ -40,7 +39,7 @@ public class MethodImpl implements IMethod { public int getFlags() { return method.flags(); } - + @Override public boolean isConstructor() { return method.name().equals(JANDEX_CONTRUCTOR_NAME); @@ -48,7 +47,7 @@ public class MethodImpl implements IMethod { @Override public IType getDeclaringType() { - return Wrappers.wrap(index, method.declaringClass(), javadocProvider); + return declaringType; } @Override @@ -85,7 +84,7 @@ public class MethodImpl implements IMethod { // sb.append(getReturnType()); // return sb.toString(); // } - + @Override public String toString() { return method.toString(); @@ -109,5 +108,9 @@ public class MethodImpl implements IMethod { return super.equals(obj); } + @Override + public String getBindingKey() { + return BindingKeyUtils.getBindingKey(method); + } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java index 4b75f0c14..18e89a815 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.jandex; +import java.io.File; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -29,14 +30,16 @@ import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; class TypeImpl implements IType { - + private ClassInfo info; private JandexIndex index; private IJavadocProvider javadocProvider; - - TypeImpl(JandexIndex index, ClassInfo info, IJavadocProvider javadocProvider) { + private File classpathContainer; + + TypeImpl(JandexIndex index, File classpathContainer, ClassInfo info, IJavadocProvider javadocProvider) { this.info = info; this.index = index; + this.classpathContainer = classpathContainer; this.javadocProvider = javadocProvider; } @@ -48,7 +51,7 @@ class TypeImpl implements IType { @Override public IType getDeclaringType() { DotName enclosingClass = info.enclosingClass(); - return enclosingClass == null ? null : index.getClassByName(enclosingClass); + return enclosingClass == null ? null : index.findType(enclosingClass.toString()); } @Override @@ -92,7 +95,7 @@ class TypeImpl implements IType { public boolean isAnnotation() { return Flags.isAnnotation(info.flags()); } - + @Override public String getFullyQualifiedName() { return info.name().toString(); @@ -100,26 +103,26 @@ class TypeImpl implements IType { @Override public IField getField(String name) { - return Wrappers.wrap(index, info.field(name), javadocProvider); + return Wrappers.wrap(this, info.field(name), javadocProvider); } @Override public Stream getFields() { return info.fields().stream().map(f -> { - return Wrappers.wrap(index, f, javadocProvider); + return Wrappers.wrap(this, f, javadocProvider); }); } @Override public IMethod getMethod(String name, Stream parameters) { List typeParameters = parameters.map(Wrappers::from).collect(Collectors.toList()); - return Wrappers.wrap(index, info.method(name, typeParameters.toArray(new Type[typeParameters.size()])), javadocProvider); + return Wrappers.wrap(this, info.method(name, typeParameters.toArray(new Type[typeParameters.size()])), javadocProvider); } @Override public Stream getMethods() { return info.methods().stream().map(m -> { - return Wrappers.wrap(index, m, javadocProvider); + return Wrappers.wrap(this, m, javadocProvider); }); } @@ -141,5 +144,14 @@ class TypeImpl implements IType { return super.equals(obj); } - + @Override + public String getBindingKey() { + return BindingKeyUtils.getBindingKey(info); + } + + @Override + public File classpathContainer() { + return classpathContainer; + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java index c6a536e37..09c2280e4 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java @@ -13,6 +13,8 @@ package org.springframework.ide.vscode.commons.jandex; import static org.springframework.ide.vscode.commons.util.Assert.isNotNull; +import java.io.File; + import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; @@ -32,32 +34,32 @@ import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.java.IVoidType; public class Wrappers { - - public static IType wrap(JandexIndex index, ClassInfo info, IJavadocProvider javadocProvider) { + + public static IType wrap(JandexIndex index, File classpathContainer, ClassInfo info, IJavadocProvider javadocProvider) { if (info == null) { return null; } - return new TypeImpl(index, info, javadocProvider); + return new TypeImpl(index, classpathContainer, info, javadocProvider); } - - public static IField wrap(JandexIndex index, FieldInfo field, IJavadocProvider javadocProvider) { + + public static IField wrap(IType declaringType, FieldInfo field, IJavadocProvider javadocProvider) { if (field == null) { return null; } - return new FieldImpl(index, field, javadocProvider); + return new FieldImpl(declaringType, field, javadocProvider); } - public static IMethod wrap(JandexIndex index, MethodInfo method, IJavadocProvider javadocProvider) { - isNotNull(index); + public static IMethod wrap(IType declaringType, MethodInfo method, IJavadocProvider javadocProvider) { + isNotNull(declaringType); isNotNull(method); - return new MethodImpl(index, method, javadocProvider); + return new MethodImpl(declaringType, method, javadocProvider); } - + public static IAnnotation wrap(AnnotationInstance annotation, IJavadocProvider javadocProvider) { isNotNull(annotation); return new AnnotationImpl(annotation, javadocProvider); } - + public static IMemberValuePair wrap(AnnotationValue annotationValue) { if (annotationValue == null) { return null; @@ -73,14 +75,14 @@ public class Wrappers { public Object getValue() { return annotationValue.value(); } - + @Override public String toString() { return annotationValue.toString(); } }; } - + public static IPrimitiveType wrap(PrimitiveType type) { switch (type.primitive()) { case SHORT: @@ -102,7 +104,7 @@ public class Wrappers { } throw new IllegalArgumentException("Invalid Java primitive type! " + type.toString()); } - + @SuppressWarnings("unchecked") static Type from(IJavaType type) { if (type == IPrimitiveType.BOOLEAN) { @@ -128,7 +130,7 @@ public class Wrappers { } throw new IllegalArgumentException("Not a Jandex wrapped typed!"); } - + public static IJavaType wrap(Type type) { switch (type.kind()) { case ARRAY: @@ -150,5 +152,5 @@ public class Wrappers { } throw new IllegalArgumentException("Invalid Java Type " + type.toString()); } - + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IField.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IField.java index 72b7a418d..5581d7796 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IField.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IField.java @@ -10,6 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.io.File; + public interface IField extends IMember { boolean isEnumConstant(); + + @Override + default File classpathContainer() { + return getDeclaringType().classpathContainer(); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java index 440608eeb..466bca95b 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java @@ -15,5 +15,6 @@ import org.springframework.ide.vscode.commons.javadoc.IJavadoc; public interface IJavaElement { String getElementName(); IJavadoc getJavaDoc(); + String getBindingKey(); boolean exists(); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java index 8506fabeb..85d6468e3 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java @@ -17,20 +17,18 @@ import java.util.List; import java.util.Optional; import java.util.function.Predicate; -import org.springframework.ide.vscode.commons.javadoc.IJavadoc; - import reactor.core.publisher.Flux; import reactor.util.function.Tuple2; -public interface IJavaProject extends IJavaElement { +public interface IJavaProject { final static String PROJECT_CACHE_FOLDER = ".sts4-cache"; IClasspath getClasspath(); ClasspathIndex getIndex(); URI getLocationUri(); + boolean exists(); - @Override default String getElementName() { return getClasspath().getName(); } @@ -59,10 +57,4 @@ public interface IJavaProject extends IJavaElement { return getIndex().findClasspathResourceContainer(fqName); } - - @Override - default IJavadoc getJavaDoc() { - //?? why is this here ?? - return null; - } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMember.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMember.java index 1ab459588..6f4d200de 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMember.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMember.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.io.File; + public interface IMember extends IJavaElement, IAnnotatable { /** @@ -32,7 +34,7 @@ public interface IMember extends IJavaElement, IAnnotatable { * @see Flags */ int getFlags(); - + /** * Returns the type in which this member is declared, or null * if this member is not declared in a type (for example, a top-level type). @@ -43,4 +45,6 @@ public interface IMember extends IJavaElement, IAnnotatable { */ IType getDeclaringType(); + File classpathContainer(); + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMethod.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMethod.java index f3a4aba31..6da1249ea 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMethod.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IMethod.java @@ -12,6 +12,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.io.File; import java.util.stream.Stream; public interface IMethod extends IMember { @@ -57,13 +58,18 @@ public interface IMethod extends IMember { // * @see Signature // */ // String getSignature(); - + /** * Returns parameter types of this method * @return */ Stream parameters(); - + boolean isConstructor(); + @Override + default File classpathContainer() { + return getDeclaringType().classpathContainer(); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java index f7a1fdec1..0220a6139 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java @@ -14,6 +14,7 @@ import java.io.File; import java.net.URI; import org.springframework.ide.vscode.commons.jandex.JandexClasspath; +import org.springframework.ide.vscode.commons.jandex.JandexIndex.JavadocProviderFactory; import org.springframework.ide.vscode.commons.util.FileObserver; import reactor.core.Disposable; @@ -24,12 +25,14 @@ public class JavaProject implements IJavaProject, Disposable { private ClasspathIndex index; private URI uri; private final FileObserver fileObserver; + private final JavadocProviderFactory javadocProviderFactory; - public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath) { + public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath, JavadocProviderFactory javadocProviderFactory) { super(); this.classpath = classpath; this.fileObserver = fileObserver; this.uri = uri; + this.javadocProviderFactory = javadocProviderFactory; } @Override @@ -40,7 +43,7 @@ public class JavaProject implements IJavaProject, Disposable { @Override public synchronized ClasspathIndex getIndex() { if (index==null) { - index = new JandexClasspath(classpath, fileObserver); + index = new JandexClasspath(classpath, fileObserver, javadocProviderFactory); } return index; } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java similarity index 51% rename from headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java rename to headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java index db3d32e1a..f7e130a2f 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java @@ -13,20 +13,28 @@ package org.springframework.ide.vscode.commons.java; import java.net.URI; import java.nio.file.Path; +import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.util.FileObserver; /** - * Abstract java project. Has a folder to store some project calculated data to speed up access + * Legacy java project. Base implementation for projects calculating classpath + * and other Java related data locally on this LS. Data calculation is + * expensive, hence there is a folder to store some project calculated data to + * speed up access * * @author Alex Boyko * */ -public abstract class AbstractJavaProject extends JavaProject { +public class LegacyJavaProject extends JavaProject { final protected Path projectDataCache; - public AbstractJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath) { - super(fileObserver, loactionUri, classpath); + public LegacyJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath) { + super(fileObserver, loactionUri, classpath, classpathResource -> { + CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, classpathResource); + return JavaDocProviders.createFor(cpe); + }); this.projectDataCache = projectDataCache; } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/JdtLsJavadocProvider.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/JdtLsJavadocProvider.java new file mode 100644 index 000000000..181cb6b0f --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/JdtLsJavadocProvider.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.javadoc; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.java.IAnnotation; +import org.springframework.ide.vscode.commons.java.IField; +import org.springframework.ide.vscode.commons.java.IJavaElement; +import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.java.IMethod; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.languageserver.JavadocParams; +import org.springframework.ide.vscode.commons.languageserver.JavadocResponse; +import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.Renderables; + +public class JdtLsJavadocProvider implements IJavadocProvider { + + private static final Logger log = LoggerFactory.getLogger(JdtLsJavadocProvider.class); + + private STS4LanguageClient client; + private String projectUri; + + public JdtLsJavadocProvider(STS4LanguageClient client, String projectUri) { + super(); + this.client = client; + this.projectUri = projectUri; + } + + private IJavadoc produceJavadocFromMd(JavadocResponse response) { + String md = response.getContent(); + if (md != null) { + final Renderable renderableDoc = Renderables.mdBlob(md); + return new IJavadoc() { + + @Override + public String raw() { + throw new UnsupportedOperationException("Raw content unavailable"); + } + + @Override + public Renderable getRenderable() { + return renderableDoc; + } + + }; + } + return null; + } + + private IJavadoc javadoc(IJavaElement element) { + try { + JavadocResponse response = client.javadoc(new JavadocParams(projectUri, element.getBindingKey())).get(10, TimeUnit.SECONDS); + return produceJavadocFromMd(response); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("", e); + return null; + } + } + + @Override + public IJavadoc getJavadoc(IType type) { + return javadoc(type); + } + + @Override + public IJavadoc getJavadoc(IField field) { + return javadoc(field); + } + + @Override + public IJavadoc getJavadoc(IMethod method) { + return javadoc(method); + } + + @Override + public IJavadoc getJavadoc(IAnnotation annotation) { + return javadoc(annotation); + } + +} diff --git a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java index fe34d2547..db122d08c 100644 --- a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java +++ b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java @@ -60,7 +60,7 @@ public class JandexClasspathTest { } JandexClasspath getJandexClasspath() { - return new JandexClasspath(getClasspath(), fileObserver); + return new JandexClasspath(getClasspath(), fileObserver, null); } public void deleteClass(String fqName, BiConsumer eventNoficator) { diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/JavadocParams.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/JavadocParams.java new file mode 100644 index 000000000..818ded907 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/JavadocParams.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver; + +public class JavadocParams { + + private String projectUri; + private String bindingKey; + + public JavadocParams(String projectUri, String bindingKey) { + super(); + this.projectUri = projectUri; + this.bindingKey = bindingKey; + } + public String getProjectUri() { + return projectUri; + } + public void setProjectUri(String projectUri) { + this.projectUri = projectUri; + } + public String getBindingKey() { + return bindingKey; + } + public void setBindingKey(String bindingKey) { + this.bindingKey = bindingKey; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/JavadocResponse.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/JavadocResponse.java new file mode 100644 index 000000000..35d494aad --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/JavadocResponse.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver; + +public class JavadocResponse { + + private String content; + + public JavadocResponse() { + super(); + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java index 92056a7ad..4e9b001f3 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java @@ -40,4 +40,7 @@ public interface STS4LanguageClient extends LanguageClient { @JsonRequest("sts/removeClasspathListener") CompletableFuture removeClasspathListener(ClasspathListenerParams classpathListenerParams); + + @JsonRequest("sts/javadoc") + CompletableFuture javadoc(JavadocParams params); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java index 0c6afc138..4e6cf0e04 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/completion/VscodeCompletionEngineAdapter.java @@ -22,6 +22,7 @@ import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.InsertTextFormat; import org.eclipse.lsp4j.MarkupContent; +import org.eclipse.lsp4j.MarkupKind; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.TextEdit; @@ -183,7 +184,10 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine { } private static void resolveItem(TextDocument doc, ICompletionProposal completion, CompletionItem item) throws Exception { - item.setDocumentation(toMarkdown(completion.getDocumentation())); + MarkupContent content = new MarkupContent(); + content.setKind(MarkupKind.MARKDOWN); + content.setValue(toMarkdown(completion.getDocumentation())); + item.setDocumentation(content); } private static void resolveEdits(TextDocument doc, ICompletionProposal completion, CompletionItem item) { diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index 6c9711889..1fa5c6e14 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -15,8 +15,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -55,71 +53,45 @@ import org.eclipse.aether.util.graph.transformer.NearestVersionSelector; import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector; import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor; import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; -import org.springframework.ide.vscode.commons.jandex.JandexIndex; -import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider; -import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl; import org.springframework.ide.vscode.commons.util.Log; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; - /** * Maven Core functionality - * + * * @author Alex Boyko * */ public class MavenCore { - + private static final String CLASSIFIER_SOURCES = "sources"; private static final String CLASSIFIER_JAVADOC = "javadoc"; private static final String CLASSIFIER_TESTS = "tests"; private static final String CLASSIFIER_TESTSOURCES = "test-sources"; - + private static final String JAVA_HOME = "java.home"; private static final String JAVA_RUNTIME_VERSION = "java.runtime.version"; private static final String JAVA_BOOT_CLASS_PATH = "sun.boot.class.path"; public static final String CLASSPATH_TXT = "classpath.txt"; public static final String POM_XML = "pom.xml"; - + private static MavenCore defaultInstance = null; - + private MavenBridge maven; - - private Supplier javaCoreIndex = Suppliers.memoize(() -> { - try { - return new JandexIndex(getJreLibs().map(path -> path.toFile()).collect(Collectors.toList()), jarFile -> findIndexFile(jarFile), (classpathResource) -> { - try { - String javaVersion = getJavaRuntimeMinorVersion(); - if (javaVersion == null) { - javaVersion = "8"; - } - URL javadocUrl = new URL("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/"); - return new HtmlJavadocProvider((type) -> TypeUrlProviderFromContainerUrl.JAVADOC_FOLDER_URL_SUPPLIER.url(javadocUrl, type.getFullyQualifiedName())); - } catch (MalformedURLException e) { - Log.log(e); - return null; - } - }); - } catch (MavenException e) { - return null; - } - }); - + public static MavenCore getDefault() { if (defaultInstance == null) { defaultInstance = new MavenCore(IMavenConfiguration.DEFAULT); } return defaultInstance; } - + public MavenCore(IMavenConfiguration config) { this.maven = new MavenBridge(config); } - + /** * Reads maven classpath text file - * + * * @param classPathFilePath * @return set of classpath entries * @throws IOException @@ -130,10 +102,10 @@ public class MavenCore { Path dir = classPathFilePath.getParent(); return Arrays.stream(text.split(File.pathSeparator)).map(dir::resolve); } - + /** * Creates Maven Project descriptor based on the pom file. - * + * * @param pom The pom file * @return Maven project instance * @throws MavenException @@ -141,14 +113,14 @@ public class MavenCore { public MavenProject readProject(File pom, boolean resolveDependencies) throws MavenException { return maven.readProject(pom, maven.createExecutionRequest(), resolveDependencies); } - + public MavenExecutionResult build(File pom) throws MavenException { return maven.compileAndGenerateJavadoc(pom); } - + /** * Taken from M2E same named method from MavenModelManager - * + * * @param repositorySystem * @param repositorySession * @param mavenProject @@ -214,10 +186,10 @@ public class MavenCore { return node; } - + /** * Calculates dependency graph for a Maven project provided the scope. - * + * * @param project Maven Project descriptor * @param scope Dependency scope * @return Set of all dependencies including transient ones @@ -229,9 +201,10 @@ public class MavenCore { DependencyNode graph = readDependencyTree(maven.lookupComponent(org.eclipse.aether.RepositorySystem.class), session, project, scope); if (graph != null) { - + ArrayList dependencyNodes = new ArrayList<>(); graph.accept(new DependencyVisitor() { + @Override public boolean visitEnter(DependencyNode node) { if (node.getDependency() != null) { dependencyNodes.add(node); @@ -239,15 +212,16 @@ public class MavenCore { return true; } + @Override public boolean visitLeave(DependencyNode dependencynode) { return true; } }); - LinkedHashSet artifacts = new LinkedHashSet<>(); + LinkedHashSet artifacts = new LinkedHashSet<>(); RepositoryUtils.toArtifacts(artifacts, dependencyNodes, Collections.singletonList(project.getArtifact().getId()), null); - + return artifacts.parallelStream().map(artifact -> { if (!artifact.isResolved()) { try { @@ -264,42 +238,42 @@ public class MavenCore { return artifact; }).collect(Collectors.toSet()); } - + return Collections.emptySet(); } - + public File localRepositoryFolder() throws MavenException { MavenExecutionRequest request = maven.createExecutionRequest(); DefaultRepositorySystemSession session = maven.createRepositorySession(request); LocalRepositoryManager lrm = session.getLocalRepositoryManager(); return lrm.getRepository().getBasedir(); } - + public Artifact getSources(Artifact artifact, List repositories) throws MavenException { return maven.resolve(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), CLASSIFIER_SOURCES, repositories, maven.createExecutionRequest()); } - + public Artifact getJavadoc(Artifact artifact, List repositories) throws MavenException { return maven.resolve(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), CLASSIFIER_JAVADOC, repositories, maven.createExecutionRequest()); } - + public Artifact getTests(Artifact artifact, List repositories) throws MavenException { return maven.resolve(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), CLASSIFIER_TESTS, repositories, maven.createExecutionRequest()); } - + public Artifact getTestSources(Artifact artifact, List repositories) throws MavenException { return maven.resolve(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), CLASSIFIER_TESTSOURCES, repositories, maven.createExecutionRequest()); } - + public Stream getJreLibs() throws MavenException { String s = (String) maven.createExecutionRequest().getSystemProperties().get(JAVA_BOOT_CLASS_PATH); return s == null ? Stream.empty() : Arrays.stream(s.split(File.pathSeparator)).map(File::new).filter(f -> f.canRead()).map(f -> Paths.get(f.toURI())); } - + public String getJavaRuntimeVersion() throws MavenException { return maven.createExecutionRequest().getSystemProperties().getProperty(JAVA_RUNTIME_VERSION); } - + public String getJavaRuntimeMinorVersion() { try { String fullVersion = getJavaRuntimeVersion(); @@ -314,32 +288,9 @@ public class MavenCore { } return null; } - + private String getJavaHome() throws MavenException { return maven.createExecutionRequest().getSystemProperties().getProperty(JAVA_HOME); } - - private File findIndexFile(File jarFile) { - String suffix = null; - try { - String javaHome = getJavaHome(); - if (javaHome != null) { - int index = javaHome.lastIndexOf('/'); - if (index != -1) { - javaHome = javaHome.substring(0, index); - } - } - if (jarFile.toString().startsWith(javaHome)) { - suffix = getJavaRuntimeVersion(); - } - } catch (MavenException e) { - Log.log(e); - } - return new File(JandexIndex.getIndexFolder().toString(), jarFile.getName() + "-" + suffix + ".jdx"); - } - - public JandexIndex getJavaIndexForJreLibs() { - return javaCoreIndex.get(); - } - + } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index b9b4e0f97..924a39fbf 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -13,7 +13,7 @@ package org.springframework.ide.vscode.commons.maven.java; import java.io.File; import java.nio.file.Path; -import org.springframework.ide.vscode.commons.java.AbstractJavaProject; +import org.springframework.ide.vscode.commons.java.LegacyJavaProject; import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; import org.springframework.ide.vscode.commons.java.IClasspath; @@ -27,7 +27,7 @@ import org.springframework.ide.vscode.commons.util.Log; * @author Alex Boyko * */ -public class MavenJavaProject extends AbstractJavaProject { +public class MavenJavaProject extends LegacyJavaProject { private final File pom; diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java index 4433f10e4..8c5e28ae0 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java @@ -21,8 +21,6 @@ import java.util.stream.Stream; import org.junit.Assume; import org.junit.Test; -import org.springframework.ide.vscode.commons.jandex.JandexClasspath; -import org.springframework.ide.vscode.commons.jandex.JandexClasspath.JavadocProviderTypes; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IType; @@ -35,12 +33,11 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; public class HtmlJavadocTest { - + private static FileObserver fileObserver = new BasicFileObserver(); private static Supplier projectSupplier = Suppliers.memoize(() -> { Path testProjectPath; try { - JandexClasspath.providerType = JavadocProviderTypes.HTML; testProjectPath = Paths.get(HtmlJavadocTest.class.getResource("/gs-rest-service-cors-boot-1.4.1-with-classpath-file").toURI()); MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); return MavenJavaProject.create(fileObserver, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); @@ -52,9 +49,9 @@ public class HtmlJavadocTest { @Test public void html_testClassJavadoc() throws Exception { Assume.assumeTrue(javaVersionHigherThan(6)); - + MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("java.util.Map"); assertNotNull(type); String expected = String.join("\n", @@ -68,39 +65,39 @@ public class HtmlJavadocTest { @Test public void html_testConstructorJavadoc() throws Exception { - Assume.assumeTrue(javaVersionHigherThan(6)); + Assume.assumeTrue(javaVersionHigherThan(6)); MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod method = type.getMethod("", Stream.empty()); assertNotNull(method); - + String expected = String.join("\n", "

ArrayList

" ); IJavadoc javaDoc = method.getJavaDoc(); assertNotNull(javaDoc); assertEquals(expected, javaDoc.getRenderable().toHtml().substring(0, expected.length())); - + } @Test public void html_testEmptyJavadocClass() throws Exception { MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("hello.Application"); assertNotNull(type); assertNull(type.getJavaDoc()); } @Test - public void html_testFieldAndMethodJavadocForJar() throws Exception { + public void html_testFieldAndMethodJavadocForJar() throws Exception { MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("org.springframework.boot.SpringApplication"); assertNotNull(type); - + IField field = type.getField("BANNER_LOCATION_PROPERTY_VALUE"); assertNotNull(field); String expected = String.join("\n", @@ -115,10 +112,10 @@ public class HtmlJavadocTest { IJavadoc javaDoc = field.getJavaDoc(); assertNotNull(javaDoc); assertEquals(expected, javaDoc.getRenderable().toHtml()); - + IMethod method = type.getMethod("getListeners", Stream.empty()); assertNotNull(method); - expected = String.join("\n", + expected = String.join("\n", "

getListeners

", "
public Set<org.springframework.context.ApplicationListener<?>> getListeners()
", "
Returns read-only ordered Set of the ApplicationListeners that will be", @@ -137,16 +134,16 @@ public class HtmlJavadocTest { @Test public void html_testInnerClassJavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("hello.Greeting$TestInnerClass"); assertNotNull(type); IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); assertEquals("
Comment for inner class
", javaDoc.getRenderable().toHtml()); - + IField field = type.getField("innerField"); assertNotNull(field); - String expected = String.join("\n", + String expected = String.join("\n", "

innerField

", "
protected int innerField
", "
Comment for inner field
" @@ -154,7 +151,7 @@ public class HtmlJavadocTest { javaDoc = field.getJavaDoc(); assertNotNull(javaDoc); assertEquals(expected, javaDoc.getRenderable().toHtml()); - + IMethod method = type.getMethod("getInnerField", Stream.empty()); assertNotNull(method); expected = String.join("\n", @@ -170,16 +167,16 @@ public class HtmlJavadocTest { @Test public void html_testInnerClassLevel2_JavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("hello.Greeting$TestInnerClass$TestInnerClassLevel2"); assertNotNull(type); IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); assertEquals("
Comment for level 2 nested class
", javaDoc.getRenderable().toHtml()); - + IField field = type.getField("innerLevel2Field"); assertNotNull(field); - String expected = String.join("\n", + String expected = String.join("\n", "

innerLevel2Field

", "
protected int innerLevel2Field
", "
Comment for level 2 inner field
" @@ -187,7 +184,7 @@ public class HtmlJavadocTest { javaDoc = field.getJavaDoc(); assertNotNull(javaDoc); assertEquals(expected, javaDoc.getRenderable().toHtml()); - + IMethod method = type.getMethod("getInnerLevel2Field", Stream.empty()); assertNotNull(method); expected = String.join("\n", @@ -204,13 +201,13 @@ public class HtmlJavadocTest { public void html_testJavadocOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); IType type = project.findType("hello.Greeting"); - + assertNotNull(type); String expected = "
Comment for Greeting class
"; IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); assertEquals(expected, javaDoc.getRenderable().toHtml()); - + IField field = type.getField("id"); assertNotNull(field); expected = String.join("\n", @@ -221,7 +218,7 @@ public class HtmlJavadocTest { javaDoc = field.getJavaDoc(); assertNotNull(javaDoc); assertEquals(expected, javaDoc.getRenderable().toHtml()); - + IMethod method = type.getMethod("getId", Stream.empty()); assertNotNull(method); expected = String.join("\n", @@ -237,14 +234,14 @@ public class HtmlJavadocTest { @Test public void html_testMethodJavadoc() throws Exception { Assume.assumeTrue(javaVersionHigherThan(6)); - + MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod method = type.getMethod("size", Stream.empty()); assertNotNull(method); - + String expected = String.join("\n", "

size

", "
public int size()
", @@ -258,9 +255,9 @@ public class HtmlJavadocTest { @Test public void html_testNestedClassJavadoc() throws Exception { Assume.assumeTrue(javaVersionHigherThan(6)); - + MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("java.util.Map$Entry"); assertNotNull(type); String expected = String.join("\n", @@ -274,7 +271,7 @@ public class HtmlJavadocTest { @Test public void html_testNoJavadocClass() throws Exception { MavenJavaProject project = projectSupplier.get();; - + IType type = project.findType("hello.GreetingController"); assertNotNull(type); assertNull(type.getJavaDoc()); @@ -283,12 +280,12 @@ public class HtmlJavadocTest { @Test public void html_testNoJavadocField() throws Exception { MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("hello.GreetingController"); assertNotNull(type); IField field = type.getField("template"); assertNotNull(field); - String expected = String.join("\n", + String expected = String.join("\n", "

template

", "
public static final String template
", "
", @@ -304,12 +301,12 @@ public class HtmlJavadocTest { @Test public void html_testNoJavadocMethod() throws Exception { MavenJavaProject project = projectSupplier.get(); - + IType type = project.findType("hello.Application"); assertNotNull(type); IMethod method = type.getMethod("corsConfigurer", Stream.empty()); assertNotNull(method); - String expected = String.join("\n", + String expected = String.join("\n", "

corsConfigurer

", "
@Bean",
 				"public org.springframework.web.servlet.config.annotation.WebMvcConfigurer corsConfigurer()
" diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java index c3f39e37e..6fca11381 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java @@ -15,6 +15,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.springframework.ide.vscode.languageserver.testharness.ClasspathTestUtil.getOutputFolder; import java.io.File; import java.nio.file.Path; @@ -33,7 +34,6 @@ import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.java.IVoidType; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.commons.util.BasicFileObserver; -import org.springframework.ide.vscode.commons.util.FileObserver; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -41,12 +41,10 @@ import com.google.common.cache.LoadingCache; import reactor.util.function.Tuple2; -import static org.springframework.ide.vscode.languageserver.testharness.ClasspathTestUtil.*; - public class JavaIndexTest { - + private static BasicFileObserver fileObserver = new BasicFileObserver(); - + private static LoadingCache mavenProjectsCache = CacheBuilder.newBuilder().build(new CacheLoader() { @Override @@ -55,93 +53,100 @@ public class JavaIndexTest { MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); return MavenJavaProject.create(fileObserver, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } - + }); - + @Test public void fuzzySearchNoFilter() throws Exception { - List> results = MavenCore.getDefault().getJavaIndexForJreLibs() - .fuzzySearchTypes("util.Map", null) + MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + List> results = project.getIndex().fuzzySearchTypes("util.Map", null) .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .block(); assertTrue(results.size() > 10); - assertEquals("java.util.Map", results.get(0).getT1().getFullyQualifiedName()); + IType type = results.get(0).getT1(); + System.out.println(type.getFullyQualifiedName() + ": " + type.getBindingKey()); + assertEquals("java.util.Map", type.getFullyQualifiedName()); } - + @Test public void fuzzySearchWithFilter() throws Exception { - List> results = MavenCore.getDefault().getJavaIndexForJreLibs() + MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + List> results = project.getIndex() .fuzzySearchTypes("util.Map", (type) -> Flags.isPrivate(type.getFlags())) .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .block(); assertTrue(results.size() > 10); - assertEquals("java.util.EnumMap$KeySet", results.get(0).getT1().getFullyQualifiedName()); + IType type = results.get(0).getT1(); + System.out.println(type.getFullyQualifiedName() + ": " + type.getBindingKey()); + assertEquals("java.util.EnumMap$KeySet", type.getFullyQualifiedName()); } - + @Test public void fuzzySearchPackage() throws Exception { - List> results = MavenCore.getDefault().getJavaIndexForJreLibs() + MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + List> results = project.getIndex() .fuzzySearchPackages("util") .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .block(); assertTrue(results.size() > 10); assertEquals("java.util", results.get(0).getT1()); } - + @Test public void findClassInJar() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); IType type = project.findType("org.springframework.test.web.client.ExpectedCount"); assertNotNull(type); } - + @Test public void findClassInOutputFolder() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); IType type = project.findType("hello.Greeting"); assertNotNull(type); } - + @Test public void classNotFound() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); IType type = project.findType("hello.NonExistentClass"); assertNull(type); } - + @Test public void voidMethodNoParams() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("clear", Stream.empty()); + System.out.println("Method clear: " + m.getBindingKey()); assertEquals("clear", m.getElementName()); assertEquals(IVoidType.DEFAULT, m.getReturnType()); assertEquals(0, m.parameters().count()); } - + @Test public void voidConstructor() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); IType type = project.findType("java.util.ArrayList"); - assertNotNull(type); + assertNotNull(type); IMethod m = type.getMethod("", Stream.empty()); assertEquals(type.getElementName(), m.getElementName()); assertEquals(IVoidType.DEFAULT, m.getReturnType()); assertEquals(0, m.parameters().count()); } - + @Test public void constructorMethodWithParams() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); IType type = project.findType("java.util.ArrayList"); - assertNotNull(type); + assertNotNull(type); IMethod m = type.getMethod("", Stream.of(IPrimitiveType.INT)); assertEquals(m.getDeclaringType().getElementName(), m.getElementName()); assertEquals(IVoidType.DEFAULT, m.getReturnType()); - assertEquals(Collections.singletonList(IPrimitiveType.INT), m.parameters().collect(Collectors.toList())); + assertEquals(Collections.singletonList(IPrimitiveType.INT), m.parameters().collect(Collectors.toList())); } - + @Test public void testFindJarResource() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java index 7bb2e5297..4b6ede123 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Renderables.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * Copyright (c) 2016, 2018 Pivotal, Inc. * 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 @@ -63,6 +63,21 @@ public class Renderables { return htmlBlob(buffer -> buffer.raw(html)); } + public static Renderable mdBlob(String md) { + return new Renderable() { + + @Override + public void renderAsMarkdown(StringBuilder buffer) { + buffer.append(md); + } + + @Override + public void renderAsHtml(HtmlBuffer buffer) { + throw new UnsupportedOperationException("Not implemented"); + } + }; + } + public static Renderable concat(Renderable... pieces) { return concat(ImmutableList.copyOf(pieces)); } @@ -96,6 +111,25 @@ public class Renderables { }; } + public static Renderable inlineSnippet(Renderable text) { + return new Renderable() { + + @Override + public void renderAsMarkdown(StringBuilder buffer) { + buffer.append("`"); + text.renderAsMarkdown(buffer); + buffer.append("`"); + } + + @Override + public void renderAsHtml(HtmlBuffer buffer) { + buffer.raw("
");
+				text.renderAsHtml(buffer);
+				buffer.raw("
"); + } + }; + } + public static Renderable paragraph(Renderable text) { return new Renderable() { diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index b322894f0..719408b41 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -94,6 +94,8 @@ import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClientAware; import org.springframework.ide.vscode.commons.languageserver.HighlightParams; +import org.springframework.ide.vscode.commons.languageserver.JavadocParams; +import org.springframework.ide.vscode.commons.languageserver.JavadocResponse; import org.springframework.ide.vscode.commons.languageserver.ProgressParams; import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; @@ -310,6 +312,11 @@ public class LanguageServerHarness { return CompletableFuture.completedFuture("ok"); } + @Override + public CompletableFuture javadoc(JavadocParams params) { + return CompletableFuture.completedFuture(new JavadocResponse()); + } + }); } diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF index 625386430..078952a08 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF @@ -7,6 +7,9 @@ Automatic-Module-Name: org.springframework.tooling.jdt.ls.commons Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.eclipse.core.runtime, org.eclipse.jdt.core, - org.eclipse.core.resources + org.eclipse.core.resources, + com.google.guava Export-Package: org.springframework.tooling.jdt.ls.commons, - org.springframework.tooling.jdt.ls.commons.classpath + org.springframework.tooling.jdt.ls.commons.classpath, + org.springframework.tooling.jdt.ls.commons.javadoc, + org.springframework.tooling.jdt.ls.commons.resources diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/javadoc/JavadocResponse.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/javadoc/JavadocResponse.java new file mode 100644 index 000000000..e63133857 --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/javadoc/JavadocResponse.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.jdt.ls.commons.javadoc; + +public class JavadocResponse { + + private String content; + + public JavadocResponse() { + super(); + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/javadoc/JavadocUtils.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/javadoc/JavadocUtils.java new file mode 100644 index 000000000..bc22192a2 --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/javadoc/JavadocUtils.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.jdt.ls.commons.javadoc; + +import java.io.IOException; +import java.io.Reader; +import java.net.URI; +import java.util.function.Function; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.ITypeParameter; +import org.springframework.tooling.jdt.ls.commons.resources.ResourceUtils; + +import com.google.common.io.CharStreams; + +public class JavadocUtils { + + @FunctionalInterface + public interface ReaderProvider { + Reader getReader(IJavaElement javaElement); + } + + private static String getString(Reader reader) { + try { + return CharStreams.toString(reader); + } catch (IOException ignored) { + //meh + } + return null; + } + + public static final String javadoc(Function readerProvider, URI projectUri, String bindingKey) throws Exception { + IJavaProject project = ResourceUtils.getJavaProject(projectUri); + IJavaElement element = project.findElement(bindingKey, null); + return computeJavadoc(readerProvider, element); + } + + private static String computeJavadoc(Function readerProvider, IJavaElement element) { + if (element == null) { + return null; + } + IMember member; + if (element instanceof ITypeParameter) { + member= ((ITypeParameter) element).getDeclaringMember(); + } else if (element instanceof IMember) { + member= (IMember) element; + } else if (element instanceof IPackageFragment) { + Reader r = readerProvider.apply(element); + if(r == null ) { + return null; + } + return getString(r); + } else { + return null; + } + + Reader r = readerProvider.apply(member); + if(r == null ) { + return null; + } + return getString(r); + } + +} diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/META-INF/MANIFEST.MF b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/META-INF/MANIFEST.MF index 69cf6e212..009dc59ba 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/META-INF/MANIFEST.MF +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/META-INF/MANIFEST.MF @@ -9,4 +9,6 @@ Require-Bundle: org.eclipse.jdt.ls.core, org.eclipse.core.runtime, org.eclipse.jdt.core, org.eclipse.core.resources, - org.springframework.tooling.jdt.ls.commons + org.springframework.tooling.jdt.ls.commons, + com.google.guava, + com.google.gson diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/plugin.xml b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/plugin.xml index ba904cbf3..9be3d0c12 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/plugin.xml +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/plugin.xml @@ -6,6 +6,12 @@ + + + + \ No newline at end of file diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/JavadocHandler.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/JavadocHandler.java new file mode 100644 index 000000000..347442046 --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/JavadocHandler.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.tooling.jdt.ls.extension; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; +import org.eclipse.jdt.ls.core.internal.javadoc.JavadocContentAccess2; +import org.springframework.tooling.jdt.ls.commons.Logger; +import org.springframework.tooling.jdt.ls.commons.javadoc.JavadocResponse; +import org.springframework.tooling.jdt.ls.commons.javadoc.JavadocUtils; + +public class JavadocHandler implements IDelegateCommandHandler { + + @Override + public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { + Map obj = (Map) arguments.get(0); + String uri = (String) obj.get("projectUri"); + URI projectUri = URI.create(uri); + String bindingKey = (String) obj.get("bindingKey"); + String content = JavadocUtils.javadoc(JavadocContentAccess2::getMarkdownContentReader, projectUri, bindingKey); + JavadocResponse response = new JavadocResponse(); + response.setContent(content); + return response; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index 79bd30132..ed1618537 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -26,9 +26,12 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.jandex.JandexIndex.JavadocProviderFactory; import org.springframework.ide.vscode.commons.java.ClasspathData; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.JavaProject; +import org.springframework.ide.vscode.commons.javadoc.JdtLsJavadocProvider; +import org.springframework.ide.vscode.commons.languageserver.JavadocParams; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListener; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; @@ -52,7 +55,7 @@ public class JdtLsProjectCache implements JavaProjectsService { private List listeners = new ArrayList<>(); private final Supplier fallback; - + public JdtLsProjectCache(SimpleLanguageServer server, Supplier fallback) { Assert.isNotNull(fallback); this.fallback = Suppliers.memoize(fallback); @@ -81,7 +84,8 @@ public class JdtLsProjectCache implements JavaProjectsService { } } else { log.debug("deleted = false"); - JavaProject newProject = new JavaProject(getFileObserver(), new URI(uri), new ClasspathData(event.name, event.classpath.getEntries())); + JdtLsJavadocProvider javadocProvider = new JdtLsJavadocProvider(server.getClient(), uri); + JavaProject newProject = new JavaProject(getFileObserver(), new URI(uri), new ClasspathData(event.name, event.classpath.getEntries()), classpathResource -> javadocProvider); JavaProject oldProject = table.put(uri, newProject); if (oldProject != null) { notifyChanged(newProject); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java index 56cab8310..9161a6287 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java @@ -147,7 +147,7 @@ public class ClassReferenceProvider extends CachingValueProvider { .fuzzySearchTypes(query, type -> allSubclasses.contains(type)) .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .flatMapIterable(l -> l) - .map(t -> StsValueHint.create(t.getT1())); + .map(t -> StsValueHint.create(javaProject, t.getT1())); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java index 94ae452ea..37b68267b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 2018 Pivotal, Inc. * 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 @@ -42,7 +42,7 @@ public class LoggerNameProvider extends CachingValueProvider { .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())), javaProject.getIndex() .fuzzySearchTypes(query, null) - .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())) + .map(t -> Tuples.of(StsValueHint.create(javaProject, t.getT1()), t.getT2())) ) .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .flatMapIterable(l -> l) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java index 0fe7deeab..fda693c63 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 2018 Pivotal, Inc. * 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 @@ -13,12 +13,14 @@ package org.springframework.ide.vscode.boot.metadata.hints; import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation; import org.springframework.ide.vscode.boot.configurationmetadata.ValueHint; +import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; +import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.util.DeprecationUtil; +import org.springframework.ide.vscode.boot.metadata.util.PropertyDocUtils; import org.springframework.ide.vscode.commons.java.IJavaElement; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.javadoc.IJavadoc; import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.Renderable; @@ -58,8 +60,8 @@ public class StsValueHint { /** * Creates a hint out of an IJavaElement. */ - public static StsValueHint create(String value, IJavaElement javaElement) { - return new StsValueHint(value, javaDocSnippet(javaElement), DeprecationUtil.extract(javaElement)) { + public static StsValueHint create(String value, IJavaProject project, IJavaElement javaElement) { + return new StsValueHint(value, javaDocSnippet(project, javaElement), DeprecationUtil.extract(javaElement)) { @Override public IJavaElement getJavaElement() { return javaElement; @@ -81,7 +83,7 @@ public class StsValueHint { if (jp!=null) { IType type = jp.findType(fqName); if (type!=null) { - return create(type); + return create(jp, type); } } } catch (Exception e) { @@ -90,8 +92,8 @@ public class StsValueHint { return null; } - public static StsValueHint create(IType klass) { - return new StsValueHint(klass.getFullyQualifiedName(), javaDocSnippet(klass), DeprecationUtil.extract(klass)) { + public static StsValueHint create(IJavaProject project, IType klass) { + return new StsValueHint(klass.getFullyQualifiedName(), javaDocSnippet(project, klass), DeprecationUtil.extract(klass)) { @Override public IJavaElement getJavaElement() { return klass; @@ -117,14 +119,10 @@ public class StsValueHint { return description; } - private static Renderable javaDocSnippet(IJavaElement je) { + private static Renderable javaDocSnippet(IJavaProject project, IJavaElement je) { return Renderables.lazy(() -> { - IJavadoc jdoc = je.getJavaDoc(); - if (jdoc != null) { - return jdoc.getRenderable(); - } else { - return Renderables.NO_DESCRIPTION; - } + SourceLinks sourceLinks = SourceLinkFactory.createSourceLinks(null); + return PropertyDocUtils.documentation(sourceLinks, project, je); }); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java index 8862fdf3b..5d5cabb56 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java @@ -122,7 +122,7 @@ public class TypeUtil { } private IJavaProject javaProject; - + public TypeUtil(IJavaProject jp) { //Note javaProject is allowed to be null, but only in unit testing context // (This is so some tests can be run without an explicit jp needing to be created) @@ -313,10 +313,10 @@ public class TypeUtil { type.getFields().filter(f -> f.isEnumConstant()).forEach(f -> { String rawName = f.getElementName(); if (addOriginal) { - enums.add(StsValueHint.create(rawName, f)); + enums.add(StsValueHint.create(rawName, javaProject, f)); } if (addLowerCased) { - enums.add(StsValueHint.create(StringUtil.upperCaseToHyphens(rawName), f)); + enums.add(StsValueHint.create(StringUtil.upperCaseToHyphens(rawName), javaProject, f)); } }); return enums.build(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/util/PropertyDocUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/util/PropertyDocUtils.java new file mode 100644 index 000000000..17dca288a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/util/PropertyDocUtils.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.metadata.util; + +import java.util.Optional; + +import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; +import org.springframework.ide.vscode.boot.java.links.SourceLinks; +import org.springframework.ide.vscode.commons.java.IJavaElement; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IMember; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.javadoc.IJavadoc; +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.Renderables; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +/** + * Boot properties documentation info utils + * + * @author Alex Boyko + * + */ +public class PropertyDocUtils { + + /** + * Generates documentation for boot property coming from java element + * + * @param sourceLinks + * @param project + * @param je + * @return + */ + public static Renderable documentation(SourceLinks sourceLinks, IJavaProject project, IJavaElement je) { + IJavadoc javadoc = je.getJavaDoc(); + Builder renderableBuilder = ImmutableList.builder(); + renderableBuilder.add(javadoc == null ? Renderables.NO_DESCRIPTION: javadoc.getRenderable()); + if (je instanceof IMember) { + IType containingType = je instanceof IType ? (IType) je : ((IMember)je).getDeclaringType(); + if (je != null) { + renderableBuilder.add(Renderables.lineBreak()); + renderableBuilder.add(Renderables.text("Type: ")); + String type = containingType.getFullyQualifiedName(); + Optional url = SourceLinkFactory.createSourceLinks(null).sourceLinkUrlForFQName(project, type); + if (url.isPresent()) { + renderableBuilder.add(Renderables.link(type, url.get())); + } else { + renderableBuilder.add(Renderables.inlineSnippet(Renderables.text(type))); + } + } + } + return Renderables.concat(renderableBuilder.build()); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java index e6840dd3e..dba2a38e9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java @@ -130,19 +130,25 @@ class PropertiesHoverCalculator { private Renderable createRenderable(StsValueHint hint) { Renderable description = hint.getDescription(); - /* - * HACK: javadoc comment from HTML javadoc provider coming from - * generated HTML javadoc is very rich and decorating it further - * with some header like labels just makes it look worse - */ - if (description.toHtml().indexOf(" renderableBuilder = ImmutableList.builder(); - renderableBuilder.add(bold(text(hint.getValue()))); - renderableBuilder.add(paragraph(description)); - return concat(renderableBuilder.build()); - } else { - return description; + try { + /** + * TODO: remove in the future once javadoc is obtained via the client from JDT LS + */ + /* + * HACK: javadoc comment from HTML javadoc provider coming from + * generated HTML javadoc is very rich and decorating it further + * with some header like labels just makes it look worse + */ + if (description.toHtml().indexOf(" renderableBuilder = ImmutableList.builder(); + renderableBuilder.add(bold(text(hint.getValue()))); + renderableBuilder.add(paragraph(description)); + return concat(renderableBuilder.build()); + } + } catch (Throwable t) { + // Ignore. Might be that HTML content not supported } + return description; } /** diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java index 8696654d9..9789f32f7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.yaml.completions; +import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.DEEMP_EXISTS; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -18,10 +20,13 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.common.InformationTemplates; import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory; import org.springframework.ide.vscode.boot.common.RelaxedNameConfig; import org.springframework.ide.vscode.boot.configurationmetadata.Deprecation; +import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory; import org.springframework.ide.vscode.boot.metadata.IndexNavigator; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.hints.HintProvider; @@ -33,10 +38,10 @@ import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyNameMode; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode; import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; +import org.springframework.ide.vscode.boot.metadata.util.PropertyDocUtils; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IJavaElement; import org.springframework.ide.vscode.commons.java.IMember; -import org.springframework.ide.vscode.commons.javadoc.IJavadoc; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.languageserver.completion.LazyProposalApplier; @@ -44,12 +49,11 @@ import org.springframework.ide.vscode.commons.languageserver.completion.Scoreabl import org.springframework.ide.vscode.commons.util.CollectionUtil; import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; -import org.springframework.ide.vscode.commons.util.text.DocumentRegion; import org.springframework.ide.vscode.commons.util.FuzzyMatcher; -import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.util.text.DocumentRegion; import org.springframework.ide.vscode.commons.yaml.completion.AbstractYamlAssistContext; import org.springframework.ide.vscode.commons.yaml.completion.TopLevelAssistContext; import org.springframework.ide.vscode.commons.yaml.completion.YamlAssistContext; @@ -66,13 +70,13 @@ import org.springframework.ide.vscode.commons.yaml.util.YamlUtil; import com.google.common.collect.ImmutableList; -import static org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal.*; - /** * Represents a context insied a "application.yml" file relative to which we can provide * content assistance. */ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistContext { + + private static Logger log = LoggerFactory.getLogger(ApplicationYamlAssistContext.class); protected final RelaxedNameConfig conf; @@ -241,7 +245,7 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon } } } catch (Exception e) { - Log.log(e); + log.error("", e); } return Collections.emptySet(); } @@ -545,15 +549,12 @@ public abstract class ApplicationYamlAssistContext extends AbstractYamlAssistCon if (jes != null) { for (IJavaElement je : jes) { if (je instanceof IMember) { - IJavadoc javadoc = je.getJavaDoc(); - if (javadoc != null) { - return javadoc.getRenderable(); - } + return PropertyDocUtils.documentation(SourceLinkFactory.createSourceLinks(null), typeUtil.getJavaProject(), je); } } } } catch (Exception e) { - Log.log(e); + log.error("", e); } return Renderables.NO_DESCRIPTION; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java index 89ef6c6c3..7d93071e1 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java @@ -21,9 +21,7 @@ import java.nio.file.Paths; import org.apache.commons.io.FileUtils; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.JavaProject; -import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; -import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; +import org.springframework.ide.vscode.commons.java.LegacyJavaProject; import org.springframework.ide.vscode.commons.maven.MavenBuilder; import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; @@ -91,7 +89,7 @@ public class ProjectsHarness { } public static final IJavaProject dummyProject() throws URISyntaxException { - return new JavaProject(new BasicFileObserver(), new URI("file:///someplace/nonexistent"), new DelegatingCachedClasspath(() -> null, null)); + return new LegacyJavaProject(new BasicFileObserver(), new URI("file:///someplace/nonexistent"), null, new DelegatingCachedClasspath(() -> null, null)); } private ProjectsHarness(FileObserver fileObserver) { diff --git a/vscode-extensions/commons-vscode/src/javadoc.ts b/vscode-extensions/commons-vscode/src/javadoc.ts new file mode 100644 index 000000000..b4034a069 --- /dev/null +++ b/vscode-extensions/commons-vscode/src/javadoc.ts @@ -0,0 +1,23 @@ +'use strict'; + + +import * as VSCode from 'vscode'; +import { LanguageClient, RequestType } from 'vscode-languageclient'; + +export function registerJavadocService(client : LanguageClient) : void { + + let addRequest = new RequestType("sts/javadoc"); + client.onRequest(addRequest, async (params: JavadocParams) => + await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.javadoc", params) + ); + +} + +interface JavadocParams { + projectUri: string; + bindingKey: string; +} + +interface JavadocResponse { + content: string; +} diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index c7f5857e2..418a144da 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -17,6 +17,7 @@ import { tmpdir } from 'os'; import { JVM, findJvm, findJdk } from '@pivotal-tools/jvm-launch-utils'; import { registerClasspathService } from './classpath'; import { registerProjectService } from './project'; +import { registerJavadocService } from './javadoc'; let p2c = P2C.createConverter(); @@ -229,6 +230,7 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser }); registerProjectService(client); registerClasspathService(client); + registerJavadocService(client); return client; }); }