GH-1431: enable index elements to compute document symbols on-demand

This commit is contained in:
Martin Lippert
2025-02-20 14:44:07 +01:00
parent f92a7a5b89
commit 888669ae92
8 changed files with 220 additions and 4 deletions

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom
* 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:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.protocol.spring;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.WorkspaceSymbol;
public class SimpleSymbolElement extends AbstractSpringIndexElement implements SymbolElement {
private final WorkspaceSymbol symbol;
public SimpleSymbolElement(WorkspaceSymbol symbol) {
this.symbol = symbol;
}
@Override
public DocumentSymbol getDocumentSymbol() {
DocumentSymbol documentSymbol = new DocumentSymbol();
documentSymbol.setName(symbol.getName());
documentSymbol.setKind(symbol.getKind());
documentSymbol.setTags(symbol.getTags());
Range range = symbol.getLocation().isLeft() ? symbol.getLocation().getLeft().getRange() : new Range();
documentSymbol.setRange(range);
documentSymbol.setSelectionRange(range);
return documentSymbol;
}
}

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom
* 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:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.protocol.spring;
import org.eclipse.lsp4j.DocumentSymbol;
public interface SymbolElement {
public DocumentSymbol getDocumentSymbol();
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom
* 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:
* Broadcom - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.index;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolKind;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.protocol.spring.SymbolElement;
public class SpringIndexToSymbolsConverter {
public static List<DocumentSymbol> createDocumentSymbols(List<SpringIndexElement> indexElements) {
List<DocumentSymbol> result = new ArrayList<>();
for (SpringIndexElement indexElement : indexElements) {
result.add(createSymbol(indexElement));
}
return result;
}
private static DocumentSymbol createSymbol(SpringIndexElement indexElement) {
DocumentSymbol symbol = null;
if (indexElement instanceof SymbolElement symbolElement) {
symbol = symbolElement.getDocumentSymbol();
}
else {
symbol = new DocumentSymbol(indexElement.toString(), SymbolKind.String,
new Range(new Position(), new Position()),
new Range(new Position(), new Position()));
}
List<SpringIndexElement> children = indexElement.getChildren();
if (children != null && children.size() > 0) {
List<DocumentSymbol> childSymbols = new ArrayList<>();
for (SpringIndexElement child : children) {
DocumentSymbol childSymbol = createSymbol(child);
if (childSymbol != null) {
childSymbols.add(childSymbol);
}
}
if (childSymbols.size() > 0) {
symbol.setChildren(childSymbols);
}
}
return symbol;
}
}

View File

@@ -22,7 +22,9 @@ import org.eclipse.lsp4j.WorkspaceSymbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
import org.springframework.ide.vscode.commons.protocol.spring.SimpleSymbolElement;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
@@ -41,6 +43,7 @@ public class RestrictedDefaultSymbolProvider extends AbstractSymbolProvider {
try {
WorkspaceSymbol symbol = DefaultSymbolProvider.provideDefaultSymbol(node, doc);
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
context.getBeans().add(new CachedBean(context.getDocURI(), new SimpleSymbolElement(symbol)));
} catch (Exception e) {
log.warn(e.getMessage());
}

View File

@@ -28,10 +28,12 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.codec.digest.DigestUtils;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.WorkspaceSymbolLocation;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -100,6 +102,28 @@ public class SpringFactoriesIndexer implements SpringIndexer {
return computeSymbols(docURI, content);
}
@Override
public List<DocumentSymbol> computeDocumentSymbols(IJavaProject project, String docURI, String content) throws Exception {
return computeSymbols(docURI, content).stream()
.map(workspaceSymbol -> convertToDocumentSymbol(workspaceSymbol))
.toList();
}
private DocumentSymbol convertToDocumentSymbol(WorkspaceSymbol workspaceSymbol) {
Either<Location, WorkspaceSymbolLocation> location = workspaceSymbol.getLocation();
Range range = null;
if (location.isLeft()) {
Location l = location.getLeft();
range = l.getRange();
}
else if (location.isRight()) {
range = new Range();
}
return new DocumentSymbol(workspaceSymbol.getName(), workspaceSymbol.getKind(), range, range);
}
private List<WorkspaceSymbol> computeSymbols(String docURI, String content) {
ImmutableList.Builder<WorkspaceSymbol> symbols = ImmutableList.builder();
PropertiesAst ast = new AntlrParser().parse(content).ast;
@@ -287,5 +311,5 @@ public class SpringFactoriesIndexer implements SpringIndexer {
IndexCacheKey key = getCacheKey(project);
cache.removeFiles(key, files, CachedSymbol.class);
}
}

View File

@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.java.utils;
import java.util.List;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.springframework.ide.vscode.commons.java.IJavaProject;
@@ -24,6 +25,7 @@ public interface SpringIndexer {
boolean isInterestedIn(String resource); // note that this might be a document URI or a standard file path on the system
List<WorkspaceSymbol> computeSymbols(IJavaProject project, String docURI, String content) throws Exception;
List<DocumentSymbol> computeDocumentSymbols(IJavaProject project, String docURI, String string) throws Exception;
void initializeProject(IJavaProject project, boolean clean) throws Exception;
void removeProject(IJavaProject project) throws Exception;

View File

@@ -49,9 +49,11 @@ import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.index.SpringIndexToSymbolsConverter;
import org.springframework.ide.vscode.boot.index.cache.IndexCache;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheKey;
import org.springframework.ide.vscode.boot.java.Annotations;
@@ -71,6 +73,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
import org.springframework.ide.vscode.commons.protocol.spring.SimpleSymbolElement;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.util.UriUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -366,6 +369,47 @@ public class SpringIndexerJava implements SpringIndexer {
return Collections.emptyList();
}
@Override
public List<DocumentSymbol> computeDocumentSymbols(IJavaProject project, String docURI, String content) throws Exception {
if (content != null) {
URI uri = URI.create(docURI);
return cuCache.withCompilationUnit(project, uri, cu -> {
List<CachedSymbol> generatedSymbols = new ArrayList<CachedSymbol>();
List<CachedBean> generatedBeans = new ArrayList<CachedBean>();
IProblemCollector voidProblemCollector = new IProblemCollector() {
@Override
public void endCollecting() {
}
@Override
public void beginCollecting() {
}
@Override
public void accept(ReconcileProblem problem) {
}
};
AtomicReference<TextDocument> docRef = new AtomicReference<>();
String file = UriUtil.toFileString(docURI);
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file,
0, docRef, content, generatedSymbols, generatedBeans, voidProblemCollector, SCAN_PASS.ONE, new ArrayList<>(), true);
scanAST(context, false);
List<SpringIndexElement> indexElements = generatedBeans.stream()
.map(cachedBean -> cachedBean.getBean())
.toList();
return SpringIndexToSymbolsConverter.createDocumentSymbols(indexElements);
});
}
return Collections.emptyList();
}
private Set<String> scanFilesInternally(IJavaProject project, DocumentDescriptor[] docs) throws Exception {
final boolean ignoreMethodBodies = false;
@@ -770,6 +814,7 @@ public class SpringIndexerJava implements SpringIndexer {
WorkspaceSymbol symbol = provideDefaultSymbol(node, context);
if (symbol != null) {
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
context.getBeans().add(new CachedBean(context.getDocURI(), new SimpleSymbolElement(symbol)));
}
}
@@ -965,5 +1010,5 @@ public class SpringIndexerJava implements SpringIndexer {
fileScanListener.fileScanned(file);
}
}
}

View File

@@ -31,9 +31,11 @@ import org.apache.commons.io.FileUtils;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DOMParser;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.index.SpringIndexToSymbolsConverter;
import org.springframework.ide.vscode.boot.index.cache.IndexCache;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheKey;
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
@@ -352,8 +354,7 @@ public class SpringIndexerXML implements SpringIndexer {
}
@Override
public List<WorkspaceSymbol> computeSymbols(IJavaProject project, String docURI, String content)
throws Exception {
public List<WorkspaceSymbol> computeSymbols(IJavaProject project, String docURI, String content) throws Exception {
if (content != null) {
List<CachedSymbol> generatedSymbols = new ArrayList<>();
List<CachedBean> generatedBeans = new ArrayList<>();
@@ -361,7 +362,24 @@ public class SpringIndexerXML implements SpringIndexer {
scanFile(project, content, docURI, 0, generatedSymbols, generatedBeans);
return generatedSymbols.stream().map(s -> s.getEnhancedSymbol()).collect(Collectors.toList());
}
return Collections.emptyList();
}
@Override
public List<DocumentSymbol> computeDocumentSymbols(IJavaProject project, String docURI, String content) throws Exception {
if (content != null) {
List<CachedSymbol> generatedSymbols = new ArrayList<>();
List<CachedBean> generatedBeans = new ArrayList<>();
scanFile(project, content, docURI, 0, generatedSymbols, generatedBeans);
return SpringIndexToSymbolsConverter.createDocumentSymbols(generatedBeans.stream().map(cachedBean -> cachedBean.getBean()).toList());
}
return Collections.emptyList();
}
}