blob: eb06b14e620aa685d5a413a544c6130a32dbbcb0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016-2017 Red Hat Inc. and others.
* 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:
* Mickael Istria (Red Hat Inc.) - initial implementation
*******************************************************************************/
package org.eclipse.lsp4e.operations.codeactions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.operations.diagnostics.LSPDiagnosticsToMarkers;
import org.eclipse.lsp4e.ui.Messages;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IMarkerResolution;
import org.eclipse.ui.IMarkerResolution2;
import org.eclipse.ui.IMarkerResolutionGenerator2;
import org.eclipse.ui.internal.progress.ProgressInfoItem;
public class LSPCodeActionMarkerResolution implements IMarkerResolutionGenerator2 {
private static final String LSP_REMEDIATION = "lspCodeActions"; //$NON-NLS-1$
private static final IMarkerResolution2 COMPUTING = new IMarkerResolution2() {
@Override
public void run(IMarker marker) {
// TODO Auto-generated method stub
// join on Future?
}
@Override
public String getLabel() {
// TODO Auto-generated method stub
return Messages.computing;
}
@Override
public Image getImage() {
// load class so image is loaded
return JFaceResources.getImage(ProgressInfoItem.class.getPackage().getName() + ".PROGRESS_DEFAULT"); //$NON-NLS-1$
}
@Override
public String getDescription() {
return Messages.computing;
}
};
@Override
public IMarkerResolution[] getResolutions(IMarker marker) {
Object att;
try {
checkMarkerResoultion(marker);
att = marker.getAttribute(LSP_REMEDIATION);
} catch (Exception e) {
LanguageServerPlugin.logError(e);
return new IMarkerResolution[0];
}
if (att == COMPUTING) {
return new IMarkerResolution[] { COMPUTING };
}
List<? extends Command> commands = (List<? extends Command>)att;
if (commands == null) {
return new CodeActionMarkerResolution[0];
}
List<IMarkerResolution> res = new ArrayList<>(commands.size());
for (Command command : commands) {
if (command != null) {
res.add(new CodeActionMarkerResolution(command));
}
}
return res.toArray(new IMarkerResolution[res.size()]);
}
private void checkMarkerResoultion(IMarker marker) throws Exception {
if (marker.getAttribute(LSP_REMEDIATION) != null) {
return;
} else {
IResource res = marker.getResource();
if (res != null && res.getType() == IResource.FILE) {
IFile file = (IFile)res;
String languageServerId = marker.getAttribute(LSPDiagnosticsToMarkers.LANGUAGE_SERVER_ID, null);
LanguageServer ls = null;
if (languageServerId != null) { // try to use same LS as the one that created the marker
LanguageServiceAccessor.getLanguageServer(file, (capabilities) -> Boolean.TRUE.equals(capabilities.getCodeActionProvider()), languageServerId);
}
if (ls == null) { // if it's not there, try any other server
ls = LanguageServiceAccessor.getLanguageServer(file, (capabilities) -> Boolean.TRUE.equals(capabilities.getCodeActionProvider()));
}
if (ls != null) {
marker.setAttribute(LSP_REMEDIATION, COMPUTING);
Diagnostic diagnostic = (Diagnostic)marker.getAttribute(LSPDiagnosticsToMarkers.LSP_DIAGNOSTIC);
CodeActionContext context = new CodeActionContext(Collections.singletonList(diagnostic));
CodeActionParams params = new CodeActionParams();
params.setContext(context);
params.setTextDocument(new TextDocumentIdentifier(LSPEclipseUtils.toUri(marker.getResource()).toString()));
params.setRange(diagnostic.getRange());
CompletableFuture<List<? extends Command>> codeAction = ls.getTextDocumentService().codeAction(params);
codeAction.thenAccept(actions -> {
try {
marker.setAttribute(LSP_REMEDIATION, actions);
} catch (CoreException e) {
LanguageServerPlugin.logError(e);
}
});
// wait a bit to avoid showing too much "Computing" without looking like a freeze
codeAction.get(300, TimeUnit.MILLISECONDS);
}
}
}
}
@Override
public boolean hasResolutions(IMarker marker) {
try {
checkMarkerResoultion(marker);
Object remediation = marker.getAttribute(LSP_REMEDIATION);
return remediation == COMPUTING || (remediation instanceof Collection && !((Collection<?>)remediation).isEmpty());
} catch (Exception ex) {
LanguageServerPlugin.logError(ex);
}
return false;
}
}