blob: 5e49ee9b0655c01302349636d2e60026332cf0d8 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2022 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;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IErrorReportingExpression;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.core.model.IWatchExpression;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugModelPresentationExtension;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.IValueDetailListener;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.statet.ecommons.debug.core.model.IndexedVariableItem;
import org.eclipse.statet.ecommons.debug.core.model.IndexedVariablePartition;
import org.eclipse.statet.ecommons.debug.core.model.VariableDim;
import org.eclipse.statet.ecommons.debug.ui.ECommonsDebugUIImageDescriptor;
import org.eclipse.statet.ecommons.debug.ui.ECommonsDebugUIResources;
import org.eclipse.statet.ecommons.ui.jface.resource.ImageDescriptorRegistry;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.debug.core.RDebugModel;
import org.eclipse.statet.r.debug.core.RDebugTarget;
import org.eclipse.statet.r.debug.core.RElementVariable;
import org.eclipse.statet.r.debug.core.RStackFrame;
import org.eclipse.statet.r.debug.core.RThread;
import org.eclipse.statet.r.debug.core.RValue;
import org.eclipse.statet.r.debug.core.RVariable;
import org.eclipse.statet.r.debug.core.breakpoints.RBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.RBreakpointStatus;
import org.eclipse.statet.r.debug.core.breakpoints.RExceptionBreakpointStatus;
import org.eclipse.statet.r.debug.core.breakpoints.RLineBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.RMethodBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.RMethodBreakpointStatus;
import org.eclipse.statet.r.debug.core.sourcelookup.RRuntimeSourceFragment;
import org.eclipse.statet.r.debug.core.sourcelookup.RSourceLookupMatch;
import org.eclipse.statet.r.ui.RLabelProvider;
import org.eclipse.statet.r.ui.RUI;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RReference;
public class RDebugModelPresentation extends LabelProvider
implements IDebugModelPresentation, IDebugModelPresentationExtension {
private static boolean gCheckLength = Platform.getWS().equals(Platform.WS_GTK);
private static boolean isResourcesInitilized= false;
private boolean isInitilized;
private ImageRegistry imageRegistry;
private ImageDescriptorRegistry imageDescriptorRegistry;
private final RLabelProvider rLabelProvider = new RLabelProvider();
private final Map<String, Object> attributes= new ConcurrentHashMap<>();
public RDebugModelPresentation() {
}
@Override
public void dispose() {
super.dispose();
this.rLabelProvider.dispose();
this.attributes.clear();
}
@Override
public void setAttribute(final String attribute, final Object value) {
if (attribute == null) {
return;
}
this.attributes.put(attribute, value);
}
protected final boolean isShowVariableTypeNames() {
final Boolean value= (Boolean) this.attributes.get(DISPLAY_VARIABLE_TYPE_NAMES);
return (value != null) ? value.booleanValue() : false;
}
@Override
public boolean requiresUIThread(final Object element) {
return (!RDebugModelPresentation.isResourcesInitilized);
}
private void init() {
final RDebugUIPlugin plugin= RDebugUIPlugin.getInstance();
this.imageRegistry= plugin.getImageRegistry();
this.imageDescriptorRegistry= plugin.getImageDescriptorRegistry();
this.isInitilized= true;
RDebugModelPresentation.isResourcesInitilized= true;
}
@Override
public Image getImage(final Object element) {
if (!this.isInitilized) {
init();
}
try {
if (element instanceof RBreakpoint) {
return getImage((RBreakpoint) element);
}
else if (element instanceof IErrorReportingExpression) {
if (element instanceof IWatchExpression) {
return null;
}
return this.imageRegistry.get(RDebugUIPlugin.IMG_OBJ_R_INSPECT_EXPRESSION);
}
else if (element instanceof RThread) {
return getImage((RThread) element);
}
else if (element instanceof IVariable) {
if (element instanceof RElementVariable) {
CombinedRElement rElement = ((RElementVariable) element).getElement();
if (rElement.getRObjectType() == RObject.TYPE_REFERENCE) {
final RObject resolved = ((RReference) rElement).getResolvedRObject();
if (resolved instanceof CombinedRElement) {
rElement = (CombinedRElement) resolved;
}
}
return this.rLabelProvider.getImage(rElement);
}
if (element instanceof IndexedVariablePartition) {
return ECommonsDebugUIResources.INSTANCE.getImage(
ECommonsDebugUIResources.OBJ_VARIABLE_PARTITION );
}
if (element instanceof IndexedVariableItem) {
return ECommonsDebugUIResources.INSTANCE.getImage(
ECommonsDebugUIResources.OBJ_VARIABLE_ITEM );
}
if (element instanceof VariableDim) {
return ECommonsDebugUIResources.INSTANCE.getImage(
ECommonsDebugUIResources.OBJ_VARIABLE_DIM );
}
}
}
catch (final CoreException e) {}
return null;
}
@Override
public String getText(final Object element) {
String text= null;
try {
if (element instanceof RDebugTarget) {
text= getText((RDebugTarget) element);
}
else if (element instanceof RBreakpoint) {
text= getText((RBreakpoint) element);
}
else if (element instanceof RThread) {
text= getText((RThread) element);
}
else if (element instanceof RStackFrame) {
text= getText((RStackFrame) element);
}
else if (element instanceof RVariable) {
text= getText((RVariable) element);
}
}
catch (final CoreException e) {}
if (gCheckLength && text != null && text.length() > 2000) {
return text.substring(0, 2000);
}
return text;
}
protected String getText(final RDebugTarget target) throws CoreException {
final StringBuilder sb = new StringBuilder();
if (target.isTerminated()) {
sb.append("<terminated> ");
}
else if (target.isDisconnected()) {
sb.append("<disconntected> ");
}
sb.append(target.getName());
if (target.isSuspended()) {
sb.append(" (Suspended)");
}
return sb.toString();
}
protected Image getImage(final RThread thread) throws CoreException {
if (thread.isSuspended() || thread.getDebugTarget().isSuspended()) {
return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED);
}
return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING);
}
protected String getText(final RThread thread) throws CoreException {
final StringBuilder sb = new StringBuilder(thread.getName());
if (thread.isSuspended()) {
final RBreakpointStatus status= getBreakpointStatus(thread);
if (status != null) {
int a = 0;
if (status instanceof RMethodBreakpointStatus) {
final RMethodBreakpointStatus mbStatus= (RMethodBreakpointStatus) status;
if (mbStatus.isEntry()) {
sb.append(" (Suspended at entry of ");
a= 1;
}
else if (mbStatus.isExit()) {
sb.append(" (Suspended at exit of ");
a= 1;
}
}
if (status instanceof RExceptionBreakpointStatus) {
sb.append(" (Suspended on ");
a= 1;
}
if (a == 0) {
sb.append(" (Suspended in ");
a = 1;
}
final String label = status.getLabel();
sb.append((label != null) ? label : "?");
sb.append(" - hit breakpoint");
sb.append(")");
}
else {
sb.append(" (Suspended)");
}
}
else if (thread.isStepping()) {
sb.append(" (Stepping)");
}
// else if (thread.isEvaluation()) {
// return name + " (Evaluating)";
// }
else {
sb.append(" (Running)");
}
return sb.toString();
}
private RBreakpointStatus getBreakpointStatus(final RThread thread) {
final IStackFrame frame = thread.getTopStackFrame();
if (frame != null) {
return frame.getAdapter(RBreakpointStatus.class);
}
return null;
}
protected String getText(final RStackFrame frame) throws CoreException {
final StringBuilder sb = new StringBuilder(32);
if (frame.getPosition() >= 0) {
sb.append(frame.getPosition());
sb.append(": ");
}
sb.append(frame.getName());
final String fileName = frame.getInfoFileName();
if (fileName != null) {
sb.append(" · ");
sb.append(fileName);
final int number = frame.getInfoLineNumber();
if (number >= 0) {
sb.append("#");
sb.append(number);
}
}
return sb.toString();
}
protected Image getImage(final RBreakpoint breakpoint) throws CoreException {
final int flags= computeBreakpointAdornmentFlags(breakpoint);
final String imageKey;
if (breakpoint.getBreakpointType() == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
imageKey= ((flags & ECommonsDebugUIImageDescriptor.ENABLED) != 0) ?
RDebugUIPlugin.IMG_OBJ_R_EXCEPTION_BREAKPOINT :
RDebugUIPlugin.IMG_OBJ_R_EXCEPTION_BREAKPOINT_DISABLED;
}
else if ((flags & ECommonsDebugUIImageDescriptor.SCRIPT) != 0) {
imageKey= ((flags & ECommonsDebugUIImageDescriptor.ENABLED) != 0) ?
RDebugUIPlugin.IMG_OBJ_R_TOPLEVEL_BREAKPOINT :
RDebugUIPlugin.IMG_OBJ_R_TOPLEVEL_BREAKPOINT_DISABLED;
}
else {
imageKey = ((flags & ECommonsDebugUIImageDescriptor.ENABLED) != 0) ?
RDebugUIPlugin.IMG_OBJ_R_BREAKPOINT :
RDebugUIPlugin.IMG_OBJ_R_BREAKPOINT_DISABLED;
}
final ImageDescriptor descriptor = new ECommonsDebugUIImageDescriptor(
RDebugUIPlugin.getInstance().getImageRegistry().getDescriptor(imageKey), flags );
return this.imageDescriptorRegistry.get(descriptor);
}
protected String getText(final RBreakpoint breakpoint) throws CoreException {
final StringBuilder text = new StringBuilder();
if (breakpoint.getBreakpointType() == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
return "R errors/stops";
}
if (breakpoint instanceof RLineBreakpoint) {
final RLineBreakpoint lineBreakpoint = (RLineBreakpoint) breakpoint;
final IResource resource = breakpoint.getMarker().getResource();
if (resource != null) {
text.append(resource.getName());
}
try {
final int lineNumber = lineBreakpoint.getLineNumber();
text.append(" ["); //$NON-NLS-1$
text.append(NLS.bind(Messages.Breakpoint_Line_label, Integer.toString(lineNumber)));
text.append(']');
}
catch (final CoreException e) {}
text.append(" - "); //$NON-NLS-1$
final String subLabel = lineBreakpoint.getSubLabel();
if (subLabel != null) {
text.append(subLabel);
text.append(' ');
text.append(Messages.Breakpoint_SubLabel_copula);
text.append(' ');
}
switch (lineBreakpoint.getElementType()) {
case RLineBreakpoint.R_COMMON_FUNCTION_ELEMENT_TYPE:
text.append(Messages.Breakpoint_Function_prefix);
text.append(' ');
break;
case RLineBreakpoint.R_S4_METHOD_ELEMENT_TYPE:
text.append(Messages.Breakpoint_S4Method_prefix);
text.append(' ');
break;
case RLineBreakpoint.R_TOPLEVEL_COMMAND_ELEMENT_TYPE:
text.append(Messages.Breakpoint_ScriptLine_prefix);
text.append(' ');
}
final String elementLabel = lineBreakpoint.getElementLabel();
if (elementLabel != null) {
text.append(elementLabel);
}
else {
text.append("?"); //$NON-NLS-1$
}
}
else {
return null;
}
return text.toString();
}
/**
* Returns the adornment flags for the given breakpoint.
* These flags are used to render appropriate overlay icons for the breakpoint.
*/
private int computeBreakpointAdornmentFlags(final RBreakpoint breakpoint) {
int flags = 0;
try {
if (breakpoint.isEnabled()) {
flags |= ECommonsDebugUIImageDescriptor.ENABLED;
}
if (breakpoint.isInstalled()) {
flags |= ECommonsDebugUIImageDescriptor.INSTALLED;
}
if (breakpoint.getBreakpointType() == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID) {
final RLineBreakpoint lineBreakpoint = (RLineBreakpoint) breakpoint;
if (lineBreakpoint.getElementType() == RLineBreakpoint.R_TOPLEVEL_COMMAND_ELEMENT_TYPE) {
flags |= ECommonsDebugUIImageDescriptor.SCRIPT;
}
else if (lineBreakpoint.isConditionEnabled()) {
flags |= ECommonsDebugUIImageDescriptor.CONDITIONAL;
}
}
else if (breakpoint.getBreakpointType() == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
final RMethodBreakpoint methodBreakpoint = (RMethodBreakpoint) breakpoint;
if (methodBreakpoint.isConditionEnabled()) {
flags |= ECommonsDebugUIImageDescriptor.CONDITIONAL;
}
if (methodBreakpoint.isEntry()) {
flags |= ECommonsDebugUIImageDescriptor.ENTRY;
}
if (methodBreakpoint.isExit()) {
flags |= ECommonsDebugUIImageDescriptor.EXIT;
}
}
else if (breakpoint.getBreakpointType() == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
}
}
catch (final CoreException e) {
}
return flags;
}
protected String getText(final RVariable variable) {
final StringBuilder text= new StringBuilder();
if (isShowVariableTypeNames()) {
try {
text.append(variable.getReferenceTypeName());
}
catch (final DebugException e) {
text.append("<unknown type>"); //$NON-NLS-1$
}
text.append(' ');
}
text.append(variable.getName());
String valueString= null;
try {
valueString= variable.getValue().getValueString();
}
catch (final DebugException e) {
valueString= "<unknown>"; //$NON-NLS-1$
}
if (!valueString.isEmpty()) {
text.append("= "); //$NON-NLS-1$
text.append(valueString);
}
return text.toString();
}
@Override
public IEditorInput getEditorInput(Object element) {
if (element instanceof IMarker) {
element = DebugPlugin.getDefault().getBreakpointManager().getBreakpoint((IMarker) element);
}
if (element instanceof RBreakpoint) {
element = ((RBreakpoint) element).getMarker().getResource();
}
if (element instanceof RSourceLookupMatch) {
element = ((RSourceLookupMatch) element).getElement();
}
if (element instanceof IFile) {
return new FileEditorInput((IFile) element);
}
if (element instanceof IFileStore) {
return new FileStoreEditorInput((IFileStore) element);
}
if (element instanceof RRuntimeSourceFragment) {
return new RRuntimeSourceEditorInput((RRuntimeSourceFragment) element);
}
return null;
}
@Override
public String getEditorId(final IEditorInput input, final Object element) {
try {
if (input instanceof IFileEditorInput) {
return IDE.getEditorDescriptor(((IFileEditorInput) input).getFile(), true, false)
.getId();
}
else if (input instanceof RRuntimeSourceEditorInput) {
return RUI.R_EDITOR_ID;
}
else {
return IDE.getEditorDescriptor(input.getName(), true, false).getId();
}
}
catch (final PartInitException e) {}
return null;
}
@Override
public void computeDetail(final IValue value, final IValueDetailListener listener) {
if (value instanceof RValue) {
listener.detailComputed(value, ((RValue) value).getDetailString());
return;
}
listener.detailComputed(value, null);
}
}