From 652f5dcbcdb0e218f287f1bed1b61c3b0aa64d1a Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 2 May 2019 11:51:57 -0400 Subject: [PATCH] PT #165080768, #165662425: Selective Java, XML symbol scanning --- atom-extensions/atom-spring-boot/package.json | 10 ++ .../ls/BootLanguageServerPreferencesPage.java | 55 +++++---- .../tooling/boot/ls/Constants.java | 2 + .../DelegatingStreamConnectionProvider.java | 12 +- .../tooling/boot/ls/PrefsInitializer.java | 10 +- .../java-data-json/Map.json | 4 +- .../java-data-json/NestedRouter3.json | 4 +- .../java-data-json/Quote.json | 4 +- .../java-data-json/ServerProperties.json | 4 +- .../gradle/GradleProjectClasspath.java | 9 ++ .../vscode/commons/java/IClasspathUtil.java | 36 ++++++ .../test/resources/java-data-json/Map.json | 18 +-- .../java-data-json/NestedRouter3.json | 16 +-- .../test/resources/java-data-json/Quote.json | 16 +-- .../java-data-json/ServerProperties.json | 18 +-- .../commons/protocol/java/Classpath.java | 39 +++++- .../maven/java/MavenProjectClasspath.java | 24 ++++ .../commons/maven/MavenProjectCacheTest.java | 8 +- .../ls/commons/classpath/ClasspathUtil.java | 12 +- .../ide/vscode/boot/app/BootJavaConfig.java | 28 +++++ .../vscode/boot/app/SpringSymbolIndex.java | 69 ++++++++--- .../vscode/boot/java/utils/SpringIndexer.java | 4 +- .../boot/java/utils/SpringIndexerJava.java | 111 ++++++++++++++++-- .../boot/java/utils/SpringIndexerXML.java | 85 ++++++++++++-- .../vscode/boot/java/utils/SymbolHandler.java | 2 + .../boot/java/utils/SymbolIndexConfig.java | 74 ++++++++++++ .../test/SpringIndexerMultiProjectTest.java | 3 +- .../java/utils/test/SpringIndexerTest.java | 18 ++- .../test/SpringIndexerXMLProjectTest.java | 54 ++++++++- .../test-annotation-indexing/pom.xml | 5 + .../src/test/java/demo/ApplicationTests.java | 16 +++ .../src/browser/boot-preferences.ts | 34 ++++-- .../vscode-spring-boot/package-lock.json | 4 +- .../vscode-spring-boot/package.json | 10 ++ 34 files changed, 701 insertions(+), 117 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolIndexConfig.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/src/test/java/demo/ApplicationTests.java diff --git a/atom-extensions/atom-spring-boot/package.json b/atom-extensions/atom-spring-boot/package.json index 957aeefa6..853964d23 100644 --- a/atom-extensions/atom-spring-boot/package.json +++ b/atom-extensions/atom-spring-boot/package.json @@ -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, diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java index 7104883f0..cb461c824 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/BootLanguageServerPreferencesPage.java @@ -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)); } diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java index cff602de6..2ea6c1139 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/Constants.java @@ -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"; } diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java index ba755affd..cbfffdd38 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java @@ -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 bootHint = new HashMap<>(); Map supportXML = new HashMap<>(); Map bootChangeDetection = new HashMap<>(); + Map 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() diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java index b96d8474c..eb1ddfaa5 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/PrefsInitializer.java @@ -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); } } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Map.json b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Map.json index 3bcd10fdb..a4e8ddb3f 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Map.json +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Map.json @@ -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", diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/NestedRouter3.json b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/NestedRouter3.json index fb4b0ca27..e8075952c 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/NestedRouter3.json +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/NestedRouter3.json @@ -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", diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Quote.json b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Quote.json index 74c9e2dbc..cc6f6146e 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Quote.json +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/Quote.json @@ -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", diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/ServerProperties.json b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/ServerProperties.json index fe3068582..165e590c2 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/ServerProperties.json +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.commons.test/java-data-json/ServerProperties.json @@ -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", diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java index 5020d33f4..14a0b8454 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java @@ -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(); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java index e82cb5551..19039b857 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java @@ -93,6 +93,42 @@ public class IClasspathUtil { } return Stream.empty(); } + + public static Stream 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 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 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 getOutputFolders(IClasspath classpath) { try { diff --git a/headless-services/commons/commons-java/src/test/resources/java-data-json/Map.json b/headless-services/commons/commons-java/src/test/resources/java-data-json/Map.json index 59dbe9520..a4e8ddb3f 100644 --- a/headless-services/commons/commons-java/src/test/resources/java-data-json/Map.json +++ b/headless-services/commons/commons-java/src/test/resources/java-data-json/Map.json @@ -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", diff --git a/headless-services/commons/commons-java/src/test/resources/java-data-json/NestedRouter3.json b/headless-services/commons/commons-java/src/test/resources/java-data-json/NestedRouter3.json index 8549f3c69..e8075952c 100644 --- a/headless-services/commons/commons-java/src/test/resources/java-data-json/NestedRouter3.json +++ b/headless-services/commons/commons-java/src/test/resources/java-data-json/NestedRouter3.json @@ -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", diff --git a/headless-services/commons/commons-java/src/test/resources/java-data-json/Quote.json b/headless-services/commons/commons-java/src/test/resources/java-data-json/Quote.json index 4386deb0b..cc6f6146e 100644 --- a/headless-services/commons/commons-java/src/test/resources/java-data-json/Quote.json +++ b/headless-services/commons/commons-java/src/test/resources/java-data-json/Quote.json @@ -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", diff --git a/headless-services/commons/commons-java/src/test/resources/java-data-json/ServerProperties.json b/headless-services/commons/commons-java/src/test/resources/java-data-json/ServerProperties.json index e098124bf..165e590c2 100644 --- a/headless-services/commons/commons-java/src/test/resources/java-data-json/ServerProperties.json +++ b/headless-services/commons/commons-java/src/test/resources/java-data-json/ServerProperties.json @@ -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", diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java index 37fac8dae..40538dc77 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java @@ -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(); + } + } \ No newline at end of file diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index 83aa60d57..7fde82402 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -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); } } diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java index 335e882c3..7f850491a 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java @@ -134,7 +134,7 @@ public class MavenProjectCacheTest { assertNotNull(cachedProject); ImmutableList 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 diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java index 6df033912..b5af7f5cc 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java @@ -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); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java index cd82a1787..85a39cdf5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java @@ -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 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"); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java index 54e65821f..2106b11d9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java @@ -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 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 configureIndexer(SymbolIndexConfig config) { + List> 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 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 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 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 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 content, SpringIndexer indexer) { this.project = project; this.docURI = docURI; this.lastModified = lastModified; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index 65dcc6ad9..b1a10ba04 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -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 content) throws Exception; void removeFile(IJavaProject project, String docURI) throws Exception; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index 687690557..a9764fd4d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -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 symbolProviders; private final SymbolCache cache; + private final JavaProjectFinder projectFinder; + private boolean scanTestJavaSources = false; - public SpringIndexerJava(SymbolHandler symbolHandler, AnnotationHierarchyAwareLookup symbolProviders, SymbolCache cache) { + public SpringIndexerJava(SymbolHandler symbolHandler, AnnotationHierarchyAwareLookup 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 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 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); + } + } + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java index d85982975..e1a61f711 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java @@ -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 namespaceHandler; private final SymbolCache cache; + private final JavaProjectFinder projectFinder; + + private String[] scanFolderGlobs = new String[0]; - public SpringIndexerXML(SymbolHandler handler, Map namespaceHandler, SymbolCache cache) { + public SpringIndexerXML(SymbolHandler handler, Map 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 content) throws Exception { List generatedSymbols = new ArrayList(); - 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 outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toList()); + String[] globs = scanFolderGlobs; + if (globs.length == 0) { + return new String[0]; + } + List matchers = new ArrayList<>(globs.length); + for (String glob : globs) { + matchers.add(FileSystems.getDefault().getPathMatcher("glob:" + glob)); + } + ImmutableList.Builder builder = ImmutableList.builder(); Files.walkFileTree(Paths.get(project.getLocationUri()), new FileVisitor() { @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); + } + } + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java index 1f0de3a07..fedc53f7d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolHandler.java @@ -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); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolIndexConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolIndexConfig.java new file mode 100644 index 000000000..e66dc254d --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolIndexConfig.java @@ -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(); + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerMultiProjectTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerMultiProjectTest.java index 8a2ff83ea..56044b0b3 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerMultiProjectTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerMultiProjectTest.java @@ -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(); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java index fb623ef65..4615aee25 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java @@ -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 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 { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java index 3ff718e69..612eae801 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java @@ -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 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 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 symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) { for (Iterator iterator = symbols.iterator(); iterator.hasNext();) { diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/pom.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/pom.xml index 1222b032d..baaad14fb 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/pom.xml +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/pom.xml @@ -25,6 +25,11 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/src/test/java/demo/ApplicationTests.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/src/test/java/demo/ApplicationTests.java new file mode 100644 index 000000000..34f49b849 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-annotation-indexing-parent/test-annotation-indexing/src/test/java/demo/ApplicationTests.java @@ -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() { + } + +} diff --git a/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts b/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts index a3910346a..741ae9da3 100644 --- a/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts +++ b/theia-extensions/theia-spring-boot/spring-boot/src/browser/boot-preferences.ts @@ -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; } diff --git a/vscode-extensions/vscode-spring-boot/package-lock.json b/vscode-extensions/vscode-spring-boot/package-lock.json index ff7d1d5f9..b800c14dd 100644 --- a/vscode-extensions/vscode-spring-boot/package-lock.json +++ b/vscode-extensions/vscode-spring-boot/package-lock.json @@ -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", diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 3bc122e1f..4836d240a 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -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,