() {
//Note: filtering is 'fast' so is done synchronously
{
@@ -114,9 +124,16 @@ public class GotoSymbolDialogModel {
return builder.build();
}
};
+
+ private String keyBindings;
+ private OKHandler okHandler = DEFAULT_OK_HANDLER;
- public GotoSymbolDialogModel(SymbolsProvider symbolsProvider) {
- this.symbolsProvider.setValue(symbolsProvider);
+ public GotoSymbolDialogModel(String keyBindings, SymbolsProvider... symbolsProviders) {
+ this.keyBindings = keyBindings;
+ Assert.isLegal(symbolsProviders.length>0);
+ this.symbolsProviders = symbolsProviders;
+ this.currentSymbolsProviderIndex = 0;
+ this.currentSymbolsProvider.setValue(symbolsProviders[0]);
if (DEBUG) {
searchBox.addListener((e, v) -> {
debug("searchBox = "+v);
@@ -142,4 +159,26 @@ public class GotoSymbolDialogModel {
return status;
}
+ public synchronized void toggleSymbolsProvider() {
+ currentSymbolsProviderIndex = (currentSymbolsProviderIndex+1)%symbolsProviders.length;
+ currentSymbolsProvider.setValue(symbolsProviders[currentSymbolsProviderIndex]);
+ }
+
+ /**
+ * Set an ok handler. The handler is meant to be called by the UI when user request to
+ * execute the dialogs action on its current selection. For example, by pressing 'ENTER'
+ * key, or by double-clicking an element.
+ *
+ * If 'okHandler' returns 'true' then the dialog is closed. Otherwise it remains open.
+ */
+ public GotoSymbolDialogModel setOkHandler(OKHandler okHandler) {
+ this.okHandler = okHandler == null ? DEFAULT_OK_HANDLER : okHandler;
+ return this;
+ }
+
+ public boolean performOk(SymbolInformation selection) {
+ return this.okHandler.performOk(selection);
+ }
+
+
}
diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java
index 94c846fbc..a29734c0d 100644
--- a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java
+++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InFileSymbolsProvider.java
@@ -11,23 +11,66 @@
package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs;
import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.lsp4e.LSPEclipseUtils;
+import org.eclipse.lsp4e.LanguageServiceAccessor;
+import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
+import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.texteditor.ITextEditor;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * SymbolsProvider that only looks for symbols in current file using the LSP 'document/symbols' api.
+ *
+ * @author Kris De Volder
+ */
+@SuppressWarnings("restriction")
public class InFileSymbolsProvider implements SymbolsProvider {
- private ITextEditor target;
+ private LSPDocumentInfo info;
- public InFileSymbolsProvider(ITextEditor target) {
+ public InFileSymbolsProvider(LSPDocumentInfo target) {
super();
- this.target = target;
+ this.info = target;
}
@Override
- public Collection fetchFor(String query) {
- // TODO Auto-generated method stub
+ public Collection fetchFor(String query) throws Exception {
+ DocumentSymbolParams params = new DocumentSymbolParams(
+ new TextDocumentIdentifier(info.getFileUri().toString()));
+ CompletableFuture> symbolsFuture = info.getLanguageClient()
+ .getTextDocumentService().documentSymbol(params);
+ List extends SymbolInformation> symbols = symbolsFuture.get();
+ Thread.sleep(2000);
+ return symbols == null ? ImmutableList.of() : ImmutableList.copyOf(symbols);
+ }
+
+ public static SymbolsProvider createFor(ITextEditor textEditor) {
+ Collection infos = LanguageServiceAccessor.getLSPDocumentInfosFor(
+ LSPEclipseUtils.getDocument(textEditor),
+ capabilities -> Boolean.TRUE.equals(capabilities.getDocumentSymbolProvider()));
+ if (infos.isEmpty()) {
+ return null;
+ }
+ // TODO maybe consider better strategy such as iterating on all LS until we have a good result
+ LSPDocumentInfo info = infos.iterator().next();
+ if (info!=null) {
+ return new InFileSymbolsProvider(info);
+ }
return null;
}
+ @Override
+ public String getName() {
+ return "Symbols in File";
+ }
}
diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java
new file mode 100644
index 000000000..1ff0cedb0
--- /dev/null
+++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/InWorkspaceSymbolsProvider.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Pivotal, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.tooling.ls.eclipse.gotosymbol.dialogs;
+
+import java.time.Duration;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.lsp4e.LanguageServiceAccessor;
+import org.eclipse.lsp4j.SymbolInformation;
+import org.eclipse.lsp4j.WorkspaceSymbolParams;
+import org.eclipse.lsp4j.services.LanguageServer;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.springframework.ide.eclipse.boot.util.Log;
+
+import com.google.common.collect.ImmutableList;
+
+@SuppressWarnings("restriction")
+public class InWorkspaceSymbolsProvider implements SymbolsProvider {
+
+ private static final Duration TIMEOUT = Duration.ofSeconds(2);
+
+ private List languageServers;
+
+ public InWorkspaceSymbolsProvider(List languageServers) {
+ this.languageServers = languageServers;
+ }
+
+ @Override
+ public String getName() {
+ return "Symbols in Workspace";
+ }
+
+ @Override
+ public Collection fetchFor(String query) throws Exception {
+ //TODO: if we want decent support for multiple language servers...
+ // consider changing SymbolsProvider api and turning the stuff in here into something producing a
+ // Flux>
+ // This will help in
+ // - supporting cancelation
+ // - executing multiple requests to different servers in parallel.
+ // - producing results per server so don't have to wait for one slow server to see the rest.
+ //However it will also add complexity to the code that consumes this and at this time we only
+ // really use this with a single language server anyways.
+ ImmutableList.Builder allSymbols = ImmutableList.builder();
+ for (LanguageServer server : this.languageServers) {
+ try {
+ WorkspaceSymbolParams params = new WorkspaceSymbolParams(query);
+ List extends SymbolInformation> symbolsFuture =
+ server.getWorkspaceService().symbol(params)
+ .get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
+ allSymbols.addAll(symbolsFuture);
+ } catch (Exception e) {
+ Log.log(e);
+ }
+ }
+ return allSymbols.build();
+ }
+
+ public static InWorkspaceSymbolsProvider createFor(ExecutionEvent event) {
+ IEditorPart part = HandlerUtil.getActiveEditor(event);
+ IResource resource = null;
+ if (part != null && part.getEditorInput() != null) {
+ resource = part.getEditorInput().getAdapter(IResource.class);
+ } else {
+ IStructuredSelection selection = HandlerUtil.getCurrentStructuredSelection(event);
+ if (selection.isEmpty() || !(selection.getFirstElement() instanceof IAdaptable)) {
+ return null;
+ }
+ IAdaptable adaptable = (IAdaptable) selection.getFirstElement();
+ resource = adaptable.getAdapter(IResource.class);
+ }
+ if (resource!=null) {
+ return createFor(resource.getProject());
+ }
+ return null;
+ }
+
+ public static InWorkspaceSymbolsProvider createFor(IProject project) {
+ List languageServers = LanguageServiceAccessor.getLanguageServers(project,
+ capabilities -> Boolean.TRUE.equals(capabilities.getWorkspaceSymbolProvider()));
+ if (!languageServers.isEmpty()) {
+ return new InWorkspaceSymbolsProvider(languageServers);
+ }
+ return null;
+ }
+
+}
diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java
index 31abba0e5..a655a6e6a 100644
--- a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java
+++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/dialogs/SymbolsProvider.java
@@ -20,5 +20,6 @@ import org.eclipse.lsp4j.SymbolInformation;
* called only from a background job.
*/
public interface SymbolsProvider {
- Collection fetchFor(String query);
+ String getName();
+ Collection fetchFor(String query) throws Exception;
}
\ No newline at end of file
diff --git a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java
index d472d74ca..4323e76a9 100644
--- a/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java
+++ b/eclipse-plugins/org.springframework.tooling.ls.eclipse.gotosymbol/src/org/springframework/tooling/ls/eclipse/gotosymbol/handlers/GotoSymbolHandler.java
@@ -11,66 +11,88 @@
*******************************************************************************/
package org.springframework.tooling.ls.eclipse.gotosymbol.handlers;
-import java.util.Collection;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
-import org.eclipse.lsp4j.DocumentSymbolParams;
-import org.eclipse.lsp4j.SymbolInformation;
-import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.lsp4j.Location;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.texteditor.ITextEditor;
import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolDialog;
import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.GotoSymbolDialogModel;
+import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.InFileSymbolsProvider;
+import org.springframework.tooling.ls.eclipse.gotosymbol.dialogs.InWorkspaceSymbolsProvider;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Our sample handler extends AbstractHandler, an IHandler base class.
- * @see org.eclipse.core.commands.IHandler
- * @see org.eclipse.core.commands.AbstractHandler
- */
@SuppressWarnings("restriction")
public class GotoSymbolHandler extends AbstractHandler {
+
+ private static final boolean DEBUG = (""+Platform.getLocation()).contains("kdvolder");
+
+ private static void debug(String string) {
+ if (DEBUG) {
+ System.out.println(string);
+ }
+ }
+ private static GotoSymbolDialogModel currentDialog = null;
+
+ public GotoSymbolHandler() {
+ debug("Creating GotoSymbolHandler");
+ }
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
- System.out.println("GotoSymbolHandler.execute");
- IEditorPart part = HandlerUtil.getActiveEditor(event);
- if (part instanceof ITextEditor) {
- final ITextEditor textEditor = (ITextEditor) part;
- Collection infos = LanguageServiceAccessor.getLSPDocumentInfosFor(
- LSPEclipseUtils.getDocument(textEditor),
- capabilities -> Boolean.TRUE.equals(capabilities.getDocumentSymbolProvider()));
- if (infos.isEmpty()) {
- return null;
- }
- // TODO maybe consider better strategy such as iterating on all LS until we have a good result
- LSPDocumentInfo info = infos.iterator().next();
- final Shell shell = HandlerUtil.getActiveShell(event);
- DocumentSymbolParams params = new DocumentSymbolParams(
- new TextDocumentIdentifier(info.getFileUri().toString()));
- CompletableFuture> symbols = info.getLanguageClient()
- .getTextDocumentService().documentSymbol(params);
-
- symbols.thenAccept((List extends SymbolInformation> t) -> {
- shell.getDisplay().asyncExec(() -> {
- List allSymbols = ImmutableList.copyOf(t);
- GotoSymbolDialogModel model = new GotoSymbolDialogModel(query -> allSymbols);
- GotoSymbolDialog dialog = new GotoSymbolDialog(shell, textEditor, model);
- dialog.open();
+ debug(">>>GotoSymbolHandler.execute");
+ if (currentDialog!=null) {
+ debug("GotoSymbolDialog already open: send 'toggle' signal");
+ currentDialog.toggleSymbolsProvider();
+ } else {
+ IEditorPart part = HandlerUtil.getActiveEditor(event);
+ if (part instanceof ITextEditor) {
+ final Shell shell = HandlerUtil.getActiveShell(event);
+ final ITextEditor textEditor = (ITextEditor) part;
+
+ GotoSymbolDialogModel model = new GotoSymbolDialogModel(getKeybindings(event), InFileSymbolsProvider.createFor(textEditor), InWorkspaceSymbolsProvider.createFor(event))
+ .setOkHandler(symbolInformation -> {
+ if (symbolInformation!=null) {
+ Location location = symbolInformation.getLocation();
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ LSPEclipseUtils.openInEditor(location, page);
+ }
+ return true;
});
- });
+ GotoSymbolDialog dialog = new GotoSymbolDialog(shell, textEditor, model, /*alignRight*/ false);
+ currentDialog = model;
+ dialog.open();
+ debug("GotoSymbolDialog opened");
+ dialog.getShell().addDisposeListener(de -> {
+ debug("GotoSymbolDialog closed!");
+ currentDialog = null;
+ });
+ }
+ debug("<<0) {
+ return bindings[0].toString();
}
return null;
}
@@ -78,18 +100,18 @@ public class GotoSymbolHandler extends AbstractHandler {
@Override
public boolean isEnabled() {
boolean r = _isEnabled();
- System.out.println("isEnabled = "+r);
return r;
}
public boolean _isEnabled() {
-
IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart();
if (part instanceof ITextEditor) {
+ debug("activePart instanceof ITextEditor");
List infos = LanguageServiceAccessor.getLSPDocumentInfosFor(
LSPEclipseUtils.getDocument((ITextEditor) part),
(capabilities) -> Boolean.TRUE.equals(capabilities.getDocumentSymbolProvider()));
return !infos.isEmpty();
}
+ debug("activePart not ITextEditor: "+part);
return false;
}
}