blob: a5742aceda10e976d1c686ac7f0996cc8b6d5d12 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2021 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.actions;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.jface.text.AbstractDocument;
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.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
import org.eclipse.statet.internal.r.debug.ui.RDebugUIPlugin;
import org.eclipse.statet.internal.r.debug.ui.RDebugUIUtils;
import org.eclipse.statet.ltk.ast.core.AstInfo;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.ast.core.util.AstSelection;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.nico.core.runtime.ToolStatus;
import org.eclipse.statet.r.core.model.RElementAccess;
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.rlang.RTokens;
import org.eclipse.statet.r.core.rsource.ast.NodeType;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.core.source.RDocumentConstants;
import org.eclipse.statet.r.debug.core.IRStackFrame;
import org.eclipse.statet.r.nico.AbstractRDbgController;
public class StepIntoSelectionHandler extends AbstractHandler {
public static RElementAccess searchAccess(final ISourceEditor editor, final IRegion region) {
try {
final IDocument document = editor.getViewer().getDocument();
final ITypedRegion partition = TextUtilities.getPartition(document,
editor.getDocumentContentInfo().getPartitioning(), region.getOffset(), false );
final SourceUnit su = editor.getSourceUnit();
if (su instanceof RSourceUnit && region.getOffset() < document.getLength()
&& ( (RDocumentConstants.R_DEFAULT_CONTENT_CONSTRAINT.matches(partition.getType())
&& !RTokens.isRobustSeparator(document.getChar(region.getOffset()), false) )
|| partition.getType() == RDocumentConstants.R_QUOTED_SYMBOL_CONTENT_TYPE
|| partition.getType() == RDocumentConstants.R_STRING_CONTENT_TYPE )) {
final RSourceUnitModelInfo info = (RSourceUnitModelInfo) su.getModelInfo(RModel.R_TYPE_ID, RModelManager.MODEL_FILE, new NullProgressMonitor());
if (info != null) {
final AstInfo astInfo = info.getAst();
final AstSelection selection = AstSelection.search(astInfo.getRoot(),
region.getOffset(), region.getOffset()+region.getLength(),
AstSelection.MODE_COVERING_SAME_LAST );
final AstNode covering = selection.getCovering();
if (covering instanceof RAstNode) {
final RAstNode node = (RAstNode) covering;
RAstNode current = node;
do {
final ImList<Object> attachments= current.getAttachments();
for (final Object attachment : attachments) {
if (attachment instanceof RElementAccess) {
final RElementAccess access= (RElementAccess) attachment;
if (access.isFunctionAccess() && access.isCallAccess()) {
if (isChild(node, access.getNode())) {
return access;
}
return null;
}
}
}
current = current.getRParent();
} while (current != null);
}
}
}
}
catch (final BadLocationException e) {
}
return null;
}
private static boolean isChild(RAstNode child, final RAstNode parent) {
do {
if (child == parent) {
return true;
}
child = child.getRParent();
} while (child != null);
return false;
}
public static void exec(final AbstractRDbgController controller,
final AbstractDocument document, final RElementAccess access,
final IWorkbenchPart part) {
try {
final RAstNode nameNode = access.getNameNode();
String code;
switch (nameNode.getNodeType()) {
case STRING_CONST:
case SYMBOL:
code = access.getSegmentName();
if (access.getNode().getNodeType() == NodeType.F_CALL) {
code = "`" + code + "`"; //$NON-NLS-1$ //$NON-NLS-2$
}
else {
code = "get('" + code + "')"; //$NON-NLS-1$ //$NON-NLS-2$
}
break;
default:
code = document.get(nameNode.getStartOffset(), nameNode.getLength());
break;
}
if (code != null && code.length() > 0) {
final IRStackFrame frame = RDebugUIUtils.getFrame(part, controller.getTool());
controller.debugStepInto((frame != null) ? frame.getPosition() : -1, code);
}
}
catch (final BadLocationException e) {}
catch (final StatusException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RDebugUIPlugin.BUNDLE_ID,
DebugException.REQUEST_FAILED,
"An error occurred when executing debug request in the R engine.",
e ),
StatusManager.LOG );
}
}
public StepIntoSelectionHandler() {
}
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
final IWorkbenchPart activePart = WorkbenchUIUtils.getActivePart(event.getApplicationContext());
final ISourceEditor editor = activePart.getAdapter(ISourceEditor.class);
if (editor == null) {
return null;
}
final AbstractRDbgController controller = RDebugUIUtils.getRDbgController(editor);
if (controller == null || controller.getStatus() != ToolStatus.STARTED_SUSPENDED) {
return null;
}
final ITextSelection selection = (ITextSelection) editor.getViewer().getSelection();
final RElementAccess access = searchAccess(editor,
new Region(selection.getOffset(), selection.getLength()) );
if (access != null) {
exec(controller, (AbstractDocument) editor.getViewer().getDocument(), access, activePart);
return null;
}
Display.getCurrent().beep();
return null;
}
}