From f0d936239b420f4ee677c010cd0534ada0041bd5 Mon Sep 17 00:00:00 2001 From: Nieraj Singh Date: Fri, 17 Jul 2020 14:30:06 -0700 Subject: [PATCH] PT 173744629 - Fix for file scope for symbols view --- .../META-INF/MANIFEST.MF | 5 +- .../dialogs/InFileSymbolsProvider.java | 65 ++++++++++----- .../gotosymbol/dialogs/SelectionTracker.java | 82 +++++++++++++++++-- .../view/SpringSymbolsViewModel.java | 2 +- 4 files changed, 125 insertions(+), 29 deletions(-) diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF index a11687138..891da63e4 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/META-INF/MANIFEST.MF @@ -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 diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java index ce5916d71..7541837f6 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java @@ -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 info; - public InFileSymbolsProvider(Supplier target) { + public InFileSymbolsProvider(Supplier info) { super(); - this.info = target; + this.info = info; } - + @Override public List> fetchFor(String query) throws Exception { - LSPDocumentInfo info = this.info.get(); - if (info!=null) { + CompletableFuture server = getServer(); + String uri = getUri(); + if (server != null && uri != null) { DocumentSymbolParams params = new DocumentSymbolParams( - new TextDocumentIdentifier(info.getFileUri().toString())); - CompletableFuture>> symbolsFuture = info.getLanguageClient() + new TextDocumentIdentifier(uri)); + CompletableFuture>> symbolsFuture = server + .get() .getTextDocumentService().documentSymbol(params); List> 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 rsrc) { - LiveExpression 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 getServer() throws Exception { + if (this.info != null && this.info.get() != null) { + return this.info.get().getInitializedLanguageClient(); + } + return null; + } + + public static SymbolsProvider createFor(LiveExpression documentData) { + Supplier 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; } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SelectionTracker.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SelectionTracker.java index 40e1c91de..6e71f3e5d 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SelectionTracker.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SelectionTracker.java @@ -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 INSTANCES = new HashMap<>(); @@ -51,9 +57,69 @@ public class SelectionTracker extends AbstractDisposable { } } - private final LiveVariable currentResource = new LiveVariable<>(); - public final LiveExpression 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 = new OldValueDisposer(this).getVar(); + private final LiveVariable 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 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 currentResource() { return currentResource; } + + public LiveExpression getDocumentData() { + return documentData; + } } diff --git a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java index fd5b1a0b6..d8ec21508 100644 --- a/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java +++ b/eclipse-language-servers/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/view/SpringSymbolsViewModel.java @@ -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); {