PT 173744629 - Fix for file scope for symbols view

This commit is contained in:
Nieraj Singh
2020-07-17 14:30:06 -07:00
parent 72b371c8f3
commit f0d936239b
4 changed files with 125 additions and 29 deletions

View File

@@ -22,7 +22,10 @@ Require-Bundle: org.eclipse.ui,
org.reactivestreams.reactive-streams;bundle-version="1.0.0",
org.springframework.ide.eclipse.boot.dash,
org.springsource.ide.eclipse.commons.frameworks.core,
org.springframework.ide.eclipse.boot
org.springframework.ide.eclipse.boot,
org.eclipse.ui.workbench,
org.eclipse.ui.ide,
org.eclipse.ui.editors
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-Activator: org.springframework.tooling.ls.eclipse.gotosymbol.GotoSymbolPlugin
Bundle-ActivationPolicy: lazy

View File

@@ -15,7 +15,6 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServiceAccessor;
@@ -25,7 +24,9 @@ import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.ui.texteditor.ITextEditor;
import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.SelectionTracker.DocumentData;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import com.google.common.collect.ImmutableList;
@@ -40,18 +41,20 @@ public class InFileSymbolsProvider implements SymbolsProvider {
private Supplier<LSPDocumentInfo> info;
public InFileSymbolsProvider(Supplier<LSPDocumentInfo> target) {
public InFileSymbolsProvider(Supplier<LSPDocumentInfo> info) {
super();
this.info = target;
this.info = info;
}
@Override
public List<Either<SymbolInformation, DocumentSymbol>> fetchFor(String query) throws Exception {
LSPDocumentInfo info = this.info.get();
if (info!=null) {
CompletableFuture<LanguageServer> server = getServer();
String uri = getUri();
if (server != null && uri != null) {
DocumentSymbolParams params = new DocumentSymbolParams(
new TextDocumentIdentifier(info.getFileUri().toString()));
CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> symbolsFuture = info.getLanguageClient()
new TextDocumentIdentifier(uri));
CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> symbolsFuture = server
.get()
.getTextDocumentService().documentSymbol(params);
List<Either<SymbolInformation, DocumentSymbol>> symbols = symbolsFuture.get();
return symbols == null ? ImmutableList.of() : ImmutableList.copyOf(symbols);
@@ -59,21 +62,39 @@ public class InFileSymbolsProvider implements SymbolsProvider {
return ImmutableList.of();
}
public static SymbolsProvider createFor(LiveExpression<IResource> rsrc) {
LiveExpression<LSPDocumentInfo> target = rsrc.apply(r -> {
// Get the existing document rather than request a document that may result
// in a connection to the associated file buffer. The reason for this is
// described in https://www.pivotaltracker.com/story/show/173267278
IDocument document = LSPEclipseUtils.getExistingDocument(r);
return getLSPDocumentInfo(document);
});
return new InFileSymbolsProvider(target::getValue);
private String getUri() {
if (this.info != null) {
LSPDocumentInfo info = this.info.get();
if (info != null) {
return info.getFileUri().toString();
}
}
return null;
}
private CompletableFuture<LanguageServer> getServer() throws Exception {
if (this.info != null && this.info.get() != null) {
return this.info.get().getInitializedLanguageClient();
}
return null;
}
public static SymbolsProvider createFor(LiveExpression<DocumentData> documentData) {
Supplier<LSPDocumentInfo> inf = () -> {
DocumentData data = documentData.getValue();
if (data != null) {
IDocument document = data.getDocument();
return getLSPDocumentInfo(document);
}
return null;
};
return new InFileSymbolsProvider(inf);
}
public static SymbolsProvider createFor(ITextEditor textEditor) {
IDocument document = LSPEclipseUtils.getDocument(textEditor);
LSPDocumentInfo info = getLSPDocumentInfo(document);
if (info!=null) {
if (info != null) {
return new InFileSymbolsProvider(() -> info);
}
return null;
@@ -100,10 +121,12 @@ public class InFileSymbolsProvider implements SymbolsProvider {
@Override
public boolean fromFile(SymbolInformation symbol) {
LSPDocumentInfo info = this.info.get();
if (info!=null && symbol != null && symbol.getLocation() != null) {
if (symbol != null && symbol.getLocation() != null) {
String symbolUri = symbol.getLocation().getUri();
return info.getFileUri().toString().equals(symbolUri);
String uri = getUri();
if (uri != null) {
return uri.toString().equals(symbolUri);
}
}
return false;
}

View File

@@ -13,27 +13,33 @@ package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.DocumentProviderRegistry;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.springframework.ide.eclipse.boot.dash.model.AbstractDisposable;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
import org.springsource.ide.eclipse.commons.livexp.util.OldValueDisposer;
@SuppressWarnings("restriction")
public class SelectionTracker extends AbstractDisposable {
private static Map<IWorkbenchWindow, SelectionTracker> INSTANCES = new HashMap<>();
@@ -51,9 +57,69 @@ public class SelectionTracker extends AbstractDisposable {
}
}
private final LiveVariable<IResource> currentResource = new LiveVariable<>();
public final LiveExpression<IProject> currentProject = currentResource.apply(r -> r==null ? null : r.getProject());
// In order for the symbols view to correctly fetching information for a selection, it needs to find an active language
// server for the given selection via LSP4E API (see the symbols view model), and document is required to find that active language server
// Typically this document will be available if an editor is open.
// However, symbols view also supports the case of showing information on a selection that does not have an open editor.
// In this case, a document needs to be available for that selection, which can be accomplished my performing part of the behaviour
// that would occur when an actual editor opens: namely connecting to that document via a document provider. This is what the document
// data does: it simulates "opening" an editor when there is a selection,and "closing" an editor for an old selection when
// there is a selection change
static class DocumentData implements Disposable {
public final IFileEditorInput input;
private final IDocumentProvider documentProvider;
public DocumentData(IFile file) {
super();
this.input = new FileEditorInput(file);
this.documentProvider = DocumentProviderRegistry.getDefault().getDocumentProvider(input);
if (this.documentProvider != null) {
try {
this.documentProvider.connect(input);
IDocument document = this.documentProvider.getDocument(input);
// This step appears to be necessary to avoid having the current language server shutdown when disconnecting
// a document (see dispose()) and a new one start up again every time a user changes selection and no editor is open
LanguageServiceAccessor.getLanguageServers(document, capabilities -> capabilities.getDocumentSymbolProvider()).get();
} catch (Exception e) {
Log.log(e);
}
}
}
@Override
public void dispose() {
if (input != null && documentProvider != null) {
documentProvider.disconnect(input);
}
}
public IDocument getDocument() {
return this.documentProvider != null ? this.documentProvider.getDocument(input) : null;
}
@Override
public String toString() {
return "DocumentData [file=" + input.getFile() + "]";
}
}
private final LiveVariable<DocumentData> documentData = new OldValueDisposer<DocumentData>(this).getVar();
private final LiveVariable<IResource> currentResource = new LiveVariable<>();
{
currentResource.onChange(this, (e, v) -> {
IResource value = currentResource.getValue();
if (value instanceof IFile) {
documentData.setValue(new DocumentData((IFile) value));
} else {
documentData.setValue(null);
}
});
}
public final LiveExpression<IProject> currentProject = currentResource.apply(r -> r==null ? null : r.getProject());
private SelectionTracker(IWorkbenchWindow wbw) {
ISelectionService selectionService = wbw.getSelectionService();
ISelectionListener selectionListener = new ISelectionListener() {
@@ -110,4 +176,8 @@ public class SelectionTracker extends AbstractDisposable {
public LiveExpression<IResource> currentResource() {
return currentResource;
}
public LiveExpression<DocumentData> getDocumentData() {
return documentData;
}
}

View File

@@ -35,7 +35,7 @@ public class SpringSymbolsViewModel {
gotoSymbols = new GotoSymbolDialogModel(null,
InWorkspaceSymbolsProvider.createFor(currentProject::getValue),
InProjectSymbolsProvider.createFor(currentProject),
InFileSymbolsProvider.createFor(currentResource)
InFileSymbolsProvider.createFor(currentSelection.getDocumentData())
)
.setFavourites(FavouritesPreference.INSTANCE);
{