| /*=============================================================================# |
| # 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; |
| } |
| |
| } |