blob: 3b02adb24772501e377566e63ee42112e2b3ab31 [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 517428] Requests sent before initialization
*******************************************************************************/
package org.eclipse.lsp4e.operations.codeactions;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4e.ui.Messages;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.menus.IWorkbenchContribution;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.texteditor.ITextEditor;
public class LSPCodeActionsMenu extends ContributionItem implements IWorkbenchContribution {
private List<LSPDocumentInfo> infos;
private Range range;
@Override
public void initialize(IServiceLocator serviceLocator) {
IEditorPart editor = LSPEclipseUtils.getActiveTextEditor();
if (editor != null) {
ITextEditor textEditor = (ITextEditor) editor;
IDocument document = LSPEclipseUtils.getDocument(textEditor);
if (document == null) {
return;
}
infos = LanguageServiceAccessor.getLSPDocumentInfosFor(document,
LSPCodeActionMarkerResolution::providesCodeActions);
ITextSelection selection = (ITextSelection) textEditor.getSelectionProvider().getSelection();
try {
this.range = new Range(LSPEclipseUtils.toPosition(selection.getOffset(), document),
LSPEclipseUtils.toPosition(selection.getOffset() + selection.getLength(), document));
} catch (BadLocationException e) {
LanguageServerPlugin.logError(e);
}
}
}
@Override
public void fill(final Menu menu, int index) {
final MenuItem item = new MenuItem(menu, SWT.NONE, index);
item.setEnabled(false);
if (infos.isEmpty()) {
item.setText(Messages.notImplemented);
return;
}
item.setText(Messages.computing);
CodeActionContext context = new CodeActionContext(Collections.emptyList());
CodeActionParams params = new CodeActionParams();
params.setTextDocument(new TextDocumentIdentifier(infos.get(0).getFileUri().toString()));
params.setRange(this.range);
params.setContext(context);
Set<CompletableFuture<?>> runningFutures = new HashSet<>();
for (LSPDocumentInfo info : this.infos) {
final CompletableFuture<List<Either<Command, CodeAction>>> codeActions = info.getInitializedLanguageClient()
.thenComposeAsync(languageServer -> languageServer.getTextDocumentService().codeAction(params));
runningFutures.add(codeActions);
codeActions.whenComplete((t, u) -> {
runningFutures.remove(codeActions);
UIJob job = new UIJob(menu.getDisplay(), Messages.updateCodeActions_menu) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (u != null) {
final MenuItem item = new MenuItem(menu, SWT.NONE, index);
item.setText(u.getMessage());
item.setImage(PlatformUI.getWorkbench().getSharedImages()
.getImage(ISharedImages.IMG_DEC_FIELD_ERROR));
item.setEnabled(false);
} else if (t != null) {
for (Either<Command, CodeAction> command : t) {
if (command != null) {
if (command.isLeft()) {
final MenuItem item = new MenuItem(menu, SWT.NONE, index);
item.setText(command.getLeft().getTitle());
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
executeCommand(info, command.getLeft());
}
});
} else if (command.isRight()) {
CodeAction codeAction = command.getRight();
final MenuItem item = new MenuItem(menu, SWT.NONE, index);
item.setText(codeAction.getTitle());
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (codeAction.getEdit() != null) {
LSPEclipseUtils.applyWorkspaceEdit(codeAction.getEdit());
}
if (codeAction.getCommand() != null) {
executeCommand(info, codeAction.getCommand());
}
}
});
}
}
}
}
if (menu.getItemCount() == 1) {
item.setText(Messages.codeActions_emptyMenu);
} else {
item.dispose();
}
return Status.OK_STATUS;
}
};
job.schedule();
});
}
super.fill(menu, index);
}
private void executeCommand(LSPDocumentInfo info, Command command) {
ServerCapabilities capabilities = info.getCapabilites();
if (capabilities != null) {
ExecuteCommandOptions provider = capabilities.getExecuteCommandProvider();
if (provider != null && provider.getCommands().contains(command.getCommand())) {
ExecuteCommandParams params = new ExecuteCommandParams();
params.setCommand(command.getCommand());
params.setArguments(command.getArguments());
info.getInitializedLanguageClient()
.thenAcceptAsync(ls -> ls.getWorkspaceService().executeCommand(params));
}
}
}
}