GH-1431: compute document symbols directly instead of going through workspace symbols

This commit is contained in:
Martin Lippert
2025-02-20 14:34:27 +01:00
parent eb99148c7a
commit f92a7a5b89
11 changed files with 133 additions and 92 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2024 Pivotal, Inc.
* Copyright (c) 2018, 2025 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
@@ -27,6 +27,7 @@ import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CodeLensParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
@@ -34,7 +35,6 @@ import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SemanticTokensLegend;
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.springframework.context.ApplicationContext;
@@ -192,7 +192,7 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
this.docSymbolHandler = new DocumentSymbolHandler() {
@Override
public List<? extends WorkspaceSymbol> handle(DocumentSymbolParams params) {
public List<? extends DocumentSymbol> handle(DocumentSymbolParams params) {
TextDocument doc = getDoc(appContext, params.getTextDocument().getUri());
LanguageId language = doc.getLanguageId();
List<LanguageServerComponents> subComponents = componentsByLanguageId.get(language);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2025 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
@@ -12,8 +12,8 @@ package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.List;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.WorkspaceSymbol;
import com.google.common.collect.ImmutableList;
@@ -22,6 +22,6 @@ public interface DocumentSymbolHandler {
DocumentSymbolHandler NO_SYMBOLS = (params) -> ImmutableList.of();
List<? extends WorkspaceSymbol> handle(DocumentSymbolParams params);
List<? extends DocumentSymbol> handle(DocumentSymbolParams params);
}

View File

@@ -1,42 +0,0 @@
/*******************************************************************************
* Copyright (c) 2019 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.List;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.WorkspaceSymbol;
import com.google.common.collect.ImmutableList;
/**
* Note if you implement HierarchicalDocumentSymbolHandler you must also implement the 'legacy'
* non-hierarchical handler because this is used as a fallback when client doesn't support
* hierarchical symbols.
*/
public interface HierarchicalDocumentSymbolHandler extends DocumentSymbolHandler {
HierarchicalDocumentSymbolHandler NO_SYMBOLS = new HierarchicalDocumentSymbolHandler() {
@Override
public List<? extends WorkspaceSymbol> handle(DocumentSymbolParams params) {
return ImmutableList.of();
}
@Override
public List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params) {
return ImmutableList.of();
}
};
List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2024 VMware Inc.
* Copyright (c) 2016, 2025 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
@@ -74,7 +74,6 @@ import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4j.jsonrpc.CompletableFutures;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
@@ -433,21 +432,12 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
//
// cancelToken.checkCanceled();
//
if (server.hasHierarchicalDocumentSymbolSupport() && h instanceof HierarchicalDocumentSymbolHandler) {
List<? extends DocumentSymbol> r = ((HierarchicalDocumentSymbolHandler)h).handleHierarchic(params);
List<? extends DocumentSymbol> r = h.handle(params);
//handle it when symbolHandler is sloppy and returns null instead of empty list.
return r == null
? ImmutableList.of()
: r.stream().map(symbolInfo -> Either.<SymbolInformation, DocumentSymbol>forRight(symbolInfo))
.collect(Collectors.toList());
} else {
List<? extends WorkspaceSymbol> r = h.handle(params);
//handle it when symbolHandler is sloppy and returns null instead of empty list.
return r == null
? ImmutableList.of()
: r.stream().map(symbolInfo -> Either.<SymbolInformation, DocumentSymbol>forLeft(new SymbolInformation(symbolInfo.getName(), symbolInfo.getKind(), symbolInfo.getLocation().getLeft(), symbolInfo.getContainerName())))
.collect(Collectors.toList());
}
: r.stream().map(symbol -> Either.<SymbolInformation, DocumentSymbol>forRight(symbol))
.collect(Collectors.toList());
});
}
else {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* Copyright (c) 2019, 2025 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
@@ -19,10 +19,9 @@ import java.util.Stack;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
@@ -40,7 +39,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocumentSymbolHandler, ITypeCollector {
public class TypeBasedYamlHierarchicalSymbolHandler implements DocumentSymbolHandler, ITypeCollector {
private static final Logger log = LoggerFactory.getLogger(TypeBasedYamlHierarchicalSymbolHandler.class);
@@ -153,12 +152,7 @@ public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocum
}
@Override
public List<? extends WorkspaceSymbol> handle(DocumentSymbolParams params) {
return baseHandler.handle(params);
}
@Override
public List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params) {
public List<? extends DocumentSymbol> handle(DocumentSymbolParams params) {
return outlineByUri.get(params.getTextDocument().getUri());
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2021 Pivotal, Inc.
* Copyright (c) 2017, 2025 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
@@ -15,11 +15,10 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.WorkspaceSymbol;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
@@ -63,8 +62,8 @@ public class TypeBasedYamlSymbolHandler implements DocumentSymbolHandler {
}
@Override
public List<? extends WorkspaceSymbol> handle(DocumentSymbolParams params) {
Builder<WorkspaceSymbol> builder = ImmutableList.builder();
public List<? extends DocumentSymbol> handle(DocumentSymbolParams params) {
Builder<DocumentSymbol> builder = ImmutableList.builder();
TextDocument doc = documents.getLatestSnapshot(params.getTextDocument().getUri());
if (doc != null) {
@@ -81,14 +80,17 @@ public class TypeBasedYamlSymbolHandler implements DocumentSymbolHandler {
return builder.build();
}
protected WorkspaceSymbol createSymbol(TextDocument doc, Node node, YType type) throws BadLocationException {
protected DocumentSymbol createSymbol(TextDocument doc, Node node, YType type) throws BadLocationException {
DocumentRegion region = NodeUtil.region(doc, node);
Location location = new Location(doc.getUri(), doc.toRange(region.getStart(), region.getLength()));
WorkspaceSymbol symbol = new WorkspaceSymbol();
Range range = doc.toRange(region.getStart(), region.getLength());
DocumentSymbol symbol = new DocumentSymbol();
symbol.setName(region.toString());
symbol.setKind(symbolKind(type));
symbol.setLocation(Either.forLeft(location));
symbol.setContainerName(containerName(type));
symbol.setRange(range);
symbol.setSelectionRange(range);
return symbol;
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Pivotal, Inc.
* Copyright (c) 2018, 2025 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
@@ -14,7 +14,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.ide.vscode.commons.languageserver.LanguageServerRunner;
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.util.LogRedirect;
@@ -48,7 +48,7 @@ public class ConcourseLanguageServerBootApp {
return new ASTTypeCache();
}
@Bean HierarchicalDocumentSymbolHandler documentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, PipelineYmlSchema schema) {
@Bean DocumentSymbolHandler documentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, PipelineYmlSchema schema) {
TypeBasedYamlSymbolHandler baseHandler = new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes());
return new TypeBasedYamlHierarchicalSymbolHandler(baseHandler, schema.getHierarchicalDefinitionTypes());
}

View File

@@ -16,10 +16,12 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -42,6 +44,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceSymbol;
@@ -51,6 +54,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ide.vscode.boot.index.SpringIndexToSymbolsConverter;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.index.cache.IndexCache;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
@@ -80,6 +84,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocu
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.BeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.DocumentElement;
import org.springframework.ide.vscode.commons.protocol.spring.MatchingBeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndex;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
@@ -732,7 +737,99 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
return Collections.emptyList();
}
}
public List<? extends DocumentSymbol> getDocumentSymbols(String docURI) {
List<DocumentSymbol> result = new ArrayList<>();
List<? extends WorkspaceSymbol> symbols = getSymbols(docURI);
for (WorkspaceSymbol symbol : symbols) {
DocumentSymbol docSymbol = new DocumentSymbol();
docSymbol.setName(symbol.getName());
docSymbol.setKind(symbol.getKind());
docSymbol.setRange(symbol.getLocation().getLeft().getRange());
docSymbol.setSelectionRange(symbol.getLocation().getLeft().getRange());
docSymbol.setTags(symbol.getTags());
result.add(docSymbol);
}
return result;
}
/*
public List<? extends WorkspaceSymbol> getSymbols(String docURI) {
List<WorkspaceSymbol> result = new ArrayList<>();
Deque<DocumentSymbol> remainingSymbols = new ArrayDeque<>();
List<? extends DocumentSymbol> documentSymbols = getDocumentSymbols(docURI);
remainingSymbols.addAll(documentSymbols);
while (!remainingSymbols.isEmpty()) {
DocumentSymbol documentSymbol = remainingSymbols.poll();
WorkspaceSymbol workspaceSymbol = new WorkspaceSymbol();
workspaceSymbol.setName(documentSymbol.getName());
workspaceSymbol.setKind(documentSymbol.getKind());
workspaceSymbol.setTags(documentSymbol.getTags());
Location location = new Location(docURI, documentSymbol.getRange());
workspaceSymbol.setLocation(Either.forLeft(location));
result.add(workspaceSymbol);
if (documentSymbol.getChildren() != null) {
remainingSymbols.addAll(documentSymbol.getChildren());
}
}
return result;
}
public List<? extends DocumentSymbol> getDocumentSymbols(String docURI) {
try {
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docURI);
URI uri = URI.create(docURI);
CompletableFuture<IJavaProject> projectInitialized = futureProjectFinder.findFuture(uri).thenCompose(project -> projectInitializedFuture(project));
IJavaProject project = projectInitialized.get(15, TimeUnit.SECONDS);
ImmutableList.Builder<DocumentSymbol> builder = ImmutableList.builder();
if (project != null && doc != null) {
// Collect symbols from the opened document
synchronized(this) {
for (SpringIndexer indexer : this.indexers) {
if (indexer.isInterestedIn(docURI)) {
try {
List<DocumentSymbol> adhocDocumentSymbols = indexer.computeDocumentSymbols(project, docURI, doc.get());
builder.addAll(adhocDocumentSymbols);
} catch (Exception e) {
log.error("{}", e);
}
}
}
}
} else {
// Take symbols from the index if there is no opened document.
DocumentElement document = springIndex.getDocument(docURI);
if (document != null) {
List<SpringIndexElement> children = document.getChildren();
builder.addAll(SpringIndexToSymbolsConverter.createDocumentSymbols(children));
}
}
return builder.build();
} catch (Exception e) {
log.warn("", e);
return Collections.emptyList();
}
}
*/
@Override
public CompletableFuture<List<Bean>> beans(BeansParams params) {
String projectName = params.getProjectName();

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 VMware, Inc.
* Copyright (c) 2022, 2025 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
@@ -49,7 +49,7 @@ public class SpringFactoriesLanguageServerComponents implements LanguageServerCo
@Override
public Optional<DocumentSymbolHandler> getDocumentSymbolProvider() {
return Optional.of(params -> springIndex.getSymbols(params.getTextDocument().getUri()));
return Optional.of(params -> springIndex.getDocumentSymbols(params.getTextDocument().getUri()));
}
}

View File

@@ -173,8 +173,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
new SpringProcessCommandHandler(server, liveDataService, liveDataLocalProcessConnector, appContext.getBeansOfType(SpringProcessConnectorRemote.class).values());
new CopilotAgentCommandHandler(server, projectFinder,responseModifier);
docSymbolProvider = params -> springSymbolIndex.getSymbols(params.getTextDocument().getUri());
docSymbolProvider = params -> springSymbolIndex.getDocumentSymbols(params.getTextDocument().getUri());
workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(springSymbolIndex,
new LiveAppURLSymbolProvider(liveDataProvider)));

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2022 Pivotal, Inc.
* Copyright (c) 2019, 2025 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
@@ -46,7 +46,7 @@ public class SpringXMLLanguageServerComponents implements LanguageServerComponen
SpelReconciler spelReconciler) {
this.projectFinder = serverParams.projectFinder;
this.docSymbolProvider = params -> springIndexer.getSymbols(params.getTextDocument().getUri());
this.docSymbolProvider = params -> springIndexer.getDocumentSymbols(params.getTextDocument().getUri());
server.doOnInitialized(this::initialized);
server.onShutdown(this::shutdown);