From 142321fc90cd15ff4a152315274fd632562cb85e Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 20 Jun 2024 12:56:15 +0200 Subject: [PATCH] reuse compilation unit cache when searching for references instead of on-demand parsing --- .../BootJavaLanguageServerComponents.java | 15 ++-- .../beans/QualifierReferencesProvider.java | 3 +- .../handlers/BootJavaReferencesHandler.java | 80 +++++++------------ .../boot/java/handlers/ReferenceProvider.java | 3 +- .../ValuePropertyReferencesProvider.java | 14 ++-- 5 files changed, 44 insertions(+), 71 deletions(-) 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 48f539ed2..7fb57f0de 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 @@ -121,25 +121,26 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent projectFinder = serverParams.projectFinder; projectObserver = serverParams.projectObserver; - this.cuCache = appContext.getBean(CompilationUnitCache.class); propertyIndexProvider = serverParams.indexProvider; SimpleWorkspaceService workspaceService = server.getWorkspaceService(); SimpleTextDocumentService documents = server.getTextDocumentService(); + this.cuCache = appContext.getBean(CompilationUnitCache.class); SpringSymbolIndex springSymbolIndex = appContext.getBean(SpringSymbolIndex.class); SpringMetamodelIndex springIndex = appContext.getBean(SpringMetamodelIndex.class); BootJavaConfig config = appContext.getBean(BootJavaConfig.class); SourceLinks sourceLinks = appContext.getBean(SourceLinks.class); - liveDataService = appContext.getBean(SpringProcessConnectorService.class); + + this.liveDataService = appContext.getBean(SpringProcessConnectorService.class); SpringProcessLiveDataProvider liveDataProvider = appContext.getBean(SpringProcessLiveDataProvider.class); - reconcileEngine = appContext.getBean(BootJavaReconcileEngine.class); - codeActionProvider = appContext.getBean(BootJavaCodeActionProvider.class); + this.reconcileEngine = appContext.getBean(BootJavaReconcileEngine.class); + this.codeActionProvider = appContext.getBean(BootJavaCodeActionProvider.class); - ReferencesHandler referencesHandler = createReferenceHandler(server, projectFinder, springIndex, springSymbolIndex); + ReferencesHandler referencesHandler = createReferenceHandler(server, projectFinder, springIndex, springSymbolIndex, cuCache); documents.onReferences(referencesHandler); // @@ -291,12 +292,12 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent } protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, - SpringMetamodelIndex index, SpringSymbolIndex symbolIndex) { + SpringMetamodelIndex index, SpringSymbolIndex symbolIndex, CompilationUnitCache cuCache) { Map providers = new HashMap<>(); providers.put(Annotations.VALUE, new ValuePropertyReferencesProvider(server)); providers.put(Annotations.QUALIFIER, new QualifierReferencesProvider(index, symbolIndex)); - return new BootJavaReferencesHandler(this, projectFinder, providers); + return new BootJavaReferencesHandler(this, cuCache, projectFinder, providers); } protected BootJavaCodeLensEngine createCodeLensEngine(SpringSymbolIndex index) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/QualifierReferencesProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/QualifierReferencesProvider.java index 3315579cd..df0a32722 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/QualifierReferencesProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/beans/QualifierReferencesProvider.java @@ -27,7 +27,6 @@ import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.protocol.spring.Bean; -import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert @@ -43,7 +42,7 @@ public class QualifierReferencesProvider implements ReferenceProvider { } @Override - public List provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { + public List provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, ITypeBinding type, int offset) { cancelToken.checkCanceled(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java index 12b2f6d35..c77b40180 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java @@ -10,19 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.handlers; -import java.io.File; +import java.net.URI; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.CancellationException; -import java.util.stream.Stream; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.lsp4j.Location; @@ -30,13 +25,11 @@ import org.eclipse.lsp4j.ReferenceParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; -import org.springframework.ide.vscode.commons.java.IClasspath; -import org.springframework.ide.vscode.commons.java.IClasspathUtil; +import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; -import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -44,12 +37,14 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class BootJavaReferencesHandler implements ReferencesHandler { - private JavaProjectFinder projectFinder; - private BootJavaLanguageServerComponents server; - private Map referenceProviders; + private final JavaProjectFinder projectFinder; + private final BootJavaLanguageServerComponents server; + private final Map referenceProviders; + private final CompilationUnitCache cuCache; - public BootJavaReferencesHandler(BootJavaLanguageServerComponents server, JavaProjectFinder projectFinder, Map specificProviders) { + public BootJavaReferencesHandler(BootJavaLanguageServerComponents server, CompilationUnitCache cuCache, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; + this.cuCache = cuCache; this.projectFinder = projectFinder; this.referenceProviders = specificProviders; } @@ -67,7 +62,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { cancelToken.checkCanceled(); - List referencesResult = provideReferences(cancelToken, doc, offset); + List referencesResult = provideReferences(cancelToken, doc.getId(), offset); if (referencesResult != null) { return referencesResult; } @@ -83,39 +78,31 @@ public class BootJavaReferencesHandler implements ReferencesHandler { return SimpleTextDocumentService.NO_REFERENCES; } - private List provideReferences(CancelChecker cancelToken, TextDocument document, int offset) throws Exception { - ASTParser parser = ASTParser.newParser(AST.JLS21); - Map options = JavaCore.getOptions(); - JavaCore.setComplianceOptions(JavaCore.VERSION_21, options); - parser.setCompilerOptions(options); - parser.setKind(ASTParser.K_COMPILATION_UNIT); - parser.setStatementsRecovery(true); - parser.setBindingsRecovery(true); - parser.setResolveBindings(true); + private List provideReferences(CancelChecker cancelToken, TextDocumentIdentifier docID, int offset) throws Exception { + Optional projectOptional = projectFinder.find(docID); - String[] classpathEntries = getClasspathEntries(document); - String[] sourceEntries = new String[] {}; - parser.setEnvironment(classpathEntries, sourceEntries, null, true); + if (projectOptional.isPresent()) { + IJavaProject project = projectOptional.get(); - String docURI = document.getUri(); - String unitName = docURI.substring(docURI.lastIndexOf("/")); - parser.setUnitName(unitName); - parser.setSource(document.get(0, document.getLength()).toCharArray()); - - cancelToken.checkCanceled(); + URI docUri = URI.create(docID.getUri()); - CompilationUnit cu = (CompilationUnit) parser.createAST(null); - ASTNode node = NodeFinder.perform(cu, offset, 0); - - if (node != null) { - cancelToken.checkCanceled(); - return provideReferencesForAnnotation(cancelToken, node, offset, document); + return cuCache.withCompilationUnit(project, docUri, cu -> { + cancelToken.checkCanceled(); + + ASTNode node = NodeFinder.perform(cu, offset, 0); + if (node != null) { + return provideReferencesForAnnotation(cancelToken, project, node, offset); + } + else { + return null; + } + }); } return null; } - private List provideReferencesForAnnotation(CancelChecker cancelToken, ASTNode node, int offset, TextDocument doc) { + private List provideReferencesForAnnotation(CancelChecker cancelToken, IJavaProject project, ASTNode node, int offset) { Annotation annotation = null; ASTNode annotationNode = node; @@ -135,10 +122,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { ReferenceProvider provider = this.referenceProviders.get(qualifiedName); if (provider != null) { - Optional projectOptional = projectFinder.find(doc.getId()); - if (projectOptional.isPresent()) { - return provider.provideReferences(cancelToken, projectOptional.get(), node, annotation, type, offset, doc); - } + return provider.provideReferences(cancelToken, project, node, annotation, type, offset); } } } @@ -147,14 +131,4 @@ public class BootJavaReferencesHandler implements ReferencesHandler { return null; } - private String[] getClasspathEntries(IDocument doc) throws Exception { - IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(doc.getUri())).get(); - IClasspath classpath = project.getClasspath(); - Stream classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream(); - return classpathEntries - .filter(file -> file.exists()) - .map(file -> file.getAbsolutePath()) - .toArray(String[]::new); - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java index 0c92f6f7b..55ac8e2be 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/ReferenceProvider.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert @@ -26,6 +25,6 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; public interface ReferenceProvider { List provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, - ITypeBinding type, int offset, TextDocument doc); + ITypeBinding type, int offset); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java index e4b12effa..67d96139d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java @@ -15,6 +15,7 @@ import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar; import java.io.File; import java.io.IOException; import java.net.URI; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -71,8 +72,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { } @Override - public List provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, - ITypeBinding type, int offset, TextDocument doc) { + public List provideReferences(CancelChecker cancelToken, IJavaProject project, ASTNode node, Annotation annotation, ITypeBinding type, int offset) { cancelToken.checkCanceled(); @@ -80,14 +80,14 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { // case: @Value("prefix<*>") if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition()); } } // case: @Value(value="prefix<*>") else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition()); } } } @@ -98,7 +98,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { return null; } - private List provideReferences(String value, int offset, int nodeStartOffset, TextDocument doc) { + private List provideReferences(String value, int offset, int nodeStartOffset) { try { LocalRange range = getPropertyRange(value, offset); @@ -194,7 +194,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { List foundLocations = new ArrayList<>(); try { - String fileContent = FileUtils.readFileToString(file); + String fileContent = FileUtils.readFileToString(file, Charset.defaultCharset()); YamlASTProvider parser = new YamlParser(); @@ -279,7 +279,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { static List findReferencesInPropertiesFile(File file, String propertyKey, BiFunction> processor) { List foundLocations = new ArrayList<>(); try { - String fileContent = FileUtils.readFileToString(file); + String fileContent = FileUtils.readFileToString(file, Charset.defaultCharset()); Parser parser = new AntlrParser(); ParseResults parseResults = parser.parse(fileContent);