Skip to content

Commit c805888

Browse files
committed
Add Support for Expression Pinning
This commit add a new option to pin an expression with current active context, making the expression always be evaluating with the pinned context
1 parent 7c90789 commit c805888

File tree

10 files changed

+272
-9
lines changed

10 files changed

+272
-9
lines changed

debug/org.eclipse.debug.core/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %pluginName
44
Bundle-SymbolicName: org.eclipse.debug.core; singleton:=true
5-
Bundle-Version: 3.23.200.qualifier
5+
Bundle-Version: 3.24.0.qualifier
66
Bundle-Activator: org.eclipse.debug.core.DebugPlugin
77
Bundle-Vendor: %providerName
88
Bundle-Localization: plugin

debug/org.eclipse.debug.core/core/org/eclipse/debug/core/model/IWatchExpression.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2008 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -94,4 +94,31 @@ public interface IWatchExpression extends IErrorReportingExpression {
9494
*/
9595
void setEnabled(boolean enabled);
9696

97+
/**
98+
* Sets a user preferred context for expression evaluation
99+
*
100+
* @param context
101+
* @since 3.24
102+
*/
103+
public default void setPinnedContext(IDebugElement context) {
104+
}
105+
106+
/**
107+
* Returns the pinned context for the given expression
108+
*
109+
* @return returns pinned <code>IDebugElement</code>
110+
* @since 3.24
111+
*/
112+
public default IDebugElement getPinnedContext() {
113+
return null;
114+
}
115+
116+
/**
117+
* Removes attached custom context
118+
*
119+
* @since 3.24
120+
*/
121+
public default void removePinnedContext() {
122+
}
123+
97124
}

debug/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2015 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -21,6 +21,7 @@
2121
import org.eclipse.debug.core.ILaunchConfiguration;
2222
import org.eclipse.debug.core.model.IDebugElement;
2323
import org.eclipse.debug.core.model.IDebugTarget;
24+
import org.eclipse.debug.core.model.IStackFrame;
2425
import org.eclipse.debug.core.model.IValue;
2526
import org.eclipse.debug.core.model.IWatchExpression;
2627
import org.eclipse.debug.core.model.IWatchExpressionDelegate;
@@ -39,6 +40,7 @@ public class WatchExpression implements IWatchExpression {
3940
protected IDebugElement fCurrentContext;
4041
private boolean fEnabled= true;
4142
private boolean fPending= false;
43+
private IDebugElement fPinnedContext;
4244

4345
/**
4446
* Creates a new watch expression with the given expression
@@ -301,4 +303,31 @@ public String[] getErrorMessages() {
301303
return fResult.getErrorMessages();
302304
}
303305

306+
/**
307+
* @see org.eclipse.debug.core.model.IWatchExpression#setPinnedContext(IDebugElement)
308+
*/
309+
@Override
310+
public void setPinnedContext(IDebugElement context) {
311+
fPinnedContext = context;
312+
}
313+
314+
/**
315+
* @see org.eclipse.debug.core.model.IWatchExpression#getPinnedContext()
316+
*/
317+
@Override
318+
public IDebugElement getPinnedContext() {
319+
if (fPinnedContext instanceof IStackFrame) {
320+
return fPinnedContext;
321+
}
322+
return null;
323+
}
324+
325+
/**
326+
* @see org.eclipse.debug.core.model.IWatchExpression#removePinnedContext()
327+
*/
328+
@Override
329+
public void removePinnedContext() {
330+
fPinnedContext = null;
331+
}
332+
304333
}

debug/org.eclipse.debug.ui/plugin.properties

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,4 +421,8 @@ prototype.decorator.label = Prototype Decorator
421421
breakpointLabel.label= Label
422422
breakpointLabel.tooltip= Provide a custom label to quickly identify breakpoint
423423
breakpointLabelCommand = EditBreakpointLabel
424-
breakpointLabelCommand.description = Opens inline editor to change breakpoint label
424+
breakpointLabelCommand.description = Opens inline editor to change breakpoint label
425+
426+
427+
PinExpressionWithCurrentAction.label=Pin Evaluation Context
428+
PinExpressionWithCurrentAction.tooltip=Pin current evaluation stackframe for this expression

debug/org.eclipse.debug.ui/plugin.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,19 @@
13051305
enablesFor="1"
13061306
id="org.eclipse.debug.ui.watchExpressionActions.EditWatchExpression">
13071307
</action>
1308+
</viewerContribution>
1309+
<viewerContribution
1310+
targetID="org.eclipse.debug.ui.ExpressionView"
1311+
id="org.eclipse.debug.ui.WatchExpressionActions22">
1312+
<action
1313+
label="%PinExpressionWithCurrentAction.label"
1314+
helpContextId="pin_current_evaluation_context"
1315+
class="org.eclipse.debug.internal.ui.actions.expressions.PinWatchContextAction"
1316+
tooltip="%PinExpressionWithCurrentAction.tooltip"
1317+
menubarPath="additions"
1318+
enablesFor="1"
1319+
id="org.eclipse.debug.ui.watchExpressionActions.PinCurrentContext">
1320+
</action>
13081321
</viewerContribution>
13091322
<viewerContribution
13101323
targetID="org.eclipse.debug.ui.ExpressionView"

debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,5 +254,7 @@ public class ActionMessages extends NLS {
254254
public static String EnableAllBreakpointsAction_3;
255255
public static String BreakpointLabelDialog;
256256
public static String RemoveFromFavoritesAction;
257+
public static String ExpressionsPinContext;
258+
public static String ExpressionsRemovePin;
257259

258260
}

debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ActionMessages.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,7 @@ VirtualFindAction_1=Unable to locate {0} in viewer
237237

238238
ToggleBreakpointsTargetManager_defaultToggleTarget_name = Default
239239
ToggleBreakpointsTargetManager_defaultToggleTarget_description = Default
240-
BreakpointLabelDialog=Provide a custom label, or blank for the default label
240+
BreakpointLabelDialog=Provide a custom label, or blank for the default label
241+
242+
ExpressionsRemovePin=Remove Pinned Context
243+
ExpressionsPinContext=Pin Evaluation Context
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 IBM Corporation.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* IBM Corporation - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.debug.internal.ui.actions.expressions;
15+
16+
import java.util.Iterator;
17+
18+
import org.eclipse.core.runtime.IAdaptable;
19+
import org.eclipse.debug.core.DebugEvent;
20+
import org.eclipse.debug.core.DebugPlugin;
21+
import org.eclipse.debug.core.IDebugEventSetListener;
22+
import org.eclipse.debug.core.ILaunch;
23+
import org.eclipse.debug.core.model.IDebugElement;
24+
import org.eclipse.debug.core.model.IExpression;
25+
import org.eclipse.debug.core.model.IStackFrame;
26+
import org.eclipse.debug.core.model.IThread;
27+
import org.eclipse.debug.core.model.IWatchExpression;
28+
import org.eclipse.debug.internal.ui.DebugUIPlugin;
29+
import org.eclipse.debug.internal.ui.actions.ActionMessages;
30+
import org.eclipse.debug.ui.DebugUITools;
31+
import org.eclipse.jface.action.IAction;
32+
import org.eclipse.jface.viewers.ISelection;
33+
import org.eclipse.jface.viewers.IStructuredSelection;
34+
import org.eclipse.swt.widgets.Event;
35+
import org.eclipse.ui.IActionDelegate2;
36+
import org.eclipse.ui.IViewActionDelegate;
37+
import org.eclipse.ui.IViewPart;
38+
import org.eclipse.ui.IWorkbenchPage;
39+
40+
/*
41+
* Associates the current stack frame with this expression, allowing its evaluation
42+
* result to remain accessible across different debug contexts.
43+
*/
44+
public class PinWatchContextAction implements IViewActionDelegate, IDebugEventSetListener, IActionDelegate2 {
45+
46+
/**
47+
* Finds the currently selected context in the UI.
48+
*
49+
* @return the current debug context
50+
*/
51+
protected IDebugElement getContext() {
52+
IAdaptable object = DebugUITools.getDebugContext();
53+
IDebugElement context = null;
54+
if (object instanceof IDebugElement iDebugElement) {
55+
context = iDebugElement;
56+
} else if (object instanceof ILaunch iLaunch) {
57+
context = iLaunch.getDebugTarget();
58+
}
59+
return context;
60+
}
61+
62+
protected IStructuredSelection getCurrentSelection() {
63+
IWorkbenchPage page = DebugUIPlugin.getActiveWorkbenchWindow().getActivePage();
64+
if (page != null) {
65+
ISelection selection = page.getSelection();
66+
if (selection instanceof IStructuredSelection sel) {
67+
return sel;
68+
}
69+
}
70+
return null;
71+
}
72+
73+
/**
74+
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
75+
*/
76+
@Override
77+
public void run(IAction action) {
78+
IDebugElement context = getContext();
79+
for (Iterator<?> iter = getCurrentSelection().iterator(); iter.hasNext();) {
80+
if (iter.next() instanceof IWatchExpression expression) {
81+
if (expression.getPinnedContext() != null) {
82+
expression.removePinnedContext();
83+
expression.setExpressionContext(context);
84+
action.setText(ActionMessages.ExpressionsPinContext);
85+
} else {
86+
expression.setPinnedContext(context);
87+
action.setText(ActionMessages.ExpressionsRemovePin);
88+
}
89+
if (expression.isEnabled()) {
90+
expression.evaluate();
91+
}
92+
}
93+
}
94+
}
95+
96+
@Override
97+
public void selectionChanged(IAction action, ISelection selection) {
98+
99+
IDebugElement debugElement = getContext();
100+
if (debugElement == null) {
101+
action.setEnabled(false);
102+
return;
103+
} else {
104+
action.setEnabled(true);
105+
}
106+
if (getCurrentSelection() == null) {
107+
return;
108+
}
109+
for (Object select : getCurrentSelection()) {
110+
if (select instanceof IWatchExpression expression) {
111+
if (expression.getPinnedContext() != null) {
112+
action.setText(ActionMessages.ExpressionsRemovePin);
113+
} else {
114+
action.setText(ActionMessages.ExpressionsPinContext);
115+
}
116+
} else {
117+
action.setEnabled(false);
118+
}
119+
120+
}
121+
122+
}
123+
124+
@Override
125+
public void handleDebugEvents(DebugEvent[] events) {
126+
for (DebugEvent event : events) {
127+
if (event.getSource() instanceof IThread thread && thread.isTerminated()) {
128+
for (IExpression exp : DebugPlugin.getDefault().getExpressionManager().getExpressions()) {
129+
if (exp instanceof IWatchExpression expression && expression.getPinnedContext() != null) {
130+
if (expression.getPinnedContext() instanceof IStackFrame frame && frame.isTerminated()) {
131+
expression.removePinnedContext();
132+
expression.setExpressionContext(getContext());
133+
}
134+
}
135+
}
136+
137+
}
138+
}
139+
140+
}
141+
142+
@Override
143+
public void init(IViewPart view) {
144+
DebugPlugin.getDefault().addDebugEventListener(this);
145+
146+
}
147+
148+
@Override
149+
public void init(IAction action) {
150+
}
151+
152+
@Override
153+
public void dispose() {
154+
DebugPlugin.getDefault().removeDebugEventListener(this);
155+
}
156+
157+
@Override
158+
public void runWithEvent(IAction action, Event event) {
159+
run(action);
160+
}
161+
162+
}

debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionLabelProvider.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2006, 2014 IBM Corporation and others.
2+
* Copyright (c) 2006, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -23,7 +23,10 @@
2323
import org.eclipse.debug.internal.ui.DebugUIMessages;
2424
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
2525
import org.eclipse.debug.ui.IDebugUIConstants;
26+
import org.eclipse.jface.resource.JFaceResources;
2627
import org.eclipse.jface.viewers.TreePath;
28+
import org.eclipse.swt.SWT;
29+
import org.eclipse.swt.graphics.FontData;
2730
import org.eclipse.swt.graphics.RGB;
2831

2932
/**
@@ -163,4 +166,19 @@ protected String getExpressionValueText(IExpression expression, IValue value, IP
163166
return null;
164167
}
165168

169+
@Override
170+
protected FontData getFontData(TreePath elementPath, IPresentationContext presentationContext, String columnId)
171+
throws CoreException {
172+
Object element = elementPath.getLastSegment();
173+
if (element instanceof IWatchExpression watchExp) {
174+
if (watchExp.getPinnedContext() != null) {
175+
var fontNew = JFaceResources.getFontDescriptor(IDebugUIConstants.PREF_VARIABLE_TEXT_FONT)
176+
.getFontData()[0];
177+
return new FontData(fontNew.getName(), fontNew.getHeight(),
178+
fontNew.getStyle() ^ (SWT.BOLD | SWT.ITALIC));
179+
}
180+
}
181+
return JFaceResources.getFontDescriptor(IDebugUIConstants.PREF_VARIABLE_TEXT_FONT).getFontData()[0];
182+
}
183+
166184
}

debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultWatchExpressionModelProxy.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2011 IBM Corporation and others.
2+
* Copyright (c) 2005, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -113,8 +113,13 @@ protected void contextActivated(ISelection selection) {
113113
}
114114
}
115115
IWatchExpression expression = (IWatchExpression)getExpression();
116-
if (expression != null){
117-
expression.setExpressionContext(context);
116+
if (expression != null) {
117+
IDebugElement pinnedContext = expression.getPinnedContext();
118+
if (pinnedContext != null) {
119+
expression.setExpressionContext(pinnedContext);
120+
} else {
121+
expression.setExpressionContext(context);
122+
}
118123
}
119124
}
120125
}

0 commit comments

Comments
 (0)