Target folder .class files updates tracking rather than classpath update
This commit is contained in:
@@ -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")
|
||||
)));
|
||||
|
||||
|
||||
@@ -113,9 +113,9 @@
|
||||
<commons-codec-version>1.13</commons-codec-version>
|
||||
|
||||
<!-- Rewrite specific properties -->
|
||||
<rewrite-version>8.1.2</rewrite-version>
|
||||
<rewrite-spring-version>5.0.1</rewrite-spring-version>
|
||||
<rewrite-gradle-tooling-api-version>1.0.0</rewrite-gradle-tooling-api-version>
|
||||
<rewrite-version>8.2.0-SNAPSHOT</rewrite-version>
|
||||
<rewrite-spring-version>5.1.0-SNAPSHOT</rewrite-spring-version>
|
||||
<rewrite-gradle-tooling-api-version>1.1.0-SNAPSHOT</rewrite-gradle-tooling-api-version>
|
||||
|
||||
<gradle-tooling.version>7.6</gradle-tooling.version>
|
||||
<gradle-core.version>6.1.1</gradle-core.version>
|
||||
|
||||
@@ -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<IPath> 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<IPath> getOutputFolders(IJavaProject jp) {
|
||||
try {
|
||||
Set<IPath> 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.
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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<String> 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);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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<CompilationUnit> future = notification.getValue();
|
||||
|
||||
if (future != null) {
|
||||
if (!future.isCancelled()) {
|
||||
if (!future.isDone() && !future.isCancelled()) {
|
||||
future.cancel(true);
|
||||
}
|
||||
Optional<IJavaProject> 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<URI, JavaParser> 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<URI> docUris = projectToDocs.getIfPresent(project.getLocationUri());
|
||||
if (docUris != null) {
|
||||
uriToCu.invalidateAll(docUris);
|
||||
|
||||
@@ -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<URI> docUris = projectToDocs.getIfPresent(project.getLocationUri());
|
||||
if (docUris != null) {
|
||||
uriToCu.invalidateAll(docUris);
|
||||
|
||||
@@ -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<String> CLASS_FILES_TO_WATCH_GLOB = List.of("**/*.class");
|
||||
|
||||
public static void listenToClassFileChanges(FileObserver fileObserver, JavaProjectFinder projectFinder, Consumer<IJavaProject> 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<IJavaProject> 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user