Skip to content

Commit 3d1ccd7

Browse files
committed
GH-1499: add additional indexing logic to allow additional reconciling of specific files once the index is complete
1 parent d3b0868 commit 3d1ccd7

File tree

8 files changed

+404
-192
lines changed

8 files changed

+404
-192
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanRegistrarDeclarationReconciler.java

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ public boolean visit(TypeDeclaration node) {
7878
if (ASTUtils.findInTypeHierarchy(type, Set.of(Annotations.BEAN_REGISTRAR_INTERFACE)) == null) {
7979
return true;
8080
}
81+
82+
if (!isIndexComplete) {
83+
throw new RequiredCompleteIndexException();
84+
}
8185

8286
List<Bean> configBeans = new ArrayList<>();
8387
Path p = Path.of(docURI);

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/RequiredCompleteAstException.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.reconcilers;
1212

13+
/**
14+
* Indicates that a reconciler or indexer requires the full AST (including method bodies)
15+
* to do its work accordingly.
16+
*/
1317
public class RequiredCompleteAstException extends RuntimeException {
14-
15-
private static final long serialVersionUID = 1L;
16-
18+
private static final long serialVersionUID = -3422411902406544588L;
1719
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.reconcilers;
12+
13+
/**
14+
* Indicates that a reconciler relies on the index to be complete before it can
15+
* reconcile a file. As a result, the corresponding file will be re-parsed and re-reconciled
16+
* once the index is complete.
17+
*
18+
* A reconciler should throw this exception only in case it really requires
19+
* access to the index and check other AST-related information first to avoid
20+
* re-reconciling too many files when it is not really necessary.
21+
*
22+
* For example the reconciler that checks BeanRegistrar implementations against
23+
* Import annotations in the index should only throw this exception if the class
24+
* is indeed an implementation of BeanRegistrar.
25+
*/
26+
public class RequiredCompleteIndexException extends RuntimeException {
27+
private static final long serialVersionUID = -6155363860106363727L;
28+
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java

+234-171
Large diffs are not rendered by default.

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaContext.java

+20-12
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ public class SpringIndexerJavaContext {
3535
private final long lastModified;
3636
private final AtomicReference<TextDocument> docRef;
3737
private final String content;
38-
private final List<CachedSymbol> generatedSymbols;
39-
private final List<CachedBean> beans;
4038
private final IProblemCollector getProblemCollector;
4139
private final List<String> nextPassFiles;
4240
private final boolean fullAst;
41+
private final boolean isIndexComplete;
42+
private final SpringIndexerJavaScanResult scanResult;
4343

4444
private final Set<String> dependencies = new HashSet<>();
4545
private final Set<String> scannedTypes = new HashSet<>();
46-
46+
4747
public SpringIndexerJavaContext(
4848
IJavaProject project,
4949
CompilationUnit cu,
@@ -52,11 +52,11 @@ public SpringIndexerJavaContext(
5252
long lastModified,
5353
AtomicReference<TextDocument> docRef,
5454
String content,
55-
List<CachedSymbol> generatedSymbols,
56-
List<CachedBean> beans,
5755
IProblemCollector problemCollector,
5856
List<String> nextPassFiles,
59-
boolean fullAst
57+
boolean fullAst,
58+
boolean isIndexComplete,
59+
SpringIndexerJavaScanResult scanResult
6060
) {
6161
super();
6262
this.project = project;
@@ -66,11 +66,11 @@ public SpringIndexerJavaContext(
6666
this.lastModified = lastModified;
6767
this.docRef = docRef;
6868
this.content = content;
69-
this.generatedSymbols = generatedSymbols;
7069
this.getProblemCollector = problemCollector;
71-
this.beans = beans;
7270
this.nextPassFiles = nextPassFiles;
7371
this.fullAst = fullAst;
72+
this.isIndexComplete = isIndexComplete;
73+
this.scanResult = scanResult;
7474
}
7575

7676
public IJavaProject getProject() {
@@ -100,13 +100,17 @@ public AtomicReference<TextDocument> getDocRef() {
100100
public String getContent() {
101101
return content;
102102
}
103+
104+
public SpringIndexerJavaScanResult getResult() {
105+
return scanResult;
106+
}
103107

104108
public List<CachedSymbol> getGeneratedSymbols() {
105-
return generatedSymbols;
109+
return getResult().getGeneratedSymbols();
106110
}
107111

108112
public List<CachedBean> getBeans() {
109-
return beans;
113+
return getResult().getGeneratedBeans();
110114
}
111115

112116
public List<String> getNextPassFiles() {
@@ -147,15 +151,19 @@ public boolean isFullAst() {
147151
return fullAst;
148152
}
149153

154+
public boolean isIndexComplete() {
155+
return isIndexComplete;
156+
}
157+
150158
public void resetDocumentRelatedElements(String docURI) {
151-
Iterator<CachedBean> beansIterator = beans.iterator();
159+
Iterator<CachedBean> beansIterator = getBeans().iterator();
152160
while (beansIterator.hasNext()) {
153161
if (beansIterator.next().getDocURI().equals(docURI)) {
154162
beansIterator.remove();
155163
}
156164
}
157165

158-
Iterator<CachedSymbol> symbolsIterator = generatedSymbols.iterator();
166+
Iterator<CachedSymbol> symbolsIterator = getGeneratedSymbols().iterator();
159167
while (symbolsIterator.hasNext()) {
160168
if (symbolsIterator.next().getDocURI().equals(docURI)) {
161169
symbolsIterator.remove();

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJavaDependencyTracker.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2019, 2020 Pivotal, Inc.
2+
* Copyright (c) 2019, 2025 Pivotal, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -13,7 +13,6 @@
1313
import java.util.Collection;
1414
import java.util.Set;
1515

16-
import org.eclipse.jdt.core.dom.ITypeBinding;
1716
import org.slf4j.Logger;
1817
import org.slf4j.LoggerFactory;
1918

@@ -26,10 +25,6 @@ public class SpringIndexerJavaDependencyTracker {
2625

2726
private Multimap<String, String> dependencies = MultimapBuilder.hashKeys().hashSetValues().build();
2827

29-
public void addDependency(String sourceFile, ITypeBinding dependsOn) {
30-
dependencies.put(sourceFile, dependsOn.getKey());
31-
}
32-
3328
public void dump() {
3429
log.info("=== Dependencies ===");
3530
for (String sourceFile : dependencies.keySet()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.utils;
12+
13+
import java.io.File;
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
16+
import java.util.Collections;
17+
import java.util.HashMap;
18+
import java.util.HashSet;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.Set;
22+
import java.util.stream.Collectors;
23+
24+
import org.eclipse.lsp4j.Diagnostic;
25+
import org.eclipse.lsp4j.WorkspaceSymbol;
26+
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
27+
import org.springframework.ide.vscode.boot.java.reconcilers.CachedDiagnostics;
28+
import org.springframework.ide.vscode.commons.java.IJavaProject;
29+
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
30+
import org.springframework.ide.vscode.commons.util.UriUtil;
31+
32+
/**
33+
* Class to capture the major results of scanning one or more files
34+
* for Spring symbol creation, indexing, and reconciling
35+
*
36+
* @author Martin Lippert
37+
*/
38+
public class SpringIndexerJavaScanResult {
39+
40+
private final Map<String, Long> markedForReconciling;
41+
42+
private final List<CachedSymbol> generatedSymbols;
43+
private final List<CachedBean> generatedBeans;
44+
private final List<CachedDiagnostics> generatedDiagnostics;
45+
46+
private final IJavaProject project;
47+
private final String[] javaFiles;
48+
49+
public SpringIndexerJavaScanResult(IJavaProject project, String[] javaFiles) {
50+
this.project = project;
51+
this.javaFiles = javaFiles;
52+
53+
this.markedForReconciling = new HashMap<>();
54+
this.generatedSymbols = new ArrayList<CachedSymbol>();
55+
this.generatedBeans = new ArrayList<CachedBean>();
56+
this.generatedDiagnostics = new ArrayList<CachedDiagnostics>();
57+
}
58+
59+
public SpringIndexerJavaScanResult(IJavaProject project, String[] javaFiles, SymbolHandler symbolHandler,
60+
CachedSymbol[] symbols, CachedBean[] beans, CachedDiagnostics[] diagnostics) {
61+
62+
this.project = project;
63+
this.javaFiles = javaFiles;
64+
65+
this.markedForReconciling = new HashMap<>();
66+
this.generatedSymbols = Arrays.asList(symbols);
67+
this.generatedBeans = Arrays.asList(beans);
68+
this.generatedDiagnostics = Arrays.asList(diagnostics);
69+
}
70+
71+
public Map<String, Long> getMarkedForReconcilingWithCompleteIndex() {
72+
return markedForReconciling;
73+
}
74+
75+
public void markForReconcilingWithCompleteIndex(String file, long lastModified) {
76+
this.markedForReconciling.put(file, lastModified);
77+
}
78+
79+
public List<CachedBean> getGeneratedBeans() {
80+
return generatedBeans;
81+
}
82+
83+
public List<CachedSymbol> getGeneratedSymbols() {
84+
return generatedSymbols;
85+
}
86+
87+
public List<CachedDiagnostics> getGeneratedDiagnostics() {
88+
return generatedDiagnostics;
89+
}
90+
91+
public void publishResults(SymbolHandler symbolHandler) {
92+
WorkspaceSymbol[] enhancedSymbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(WorkspaceSymbol[]::new);
93+
Map<String, List<SpringIndexElement>> allBeans = generatedBeans.stream().filter(cachedBean -> cachedBean.getBean() != null).collect(Collectors.groupingBy(CachedBean::getDocURI, Collectors.mapping(CachedBean::getBean, Collectors.toList())));
94+
Map<String, List<Diagnostic>> diagnosticsByDoc = generatedDiagnostics.stream().filter(cachedDiagnostic -> cachedDiagnostic.getDiagnostic() != null).collect(Collectors.groupingBy(CachedDiagnostics::getDocURI, Collectors.mapping(CachedDiagnostics::getDiagnostic, Collectors.toList())));
95+
96+
addEmptyDiagnostics(diagnosticsByDoc, javaFiles); // to make sure that files without diagnostics publish an empty array of diagnostics
97+
98+
symbolHandler.addSymbols(this.project, enhancedSymbols, allBeans, diagnosticsByDoc);
99+
}
100+
101+
private void addEmptyDiagnostics(Map<String, List<Diagnostic>> diagnosticsByDoc, String[] javaFiles) {
102+
for (int i = 0; i < javaFiles.length; i++) {
103+
File file = new File(javaFiles[i]);
104+
String docURI = UriUtil.toUri(file).toASCIIString();
105+
106+
if (!diagnosticsByDoc.containsKey(docURI)) {
107+
diagnosticsByDoc.put(docURI, Collections.emptyList());
108+
}
109+
}
110+
}
111+
112+
}

headless-services/spring-boot-language-server/src/test/resources/test-projects/test-framework-7-indexing/src/main/java/com/example/RandomSampleClass.java

Whitespace-only changes.

0 commit comments

Comments
 (0)