1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-22 22:22:11 +02:00

Bug 337881: [Memory Browser] Persist the Go To Address history in the launch configuration. (Changes resulting from code review.)

This commit is contained in:
John Cortell 2011-03-04 16:45:30 +00:00
parent 38f65aca63
commit d71b3a2ce7
4 changed files with 192 additions and 101 deletions

View file

@ -27,7 +27,7 @@ public class ClearExpressionsListAction implements IViewActionDelegate {
public void run(IAction action) { public void run(IAction action) {
if ( fView instanceof MemoryBrowser ) { if ( fView instanceof MemoryBrowser ) {
MemoryBrowser browser = (MemoryBrowser) fView; MemoryBrowser browser = (MemoryBrowser) fView;
browser.clearExpressionsFromList(null); browser.clearExpressionHistoryForActiveTab();
} }
} }

View file

@ -14,10 +14,9 @@ package org.eclipse.cdt.debug.ui.memory.memorybrowser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.eclipse.cdt.debug.core.model.provisional.ITargetLabelProvider; import org.eclipse.cdt.debug.core.model.provisional.IRecurringDebugContext;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
@ -43,8 +42,19 @@ import org.eclipse.ui.PlatformUI;
public class GoToAddressBarWidget { public class GoToAddressBarWidget {
private static String SEPARATOR = "<sperator>"; /**
private static String UNKNOWN_TARGET_NAME = "Unknown"; * Character sequence that is unlikely to appear naturally in a recurring
* debug context ID or memory space ID
*/
private static String SEPARATOR = "<seperator>";
/**
* At a minimum, the expression history is kept on a per launch
* configuration basis. Where debug contexts (processes, in practice) can
* provide a recurring ID, we further divide the history by those IDs. This
* constant is used when no recurring context ID is available.
*/
private static String UNKNOWN_CONTEXT_ID = "Unknown";
private Combo fExpression; private Combo fExpression;
private ControlDecoration fEmptyExpression; private ControlDecoration fEmptyExpression;
private ControlDecoration fWrongExpression; private ControlDecoration fWrongExpression;
@ -87,10 +97,12 @@ public class GoToAddressBarWidget {
return fComposite; return fComposite;
} }
/** The launch configuration attribute prefix used to persist expression history */
private final static String SAVED_EXPRESSIONS = "saved_expressions"; //$NON-NLS-1$ private final static String SAVED_EXPRESSIONS = "saved_expressions"; //$NON-NLS-1$
private final static int MAX_SAVED_EXPRESSIONS = 15 ; private final static int MAX_SAVED_EXPRESSIONS = 15 ;
private void saveExpression( String memorySpace, Object context, String expr ) { private void addExpressionToHistoryPersistence( Object context, String expr, String memorySpace ) {
/* /*
* Get the saved expressions if any. * Get the saved expressions if any.
* *
@ -104,7 +116,7 @@ public class GoToAddressBarWidget {
return; return;
} }
String targetName = getTargetName(context); String contextID = getRecurringContextID(context);
ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();
String currentExpressions = ""; String currentExpressions = "";
@ -112,7 +124,7 @@ public class GoToAddressBarWidget {
try { try {
ILaunchConfigurationWorkingCopy wc = launchConfiguration.getWorkingCopy(); ILaunchConfigurationWorkingCopy wc = launchConfiguration.getWorkingCopy();
if (wc != null) { if (wc != null) {
currentExpressions = wc.getAttribute(getSaveExpressionKey(targetName,memorySpace), ""); currentExpressions = wc.getAttribute(getSaveExpressionKey(contextID,memorySpace), "");
StringTokenizer st = new StringTokenizer(currentExpressions, ","); //$NON-NLS-1$ StringTokenizer st = new StringTokenizer(currentExpressions, ","); //$NON-NLS-1$
/* /*
@ -141,7 +153,7 @@ public class GoToAddressBarWidget {
} }
currentExpressions += list.get(idx); currentExpressions += list.get(idx);
} }
wc.setAttribute(getSaveExpressionKey(targetName,memorySpace), currentExpressions); wc.setAttribute(getSaveExpressionKey(contextID,memorySpace), currentExpressions);
wc.doSave(); wc.doSave();
} }
} }
@ -151,47 +163,67 @@ public class GoToAddressBarWidget {
} }
} }
public void deleteExpressions(Object context) { /**
* Clear all expression history persisted in the launch configuration that
if(context == null) * created the given debug context
{ *
* @param context
* the debug context. In practice, this will always be a process
* context
*/
public void clearExpressionHistoryPersistence(Object context) {
if(context == null) {
return; return;
} }
ILaunch launch = getLaunch(context); ILaunch launch = getLaunch(context);
if(launch == null) if(launch == null) {
{
return; return;
} }
// We maintain history for every process this launch configuration has
// launched. And where memory spaces are involved, each space has its
// own history. Here we just wipe out the persistence of all processes
// and memory spaces stored in the launch configuration that created the
// given processes.
ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();
if (launchConfiguration != null) { if (launchConfiguration != null) {
try { try {
ILaunchConfigurationWorkingCopy wc = launchConfiguration.getWorkingCopy(); ILaunchConfigurationWorkingCopy wc = launchConfiguration.getWorkingCopy();
if (wc != null) { if (wc != null) {
@SuppressWarnings("unchecked") Map<?,?> attributes = wc.getAttributes();
Map<String,Object> attributes = (Map<String,Object>)wc.getAttributes(); Iterator<?> iterator = attributes.keySet().iterator();
if (attributes != null && !attributes.isEmpty()) { while (iterator.hasNext()) {
String key = (String)iterator.next();
Iterator<String> iterator = attributes.keySet().iterator(); if (key.startsWith(SAVED_EXPRESSIONS)) {
while(iterator.hasNext()) wc.removeAttribute(key);
{
String key = iterator.next();
if(key.startsWith(SAVED_EXPRESSIONS))
{
wc.removeAttribute(key);
}
} }
wc.doSave(); }
} wc.doSave();
} }
} }
catch(CoreException e) { catch(CoreException e) {
// Some unexpected snag working with the launch configuration
MemoryBrowserPlugin.log(e);
} }
} }
} }
private String[] getSavedExpressions(String memorySpace, Object context) { /**
* Get the expression history persisted in the launch configuration for the
* given debug context and memory space (where applicable)
*
* @param context
* the debug context. In practice, this will always be a process
* context
* @param memorySpace
* memory space ID or null if not applicable
* @return a list of expressions, or empty collection if no history
* available (never null)
* @throws CoreException
* if there's a problem working with the launch configuration
*/
private String[] getSavedExpressions(Object context, String memorySpace) throws CoreException {
/* /*
* Get the saved expressions if any. * Get the saved expressions if any.
* *
@ -201,19 +233,14 @@ public class GoToAddressBarWidget {
*/ */
ILaunch launch = getLaunch(context); ILaunch launch = getLaunch(context);
if(launch == null) if(launch == null) {
{
return new String[0]; return new String[0];
} }
ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();
String expressions = ""; String expressions = "";
if (launchConfiguration != null) { if (launchConfiguration != null) {
try { expressions = launchConfiguration.getAttribute(getSaveExpressionKey(getRecurringContextID(context),memorySpace), "");
expressions = launchConfiguration.getAttribute(getSaveExpressionKey(getTargetName(context),memorySpace), "");
}
catch(CoreException e) {
}
} }
StringTokenizer st = new StringTokenizer(expressions, ","); //$NON-NLS-1$ StringTokenizer st = new StringTokenizer(expressions, ","); //$NON-NLS-1$
@ -221,30 +248,43 @@ public class GoToAddressBarWidget {
* Parse through the list creating an ordered array for display. * Parse through the list creating an ordered array for display.
*/ */
ArrayList<String> list = new ArrayList<String>(); ArrayList<String> list = new ArrayList<String>();
while(st.hasMoreElements()) while(st.hasMoreElements()) {
{ list.add(st.nextToken());
String expr = (String) st.nextElement();
list.add(expr);
} }
return list.toArray(new String[list.size()]); return list.toArray(new String[list.size()]);
} }
public void loadSavedExpressions(String memorySpace, Object context) /**
* Populate the expression history combobox based on the history persisted
* in the launch configuration for the given context and memory space (where
* applicable)
*
* @param context
* the debug context. In practice, this will always be a process
* context
* @param memorySpace
* memory space ID; null if not applicable
*/
public void loadSavedExpressions(Object context, String memorySpace)
{ {
String[] expressions = getSavedExpressions(memorySpace, context);
String text = fExpression.getText(); try {
fExpression.removeAll(); String[] expressions = getSavedExpressions(context, memorySpace);
for(int idx=0; idx < expressions.length; idx++) String currentExpression = fExpression.getText();
{ fExpression.removeAll();
fExpression.add(expressions[idx]); for (String expression : expressions) {
} fExpression.add(expression);
if(text != null) }
{ if (currentExpression != null) {
fExpression.setText(text); fExpression.setText(currentExpression);
}
} catch (CoreException e) {
// Unexpected snag dealing with launch configuration
MemoryBrowserPlugin.log(e);
} }
} }
public void addExpressionToList( String memorySpace, Object context, String expr ) { public void addExpressionToHistory(Object context, String expr, String memorySpace) {
/* /*
* Make sure it does not already exist, we do not want to show duplicates. * Make sure it does not already exist, we do not want to show duplicates.
*/ */
@ -257,7 +297,7 @@ public class GoToAddressBarWidget {
} }
/* /*
* Add the new expression to the dropdown. * Add the new expression to the combobox
*/ */
fExpression.add(expr); fExpression.add(expr);
@ -265,20 +305,28 @@ public class GoToAddressBarWidget {
/* /*
* Add it to the persistense database. * Add it to the persistense database.
*/ */
saveExpression(memorySpace, context, expr); addExpressionToHistoryPersistence(context, expr, memorySpace);
} }
public void clearExpressionsFromList(String[] memorySpaces, Object context) { /**
* Clears the history of expressions for the given debug context, both in
* the GUI and the persistence data
*
* @param context
* the debug context. In practice, this will always be a process
* context.
*/
public void clearExpressionHistory(Object context) {
/* /*
* Clean up the combo list. * Clear the combobox
*/ */
fExpression.removeAll(); fExpression.removeAll();
fExpression.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); fExpression.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
/* /*
* Clean out the expression persistense. * Clear the history persisted in the launch configuration
*/ */
deleteExpressions(context); clearExpressionHistoryPersistence(context);
/* /*
* Make sure the status image indicator shows OK. * Make sure the status image indicator shows OK.
@ -407,38 +455,52 @@ public class GoToAddressBarWidget {
} }
private String getTargetName(Object context) /**
* Get the identifier for the given context if it is a recurring one. See
* {@link IRecurringDebugContext}
*
* @param context
* the debug context
* @return the ID or UNKNOWN_CONTEXT_ID if the context is non-recurring or
* can't provide us its ID
*/
private String getRecurringContextID(Object context)
{ {
String targetName = null; String id = UNKNOWN_CONTEXT_ID;
if(context instanceof IAdaptable) if (context instanceof IAdaptable) {
{
IAdaptable adaptable = (IAdaptable) context; IAdaptable adaptable = (IAdaptable) context;
ITargetLabelProvider labelProvider = (ITargetLabelProvider)adaptable.getAdapter(ITargetLabelProvider.class); IRecurringDebugContext recurringDebugContext = (IRecurringDebugContext)adaptable.getAdapter(IRecurringDebugContext.class);
if(labelProvider != null) if (recurringDebugContext != null) {
{ try {
try id = recurringDebugContext.getContextID();
{
targetName = labelProvider.getLabel();
} }
catch(DebugException e) catch(DebugException e) {
{ // If the context can't give us the ID, just treat it as a
// non-recurring context
} }
} }
} }
if(targetName == null || targetName.trim().length() == 0) return id;
{
targetName = UNKNOWN_TARGET_NAME;
}
return targetName;
} }
private String getSaveExpressionKey(String targetName, String memorySpace) /**
{ * Get a key that we can use to persist the expression history for the given
String key = SAVED_EXPRESSIONS + SEPARATOR + targetName.trim(); * debug context and memory space (where applicable). The key is used within
if(memorySpace != null && memorySpace.trim().length() > 0) * the scope of a launch configuration.
{ *
key += SEPARATOR + memorySpace.trim(); * @param contextID
* a recurring debug context ID; see
* {@link IRecurringDebugContext}
* @param memorySpace
* a memory space identifier, or null if not applicable
* @return they key which will be used to persist the expression history
*/
private String getSaveExpressionKey(String contextID, String memorySpace) {
assert contextID.length() > 0;
String key = SAVED_EXPRESSIONS + SEPARATOR + contextID;
if (memorySpace != null && memorySpace.length() > 0) {
key += SEPARATOR + memorySpace;
} }
return key; return key;
} }

View file

@ -23,7 +23,6 @@ import java.util.Map;
import org.eclipse.cdt.debug.core.model.provisional.IMemoryRenderingViewportProvider; import org.eclipse.cdt.debug.core.model.provisional.IMemoryRenderingViewportProvider;
import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval; import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval;
import org.eclipse.cdt.debug.core.model.provisional.ITargetLabelProvider;
import org.eclipse.cdt.debug.internal.core.CRequest; import org.eclipse.cdt.debug.internal.core.CRequest;
import org.eclipse.cdt.debug.ui.provisional.IRepositionableMemoryRendering2; import org.eclipse.cdt.debug.ui.provisional.IRepositionableMemoryRendering2;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
@ -84,6 +83,7 @@ import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Color;
@ -241,36 +241,31 @@ public class MemoryBrowser extends ViewPart implements IDebugContextListener, IM
fGotoAddressBar = new GoToAddressBarWidget(); fGotoAddressBar = new GoToAddressBarWidget();
fGotoAddressBarControl = fGotoAddressBar.createControl(fMainComposite); fGotoAddressBarControl = fGotoAddressBar.createControl(fMainComposite);
fGotoAddressBar.getButton(IDialogConstants.OK_ID).addSelectionListener(new SelectionListener() { fGotoAddressBar.getButton(IDialogConstants.OK_ID).addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {}
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
performGo(false); performGo(false);
} }
}); });
fGotoAddressBar.getButton(GoToAddressBarWidget.ID_GO_NEW_TAB).addSelectionListener(new SelectionListener() { fGotoAddressBar.getButton(GoToAddressBarWidget.ID_GO_NEW_TAB).addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {}
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
performGo(true); performGo(true);
} }
}); });
fGotoAddressBar.getExpressionWidget().addSelectionListener(new SelectionListener() { fGotoAddressBar.getExpressionWidget().addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {}
public void widgetDefaultSelected(SelectionEvent e) { public void widgetDefaultSelected(SelectionEvent e) {
performGo(false); performGo(false);
} }
}); });
fGotoMemorySpaceControl.addSelectionListener(new SelectionListener() { fGotoMemorySpaceControl.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {}
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
if(fGotoMemorySpaceControl.getItemCount() >= 2) if(fGotoMemorySpaceControl.getItemCount() >= 2) {
{
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl;
if (activeFolder != null) { if (activeFolder != null) {
final Object context = activeFolder.getData(KEY_CONTEXT); final Object context = activeFolder.getData(KEY_CONTEXT);
fGotoAddressBar.loadSavedExpressions(fGotoMemorySpaceControl.getText(), context); fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.getText());
} }
} }
} }
@ -336,11 +331,14 @@ public class MemoryBrowser extends ViewPart implements IDebugContextListener, IM
return false; return false;
} }
public void clearExpressionsFromList(String memorySpace) { /**
* Clears the expression history for the active tab
*/
public void clearExpressionHistoryForActiveTab() {
final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl; final CTabFolder activeFolder = (CTabFolder) fStackLayout.topControl;
if (activeFolder != null) { if (activeFolder != null) {
final Object context = activeFolder.getData(KEY_CONTEXT); final Object context = activeFolder.getData(KEY_CONTEXT);
fGotoAddressBar.clearExpressionsFromList(fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getItems() : new String[]{""}, context); fGotoAddressBar.clearExpressionHistory(context);
} }
} }
@ -422,7 +420,7 @@ public class MemoryBrowser extends ViewPart implements IDebugContextListener, IM
if (activeFolder != null) { if (activeFolder != null) {
context = activeFolder.getData(KEY_CONTEXT); context = activeFolder.getData(KEY_CONTEXT);
} }
fGotoAddressBar.addExpressionToList(memorySpace, context, expression); fGotoAddressBar.addExpressionToHistory(context, expression, memorySpace);
performGo(inNewTab, expression, memorySpace); performGo(inNewTab, expression, memorySpace);
} }
} }
@ -909,7 +907,7 @@ public class MemoryBrowser extends ViewPart implements IDebugContextListener, IM
CTabItem tabItem = (CTabItem)e.item; CTabItem tabItem = (CTabItem)e.item;
updateExpression(tabItem); updateExpression(tabItem);
updateMemorySpaceControlSelection(tabItem); updateMemorySpaceControlSelection(tabItem);
fGotoAddressBar.loadSavedExpressions(fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : "", context); fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : null);
getSite().getSelectionProvider().setSelection(new StructuredSelection(tabItem.getData(KEY_RENDERING))); getSite().getSelectionProvider().setSelection(new StructuredSelection(tabItem.getData(KEY_RENDERING)));
handleTabActivated(tabItem); handleTabActivated(tabItem);
} }
@ -920,7 +918,7 @@ public class MemoryBrowser extends ViewPart implements IDebugContextListener, IM
fStackLayout.topControl = tabFolder; fStackLayout.topControl = tabFolder;
// set empty initial expression // set empty initial expression
fGotoAddressBar.setExpressionText(""); //$NON-NLS-1$ fGotoAddressBar.setExpressionText(""); //$NON-NLS-1$
fGotoAddressBar.loadSavedExpressions(fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : "", context); fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : null);
} }
// update debug context to the new selection // update debug context to the new selection
tabFolder.setData(KEY_CONTEXT, context); tabFolder.setData(KEY_CONTEXT, context);
@ -955,7 +953,7 @@ public class MemoryBrowser extends ViewPart implements IDebugContextListener, IM
updateExpression(activeFolder.getSelection()); updateExpression(activeFolder.getSelection());
updateMemorySpaceControlSelection(activeFolder.getSelection()); updateMemorySpaceControlSelection(activeFolder.getSelection());
fGotoAddressBar.loadSavedExpressions(fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : "", context); fGotoAddressBar.loadSavedExpressions(context, fGotoMemorySpaceControl.isVisible() ? fGotoMemorySpaceControl.getText() : null);
fStackLayout.topControl.getParent().layout(true); fStackLayout.topControl.getParent().layout(true);
} }

View file

@ -11,6 +11,9 @@
package org.eclipse.cdt.debug.ui.memory.memorybrowser; package org.eclipse.cdt.debug.ui.memory.memorybrowser;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
@ -69,4 +72,32 @@ public class MemoryBrowserPlugin extends AbstractUIPlugin {
public static ImageDescriptor getImageDescriptor(String path) { public static ImageDescriptor getImageDescriptor(String path) {
return imageDescriptorFromPlugin(PLUGIN_ID, path); return imageDescriptorFromPlugin(PLUGIN_ID, path);
} }
/**
* Logs the specified status with this plug-in's log.
*
* @param status status to log
*/
public static void log(IStatus status) {
getDefault().getLog().log(status);
}
/**
* Logs the specified throwable with this plug-in's log.
*
* @param t throwable to log
*/
public static void log(Throwable t) {
log(newErrorStatus("Error logged from Debug UI: ", t)); //$NON-NLS-1$
}
/**
* Returns a new error status for this plug-in with the given message
* @param message the message to be included in the status
* @param exception the exception to be included in the status or <code>null</code> if none
* @return a new error status
*/
public static IStatus newErrorStatus(String message, Throwable exception) {
return new Status(IStatus.ERROR, PLUGIN_ID, IDebugUIConstants.INTERNAL_ERROR, message, exception);
}
} }