mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-23 08:55:25 +02:00
Bug 342141 - Executables view content goes stale in various scenarios
This commit is contained in:
parent
78cb17e708
commit
9782f044c7
14 changed files with 1075 additions and 526 deletions
|
@ -22,6 +22,7 @@ import org.eclipse.cdt.core.CCorePlugin;
|
|||
import org.eclipse.cdt.core.model.CoreModel;
|
||||
import org.eclipse.cdt.core.model.ICProject;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.debug.internal.core.Trace;
|
||||
import org.eclipse.cdt.internal.core.model.CModelManager;
|
||||
import org.eclipse.cdt.internal.core.model.ExternalTranslationUnit;
|
||||
import org.eclipse.cdt.internal.core.model.TranslationUnit;
|
||||
|
@ -40,7 +41,29 @@ import org.eclipse.core.runtime.content.IContentTypeManager;
|
|||
|
||||
public class Executable extends PlatformObject {
|
||||
|
||||
/**
|
||||
* Poorly named. This does not determine if the the file is an executable
|
||||
* but rather a binary. Use {@link #isBinaryFile(IPath)} instead.
|
||||
*
|
||||
* @deprecated use {@link #isBinaryFile(IPath)}
|
||||
*/
|
||||
@Deprecated
|
||||
static public boolean isExecutableFile(IPath path) {
|
||||
return isBinaryFile(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given file is a binary file. For our purposes, an
|
||||
* "executable" is a runnable program (an .exe file on Windows, e.g.,) or a
|
||||
* shared library. A binary can be an executable but it can also be an
|
||||
* instruction-containing artifact of a build, which typically is linked to
|
||||
* make an executable (.e.,g .o and .obj files)
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @since 7.1
|
||||
*/
|
||||
static public boolean isBinaryFile(IPath path) {
|
||||
// ignore directories
|
||||
if (path.toFile().isDirectory()) {
|
||||
return false;
|
||||
|
@ -159,9 +182,12 @@ public class Executable extends PlatformObject {
|
|||
* @since 6.0
|
||||
*/
|
||||
public synchronized ITranslationUnit[] getSourceFiles(IProgressMonitor monitor) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null);
|
||||
|
||||
if (!refreshSourceFiles && !remapSourceFiles)
|
||||
if (!refreshSourceFiles && !remapSourceFiles) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "returning cached result"); //$NON-NLS-1$
|
||||
return sourceFiles.toArray(new TranslationUnit[sourceFiles.size()]) ;
|
||||
}
|
||||
|
||||
// Try to get the list of source files used to build the binary from the
|
||||
// symbol information.
|
||||
|
@ -274,6 +300,8 @@ public class Executable extends PlatformObject {
|
|||
* @since 6.0
|
||||
*/
|
||||
public void setRefreshSourceFiles(boolean refreshSourceFiles) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, refreshSourceFiles);
|
||||
|
||||
this.refreshSourceFiles = refreshSourceFiles;
|
||||
}
|
||||
|
||||
|
@ -309,6 +337,7 @@ public class Executable extends PlatformObject {
|
|||
* @since 7.0
|
||||
*/
|
||||
public void setRemapSourceFiles(boolean remapSourceFiles) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, remapSourceFiles);
|
||||
this.remapSourceFiles = remapSourceFiles;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,31 +11,36 @@
|
|||
|
||||
package org.eclipse.cdt.debug.core.executables;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.cdt.core.model.CoreModel;
|
||||
import org.eclipse.cdt.core.model.ElementChangedEvent;
|
||||
import org.eclipse.cdt.core.model.IBinary;
|
||||
import org.eclipse.cdt.core.model.ICElement;
|
||||
import org.eclipse.cdt.core.model.ICElementDelta;
|
||||
import org.eclipse.cdt.core.model.ICProject;
|
||||
import org.eclipse.cdt.core.model.IElementChangedListener;
|
||||
import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent;
|
||||
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
|
||||
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener;
|
||||
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
|
||||
import org.eclipse.cdt.debug.internal.core.Trace;
|
||||
import org.eclipse.cdt.debug.internal.core.executables.StandardExecutableImporter;
|
||||
import org.eclipse.cdt.debug.internal.core.executables.StandardSourceFileRemappingFactory;
|
||||
import org.eclipse.cdt.debug.internal.core.executables.StandardSourceFilesProvider;
|
||||
import org.eclipse.cdt.internal.core.model.CModelManager;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceChangeEvent;
|
||||
import org.eclipse.core.resources.IResourceChangeListener;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IResourceDeltaVisitor;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IConfigurationElement;
|
||||
|
@ -52,9 +57,11 @@ import org.eclipse.core.runtime.Status;
|
|||
import org.eclipse.core.runtime.SubMonitor;
|
||||
import org.eclipse.core.runtime.SubProgressMonitor;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.osgi.service.debug.DebugOptions;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationListener;
|
||||
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
|
||||
import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
|
||||
|
||||
/**
|
||||
* The Executables Manager maintains a collection of executables built by all of
|
||||
|
@ -64,35 +71,119 @@ import org.osgi.framework.ServiceReference;
|
|||
* @author Ken Ryall
|
||||
*
|
||||
*/
|
||||
public class ExecutablesManager extends PlatformObject implements IResourceChangeListener, ICProjectDescriptionListener {
|
||||
public class ExecutablesManager extends PlatformObject implements ICProjectDescriptionListener, IElementChangedListener {
|
||||
|
||||
private static final String EXECUTABLES_MANAGER_DEBUG_TRACING = CDebugCorePlugin.PLUGIN_ID + "EXECUTABLES_MANAGER_DEBUG_TRACING"; //$NON-NLS-1$
|
||||
|
||||
private Map<IProject, IProjectExecutablesProvider> executablesProviderMap = new HashMap<IProject, IProjectExecutablesProvider>();
|
||||
private Map<IProject, List<Executable>> executablesMap = new HashMap<IProject, List<Executable>>();
|
||||
private List<IExecutablesChangeListener> changeListeners = Collections.synchronizedList(new ArrayList<IExecutablesChangeListener>());
|
||||
private List<IProjectExecutablesProvider> executableProviders;
|
||||
private List<ISourceFilesProvider> sourceFileProviders;
|
||||
private List<ISourceFileRemappingFactory> sourceFileRemappingFactories;
|
||||
private List<IExecutableImporter> executableImporters;
|
||||
|
||||
private boolean DEBUG;
|
||||
|
||||
private Job refreshJob = new Job("Get Executables") { //$NON-NLS-1$
|
||||
/**
|
||||
* Map of launch config names to the path locator memento string in the
|
||||
* launch config, recorded in the most recent launch config change
|
||||
* notification. We use this to ensure we flush source file mappings only
|
||||
* when the launch config change involves a change to the source locators.
|
||||
*/
|
||||
private Map<String, String> locatorMementos = new HashMap<String,String>();
|
||||
|
||||
|
||||
/**
|
||||
* A cache of the executables in the workspace, categorized by project.
|
||||
*
|
||||
* <p>
|
||||
* This cache is updated by scheduling an asynchronous search. SearchJob is
|
||||
* the only class that should <i>modify</i> this collection, including the
|
||||
* sub collections of Executable objects. The collection can be read from
|
||||
* any thread at any time. All access (read or write) must be serialized by
|
||||
* synchronizing on the Map object.
|
||||
* <p>
|
||||
* The same Executable may appear more than once.
|
||||
*/
|
||||
private Map<IProject, List<Executable>> executablesMap = new HashMap<IProject, List<Executable>>();
|
||||
|
||||
/**
|
||||
* Provide a flat list of the executables in {@link #executablesMap}, with
|
||||
* duplicates removed. That is effectively the list of all executables in
|
||||
* the workspace that we know of as of now.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<Executable> flattenExecutablesMap() {
|
||||
List<Executable> result = new ArrayList<Executable>(executablesMap.size() * 5); // most projects will have less than five executables
|
||||
synchronized (executablesMap) {
|
||||
for (List<Executable> exes : executablesMap.values()) {
|
||||
for (Executable exe : exes) {
|
||||
if (!result.contains(exe)) {
|
||||
result.add(exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Job which searches through CDT projects for executables. Only one thread
|
||||
* should be running this job at any one time. Running job should be
|
||||
* cancelled and verified terminated before initiating another.
|
||||
*/
|
||||
class SearchJob extends Job {
|
||||
SearchJob() {
|
||||
super("Executables Search"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The projects given to us when scheduled. If null, flush our entire
|
||||
* cache and search all projects
|
||||
*/
|
||||
private IProject[] projectsToRefresh;
|
||||
|
||||
@Override
|
||||
public IStatus run(IProgressMonitor monitor) {
|
||||
|
||||
trace("Get Executables job started at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Search for executables started"); //$NON-NLS-1$
|
||||
|
||||
List<IProject> projects = getProjectsToCheck();
|
||||
IStatus status = Status.OK_STATUS;
|
||||
|
||||
// The executables we know of now. We'll compare the search results
|
||||
// to this and see if we need to notify change listeners
|
||||
List<Executable> before = flattenExecutablesMap();
|
||||
|
||||
// Get the CDT projects in the workspace that we have no cached
|
||||
// results for (are not in 'executablesMap'). Also, we may have been
|
||||
// asked to refresh the cache for some projects we've search before
|
||||
List<IProject> projects = new ArrayList<IProject>();
|
||||
synchronized (executablesMap) {
|
||||
if (projectsToRefresh == null) {
|
||||
executablesMap.clear();
|
||||
}
|
||||
else {
|
||||
for (IProject project : projectsToRefresh) {
|
||||
executablesMap.remove(project);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the list of projects we plan to search
|
||||
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
|
||||
if (!executablesMap.containsKey(project) && CoreModel.hasCNature(project)) {
|
||||
projects.add(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SubMonitor subMonitor = SubMonitor.convert(monitor, projects.size());
|
||||
|
||||
for (IProject project : projects) {
|
||||
if (subMonitor.isCanceled()) {
|
||||
trace("Get Executables job cancelled at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
return Status.CANCEL_STATUS;
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Search for executables canceled"); //$NON-NLS-1$
|
||||
status = Status.CANCEL_STATUS;
|
||||
break; // we've already changed our model; stop searching but proceed to notify listeners that the model changed
|
||||
}
|
||||
|
||||
subMonitor.subTask("Checking project: " + project.getName()); //$NON-NLS-1$
|
||||
|
@ -100,36 +191,78 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
// get the executables provider for this project
|
||||
IProjectExecutablesProvider provider = getExecutablesProviderForProject(project);
|
||||
if (provider != null) {
|
||||
trace("Getting executables for project: " + project.getName() + " using " + provider.toString()); //$NON-NLS-1$//$NON-NLS-2$
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Getting executables for project: " + project.getName() + " using " + provider.toString()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
List<Executable> executables = provider.getExecutables(project, subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE));
|
||||
// store the list of executables for this project
|
||||
synchronized (executablesMap) {
|
||||
if (!monitor.isCanceled()) {
|
||||
executablesMap.put(project, executables);
|
||||
}
|
||||
executablesMap.put(project, executables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// See if, after all that work, there's a net change in the
|
||||
// executables list. If so, notify listeners.
|
||||
List<Executable> after = flattenExecutablesMap();
|
||||
List<Executable> removed = before;
|
||||
List<Executable> added = new ArrayList<Executable>(after.size());
|
||||
for (Executable a : after) {
|
||||
if (!removed.remove(a)) {
|
||||
added.add(a);
|
||||
}
|
||||
}
|
||||
// notify the listeners
|
||||
synchronized (changeListeners) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
listener.executablesListChanged();
|
||||
if (removed.size() > 0 || added.size() > 0) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
// New interface
|
||||
if (listener instanceof IExecutablesChangeListener2) {
|
||||
if (removed.size() > 0) {
|
||||
((IExecutablesChangeListener2)listener).executablesRemoved(removed);
|
||||
}
|
||||
if (added.size() > 0) {
|
||||
((IExecutablesChangeListener2)listener).executablesAdded(added);
|
||||
}
|
||||
}
|
||||
// Old interface
|
||||
listener.executablesListChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace("Get Executables job finished at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Search for executables finished"); //$NON-NLS-1$
|
||||
|
||||
return Status.OK_STATUS;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the search job. Use this, not the standard Job.schedule()
|
||||
* method.
|
||||
*
|
||||
* @param projectsToRefresh
|
||||
* if null, all CDT projects in the workspace are searched.
|
||||
* If not null, we search only newly present projects and the
|
||||
* projects provided (even if searched before). Empty list
|
||||
* can be passed to search only newly present projects.
|
||||
*/
|
||||
public void schedule(IProject[] projectsToRefresh) {
|
||||
this.projectsToRefresh = projectsToRefresh;
|
||||
super.schedule();
|
||||
}
|
||||
};
|
||||
|
||||
/** The search job. We only let one of these run at any one time */
|
||||
private SearchJob searchJob = new SearchJob();
|
||||
|
||||
private static ExecutablesManager executablesManager = null;
|
||||
/** Lock used to serialize the search jobs */
|
||||
private Object searchSchedulingLock = new Object();
|
||||
|
||||
/** The singleton */
|
||||
private static ExecutablesManager executablesManager;
|
||||
|
||||
/**
|
||||
* Get the executables manager instance
|
||||
* @return the executables manager
|
||||
* @return the singleton manager
|
||||
*/
|
||||
public static ExecutablesManager getExecutablesManager() {
|
||||
if (executablesManager == null)
|
||||
|
@ -138,25 +271,7 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
}
|
||||
|
||||
public ExecutablesManager() {
|
||||
|
||||
// check if debugging is enabled
|
||||
BundleContext context = CDebugCorePlugin.getDefault().getBundle().getBundleContext();
|
||||
if (context != null) {
|
||||
ServiceReference reference = CDebugCorePlugin.getDefault().getBundle().getBundleContext().getServiceReference(DebugOptions.class.getName());
|
||||
if (reference != null) {
|
||||
DebugOptions service = (DebugOptions) context.getService(reference);
|
||||
if (service != null) {
|
||||
try {
|
||||
DEBUG = service.getBooleanOption(EXECUTABLES_MANAGER_DEBUG_TRACING, false);
|
||||
} finally {
|
||||
// we have what we want - release the service
|
||||
context.ungetService(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshJob.setPriority(Job.SHORT);
|
||||
searchJob.setPriority(Job.SHORT);
|
||||
|
||||
// load the extension points
|
||||
loadExecutableProviderExtensions();
|
||||
|
@ -171,12 +286,101 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
executableImporters.add(0, new StandardExecutableImporter());
|
||||
|
||||
// listen for events we're interested in
|
||||
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.POST_BUILD);
|
||||
CModelManager.getDefault().addElementChangedListener(this);
|
||||
CoreModel.getDefault().getProjectDescriptionManager().addCProjectDescriptionListener(this,
|
||||
CProjectDescriptionEvent.APPLIED);
|
||||
|
||||
// Listen for changes to the global source locators. These locators
|
||||
// affect how source files are found locally. The Executable objects
|
||||
// cache their local source file paths and rely on us to tell them to
|
||||
// flush those caches when applicable locators change.
|
||||
CDebugCorePlugin.getDefault().getCommonSourceLookupDirector().addParticipants(new ISourceLookupParticipant[] { new ISourceLookupParticipant(){
|
||||
|
||||
public void init(ISourceLookupDirector director) {}
|
||||
public Object[] findSourceElements(Object object) { return new Object[0]; }
|
||||
public String getSourceName(Object object) throws CoreException { return ""; } //$NON-NLS-1$
|
||||
public void dispose() {}
|
||||
public void sourceContainersChanged(ISourceLookupDirector director) {
|
||||
// Unfortunately, it would be extremely difficult/costly to
|
||||
// determine which binaries are effected by the source locator
|
||||
// change, so we have to tell all Executables to flush
|
||||
flushExecutablesSourceMappings();
|
||||
}
|
||||
} });
|
||||
|
||||
// Source locators are also in launch configurations, and those too come
|
||||
// into play when an Executable looks for a source file locally. So,
|
||||
// listen for changes in those locators, too.
|
||||
DebugPlugin.getDefault().getLaunchManager().addLaunchConfigurationListener(new ILaunchConfigurationListener() {
|
||||
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
|
||||
// Expect lots of noise for working copies. We only care about
|
||||
// changes to actual configs
|
||||
if (configuration.isWorkingCopy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the source locators in the launch config were not modified, then no-op
|
||||
try {
|
||||
String configName = configuration.getName();
|
||||
String mementoBefore = locatorMementos.get(configName);
|
||||
String mementoNow = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, ""); //$NON-NLS-1$
|
||||
if (mementoNow.equals(mementoBefore)) {
|
||||
return; // launch config change had no affect on source locators
|
||||
}
|
||||
locatorMementos.put(configName, mementoNow);
|
||||
} catch (CoreException e) {
|
||||
CDebugCorePlugin.log(e);
|
||||
}
|
||||
|
||||
// TODO: For now, just tell all Executables to flush. Look
|
||||
// into identifying which binary the config is associated
|
||||
// with so we can flush only that Executable
|
||||
flushExecutablesSourceMappings();
|
||||
}
|
||||
public void launchConfigurationRemoved(ILaunchConfiguration configuration) { configAddedOrRemoved(configuration); }
|
||||
public void launchConfigurationAdded(ILaunchConfiguration configuration) { configAddedOrRemoved(configuration); }
|
||||
private void configAddedOrRemoved(ILaunchConfiguration configuration) {
|
||||
// Expect lots of noise for working copies. We only care about
|
||||
// changes to actual configs
|
||||
if (configuration.isWorkingCopy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The addition or removal of a launch config could affect
|
||||
// how files are found. It would be extremely costly to
|
||||
// determine here whether it will or not, so assume it will.
|
||||
|
||||
// TODO: For now, just tell all Executables to flush. Look
|
||||
// into identifying which binary the config is associated
|
||||
// with so we can flush only that Executable
|
||||
flushExecutablesSourceMappings();
|
||||
}
|
||||
});
|
||||
|
||||
// schedule a refresh so we get up to date
|
||||
scheduleRefresh();
|
||||
scheduleExecutableSearch(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell all Executable objects to flush their source file mappings, then
|
||||
* notify our listeners that the executables changed. Even though the
|
||||
* binaries may not have actually changed, the impact to a client of
|
||||
* Executable is the same. If the client has cached any of the source file
|
||||
* information the Executable provided, that info can no longer be trusted.
|
||||
* The primary purpose of an Executable is to provide source file path
|
||||
* information--not only the compile paths burned into the executable but
|
||||
* also the local mappings of those paths.
|
||||
*/
|
||||
private void flushExecutablesSourceMappings() {
|
||||
List<Executable> exes = flattenExecutablesMap();
|
||||
for (Executable exe : exes) {
|
||||
exe.setRemapSourceFiles(true);
|
||||
}
|
||||
synchronized (changeListeners) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
listener.executablesChanged(exes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,44 +400,38 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the list of executables in the workspace.
|
||||
* @param wait whether or not to wait if the list is being refreshed when this
|
||||
* method is called. when true, this call will not return until the list is
|
||||
* complete. when false, it will return with the last known list. if calling
|
||||
* from any UI, you should not block the UI waiting for this to return, but rather
|
||||
* register as an {@link IExecutablesChangeListener} to get notifications when the
|
||||
* list changes.
|
||||
* @return the list of executables which may be empty
|
||||
* Gets the list of executables in the workspace. This method doesn't
|
||||
* initiate a search. It returns the cached results of the most recent
|
||||
* search, or waits for the ongoing search to complete.
|
||||
*
|
||||
* @param wait
|
||||
* Whether or not to wait if the cache is in the process of being
|
||||
* updated when this method is called. When true, the call will
|
||||
* block until the update is complete. When false, it will return
|
||||
* the current cache. Callers on the UI thread should pass false
|
||||
* to avoid temporarily freezing the UI. Note that clients can
|
||||
* register as a {@link IExecutablesChangeListener} or
|
||||
* {@link IExecutablesChangeListener2}to get notifications when
|
||||
* the cache changes.
|
||||
* @return the list of executables; may be empty. List will not have
|
||||
* duplicates.
|
||||
* @since 7.0
|
||||
*/
|
||||
public Collection<Executable> getExecutables(boolean wait) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, Boolean.valueOf(wait));
|
||||
|
||||
trace("getExecutables called at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
|
||||
List<Executable> executables = new ArrayList<Executable>();
|
||||
|
||||
if (wait && refreshJob.getState() != Job.NONE) {
|
||||
trace("waiting for refresh job to finish at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
// Wait for running search to finish, if asked to
|
||||
if (wait && searchJob.getState() != Job.NONE) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Waiting for executable search to finish..."); //$NON-NLS-1$
|
||||
try {
|
||||
refreshJob.join();
|
||||
searchJob.join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
trace("refresh job finished at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "...executable search finished."); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
synchronized (executablesMap) {
|
||||
for (List<Executable> exes : executablesMap.values()) {
|
||||
for (Executable exe : exes) {
|
||||
if (!executables.contains(exe)) {
|
||||
executables.add(exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace("getExecutables returned at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
|
||||
return executables;
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceExit(null);
|
||||
return flattenExecutablesMap();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,7 +498,7 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
}
|
||||
|
||||
if (handled)
|
||||
scheduleRefresh();
|
||||
scheduleExecutableSearch(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -329,10 +527,10 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
* @return an array of source files which may be empty
|
||||
*/
|
||||
public String[] getSourceFiles(final Executable executable, IProgressMonitor monitor) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, executable);
|
||||
|
||||
String[] result = new String[0];
|
||||
|
||||
trace("getSourceFiles called at " + getStringFromTimestamp(System.currentTimeMillis()) + " for " + executable.getPath().toOSString()); //$NON-NLS-1$//$NON-NLS-2$
|
||||
|
||||
synchronized (sourceFileProviders) {
|
||||
Collections.sort(sourceFileProviders, new Comparator<ISourceFilesProvider>() {
|
||||
|
||||
|
@ -351,17 +549,14 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
String[] sourceFiles = provider.getSourceFiles(executable, new SubProgressMonitor(monitor, 1000));
|
||||
if (sourceFiles.length > 0) {
|
||||
result = sourceFiles;
|
||||
|
||||
trace("getSourceFiles got " + sourceFiles.length + " files from " + provider.toString()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Got " + sourceFiles.length + " files from " + provider.toString()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
break;
|
||||
}
|
||||
}
|
||||
monitor.done();
|
||||
}
|
||||
|
||||
trace("getSourceFiles returned at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceExit(null, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -382,221 +577,119 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
IProjectExecutablesProvider provider = getExecutablesProviderForProject(executable.getProject());
|
||||
if (provider != null) {
|
||||
IStatus result = provider.removeExecutable(executable, new SubProgressMonitor(monitor, 1));
|
||||
if (result.isOK()) {
|
||||
// remove the exe from the list
|
||||
List<Executable> exes = executablesMap.get(executable.getProject());
|
||||
if (exes != null) {
|
||||
exes.remove(executable);
|
||||
}
|
||||
} else {
|
||||
if (!result.isOK()) {
|
||||
status.add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify listeners that the list has changed. only do this if at least one delete succeeded.
|
||||
if (status.getChildren().length != executables.length) {
|
||||
synchronized (changeListeners) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
listener.executablesListChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to directly call our listeners. The file removal will
|
||||
// cause a C model change, which we will react to by then calling the
|
||||
// listeners
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the list of executables for the given projects
|
||||
* @param projects the list of projects, or null. if null or the list
|
||||
* is empty, all projects will be refreshed.
|
||||
* Initiates an asynchronous search of workspace CDT projects for
|
||||
* executables. If a search is ongoing, it's cancelled and a new one is
|
||||
* started. In all cases, this method returns quickly (does not wait/block).
|
||||
*
|
||||
* <p>
|
||||
* Listeners are notified when the search is complete and there is a change
|
||||
* in the collection of found executables. The results of the search can be
|
||||
* obtained by calling {@link #getExecutables(boolean)}.
|
||||
*
|
||||
* @param projectsToRefresh
|
||||
* if null, we discard our entire Executables cache and search
|
||||
* all CDT projects in the workspace. If not null, we purge our
|
||||
* cache for only the given projects then search in all CDT
|
||||
* projects for which we have no cache. Passing a project that we
|
||||
* have no cache for is innocuous. In all cases, we search for
|
||||
* executables in any newly available projects. This parameter is
|
||||
* simply a way to get us to <i>not</i> skip one or more projects
|
||||
* we already have the executables list for.
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
public void refresh(List<IProject> projects) {
|
||||
if (projects == null || projects.size() == 0) {
|
||||
// clear the entire cache
|
||||
executablesMap.clear();
|
||||
} else {
|
||||
for (IProject project : projects) {
|
||||
executablesMap.remove(project);
|
||||
}
|
||||
}
|
||||
|
||||
scheduleRefresh();
|
||||
public void refresh(List<IProject> projectsToRefresh) {
|
||||
scheduleExecutableSearch(projectsToRefresh != null ? projectsToRefresh.toArray(new IProject[projectsToRefresh.size()]) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 7.0
|
||||
* @deprecated we no longer listen directly for platform resource changes
|
||||
* but rather C model changes
|
||||
*/
|
||||
public void resourceChanged(IResourceChangeEvent event) {
|
||||
|
||||
synchronized (executablesMap) {
|
||||
// project needs to be refreshed after a build/clean as the binary may
|
||||
// be added/removed/renamed etc.
|
||||
if (event.getType() == IResourceChangeEvent.POST_BUILD) {
|
||||
Object obj = event.getSource();
|
||||
if (obj != null && obj instanceof IProject) {
|
||||
try {
|
||||
// make sure there's at least one builder for the project. this gets called even
|
||||
// when there are no builder (e.g. the Executables project for imported executables).
|
||||
IProject project = (IProject)obj;
|
||||
if (project.getDescription().getBuildSpec().length > 0) {
|
||||
if (executablesMap.containsKey(obj)) {
|
||||
List<Executable> executables = executablesMap.remove(obj);
|
||||
|
||||
trace("Scheduling refresh because project " + ((IProject)obj).getName() + " built or cleaned"); //$NON-NLS-1$//$NON-NLS-2$
|
||||
|
||||
scheduleRefresh();
|
||||
|
||||
// notify the listeners that these executables have possibly changed
|
||||
if (executables != null && executables.size() > 0) {
|
||||
synchronized (changeListeners) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
listener.executablesChanged(executables);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// refresh when projects are opened or closed. note that deleted
|
||||
// projects are handled later in this method. new projects are handled
|
||||
// in handleEvent. resource changed events always start at the workspace
|
||||
// root, so projects are the next level down
|
||||
boolean refreshNeeded = false;
|
||||
IResourceDelta[] projects = event.getDelta().getAffectedChildren();
|
||||
for (IResourceDelta projectDelta : projects) {
|
||||
if ((projectDelta.getFlags() & IResourceDelta.OPEN) != 0) {
|
||||
if (projectDelta.getKind() == IResourceDelta.CHANGED) {
|
||||
// project was opened or closed
|
||||
if (executablesMap.containsKey(projectDelta.getResource())) {
|
||||
executablesMap.remove(projectDelta.getResource());
|
||||
}
|
||||
refreshNeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshNeeded) {
|
||||
trace("Scheduling refresh because project(s) opened or closed"); //$NON-NLS-1$
|
||||
|
||||
scheduleRefresh();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
event.getDelta().accept(new IResourceDeltaVisitor() {
|
||||
|
||||
public boolean visit(IResourceDelta delta) throws CoreException {
|
||||
if (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.REMOVED) {
|
||||
IResource deltaResource = delta.getResource();
|
||||
if (deltaResource != null) {
|
||||
boolean refresh = false;
|
||||
if (delta.getKind() == IResourceDelta.REMOVED && deltaResource instanceof IProject) {
|
||||
// project deleted
|
||||
if (executablesMap.containsKey(deltaResource)) {
|
||||
executablesMap.remove(deltaResource);
|
||||
refresh = true;
|
||||
|
||||
trace("Scheduling refresh because project " + deltaResource.getName() + " deleted"); //$NON-NLS-1$//$NON-NLS-2$
|
||||
}
|
||||
} else {
|
||||
// see if a binary has been added/removed
|
||||
IPath resourcePath = deltaResource.getLocation();
|
||||
if (resourcePath != null && Executable.isExecutableFile(resourcePath)) {
|
||||
if (executablesMap.containsKey(deltaResource.getProject())) {
|
||||
executablesMap.remove(deltaResource.getProject());
|
||||
refresh = true;
|
||||
|
||||
trace("Scheduling refresh because a binary was added/removed"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh) {
|
||||
scheduleRefresh();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (CoreException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@Deprecated
|
||||
public void resourceChanged(IResourceChangeEvent event) {}
|
||||
|
||||
/**
|
||||
* @since 7.0
|
||||
*/
|
||||
public void handleEvent(CProjectDescriptionEvent event) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, event);
|
||||
|
||||
// this handles the cases where the active build configuration changes,
|
||||
// and when new projects are created or loaded at startup.
|
||||
boolean refresh = false;
|
||||
|
||||
int eventType = event.getEventType();
|
||||
|
||||
if (eventType == CProjectDescriptionEvent.APPLIED) {
|
||||
|
||||
synchronized (executablesMap) {
|
||||
// see if the active build config has changed
|
||||
ICProjectDescription newDesc = event.getNewCProjectDescription();
|
||||
ICProjectDescription oldDesc = event.getOldCProjectDescription();
|
||||
if (oldDesc != null && newDesc != null) {
|
||||
String newConfigName = newDesc.getActiveConfiguration().getName();
|
||||
String oldConfigName = oldDesc.getActiveConfiguration().getName();
|
||||
if (!newConfigName.equals(oldConfigName)) {
|
||||
if (executablesMap.containsKey(newDesc.getProject())) {
|
||||
executablesMap.remove(newDesc.getProject());
|
||||
refresh = true;
|
||||
// see if the active build config has changed
|
||||
ICProjectDescription newDesc = event.getNewCProjectDescription();
|
||||
ICProjectDescription oldDesc = event.getOldCProjectDescription();
|
||||
if (oldDesc != null && newDesc != null) {
|
||||
String newConfigName = newDesc.getActiveConfiguration().getName();
|
||||
String oldConfigName = oldDesc.getActiveConfiguration().getName();
|
||||
if (!newConfigName.equals(oldConfigName)) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Scheduling refresh because active build configuration changed"); //$NON-NLS-1$
|
||||
scheduleExecutableSearch(new IProject[]{newDesc.getProject()});
|
||||
}
|
||||
} else if (newDesc != null && oldDesc == null) {
|
||||
// project just created
|
||||
scheduleExecutableSearch(null);
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Scheduling refresh because project " + newDesc.getProject().getName() + " created"); //$NON-NLS-1$//$NON-NLS-2$
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace("Scheduling refresh because active build configuration changed"); //$NON-NLS-1$
|
||||
/**
|
||||
* Initiates an asynchronous search of workspace CDT projects for
|
||||
* executables. For details, see {@link #refresh(List)}, which is a public
|
||||
* wrapper for this internal method. This method is more aptly named and
|
||||
* takes an array instead of a list
|
||||
*/
|
||||
private void scheduleExecutableSearch(final IProject[] projectsToRefresh) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, projectsToRefresh);
|
||||
|
||||
// Don't schedule multiple search jobs simultaneously. If one is
|
||||
// running, cancel it, wait for it to terminate, then schedule a new
|
||||
// one. However we must not block our caller, so spawn an intermediary
|
||||
// thread to do that leg work. This isn't an efficient design, but these
|
||||
// searches aren't done in high volume.
|
||||
Job job = new Job("Executable search scheduler") { //$NON-NLS-1$
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
synchronized (searchSchedulingLock) {
|
||||
searchJob.cancel();
|
||||
if (searchJob.getState() != Job.NONE) {
|
||||
try {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Waiting for canceled job to terminate"); //$NON-NLS-1$
|
||||
searchJob.join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
} else if (newDesc != null && oldDesc == null) {
|
||||
// project just created
|
||||
refresh = true;
|
||||
|
||||
trace("Scheduling refresh because project " + newDesc.getProject().getName() + " created"); //$NON-NLS-1$//$NON-NLS-2$
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Scheduling new search job"); //$NON-NLS-1$
|
||||
searchJob.schedule(projectsToRefresh);
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh) {
|
||||
scheduleRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
private List<IProject> getProjectsToCheck() {
|
||||
|
||||
List<IProject> projects = new ArrayList<IProject>();
|
||||
|
||||
synchronized (executablesMap) {
|
||||
// look for any CDT projects not in our cache
|
||||
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
|
||||
if (!executablesMap.containsKey(project)) {
|
||||
if (CoreModel.hasCNature(project)) {
|
||||
projects.add(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return projects;
|
||||
}
|
||||
|
||||
private void scheduleRefresh() {
|
||||
trace("scheduleRefresh called at " + getStringFromTimestamp(System.currentTimeMillis())); //$NON-NLS-1$
|
||||
|
||||
refreshJob.cancel();
|
||||
refreshJob.schedule();
|
||||
|
||||
};
|
||||
job.setPriority(Job.SHORT);
|
||||
job.schedule();
|
||||
}
|
||||
|
||||
private IProjectExecutablesProvider getExecutablesProviderForProject(IProject project) {
|
||||
|
@ -762,14 +855,168 @@ public class ExecutablesManager extends PlatformObject implements IResourceChang
|
|||
}
|
||||
}
|
||||
|
||||
private void trace(String msg) {
|
||||
if (DEBUG) {
|
||||
// TODO use Logger?
|
||||
System.out.println(msg);
|
||||
/**
|
||||
* We listen to C model changes and see if they affect what executables are
|
||||
* in the workspace, and/or if the executables we already know of have
|
||||
* changed.
|
||||
*
|
||||
* @since 7.1
|
||||
*/
|
||||
public void elementChanged(ElementChangedEvent event) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null);
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "event = \n" + event); // must be done separately because of traceEntry() limitation //$NON-NLS-1$
|
||||
|
||||
// Examine the event and figure out what needs to be done
|
||||
Set<IProject> refreshProjects = new HashSet<IProject>(5);
|
||||
Set<Executable> executablesChanged = new HashSet<Executable>(5);
|
||||
Set<Executable> executablesRemoved = new HashSet<Executable>(5);
|
||||
processDeltas(event.getDelta().getAddedChildren(), null, refreshProjects, executablesRemoved, executablesChanged);
|
||||
processDeltas(event.getDelta().getChangedChildren(), null, refreshProjects, executablesRemoved, executablesChanged);
|
||||
processDeltas(event.getDelta().getRemovedChildren(), null, refreshProjects, executablesRemoved, executablesChanged);
|
||||
|
||||
// Schedule executable searches in projects
|
||||
if (refreshProjects.size() > 0) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "One or more projects need to be re-searched"); //$NON-NLS-1$
|
||||
scheduleExecutableSearch(refreshProjects.toArray(new IProject[refreshProjects.size()]));
|
||||
}
|
||||
|
||||
// Invalidate the source file cache in changed Executables and inform
|
||||
// listeners
|
||||
if (executablesChanged.size() > 0) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "One or more executables changed"); //$NON-NLS-1$
|
||||
for (Executable exec : executablesChanged) {
|
||||
exec.setRefreshSourceFiles(true);
|
||||
}
|
||||
List<Executable> list = Arrays.asList(executablesChanged.toArray(new Executable[executablesChanged.size()]));
|
||||
synchronized (changeListeners) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
listener.executablesChanged(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (executablesRemoved.size() > 0) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "One or more executables were removed"); //$NON-NLS-1$
|
||||
List<Executable> list = Arrays.asList(executablesRemoved.toArray(new Executable[executablesRemoved.size()]));
|
||||
synchronized (changeListeners) {
|
||||
for (IExecutablesChangeListener listener : changeListeners) {
|
||||
if (listener instanceof IExecutablesChangeListener2) {
|
||||
((IExecutablesChangeListener2)listener).executablesRemoved(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private String getStringFromTimestamp(long timestamp) {
|
||||
return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(new Date(timestamp));
|
||||
/**
|
||||
* Drills down a hierarchy of CDT model change events to determine the
|
||||
* course of action.
|
||||
*
|
||||
* @param deltas
|
||||
* CDT model events received by the viewer
|
||||
* @param cproject
|
||||
* the project the resources in [deltas] belong to
|
||||
* @param projectsToRefresh
|
||||
* implementation populates (appends) this list with the projects
|
||||
* that need to be searched for executables. Note that Executable
|
||||
* objects are created by an async job. The best we can do here
|
||||
* is identify the projects that need to be searched. We can't
|
||||
* provide a list of added Executables objects since they haven't
|
||||
* been created yet.
|
||||
* @param removedExecutables
|
||||
* implementation populates (appends) this list with the
|
||||
* Executable objects that have been removed, requiring listeners
|
||||
* to be notified.
|
||||
* @param changedExecutables
|
||||
* implementation populates (appends) this list with the
|
||||
* Executable objects that have changed, requiring listeners to
|
||||
* be notified.
|
||||
*/
|
||||
private void processDeltas(ICElementDelta[] deltas, ICProject cproject, final Set<IProject> projectsToRefresh, final Set<Executable> removedExecutables, final Set<Executable> changedExecutables) {
|
||||
for (ICElementDelta delta : deltas) {
|
||||
ICElement element = delta.getElement();
|
||||
if (element instanceof ICProject) {
|
||||
// When a project is deleted, we get a REMOVED delta for the
|
||||
// project only--none for the elements in the project.
|
||||
IProject project = ((ICProject)element).getProject();
|
||||
if (delta.getKind() == ICElementDelta.REMOVED) {
|
||||
projectsToRefresh.add(project);
|
||||
List<Executable> execs = null;
|
||||
synchronized (executablesMap) {
|
||||
execs = executablesMap.get(project);
|
||||
}
|
||||
if (execs != null) {
|
||||
for (Executable exec : execs) {
|
||||
if (exec.getResource().equals(delta.getElement().getResource())) {
|
||||
removedExecutables.add(exec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Note that it's not our job to update 'executablesMap'.
|
||||
// The async exec search job will do that.
|
||||
}
|
||||
}
|
||||
else if (element instanceof IBinary) {
|
||||
IProject project = cproject.getProject();
|
||||
switch (delta.getKind()) {
|
||||
case ICElementDelta.ADDED:
|
||||
projectsToRefresh.add(project);
|
||||
break;
|
||||
case ICElementDelta.REMOVED: {
|
||||
projectsToRefresh.add(project);
|
||||
List<Executable> execs = null;
|
||||
synchronized (executablesMap) {
|
||||
execs = executablesMap.get(project);
|
||||
}
|
||||
if (execs != null) {
|
||||
for (Executable exec : execs) {
|
||||
if (exec.getResource().equals(delta.getElement().getResource())) {
|
||||
removedExecutables.add(exec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note that it's not our job to update 'executablesMap'.
|
||||
// The async exec search job will do that.
|
||||
break;
|
||||
}
|
||||
|
||||
case ICElementDelta.CHANGED: {
|
||||
List<Executable> execs = null;
|
||||
synchronized (executablesMap) {
|
||||
execs = executablesMap.get(project);
|
||||
}
|
||||
if (execs == null) {
|
||||
// Somehow, we missed the addition of the
|
||||
// project. Request that the project be
|
||||
// searched for executables
|
||||
projectsToRefresh.add(project);
|
||||
}
|
||||
else {
|
||||
// See if it's one of the executables we
|
||||
// already know is in the project. If
|
||||
// so, then we just need to tell
|
||||
// listeners the executable changed
|
||||
for (Executable exec : execs) {
|
||||
if (exec.getResource().equals(delta.getElement().getResource())) {
|
||||
changedExecutables.add(exec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (element instanceof ICProject) {
|
||||
cproject = (ICProject)element;
|
||||
}
|
||||
// recursively call ourselves to handle this delta's children
|
||||
processDeltas(delta.getAffectedChildren(), cproject, projectsToRefresh, removedExecutables, changedExecutables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,19 +14,65 @@ package org.eclipse.cdt.debug.core.executables;
|
|||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Listener interface for finding out when the list of Executable objects in the
|
||||
* workspace changes or when the objects themselves change.
|
||||
*
|
||||
* <p>
|
||||
* Executable objects are ephemeral representations of Eclipse workspace model
|
||||
* elements. A particular executable in the workspace is typically represented
|
||||
* by many Executable objects. For example, an executable in the workspace that
|
||||
* changes twice can cause the listener's {@link #executablesChanged(List)} to
|
||||
* be called with a different Executable instance each of the two times it's invoked.
|
||||
*
|
||||
*/
|
||||
public interface IExecutablesChangeListener extends EventListener {
|
||||
|
||||
/**
|
||||
* Called whenever the list of executables in the workspace changes, e.g. a
|
||||
* project was opened/closed/created/deleted
|
||||
* Called whenever the list of executables in the workspace changes. Many
|
||||
* types of operations cause the list to change, for example:
|
||||
* <ul>
|
||||
* <li>project is built for the first time
|
||||
* <li>project with executables already in place is open, closed, removed or
|
||||
* cleaned
|
||||
* <li>user deletes one or more executables
|
||||
* </ul>
|
||||
*
|
||||
* Clients can get the list by calling {@link ExecutablesManager#getExecutables()}
|
||||
*
|
||||
* @since 7.0
|
||||
*/
|
||||
public void executablesListChanged();
|
||||
|
||||
/**
|
||||
* Called whenever some executables have changed, e.g. when a project is rebuilt or
|
||||
* cleaned. The content may have changed for example, so the list of source files
|
||||
* may be different.
|
||||
* Called whenever one or more executables have changed, e.g. when a project
|
||||
* is rebuilt. This is sometimes also called if the executable has not
|
||||
* changed (i.e., the file on disk) but the information the Executable
|
||||
* object provides has changed. One such case is when there's a change in
|
||||
* the source locators, as such locators guide the Executable in finding the
|
||||
* local path for the compile path.
|
||||
*
|
||||
* <p>
|
||||
* The Executable instances in the given list have had their caches flushed
|
||||
* by ExecutableManager. Clients that keep references to Executable objects
|
||||
* must keep in mind that those particular instances may no longer be
|
||||
* managed by ExecutableManager and as such it is the client's
|
||||
* responsibility to tell those instances to flush when this listener method
|
||||
* is called. E.g.,
|
||||
*
|
||||
* <p><pre>
|
||||
* public void executablesChanged(List<Executable> executables) {
|
||||
* for (Executable e : executables) {
|
||||
* if (e.equals(fExecutable) {
|
||||
* fExecutable.setRefreshSourceFiles(true);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This is not called when an executable is added or removed
|
||||
*
|
||||
* @param executables
|
||||
* @since 7.0
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2011 Freescale Semiconductor and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Freescale Semiconductor - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.debug.core.executables;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Extension of IExecutablesChangeListener which allows listeners to more
|
||||
* precisely find out when an Executable is added or removed from the workspace
|
||||
*
|
||||
* @since 7.1
|
||||
*/
|
||||
public interface IExecutablesChangeListener2 extends IExecutablesChangeListener {
|
||||
|
||||
/**
|
||||
* Called when one or more Executable objects have been added to the
|
||||
* workspace
|
||||
*/
|
||||
public void executablesAdded(List<Executable> executables);
|
||||
|
||||
/**
|
||||
* Called when one or more Executable objects have been removed from the
|
||||
* workspace
|
||||
*/
|
||||
public void executablesRemoved(List<Executable> executables);
|
||||
|
||||
}
|
|
@ -167,7 +167,7 @@ public class StandardExecutableImporter implements IExecutableImporter {
|
|||
}
|
||||
|
||||
private void ensureBinaryType(IPath exectuableFilePath) {
|
||||
if (Executable.isExecutableFile(exectuableFilePath))
|
||||
if (Executable.isBinaryFile(exectuableFilePath))
|
||||
return;
|
||||
String ext = exectuableFilePath.getFileExtension();
|
||||
if (ext != null) {
|
||||
|
|
|
@ -45,7 +45,7 @@ public class StandardSourceFilesProvider extends PlatformObject implements ISour
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!Executable.isExecutableFile(executable.getPath()))
|
||||
if (!Executable.isBinaryFile(executable.getPath()))
|
||||
return null;
|
||||
|
||||
File f = new File(path.toOSString());
|
||||
|
|
|
@ -69,10 +69,11 @@ public class CSourceFinder implements ISourceFinder, ILaunchConfigurationListene
|
|||
* launch config. This is a heavy operation. As an optimization, we cache
|
||||
* the locators we create and discard when the launch config changes or is
|
||||
* disposed. Collection is subject to be changed by listener invocations.
|
||||
* Map key is the launch configuration name.
|
||||
*
|
||||
* @see CSourceFinder#getLocator(ILaunchConfiguration)
|
||||
*/
|
||||
private Map<ILaunchConfiguration, ISourceLocator> fConfigLocators = Collections.synchronizedMap(new HashMap<ILaunchConfiguration, ISourceLocator>());
|
||||
private Map<String, ISourceLocator> fConfigLocators = Collections.synchronizedMap(new HashMap<String, ISourceLocator>());
|
||||
|
||||
/**
|
||||
* We use this when we don't have an ILaunch or ILaunchConfiguration
|
||||
|
@ -134,13 +135,15 @@ public class CSourceFinder implements ISourceFinder, ILaunchConfigurationListene
|
|||
if (fLaunchLocator == null) {
|
||||
for (ILaunchConfiguration config : lmgr.getLaunchConfigurations()) {
|
||||
if (isMatch(config)) {
|
||||
String configName = config.getName();
|
||||
|
||||
// Search our cache of locators that we
|
||||
// instantiate for configurations. Create one if
|
||||
// not found
|
||||
ISourceLocator configLocator = fConfigLocators.get(config);
|
||||
ISourceLocator configLocator = fConfigLocators.get(configName);
|
||||
if (configLocator == null) {
|
||||
configLocator = getLocator(config); // heavy operation
|
||||
fConfigLocators.put(config, configLocator); // cache to avoid next time
|
||||
fConfigLocators.put(configName, configLocator); // cache to avoid next time
|
||||
}
|
||||
// In practice, a config's locator is always an ISourceLookupDirector
|
||||
if (configLocator instanceof ISourceLookupDirector) {
|
||||
|
@ -330,14 +333,15 @@ public class CSourceFinder implements ISourceFinder, ILaunchConfigurationListene
|
|||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationChanged(org.eclipse.debug.core.ILaunchConfiguration)
|
||||
*/
|
||||
public void launchConfigurationChanged(ILaunchConfiguration config) {
|
||||
synchronized public void launchConfigurationChanged(ILaunchConfiguration config) {
|
||||
// We don't care if it's a working copy.
|
||||
if (config.isWorkingCopy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the source locator attribute may have changed
|
||||
fConfigLocators.remove(config);
|
||||
if ((fLaunchLocator != null) && (fLaunchLocator.getLaunchConfiguration() == config)) {
|
||||
fConfigLocators.remove(config.getName());
|
||||
if ((fLaunchLocator != null) && (fLaunchLocator.getLaunchConfiguration().getName() == config.getName())) {
|
||||
fLaunchLocator = null;
|
||||
}
|
||||
}
|
||||
|
@ -345,9 +349,14 @@ public class CSourceFinder implements ISourceFinder, ILaunchConfigurationListene
|
|||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationRemoved(org.eclipse.debug.core.ILaunchConfiguration)
|
||||
*/
|
||||
public void launchConfigurationRemoved(ILaunchConfiguration config) {
|
||||
fConfigLocators.remove(config);
|
||||
if ((fLaunchLocator != null) && (fLaunchLocator.getLaunchConfiguration() == config)) {
|
||||
synchronized public void launchConfigurationRemoved(ILaunchConfiguration config) {
|
||||
// We don't care if it's a working copy.
|
||||
if (config.isWorkingCopy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fConfigLocators.remove(config.getName());
|
||||
if ((fLaunchLocator != null) && (fLaunchLocator.getLaunchConfiguration().getName() == config.getName())) {
|
||||
fLaunchLocator = null;
|
||||
}
|
||||
}
|
||||
|
@ -367,7 +376,7 @@ public class CSourceFinder implements ISourceFinder, ILaunchConfigurationListene
|
|||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.core.ILaunchesListener#launchesAdded(org.eclipse.debug.core.ILaunch[])
|
||||
*/
|
||||
synchronized public void launchesAdded(ILaunch[] launches) {
|
||||
public void launchesAdded(ILaunch[] launches) {
|
||||
// If there's a new launch in town, we need to take it into
|
||||
// consideration. E.g., if it targets our binary, and we're currently
|
||||
// searching using an inactive launch configuration's locator, then the
|
||||
|
@ -375,7 +384,9 @@ public class CSourceFinder implements ISourceFinder, ILaunchConfigurationListene
|
|||
for (ILaunch launch : launches) {
|
||||
ILaunchConfiguration config = launch.getLaunchConfiguration();
|
||||
if (config != null && isMatch(config)) {
|
||||
fLaunchLocator = null;
|
||||
synchronized(this) {
|
||||
fLaunchLocator = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,28 +11,44 @@
|
|||
|
||||
package org.eclipse.cdt.debug.internal.ui.views.executables;
|
||||
|
||||
import com.ibm.icu.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.debug.core.executables.Executable;
|
||||
import org.eclipse.cdt.debug.core.executables.ExecutablesManager;
|
||||
import org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jface.viewers.ColumnLabelProvider;
|
||||
import org.eclipse.jface.viewers.IStructuredContentProvider;
|
||||
import org.eclipse.jface.viewers.ITreeContentProvider;
|
||||
import org.eclipse.jface.viewers.TreeViewer;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.jface.viewers.ViewerCell;
|
||||
import org.eclipse.ui.progress.WorkbenchJob;
|
||||
|
||||
class ExecutablesContentProvider extends ColumnLabelProvider implements IStructuredContentProvider, ITreeContentProvider {
|
||||
import com.ibm.icu.text.DateFormat;
|
||||
|
||||
public ExecutablesContentProvider(TreeViewer viewer) {
|
||||
class ExecutablesContentProvider extends ColumnLabelProvider implements IStructuredContentProvider, ITreeContentProvider, IExecutablesChangeListener {
|
||||
|
||||
final private TreeViewer viewer;
|
||||
|
||||
public ExecutablesContentProvider(final TreeViewer viewer) {
|
||||
this.viewer = viewer;
|
||||
ExecutablesManager.getExecutablesManager().addExecutablesChangeListener(this);
|
||||
}
|
||||
|
||||
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.jface.viewers.BaseLabelProvider#dispose()
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
ExecutablesManager.getExecutablesManager().removeExecutablesChangeListener(this);
|
||||
}
|
||||
|
||||
public Object[] getElements(final Object inputElement) {
|
||||
|
@ -100,4 +116,28 @@ class ExecutablesContentProvider extends ColumnLabelProvider implements IStructu
|
|||
return new Object[] {};
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener#executablesListChanged()
|
||||
*/
|
||||
public void executablesListChanged() {
|
||||
new WorkbenchJob("execs list changed") { //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
viewer.refresh(null);
|
||||
if (viewer instanceof BaseViewer) {
|
||||
((BaseViewer)viewer).packColumns();
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener#executablesChanged(java.util.List)
|
||||
*/
|
||||
public void executablesChanged(List<Executable> executables) {
|
||||
// Our concern is only if the list of executables changed. The
|
||||
// content provider for the source files viewer will care about
|
||||
// whether the Executables themselves change
|
||||
}
|
||||
}
|
|
@ -58,7 +58,7 @@ import org.eclipse.ui.XMLMemento;
|
|||
import org.eclipse.ui.actions.ActionFactory;
|
||||
import org.eclipse.ui.dialogs.ListSelectionDialog;
|
||||
import org.eclipse.ui.part.ViewPart;
|
||||
import org.eclipse.ui.progress.UIJob;
|
||||
import org.eclipse.ui.progress.WorkbenchJob;
|
||||
|
||||
/**
|
||||
* ExecutablesView displays a list of executable files either in the workspace
|
||||
|
@ -176,6 +176,7 @@ public class ExecutablesView extends ViewPart {
|
|||
|
||||
class ColumnLabelProvider extends LabelProvider {
|
||||
|
||||
@Override
|
||||
public String getText(Object element) {
|
||||
return (String) element;
|
||||
}
|
||||
|
@ -193,6 +194,7 @@ public class ExecutablesView extends ViewPart {
|
|||
*
|
||||
* @see org.eclipse.jface.action.Action#run()
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
ListSelectionDialog dialog = new ListSelectionDialog(ExecutablesView.this.getExecutablesViewer().getTree().getShell(), this,
|
||||
new ColumnContentProvider(), new ColumnLabelProvider(), Messages.ExecutablesView_SelectColumns);
|
||||
|
@ -250,7 +252,6 @@ public class ExecutablesView extends ViewPart {
|
|||
// Create the two sub viewers.
|
||||
executablesViewer = new ExecutablesViewer(this, sashForm, SWT.FULL_SELECTION | SWT.BORDER | SWT.MULTI);
|
||||
focusedViewer = executablesViewer;
|
||||
ExecutablesManager.getExecutablesManager().addExecutablesChangeListener(executablesViewer);
|
||||
sourceFilesViewer = new SourceFilesViewer(this, sashForm, SWT.BORDER | SWT.MULTI);
|
||||
|
||||
executablesViewer.getTree().addFocusListener(new FocusListener() {
|
||||
|
@ -416,6 +417,7 @@ public class ExecutablesView extends ViewPart {
|
|||
private Action createRemoveAction() {
|
||||
Action action = new Action(Messages.ExecutablesView_Remove) {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ISelection selection = getExecutablesViewer().getSelection();
|
||||
if (selection instanceof IStructuredSelection)
|
||||
|
@ -432,8 +434,9 @@ public class ExecutablesView extends ViewPart {
|
|||
|
||||
if (confirm)
|
||||
{
|
||||
Job removeJob = new UIJob(Messages.ExecutablesView_RemoveExes) {
|
||||
Job removeJob = new WorkbenchJob(Messages.ExecutablesView_RemoveExes) {
|
||||
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
IStatus result = ExecutablesManager.getExecutablesManager().removeExecutables(selectedExesArray, monitor);
|
||||
if (result.getSeverity() != IStatus.OK)
|
||||
|
@ -490,6 +493,7 @@ public class ExecutablesView extends ViewPart {
|
|||
|
||||
private Action createImportAction() {
|
||||
Action action = new Action(Messages.ExecutablesView_Import) {
|
||||
@Override
|
||||
public void run() {
|
||||
FileDialog dialog = new FileDialog(getViewSite().getShell(), SWT.NONE);
|
||||
dialog.setText(Messages.ExecutablesView_SelectExeFile);
|
||||
|
@ -515,8 +519,10 @@ public class ExecutablesView extends ViewPart {
|
|||
|
||||
private Action createRefreshAction() {
|
||||
Action action = new Action(Messages.ExecutablesView_Refresh) {
|
||||
@Override
|
||||
public void run() {
|
||||
ExecutablesManager.getExecutablesManager().refresh(null);
|
||||
sourceFilesViewer.restartCanceledExecutableParse();
|
||||
}
|
||||
};
|
||||
action.setToolTipText(Messages.ExecutablesView_RefreshList);
|
||||
|
@ -574,7 +580,6 @@ public class ExecutablesView extends ViewPart {
|
|||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ExecutablesManager.getExecutablesManager().removeExecutablesChangeListener(executablesViewer);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,19 +10,12 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.debug.internal.ui.views.executables;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.debug.core.executables.Executable;
|
||||
import org.eclipse.cdt.debug.core.executables.ExecutablesManager;
|
||||
import org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jface.action.IMenuListener;
|
||||
import org.eclipse.jface.action.IMenuManager;
|
||||
import org.eclipse.jface.action.MenuManager;
|
||||
import org.eclipse.jface.action.Separator;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.jface.viewers.ViewerComparator;
|
||||
import org.eclipse.jface.viewers.ViewerDropAdapter;
|
||||
|
@ -35,12 +28,11 @@ import org.eclipse.swt.widgets.Composite;
|
|||
import org.eclipse.swt.widgets.Menu;
|
||||
import org.eclipse.swt.widgets.TreeColumn;
|
||||
import org.eclipse.ui.IWorkbenchActionConstants;
|
||||
import org.eclipse.ui.progress.UIJob;
|
||||
|
||||
/**
|
||||
* Displays the list of executables gathered by the ExecutablesManager
|
||||
*/
|
||||
public class ExecutablesViewer extends BaseViewer implements IExecutablesChangeListener {
|
||||
public class ExecutablesViewer extends BaseViewer {
|
||||
|
||||
private static final String P_COLUMN_ORDER_KEY_EXE = "columnOrderKeyEXE"; //$NON-NLS-1$
|
||||
private static final String P_SORTED_COLUMN_INDEX_KEY_EXE = "sortedColumnIndexKeyEXE"; //$NON-NLS-1$
|
||||
|
@ -155,6 +147,7 @@ public class ExecutablesViewer extends BaseViewer implements IExecutablesChangeL
|
|||
protected ViewerComparator getViewerComparator(int sortType) {
|
||||
if (sortType == ExecutablesView.PROJECT) {
|
||||
return new ExecutablesViewerComparator(sortType, column_sort_order[ExecutablesView.PROJECT]) {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public int compare(Viewer viewer, Object e1, Object e2) {
|
||||
Executable entry1 = (Executable) e1;
|
||||
|
@ -192,46 +185,4 @@ public class ExecutablesViewer extends BaseViewer implements IExecutablesChangeL
|
|||
// default visible columns
|
||||
return "1,1,1,0,0,0"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public void executablesChanged(final List<Executable> executables) {
|
||||
// some executables have been updated. if one of them is currently
|
||||
// selected, we need to update the source file list
|
||||
UIJob refreshJob = new UIJob(Messages.ExecutablesViewer_RefreshExecutablesView) {
|
||||
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
// if the user has selected an executable, they expect its
|
||||
// list of source files to be refreshed automatically
|
||||
if (getSelection() != null &&
|
||||
getSelection() instanceof IStructuredSelection) {
|
||||
IStructuredSelection selection = (IStructuredSelection)getSelection();
|
||||
|
||||
Object firstElement = selection.getFirstElement();
|
||||
if (firstElement instanceof Executable) {
|
||||
Executable executable = (Executable) firstElement;
|
||||
if (executables.contains(executable)) {
|
||||
executable.setRefreshSourceFiles(true);
|
||||
setSelection(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
};
|
||||
refreshJob.schedule();
|
||||
}
|
||||
|
||||
public void executablesListChanged() {
|
||||
// Executables list has changed so refresh the view.
|
||||
UIJob refreshJob = new UIJob(Messages.ExecutablesViewer_RefreshExecutablesView) {
|
||||
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
refresh(null);
|
||||
packColumns();
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
};
|
||||
refreshJob.schedule();
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ public class Messages extends NLS {
|
|||
public static String SourceFilesContentProvider_NoFilesFound;
|
||||
public static String SourceFilesContentProvider_ReadingDebugSymbolInformationLabel;
|
||||
public static String SourceFilesContentProvider_Refreshing;
|
||||
public static String SourceFilesContentProvider_Canceled;
|
||||
public static String SourceFilesViewer_RefreshSourceFiles;
|
||||
public static String SourceFilesViewer_Location;
|
||||
public static String SourceFilesViewer_Modified;
|
||||
|
|
|
@ -48,6 +48,7 @@ ExecutablesViewer_Type=Type
|
|||
SourceFilesContentProvider_NoFilesFound=No source files found in
|
||||
SourceFilesContentProvider_ReadingDebugSymbolInformationLabel=Reading Debug Symbol Information:
|
||||
SourceFilesContentProvider_Refreshing=Refreshing...
|
||||
SourceFilesContentProvider_Canceled=Parse canceled. Hit refresh to restart.
|
||||
SourceFilesViewer_RefreshSourceFiles=Refresh Source Files
|
||||
SourceFilesViewer_Location=Location
|
||||
SourceFilesViewer_Modified=Modified
|
||||
|
|
|
@ -18,7 +18,8 @@ import java.util.Map;
|
|||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.debug.core.executables.Executable;
|
||||
import org.eclipse.cdt.debug.core.executables.ExecutablesManager;
|
||||
import org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener;
|
||||
import org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener2;
|
||||
import org.eclipse.cdt.debug.internal.core.Trace;
|
||||
import org.eclipse.cdt.debug.internal.ui.views.executables.SourceFilesViewer.TranslationUnitInfo;
|
||||
import org.eclipse.cdt.ui.CElementContentProvider;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
|
@ -30,8 +31,9 @@ import org.eclipse.core.runtime.jobs.Job;
|
|||
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.ui.progress.WorkbenchJob;
|
||||
|
||||
public class SourceFilesContentProvider extends CElementContentProvider implements IExecutablesChangeListener {
|
||||
public class SourceFilesContentProvider extends CElementContentProvider implements IExecutablesChangeListener2 {
|
||||
|
||||
static class QuickParseJob extends Job {
|
||||
final Executable executable;
|
||||
|
@ -45,18 +47,65 @@ public class SourceFilesContentProvider extends CElementContentProvider implemen
|
|||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
tus = executable.getSourceFiles(monitor);
|
||||
return Status.OK_STATUS;
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Quick parsing of executable for source files has begun (" + this + ')'); //$NON-NLS-1$
|
||||
|
||||
// Ask the Executable for its source files. This could take a while...
|
||||
ITranslationUnit[] mytus = executable.getSourceFiles(monitor);
|
||||
|
||||
IStatus status;
|
||||
if (!monitor.isCanceled()) {
|
||||
tus = mytus;
|
||||
status = Status.OK_STATUS;
|
||||
}
|
||||
else {
|
||||
status = Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Quick parsing of executable has finished, status is " + status); //$NON-NLS-1$
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/** contains running jobs */
|
||||
/**
|
||||
* The collection of running file parsing jobs. Each executable file (not
|
||||
* object) can independently be parsed, and these parses can happen
|
||||
* simultaneously. Normally, each executable file has at most one ongoing
|
||||
* parse. An exception is when a search is canceled. We don't wait for the
|
||||
* search to actually end if a subsequent search comes in shortly after the
|
||||
* first one is canceled. We cancel the first one, remove it from this list,
|
||||
* schedule a new one, then add that to the list. It's safe to assume the
|
||||
* canceled one will complete before the new one.
|
||||
*
|
||||
* <p> This collection must be accessed only from the UI thread
|
||||
*/
|
||||
private Map<IPath, QuickParseJob> pathToJobMap = new HashMap<IPath, SourceFilesContentProvider.QuickParseJob>();
|
||||
|
||||
/** those executables for which we asked the question and got a result.
|
||||
* NOTE: this contains a duplicate of into in Executable, because we can't
|
||||
* guarantee or check whether Executable still has the info itself. */
|
||||
private Map<IPath, ITranslationUnit[]> fetchedExecutables = new HashMap<IPath, ITranslationUnit[]>();
|
||||
private static class TUData{
|
||||
/** Constructor used when search completes successfully */
|
||||
public TUData(ITranslationUnit[] tus, long timestamp) {
|
||||
this.tus = tus;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
/** Constructor used when search is canceled */
|
||||
public TUData() {
|
||||
this.canceled = true;
|
||||
}
|
||||
|
||||
ITranslationUnit[] tus;
|
||||
/** IResource.getModificationStamp value of when this data was last updated */
|
||||
long timestamp;
|
||||
|
||||
boolean canceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cached file info. Key is the path of the executable. This collection must be accessed only on the UI thread.
|
||||
*/
|
||||
private Map<IPath, TUData> fetchedExecutables = new HashMap<IPath, TUData>();
|
||||
|
||||
private final SourceFilesViewer viewer;
|
||||
|
||||
|
@ -72,12 +121,14 @@ public class SourceFilesContentProvider extends CElementContentProvider implemen
|
|||
@Override
|
||||
public void dispose() {
|
||||
ExecutablesManager.getExecutablesManager().removeExecutablesChangeListener(this);
|
||||
synchronized (fetchedExecutables) {
|
||||
fetchedExecutables.clear();
|
||||
}
|
||||
synchronized (pathToJobMap) {
|
||||
pathToJobMap.clear();
|
||||
}
|
||||
new WorkbenchJob("") { //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
fetchedExecutables.clear();
|
||||
pathToJobMap.clear();
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -92,63 +143,71 @@ public class SourceFilesContentProvider extends CElementContentProvider implemen
|
|||
return super.hasChildren(element);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.internal.ui.BaseCElementContentProvider#getElements(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object[] getElements(Object inputElement) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, inputElement);
|
||||
|
||||
if (inputElement instanceof Executable) {
|
||||
final Executable executable = (Executable) inputElement;
|
||||
final IPath exePath = executable.getPath();
|
||||
|
||||
// look for a job that is currently fetching this info
|
||||
QuickParseJob job;
|
||||
synchronized (pathToJobMap) {
|
||||
job = pathToJobMap.get(exePath);
|
||||
}
|
||||
job = pathToJobMap.get(exePath);
|
||||
if (job != null) {
|
||||
// job is still running
|
||||
return new String[] { Messages.SourceFilesContentProvider_Refreshing };
|
||||
}
|
||||
|
||||
// see if we already checked
|
||||
synchronized (fetchedExecutables) {
|
||||
if (fetchedExecutables.containsKey(exePath)) {
|
||||
return fetchedExecutables.get(exePath);
|
||||
}
|
||||
}
|
||||
|
||||
// start a background job to look for the sources
|
||||
// create a background job to look for the sources but don't start it yet
|
||||
job = new QuickParseJob(executable);
|
||||
synchronized (pathToJobMap) {
|
||||
pathToJobMap.put(exePath, job);
|
||||
pathToJobMap.put(exePath, job);
|
||||
|
||||
// See if we have the result cached for this executable. If so
|
||||
// return that. It's also possible that the most resent search was
|
||||
// canceled
|
||||
Object[] cachedResult = null;
|
||||
TUData tud = fetchedExecutables.get(exePath);
|
||||
if (tud != null) {
|
||||
if (tud.canceled)
|
||||
cachedResult = new String[]{Messages.SourceFilesContentProvider_Canceled};
|
||||
else
|
||||
cachedResult = tud.tus;
|
||||
}
|
||||
if (cachedResult != null) {
|
||||
pathToJobMap.remove(exePath); // removed the unused search job
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
// once the job finishes, update the viewer
|
||||
// Schedule the job; once it finishes, update the viewer
|
||||
final QuickParseJob theJob = job;
|
||||
job.addJobChangeListener(new JobChangeAdapter() {
|
||||
public void done(IJobChangeEvent event) {
|
||||
synchronized (pathToJobMap) {
|
||||
pathToJobMap.values().remove(theJob);
|
||||
}
|
||||
if (event.getResult().isOK()) {
|
||||
synchronized (fetchedExecutables) {
|
||||
fetchedExecutables.put(exePath, theJob.tus);
|
||||
}
|
||||
Display.getDefault().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
// update the viewer
|
||||
if (!viewer.getControl().isDisposed()) {
|
||||
viewer.getTree().setLayoutDeferred(true);
|
||||
viewer.refresh(executable);
|
||||
viewer.packColumns();
|
||||
viewer.getTree().setLayoutDeferred(false);
|
||||
}
|
||||
@Override
|
||||
public void done(final IJobChangeEvent event) {
|
||||
new WorkbenchJob("refreshing source files viewer"){ //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
if (event.getResult().isOK()) {
|
||||
fetchedExecutables.put(exePath, new TUData(theJob.tus, theJob.executable.getResource().getModificationStamp()));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
fetchedExecutables.put(exePath, new TUData());
|
||||
}
|
||||
|
||||
pathToJobMap.values().remove(theJob);
|
||||
|
||||
refreshViewer(executable);
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
job.schedule();
|
||||
|
||||
// while it's running...
|
||||
// show the user a string that lets him know we're searching
|
||||
return new String[] { Messages.SourceFilesContentProvider_Refreshing };
|
||||
}
|
||||
return new Object[] {};
|
||||
|
@ -159,46 +218,62 @@ public class SourceFilesContentProvider extends CElementContentProvider implemen
|
|||
* @see org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener#executablesListChanged()
|
||||
*/
|
||||
public void executablesListChanged() {
|
||||
// Don't clear executables -- closing/opening project doesn't imply
|
||||
// the info is different. But cancel all the jobs in case projects
|
||||
// were closed. It's non-obvious how to map executables to projects,
|
||||
// so just bail and cancel all the current parsing. The viewer
|
||||
// will be refreshed and re-request source lists for any executables
|
||||
// that are still applicable.
|
||||
cancelQuickParseJobs();
|
||||
// we react via IExecutablesChangeListener2 methods
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void cancelQuickParseJobs() {
|
||||
synchronized (pathToJobMap) {
|
||||
for (QuickParseJob job : pathToJobMap.values()) {
|
||||
job.cancel();
|
||||
}
|
||||
pathToJobMap.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener#executablesChanged(java.util.List)
|
||||
*/
|
||||
public void executablesChanged(List<Executable> executables) {
|
||||
for (Executable executable : executables) {
|
||||
IPath exePath = executable.getPath();
|
||||
synchronized (fetchedExecutables) {
|
||||
fetchedExecutables.remove(exePath);
|
||||
}
|
||||
synchronized (pathToJobMap) {
|
||||
QuickParseJob job = pathToJobMap.get(exePath);
|
||||
if (job != null) {
|
||||
job.cancel();
|
||||
pathToJobMap.remove(exePath);
|
||||
public void executablesChanged(final List<Executable> executables) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, executables);
|
||||
|
||||
new WorkbenchJob("Refreshing viewer") { //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
for (Executable executable : executables) {
|
||||
IPath exePath = executable.getPath();
|
||||
fetchedExecutables.remove(exePath);
|
||||
QuickParseJob job = pathToJobMap.get(exePath);
|
||||
if (job != null) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Cancelling QuickParseJob: " + job); //$NON-NLS-1$
|
||||
job.cancel();
|
||||
pathToJobMap.remove(exePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!viewer.getControl().isDisposed()) {
|
||||
// See if our current input is one of the executables that has changed.
|
||||
for (Executable executable : executables) {
|
||||
if (executable.equals(fInput)) {
|
||||
// Executable.equals() is not a simple reference
|
||||
// check. Two Executable objects are equal if they
|
||||
// represent the same file on disk. I.e., our input
|
||||
// object might not be one of the instances on the
|
||||
// changed-list, but for sure the file on disk has
|
||||
// changed. Now, the manager that called this
|
||||
// listener has already told the Executable
|
||||
// instances on the changed list to flush their
|
||||
// source files list. However, if our input is not
|
||||
// exactly one of those references, it means the
|
||||
// manager is no longer managing the Executable
|
||||
// that's our input. In that case, it's up to us to
|
||||
// tell that Executable to flush its source file
|
||||
// cache so that refreshing the viewer will cause a
|
||||
// fresh fetch of the source file information.
|
||||
Executable execInput = (Executable)fInput;
|
||||
if (executable != execInput) {
|
||||
execInput.setRefreshSourceFiles(true);
|
||||
}
|
||||
refreshViewer(execInput);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
}.schedule();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
@ -208,14 +283,178 @@ public class SourceFilesContentProvider extends CElementContentProvider implemen
|
|||
public void inputChanged(Viewer viewer, Object oldInput, final Object newInput) {
|
||||
super.inputChanged(viewer, oldInput, newInput);
|
||||
|
||||
Display.getDefault().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
new WorkbenchJob("Refreshing viewer") { //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
// pack because the quick parse job won't run
|
||||
if (newInput instanceof Executable
|
||||
&& fetchedExecutables.containsKey(((Executable) newInput).getPath()))
|
||||
SourceFilesContentProvider.this.viewer.packColumns();
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
});
|
||||
}.schedule();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener2#executablesAdded(java.util.List)
|
||||
*/
|
||||
public void executablesAdded(final List<Executable> executables) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, executables);
|
||||
|
||||
// Throw out our cached translation units for the executable *file* but
|
||||
// only if the file hasn't changed. Executable objects come and go
|
||||
// independently of the file on disk.
|
||||
new WorkbenchJob("executables removed") { //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
for (Executable exec : executables) {
|
||||
final IPath exePath = exec.getPath();
|
||||
final long timestamp = exec.getResource().getModificationStamp();
|
||||
TUData tud = fetchedExecutables.get(exePath);
|
||||
if (tud != null && tud.timestamp != timestamp) {
|
||||
fetchedExecutables.remove(exePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!viewer.getControl().isDisposed()) {
|
||||
// See if current viewer input is one of the executables that
|
||||
// was added. If so, this is likely an exec that was rebuilt
|
||||
// and CDT missed sending a REMOVED model event. There's
|
||||
// some crazy race condition going on, but basically CDT
|
||||
// sends out an event that the binary has changed, then
|
||||
// sends one that says it was added. Anyway, the best thing
|
||||
// for us to do is to cause a refresh of the viewer since
|
||||
// the addition notification probably caused us to cancel
|
||||
// the parse of the exec that was initiated by the change
|
||||
// event and the viewer will be stuck with a "canceled"
|
||||
// message in the viewer table.
|
||||
for (Executable executable : executables) {
|
||||
if (executable.equals(fInput)) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "refreshing viewer; added executable is our current input"); //$NON-NLS-1$
|
||||
refreshViewer((Executable)fInput);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener2#executablesRemoved(java.util.List)
|
||||
*/
|
||||
public void executablesRemoved(final List<Executable> executables) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, executables);
|
||||
|
||||
// The fact that the Executable was removed from the workspace doesn't
|
||||
// mean we need to throw out the source file info we've cached. If a
|
||||
// project is closed then reopened, we are able to reuse the info as
|
||||
// long as the timestamp of the resource hasn't changed. But, there's no
|
||||
// point in continuing any ongoing searches in the executables.
|
||||
new WorkbenchJob("executables removed") { //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
for (Executable exec : executables) {
|
||||
final IPath exePath = exec.getPath();
|
||||
QuickParseJob job = pathToJobMap.get(exePath);
|
||||
if (job != null) {
|
||||
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "Cancelling QuickParseJob: " + job); //$NON-NLS-1$
|
||||
job.cancel();
|
||||
pathToJobMap.remove(exePath);
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts a parse of the current input (Executable) if and only if its
|
||||
* last search was canceled. The viewer is refresh accordingly.
|
||||
*
|
||||
* <p>
|
||||
* Must be called on the UI thread
|
||||
*
|
||||
*/
|
||||
public void restartCanceledExecutableParse() {
|
||||
assert Display.getCurrent() != null;
|
||||
|
||||
Object input = viewer.getInput();
|
||||
if (input instanceof Executable) {
|
||||
final Executable executable = (Executable)input;
|
||||
final IPath exePath = executable.getPath();
|
||||
|
||||
// Ignore restart if there's an ongoing search.
|
||||
QuickParseJob job;
|
||||
job = pathToJobMap.get(exePath);
|
||||
if (job != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TUData tud = fetchedExecutables.get(exePath);
|
||||
|
||||
// Ignore request if the most recent search wasn't canceled
|
||||
if (tud != null && !tud.canceled) {
|
||||
pathToJobMap.remove(exePath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and schedule a parse job. Once the job finishes, update
|
||||
// the viewer
|
||||
job = new QuickParseJob(executable);
|
||||
pathToJobMap.put(exePath, job);
|
||||
final QuickParseJob theJob = job;
|
||||
job.addJobChangeListener(new JobChangeAdapter() {
|
||||
@Override
|
||||
public void done(final IJobChangeEvent event) {
|
||||
|
||||
new WorkbenchJob("refreshing source files viewer"){ //$NON-NLS-1$
|
||||
@Override
|
||||
public IStatus runInUIThread(IProgressMonitor monitor) {
|
||||
// Update the model with the search results
|
||||
if (event.getResult().isOK()) {
|
||||
fetchedExecutables.put(exePath, new TUData(theJob.tus, theJob.executable.getResource().getModificationStamp()));
|
||||
}
|
||||
else {
|
||||
// The search job apparently always completes
|
||||
// successfully or it was canceled (failure was
|
||||
// not a considered outcome). If it was canceled,
|
||||
// well then we're back to where we started
|
||||
fetchedExecutables.put(exePath, new TUData());
|
||||
}
|
||||
pathToJobMap.values().remove(theJob);
|
||||
|
||||
refreshViewer(executable);
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
});
|
||||
|
||||
job.schedule();
|
||||
|
||||
// The viewer is currently showing "search canceled". Cause an
|
||||
// immediate refresh so that it shows "refreshing" while the new
|
||||
// search is ongoing
|
||||
refreshViewer(executable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to invoke a viewer refresh for the given element
|
||||
* @param input the Executable to show content for
|
||||
*
|
||||
* <p> Must be called on the UI thread
|
||||
*/
|
||||
private void refreshViewer(Executable input) {
|
||||
if (!viewer.getControl().isDisposed()) {
|
||||
viewer.getTree().setLayoutDeferred(true);
|
||||
viewer.refresh(input);
|
||||
viewer.packColumns();
|
||||
viewer.getTree().setLayoutDeferred(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,35 +11,28 @@
|
|||
package org.eclipse.cdt.debug.internal.ui.views.executables;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.core.model.ICElement;
|
||||
import org.eclipse.cdt.core.model.ISourceReference;
|
||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
|
||||
import org.eclipse.cdt.debug.core.executables.Executable;
|
||||
import org.eclipse.cdt.debug.core.executables.ExecutablesManager;
|
||||
import org.eclipse.cdt.debug.core.executables.IExecutablesChangeListener;
|
||||
import org.eclipse.cdt.debug.internal.ui.sourcelookup.CSourceNotFoundEditorInput;
|
||||
import org.eclipse.cdt.debug.ui.ICDebugUIConstants;
|
||||
import org.eclipse.cdt.internal.core.util.LRUCache;
|
||||
import org.eclipse.cdt.internal.ui.util.EditorUtility;
|
||||
import org.eclipse.cdt.ui.CUIPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationListener;
|
||||
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
|
||||
import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
|
||||
import org.eclipse.jface.viewers.IOpenListener;
|
||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||
import org.eclipse.jface.viewers.OpenEvent;
|
||||
import org.eclipse.jface.viewers.Viewer;
|
||||
import org.eclipse.jface.viewers.ViewerComparator;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.DisposeEvent;
|
||||
import org.eclipse.swt.events.DisposeListener;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Tree;
|
||||
import org.eclipse.swt.widgets.TreeColumn;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
|
@ -50,7 +43,7 @@ import org.eclipse.ui.PartInitException;
|
|||
* Displays the list of source files for the executable selected in the
|
||||
* ExecutablesViewer.
|
||||
*/
|
||||
public class SourceFilesViewer extends BaseViewer implements ISourceLookupParticipant, ILaunchConfigurationListener {
|
||||
public class SourceFilesViewer extends BaseViewer {
|
||||
|
||||
/** Information from an ITranslationUnit for the various displayed columns */
|
||||
static class TranslationUnitInfo {
|
||||
|
@ -100,25 +93,21 @@ public class SourceFilesViewer extends BaseViewer implements ISourceLookupPartic
|
|||
openSourceFile(event);
|
||||
}
|
||||
});
|
||||
|
||||
// We implement ISourceLookupParticipant so we can listen for changes to
|
||||
// source lookup as this viewer shows both original and remapped
|
||||
// locations
|
||||
CDebugCorePlugin.getDefault().getCommonSourceLookupDirector().addParticipants(new ISourceLookupParticipant[] { this });
|
||||
|
||||
// We also look for launch configuration changes, since their source
|
||||
// locators are involved in source path remapping, too
|
||||
DebugPlugin.getDefault().getLaunchManager().addLaunchConfigurationListener(this);
|
||||
|
||||
sourceFilesTree.addDisposeListener(new DisposeListener() {
|
||||
|
||||
public void widgetDisposed(DisposeEvent e) {
|
||||
DebugPlugin.getDefault().getLaunchManager().removeLaunchConfigurationListener(SourceFilesViewer.this);
|
||||
|
||||
CDebugCorePlugin.getDefault().getCommonSourceLookupDirector().removeParticipants(
|
||||
new ISourceLookupParticipant[] { SourceFilesViewer.this });
|
||||
ExecutablesManager.getExecutablesManager().addExecutablesChangeListener(new IExecutablesChangeListener(){
|
||||
public void executablesListChanged() {
|
||||
// this doesn't directly affect us
|
||||
}
|
||||
});
|
||||
|
||||
public void executablesChanged(List<Executable> executables) {
|
||||
// TODO: be more selective; we don't know what TUs go with which executables yet
|
||||
flushTranslationUnitCache();
|
||||
|
||||
// Note that we don't invoke a viewer refresh. Our content
|
||||
// provider needs to also be listening for this notification.
|
||||
// It's up to him to invoke a refresh on us if the model has
|
||||
// been affected by the Executable change
|
||||
}});
|
||||
}
|
||||
|
||||
private void openSourceFile(OpenEvent event) {
|
||||
|
@ -198,10 +187,12 @@ public class SourceFilesViewer extends BaseViewer implements ISourceLookupPartic
|
|||
typeColumn.addSelectionListener(new ColumnSelectionAdapter(ExecutablesView.TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewerComparator getViewerComparator(int sortType) {
|
||||
if (sortType == ExecutablesView.ORG_LOCATION) {
|
||||
return new ExecutablesViewerComparator(sortType, column_sort_order[ExecutablesView.ORG_LOCATION]) {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public int compare(Viewer viewer, Object e1, Object e2) {
|
||||
if (e1 instanceof ITranslationUnit && e2 instanceof ITranslationUnit) {
|
||||
|
@ -219,40 +210,6 @@ public class SourceFilesViewer extends BaseViewer implements ISourceLookupPartic
|
|||
return new ExecutablesViewerComparator(sortType, column_sort_order[sortType]);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public Object[] findSourceElements(Object object) throws CoreException {
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
public String getSourceName(Object object) throws CoreException {
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public void init(ISourceLookupDirector director) {
|
||||
}
|
||||
|
||||
public void sourceContainersChanged(ISourceLookupDirector director) {
|
||||
refreshContent();
|
||||
}
|
||||
|
||||
private void refreshContent() {
|
||||
Display.getDefault().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
Object input = getInput();
|
||||
if (input != null && input instanceof Executable) {
|
||||
((Executable)input).setRemapSourceFiles(true);
|
||||
|
||||
// TODO: be more selective; we don't know what TUs go with which executables yet
|
||||
flushTranslationUnitCache();
|
||||
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getColumnOrderKey() {
|
||||
return P_COLUMN_ORDER_KEY_SF;
|
||||
|
@ -279,34 +236,6 @@ public class SourceFilesViewer extends BaseViewer implements ISourceLookupPartic
|
|||
return "1,1,0,0,0,0"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationAdded(org.eclipse.debug.core.ILaunchConfiguration)
|
||||
*/
|
||||
public void launchConfigurationAdded(ILaunchConfiguration configuration) {
|
||||
if (!configuration.isWorkingCopy()) {
|
||||
refreshContent();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationChanged(org.eclipse.debug.core.ILaunchConfiguration)
|
||||
*/
|
||||
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
|
||||
if (!configuration.isWorkingCopy()) {
|
||||
refreshContent();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.core.ILaunchConfigurationListener#launchConfigurationRemoved(org.eclipse.debug.core.ILaunchConfiguration)
|
||||
*/
|
||||
public void launchConfigurationRemoved(ILaunchConfiguration configuration) {
|
||||
if (!configuration.isWorkingCopy()) {
|
||||
refreshContent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static TranslationUnitInfo fetchTranslationUnitInfo(Executable executable, Object element) {
|
||||
if (!(element instanceof ITranslationUnit)) {
|
||||
return null;
|
||||
|
@ -359,4 +288,17 @@ public class SourceFilesViewer extends BaseViewer implements ISourceLookupPartic
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* The view's refresh action calls this to restart an executable parse for
|
||||
* the current input if the most recent search (for that element) was
|
||||
* canceled. If it wasn't canceled, this is a no-op.
|
||||
*/
|
||||
public void restartCanceledExecutableParse() {
|
||||
SourceFilesContentProvider provider = (SourceFilesContentProvider)getContentProvider();
|
||||
if (provider != null) {
|
||||
provider.restartCanceledExecutableParse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue