| /*=============================================================================# |
| # Copyright (c) 2008, 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.util.ArrayList; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| 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.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.IStreamListener; |
| import org.eclipse.debug.core.model.IStreamMonitor; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.jface.text.source.SourceViewer; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.handlers.HandlerUtil; |
| import org.eclipse.ui.progress.IWorkbenchSiteProgressService; |
| import org.eclipse.ui.services.IServiceLocator; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| import org.eclipse.statet.jcommons.ts.core.Tool; |
| import org.eclipse.statet.jcommons.ts.core.ToolService; |
| |
| import org.eclipse.statet.ecommons.runtime.core.util.StatusUtils; |
| import org.eclipse.statet.ecommons.text.IndentUtil; |
| import org.eclipse.statet.ecommons.text.TextUtil; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| |
| import org.eclipse.statet.internal.r.debug.ui.RLaunchingMessages; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| import org.eclipse.statet.ltk.ui.util.LTKWorkbenchUIUtil; |
| import org.eclipse.statet.nico.core.runtime.ConsoleRunnable; |
| import org.eclipse.statet.nico.core.runtime.IRequireSynch; |
| import org.eclipse.statet.nico.core.runtime.SubmitType; |
| import org.eclipse.statet.nico.core.runtime.ToolController; |
| import org.eclipse.statet.nico.ui.NicoUI; |
| import org.eclipse.statet.nico.ui.NicoUITools; |
| import org.eclipse.statet.r.console.core.IRBasicAdapter; |
| import org.eclipse.statet.r.console.core.RConsoleTool; |
| import org.eclipse.statet.r.console.core.RWorkspace; |
| import org.eclipse.statet.r.core.IRCoreAccess; |
| import org.eclipse.statet.r.core.RCore; |
| import org.eclipse.statet.r.core.RUtil; |
| import org.eclipse.statet.r.ui.RUI; |
| import org.eclipse.statet.r.ui.editors.IRSourceEditor; |
| |
| |
| /** |
| * |
| */ |
| public class SubmitSelectionAndPasteOutputHandler extends AbstractHandler { |
| |
| |
| private static class R implements ConsoleRunnable, Runnable { |
| |
| private ISourceEditor fEditor; |
| private IDocument fDocument; |
| private String[] fLines; |
| private Position fPosition; |
| private StringBuilder fOutput; |
| |
| |
| R(final ISourceEditor editor) { |
| fEditor = editor; |
| } |
| |
| private boolean setupSource(final ITextSelection selection) { |
| final SourceViewer viewer = fEditor.getViewer(); |
| fDocument = viewer.getDocument(); |
| try { |
| if (selection.getLength() > 0) { |
| final ArrayList<String> lines= new ArrayList<>(0); |
| TextUtil.addLines(fDocument, selection.getOffset(), selection.getLength(), lines); |
| fLines = lines.toArray(new String[lines.size()]); |
| |
| final int start = selection.getOffset(); |
| int end = start + selection.getLength(); |
| final char c = fDocument.getChar(end-1); |
| if (c == '\n') { |
| end--; |
| if (end > 0 && fDocument.getChar(end-1) == '\r') { |
| end--; |
| } |
| } |
| else if (c == '\r') { |
| end--; |
| } |
| fPosition = new Position(start, end - start); |
| } |
| else { |
| final IRegion line = fDocument.getLineInformationOfOffset(selection.getOffset()); |
| fLines = new String[] { fDocument.get(line.getOffset(), line.getLength()) }; |
| fPosition = new Position(line.getOffset(), line.getLength()); |
| } |
| fDocument.addPosition(fPosition); |
| return true; |
| } |
| catch (final BadLocationException e) { |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1, "An error occurred preparing Run and Paste Output", e)); //$NON-NLS-1$ |
| return false; |
| } |
| } |
| |
| public void dispose() { |
| if (fDocument != null && fPosition != null) { |
| fDocument.removePosition(fPosition); |
| } |
| fEditor = null; |
| fDocument = null; |
| fPosition = null; |
| fOutput = null; |
| fLines = null; |
| } |
| |
| @Override |
| public String getTypeId() { |
| return "editor/run-and-paste"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getLabel() { |
| return RLaunchingMessages.SubmitCodeAndPasteOutput_RTask_label; |
| } |
| |
| @Override |
| public SubmitType getSubmitType() { |
| return SubmitType.EDITOR; |
| } |
| |
| @Override |
| public boolean canRunIn(final Tool tool) { |
| return (tool.isProvidingFeatureSet(RConsoleTool.R_BASIC_FEATURESET_ID)); |
| } |
| |
| @Override |
| public boolean changed(final int event, final Tool process) { |
| switch (event) { |
| case REMOVING_FROM: |
| case BEING_ABANDONED: |
| UIAccess.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| dispose(); |
| } |
| }); |
| break; |
| } |
| return true; |
| } |
| |
| @Override |
| public void run(final ToolService service, |
| final ProgressMonitor m) throws StatusException { |
| final IRBasicAdapter r = (IRBasicAdapter) service; |
| fOutput = new StringBuilder(200); |
| final IStreamListener listener = new IStreamListener() { |
| @Override |
| public void streamAppended(final String text, final IStreamMonitor monitor) { |
| fOutput.append(text); |
| } |
| }; |
| final ToolController controller = r.getController(); |
| r.briefAboutToChange(); |
| try { |
| controller.getStreams().getOutputStreamMonitor().addListener(listener); |
| controller.getStreams().getErrorStreamMonitor().addListener(listener); |
| for (int i = 0; i < fLines.length; i++) { |
| if (m.isCanceled()) { |
| return; |
| } |
| m.beginSubTask(fLines[i]); |
| r.submitToConsole(fLines[i], m); |
| } |
| if (r instanceof IRequireSynch) { |
| final Pattern pattern = ((IRequireSynch) r).synch(m); |
| if (pattern != null) { |
| final Matcher matcher = pattern.matcher(fOutput); |
| int idx = -1; |
| while (matcher.find()) { |
| idx = matcher.start(); |
| } |
| if (idx >= 0) { |
| fOutput.delete(idx, fOutput.length()); |
| } |
| } |
| } |
| } |
| finally { |
| r.briefChanged(RWorkspace.REFRESH_AUTO); |
| controller.getStreams().getOutputStreamMonitor().removeListener(listener); |
| controller.getStreams().getErrorStreamMonitor().removeListener(listener); |
| UIAccess.getDisplay().asyncExec(this); |
| } |
| } |
| |
| protected IRCoreAccess getRCoreAccess() { |
| final ISourceEditor editor= fEditor; |
| return (editor instanceof IRSourceEditor) ? |
| ((IRSourceEditor) editor).getRCoreAccess() : |
| RCore.WORKBENCH_ACCESS; |
| } |
| |
| @Override |
| public void run() { |
| // After R in display |
| final SourceViewer viewer = fEditor.getViewer(); |
| if (!UIAccess.isOkToUse(viewer) |
| || (viewer.getDocument() != fDocument) |
| || fPosition.isDeleted()) { |
| return; |
| } |
| |
| IWorkbenchSiteProgressService progressService = null; |
| final ISourceEditor editor = fEditor.getAdapter(ISourceEditor.class); |
| if (editor != null) { |
| final IServiceLocator serviceLocator = editor.getServiceLocator(); |
| if (serviceLocator != null) { |
| progressService = serviceLocator.getService(IWorkbenchSiteProgressService.class); |
| } |
| } |
| if (progressService != null) { |
| progressService.incrementBusy(); |
| } |
| |
| try { |
| final IndentUtil util= new IndentUtil(fDocument, getRCoreAccess().getRCodeStyle()); |
| |
| final int indent = util.getMultilineIndentColumn(fDocument.getLineOfOffset(fPosition.getOffset()), |
| fDocument.getLineOfOffset(fPosition.getOffset() + fPosition.getLength())); |
| final String delimiter = TextUtilities.getDefaultLineDelimiter(fDocument); |
| final String prefix = delimiter + util.createIndentString(indent) + "# "; //$NON-NLS-1$ |
| |
| final String[] lines = RUtil.LINE_SEPARATOR_PATTERN.split(fOutput); |
| final int size = fOutput.length() + lines.length*(prefix.length()-1) + 2; |
| fOutput.setLength(0); |
| fOutput.ensureCapacity(size); |
| for (int i = 0; i < lines.length; i++) { |
| fOutput.append(prefix); |
| fOutput.append(lines[i]); |
| } |
| fOutput.append(delimiter); |
| final int pos = fPosition.getOffset()+fPosition.getLength(); |
| fDocument.replace(pos, 0, fOutput.toString()); |
| viewer.revealRange(pos, fOutput.length()); |
| if (progressService != null) { |
| progressService.warnOfContentChange(); |
| } |
| } |
| catch (final BadLocationException e) { |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1, |
| RLaunchingMessages.SubmitCodeAndPasteOutput_error_WhenPasting_message, e), |
| StatusManager.LOG | StatusManager.SHOW); |
| } |
| finally { |
| dispose(); |
| if (progressService != null) { |
| progressService.decrementBusy(); |
| } |
| } |
| |
| } |
| } |
| |
| |
| public SubmitSelectionAndPasteOutputHandler() { |
| } |
| |
| |
| @Override |
| public Object execute(final ExecutionEvent event) throws ExecutionException { |
| final IWorkbenchPart workbenchPart = HandlerUtil.getActivePart(event); |
| final ISourceEditor editor = workbenchPart.getAdapter(ISourceEditor.class); |
| if (editor != null) { |
| if (!editor.isEditable(true)) { |
| cancel(null, new Status(IStatus.ERROR, RUI.BUNDLE_ID, |
| RLaunchingMessages.SubmitCodeAndPasteOutput_info_WriteProtected_status), event); |
| return null; |
| } |
| final SourceViewer viewer = editor.getViewer(); |
| final ITextSelection selection = (ITextSelection) viewer.getSelection(); |
| final R r = new R(editor); |
| if (!r.setupSource(selection)) { |
| cancel(r, new Status(IStatus.ERROR, RUI.BUNDLE_ID, |
| RLaunchingMessages.SubmitCodeAndPasteOutput_error_Unspecific_status), null); |
| return null; |
| } |
| final Tool tool= NicoUI.getToolRegistry().getActiveToolSession(UIAccess.getActiveWorkbenchPage(true)) |
| .getTool(); |
| try { |
| NicoUITools.accessTool(RConsoleTool.TYPE, tool); |
| } |
| catch (final CoreException e) { |
| cancel(r, e.getStatus(), event); |
| return null; |
| } |
| final org.eclipse.statet.jcommons.status.Status status= tool.getQueue().add(r); |
| if (status.getSeverity() >= Status.ERROR) { |
| cancel(r, StatusUtils.convert(status), event); |
| } |
| return null; |
| } |
| |
| LaunchShortcutUtil.handleUnsupportedExecution(event); |
| return null; |
| } |
| |
| private void cancel(final R r, final IStatus status, final ExecutionEvent executionEvent) { |
| if (r != null) { |
| r.dispose(); |
| } |
| LTKWorkbenchUIUtil.indicateStatus(status, executionEvent); |
| } |
| |
| } |