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 fdf17a3c4..ce2e33a6e 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
@@ -170,6 +170,7 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
FileSystems.getDefault().getPathMatcher("glob:**/*.json"),
FileSystems.getDefault().getPathMatcher("glob:**/*.yml"),
FileSystems.getDefault().getPathMatcher("glob:**/*.properties"),
+ FileSystems.getDefault().getPathMatcher("glob:**/*.class"),
FileSystems.getDefault().getPathMatcher("glob:**/META-INF/spring/*.factories")
)));
diff --git a/headless-services/commons/pom.xml b/headless-services/commons/pom.xml
index b194f9fbe..f7bb73edf 100644
--- a/headless-services/commons/pom.xml
+++ b/headless-services/commons/pom.xml
@@ -113,9 +113,9 @@
1.13
- 8.1.2
- 5.0.1
- 1.0.0
+ 8.2.0-SNAPSHOT
+ 5.1.0-SNAPSHOT
+ 1.1.0-SNAPSHOT
7.6
6.1.1
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathListenerManager.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathListenerManager.java
index eadc31952..5919c4e32 100644
--- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathListenerManager.java
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathListenerManager.java
@@ -10,30 +10,22 @@
*******************************************************************************/
package org.springframework.tooling.jdt.ls.commons.classpath;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Queue;
-import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
-import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceDelta;
-import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ElementChangedEvent;
-import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.core.JavaModelException;
import org.springframework.tooling.jdt.ls.commons.Logger;
/**
@@ -87,8 +79,7 @@ public class ClasspathListenerManager {
// Classpath unchanged but maven/gradle repo cache has JAR's removed or downloaded
// See individual method comments for more details
|| isClasspathManifestFileChanged(jp, delta)
- || areClasspathJarsChanged(delta)
- || areOutputFoldersContentChanged(jp, delta)) {
+ || areClasspathJarsChanged(delta)) {
listener.classpathChanged(jp);
}
break;
@@ -116,53 +107,6 @@ public class ClasspathListenerManager {
return false;
}
- private boolean areOutputFoldersContentChanged(IJavaProject jp, IJavaElementDelta delta) {
- Collection outputFolders = getOutputFolders(jp);
- if (delta.getResourceDeltas() != null && (delta.getFlags() & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_CHILDREN)) != 0) {
- for (IResourceDelta resourceDelta : delta.getResourceDeltas()) {
- if (outputFolders.stream().anyMatch(of -> resourceDelta.getFullPath().isPrefixOf(of))) {
- return areClassFilesChangedOrAdded(resourceDelta);
- }
- }
- }
- return false;
- }
-
- private boolean areClassFilesChangedOrAdded(IResourceDelta resourceDelta) {
- if (resourceDelta.getResource() instanceof IContainer) {
- for (IResourceDelta rd : resourceDelta.getAffectedChildren()) {
- if(areClassFilesChangedOrAdded(rd)) {
- return true;
- }
- }
- return false;
- } else {
- if ("class".equals(resourceDelta.getResource().getFileExtension())) {
- switch (resourceDelta.getKind()) {
- case IResourceDelta.ADDED:
- return true;
- case IResourceDelta.CHANGED:
- return (resourceDelta.getFlags() & IResourceDelta.CONTENT) != 0;
- }
- }
- return false;
- }
- }
-
- private Collection getOutputFolders(IJavaProject jp) {
- try {
- Set outputFolders = new HashSet<>();;
- for (IClasspathEntry cpe : jp.getRawClasspath()) {
- if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE && cpe.getOutputLocation() != null) {
- outputFolders.add(cpe.getOutputLocation());
- }
- }
- return outputFolders;
- } catch (JavaModelException e) {
- return Collections.emptyList();
- }
- }
-
/**
* When Maven project update is completed .classpath file content changed is one
* of the resource delta's expected.
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java
index f75255dca..31dd5dbed 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java
@@ -29,6 +29,7 @@ import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
+import org.springframework.ide.vscode.boot.java.utils.ServerUtils;
import org.springframework.ide.vscode.boot.java.utils.SymbolCache;
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
@@ -80,7 +81,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
private VscodeCompletionEngineAdapter completionEngineAdapter;
private static final Logger log = LoggerFactory.getLogger(BootLanguageServerInitializer.class);
-
+
private ProjectObserver.Listener reconcileDocumentsForProjectChange(SimpleLanguageServer s, CompositeLanguageServerComponents c, JavaProjectFinder projectFinder) {
return new ProjectObserver.Listener() {
@@ -90,28 +91,28 @@ public class BootLanguageServerInitializer implements InitializingBean {
@Override
public void created(IJavaProject project) {
- validateAll(project);
+ validateAll(c, s, project);
}
@Override
public void changed(IJavaProject project) {
- validateAll(project);
+ validateAll(c, s, project);
}
- private void validateAll(IJavaProject project) {
- c.getReconcileEngine().ifPresent(reconciler -> {
- log.debug("A project changed {}, triggering reconcile on all project's open documents",
- project.getElementName());
- for (TextDocument doc : s.getTextDocumentService().getAll()) {
- if (projectFinder.find(doc.getId()).orElse(null) == project) {
- s.validateWith(doc.getId(), reconciler);
- }
- }
- });
- }
};
}
+ private void validateAll(CompositeLanguageServerComponents c, SimpleLanguageServer s, IJavaProject project) {
+ c.getReconcileEngine().ifPresent(reconciler -> {
+ log.debug("A project changed {}, triggering reconcile on all project's open documents",
+ project.getElementName());
+ for (TextDocument doc : s.getTextDocumentService().getAll()) {
+ if (projectFinder.find(doc.getId()).orElse(null) == project) {
+ s.validateWith(doc.getId(), reconciler);
+ }
+ }
+ });
+ }
@Override
public void afterPropertiesSet() throws Exception {
//TODO: CompositeLanguageServerComponents object instance serves no purpose anymore. The constructor really just contains
@@ -191,6 +192,8 @@ public class BootLanguageServerInitializer implements InitializingBean {
TextDocument doc = params.getDocument();
server.validateWith(doc.getId(), reconcileEngine);
});
+
+ ServerUtils.listenToClassFileChanges(server.getWorkspaceService().getFileObserver(), projectFinder, project -> validateAll(components, server, project));
});
config.addListener(evt -> reconcile());
params.projectObserver.addListener(reconcileDocumentsForProjectChange(server, components, params.projectFinder));
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaProjectReconcilerScheduler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaProjectReconcilerScheduler.java
index 611474ce2..503be019e 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaProjectReconcilerScheduler.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaProjectReconcilerScheduler.java
@@ -16,6 +16,7 @@ import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
+import org.springframework.ide.vscode.boot.java.utils.ServerUtils;
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;
@@ -25,8 +26,6 @@ public class BootJavaProjectReconcilerScheduler extends ProjectReconcileSchedule
private static final Logger log = LoggerFactory.getLogger(BootJavaProjectReconcilerScheduler.class);
-// private static final List FILES_TO_WATCH_GLOB = List.of("**/*.java");
-
private ProjectObserver projectObserver;
private BootJavaConfig config;
private RewriteRecipeRepository recipesRepo;
@@ -80,29 +79,11 @@ public class BootJavaProjectReconcilerScheduler extends ProjectReconcileSchedule
}
});
-// getServer().getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles);
-// getServer().getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles);
+ ServerUtils.listenToClassFileChanges(getServer().getWorkspaceService().getFileObserver(), getProjectFinder(), this::scheduleValidation);
// TODO: index update even happens on every file save. Very expensive to blindly reconcile all projects.
// Need to figure out a check if spring index has any changes
// springIndexer.onUpdate(v -> reconcile());
}
-// private void handleFiles(String[] files) {
-// for (String f : files) {
-// URI uri = URI.create(f);
-// TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
-// TextDocument doc = getServer().getTextDocumentService().getLatestSnapshot(docId.getUri());
-// if (doc == null) {
-// getProjectFinder().find(docId).ifPresent(project -> {
-// Path p = Paths.get(uri);
-// if (IClasspathUtil.getSourceFolders(project.getClasspath())
-// .filter(folder -> p.startsWith(folder.toPath())).findFirst().isPresent()) {
-// scheduleValidation(project);
-// }
-// });
-// }
-// }
-// }
-
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java
index 5b2ffcd74..8ea86f900 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java
@@ -28,8 +28,8 @@ import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.eclipse.lsp4j.TextDocumentIdentifier;
-import org.openrewrite.Tree;
import org.openrewrite.Parser.Input;
+import org.openrewrite.Tree;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.marker.JavaSourceSet;
import org.openrewrite.java.tree.J.CompilationUnit;
@@ -38,6 +38,7 @@ import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.utils.DocumentContentProvider;
+import org.springframework.ide.vscode.boot.java.utils.ServerUtils;
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;
@@ -90,7 +91,7 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
CompletableFuture future = notification.getValue();
if (future != null) {
- if (!future.isCancelled()) {
+ if (!future.isDone() && !future.isCancelled()) {
future.cancel(true);
}
Optional project = projectFinder.find(new TextDocumentIdentifier(uri.toASCIIString()));
@@ -98,8 +99,19 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
JavaParser parser = javaParsers.getIfPresent(project.get().getLocationUri());
if (parser != null) {
- parser.reset(List.of(uri));
-// parser.reset();
+// if (future.isDone()) {
+// try {
+// CompilationUnit cu = future.get();
+// if (cu != null) {
+// parser.resetCUs(List.of(cu));
+// return;
+// }
+// } catch (Throwable t) {
+// logger.error("", t);
+// }
+// }
+// parser.reset(List.of(uri));
+ parser.reset();
}
}
}
@@ -113,6 +125,7 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
@Override
public void onRemoval(RemovalNotification notification) {
+ logger.info("CU Cache: invalidate project {}", notification.getKey());
sourceSetClasspath.invalidate(notification.getKey());
}
})
@@ -164,6 +177,10 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
this.projectObserver.addListener(this.projectListener);
}
+ if (server != null) {
+ ServerUtils.listenToClassFileChanges(server.getWorkspaceService().getFileObserver(), projectFinder, this::invalidateProject);
+ }
+
}
public void dispose() {
@@ -187,8 +204,6 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
}
private void invalidateProject(IJavaProject project) {
- logger.info("CU Cache: invalidate project <{}>", project.getElementName());
-
Set docUris = projectToDocs.getIfPresent(project.getLocationUri());
if (docUris != null) {
uriToCu.invalidateAll(docUris);
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java
index 48dff22e9..413900c38 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java
@@ -147,6 +147,10 @@ public final class CompilationUnitCache implements DocumentContentProvider {
this.projectObserver.addListener(this.projectListener);
}
+ if (server != null) {
+ ServerUtils.listenToClassFileChanges(server.getWorkspaceService().getFileObserver(), projectFinder, this::invalidateProject);
+ }
+
}
public void dispose() {
@@ -329,8 +333,6 @@ public final class CompilationUnitCache implements DocumentContentProvider {
}
private synchronized void invalidateProject(IJavaProject project) {
- logger.info("Invalidate project <{}>", project.getElementName());
-
Set docUris = projectToDocs.getIfPresent(project.getLocationUri());
if (docUris != null) {
uriToCu.invalidateAll(docUris);
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ServerUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ServerUtils.java
new file mode 100644
index 000000000..f9688c529
--- /dev/null
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ServerUtils.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2023 VMware, 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:
+ * VMware, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.ide.vscode.boot.java.utils;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.FileObserver;
+
+public class ServerUtils {
+
+ private static final Logger log = LoggerFactory.getLogger(ServerUtils.class);
+
+ private static final List CLASS_FILES_TO_WATCH_GLOB = List.of("**/*.class");
+
+ public static void listenToClassFileChanges(FileObserver fileObserver, JavaProjectFinder projectFinder, Consumer callback) {
+ fileObserver.onFilesChanged(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
+ fileObserver.onFilesCreated(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
+ }
+
+ private static void handleFiles(JavaProjectFinder projectFinder, String[] files, Consumer callback) {
+ for (String f : files) {
+ URI uri = URI.create(f);
+ TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
+ projectFinder.find(docId).ifPresent(project -> {
+ Path p = Paths.get(uri);
+ if (IClasspathUtil.getOutputFolders(project.getClasspath()).anyMatch(folder -> p.startsWith(folder.toPath()))) {
+ try {
+ callback.accept(project);
+ } catch (Throwable t) {
+ log.error("", t);
+ }
+ }
+ });
+ }
+ }
+
+
+}