PT #165080768, #165662425: Selective Java, XML symbol scanning

This commit is contained in:
BoykoAlex
2019-05-02 11:51:57 -04:00
parent ad87ea5615
commit 652f5dcbcd
34 changed files with 701 additions and 117 deletions

View File

@@ -25,11 +25,21 @@
"default": true,
"description": "Enable/Disable Spring running Boot application live hints decorators in the source code"
},
"scan-java-test-sources.on": {
"type": "boolean",
"default": false,
"description": "Enable/Disable Java test sources files scanning"
},
"support-spring-xml-config.on": {
"type": "boolean",
"default": false,
"description": "Enable/Disable Support for Spring XML Config files"
},
"support-spring-xml-config.scan-folders-globs": {
"type": "string",
"default": "**/src/main/**",
"description": "Scan Spring XML in folders"
},
"change-detection.on": {
"type": "boolean",
"default": false,

View File

@@ -12,15 +12,18 @@ package org.springframework.tooling.boot.ls;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Group;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.springframework.tooling.ls.eclipse.commons.LanguageServerCommonsActivator;
@@ -35,6 +38,7 @@ import org.springframework.tooling.ls.eclipse.commons.preferences.PreferenceCons
public class BootLanguageServerPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public BootLanguageServerPreferencesPage() {
super(GRID);
}
/**
@@ -79,37 +83,48 @@ public class BootLanguageServerPreferencesPage extends FieldEditorPreferencePage
public void init(IWorkbench workbench) {
setPreferenceStore(BootLanguageServerPlugin.getDefault().getPreferenceStore());
}
@Override
protected void adjustGridLayout() {
// Keep empty
}
@Override
protected void createFieldEditors() {
Composite parent = getFieldEditorParent();
Label l = new Label(parent, SWT.NONE);
l.setFont(parent.getFont());
l.setText("Settings for Spring Boot Live Beans data:");
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
gd.grabExcessHorizontalSpace = false;
l.setLayoutData(gd);
final IPreferenceStore commonsLsPrefs = LanguageServerCommonsActivator.getInstance().getPreferenceStore();
Composite contents = new Composite(getFieldEditorParent(), SWT.NONE);
GridData gd = new GridData(GridData.FILL_BOTH);
contents.setLayoutData(gd);
contents.setLayout(new GridLayout());
Group liveBeansGroup = new Group(contents, SWT.NONE);
liveBeansGroup.setText("Spring Boot Live Beans");
liveBeansGroup.setLayout(new GridLayout(1, false));
liveBeansGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
addField(new BooleanFieldEditor(Constants.PREF_BOOT_HINTS, "Live Boot Hint Decorators", parent));
addField(new BooleanFieldEditor(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, "Highlights CodeLens (experimental)", parent) {
addField(new BooleanFieldEditor(Constants.PREF_BOOT_HINTS, "Live Boot Hint Decorators", liveBeansGroup));
addField(new BooleanFieldEditor(PreferenceConstants.HIGHLIGHT_CODELENS_PREFS, "Highlights CodeLens", liveBeansGroup) {
@Override
public IPreferenceStore getPreferenceStore() {
return commonsLsPrefs;
}
});
addField(new BooleanFieldEditor(Constants.PREF_CHANGE_DETECTION, "Live Boot Change Detection", liveBeansGroup));
addField(new BooleanFieldEditor(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, "Support Spring XML Config files (experimental)", parent));
addField(new BooleanFieldEditor(Constants.PREF_CHANGE_DETECTION, "Live Boot Change Detection", parent));
Group symbolGroup = new Group(contents, SWT.NONE);
symbolGroup.setText("Spring Symbols");
symbolGroup.setLayout(new GridLayout(1, false));
symbolGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
l = new Label(parent, SWT.NONE);
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
l.setLayoutData(gd);
addField(new BooleanFieldEditor(Constants.PREF_SCAN_JAVA_TEST_SOURCES, "Scan Java test sources", symbolGroup));
addField(new BooleanFieldEditor(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, "Scan Spring XML Config files (experimental)", symbolGroup));
Composite scanFoldersComposite = new Composite(symbolGroup, SWT.NONE);
scanFoldersComposite.setFont(symbolGroup.getFont());
scanFoldersComposite.setLayoutData(GridDataFactory.swtDefaults().span(2, 1).align(GridData.FILL, GridData.BEGINNING).grab(true, false).create());
scanFoldersComposite.setLayout(new GridLayout(2, false));
addField(new StringFieldEditor(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "Scan Spring XML in folders:", scanFoldersComposite));
}

View File

@@ -19,6 +19,8 @@ public class Constants {
public static final String PREF_BOOT_HINTS = "boot-java.boot-hints.on";
public static final String PREF_SUPPORT_SPRING_XML_CONFIGS = "boot-java.support-spring-xml-config.on";
public static final String PREF_XML_CONFIGS_SCAN_FOLDERS = "boot-java.support-spring-xml-config.scan-folders-globs";
public static final String PREF_SCAN_JAVA_TEST_SOURCES = "boot-java.scan-java-test-sources";
public static final String PREF_CHANGE_DETECTION = "boot-java.change-detection.on";
}

View File

@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.lsp4e.server.StreamConnectionProvider;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
@@ -206,14 +207,19 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
Map<String, Object> bootHint = new HashMap<>();
Map<String, Object> supportXML = new HashMap<>();
Map<String, Object> bootChangeDetection = new HashMap<>();
Map<String, Object> scanTestJavaSources = new HashMap<>();
bootHint.put("on", BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_BOOT_HINTS));
supportXML.put("on", BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS));
bootChangeDetection.put("on", BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_CHANGE_DETECTION));
IPreferenceStore preferenceStore = BootLanguageServerPlugin.getDefault().getPreferenceStore();
bootHint.put("on", preferenceStore.getBoolean(Constants.PREF_BOOT_HINTS));
supportXML.put("on", preferenceStore.getBoolean(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS));
supportXML.put("scan-folders-globs", preferenceStore.getString(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS));
bootChangeDetection.put("on", preferenceStore.getBoolean(Constants.PREF_CHANGE_DETECTION));
scanTestJavaSources.put("on", preferenceStore.getBoolean(Constants.PREF_SCAN_JAVA_TEST_SOURCES));
bootJavaObj.put("boot-hints", bootHint);
bootJavaObj.put("support-spring-xml-config", supportXML);
bootJavaObj.put("change-detection", bootChangeDetection);
bootJavaObj.put("scan-java-test-sources", scanTestJavaSources);
bootJavaObj.put("remote-apps", BootLanguageServerPlugin.getRemoteBootApps().getValues()
.stream()

View File

@@ -11,6 +11,7 @@
package org.springframework.tooling.boot.ls;
import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
import org.eclipse.jface.preference.IPreferenceStore;
/**
* Preferences initializer for Boot-Java LS extension
@@ -25,9 +26,12 @@ public class PrefsInitializer extends AbstractPreferenceInitializer {
@Override
public void initializeDefaultPreferences() {
BootLanguageServerPlugin.getDefault().getPreferenceStore().setDefault(Constants.PREF_BOOT_HINTS, true);
BootLanguageServerPlugin.getDefault().getPreferenceStore().setDefault(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, false);
BootLanguageServerPlugin.getDefault().getPreferenceStore().setDefault(Constants.PREF_CHANGE_DETECTION, false);
IPreferenceStore preferenceStore = BootLanguageServerPlugin.getDefault().getPreferenceStore();
preferenceStore.setDefault(Constants.PREF_BOOT_HINTS, true);
preferenceStore.setDefault(Constants.PREF_SUPPORT_SPRING_XML_CONFIGS, false);
preferenceStore.setDefault(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "**/src/main/**");
preferenceStore.setDefault(Constants.PREF_CHANGE_DETECTION, false);
preferenceStore.setDefault(Constants.PREF_SCAN_JAVA_TEST_SOURCES, false);
}
}

View File

@@ -703,7 +703,9 @@
"path": "/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/rt.jar",
"sourceContainerUrl": "file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/src.zip",
"isSystem": true,
"isOwn": false
"isOwn": false,
"isTest": false,
"isJavaContent": false
}
},
"fqName": "java.util.Map",

View File

@@ -53,7 +53,9 @@
"path": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/src/main/java",
"outputFolder": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/target/classes",
"isSystem": false,
"isOwn": true
"isOwn": true,
"isTest": false,
"isJavaContent": true
}
},
"fqName": "org.test.NestedRouter3",

View File

@@ -258,7 +258,9 @@
"path": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/src/main/java",
"outputFolder": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/target/classes",
"isSystem": false,
"isOwn": true
"isOwn": true,
"isTest": false,
"isJavaContent": true
}
},
"fqName": "org.test.Quote",

View File

@@ -615,7 +615,9 @@
"path": "/Users/aboyko/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.0.0.RELEASE/spring-boot-autoconfigure-2.0.0.RELEASE.jar",
"sourceContainerUrl": "file:/Users/aboyko/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.0.0.RELEASE/spring-boot-autoconfigure-2.0.0.RELEASE-sources.jar",
"isSystem": false,
"isOwn": false
"isOwn": false,
"isTest": false,
"isJavaContent": false
}
},
"fqName": "org.springframework.boot.autoconfigure.web.ServerProperties",

View File

@@ -98,6 +98,15 @@ public class GradleProjectClasspath implements IClasspath {
for (EclipseSourceDirectory sf : project.getSourceDirectories()) {
CPE cpe = createSourceCPE(project, sf);
cpe.setOwn(true);
// TODO: figure out how to differentiate source java folder from resources
cpe.setJavaContent(true);
boolean isTest = false;
try {
isTest = sf.getClasspathAttributes().stream().filter(attr -> "gradle_used_by_scope".equals(attr.getName()) && "test".equals(attr.getValue())).findFirst().isPresent();
} catch (Throwable t) {
log.error("{}", t);
}
cpe.setTest(isTest);
entries.add(cpe);
}
return entries.build();

View File

@@ -93,6 +93,42 @@ public class IClasspathUtil {
}
return Stream.empty();
}
public static Stream<File> getProjectJavaSourceFolders(IClasspath classpath) {
try {
if (classpath != null) {
return classpath.getClasspathEntries().stream().filter(Classpath::isProjectJavaSource)
.map(cpe -> new File(cpe.getPath()));
}
} catch (Exception e) {
log.error("", e);
}
return Stream.empty();
}
public static Stream<File> getProjectJavaSourceFoldersWithoutTests(IClasspath classpath) {
try {
if (classpath != null) {
return classpath.getClasspathEntries().stream().filter(Classpath::isProjectNonTestJavaSource)
.map(cpe -> new File(cpe.getPath()));
}
} catch (Exception e) {
log.error("", e);
}
return Stream.empty();
}
public static Stream<File> getProjectTestJavaSources(IClasspath classpath) {
try {
if (classpath != null) {
return classpath.getClasspathEntries().stream().filter(Classpath::isProjectTestJavaSource)
.map(cpe -> new File(cpe.getPath()));
}
} catch (Exception e) {
log.error("", e);
}
return Stream.empty();
}
public static Stream<File> getOutputFolders(IClasspath classpath) {
try {

View File

@@ -1,12 +1,5 @@
{
"fqName": "java.util.Map",
"bindingKey": "Ljava/util/Map;",
"clazz": false,
"annotation": false,
"interfaze": true,
"enam": false,
"superClassName": "java.lang.Object",
"superInterfaceNames": [],
"fields": [],
"methods": [
{
@@ -710,9 +703,18 @@
"path": "/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/rt.jar",
"sourceContainerUrl": "file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/src.zip",
"isSystem": true,
"isOwn": false
"isOwn": false,
"isTest": false,
"isJavaContent": false
}
},
"fqName": "java.util.Map",
"clazz": false,
"annotation": false,
"interfaze": true,
"enam": false,
"superClassName": "java.lang.Object",
"superInterfaceNames": [],
"flags": 1537,
"name": "Map",
"handleIdentifier": "\u003dtest-webflux-project/\\/Library\\/Java\\/JavaVirtualMachines\\/jdk1.8.0_151.jdk\\/Contents\\/Home\\/jre\\/lib\\/rt.jar\u003cjava.util(Map.class[Map",

View File

@@ -1,11 +1,5 @@
{
"fqName": "org.test.NestedRouter3",
"bindingKey": "Lorg/test/NestedRouter3;",
"clazz": true,
"annotation": false,
"interfaze": false,
"enam": false,
"superInterfaceNames": [],
"fields": [],
"methods": [
{
@@ -59,9 +53,17 @@
"path": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/src/main/java",
"outputFolder": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/target/classes",
"isSystem": false,
"isOwn": true
"isOwn": true,
"isTest": false,
"isJavaContent": true
}
},
"fqName": "org.test.NestedRouter3",
"clazz": true,
"annotation": false,
"interfaze": false,
"enam": false,
"superInterfaceNames": [],
"flags": 1,
"name": "NestedRouter3",
"handleIdentifier": "\u003dtest-webflux-project/src\\/main\\/java\u003corg.test{NestedRouter3.java[NestedRouter3",

View File

@@ -1,11 +1,5 @@
{
"fqName": "org.test.Quote",
"bindingKey": "Lorg/test/Quote;",
"clazz": true,
"annotation": false,
"interfaze": false,
"enam": false,
"superInterfaceNames": [],
"fields": [
{
"bindingKey": "Lorg/test/Quote;.MATH_CONTEXT",
@@ -264,9 +258,17 @@
"path": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/src/main/java",
"outputFolder": "/Users/aboyko/Documents/junit-workspace/test-webflux-project/target/classes",
"isSystem": false,
"isOwn": true
"isOwn": true,
"isTest": false,
"isJavaContent": true
}
},
"fqName": "org.test.Quote",
"clazz": true,
"annotation": false,
"interfaze": false,
"enam": false,
"superInterfaceNames": [],
"flags": 1,
"name": "Quote",
"handleIdentifier": "\u003dtest-webflux-project/src\\/main\\/java\u003corg.test{Quote.java[Quote",

View File

@@ -1,12 +1,5 @@
{
"fqName": "org.springframework.boot.autoconfigure.web.ServerProperties",
"bindingKey": "Lorg/springframework/boot/autoconfigure/web/ServerProperties;",
"clazz": true,
"annotation": false,
"interfaze": false,
"enam": false,
"superClassName": "java.lang.Object",
"superInterfaceNames": [],
"fields": [
{
"bindingKey": "Lorg/springframework/boot/autoconfigure/web/ServerProperties;.port",
@@ -622,9 +615,18 @@
"path": "/Users/aboyko/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.0.0.RELEASE/spring-boot-autoconfigure-2.0.0.RELEASE.jar",
"sourceContainerUrl": "file:/Users/aboyko/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.0.0.RELEASE/spring-boot-autoconfigure-2.0.0.RELEASE-sources.jar",
"isSystem": false,
"isOwn": false
"isOwn": false,
"isTest": false,
"isJavaContent": false
}
},
"fqName": "org.springframework.boot.autoconfigure.web.ServerProperties",
"clazz": true,
"annotation": false,
"interfaze": false,
"enam": false,
"superClassName": "java.lang.Object",
"superInterfaceNames": [],
"flags": 1,
"name": "ServerProperties",
"handleIdentifier": "\u003dtest-webflux-project/\\/Users\\/aboyko\\/.m2\\/repository\\/org\\/springframework\\/boot\\/spring-boot-autoconfigure\\/2.0.0.RELEASE\\/spring-boot-autoconfigure-2.0.0.RELEASE.jar\u003corg.springframework.boot.autoconfigure.web(ServerProperties.class[ServerProperties",

View File

@@ -58,6 +58,8 @@ public class Classpath {
private URL javadocContainerUrl;
private boolean isSystem = false;
private boolean isOwn = false;
private boolean isTest = false;
private boolean isJavaContent = false;
public String getOutputFolder() {
return outputFolder;
@@ -133,19 +135,37 @@ public class Classpath {
this.isOwn = isOwn;
}
public boolean isTest() {
return isTest;
}
public void setTest(boolean isTest) {
this.isTest = isTest;
}
public boolean isJavaContent() {
return isJavaContent;
}
public void setJavaContent(boolean isJavaContent) {
this.isJavaContent = isJavaContent;
}
@Override
public String toString() {
return "CPE [kind=" + kind + ", path=" + path + ", outputFolder=" + outputFolder + ", sourceContainerUrl="
+ sourceContainerUrl + ", javadocContainerUrl=" + javadocContainerUrl + ", isSystem=" + isSystem
+ ", isOwn=" + isOwn + "]";
+ ", isOwn=" + isOwn + ", isTest=" + isTest + ", isJavaContent=" + isJavaContent + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (isJavaContent ? 1231 : 1237);
result = prime * result + (isOwn ? 1231 : 1237);
result = prime * result + (isSystem ? 1231 : 1237);
result = prime * result + (isTest ? 1231 : 1237);
result = prime * result + ((javadocContainerUrl == null) ? 0 : javadocContainerUrl.hashCode());
result = prime * result + ((kind == null) ? 0 : kind.hashCode());
result = prime * result + ((outputFolder == null) ? 0 : outputFolder.hashCode());
@@ -163,10 +183,14 @@ public class Classpath {
if (getClass() != obj.getClass())
return false;
CPE other = (CPE) obj;
if (isJavaContent != other.isJavaContent)
return false;
if (isOwn != other.isOwn)
return false;
if (isSystem != other.isSystem)
return false;
if (isTest != other.isTest)
return false;
if (javadocContainerUrl == null) {
if (other.javadocContainerUrl != null)
return false;
@@ -208,5 +232,18 @@ public class Classpath {
public static boolean isProjectSource(CPE e) {
return isSource(e) && e.isOwn();
}
public static boolean isProjectJavaSource(CPE cpe) {
return isProjectSource(cpe) && cpe.isJavaContent();
}
public static boolean isProjectNonTestJavaSource(CPE cpe) {
return isProjectJavaSource(cpe) && !cpe.isTest();
}
public static boolean isProjectTestJavaSource(CPE cpe) {
return isProjectJavaSource(cpe) && cpe.isTest();
}
}

View File

@@ -123,6 +123,8 @@ public class MavenProjectClasspath implements IClasspath {
File outputFolder = new File(project.getBuild().getOutputDirectory());
CPE cpe = CPE.source(sourceFolder, outputFolder);
cpe.setOwn(true);
cpe.setTest(false);
cpe.setJavaContent(true);
safe(() -> {
String reportingDir = project.getModel().getReporting().getOutputDirectory();
if (reportingDir!=null) {
@@ -132,6 +134,24 @@ public class MavenProjectClasspath implements IClasspath {
});
entries.add(cpe);
}
{ //test/java
File sourceFolder = new File(project.getBuild().getTestSourceDirectory());
if (sourceFolder.exists()) {
File outputFolder = new File(project.getBuild().getTestOutputDirectory());
CPE cpe = CPE.source(sourceFolder, outputFolder);
cpe.setOwn(true);
cpe.setTest(true);
cpe.setJavaContent(true);
safe(() -> {
String reportingDir = project.getModel().getReporting().getOutputDirectory();
if (reportingDir!=null) {
File apidocs = new File(new File(reportingDir), "apidocs");
cpe.setJavadocContainerUrl(apidocs.toURI().toURL());
}
});
entries.add(cpe);
}
}
{ //main/resources
for (Resource resource : project.getBuild().getResources()) {
File sourceFolder = new File(resource.getDirectory());
@@ -141,6 +161,8 @@ public class MavenProjectClasspath implements IClasspath {
}
CPE cpe = CPE.source(sourceFolder, new File(targetPath));
cpe.setOwn(true);
cpe.setTest(false);
cpe.setJavaContent(false);
entries.add(cpe);
}
}
@@ -153,6 +175,8 @@ public class MavenProjectClasspath implements IClasspath {
}
CPE cpe = CPE.source(sourceFolder, targetPath==null ? null : new File(targetPath));
cpe.setOwn(true);
cpe.setTest(true);
cpe.setJavaContent(false);
entries.add(cpe);
}
}

View File

@@ -134,7 +134,7 @@ public class MavenProjectCacheTest {
assertNotNull(cachedProject);
ImmutableList<CPE> calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
assertEquals(50, calculatedClassPath.stream().filter(cpe -> !cpe.isSystem()).count());
assertEquals(51, calculatedClassPath.stream().filter(cpe -> !cpe.isSystem()).count());
fileObserver.notifyFileChanged(pomFile.toURI().toString());
assertNull(projectChanged[0]);
@@ -145,7 +145,7 @@ public class MavenProjectCacheTest {
assertNotNull(projectChanged[0]);
assertEquals(cachedProject, projectChanged[0]);
calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
assertEquals(51, calculatedClassPath.stream().filter(cpe -> !cpe.isSystem()).count());
assertEquals(52, calculatedClassPath.stream().filter(cpe -> !cpe.isSystem()).count());
fileObserver.notifyFileDeleted(pomFile.toURI().toString());
assertEquals(cachedProject, projectDeleted[0]);
@@ -191,7 +191,7 @@ public class MavenProjectCacheTest {
}).get(30, TimeUnit.SECONDS);
assertTrue(classpathCacheFile.exists());
assertEquals(50, project.getClasspath().getClasspathEntries().stream().filter(cpe -> !cpe.isSystem()).count());
assertEquals(51, project.getClasspath().getClasspathEntries().stream().filter(cpe -> !cpe.isSystem()).count());
progressDone.set(false);
@@ -200,7 +200,7 @@ public class MavenProjectCacheTest {
// Check loaded from cache file
project = cache.project(pomFile);
assertEquals(50, project.getClasspath().getClasspathEntries().stream().filter(cpe -> !cpe.isSystem()).count());
assertEquals(51, project.getClasspath().getClasspathEntries().stream().filter(cpe -> !cpe.isSystem()).count());
}
@Test

View File

@@ -131,7 +131,17 @@ public class ClasspathUtil {
return resolveDependencyProjectCPEs(projectPath);
} else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
CPE cpe = createSourceCPE(javaProject, entry);
cpe.setOwn(true);
if (cpe != null) {
cpe.setOwn(true);
cpe.setTest(entry.isTest());
// If any package fragment roots from classpath entry has children then there is java content in the source folder
for (IPackageFragmentRoot root : ((JavaProject)javaProject).computePackageFragmentRoots(entry)) {
if (root.hasChildren()) {
cpe.setJavaContent(true);
break;
}
}
}
return cpe == null ? null : Collections.singletonList(cpe);
}
}

View File

@@ -10,8 +10,13 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.app;
import java.nio.file.FileSystems;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.ide.vscode.commons.languageserver.util.ListenerList;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
@@ -26,6 +31,8 @@ import org.springframework.stereotype.Component;
*/
@Component
public class BootJavaConfig implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(BootJavaConfig.class);
//TODO: Consider changing this to something that raises Spring application events.
// I.e. like described in here: https://www.baeldung.com/spring-events
@@ -48,6 +55,27 @@ public class BootJavaConfig implements InitializingBean {
Boolean enabled = settings.getBoolean("boot-java", "support-spring-xml-config", "on");
return enabled != null && enabled.booleanValue();
}
public boolean isScanJavaTestSourcesEnabled() {
Boolean enabled = settings.getBoolean("boot-java", "scan-java-test-sources", "on");
return enabled != null && enabled.booleanValue();
}
public String[] xmlBeansFoldersToScan() {
String folders = settings.getString("boot-java", "support-spring-xml-config", "scan-folders-globs");
String[] patterns = folders == null ? new String[0] : folders.split("\\s*,\\s*");
// Validate patterns
List<String> validatedPatterns = new ArrayList<>(patterns.length);
for (String pattern : patterns) {
try {
FileSystems.getDefault().getPathMatcher("glob:" + pattern);
validatedPatterns.add(pattern);
} catch (Throwable t) {
log.error("Failed to parse glob pattern: '{}'", pattern);
}
}
return validatedPatterns.toArray(new String[validatedPatterns.size()]);
}
public boolean isChangeDetectionEnabled() {
Boolean enabled = settings.getBoolean("boot-java", "change-detection", "on");

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.app;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
@@ -48,6 +49,7 @@ import org.springframework.ide.vscode.boot.java.utils.SpringIndexerXMLNamespaceH
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerXMLNamespaceHandlerBeans;
import org.springframework.ide.vscode.boot.java.utils.SymbolCache;
import org.springframework.ide.vscode.boot.java.utils.SymbolHandler;
import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
@@ -61,6 +63,8 @@ import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import org.springframework.stereotype.Component;
import com.google.common.base.Supplier;
/**
* @author Martin Lippert
*/
@@ -136,12 +140,18 @@ public class SpringSymbolIndex implements InitializingBean {
public void addSymbol(IJavaProject project, String docURI, EnhancedSymbolInformation enhancedSymbol) {
SpringSymbolIndex.this.addSymbol(project, docURI, enhancedSymbol);
}
@Override
public void removeSymbols(IJavaProject project, String docURI) {
SpringSymbolIndex.this.removeSymbolsByDoc(project, docURI);
}
};
Map<String, SpringIndexerXMLNamespaceHandler> namespaceHandler = new HashMap<>();
namespaceHandler.put("http://www.springframework.org/schema/beans", new SpringIndexerXMLNamespaceHandlerBeans());
springIndexerXML = new SpringIndexerXML(handler, namespaceHandler, this.cache);
springIndexerJava = new SpringIndexerJava(handler, specificProviders, this.cache);
springIndexerXML = new SpringIndexerXML(handler, namespaceHandler, this.cache, projectFinder());
springIndexerJava = new SpringIndexerJava(handler, specificProviders, this.cache, projectFinder());
this.indexer = new SpringIndexer[] {springIndexerJava};
@@ -166,7 +176,11 @@ public class SpringSymbolIndex implements InitializingBean {
}
});
config.addListener(evt -> {
this.configureIndexer(config.isSpringXMLSupportEnabled());
this.configureIndexer(SymbolIndexConfig.builder()
.scanXml(config.isSpringXMLSupportEnabled())
.xmlScanFoldersGlobs(config.xmlBeansFoldersToScan())
.scanTestJavaSources(config.isScanJavaTestSourcesEnabled())
.build());
});
server.doOnInitialized(this::serverInitialized);
server.onShutdown(this::shutdown);
@@ -186,11 +200,13 @@ public class SpringSymbolIndex implements InitializingBean {
});
}
public void configureIndexer(boolean springXMLSupportEnabled) {
public CompletableFuture<Void> configureIndexer(SymbolIndexConfig config) {
List<CompletableFuture<?>> futuresList = new ArrayList<>();
synchronized (this) {
if (springXMLSupportEnabled && !(Arrays.asList(this.indexer).contains(springIndexerXML))) {
if (config.isScanXml() && !(Arrays.asList(this.indexer).contains(springIndexerXML))) {
this.indexer = new SpringIndexer[] {springIndexerJava, springIndexerXML};
futuresList.add(CompletableFuture.runAsync(() -> springIndexerXML.setScanFolderGlobs(config.getXmlScanFoldersGlobs())));
List<String> globPattern = Arrays.asList(springIndexerXML.getFileWatchPatterns());
watchXMLDeleteRegistration = getWorkspaceService().getFileObserver().onFileDeleted(globPattern, (file) -> {
@@ -202,10 +218,11 @@ public class SpringSymbolIndex implements InitializingBean {
watchXMLChangedRegistration = getWorkspaceService().getFileObserver().onFileChanged(globPattern, (file) -> {
updateDocument(new TextDocumentIdentifier(file).getUri(), null);
});
}
else if (!springXMLSupportEnabled && Arrays.asList(this.indexer).contains(springIndexerXML)) {
else if (!config.isScanXml() && Arrays.asList(this.indexer).contains(springIndexerXML)) {
this.indexer = new SpringIndexer[] {springIndexerJava};
futuresList.add(CompletableFuture.runAsync(() -> springIndexerXML.setScanFolderGlobs(new String[0])));
getWorkspaceService().getFileObserver().unsubscribe(watchXMLChangedRegistration);
getWorkspaceService().getFileObserver().unsubscribe(watchXMLCreatedRegistration);
@@ -214,8 +231,12 @@ public class SpringSymbolIndex implements InitializingBean {
watchXMLChangedRegistration = null;
watchXMLCreatedRegistration = null;
watchXMLDeleteRegistration = null;
} else if (config.isScanXml()) {
futuresList.add(CompletableFuture.runAsync(() -> springIndexerXML.setScanFolderGlobs(config.getXmlScanFoldersGlobs())));
}
futuresList.add(CompletableFuture.runAsync(() -> springIndexerJava.setScanTestJavaSources(config.isScanTestJavaSources())));
}
return CompletableFuture.allOf(futuresList.toArray(new CompletableFuture<?>[futuresList.size()]));
}
public void shutdown() {
@@ -291,7 +312,14 @@ public class SpringSymbolIndex implements InitializingBean {
try {
File file = new File(new URI(docURI));
long lastModified = file.lastModified();
String content = FileUtils.readFileToString(file);
Supplier<String> content = () -> {
try {
return FileUtils.readFileToString(file);
} catch (IOException e) {
log.error("{}", e);
return "";
}
};
UpdateItem updateItem = new UpdateItem(maybeProject.get(), docURI, lastModified, content, indexer);
futures.add(CompletableFuture.runAsync(updateItem, this.updateQueue));
@@ -323,12 +351,21 @@ public class SpringSymbolIndex implements InitializingBean {
try {
File file = new File(new URI(docURI));
long lastModified = file.lastModified();
Supplier<String> contentSupplier = () -> {
if (content == null) {
try {
return FileUtils.readFileToString(file);
} catch (IOException e) {
log.error("{}", e);
return "";
}
} else {
return content;
}
};
if (content == null) {
content = FileUtils.readFileToString(file);
}
UpdateItem updateItem = new UpdateItem(maybeProject.get(), docURI, lastModified, content, indexer);
UpdateItem updateItem = new UpdateItem(maybeProject.get(), docURI, lastModified, contentSupplier, indexer);
futures.add(CompletableFuture.runAsync(updateItem, this.updateQueue));
}
catch (Exception e) {
@@ -488,12 +525,12 @@ public class SpringSymbolIndex implements InitializingBean {
private class UpdateItem implements Runnable {
private final String docURI;
private final String content;
private final Supplier<String> content;
private final IJavaProject project;
private final SpringIndexer indexer;
private final long lastModified;
public UpdateItem(IJavaProject project, String docURI, long lastModified, String content, SpringIndexer indexer) {
public UpdateItem(IJavaProject project, String docURI, long lastModified, Supplier<String> content, SpringIndexer indexer) {
this.project = project;
this.docURI = docURI;
this.lastModified = lastModified;

View File

@@ -12,6 +12,8 @@ package org.springframework.ide.vscode.boot.java.utils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import com.google.common.base.Supplier;
/**
* @author Martin Lippert
*/
@@ -23,7 +25,7 @@ public interface SpringIndexer {
void initializeProject(IJavaProject project) throws Exception;
void removeProject(IJavaProject project) throws Exception;
void updateFile(IJavaProject project, String docURI, long lastModified, String content) throws Exception;
void updateFile(IJavaProject project, String docURI, long lastModified, Supplier<String> content) throws Exception;
void removeFile(IJavaProject project, String docURI) throws Exception;

View File

@@ -11,8 +11,10 @@
package org.springframework.ide.vscode.boot.java.utils;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
@@ -23,6 +25,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
@@ -46,9 +49,12 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.util.UriUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.base.Supplier;
/**
* @author Martin Lippert
*/
@@ -63,11 +69,15 @@ public class SpringIndexerJava implements SpringIndexer {
private final SymbolHandler symbolHandler;
private final AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders;
private final SymbolCache cache;
private final JavaProjectFinder projectFinder;
private boolean scanTestJavaSources = false;
public SpringIndexerJava(SymbolHandler symbolHandler, AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders, SymbolCache cache) {
public SpringIndexerJava(SymbolHandler symbolHandler, AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders, SymbolCache cache,
JavaProjectFinder projectFimder) {
this.symbolHandler = symbolHandler;
this.symbolProviders = symbolProviders;
this.cache = cache;
this.projectFinder = projectFimder;
}
@Override
@@ -100,8 +110,10 @@ public class SpringIndexerJava implements SpringIndexer {
}
@Override
public void updateFile(IJavaProject project, String docURI, long lastModified, String content) throws Exception {
scanFile(project, docURI, lastModified, content);
public void updateFile(IJavaProject project, String docURI, long lastModified, Supplier<String> content) throws Exception {
if (shouldProcessDocument(project, docURI)) {
scanFile(project, docURI, lastModified, content.get());
}
}
@Override
@@ -110,6 +122,14 @@ public class SpringIndexerJava implements SpringIndexer {
String file = new File(new URI(docURI)).getAbsolutePath();
this.cache.removeFile(cacheKey, file);
}
private boolean shouldProcessDocument(IJavaProject project, String docURI) {
Path path = Paths.get(URI.create(docURI));
return foldersToScan(project)
.filter(sourceFolder -> path.startsWith(sourceFolder.toPath()))
.findFirst()
.isPresent();
}
private void scanFile(IJavaProject project, String docURI, long lastModified, String content) throws Exception {
ASTParser parser = createParser(project, false);
@@ -355,13 +375,27 @@ public class SpringIndexerJava implements SpringIndexer {
.map(file -> file.getAbsolutePath())
.toArray(String[]::new);
}
private Stream<File> foldersToScan(IJavaProject project) {
IClasspath classpath = project.getClasspath();
return scanTestJavaSources ? IClasspathUtil.getProjectJavaSourceFolders(classpath)
: IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(classpath);
}
private String[] getFiles(IJavaProject project) throws Exception {
return Files.walk(Paths.get(project.getLocationUri()))
.filter(path -> path.getFileName().toString().endsWith(".java"))
.filter(Files::isRegularFile)
.map(path -> path.toAbsolutePath().toString())
.toArray(String[]::new);
return foldersToScan(project)
.flatMap(folder -> {
try {
return Files.walk(folder.toPath());
} catch (IOException e) {
log.error("{}", e);
return Stream.empty();
}
})
.filter(path -> path.getFileName().toString().endsWith(".java"))
.filter(Files::isRegularFile)
.map(path -> path.toAbsolutePath().toString())
.toArray(String[]::new);
}
private SymbolCacheKey getCacheKey(IJavaProject project) {
@@ -376,4 +410,65 @@ public class SpringIndexerJava implements SpringIndexer {
return new SymbolCacheKey(project.getElementName() + "-java-", DigestUtils.md5Hex(classpathIdentifier).toUpperCase());
}
public void setScanTestJavaSources(boolean scanTestJavaSources) {
if (this.scanTestJavaSources != scanTestJavaSources) {
this.scanTestJavaSources = scanTestJavaSources;
if (scanTestJavaSources) {
addTestsJavaSourcesToIndex();
} else {
removeTestJavaSourcesFromIndex();
}
}
}
private void removeTestJavaSourcesFromIndex() {
for (IJavaProject project : projectFinder.all()) {
Path[] testJavaFiles = IClasspathUtil.getProjectTestJavaSources(project.getClasspath()).flatMap(folder -> {
try {
return Files.walk(folder.toPath());
} catch (IOException e) {
log.error("{}", e);
return Stream.empty();
}
})
.filter(path -> path.getFileName().toString().endsWith(".java"))
.filter(Files::isRegularFile).toArray(Path[]::new);
try {
for (Path path : testJavaFiles) {
URI docUri = UriUtil.toUri(path.toFile());
symbolHandler.removeSymbols(project, docUri.toString());
}
} catch (Exception e) {
log.error("{}", e);
}
}
}
private void addTestsJavaSourcesToIndex() {
for (IJavaProject project : projectFinder.all()) {
Path[] testJavaFiles = IClasspathUtil.getProjectTestJavaSources(project.getClasspath()).flatMap(folder -> {
try {
return Files.walk(folder.toPath());
} catch (IOException e) {
log.error("{}", e);
return Stream.empty();
}
})
.filter(path -> path.getFileName().toString().endsWith(".java"))
.filter(Files::isRegularFile).toArray(Path[]::new);
try {
for (Path path : testJavaFiles) {
File file = path.toFile();
URI docUri = UriUtil.toUri(file);
String content = FileUtils.readFileToString(file);
scanFile(project, docUri.toString(), file.lastModified(), content);
}
} catch (Exception e) {
log.error("{}", e);
}
}
}
}

View File

@@ -13,13 +13,16 @@ package org.springframework.ide.vscode.boot.java.utils;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@@ -36,9 +39,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.util.UriUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
/**
@@ -51,16 +56,39 @@ public class SpringIndexerXML implements SpringIndexer {
private final SymbolHandler symbolHandler;
private final Map<String, SpringIndexerXMLNamespaceHandler> namespaceHandler;
private final SymbolCache cache;
private final JavaProjectFinder projectFinder;
private String[] scanFolderGlobs = new String[0];
public SpringIndexerXML(SymbolHandler handler, Map<String, SpringIndexerXMLNamespaceHandler> namespaceHandler, SymbolCache cache) {
public SpringIndexerXML(SymbolHandler handler, Map<String, SpringIndexerXMLNamespaceHandler> namespaceHandler,
SymbolCache cache, JavaProjectFinder projectFinder) {
this.symbolHandler = handler;
this.namespaceHandler = namespaceHandler;
this.cache = cache;
this.projectFinder = projectFinder;
}
public void setScanFolderGlobs(String[] scanFolderGlobs) {
if (!Arrays.equals(this.scanFolderGlobs, scanFolderGlobs)) {
clearIndex();
this.scanFolderGlobs = scanFolderGlobs;
populateIndex();
}
}
@Override
public String[] getFileWatchPatterns() {
return new String[] {"**/*.xml"};
String[] patterns = new String[scanFolderGlobs.length];
for (int i = 0; i < scanFolderGlobs.length; i++) {
StringBuilder sb = new StringBuilder();
sb.append(scanFolderGlobs[i]);
if (scanFolderGlobs[i].charAt(scanFolderGlobs[i].length() - 1) != '/') {
sb.append('/');
}
sb.append("*.xml");
patterns[i] = sb.toString();
}
return patterns;
}
@Override
@@ -112,11 +140,11 @@ public class SpringIndexerXML implements SpringIndexer {
}
@Override
public void updateFile(IJavaProject project, String docURI, long lastModified, String content) throws Exception {
public void updateFile(IJavaProject project, String docURI, long lastModified, Supplier<String> content) throws Exception {
List<CachedSymbol> generatedSymbols = new ArrayList<CachedSymbol>();
scanFile(project, content, docURI, lastModified, generatedSymbols);
scanFile(project, content.get(), docURI, lastModified, generatedSymbols);
SymbolCacheKey cacheKey = getCacheKey(project);
String file = new File(new URI(docURI)).getAbsolutePath();
@@ -188,20 +216,35 @@ public class SpringIndexerXML implements SpringIndexer {
}
private String[] getFiles(IJavaProject project) throws Exception {
List<Path> outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toList());
String[] globs = scanFolderGlobs;
if (globs.length == 0) {
return new String[0];
}
List<PathMatcher> matchers = new ArrayList<>(globs.length);
for (String glob : globs) {
matchers.add(FileSystems.getDefault().getPathMatcher("glob:" + glob));
}
ImmutableList.Builder<String> builder = ImmutableList.builder();
Files.walkFileTree(Paths.get(project.getLocationUri()), new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return outputFolders.contains(dir) ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString();
if (fileName.endsWith(".xml") && !"pom.xml".equals(fileName)) {
builder.add(file.toAbsolutePath().toString());
if (fileName.endsWith(".xml")) {
Path parent = file.getParent();
if (parent != null) {
for (PathMatcher matcher : matchers) {
if (matcher.matches(parent)) {
builder.add(file.toAbsolutePath().toString());
}
}
}
}
return FileVisitResult.CONTINUE;
}
@@ -232,5 +275,29 @@ public class SpringIndexerXML implements SpringIndexer {
return new SymbolCacheKey(project.getElementName() + "-xml-", DigestUtils.md5Hex(classpathIdentifier).toUpperCase());
}
private void clearIndex() {
for (IJavaProject project : projectFinder.all()) {
try {
for (String file : getFiles(project)) {
String docUri = UriUtil.toUri(new File(file)).toString();
symbolHandler.removeSymbols(project, docUri);
removeFile(project, docUri);
}
} catch (Exception e) {
log.error("{}", e);
}
}
}
private void populateIndex() {
for (IJavaProject project : projectFinder.all()) {
try {
initializeProject(project);
} catch (Exception e) {
log.error("{}", e);
}
}
}
}

View File

@@ -19,5 +19,7 @@ import org.springframework.ide.vscode.commons.java.IJavaProject;
public interface SymbolHandler {
void addSymbol(IJavaProject project, String docURI, EnhancedSymbolInformation enhancedSymbol);
void removeSymbols(IJavaProject project, String docURI);
}

View File

@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) 2019 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
public interface SymbolIndexConfig {
static class Builder {
private boolean scanXml = false;
private boolean scanTestJavaSources = false;
private String[] xmlScanFoldersGlobs = new String[0];
private Builder() {
}
public Builder scanXml(boolean scanXml) {
this.scanXml = scanXml;
return this;
}
public Builder scanTestJavaSources(boolean scanTestJavaSources) {
this.scanTestJavaSources = scanTestJavaSources;
return this;
}
public Builder xmlScanFoldersGlobs(String[] xmlScanFoldersGlobs) {
this.xmlScanFoldersGlobs = xmlScanFoldersGlobs;
return this;
}
public SymbolIndexConfig build() {
return new SymbolIndexConfig() {
@Override
public boolean isScanXml() {
return scanXml;
}
@Override
public boolean isScanTestJavaSources() {
return scanTestJavaSources;
}
@Override
public String[] getXmlScanFoldersGlobs() {
return xmlScanFoldersGlobs;
}
};
}
}
boolean isScanXml();
boolean isScanTestJavaSources();
String[] getXmlScanFoldersGlobs();
static Builder builder() {
return new Builder();
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.util.UriUtil;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
@@ -52,7 +53,7 @@ public class SpringIndexerMultiProjectTest {
@Before
public void setup() throws Exception {
harness.intialize(null);
indexer.configureIndexer(false);
indexer.configureIndexer(SymbolIndexConfig.builder().scanXml(false).build()).get(5, TimeUnit.SECONDS);
projectUri1 = UriUtil.toUri(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-large-multiproject-1/").toURI())).toString();
projectFinder.find(new TextDocumentIdentifier(projectUri1)).get();

View File

@@ -33,6 +33,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.util.Assert;
@@ -59,7 +60,7 @@ public class SpringIndexerTest {
@Before
public void setup() throws Exception {
harness.intialize(null);
indexer.configureIndexer(false);
indexer.configureIndexer(SymbolIndexConfig.builder().scanXml(false).build()).get(5, TimeUnit.SECONDS);;
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
projectDir = directory.toURI().toString();
@@ -92,6 +93,21 @@ public class SpringIndexerTest {
docUri = directory.toPath().resolve("src/main/java/org/test/ClassWithDefaultSymbol.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@Configurable", docUri, 4, 0, 4, 13));
}
@Test
public void testScanTestJavaSources() throws Exception {
indexer.configureIndexer(SymbolIndexConfig.builder().scanTestJavaSources(true).build()).get(1, TimeUnit.SECONDS);
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(8, allSymbols.size());
String docUri = directory.toPath().resolve("src/test/java/demo/ApplicationTests.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@SpringBootTest", docUri, 8, 0, 8, 15));
indexer.configureIndexer(SymbolIndexConfig.builder().scanTestJavaSources(false).build()).get(1, TimeUnit.SECONDS);
allSymbols = indexer.getAllSymbols("");
assertEquals(7, allSymbols.size());
assertFalse(containsSymbol(allSymbols, "@SpringBootTest", docUri, 8, 0, 8, 15));
}
@Test
public void testRetrievingSymbolsPerDocument() throws Exception {

View File

@@ -31,6 +31,7 @@ import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation;
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
@@ -56,7 +57,11 @@ public class SpringIndexerXMLProjectTest {
@Before
public void setup() throws Exception {
harness.intialize(null);
indexer.configureIndexer(true);
indexer.configureIndexer(SymbolIndexConfig.builder()
.scanXml(true)
.xmlScanFoldersGlobs(new String[] { "**/src/main/**", "**/config" })
.build())
.get(5, TimeUnit.SECONDS);;
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-xml-project/").toURI());
projectDir = directory.toURI().toString();
@@ -65,7 +70,7 @@ public class SpringIndexerXMLProjectTest {
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(5, TimeUnit.SECONDS);
initProject.get(50000, TimeUnit.SECONDS);
}
@Test
@@ -111,6 +116,51 @@ public class SpringIndexerXMLProjectTest {
assertEquals(1, addon.size());
assertEquals("sb", ((BeansSymbolAddOnInformation)addon.get(0)).getBeanID());
}
@Test
public void testReindexXMLConfig() throws Exception {
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(5, allSymbols.size());
indexer.configureIndexer(SymbolIndexConfig.builder()
.scanXml(true)
.build())
.get(2, TimeUnit.SECONDS);;
allSymbols = indexer.getAllSymbols("");
assertEquals(0, allSymbols.size());
indexer.configureIndexer(SymbolIndexConfig.builder()
.scanXml(true)
.xmlScanFoldersGlobs(new String[] { "**/src/main/**" })
.build())
.get(2, TimeUnit.SECONDS);
allSymbols = indexer.getAllSymbols("");
assertEquals(1, allSymbols.size());
indexer.configureIndexer(SymbolIndexConfig.builder()
.scanXml(true)
.xmlScanFoldersGlobs(new String[] { "**/config", "**/src/main/**" })
.build())
.get(2, TimeUnit.SECONDS);
allSymbols = indexer.getAllSymbols("");
assertEquals(5, allSymbols.size());
indexer.configureIndexer(SymbolIndexConfig.builder()
.scanXml(true)
.xmlScanFoldersGlobs(new String[] { "**/config" })
.build())
.get(2, TimeUnit.SECONDS);
allSymbols = indexer.getAllSymbols("");
assertEquals(4, allSymbols.size());
indexer.configureIndexer(SymbolIndexConfig.builder()
.scanXml(false)
.xmlScanFoldersGlobs(new String[] { "**/config", "**/src/main/**" })
.build())
.get(2, TimeUnit.SECONDS);
allSymbols = indexer.getAllSymbols("");
assertEquals(0, allSymbols.size());
}
private boolean containsSymbol(List<? extends SymbolInformation> symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) {
for (Iterator<? extends SymbolInformation> iterator = symbols.iterator(); iterator.hasNext();) {

View File

@@ -25,6 +25,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,16 @@
package demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Test
public void contextLoads() {
}
}

View File

@@ -13,30 +13,40 @@ import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceCo
// tslint:disable:max-line-length
export const HIGHLIGHTS_PREF_NAME = 'spring-boot.boot-hints.on';
export const XML_SUPPORT_PREF_NAME = 'spring-boot.support-spring-xml-config.on';
export const CODELENS_PREF_NAME = 'spring-boot.highlight-codelens.on';
export const HIGHLIGHTS_PREF_NAME = 'boot-java.boot-hints.on';
export const XML_SUPPORT_PREF_NAME = 'boot-java.support-spring-xml-config.on';
export const CODELENS_PREF_NAME = 'boot-java.highlight-codelens.on';
export const BootConfigSchema: PreferenceSchema = {
'type': 'object',
'title': 'Spring Boot Configuration',
properties: {
'spring-boot.boot-hints.on': {
'boot-java.boot-hints.on': {
type: 'boolean',
description: 'Enable/Disable Spring running Boot application live hints decorators in Java source code.',
default: true
},
'spring-boot.support-spring-xml-config.on': {
'boot-java.scan-java-test-sources.on': {
type: 'boolean',
description: 'Enable/Disable Java test sources files scanning',
default: false
},
'boot-java.support-spring-xml-config.on': {
type: 'boolean',
description: 'Enable/Disable Support for Spring XML Config files',
default: false
},
'spring-boot.change-detection.on': {
'boot-java.support-spring-xml-config.scan-folders-globs': {
type: 'string',
description: 'Scan Spring XML in folders',
default: '**/src/main/**'
},
'boot-java.change-detection.on': {
type: 'boolean',
description: 'Enable/Disable detecting changes of running Spring Boot applications.',
default: false
},
'spring-boot.highlight-codelens.on': {
'boot-java.highlight-codelens.on': {
type: 'boolean',
default: true,
description: 'Enable/Disable Spring running Boot application Code Lenses'
@@ -55,10 +65,12 @@ export const BootConfigSchema: PreferenceSchema = {
};
export interface BootConfiguration {
'spring-boot.boot-hints.on': boolean;
'spring-boot.support-spring-xml-config.on': boolean;
'spring-boot.change-detection.on': boolean;
'spring-boot.highlight-codelens.on': boolean;
'boot-java.boot-hints.on': boolean;
'boot-java.scan-java-test-sources.on': boolean;
'boot-java.support-spring-xml-config.on': boolean;
'boot-java.support-spring-xml-config.scan-folders-globs': string;
'boot-java.change-detection.on': boolean;
'boot-java.highlight-codelens.on': boolean;
'spring-boot.ls.javahome': string;
'spring-boot.ls.vmargs': string;
}

View File

@@ -1,12 +1,12 @@
{
"name": "vscode-spring-boot",
"version": "1.6.0",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@pivotal-tools/commons-vscode": {
"version": "file:../commons-vscode/pivotal-tools-commons-vscode-0.2.2.tgz",
"integrity": "sha512-hgbMlJKDCkG3xQsTNEKFcGgic/zuSkkqRD2HOx5xeHbPdFAuQbv7eXNNFRxaw1cLU8re/s4gHWAa56bESqP5RQ==",
"integrity": "sha512-4nZnq7/ehz1nfZcHz5YZInEfWowepSysA2fTW0bAKoTsAQivmP92qly88HXf4tM1tOLxahyqAK5Rx7lbbct2wQ==",
"requires": {
"@pivotal-tools/jvm-launch-utils": "0.0.11",
"deep-equal": "^1.0.1",

View File

@@ -73,11 +73,21 @@
"default": true,
"description": "Enable/Disable Spring running Boot application live hints decorators in Java source code"
},
"boot-java.scan-java-test-sources.on": {
"type": "boolean",
"default": false,
"description": "Enable/Disable Java test sources files scanning"
},
"boot-java.support-spring-xml-config.on": {
"type": "boolean",
"default": false,
"description": "Enable/Disable Support for Spring XML Config files"
},
"boot-java.support-spring-xml-config.scan-folders-globs": {
"type": "string",
"default": "**/src/main/**",
"description": "Scan Spring XML in folders"
},
"boot-java.highlight-codelens.on": {
"type": "boolean",
"default": true,