blob: dd1398aa7dfc94bab6a92f4270038a4b47b1b6a1 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2019 Stephan Wahlbrink 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, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.debug.ui.launcher;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.internal.r.debug.ui.RLaunchingMessages;
import org.eclipse.statet.ltk.ast.core.AstInfo;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.util.LTKWorkbenchUIUtil;
import org.eclipse.statet.r.core.model.IRModelInfo;
import org.eclipse.statet.r.core.model.IRModelManager;
import org.eclipse.statet.r.core.model.IRSourceUnit;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.core.rsource.ast.RAst;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.launching.RCodeLaunching;
import org.eclipse.statet.r.launching.RCodeLaunching.SourceRegion;
import org.eclipse.statet.r.ui.RUI;
/**
* Launch shortcut, which submits the commands (touched by selection)
* and does not change the focus.
*
* Supports only text editors with input supporting R AST.
*/
public class SubmitEntireCommandHandler extends AbstractHandler {
protected static class Data {
ISourceEditor editor;
ITextSelection selection;
AbstractDocument document;
IRModelInfo model;
AstInfo ast;
RAstNode[] nodes;
IRSourceUnit su;
List<SourceRegion> regions;
}
private final boolean fGotoConsole;
public SubmitEntireCommandHandler() {
this(false);
}
protected SubmitEntireCommandHandler(final boolean gotoConsole) {
fGotoConsole = gotoConsole;
}
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
final IWorkbenchPart workbenchPart = HandlerUtil.getActivePart(event);
final AtomicReference<IStatus> success= new AtomicReference<>();
try {
if (workbenchPart instanceof IEditorPart) {
final Data data = new Data();
data.editor = workbenchPart.getAdapter(ISourceEditor.class);
if (data.editor != null) {
data.selection = (ITextSelection) data.editor.getViewer().getSelection();
if (data.selection != null) {
workbenchPart.getSite().getService(IProgressService.class)
.busyCursorWhile(new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor)
throws InvocationTargetException {
try {
success.set(doLaunch(data, monitor));
}
catch (final CoreException e) {
throw new InvocationTargetException(e);
}
}
});
}
}
}
}
catch (final InvocationTargetException e) {
LaunchShortcutUtil.handleRLaunchException(e.getTargetException(),
getErrorMessage(), event);
return null;
}
catch (final InterruptedException e) {
return null;
}
final IStatus status = success.get();
if (status != null
&& status.getSeverity() != IStatus.OK && status.getSeverity() != IStatus.CANCEL) {
LTKWorkbenchUIUtil.indicateStatus(status, event);
}
return null;
}
protected String getErrorMessage() {
return RLaunchingMessages.RCommandLaunch_error_message;
}
protected IStatus doLaunch(final Data data, final IProgressMonitor monitor)
throws CoreException {
{ final ISourceUnit su = data.editor.getSourceUnit();
if (su instanceof IRSourceUnit) {
data.su = (IRSourceUnit) su;
}
else {
return LaunchShortcutUtil.createUnsupported();
}
}
assert (data.su.getDocument(monitor) == data.editor.getViewer().getDocument());
data.document = data.su.getDocument(monitor);
monitor.subTask(RLaunchingMessages.RCodeLaunch_UpdateStructure_task);
synchronized ((data.document instanceof ISynchronizable) ?
((ISynchronizable) data.document).getLockObject() : data.document) {
data.model = (IRModelInfo) data.su.getModelInfo(RModel.R_TYPE_ID,
IRModelManager.MODEL_FILE, monitor );
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (data.model != null) {
data.ast = data.model.getAst();
}
else {
data.ast = data.su.getAstInfo(null, true, monitor);
}
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (data.ast == null) {
return LaunchShortcutUtil.createUnsupported();
}
final IStatus status = getRegions(data);
if (!status.isOK() || data.regions == null) {
return status;
}
}
monitor.subTask(RLaunchingMessages.RCodeLaunch_SubmitCode_task);
if (RCodeLaunching.runRCodeDirect(data.regions, this.fGotoConsole)) {
postLaunch(data);
}
return Status.OK_STATUS;
}
protected IStatus getRegions(final Data data)
throws CoreException {
final RAstNode[] nodes = RAst.findDeepestCommands(data.ast.getRoot(),
data.selection.getOffset(), data.selection.getOffset()+data.selection.getLength() );
if (nodes == null || nodes.length == 0) {
final RAstNode next = RAst.findNextCommands(data.ast.getRoot(),
data.selection.getOffset()+data.selection.getLength() );
if (next != null) {
UIAccess.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
data.editor.selectAndReveal(next.getStartOffset(), 0);
}
});
}
return Status.OK_STATUS;
}
try {
data.nodes = nodes;
final List<SourceRegion> list= new ArrayList<>(nodes.length);
for (int i = 0; i < nodes.length; i++) {
if (RAst.hasErrors(nodes[i])) {
return new Status(IStatus.ERROR, RUI.BUNDLE_ID,
RLaunchingMessages.SubmitCode_info_SyntaxError_message );
}
final SourceRegion region = new SourceRegion(data.su, data.document);
region.setBegin(checkStart(data.document, nodes[i].getStartOffset()));
region.setEnd(nodes[i].getEndOffset());
region.setCode(data.document.get(region.getOffset(), region.getLength()));
region.setNode(nodes[i]);
list.add(region);
}
data.regions = list;
return Status.OK_STATUS;
}
catch (final BadLocationException e) {
throw new CoreException(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1,
RLaunchingMessages.SubmitCode_error_WhenAnalyzingAndCollecting_message, e));
}
}
protected int checkStart(final IDocument doc, final int offset) throws BadLocationException {
final int startLine = doc.getLineOfOffset(offset);
final int lineOffset = doc.getLineOffset(startLine);
if (offset == lineOffset) {
return offset;
}
final String s = doc.get(lineOffset, offset-lineOffset);
for (int i = 0; i < s.length(); i++) {
final char c = s.charAt(i);
if (c != ' ' && c != '\t') {
return offset;
}
}
return lineOffset;
}
protected void postLaunch(final Data data) {
}
}