| /*=============================================================================# |
| # Copyright (c) 2007, 2020 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.element.SourceUnit; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| import org.eclipse.statet.ltk.ui.util.LTKWorkbenchUIUtil; |
| import org.eclipse.statet.r.core.model.RModel; |
| import org.eclipse.statet.r.core.model.RModelManager; |
| import org.eclipse.statet.r.core.model.RSourceUnit; |
| import org.eclipse.statet.r.core.model.RSourceUnitModelInfo; |
| import org.eclipse.statet.r.core.rsource.ast.RAsts; |
| 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; |
| RSourceUnitModelInfo model; |
| AstInfo ast; |
| RAstNode[] nodes; |
| RSourceUnit 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 SourceUnit su = data.editor.getSourceUnit(); |
| if (su instanceof RSourceUnit) { |
| data.su = (RSourceUnit)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 = (RSourceUnitModelInfo)data.su.getModelInfo(RModel.R_TYPE_ID, |
| RModelManager.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 = RAsts.findDeepestCommands(data.ast.getRoot(), |
| data.selection.getOffset(), data.selection.getOffset()+data.selection.getLength() ); |
| if (nodes == null || nodes.length == 0) { |
| final RAstNode next = RAsts.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 (RAsts.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) { |
| } |
| |
| } |