Resurrected project changed event to optimize project reconciling

This commit is contained in:
aboyko
2023-01-13 18:20:41 -05:00
parent 32e4df9d99
commit 62a3084b80
10 changed files with 69 additions and 56 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2022 Pivotal, Inc.
* Copyright (c) 2018, 2023 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
@@ -11,9 +11,9 @@
package org.springframework.ide.vscode.boot.app;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -38,6 +38,7 @@ 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;
import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.completion.CompositeCompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
@@ -262,26 +263,19 @@ public class BootLanguageServerInitializer implements InitializingBean {
}
components.getReconcileEngine().ifPresent(reconcileEngine -> {
Map<IJavaProject, List<TextDocumentIdentifier>> projectsToDocs = new HashMap<>();
for (String f : files) {
URI uri = URI.create(f);
TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toString());
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docId.getUri());
if (doc == null) {
projectFinder.find(docId).ifPresent(project -> {
List<TextDocumentIdentifier> docIds = projectsToDocs.get(project);
if (docIds == null) {
docIds = new ArrayList<>();
projectsToDocs.put(project, docIds);
Path p = Paths.get(uri);
if (IClasspathUtil.getSourceFolders(project.getClasspath()).filter(folder -> p.startsWith(folder.toPath())).findFirst().isPresent()) {
validateProject(project, reconcileEngine);
}
docIds.add(docId);
});
}
}
for (IJavaProject p : projectsToDocs.keySet()) {
validateProject(p, reconcileEngine);
}
});
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 VMware, Inc.
* Copyright (c) 2022, 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
@@ -41,7 +41,7 @@ public class BootVersionValidationEngine {
@Override
public void changed(IJavaProject project) {
validate(project);
}
});
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2022 Pivotal, Inc.
* Copyright (c) 2016, 2023 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
@@ -188,7 +188,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
highlightsEngine = createDocumentHighlightEngine(indexer);
documents.onDocumentHighlight(highlightsEngine);
JdtReconciler jdtReconciler = new JdtReconciler(cuCache);
JdtReconciler jdtReconciler = new JdtReconciler(cuCache, config);
RewriteCompilationUnitCache orCompilationUnitCache = appContext.getBean(RewriteCompilationUnitCache.class);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 VMware, Inc.
* Copyright (c) 2022, 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
@@ -26,6 +26,7 @@ import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.java.handlers.SpelExpressionReconciler;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.value.Constants;
@@ -55,9 +56,12 @@ public class JdtReconciler implements JavaReconciler {
private final CompilationUnitCache compilationUnitCache;
private final AnnotationReconciler[] reconcilers;
private final SpelExpressionReconciler spelExpressionReconciler;
private BootJavaConfig config;
public JdtReconciler(CompilationUnitCache compilationUnitCache) {
this.compilationUnitCache = compilationUnitCache;
public JdtReconciler(CompilationUnitCache compilationUnitCache, BootJavaConfig config) {
this.compilationUnitCache = compilationUnitCache;
this.config = config;
this.spelExpressionReconciler = new SpelExpressionReconciler();
this.reconcilers = new AnnotationReconciler[] {
@@ -95,7 +99,6 @@ public class JdtReconciler implements JavaReconciler {
@Override
public void reconcile(IJavaProject project, final IDocument doc, final IProblemCollector problemCollector) {
log.info("reconciling (JDT): " + project.getElementName() + " - " + doc.getUri());
long start = System.currentTimeMillis();
URI uri = URI.create(doc.getUri());
@@ -107,7 +110,7 @@ public class JdtReconciler implements JavaReconciler {
});
long end = System.currentTimeMillis();
log.info("reconciling (JDT): " + project.getElementName() + " done in " + (end - start) + "ms");
log.info("reconciling (JDT): " + doc.getUri() + " done in " + (end - start) + "ms");
}
private void reconcileAST(IJavaProject project, IDocument doc, CompilationUnit cu, IProblemCollector problemCollector) {
@@ -159,7 +162,15 @@ public class JdtReconciler implements JavaReconciler {
@Override
public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs,
Function<TextDocument, IProblemCollector> problemCollectorFactory) {
log.info("reconciling (JDT, multiple docs): " + project.getElementName() + " - " + docs.size());
if (config.isRewriteReconcileEnabled()) {
// long start = System.currentTimeMillis();
//
//
//
// long end = System.currentTimeMillis();
// log.info("reconciling project (JDT): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms");
}
return Collections.emptyMap();
}

View File

@@ -61,8 +61,8 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
private final SimpleTextDocumentService documentService;
private final Cache<URI, CompletableFuture<CompilationUnit>> uriToCu;
private final Cache<IJavaProject, Set<URI>> projectToDocs;
private final Cache<IJavaProject, JavaParser> javaParsers;
private final Cache<URI, Set<URI>> projectToDocs;
private final Cache<URI, JavaParser> javaParsers;
public RewriteCompilationUnitCache(JavaProjectFinder projectFinder, SimpleLanguageServer server, ProjectObserver projectObserver) {
// this.projectFinder = projectFinder;
@@ -87,11 +87,10 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
Optional<IJavaProject> project = projectFinder.find(new TextDocumentIdentifier(uri.toString()));
if (project.isPresent()) {
JavaParser parser = javaParsers.getIfPresent(project.get());
JavaParser parser = javaParsers.getIfPresent(project.get().getLocationUri());
if (parser != null) {
parser.reset();
}
// javaParsers.invalidate(project.get());
}
}
}
@@ -155,7 +154,7 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
private JavaParser loadJavaParser(IJavaProject project) {
try {
return javaParsers.get(project, () -> ORAstUtils.createJavaParser(project));
return javaParsers.get(project.getLocationUri(), () -> ORAstUtils.createJavaParser(project));
} catch (ExecutionException e) {
logger.error("{}", e);
return null;
@@ -170,12 +169,12 @@ 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);
Set<URI> docUris = projectToDocs.getIfPresent(project.getLocationUri());
if (docUris != null) {
uriToCu.invalidateAll(docUris);
projectToDocs.invalidate(project);
projectToDocs.invalidate(project.getLocationUri());
}
javaParsers.invalidate(project);
javaParsers.invalidate(project.getLocationUri());
}
@Override
@@ -240,7 +239,7 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis
CompilationUnit cu = cus.get(0);
if (cu != null) {
projectToDocs.get(project, () -> new HashSet<>()).add(uri);
projectToDocs.get(project.getLocationUri(), () -> new HashSet<>()).add(uri);
return cu;
} else {
throw new IllegalStateException("Failed to parse Java source");

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 VMware, Inc.
* Copyright (c) 2022, 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
@@ -79,7 +79,6 @@ public class RewriteReconciler implements JavaReconciler {
return;
}
log.info("reconciling (OpenRewrite): " + project.getElementName() + " - " + doc.getUri());
long start = System.currentTimeMillis();
try {
@@ -104,7 +103,7 @@ public class RewriteReconciler implements JavaReconciler {
}
long end = System.currentTimeMillis();
log.info("reconciling (OpenRewrite): " + project.getElementName() + " done in " + (end - start) + "ms");
log.info("reconciling (OpenRewrite): " + doc.getUri() + " done in " + (end - start) + "ms");
}
private List<ReconcileProblem> createProblems(IDocument doc, FixAssistMarker m, J astNode) {
@@ -143,7 +142,10 @@ public class RewriteReconciler implements JavaReconciler {
public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs,
Function<TextDocument, IProblemCollector> problemCollectorFactory) {
log.info("reconciling (OpenRewrite, multiple docs): " + project.getElementName() + " - " + docs.size());
if (!config.isRewriteReconcileEnabled()) {
return Collections.emptyMap();
}
long start = System.currentTimeMillis();
Map<IDocument, Collection<ReconcileProblem>> allProblems = new HashMap<>();
@@ -165,7 +167,7 @@ public class RewriteReconciler implements JavaReconciler {
allProblems.putAll(doReconcile(project, testSources, problemCollectorFactory, javaParser));
long end = System.currentTimeMillis();
log.info("reconciling (OpenRewrite, multiple docs): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms");
log.info("reconciling project (OpenRewrite): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms");
return allProblems;
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2022 Pivotal, Inc.
* Copyright (c) 2017, 2023 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
@@ -68,8 +68,8 @@ public final class CompilationUnitCache implements DocumentContentProvider {
private final SimpleTextDocumentService documentService;
private final Cache<URI, CompletableFuture<CompilationUnit>> uriToCu;
private final Cache<IJavaProject, Set<URI>> projectToDocs;
private final Cache<IJavaProject, Tuple2<List<Classpath>, INameEnvironmentWithProgress>> lookupEnvCache;
private final Cache<URI, Set<URI>> projectToDocs;
private final Cache<URI, Tuple2<List<Classpath>, INameEnvironmentWithProgress>> lookupEnvCache;
public CompilationUnitCache(JavaProjectFinder projectFinder, SimpleLanguageServer server, ProjectObserver projectObserver) {
this.projectFinder = projectFinder;
@@ -224,7 +224,7 @@ public final class CompilationUnitCache implements DocumentContentProvider {
if (cu != null) {
try {
projectToDocs.get(project, () -> new HashSet<>()).add(uri);
projectToDocs.get(project.getLocationUri(), () -> new HashSet<>()).add(uri);
logger.debug("CU Cache: start work on AST for {}", uri.toString());
return requestor.apply(cu);
@@ -287,7 +287,7 @@ public final class CompilationUnitCache implements DocumentContentProvider {
private Tuple2<List<Classpath>, INameEnvironmentWithProgress> loadLookupEnvTuple(IJavaProject project) {
try {
return lookupEnvCache.get(project, () -> {
return lookupEnvCache.get(project.getLocationUri(), () -> {
List<Classpath> classpaths = createClasspath(getClasspathEntries(project));
INameEnvironmentWithProgress environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()]));
return Tuples.of(classpaths, environment);
@@ -320,12 +320,12 @@ public final class CompilationUnitCache implements DocumentContentProvider {
private void invalidateProject(IJavaProject project) {
logger.debug("CU Cache: invalidate project <{}>", project.getElementName());
Set<URI> docUris = projectToDocs.getIfPresent(project);
Set<URI> docUris = projectToDocs.getIfPresent(project.getLocationUri());
if (docUris != null) {
uriToCu.invalidateAll(docUris);
projectToDocs.invalidate(project);
projectToDocs.invalidate(project.getLocationUri());
}
lookupEnvCache.invalidate(project);
lookupEnvCache.invalidate(project.getLocationUri());
}
@Override

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Pivotal, Inc.
* Copyright (c) 2018, 2023 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.boot.java.utils;
import java.net.URI;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
@@ -41,7 +42,7 @@ public class SpringLiveChangeDetectionWatchdog {
private final SourceLinks sourceLinks;
private final ChangeDetectionHistory changeHistory;
private final Set<IJavaProject> observedProjects;
private final Set<URI> observedProjects;
private boolean changeDetectionEnabled = false;
private ScheduledThreadPoolExecutor timer;
@@ -54,7 +55,7 @@ public class SpringLiveChangeDetectionWatchdog {
Duration pollingInterval,
SourceLinks sourceLinks
) {
this.observedProjects = new HashSet<>();
this.observedProjects = new HashSet<URI>();
this.server = server;
@@ -68,12 +69,12 @@ public class SpringLiveChangeDetectionWatchdog {
@Override
public void deleted(IJavaProject project) {
observedProjects.remove(project);
observedProjects.remove(project.getLocationUri());
}
@Override
public void created(IJavaProject project) {
observedProjects.add(project);
observedProjects.add(project.getLocationUri());
}
@Override

View File

@@ -350,15 +350,19 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService, Serv
log.debug("deleted = false");
URI projectUri = new URI(uri);
ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries());
IProjectBuild projectBuild = from(event.projectBuild);
IJavaProject oldProject = table.get(uri);
if (oldProject != null && classpath.equals(oldProject.getClasspath())) {
// nothing has changed
return;
}
IProjectBuild projectBuild = from(event.projectBuild);
IJavaProject newProject = IS_JANDEX_INDEX
? new JavaProject(getFileObserver(), projectUri, classpath,
JdtLsProjectCache.this, projectBuild)
: new JdtLsJavaProject(server.getClient(), projectUri, classpath, JdtLsProjectCache.this, projectBuild);
IJavaProject oldProject = table.put(uri, newProject);
table.put(uri, newProject);
if (oldProject != null) {
notifyDelete(oldProject);
notifyCreated(newProject);
notifyChanged(newProject);
} else {
notifyCreated(newProject);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 Pivotal, Inc.
* Copyright (c) 2020, 2023 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
@@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.app.BootLanguageServerParams;
import org.springframework.ide.vscode.boot.bootiful.AdHocPropertyHarnessTestConf;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
@@ -75,6 +76,7 @@ public class ValueSpelExpressionValidationTest {
@Autowired private JavaProjectFinder projectFinder;
@Autowired private CompilationUnitCache compilationUnitCache;
@Autowired private SimpleLanguageServer server;
@Autowired private BootJavaConfig config;
private File directory;
private String docUri;
@@ -146,7 +148,7 @@ public class ValueSpelExpressionValidationTest {
problemCollector = new TestProblemCollector();
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
new JdtReconciler(compilationUnitCache)
new JdtReconciler(compilationUnitCache, config)
}, server.getTextDocumentService(), null);
}