PT #156965730: Get Javadoc from JDT LS or eclipse client via sts/javadoc
This commit is contained in:
@@ -10,8 +10,14 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.FeatureBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.FeatureNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -10,8 +10,14 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.FeatureBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.FeatureNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -10,8 +10,14 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.FeatureBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.FeatureNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -10,8 +10,14 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.FeatureBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.FeatureNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
<classpathentry kind="src" path="src/"/>
|
||||
<classpathentry exported="true" kind="lib" path="lib/remark-1.0.0.jar"/>
|
||||
<classpathentry exported="true" kind="lib" path="lib/jsoup-1.9.2.jar"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
1
eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.gitignore
vendored
Normal file
1
eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/lib/
|
||||
@@ -20,8 +20,14 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,4 +12,58 @@
|
||||
<artifactId>org.springframework.tooling.ls.eclipse.commons</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<!-- make sure lib dir is removed after clean to avoid "dirty" build -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>${basedir}/lib</directory>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<?m2e execute onConfiguration?>
|
||||
<id>get-libs</id>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<phase>validate</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>com.kotcrab.remark</groupId>
|
||||
<artifactId>remark</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.9.2</version>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<outputDirectory>${basedir}/lib/</outputDirectory>
|
||||
<!-- baseVersion is to avoid SNAPSHOT dependencies being copied with
|
||||
ever daily changing timestamp -->
|
||||
<useBaseVersion>true</useBaseVersion>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object> removeClasspathListener(ClasspathListenerParams classpathListenerParams);
|
||||
|
||||
|
||||
@JsonRequest("sts/javadoc")
|
||||
CompletableFuture<JavadocResponse> javadoc(JavadocParams params);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object> removeClasspathListener(ClasspathListenerParams params) {
|
||||
return CompletableFuture.completedFuture(classpathService.removeClasspathListener(params.getCallbackCommandId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<JavadocResponse> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<File, Supplier<Optional<IndexView>>> index;
|
||||
|
||||
private Map<File, Supplier<List<Tuple3<String, File, ClassInfo>>>> knownTypes;
|
||||
|
||||
private Map<File, Supplier<List<String>>> knownPackages;
|
||||
|
||||
private BasicJandexIndex[] baseIndex;
|
||||
|
||||
BasicJandexIndex(Collection<File> 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<IndexView> 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<IndexView> indexFolder(File folder) {
|
||||
Indexer indexer = new Indexer();
|
||||
for (Iterator<File> 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<IndexView> 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<File, ClassInfo> getClassByName(DotName fqName) {
|
||||
// First look for type in the base index array
|
||||
return (baseIndex == null ? Stream.<Tuple2<File, ClassInfo>>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<Tuple2<File, ClassInfo>> findMatch(DotName fqName) {
|
||||
return (baseIndex == null ? Stream.<Optional<Tuple2<File, ClassInfo>>>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<File> findClasspathResourceForType(String fqName) {
|
||||
Optional<Tuple2<File, ClassInfo>> match = findMatch(DotName.createSimple(fqName));
|
||||
return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null);
|
||||
}
|
||||
|
||||
private Stream<Tuple2<File, IndexView>> 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<Tuple3<String, File, ClassInfo>> getKnownTypesStream(File file) {
|
||||
Optional<IndexView> 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<String> getKnownPackages(File file) {
|
||||
Optional<IndexView> 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<Tuple3<File, ClassInfo, Double>> fuzzySearchTypes(String searchTerm) {
|
||||
Flux<Tuple3<File, ClassInfo, Double>> 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<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm) {
|
||||
Flux<Tuple2<String, Double>> 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<Tuple2<File, ClassInfo>> allSubtypesOf(DotName name, boolean isInterface) {
|
||||
Flux<Tuple2<File, ClassInfo>> flux = Flux.fromIterable(index.keySet()).publishOn(Schedulers.parallel()).flatMap(file -> {
|
||||
Optional<IndexView> 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<JandexIndex> 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<URL> 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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<File, Supplier<Optional<IndexView>>> index;
|
||||
|
||||
private JavadocProviderFactory javadocProviderFactory;
|
||||
|
||||
private Map<File, Supplier<List<Tuple2<String, IType>>>> knownTypes;
|
||||
|
||||
private Map<File, Supplier<List<String>>> knownPackages;
|
||||
|
||||
private Cache<File, IJavadocProvider> 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<File> 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<IndexView> 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<IndexView> indexFolder(File folder) {
|
||||
Indexer indexer = new Indexer();
|
||||
for (Iterator<File> 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<IndexView> 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.<IType>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<Tuple2<File, ClassInfo>> findMatch(DotName fqName) {
|
||||
return (baseIndex == null ? Stream.<Optional<Tuple2<File, ClassInfo>>>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<File> findClasspathResourceForType(String fqName) {
|
||||
Optional<Tuple2<File, ClassInfo>> match = findMatch(DotName.createSimple(fqName));
|
||||
return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null);
|
||||
return createType(getClassByName(DotName.createSimple(fqName)));
|
||||
}
|
||||
|
||||
private IType createType(Tuple2<File, ClassInfo> 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<Tuple2<File, IndexView>> 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<Tuple2<String, IType>> getKnownTypesStream(File file) {
|
||||
Optional<IndexView> 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<String> getKnownPackages(File file) {
|
||||
Optional<IndexView> 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<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, Predicate<IType> typeFilter) {
|
||||
Flux<Tuple2<IType, Double>> 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<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm) {
|
||||
Flux<Tuple2<String, Double>> 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<IType> allSubtypesOf(IType type) {
|
||||
DotName name = DotName.createSimple(type.getFullyQualifiedName());
|
||||
Flux<IType> flux = Flux.fromIterable(index.keySet()).publishOn(Schedulers.parallel()).flatMap(file -> {
|
||||
Optional<IndexView> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<JandexSystemLibsIndex> INSTANCE = Suppliers.memoize(() -> new JandexSystemLibsIndex());
|
||||
|
||||
private Cache<Path, JandexIndex> cache;
|
||||
private Cache<Path, BasicJandexIndex> cache;
|
||||
|
||||
private JandexSystemLibsIndex() {
|
||||
this.cache = CacheBuilder.newBuilder().build(new CacheLoader<Path, JandexIndex>() {
|
||||
this.cache = CacheBuilder.newBuilder().build(new CacheLoader<Path, BasicJandexIndex>() {
|
||||
|
||||
@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<File> jars) {
|
||||
return jars.stream().map(jar -> jar.toPath().getParent()).distinct().map(folder -> index(folder)).filter(Objects::nonNull).toArray(JandexIndex[]::new);
|
||||
public BasicJandexIndex[] fromJars(Collection<File> 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<File> 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;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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 = "<init>";
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<IField> 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<IJavaType> parameters) {
|
||||
List<Type> 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<IMethod> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@ import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
|
||||
public interface IJavaElement {
|
||||
String getElementName();
|
||||
IJavadoc getJavaDoc();
|
||||
String getBindingKey();
|
||||
boolean exists();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <code>null</code>
|
||||
* 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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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<IJavaType> parameters();
|
||||
|
||||
|
||||
boolean isConstructor();
|
||||
|
||||
@Override
|
||||
default File classpathContainer() {
|
||||
return getDeclaringType().classpathContainer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<BasicFileObserver, String> eventNoficator) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,4 +40,7 @@ public interface STS4LanguageClient extends LanguageClient {
|
||||
|
||||
@JsonRequest("sts/removeClasspathListener")
|
||||
CompletableFuture<Object> removeClasspathListener(ClasspathListenerParams classpathListenerParams);
|
||||
|
||||
@JsonRequest("sts/javadoc")
|
||||
CompletableFuture<JavadocResponse> javadoc(JavadocParams params);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<JandexIndex> 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<DependencyNode> 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<Artifact> artifacts = new LinkedHashSet<>();
|
||||
LinkedHashSet<Artifact> 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<ArtifactRepository> 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<ArtifactRepository> 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<ArtifactRepository> 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<ArtifactRepository> repositories) throws MavenException {
|
||||
return maven.resolve(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), CLASSIFIER_TESTSOURCES, repositories, maven.createExecutionRequest());
|
||||
}
|
||||
|
||||
|
||||
public Stream<Path> 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<MavenJavaProject> 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("<init>", Stream.empty());
|
||||
assertNotNull(method);
|
||||
|
||||
|
||||
String expected = String.join("\n",
|
||||
"<h4>ArrayList</h4>"
|
||||
);
|
||||
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",
|
||||
"<h4>getListeners</h4>",
|
||||
"<pre>public <a href=\"http://docs.oracle.com/javase/6/docs/api/java/util/Set.html?is-external=true\" title=\"class or interface in java.util\">Set</a><org.springframework.context.ApplicationListener<?>> getListeners()</pre>",
|
||||
"<div class=\"block\">Returns read-only ordered Set of the <code>ApplicationListener</code>s 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("<div class=\"block\">Comment for inner class</div>", javaDoc.getRenderable().toHtml());
|
||||
|
||||
|
||||
IField field = type.getField("innerField");
|
||||
assertNotNull(field);
|
||||
String expected = String.join("\n",
|
||||
String expected = String.join("\n",
|
||||
"<h4>innerField</h4>",
|
||||
"<pre>protected int innerField</pre>",
|
||||
"<div class=\"block\">Comment for inner field</div>"
|
||||
@@ -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("<div class=\"block\">Comment for level 2 nested class</div>", javaDoc.getRenderable().toHtml());
|
||||
|
||||
|
||||
IField field = type.getField("innerLevel2Field");
|
||||
assertNotNull(field);
|
||||
String expected = String.join("\n",
|
||||
String expected = String.join("\n",
|
||||
"<h4>innerLevel2Field</h4>",
|
||||
"<pre>protected int innerLevel2Field</pre>",
|
||||
"<div class=\"block\">Comment for level 2 inner field</div>"
|
||||
@@ -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 = "<div class=\"block\">Comment for Greeting class</div>";
|
||||
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",
|
||||
"<h4>size</h4>",
|
||||
"<pre>public int size()</pre>",
|
||||
@@ -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",
|
||||
"<h4>template</h4>",
|
||||
"<pre>public static final <a href=\"http://docs.oracle.com/javase/8/docs/api/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a> template</pre>",
|
||||
"<dl>",
|
||||
@@ -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",
|
||||
"<h4>corsConfigurer</h4>",
|
||||
"<pre>@Bean",
|
||||
"public org.springframework.web.servlet.config.annotation.WebMvcConfigurer corsConfigurer()</pre>"
|
||||
|
||||
@@ -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<String, MavenJavaProject> mavenProjectsCache = CacheBuilder.newBuilder().build(new CacheLoader<String, MavenJavaProject>() {
|
||||
|
||||
@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<Tuple2<IType, Double>> results = MavenCore.getDefault().getJavaIndexForJreLibs()
|
||||
.fuzzySearchTypes("util.Map", null)
|
||||
MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file");
|
||||
List<Tuple2<IType, Double>> 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<Tuple2<IType, Double>> results = MavenCore.getDefault().getJavaIndexForJreLibs()
|
||||
MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file");
|
||||
List<Tuple2<IType, Double>> 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<Tuple2<String, Double>> results = MavenCore.getDefault().getJavaIndexForJreLibs()
|
||||
MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file");
|
||||
List<Tuple2<String, Double>> 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("<init>", 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("<init>", 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");
|
||||
|
||||
@@ -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("<pre>");
|
||||
text.renderAsHtml(buffer);
|
||||
buffer.raw("</pre>");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Renderable paragraph(Renderable text) {
|
||||
return new Renderable() {
|
||||
|
||||
|
||||
@@ -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<S extends SimpleLanguageServerWrapper> {
|
||||
return CompletableFuture.completedFuture("ok");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<JavadocResponse> javadoc(JavadocParams params) {
|
||||
return CompletableFuture.completedFuture(new JavadocResponse());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<IJavaElement, Reader> 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<IJavaElement, Reader> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
<command id="sts.java.addClasspathListener"/>
|
||||
<command id="sts.java.removeClasspathListener"/>
|
||||
</delegateCommandHandler>
|
||||
<delegateCommandHandler
|
||||
class="org.springframework.tooling.jdt.ls.extension.JavadocHandler">
|
||||
<command
|
||||
id="sts.java.javadoc">
|
||||
</command>
|
||||
</delegateCommandHandler>
|
||||
</extension>
|
||||
</plugin>
|
||||
|
||||
@@ -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<Object> arguments, IProgressMonitor monitor) throws Exception {
|
||||
Map<String, Object> obj = (Map<String, Object>) 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Listener> listeners = new ArrayList<>();
|
||||
|
||||
private final Supplier<JavaProjectsService> fallback;
|
||||
|
||||
|
||||
public JdtLsProjectCache(SimpleLanguageServer server, Supplier<JavaProjectsService> 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);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<Renderable> 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<String> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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("<h") == -1) {
|
||||
Builder<Renderable> 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("<h") == -1) {
|
||||
Builder<Renderable> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
23
vscode-extensions/commons-vscode/src/javadoc.ts
Normal file
23
vscode-extensions/commons-vscode/src/javadoc.ts
Normal file
@@ -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<JavadocParams, JavadocResponse, void, void>("sts/javadoc");
|
||||
client.onRequest(addRequest, async (params: JavadocParams) =>
|
||||
<JavadocResponse> await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.javadoc", params)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
interface JavadocParams {
|
||||
projectUri: string;
|
||||
bindingKey: string;
|
||||
}
|
||||
|
||||
interface JavadocResponse {
|
||||
content: string;
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user