From 151ad12d251704db603731724455b224ae49beb6 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 13 Jul 2023 10:12:21 +0200 Subject: [PATCH] GH-1068: foundation to generate and cache diagnostics in symbol machinery --- .../boot/java/utils/SpringIndexerJava.java | 50 +++++++++++++++---- .../java/utils/SpringIndexerJavaContext.java | 8 +++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index 1aa7f9ee2..6c8176ba7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -55,6 +55,7 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyA import org.springframework.ide.vscode.boot.java.beans.CachedBean; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; +import org.springframework.ide.vscode.boot.java.reconcilers.CachedDiagnostics; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -83,6 +84,7 @@ public class SpringIndexerJava implements SpringIndexer { private static final String SYMBOL_KEY = "symbols"; private static final String BEANS_KEY = "beans"; + private static final String DIAGNOSTICS_KEY = "diagnostics"; private final SymbolHandler symbolHandler; private final AnnotationHierarchyAwareLookup symbolProviders; @@ -132,19 +134,24 @@ public class SpringIndexerJava implements SpringIndexer { public void removeProject(IJavaProject project) throws Exception { IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); this.cache.remove(symbolsCacheKey); this.cache.remove(beansCacheKey); + this.cache.remove(diagnosticsCacheKey); } @Override public void updateFile(IJavaProject project, DocumentDescriptor updatedDoc, String content) throws Exception { IndexCacheKey symbolCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); if (updatedDoc != null && shouldProcessDocument(project, updatedDoc.getDocURI())) { if (isCacheOutdated(symbolCacheKey, updatedDoc.getDocURI(), updatedDoc.getLastModified()) - || isCacheOutdated(beansCacheKey, updatedDoc.getDocURI(), updatedDoc.getLastModified())) { + || isCacheOutdated(beansCacheKey, updatedDoc.getDocURI(), updatedDoc.getLastModified()) + || isCacheOutdated(diagnosticsCacheKey, updatedDoc.getDocURI(), updatedDoc.getLastModified())) { + this.symbolHandler.removeSymbols(project, updatedDoc.getDocURI()); scanFile(project, updatedDoc, content); } @@ -168,21 +175,27 @@ public class SpringIndexerJava implements SpringIndexer { public void removeFiles(IJavaProject project, String[] docURIs) throws Exception { IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); for (String docURI : docURIs) { String file = new File(new URI(docURI)).getAbsolutePath(); + this.cache.removeFile(symbolsCacheKey, file, CachedSymbol.class); - this.cache.removeFile(beansCacheKey, file, CachedSymbol.class); + this.cache.removeFile(beansCacheKey, file, CachedBean.class); + this.cache.removeFile(diagnosticsCacheKey, file, CachedDiagnostics.class); } } private DocumentDescriptor[] filterDocuments(IJavaProject project, DocumentDescriptor[] updatedDocs) { IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); return Arrays.stream(updatedDocs).filter(doc -> shouldProcessDocument(project, doc.getDocURI())) .filter(doc -> isCacheOutdated(symbolsCacheKey, doc.getDocURI(), doc.getLastModified()) - || isCacheOutdated(beansCacheKey, doc.getDocURI(), doc.getLastModified())).toArray(DocumentDescriptor[]::new); + || isCacheOutdated(beansCacheKey, doc.getDocURI(), doc.getLastModified()) + || isCacheOutdated(diagnosticsCacheKey, doc.getDocURI(), doc.getLastModified())) + .toArray(DocumentDescriptor[]::new); } private boolean shouldProcessDocument(IJavaProject project, String docURI) { @@ -235,19 +248,22 @@ public class SpringIndexerJava implements SpringIndexer { if (cu != null) { List generatedSymbols = new ArrayList(); List generatedBeans = new ArrayList(); + List generatedDiagnostics = new ArrayList(); AtomicReference docRef = new AtomicReference<>(); String file = UriUtil.toFileString(docURI); SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file, - lastModified, docRef, content, generatedSymbols, generatedBeans, SCAN_PASS.ONE, new ArrayList<>()); + lastModified, docRef, content, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE, new ArrayList<>()); scanAST(context); IndexCacheKey symbolCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); this.cache.update(symbolCacheKey, file, lastModified, generatedSymbols, context.getDependencies(), CachedSymbol.class); this.cache.update(beansCacheKey, file, lastModified, generatedBeans, context.getDependencies(), CachedBean.class); + this.cache.update(diagnosticsCacheKey, file, lastModified, generatedDiagnostics, context.getDependencies(), CachedDiagnostics.class); // dependencyTracker.dump(); EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); @@ -276,11 +292,12 @@ public class SpringIndexerJava implements SpringIndexer { if (cu != null) { List generatedSymbols = new ArrayList(); List generatedBeans = new ArrayList(); + List generatedDiagnostics = new ArrayList(); AtomicReference docRef = new AtomicReference<>(); String file = UriUtil.toFileString(docURI); SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file, - 0, docRef, content, generatedSymbols, generatedBeans, SCAN_PASS.ONE, new ArrayList<>()); + 0, docRef, content, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE, new ArrayList<>()); scanAST(context); @@ -314,6 +331,7 @@ public class SpringIndexerJava implements SpringIndexer { List generatedSymbols = new ArrayList(); List generatedBeans = new ArrayList(); + List generatedDiagnostics = new ArrayList(); Multimap dependencies = MultimapBuilder.hashKeys().hashSetValues().build(); @@ -329,7 +347,7 @@ public class SpringIndexerJava implements SpringIndexer { AtomicReference docRef = new AtomicReference<>(); SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath, - lastModified, docRef, null, generatedSymbols, generatedBeans, SCAN_PASS.ONE, new ArrayList<>()); + lastModified, docRef, null, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE, new ArrayList<>()); scanAST(context); @@ -348,9 +366,11 @@ public class SpringIndexerJava implements SpringIndexer { IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); this.cache.update(symbolsCacheKey, javaFiles, lastModified, generatedSymbols, dependencies, CachedSymbol.class); this.cache.update(beansCacheKey, javaFiles, lastModified, generatedBeans, dependencies, CachedBean.class); + this.cache.update(diagnosticsCacheKey, javaFiles, lastModified, generatedDiagnostics, dependencies, CachedDiagnostics.class); return scannedTypes; } @@ -389,39 +409,46 @@ public class SpringIndexerJava implements SpringIndexer { private void scanFiles(IJavaProject project, String[] javaFiles) throws Exception { IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + IndexCacheKey diagnosticsCacheKey = getCacheKey(project, DIAGNOSTICS_KEY); Pair> cachedSymbols = this.cache.retrieve(symbolsCacheKey, javaFiles, CachedSymbol.class); Pair> cachedBeans = this.cache.retrieve(beansCacheKey, javaFiles, CachedBean.class); + Pair> cachedDiagnostics = this.cache.retrieve(diagnosticsCacheKey, javaFiles, CachedDiagnostics.class); CachedSymbol[] symbols; CachedBean[] beans; + CachedDiagnostics[] diagnostics; if (cachedSymbols == null || cachedBeans == null) { List generatedSymbols = new ArrayList(); List generatedBeans = new ArrayList(); + List generatedDiagnostics = new ArrayList(); log.info("scan java files, AST parse, pass 1 for files: {}", javaFiles.length); - String[] pass2Files = scanFiles(project, javaFiles, generatedSymbols, generatedBeans, SCAN_PASS.ONE); + String[] pass2Files = scanFiles(project, javaFiles, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.ONE); if (pass2Files.length > 0) { log.info("scan java files, AST parse, pass 2 for files: {}", javaFiles.length); - scanFiles(project, pass2Files, generatedSymbols, generatedBeans, SCAN_PASS.TWO); + scanFiles(project, pass2Files, generatedSymbols, generatedBeans, generatedDiagnostics, SCAN_PASS.TWO); } log.info("scan java files done, number of symbols created: " + generatedSymbols.size()); this.cache.store(symbolsCacheKey, javaFiles, generatedSymbols, dependencyTracker.getAllDependencies(), CachedSymbol.class); this.cache.store(beansCacheKey, javaFiles, generatedBeans, dependencyTracker.getAllDependencies(), CachedBean.class); + this.cache.store(diagnosticsCacheKey, javaFiles, generatedDiagnostics, dependencyTracker.getAllDependencies(), CachedDiagnostics.class); // dependencyTracker.dump(); symbols = (CachedSymbol[]) generatedSymbols.toArray(new CachedSymbol[generatedSymbols.size()]); beans = (CachedBean[]) generatedBeans.toArray(new CachedBean[generatedBeans.size()]); + diagnostics = (CachedDiagnostics[]) generatedDiagnostics.toArray(new CachedDiagnostics[generatedDiagnostics.size()]); } else { symbols = cachedSymbols.getLeft(); beans = cachedBeans.getLeft(); + diagnostics = cachedDiagnostics.getLeft(); log.info("scan java files used cached data: {} - no. of cached symbols retrieved: {}", project.getElementName(), symbols.length); this.dependencyTracker.restore(cachedSymbols.getRight()); @@ -435,8 +462,9 @@ public class SpringIndexerJava implements SpringIndexer { } } - private String[] scanFiles(IJavaProject project, String[] javaFiles, List generatedSymbols, List generatedBeans, SCAN_PASS pass) - throws Exception { + private String[] scanFiles(IJavaProject project, String[] javaFiles, List generatedSymbols, List generatedBeans, + List generatedDiagnostics, SCAN_PASS pass) throws Exception { + ASTParser parser = createParser(project, SCAN_PASS.ONE.equals(pass)); List nextPassFiles = new ArrayList<>(); @@ -449,7 +477,7 @@ public class SpringIndexerJava implements SpringIndexer { AtomicReference docRef = new AtomicReference<>(); SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath, - lastModified, docRef, null, generatedSymbols, generatedBeans, pass, nextPassFiles); + lastModified, docRef, null, generatedSymbols, generatedBeans, generatedDiagnostics, pass, nextPassFiles); scanAST(context); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java index 4ee16f867..5058a42dc 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java @@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ITypeBinding; import org.springframework.ide.vscode.boot.java.beans.CachedBean; +import org.springframework.ide.vscode.boot.java.reconcilers.CachedDiagnostics; import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -36,6 +37,7 @@ public class SpringIndexerJavaContext { private final String content; private final List generatedSymbols; private final List beans; + private final List diagnostics; private final SCAN_PASS pass; private final List nextPassFiles; @@ -52,6 +54,7 @@ public class SpringIndexerJavaContext { String content, List generatedSymbols, List beans, + List diagnostics, SCAN_PASS pass, List nextPassFiles ) { @@ -65,6 +68,7 @@ public class SpringIndexerJavaContext { this.content = content; this.generatedSymbols = generatedSymbols; this.beans = beans; + this.diagnostics = diagnostics; this.pass = pass; this.nextPassFiles = nextPassFiles; } @@ -104,6 +108,10 @@ public class SpringIndexerJavaContext { public List getBeans() { return beans; } + + public List getDiagnostics() { + return diagnostics; + } public SCAN_PASS getPass() { return pass;