diff --git a/atom-extensions/atom-spring-boot/lib/boot-sts-adapter.ts b/atom-extensions/atom-spring-boot/lib/boot-sts-adapter.ts index d028a686f..37f036a5e 100644 --- a/atom-extensions/atom-spring-boot/lib/boot-sts-adapter.ts +++ b/atom-extensions/atom-spring-boot/lib/boot-sts-adapter.ts @@ -1,11 +1,16 @@ import {StsAdapter, HighlightParams} from '@pivotal-tools/atom-languageclient-commons'; import {Convert} from 'atom-languageclient'; import { Range } from 'vscode-languageserver-protocol'; -import {TextEditor} from 'atom'; +import {TextEditor, DecorationOptions } from 'atom'; -const BOOT_DATA_MARKER_TYPE: any = 'BootApp-Hint'; const BOOT_HINT_GUTTER_NAME = 'boot-hint-gutter'; +const DECORATION_OPTIONS: DecorationOptions = { + type: 'highlight', + class: 'boot-hint', + gutterName: BOOT_HINT_GUTTER_NAME +}; + export class BootStsAdapter extends StsAdapter { constructor() { @@ -17,7 +22,7 @@ export class BootStsAdapter extends StsAdapter { } private markHintsForEditor(editor: TextEditor, ranges: Range[]) { - editor.findMarkers(BOOT_DATA_MARKER_TYPE).forEach(m => m.destroy()); + editor.getDecorations(DECORATION_OPTIONS).map(decoration => decoration.getMarker()).forEach(m => m.destroy()); if (Array.isArray(ranges)) { ranges.forEach(range => this.createHintMarker(editor, range)); } @@ -33,13 +38,10 @@ export class BootStsAdapter extends StsAdapter { private createHintMarker(editor: TextEditor, range: Range) { // Create marker model - const marker = editor.markBufferRange(Convert.lsRangeToAtomRange(range), BOOT_DATA_MARKER_TYPE); + const marker = editor.markBufferRange(Convert.lsRangeToAtomRange(range)); // Marker around the text in the editor - editor.decorateMarker(marker, { - type: 'highlight', - class: 'boot-hint' - }); + editor.decorateMarker(marker, DECORATION_OPTIONS); // Marker in the diagnostic gutter let gutter = editor.gutterWithName(BOOT_HINT_GUTTER_NAME); @@ -53,4 +55,5 @@ export class BootStsAdapter extends StsAdapter { iconElement.setAttribute('class', 'gutter-boot-hint'); gutter.decorateMarker(marker, {item: iconElement}); } + } \ No newline at end of file diff --git a/atom-extensions/atom-spring-boot/package.json b/atom-extensions/atom-spring-boot/package.json index 8105e05f8..ae159e74b 100644 --- a/atom-extensions/atom-spring-boot/package.json +++ b/atom-extensions/atom-spring-boot/package.json @@ -29,6 +29,11 @@ "type": "boolean", "default": false, "description": "Enable/Disable detecting changes of running Spring Boot applications" + }, + "strict-project-matching.on": { + "type": "boolean", + "default": false, + "description": "Use restrictive mechanism to match live running boot apps automatically with projects in your workspace" } }, "dependencies": { 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 9a9284fcf..0f9c934e6 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 @@ -39,6 +39,9 @@ public class BootLanguageServerPreferencesPage extends FieldEditorPreferencePage BooleanFieldEditor liveChangeDetectionPrefEditor = new BooleanFieldEditor(Constants.PREF_CHANGE_DETECTION, "Live Boot Change Detection", getFieldEditorParent()); addField(liveChangeDetectionPrefEditor); + + BooleanFieldEditor strictProjectMatchingPrefEditor = new BooleanFieldEditor(Constants.PREF_STRICT_PROJECT_MATCH, "Use restrictive mechanism to match live running boot apps automatically with projects in your workspace", getFieldEditorParent()); + addField(strictProjectMatchingPrefEditor); } } 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 f187a70ac..1b55284de 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 @@ -20,5 +20,6 @@ public class Constants { public static final String PREF_BOOT_HINTS = "boot-java.boot-hints.on"; public static final String PREF_CHANGE_DETECTION = "boot-java.change-detection.on"; + public static final String PREF_STRICT_PROJECT_MATCH = "boot-java.strict-project-matching.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 b347e8c78..0cda14d40 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 @@ -125,13 +125,18 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi Map bootJavaObj = new HashMap<>(); Map bootHint = new HashMap<>(); Map bootChangeDetection = new HashMap<>(); + Map bootStrictProjectMatching = new HashMap<>(); bootHint.put("on", BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_BOOT_HINTS)); bootChangeDetection.put("on", BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_CHANGE_DETECTION)); + bootStrictProjectMatching.put("on", BootLanguageServerPlugin.getDefault().getPreferenceStore().getBoolean(Constants.PREF_STRICT_PROJECT_MATCH)); bootJavaObj.put("boot-hints", bootHint); bootJavaObj.put("change-detection", bootChangeDetection); + bootJavaObj.put("strict-project-matching", bootStrictProjectMatching); + settings.put("boot-java", bootJavaObj); + this.languageServer.getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(settings)); } 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 cb8fdaa5e..2d054b1d0 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 @@ -27,6 +27,7 @@ public class PrefsInitializer extends AbstractPreferenceInitializer { public void initializeDefaultPreferences() { BootLanguageServerPlugin.getDefault().getPreferenceStore().setDefault(Constants.PREF_BOOT_HINTS, true); BootLanguageServerPlugin.getDefault().getPreferenceStore().setDefault(Constants.PREF_CHANGE_DETECTION, false); + BootLanguageServerPlugin.getDefault().getPreferenceStore().setDefault(Constants.PREF_STRICT_PROJECT_MATCH, false); } } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java index 331b65e72..3dd28f924 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java @@ -100,7 +100,7 @@ public class InWorkspaceSymbolsProvider implements SymbolsProvider { public static InWorkspaceSymbolsProvider createFor(IProject project) { List languageServers = LanguageServiceAccessor.getLanguageServers(project, - capabilities -> Boolean.TRUE.equals(capabilities.getWorkspaceSymbolProvider())); + capabilities -> Boolean.TRUE.equals(capabilities.getWorkspaceSymbolProvider()), true); if (!languageServers.isEmpty()) { return new InWorkspaceSymbolsProvider(languageServers); } diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java index c5dba0f4b..51b536ba4 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java @@ -30,4 +30,11 @@ public interface SpringBootApp { Properties getSystemProperties() throws Exception; JMXConnector getJmxConnector() throws MalformedURLException, IOException; + default String getSystemProperties(String string) throws Exception { + Object r = getSystemProperties().get(string); + if (r instanceof String) { + return (String) r; + } + return null; + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java index 327876984..4eb42e670 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java @@ -16,6 +16,8 @@ import java.util.Arrays; import java.util.Optional; import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.springframework.ide.vscode.boot.java.handlers.DefaultRunningAppProvider; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog; import org.springframework.ide.vscode.boot.jdt.ls.JavaProjectsService; @@ -48,7 +50,7 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; /** * Parameters for creating Boot Properties language server - * + * * @author Alex Boyko * @author Kris De Volder */ @@ -58,12 +60,12 @@ public class BootLanguageServerParams { public final JavaProjectFinder projectFinder; public final ProjectObserver projectObserver; public final SpringPropertyIndexProvider indexProvider; - + //Boot Properies public final TypeUtilProvider typeUtilProvider; - + //Boot Java - public final RunningAppProvider runningAppProvider; + public final ProjectAwareRunningAppProvider runningAppProvider; public final Duration watchDogInterval; public BootLanguageServerParams( @@ -71,7 +73,7 @@ public class BootLanguageServerParams { ProjectObserver projectObserver, SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, - RunningAppProvider runningAppProvider, + ProjectAwareRunningAppProvider runningAppProvider, Duration watchDogInterval ) { super(); @@ -100,7 +102,7 @@ public class BootLanguageServerParams { jdtProjectCache, indexProvider, (IDocument doc) -> new TypeUtil(jdtProjectCache.find(new TextDocumentIdentifier(doc.getUri()))), - RunningAppProvider.DEFAULT, + new DefaultRunningAppProvider(RunningAppProvider.DEFAULT), SpringLiveHoverWatchdog.DEFAULT_INTERVAL ); }; @@ -108,9 +110,9 @@ public class BootLanguageServerParams { private static JavaProjectsService createFallbackProjectCache(SimpleLanguageServer server) { CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); - - JavadocService javadocService = (uri, cpe) -> JavaDocProviders.createFor(cpe); - + + JavadocService javadocService = (uri, cpe) -> JavaDocProviders.createFor(cpe); + MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER), javadocService); javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); @@ -118,19 +120,19 @@ public class BootLanguageServerParams { javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); - + return new JavaProjectsService() { - + @Override public void removeListener(Listener listener) { projectObserver.removeListener(listener); } - + @Override public void addListener(Listener listener) { projectObserver.addListener(listener); } - + @Override public Optional find(TextDocumentIdentifier doc) { return javaProjectFinder.find(doc); @@ -162,7 +164,7 @@ public class BootLanguageServerParams { projectObserver, indexProvider, typeUtilProvider, - RunningAppProvider.NULL, + ProjectAwareRunningAppProvider.NULL, SpringLiveHoverWatchdog.DEFAULT_INTERVAL ); }; @@ -190,7 +192,7 @@ public class BootLanguageServerParams { projectObserver, indexProvider, (IDocument doc) -> new TypeUtil(javaProjectFinder.find(new TextDocumentIdentifier(doc.getUri()))), - RunningAppProvider.NULL, + ProjectAwareRunningAppProvider.NULL, SpringLiveHoverWatchdog.DEFAULT_INTERVAL ); }; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaConfig.java index 7d2dc7b73..81a0353e5 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaConfig.java @@ -33,6 +33,11 @@ public class BootJavaConfig { return enabled != null && enabled.booleanValue(); } + public boolean isStrictProjectMatchingEnabled() { + Boolean enabled = settings.getBoolean("boot-java", "strict-project-matching", "on"); + return enabled != null && enabled.booleanValue(); + } + public void handleConfigurationChange(Settings newConfig) { Log.info("Settings received: "+newConfig); this.settings = newConfig; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index fb1ba06a9..e1e172aa8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -36,8 +36,10 @@ import org.springframework.ide.vscode.boot.java.handlers.BootJavaReferencesHandl import org.springframework.ide.vscode.boot.java.handlers.BootJavaWorkspaceSymbolHandler; import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider; import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider; +import org.springframework.ide.vscode.boot.java.handlers.DefaultRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.HighlightProvider; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; @@ -196,6 +198,10 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent else { liveChangeDetectionWatchdog.disableHighlights(); } + + if (serverParams.runningAppProvider instanceof DefaultRunningAppProvider) { + ((DefaultRunningAppProvider) serverParams.runningAppProvider).setStrictProjectMatching(config.isStrictProjectMatchingEnabled()); + } }); server.onInitialize(this::initialize); @@ -287,7 +293,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent } protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder, - RunningAppProvider runningAppProvider) { + ProjectAwareRunningAppProvider runningAppProvider) { AnnotationHierarchyAwareLookup providers = new AnnotationHierarchyAwareLookup<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index 9318ace1c..183099260 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -58,9 +58,9 @@ public class BootJavaHoverProvider implements HoverHandler { private JavaProjectFinder projectFinder; private BootJavaLanguageServerComponents server; private AnnotationHierarchyAwareLookup hoverProviders; - private RunningAppProvider runningAppProvider; + private ProjectAwareRunningAppProvider runningAppProvider; - public BootJavaHoverProvider(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder, AnnotationHierarchyAwareLookup specificProviders, RunningAppProvider runningAppProvider) { + public BootJavaHoverProvider(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder, AnnotationHierarchyAwareLookup specificProviders, ProjectAwareRunningAppProvider runningAppProvider) { this.server = server; this.projectFinder = projectFinder; this.hoverProviders = specificProviders; @@ -294,7 +294,7 @@ public class BootJavaHoverProvider implements HoverHandler { private SpringBootApp[] getRunningSpringApps(IJavaProject project) { try { - return runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]); + return runningAppProvider.getAllRunningSpringApps(project).toArray(new SpringBootApp[0]); } catch (Exception e) { Log.log(e); return new SpringBootApp[0]; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/DefaultRunningAppProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/DefaultRunningAppProvider.java new file mode 100644 index 000000000..a2257edb6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/DefaultRunningAppProvider.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.handlers; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; +import org.springframework.ide.vscode.commons.util.CollectorUtil; + +/** + * @author Martin Lippert + */ +public class DefaultRunningAppProvider implements ProjectAwareRunningAppProvider { + + private RunningAppProvider allApps; + + public DefaultRunningAppProvider(RunningAppProvider allApps) { + this.allApps = allApps; + } + + private boolean strictProjectMatchingEnabled = false; + + public void setStrictProjectMatching(boolean strictProjectMatchingEnabled) { + System.out.println("USE STRICT MODE: " + strictProjectMatchingEnabled); + this.strictProjectMatchingEnabled = strictProjectMatchingEnabled; + } + + @Override + public Collection getAllRunningSpringApps(IJavaProject project) throws Exception { + Collection apps = allApps.getAllRunningSpringApps(); + + if (project != null && strictProjectMatchingEnabled) { + return apps.stream().filter((app) -> { + return doesProjectMatch(app, project); + }).collect(CollectorUtil.toImmutableList()); + } + else { + return apps; + } + } + + private boolean doesProjectMatch(SpringBootApp app, IJavaProject project) { + if (doesProjectNameMatch(app, project)) return true; + if (doesProjectThinJarWrapperMatch(app, project)) return true; + if (doesClasspathMatch(app, project)) return true; + + return false; + } + + public static boolean doesClasspathMatch(SpringBootApp app, IJavaProject project) { + try { + Set runningAppClasspath = new HashSet<>(); + Collections.addAll(runningAppClasspath, app.getClasspath()); + + return doesClasspathMatch(runningAppClasspath, project); + } + catch (Exception e) { + return false; + } + } + + public static boolean doesClasspathMatch(Set runningAppClasspath, IJavaProject project) throws Exception { + IClasspath classpath = project.getClasspath(); + Collection entries = classpath.getClasspathEntries(); + for (CPE cpe : entries) { + if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) { + String path = cpe.getOutputFolder(); + if (runningAppClasspath.contains(path)) { + return true; + } + } + } + + return false; + } + + public static boolean doesProjectThinJarWrapperMatch(SpringBootApp app, IJavaProject project) { + return false; + } + + public static boolean doesProjectNameMatch(SpringBootApp app, IJavaProject project) { + try { + String projectName = app.getSystemProperties("spring.boot.project.name"); + return projectName != null && projectName.equals(project.getElementName()); + } + catch (Exception e) { + return false; + } + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ProjectAwareRunningAppProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ProjectAwareRunningAppProvider.java new file mode 100644 index 000000000..a2b051be7 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ProjectAwareRunningAppProvider.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.handlers; + +import java.util.Collection; + +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.java.IJavaProject; + +import com.google.common.collect.ImmutableList; + +public interface ProjectAwareRunningAppProvider { + + static final ProjectAwareRunningAppProvider NULL = (project) -> ImmutableList.of(); + + Collection getAllRunningSpringApps(IJavaProject javaProject) throws Exception; +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppProvider.java index 116212fb9..01a777c24 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/RunningAppProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 2018 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,8 +13,8 @@ package org.springframework.ide.vscode.boot.java.handlers; import java.util.Collection; import org.springframework.ide.vscode.commons.boot.app.cli.LocalSpringBootApp; -import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.RemoteSpringBootApp; +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import com.google.common.collect.ImmutableList; @@ -50,5 +50,8 @@ public interface RunningAppProvider { public static final RunningAppProvider NULL = () -> ImmutableList.of(); + /** + * returns all running spring boot applications on the local machine. + */ Collection getAllRunningSpringApps() throws Exception; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java index 5e4031f05..de38ff057 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 2018 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -21,6 +21,7 @@ import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; @@ -31,9 +32,9 @@ import org.springframework.ide.vscode.commons.util.Log; */ public class LiveAppURLSymbolProvider { - private final RunningAppProvider runningAppProvider; + private final ProjectAwareRunningAppProvider runningAppProvider; - public LiveAppURLSymbolProvider(RunningAppProvider runningAppProvider) { + public LiveAppURLSymbolProvider(ProjectAwareRunningAppProvider runningAppProvider) { this.runningAppProvider = runningAppProvider; } @@ -43,7 +44,7 @@ public class LiveAppURLSymbolProvider { List result = new ArrayList<>(); try { - SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]); + SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps(null).toArray(new SpringBootApp[0]); for (SpringBootApp app : runningApps) { try { String host = app.getHost(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java index 3340a4edf..f834f3eff 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveChangeDetectionWatchdog.java @@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.java.utils; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -33,18 +32,17 @@ import org.eclipse.lsp4j.Range; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.boot.java.handlers.DefaultRunningAppProvider; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.links.VSCodeSourceLinks; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; -import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; -import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; -import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; /** @@ -59,20 +57,21 @@ public class SpringLiveChangeDetectionWatchdog { private final long POLLING_INTERVAL_MILLISECONDS; private final SimpleLanguageServer server; - private final RunningAppProvider runningAppProvider; + private final ProjectAwareRunningAppProvider runningAppProvider; private final SourceLinks sourceLinks; private final ChangeDetectionHistory changeHistory; private final Set observedProjects; private boolean changeDetectionEnabled = false; + private Timer timer; public SpringLiveChangeDetectionWatchdog( BootJavaLanguageServerComponents bootJavaLanguageServerComponents, SimpleLanguageServer server, ProjectObserver projectObserver, - RunningAppProvider runningAppProvider, + ProjectAwareRunningAppProvider runningAppProvider, JavaProjectFinder projectFinder, Duration pollingInterval ) { @@ -134,7 +133,7 @@ public class SpringLiveChangeDetectionWatchdog { public void update() { if (changeDetectionEnabled) { try { - SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]); + SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps(null).toArray(new SpringBootApp[0]); Change[] changes = changeHistory.checkForChanges(runningBootApps); if (changes != null && changes.length > 0) { for (Change change : changes) { @@ -215,16 +214,9 @@ public class SpringLiveChangeDetectionWatchdog { Collections.addAll(runningClasspath, app.getClasspath()); for (IJavaProject project : this.observedProjects) { - IClasspath classpath = project.getClasspath(); - Collection entries = classpath.getClasspathEntries(); - for (CPE cpe : entries) { - if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) { - String path = cpe.getOutputFolder(); - if (runningClasspath.contains(path)) { - result.add(project); - break; - } - } + if (DefaultRunningAppProvider.doesClasspathMatch(runningClasspath, project)) { + result.add(project); + break; } } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java index b85c0d78c..95db6ad15 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringLiveHoverWatchdog.java @@ -23,6 +23,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -38,14 +39,14 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; public class SpringLiveHoverWatchdog { public static final Duration DEFAULT_INTERVAL = Duration.ofMillis(5000); - + Logger logger = LoggerFactory.getLogger(SpringLiveHoverWatchdog.class); private final long POLLING_INTERVAL_MILLISECONDS; private final Set watchedDocs; private final SimpleLanguageServer server; private final BootJavaHoverProvider hoverProvider; - private RunningAppProvider runningAppProvider; + private ProjectAwareRunningAppProvider runningAppProvider; private boolean highlightsEnabled = true; @@ -53,6 +54,7 @@ public class SpringLiveHoverWatchdog { private JavaProjectFinder projectFinder; + private void refreshEnablement() { boolean shouldEnable = highlightsEnabled && hasInterestingProject(watchedDocs.stream()); if (shouldEnable) { @@ -69,7 +71,7 @@ public class SpringLiveHoverWatchdog { public SpringLiveHoverWatchdog( SimpleLanguageServer server, BootJavaHoverProvider hoverProvider, - RunningAppProvider runningAppProvider, + ProjectAwareRunningAppProvider runningAppProvider, JavaProjectFinder projectFinder, ProjectObserver projectChanges, Duration pollingInterval @@ -144,7 +146,8 @@ public class SpringLiveHoverWatchdog { try { if (runningBootApps == null) { - runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]); + IJavaProject project = identifyProject(docURI); + runningBootApps = runningAppProvider.getAllRunningSpringApps(project).toArray(new SpringBootApp[0]); } boolean hasCurrentRunningBootApps = runningBootApps != null && runningBootApps.length > 0; @@ -167,8 +170,10 @@ public class SpringLiveHoverWatchdog { protected void update() { if (this.watchedDocs.size() > 0) { try { - SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]); for (String docURI : watchedDocs) { + IJavaProject project = identifyProject(docURI); + SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps(project).toArray(new SpringBootApp[0]); + update(docURI, runningBootApps); } } catch (Exception e) { @@ -177,6 +182,16 @@ public class SpringLiveHoverWatchdog { } } + private IJavaProject identifyProject(String docURI) { + TextDocument doc = this.server.getTextDocumentService().get(docURI); + if (doc != null) { + return projectFinder.find(doc.getId()).orElse(null); + } + else { + return null; + } + } + private void publishLiveHints(String docURI, Range[] ranges) { server.getClient().highlight(new HighlightParams(new TextDocumentIdentifier(docURI), Arrays.asList(ranges))); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java index 1b34032fb..bb174a617 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java @@ -28,6 +28,7 @@ import org.springframework.ide.vscode.boot.BootLanguageServer; import org.springframework.ide.vscode.boot.BootLanguageServerParams; import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest; import org.springframework.ide.vscode.boot.editor.harness.StyledStringMatcher; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog; import org.springframework.ide.vscode.boot.metadata.CachingValueProvider; @@ -1630,7 +1631,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { ProjectObserver.NULL, md.getIndexProvider(), typeUtilProvider, - RunningAppProvider.NULL, + ProjectAwareRunningAppProvider.NULL, SpringLiveHoverWatchdog.DEFAULT_INTERVAL ) ); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index c94a59706..a77d7baf0 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -28,6 +28,7 @@ import org.springframework.ide.vscode.boot.BootLanguageServer; import org.springframework.ide.vscode.boot.BootLanguageServerParams; import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest; import org.springframework.ide.vscode.boot.editor.harness.StyledStringMatcher; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog; import org.springframework.ide.vscode.boot.metadata.CachingValueProvider; @@ -3870,7 +3871,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ProjectObserver.NULL, md.getIndexProvider(), typeUtilProvider, - RunningAppProvider.NULL, + ProjectAwareRunningAppProvider.NULL, SpringLiveHoverWatchdog.DEFAULT_INTERVAL ) ); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java index 3b257775a..956f3566f 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java @@ -18,6 +18,7 @@ import org.junit.Assert; import org.springframework.ide.vscode.boot.BootLanguageServer; import org.springframework.ide.vscode.boot.BootLanguageServerParams; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; +import org.springframework.ide.vscode.boot.java.handlers.ProjectAwareRunningAppProvider; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; @@ -35,7 +36,7 @@ public class BootJavaLanguageServerHarness extends LanguageServerHarness getServerWrapper().getComponents().getProjectFinder().find(doc); - + /** * Creates a builder and initializes it so that it sets up a test harness with * the 'real stuff'. I.e project finder and other injected components are like @@ -54,7 +55,7 @@ public class BootJavaLanguageServerHarness extends LanguageServerHarness provider.getAllRunningSpringApps(); return this; } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java index 7d5ec9281..7bd08fdd2 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 2018 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.project.harness; +import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 07ec73c70..197a1c525 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -73,6 +73,11 @@ "default": false, "description": "Enable/Disable detecting changes of running Spring Boot applications" }, + "boot-java.strict-project-matching.on": { + "type": "boolean", + "default": false, + "description": "Use restrictive mechanism to match live running boot apps automatically with projects in your workspace" + }, "spring-boot.ls.java.home": { "type": [ "string",