blob: 562fd89d04345dcbba118d72eb7fe0550772f51b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Red Hat Inc. and others.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Mickael Istria (Red Hat Inc.) - initial implementation
* Lucas Bullen (Red Hat Inc.) - Bug 520053 - Clicking nodes in the 'Outline' should navigate
*******************************************************************************/
package org.eclipse.lsp4e.outline;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4e.LanguageServersRegistry;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.ContentOutline;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
public class EditorToOutlineAdapterFactory implements IAdapterFactory {
private static final Map<IEditorPart, LanguageServer> lsCache = Collections.synchronizedMap(new HashMap<>());
@Override
public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
if (adapterType == IContentOutlinePage.class && adaptableObject instanceof IEditorPart
&& LanguageServersRegistry.getInstance()
.canUseLanguageServer(((IEditorPart) adaptableObject).getEditorInput())) {
// first try to get / remove language server from cache from a previous call
LanguageServer server = lsCache.remove(adaptableObject);
if (server != null) {
return (T) createOutlinePage(adaptableObject, server);
}
IDocument document = LSPEclipseUtils.getDocument(((IEditorPart) adaptableObject).getEditorInput());
if (document != null) {
CompletableFuture<List<@NonNull LanguageServer>> languageServers = LanguageServiceAccessor
.getLanguageServers(document,
capabilities -> LSPEclipseUtils.hasCapability(capabilities.getDocumentSymbolProvider()));
List<@NonNull LanguageServer> servers = Collections.emptyList();
try {
servers = languageServers.get(50, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
refreshContentOutlineAsync(languageServers, (IEditorPart) adaptableObject);
} catch (ExecutionException e) {
LanguageServerPlugin.logError(e);
} catch (InterruptedException e) {
LanguageServerPlugin.logError(e);
Thread.currentThread().interrupt();
}
if (!servers.isEmpty()) {
LanguageServer languageServer = servers.get(0); // TODO consider other strategies (select, //
// merge...?)
return (T) createOutlinePage(adaptableObject, languageServer);
}
}
}
return null;
}
@Override
public Class<?>[] getAdapterList() {
return new Class<?>[] { IContentOutlinePage.class };
}
private static CNFOutlinePage createOutlinePage(Object adaptableObject, LanguageServer languageServer) {
ITextEditor textEditor = null;
if (adaptableObject instanceof ITextEditor) {
textEditor = (ITextEditor) adaptableObject;
}
return new CNFOutlinePage(languageServer, textEditor);
}
private static void refreshContentOutlineAsync(CompletableFuture<List<@NonNull LanguageServer>> languageServers,
IEditorPart editorPart) {
// try to get language server asynchronously
languageServers.thenAcceptAsync(servers -> {
if (!servers.isEmpty()) {
Display.getDefault().asyncExec(() -> {
IViewPart viewPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.findView("org.eclipse.ui.views.ContentOutline"); //$NON-NLS-1$
if (viewPart instanceof ContentOutline) {
lsCache.put(editorPart, servers.get(0));
((ContentOutline)viewPart).partActivated(editorPart);
}
});
}
});
}
}