1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-06 17:26:01 +02:00

Bug 530443: Add support for "info sources" MI equivalent

*Implementation of -file-list-exec-source-files MI command.
*Add new Debug Sources view with tree-like structure

Change-Id: I6e734799712c059c8e53aa882777dfebd85aa0d5
Also-by: Jonah Graham <jonah@kichwacoders.com>
Signed-off-by: Baha El Kassaby <baha.elkassaby@gmail.com>
This commit is contained in:
Baha El Kassaby 2018-05-02 21:52:39 +02:00 committed by Jonah Graham
parent 7e267a4791
commit 949efc6572
38 changed files with 2313 additions and 23 deletions

View file

@ -37,6 +37,8 @@ Export-Package: org.eclipse.cdt.dsf.gdb.internal.ui;x-friends:="org.eclipse.cdt.
org.eclipse.cdt.dsf.gdb.internal.ui.commands;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.ui.console;x-friends:="org.eclipse.cdt.examples.dsf.gdb",
org.eclipse.cdt.dsf.gdb.internal.ui.console.actions;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.ui.disassembly;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.ui.launching;x-friends:="org.eclipse.cdt.debug.gdbjtag.ui,org.eclipse.cdt.examples.dsf.gdb,org.eclipse.cdt.docker.launcher",
org.eclipse.cdt.dsf.gdb.internal.ui.memory;x-internal:=true,

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

View file

@ -36,6 +36,7 @@ tracepoints.action.page.label=Actions
tracepointActionsPrefPage.name=Tracepoint Actions\u0020
dynamicPrintf.property.common=Common
action.addDynamicPrintf.label=Add Dynamic-Printf...
debugSourcesRefresh.name=Refresh
# Tracepoints
view.traceControl.name=Trace Control
@ -59,6 +60,7 @@ action.fetchMoreChildren.label=Fetch More Children
# OS view
view.osresources.name=OS Resources
view.debugsources.name=Debug Sources
command.connect.description = Connect to selected processes
command.connect.name = Connect

View file

@ -296,6 +296,14 @@
name="%view.osresources.name"
icon="icons/full/view16/osresources_view.gif">
</view>
<view
category="org.eclipse.debug.ui"
class="org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesView"
icon="icons/full/view16/debugsources_view.gif"
id="org.eclipse.cdt.dsf.gdb.ui.debugsources.view"
name="%view.debugsources.name"
restorable="true">
</view>
</extension>
<extension
point="org.eclipse.ui.perspectiveExtensions">
@ -327,6 +335,18 @@
id="org.eclipse.cdt.debug.ui.debuggerConsoleView">
</viewShortcut>
</perspectiveExtension>
<perspectiveExtension
targetID="org.eclipse.debug.ui.DebugPerspective">
<view
relative="org.eclipse.ui.console.ConsoleView"
visible="false"
relationship="stack"
id="org.eclipse.cdt.dsf.gdb.ui.debugsources.view">
</view>
<viewShortcut
id="org.eclipse.cdt.dsf.gdb.ui.debugsources.view">
</viewShortcut>
</perspectiveExtension>
</extension>
<extension
point="org.eclipse.ui.commands">
@ -630,4 +650,25 @@
name="GDB Remote Serial">
</wizard2>
</extension>
<extension
point="org.eclipse.debug.ui.contextViewBindings">
<contextViewBinding
autoOpen="false"
contextId="org.eclipse.cdt.debug.ui.debugging"
viewId="org.eclipse.cdt.dsf.gdb.ui.debugsources.view">
</contextViewBinding>
</extension>
<extension point="org.eclipse.ui.viewActions">
<viewContribution
id="org.eclipse.cdt.dsf.gdb.ui.debugsources.view.refresh"
targetID="org.eclipse.cdt.dsf.gdb.ui.debugsources.view">
<action
class="org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions.DebugSourcesViewRefresh"
icon="icons/refresh.gif"
id="org.eclipse.cdt.dsf.gdb.ui.debugsources.view.refresh"
label="%debugSourcesRefresh.name"
toolbarPath="additions">
</action>
</viewContribution>
</extension>
</plugin>

View file

@ -0,0 +1,119 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
import java.io.File;
import java.util.Set;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesTreeElement.FileExist;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;
public class DebugSourcesLabelProvider extends ColumnLabelProvider {
private int index;
private boolean flattenFoldersWithNoFiles = true;
private boolean showExistingFilesOnly = true;
/**
*
* @param index
* of column
*/
public DebugSourcesLabelProvider(int index) {
this.index = index;
}
public void setFlattenFoldersWithNoFiles(boolean flattenFoldersWithNoFiles) {
this.flattenFoldersWithNoFiles = flattenFoldersWithNoFiles;
}
public boolean isFlattenFoldersWithNoFiles() {
return flattenFoldersWithNoFiles;
}
public void setShowExistingFilesOnly(boolean showExistingFilesOnly) {
this.showExistingFilesOnly = showExistingFilesOnly;
}
public boolean isShowExistingFilesOnly() {
return showExistingFilesOnly;
}
@Override
public String getText(Object element) {
return getLabel(element, index);
}
private String getLabel(Object element, int columnIdx) {
String emptyString = ""; //$NON-NLS-1$
if (element instanceof DebugSourcesTreeElement) {
DebugSourcesTreeElement node = (DebugSourcesTreeElement) element;
if (columnIdx == 0) {
Set<DebugSourcesTreeElement> children;
StringBuilder sb = new StringBuilder();
sb.append(node.getName());
if (flattenFoldersWithNoFiles) {
while (true) {
if (node.getFullPath() != null) {
break;
}
children = node.getChildren(showExistingFilesOnly);
if (children.size() != 1) {
break;
}
DebugSourcesTreeElement child = children.iterator().next();
if (child.getFullPath() != null) {
break;
}
node = child;
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != File.separatorChar) {
sb.append(File.separatorChar);
}
sb.append(node.getName());
}
}
return sb.toString();
}
if (columnIdx == 1) {
return node.hasChildren() ? emptyString : (String) node.getFullPath();
}
}
return emptyString;
}
@Override
public Color getForeground(Object element) {
if (index == 1) {
if (element instanceof DebugSourcesTreeElement) {
DebugSourcesTreeElement node = (DebugSourcesTreeElement) element;
if (node.getExists() == FileExist.NO) {
return Display.getDefault().getSystemColor(SWT.COLOR_GRAY);
}
}
}
return super.getForeground(element);
}
@Override
public Font getFont(Object element) {
// TODO Auto-generated method stub
return super.getFont(element);
}
}

View file

@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
import org.eclipse.osgi.util.NLS;
/**
*
*/
public class DebugSourcesMessages extends NLS {
public static String DebugSourcesMessages_name_column;
public static String DebugSourcesMessages_path_column;
public static String DebugSourcesMessages_unknown;
public static String DebugSourcesMessages_filter_search_tooltip;
public static String DebugSourcesMessages_sort_name_column_tooltip;
public static String DebugSourcesMessages_sort_path_column_tooltip;
public static String DebugSourcesExpandAction_name;
public static String DebugSourcesExpandAction_description;
public static String DebugSourcesCollapseAction_name;
public static String DebugSourcesCollapseAction_description;
public static String DebugSourcesFlattendedTree_name;
public static String DebugSourcesFlattendedTree_description;
public static String DebugSourcesNormalTree_description;
public static String DebugSourcesNormalTree_name;
public static String DebugSourcesShowExistingFilesOnly_description;
public static String DebugSourcesShowExistingFilesOnly_name;
public static String DebugSourcesView_unrooted;
public static String GdbDebugSourcesPreferences_name;
static {
// initialize resource bundle
NLS.initializeMessages(DebugSourcesMessages.class.getName(), DebugSourcesMessages.class);
}
private DebugSourcesMessages() {
}
}

View file

@ -0,0 +1,35 @@
##########################################################################
# Copyright (c) 2018, 2019 Kichwa Coders and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
# which accompanies this distribution, and is available at
# https://www.eclipse.org/legal/epl-2.0/
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Baha El-Kassaby - Initial API and implementation
##########################################################################
DebugSourcesMessages_name_column=Name
DebugSourcesMessages_path_column=Path
DebugSourcesMessages_unknown=Unknown
DebugSourcesMessages_filter_search_tooltip=Search on debug sources file names
DebugSourcesMessages_sort_name_column_tooltip=Left-click on the \"Name\" column to sort by name
DebugSourcesMessages_sort_path_column_tooltip=Left-click on the \"Path\" column to sort by path
DebugSourcesExpandAction_name=Expand
DebugSourcesExpandAction_description=Expand the tree structure
DebugSourcesCollapseAction_name=Collapse
DebugSourcesCollapseAction_description=Collapse the tree structure
DebugSourcesFlattendedTree_name=Flat View
DebugSourcesFlattendedTree_description=Display tree by flattening folders with no files.
DebugSourcesNormalTree_description=Normal tree view
DebugSourcesNormalTree_name=Normal View
DebugSourcesShowExistingFilesOnly_description=Show only files that are found on disk.
DebugSourcesShowExistingFilesOnly_name=Show
DebugSourcesView_unrooted=<unrooted>
GdbDebugSourcesPreferences_name=Preferences...

View file

@ -0,0 +1,105 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
import java.util.Set;
import org.eclipse.jface.viewers.ITreeContentProvider;
public class DebugSourcesTreeContentProvider implements ITreeContentProvider {
private boolean flattenFoldersWithNoFiles = true;
private boolean showExistingFilesOnly = true;
public DebugSourcesTreeContentProvider() {
}
public void setFlattenFoldersWithNoFiles(boolean flattenFoldersWithNoFiles) {
this.flattenFoldersWithNoFiles = flattenFoldersWithNoFiles;
}
public boolean isFlattenFoldersWithNoFiles() {
return flattenFoldersWithNoFiles;
}
public void setShowExistingFilesOnly(boolean showExistingFilesOnly) {
this.showExistingFilesOnly = showExistingFilesOnly;
}
public boolean isShowExistingFilesOnly() {
return showExistingFilesOnly;
}
@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof DebugSourcesTreeElement) {
DebugSourcesTreeElement tree = (DebugSourcesTreeElement) inputElement;
Set<DebugSourcesTreeElement> children = tree.getChildren(showExistingFilesOnly);
return children.toArray();
}
return null;
}
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof DebugSourcesTreeElement) {
DebugSourcesTreeElement tree = (DebugSourcesTreeElement) parentElement;
Set<DebugSourcesTreeElement> children = tree.getChildren(showExistingFilesOnly);
if (flattenFoldersWithNoFiles) {
if (children.size() == 1) {
DebugSourcesTreeElement child = children.iterator().next();
if (child.getFullPath() == null) {
return getChildren(child);
}
}
}
return children.toArray();
}
return null;
}
@Override
public Object getParent(Object element) {
if (element == null)
return null;
if (element instanceof DebugSourcesTreeElement) {
DebugSourcesTreeElement node = (DebugSourcesTreeElement) element;
DebugSourcesTreeElement parent = node.getParent();
if (parent == null) {
return null;
}
if (flattenFoldersWithNoFiles) {
DebugSourcesTreeElement grandParent = parent.getParent();
if (grandParent != null) {
Set<DebugSourcesTreeElement> children = grandParent.getChildren(showExistingFilesOnly);
if (children.size() == 1) {
return getParent(parent);
}
}
}
return parent;
}
return null;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof DebugSourcesTreeElement) {
return getChildren(element).length > 0;
}
return false;
}
}

View file

@ -0,0 +1,220 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A basic tree used to display source files in the debug view
*/
public class DebugSourcesTreeElement {
public enum FileExist {
YES, NO, UNKNOWN;
}
// preserve insertion order with LinkedHashSet
private final Set<DebugSourcesTreeElement> children = new LinkedHashSet<>();
private final String name;
private final String fullPath;
private DebugSourcesTreeElement parent;
private FileExist exists;
/**
*
* @param name part of the path
* @param exists
*/
public DebugSourcesTreeElement(String name, FileExist exists) {
this.name = name;
this.fullPath = null;
this.exists = exists;
}
/**
* If node is a leaf, add the fullPath as well
*
* @param name part of the path
* @param leafData full path of the file
*/
private DebugSourcesTreeElement(String name, String fullPath, FileExist exist) {
this.name = name;
this.fullPath = fullPath;
this.exists = exist;
}
/**
*
* @param name part of the path
* @param exists
* @return a new node if not already existing, existing node otherwise
*/
public DebugSourcesTreeElement addNode(String name, FileExist exists) {
for (DebugSourcesTreeElement child : children) {
if (child.name.equals(name)) {
if (exists == FileExist.YES) {
child.exists = FileExist.YES;
}
return child;
}
}
return addChild(new DebugSourcesTreeElement(name, exists));
}
/**
*
* @param name part of the path
* @param fullPath of leaf
* @return a new leaf if not already existing, existing leaf otherwise
*/
public DebugSourcesTreeElement addLeaf(String name, String fullPath, FileExist exists) {
for (DebugSourcesTreeElement child : children) {
if (child.name.equals(name)) {
return child;
}
}
return addChild(new DebugSourcesTreeElement(name, fullPath, exists));
}
private DebugSourcesTreeElement addChild(DebugSourcesTreeElement child) {
children.add(child);
return child;
}
/**
*
* @return list of children of the node
*/
public Set<DebugSourcesTreeElement> getChildren() {
return children;
}
/**
*
* @param filesThatMayExistOnly only include files that may exist
* @return list of children of the node
*/
public Set<DebugSourcesTreeElement> getChildren(boolean filesThatMayExistOnly) {
if (filesThatMayExistOnly) {
return children.stream().filter(c -> c.getExists() != FileExist.NO).collect(Collectors.toSet());
} else {
return children;
}
}
/**
*
* @return true if node has children, false otherwise
*/
public boolean hasChildren() {
if (children != null && children.size() > 0)
return true;
return false;
}
/**
*
* @return name of the file or folder segment
*/
public String getName() {
return name;
}
/**
*
* @return full path to file (or null if not a file)
*/
public String getFullPath() {
return fullPath;
}
/**
* Return true for leaf data that really exists on disk.
*
* This can be used to display differently.
*/
public FileExist getExists() {
return exists;
}
/**
*
* @return node parent
*/
public DebugSourcesTreeElement getParent() {
return parent;
}
/**
* Set parent of node
*
* @param parent
*/
public void setParent(DebugSourcesTreeElement parent) {
this.parent = parent;
}
public void setExist(FileExist exists) {
this.exists = exists;
}
@Override
public String toString() {
return Objects.toString(getName());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((exists == null) ? 0 : exists.hashCode());
result = prime * result + ((fullPath == null) ? 0 : fullPath.hashCode());
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DebugSourcesTreeElement other = (DebugSourcesTreeElement) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (exists != other.exists)
return false;
if (fullPath == null) {
if (other.fullPath != null)
return false;
} else if (!fullPath.equals(other.fullPath))
return false;
if (parent == null) {
if (other.parent != null)
return false;
} else if (!parent.equals(other.parent))
return false;
return true;
}
}

View file

@ -0,0 +1,736 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Tracy Miranda / Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Set;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceNotFoundElement;
import org.eclipse.cdt.debug.internal.core.sourcelookup.ICSourceNotFoundDescription;
import org.eclipse.cdt.debug.internal.ui.sourcelookup.CSourceNotFoundEditorInput;
import org.eclipse.cdt.debug.ui.ICDebugUIConstants;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesTreeElement.FileExist;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions.DebugSourcesCollapseAction;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions.DebugSourcesExpandAction;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions.DebugSourcesFlattendedTree;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions.DebugSourcesNormalTree;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions.DebugSourcesShowExistingFilesOnly;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles.IDebugSourceFileInfo;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles.IDebugSourceFilesChangedEvent;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.ViewPart;
@SuppressWarnings("restriction")
public class DebugSourcesView extends ViewPart implements IDebugContextListener {
public static final String ID = "org.eclipse.cdt.dsf.gdb.ui.debugsources.view"; //$NON-NLS-1$
private static final String KEY_FLATTEN_FOLDERS_WITH_NO_FILES = "KEY_FLATTEN_FOLDERS_WITH_NO_FILES"; //$NON-NLS-1$
private static final String KEY_SHOW_EXISTING_FILES_ONLY = "KEY_SHOW_EXISTING_FILES_ONLY"; //$NON-NLS-1$
private DsfSession fSession;
private TreeViewer viewer;
private DebugSourcesViewComparator<DebugSourcesTreeElement> comparator;
private IContainerDMContext dmcontext;
private DebugSourcesTreeElement debugTree;
private IMemento fMemento;
public DebugSourcesView() {
}
@Override
public void createPartControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(1, false));
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final DebugPatternFilter filter = new DebugPatternFilter();
int treeStyle = SWT.MULTI | SWT.FULL_SELECTION | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
FilteredTree tree = new FilteredTree(composite, treeStyle, filter, true, true);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
tree.getFilterControl().setToolTipText(DebugSourcesMessages.DebugSourcesMessages_filter_search_tooltip);
viewer = tree.getViewer();
viewer.getTree().setLinesVisible(true);
viewer.getTree().setHeaderVisible(true);
viewer.setContentProvider(new DebugSourcesTreeContentProvider());
viewer.setUseHashlookup(true);
comparator = new DebugSourcesViewComparator<>();
createColumns(viewer);
loadState();
viewer.setComparator(comparator);
viewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection thisSelection = (IStructuredSelection) event.getSelection();
Object selectedNode = thisSelection.getFirstElement();
if (selectedNode instanceof DebugSourcesTreeElement) {
DebugSourcesTreeElement node = (DebugSourcesTreeElement) selectedNode;
// only leafs can be opened!
if (!node.hasChildren()) {
openSourceFile(node.getFullPath());
}
}
}
});
createActions(viewer);
registerForEvents();
DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow())
.addDebugContextListener(this);
}
@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
fMemento = memento;
super.init(site, memento);
}
public void loadState() {
boolean flattenFoldersWithNoFiles = true;
boolean showExistingFilesOnly = true;
if (fMemento != null) {
Boolean b = fMemento.getBoolean(KEY_FLATTEN_FOLDERS_WITH_NO_FILES);
if (b != null) {
flattenFoldersWithNoFiles = b;
}
b = fMemento.getBoolean(KEY_SHOW_EXISTING_FILES_ONLY);
if (b != null) {
showExistingFilesOnly = b;
}
}
if (viewer != null) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer
.getContentProvider();
contentProvider.setFlattenFoldersWithNoFiles(flattenFoldersWithNoFiles);
contentProvider.setShowExistingFilesOnly(showExistingFilesOnly);
for (int i = 0; i < viewer.getTree().getColumnCount(); i++) {
DebugSourcesLabelProvider labelProvider = (DebugSourcesLabelProvider) viewer.getLabelProvider(i);
labelProvider.setFlattenFoldersWithNoFiles(flattenFoldersWithNoFiles);
labelProvider.setShowExistingFilesOnly(showExistingFilesOnly);
}
}
}
@Override
public void saveState(IMemento memento) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer.getContentProvider();
memento.putBoolean(KEY_FLATTEN_FOLDERS_WITH_NO_FILES, contentProvider.isFlattenFoldersWithNoFiles());
memento.putBoolean(KEY_SHOW_EXISTING_FILES_ONLY, contentProvider.isShowExistingFilesOnly());
super.saveState(memento);
}
private void createColumns(TreeViewer viewer) {
String[] titles = { DebugSourcesMessages.DebugSourcesMessages_name_column,
DebugSourcesMessages.DebugSourcesMessages_path_column };
String[] tooltips = { DebugSourcesMessages.DebugSourcesMessages_sort_name_column_tooltip,
DebugSourcesMessages.DebugSourcesMessages_sort_path_column_tooltip };
int[] bounds = { 300, 800 };
ColumnViewerToolTipSupport.enableFor(viewer);
for (int i = 0; i < titles.length; i++) {
TreeViewerColumn tc = createTreeViewerColumn(viewer, titles[i], bounds[i], i);
tc.getColumn().setToolTipText(tooltips[i]);
tc.setLabelProvider(new DebugSourcesLabelProvider(i));
}
}
private TreeViewerColumn createTreeViewerColumn(TreeViewer viewer, String title, int bound, final int colNumber) {
final TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
final TreeColumn column = viewerColumn.getColumn();
column.setText(title);
column.setWidth(bound);
column.setResizable(true);
column.setMoveable(true);
if (colNumber == 0)
comparator.setColumn(e -> e.getName(), colNumber);
if (colNumber == 1)
comparator.setColumn(e -> e.getFullPath() != null ? e.getFullPath() : e.getName(), colNumber);
column.addSelectionListener(getSelectionAdapter(column, colNumber));
return viewerColumn;
}
private SelectionAdapter getSelectionAdapter(final TreeColumn column, final int index) {
SelectionAdapter selectionAdapter = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (index == 0)
comparator.setColumn(e1 -> e1.getName(), index);
if (index == 1)
comparator.setColumn(e1 -> e1.getFullPath() != null ? e1.getFullPath() : e1.getName(), index);
int dir = comparator.getDirection();
viewer.getTree().setSortDirection(dir);
viewer.getTree().setSortColumn(column);
viewer.refresh();
// This is due to the tree collapsing to level 2 at every comparison
viewer.expandAll();
}
};
return selectionAdapter;
}
private void createActions(TreeViewer viewer) {
IActionBars actionBars = getViewSite().getActionBars();
IToolBarManager toolBar = actionBars.getToolBarManager();
toolBar.add(new DebugSourcesExpandAction(viewer));
toolBar.add(new DebugSourcesCollapseAction(viewer));
toolBar.add(new DebugSourcesFlattendedTree(viewer));
toolBar.add(new DebugSourcesNormalTree(viewer));
toolBar.add(new DebugSourcesShowExistingFilesOnly(viewer));
}
private DsfSession getSession() {
if (viewer == null || viewer.getControl().isDisposed()) {
return null;
}
// Get the debug selection to know what the user is looking at in the Debug view
IAdaptable context = DebugUITools.getDebugContext();
if (context == null) {
return null;
}
// Extract the data model context to use with the DSF services
IDMContext dmcontext = context.getAdapter(IDMContext.class);
if (dmcontext == null) {
// Not dealing with a DSF session
return null;
}
// Extract DSF session id from the DM context
String sessionId = dmcontext.getSessionId();
// Get the full DSF session to have access to the DSF executor
DsfSession session = DsfSession.getSession(sessionId);
if (session == null) {
// It could be that this session is no longer active
return null;
}
if (!session.isActive() || session.getExecutor().isShutdown()) {
return null;
}
return session;
}
private void asyncExecRegisterForEvents() {
if (getSite() == null || getSite().getShell() == null || getSite().getShell().getDisplay() == null
|| getSite().getShell().getDisplay().isDisposed()) {
return;
}
getSite().getShell().getDisplay().asyncExec(this::registerForEvents);
}
private void registerForEvents() {
DsfSession session = getSession();
if (session == null) {
return;
}
// Get the debug selection to know what the user is looking at in the Debug view
IAdaptable context = DebugUITools.getDebugContext();
if (context == null) {
return;
}
// Extract the data model context to use with the DSF services
IDMContext dmcontext = context.getAdapter(IDMContext.class);
if (dmcontext == null) {
// Not dealing with a DSF session
return;
}
registerForEvents(session);
// Show the current frame if there is one
displaySourceFiles(session, dmcontext);
}
@Override
public void setFocus() {
viewer.getControl().setFocus();
}
@Override
public void dispose() {
super.dispose();
viewer.getControl().dispose();
DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow())
.removeDebugContextListener(this);
if (fSession != null) {
DsfSession lastSession = fSession;
if (!lastSession.getExecutor().isShutdown()) {
lastSession.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
lastSession.removeServiceEventListener(DebugSourcesView.this);
}
});
}
}
}
/**
* This method registers with the specified session to receive DSF events.
*
* @param session
* The session for which we want to receive events
*/
private void registerForEvents(DsfSession session) {
if (session != null) {
if (fSession != session) {
if (fSession != null) {
DsfSession lastSession = fSession;
if (!lastSession.getExecutor().isShutdown()) {
lastSession.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
lastSession.removeServiceEventListener(DebugSourcesView.this);
}
});
}
}
fSession = session;
fSession.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
fSession.addServiceEventListener(DebugSourcesView.this, null);
}
});
}
}
}
private void displaySourceFiles(DsfSession session, IDMContext dmcontext) {
if (session.getExecutor().isShutdown()) {
// can't do anything
return;
}
IContainerDMContext containerDMContext = DMContexts.getAncestorOfType(dmcontext, IContainerDMContext.class);
if (containerDMContext == null || Objects.equals(containerDMContext, this.dmcontext)) {
return;
}
this.dmcontext = containerDMContext;
session.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
IDebugSourceFiles srcService = tracker.getService(IDebugSourceFiles.class);
// Don't forgot to dispose of a tracker before it goes out of scope
tracker.dispose();
if (srcService == null) {
// service not available. The debug session
// is probably terminating.
return;
}
// Get the full DSF session to have access to the DSF executor
srcService.getSources(containerDMContext,
new DataRequestMonitor<IDebugSourceFileInfo[]>(session.getExecutor(), null) {
@Override
protected void handleSuccess() {
// The service called 'handleSuccess()' so we know there is no error.
IDebugSourceFileInfo[] srcFileInfo = getData();
// We have a frame context. It is just a 'pointer' though.
// We need to get the data associated with it.
// Populate the tree synchronously
PopulateTreeJob populateTreeJob = new PopulateTreeJob(srcFileInfo);
CheckFileExistenceJob checkFileExistenceJob = new CheckFileExistenceJob();
populateTreeJob.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
debugTree = populateTreeJob.getTree();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (!viewer.getControl().isDisposed()) {
viewer.setInput(debugTree);
}
}
});
if (checkFileExistenceJob.getState() == Job.RUNNING)
checkFileExistenceJob.cancel();
checkFileExistenceJob.schedule();
}
});
checkFileExistenceJob.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (!viewer.getControl().isDisposed()) {
viewer.refresh();
}
}
});
}
});
// return all populate and file check jobs already running and cancel them.
IJobManager jobMan = Job.getJobManager();
Job[] populateJobS = jobMan.find(POPULATE_FAMILY);
for (Job job : populateJobS) {
job.cancel();
}
Job[] fileCheckJobS = jobMan.find(FILECHECK_FAMILY);
for (Job job : fileCheckJobS) {
job.cancel();
}
populateTreeJob.schedule();
}
@Override
protected void handleError() {
// Ignore errors when we select elements
// that don't contain frames
}
});
}
});
}
// This method must be public for the DSF callback to be found
@DsfServiceEventHandler
public void eventReceived(ISuspendedDMEvent event) {
asyncExecRegisterForEvents();
}
// This method must be public for the DSF callback to be found
@DsfServiceEventHandler
public void eventReceived(IDebugSourceFilesChangedEvent event) {
asyncExecRegisterForEvents();
}
public boolean canRefresh() {
return getSession() != null;
}
public void refresh() {
this.dmcontext = null; // force the refresh
DsfSession session = getSession();
if (session == null) {
return;
}
session.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
IDebugSourceFiles srcService = tracker.getService(IDebugSourceFiles.class);
// Don't forgot to dispose of a tracker before it goes out of scope
tracker.dispose();
if (srcService instanceof ICachingService) {
ICachingService cache = (ICachingService) srcService;
cache.flushCache(dmcontext);
}
}
});
}
@Override
public void debugContextChanged(DebugContextEvent event) {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
registerForEvents();
}
}
private void openSourceFile(String fullPath) {
if (fullPath == null) {
return;
}
Path path = Paths.get(fullPath);
boolean exists = Files.exists(path);
IEditorInput editorInput = null;
String editorId = null;
if (exists) {
try {
URI uriLocation = path.toUri();
IFileStore fileStore = EFS.getStore(uriLocation);
editorInput = new FileStoreEditorInput(fileStore);
editorId = IDE.getEditorDescriptorForFileStore(fileStore, false).getId();
} catch (CoreException e1) {
CSourceNotFoundElement element = new CSourceNotFoundElement(new TempElement(), null, fullPath);
editorInput = new CSourceNotFoundEditorInput(element);
editorId = ICDebugUIConstants.CSOURCENOTFOUND_EDITOR_ID;
}
} else {
CSourceNotFoundElement element = new CSourceNotFoundElement(new TempElement(), null, fullPath);
editorInput = new CSourceNotFoundEditorInput(element);
editorId = ICDebugUIConstants.CSOURCENOTFOUND_EDITOR_ID;
}
IWorkbenchPage page = CUIPlugin.getActivePage();
try {
page.openEditor(editorInput, editorId);
} catch (PartInitException e) {
GdbUIPlugin.log(e);
}
}
private class TempElement implements IAdaptable, ICSourceNotFoundDescription {
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> adapter) {
if (adapter == ICSourceNotFoundDescription.class)
return (T) this;
return null;
}
@Override
public String getDescription() {
return DebugSourcesMessages.DebugSourcesMessages_unknown;
}
@Override
public boolean isAddressOnly() {
return false;
}
}
class DebugPatternFilter extends PatternFilter {
@Override
protected boolean isLeafMatch(Viewer viewer, Object element) {
String name = ((DebugSourcesTreeElement) element).getName();
String path = ((DebugSourcesTreeElement) element).getFullPath();
return wordMatches(path) || wordMatches(name);
}
}
private static final String POPULATE_FAMILY = "populateJobFamily"; //$NON-NLS-1$
private static final String FILECHECK_FAMILY = "fileCheckJobFamily"; //$NON-NLS-1$
/**
* Job used to populate the tree
*
*/
class PopulateTreeJob extends Job {
private IDebugSourceFileInfo[] srcFileInfo;
private DebugSourcesTreeElement populateTree;
public PopulateTreeJob(IDebugSourceFileInfo[] srcFileInfo) {
super("Populate Tree Job"); //$NON-NLS-1$
this.srcFileInfo = srcFileInfo;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
if (srcFileInfo == null)
return Status.CANCEL_STATUS;
populateTree = populateTree(srcFileInfo, monitor);
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return POPULATE_FAMILY.equals(family);
}
public DebugSourcesTreeElement getTree() {
return populateTree;
}
private DebugSourcesTreeElement populateTree(IDebugSourceFileInfo[] srcFileInfo, IProgressMonitor monitor) {
DebugSourcesTreeElement debugTree = new DebugSourcesTreeElement("", FileExist.UNKNOWN); //$NON-NLS-1$
DebugSourcesTreeElement current = debugTree;
for (int i = 0; i < srcFileInfo.length; i++) {
DebugSourcesTreeElement root = current;
DebugSourcesTreeElement parent = root;
String path = srcFileInfo[i].getPath();
if (path == null) {
continue;
}
// Use Path API to clean the path
try {
Path p = Paths.get(path);
Path filename = p.getFileName();
// add root
Path pRoot = p.getRoot();
if (pRoot == null || !p.isAbsolute()) {
current = current.addLeaf(DebugSourcesMessages.DebugSourcesView_unrooted, p.toString(),
FileExist.UNKNOWN);
} else if (pRoot.equals(filename)) {
current = current.addLeaf(srcFileInfo[i].getName(), p.toString(), FileExist.UNKNOWN);
} else {
current = current.addNode(pRoot.toString(), FileExist.UNKNOWN);
}
parent = current;
// Add each sub-path
Path normalizedPath = p.normalize();
for (Path subpath : normalizedPath) {
if (subpath.equals(filename)) { // this is a leaf
current = current.addLeaf(srcFileInfo[i].getName(), p.toString(), FileExist.UNKNOWN);
} else {
current = current.addNode(subpath.toString(), FileExist.UNKNOWN);
}
current.setParent(parent);
parent = current;
}
current = root;
parent = root;
} catch (InvalidPathException e) {
GdbUIPlugin.log(e);
}
if (monitor != null && monitor.isCanceled()) {
return current;
}
}
return current;
}
}
/**
* Job used to check the existence of a file, updates the tree accordingly
*
*/
class CheckFileExistenceJob extends Job {
public CheckFileExistenceJob() {
super("Checking file existence"); //$NON-NLS-1$
}
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
DebugSourcesTreeElement tmpTree = debugTree;
traverseDebugTree(tmpTree, monitor);
debugTree = tmpTree;
if (monitor != null && monitor.isCanceled())
return Status.CANCEL_STATUS;
} catch (Exception e) {
GdbUIPlugin.log(e);
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return FILECHECK_FAMILY.equals(family);
}
/**
* Sets the file exist field of a leaf node
*
* @param child
* @param monitor
*/
private void traverseDebugTree(DebugSourcesTreeElement child, IProgressMonitor monitor) {
Set<DebugSourcesTreeElement> children = child.getChildren();
for (DebugSourcesTreeElement each : children) {
String path = each.getFullPath();
if (path != null) {
Path p = Paths.get(path);
boolean exists = Files.exists(p);
each.setExist(exists ? FileExist.YES : FileExist.NO);
// if leaf, and it exists, we need to make the parent nodes exist too
if (!each.hasChildren() && exists) {
traverseParent(each);
}
}
if (monitor != null && monitor.isCanceled())
return;
traverseDebugTree(each, monitor);
}
if (child.getExists() == FileExist.UNKNOWN) {
child.setExist(FileExist.NO);
}
}
private void traverseParent(DebugSourcesTreeElement node) {
DebugSourcesTreeElement parent = node.getParent();
if (parent.getExists() != FileExist.YES) {
parent.setExist(FileExist.YES);
if (parent.getParent() != null) {
traverseParent(parent);
}
}
}
}
}

View file

@ -0,0 +1,85 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
import java.util.function.Function;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
/**
* Comparator used for the Debug Sources Table viewer
*
*/
public class DebugSourcesViewComparator<T> extends ViewerComparator {
private Function<T, Comparable<?>> func;
private int propertyIndex;
private enum Direction {
ASCENDING, DESCENDING;
/**
*
* @param direction the previous direction
* @return the new direction
*/
public static Direction toggle(Direction direction) {
return direction == ASCENDING ? DESCENDING : ASCENDING;
}
}
private Direction direction = Direction.DESCENDING;
public DebugSourcesViewComparator(Function<T, Comparable<?>> func) {
this.func = func;
}
public DebugSourcesViewComparator() {
this.propertyIndex = 0;
direction = Direction.DESCENDING;
}
public int getDirection() {
return direction == Direction.DESCENDING ? SWT.DOWN : SWT.UP;
}
public void setColumn(Function<T, Comparable<?>> column, int idx) {
this.func = column;
if (idx == this.propertyIndex) {
// Same column as last sort; toggle the direction
direction = Direction.toggle(direction);
} else {
// New column
this.propertyIndex = idx;
direction = Direction.DESCENDING;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
Comparable s1 = func.apply((T) e1);
Comparable s2 = func.apply((T) e2);
int rc = s1.compareTo(s2);
// If descending order, flip the direction
if (direction.equals(Direction.DESCENDING)) {
rc = -rc;
}
return rc;
}
}

View file

@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2018, 2919 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources;
/**
*
*/
public interface IDebugSourcesImagesConst {
public static final String IMG_COLLAPSE_DEBUG_SOURCES = "icons/full/elcl16/collapse_all.gif"; //$NON-NLS-1$
public static final String IMG_EXPAND_DEBUG_SOURCES = "icons/full/elcl16/expand_all.gif"; //$NON-NLS-1$
public static final String IMG_FLAT_LAYOUT = "icons/full/elcl16/flatLayout.png"; //$NON-NLS-1$
public static final String IMG_NORMAL_LAYOUT = "icons/full/elcl16/hierarchicalLayout.png"; //$NON-NLS-1$
public static final String IMG_SHOW_EXISTING_FILES_ONLY = "icons/full/elcl16/existingFiles.gif"; //$NON-NLS-1$
}

View file

@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;
import java.util.Optional;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesMessages;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.IDebugSourcesImagesConst;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ResourceLocator;
import org.eclipse.jface.viewers.TreeViewer;
/**
* Action to collapse of the nodes of the Debug Sources tree
*/
public class DebugSourcesCollapseAction extends Action {
private final TreeViewer viewer;
public DebugSourcesCollapseAction(TreeViewer viewer) {
super();
this.viewer = viewer;
if (viewer == null || viewer.getControl().isDisposed()) {
setEnabled(false);
}
setText(DebugSourcesMessages.DebugSourcesCollapseAction_name);
setToolTipText(DebugSourcesMessages.DebugSourcesCollapseAction_description);
Optional<ImageDescriptor> descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID,
IDebugSourcesImagesConst.IMG_COLLAPSE_DEBUG_SOURCES);
descriptor.ifPresent(this::setImageDescriptor);
}
@Override
public void run() {
if (viewer != null) {
viewer.collapseAll();
}
}
}

View file

@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Baha El-Kassaby - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;
import java.util.Optional;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesMessages;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.IDebugSourcesImagesConst;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ResourceLocator;
import org.eclipse.jface.viewers.TreeViewer;
/**
* Action to Expand all the nodes of the Debug Sources tree
*/
public class DebugSourcesExpandAction extends Action {
private final TreeViewer viewer;
public DebugSourcesExpandAction(TreeViewer viewer) {
this.viewer = viewer;
if (viewer == null || viewer.getControl().isDisposed()) {
setEnabled(false);
}
setText(DebugSourcesMessages.DebugSourcesExpandAction_name);
setToolTipText(DebugSourcesMessages.DebugSourcesExpandAction_description);
Optional<ImageDescriptor> descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID,
IDebugSourcesImagesConst.IMG_EXPAND_DEBUG_SOURCES);
descriptor.ifPresent(this::setImageDescriptor);
}
@Override
public void run() {
if (viewer != null) {
viewer.expandAll();
}
}
}

View file

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham- Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;
import java.util.Optional;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesLabelProvider;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesMessages;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesTreeContentProvider;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.IDebugSourcesImagesConst;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ResourceLocator;
import org.eclipse.jface.viewers.TreeViewer;
public class DebugSourcesFlattendedTree extends Action {
private final TreeViewer viewer;
public DebugSourcesFlattendedTree(TreeViewer viewer) {
super(null, IAction.AS_RADIO_BUTTON);
this.viewer = viewer;
if (viewer == null || viewer.getControl().isDisposed()) {
setEnabled(false);
}
setText(DebugSourcesMessages.DebugSourcesFlattendedTree_name);
setToolTipText(DebugSourcesMessages.DebugSourcesFlattendedTree_description);
Optional<ImageDescriptor> descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID,
IDebugSourcesImagesConst.IMG_FLAT_LAYOUT);
descriptor.ifPresent(this::setImageDescriptor);
if (viewer != null) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer
.getContentProvider();
setChecked(contentProvider.isFlattenFoldersWithNoFiles());
}
}
@Override
public void run() {
if (isChecked()) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer
.getContentProvider();
contentProvider.setFlattenFoldersWithNoFiles(true);
for (int i = 0; i < viewer.getTree().getColumnCount(); i++) {
DebugSourcesLabelProvider labelProvider = (DebugSourcesLabelProvider) viewer.getLabelProvider(i);
labelProvider.setFlattenFoldersWithNoFiles(true);
}
viewer.refresh(true);
}
}
}

View file

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham- Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;
import java.util.Optional;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesLabelProvider;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesMessages;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesTreeContentProvider;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.IDebugSourcesImagesConst;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ResourceLocator;
import org.eclipse.jface.viewers.TreeViewer;
public class DebugSourcesNormalTree extends Action {
private final TreeViewer viewer;
public DebugSourcesNormalTree(TreeViewer viewer) {
super(null, IAction.AS_RADIO_BUTTON);
this.viewer = viewer;
if (viewer == null || viewer.getControl().isDisposed()) {
setEnabled(false);
}
setText(DebugSourcesMessages.DebugSourcesNormalTree_name);
setToolTipText(DebugSourcesMessages.DebugSourcesNormalTree_description);
Optional<ImageDescriptor> descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID,
IDebugSourcesImagesConst.IMG_NORMAL_LAYOUT);
descriptor.ifPresent(this::setImageDescriptor);
if (viewer != null) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer
.getContentProvider();
setChecked(!contentProvider.isFlattenFoldersWithNoFiles());
}
}
@Override
public void run() {
if (isChecked()) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer
.getContentProvider();
contentProvider.setFlattenFoldersWithNoFiles(false);
for (int i = 0; i < viewer.getTree().getColumnCount(); i++) {
DebugSourcesLabelProvider labelProvider = (DebugSourcesLabelProvider) viewer.getLabelProvider(i);
labelProvider.setFlattenFoldersWithNoFiles(false);
}
viewer.refresh(true);
}
}
}

View file

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham- Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;
import java.util.Optional;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesLabelProvider;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesMessages;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesTreeContentProvider;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.IDebugSourcesImagesConst;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ResourceLocator;
import org.eclipse.jface.viewers.TreeViewer;
public class DebugSourcesShowExistingFilesOnly extends Action {
private final TreeViewer viewer;
public DebugSourcesShowExistingFilesOnly(TreeViewer viewer) {
super(null, IAction.AS_CHECK_BOX);
this.viewer = viewer;
if (viewer == null || viewer.getControl().isDisposed()) {
setEnabled(false);
}
setText(DebugSourcesMessages.DebugSourcesShowExistingFilesOnly_name);
setToolTipText(DebugSourcesMessages.DebugSourcesShowExistingFilesOnly_description);
Optional<ImageDescriptor> descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID,
IDebugSourcesImagesConst.IMG_SHOW_EXISTING_FILES_ONLY);
descriptor.ifPresent(this::setImageDescriptor);
if (viewer != null) {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer
.getContentProvider();
setChecked(contentProvider.isShowExistingFilesOnly());
}
}
@Override
public void run() {
DebugSourcesTreeContentProvider contentProvider = (DebugSourcesTreeContentProvider) viewer.getContentProvider();
boolean showExistingFilesOnly = contentProvider.isShowExistingFilesOnly();
showExistingFilesOnly = !showExistingFilesOnly;
contentProvider.setShowExistingFilesOnly(showExistingFilesOnly);
for (int i = 0; i < viewer.getTree().getColumnCount(); i++) {
DebugSourcesLabelProvider labelProvider = (DebugSourcesLabelProvider) viewer.getLabelProvider(i);
labelProvider.setShowExistingFilesOnly(showExistingFilesOnly);
}
setChecked(showExistingFilesOnly);
viewer.refresh(true);
}
}

View file

@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham- Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.actions;
import org.eclipse.cdt.dsf.gdb.internal.ui.debugsources.DebugSourcesView;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.debug.ui.contexts.IDebugContextService;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IActionDelegate2;
import org.eclipse.ui.IViewActionDelegate;
import org.eclipse.ui.IViewPart;
public class DebugSourcesViewRefresh implements IViewActionDelegate, IDebugContextListener, IActionDelegate2 {
private IViewPart view;
private IAction action;
@Override
public void selectionChanged(IAction action, ISelection selection) {
this.action = action;
updateEnablement();
}
@Override
public void init(IViewPart view) {
this.view = view;
if (view != null) {
IDebugContextService debugContextService = DebugUITools.getDebugContextManager()
.getContextService(view.getSite().getWorkbenchWindow());
debugContextService.addPostDebugContextListener(this);
}
updateEnablement();
}
private void updateEnablement() {
if (view instanceof DebugSourcesView) {
DebugSourcesView debugSourcesView = (DebugSourcesView) view;
action.setEnabled(debugSourcesView.canRefresh());
} else {
action.setEnabled(false);
}
}
@Override
public void init(IAction action) {
this.action = action;
updateEnablement();
}
@Override
public void dispose() {
if (view != null) {
DebugUITools.getDebugContextManager().getContextService(view.getSite().getWorkbenchWindow())
.removePostDebugContextListener(this);
view = null;
}
updateEnablement();
}
@Override
public void debugContextChanged(DebugContextEvent event) {
updateEnablement();
}
@Override
public void run(IAction action) {
throw new UnsupportedOperationException("call runWithEvent instead"); //$NON-NLS-1$
}
@Override
public void runWithEvent(IAction action, Event event) {
if (view instanceof DebugSourcesView) {
DebugSourcesView debugSourcesView = (DebugSourcesView) view;
debugSourcesView.refresh();
}
}
}

View file

@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-Vendor: %providerName
Bundle-SymbolicName: org.eclipse.cdt.dsf.gdb;singleton:=true
Bundle-Version: 5.7.300.qualifier
Bundle-Version: 5.8.0.qualifier
Bundle-Activator: org.eclipse.cdt.dsf.gdb.internal.GdbPlugin
Bundle-Localization: plugin
Require-Bundle: org.eclipse.core.runtime,

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Kichwa Coders and others.
* Copyright (c) 2015, 2018 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@ -13,6 +13,10 @@
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
@ -24,13 +28,19 @@ import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.launching.GdbSourceLookupDirector;
import org.eclipse.cdt.dsf.mi.service.CSourceLookup;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MiSourceFilesInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MiSourceFilesInfo.SourceFileInfo;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
@ -40,9 +50,16 @@ import org.eclipse.core.runtime.Status;
*
* @since 5.0
*/
public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup {
public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup, IDebugSourceFiles, ICachingService {
private ICommandControl fCommand;
private static class DebugSourceFilesChangedEvent extends AbstractDMEvent<IDMContext>
implements IDebugSourceFilesChangedEvent {
public DebugSourceFilesChangedEvent(IDMContext context) {
super(context);
}
}
private ICommandControlService fCommand;
private CommandFactory fCommandFactory;
private Map<ISourceLookupDMContext, CSourceLookupDirector> fDirectors = new HashMap<>();
/**
@ -50,6 +67,8 @@ public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup {
*/
private Map<String, String> fCachedEntries = Collections.emptyMap();
private CommandCache fDebugSourceFilesCache;
public GDBSourceLookup(DsfSession session) {
super(session);
}
@ -65,12 +84,14 @@ public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup {
}
private void doInitialize(RequestMonitor rm) {
fCommand = getServicesTracker().getService(ICommandControl.class);
fCommand = getServicesTracker().getService(ICommandControlService.class);
fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
register(new String[] { IGDBSourceLookup.class.getName(), GDBSourceLookup.class.getName() },
new Hashtable<String, String>());
fDebugSourceFilesCache = new CommandCache(getSession(), fCommand);
fDebugSourceFilesCache.setContextAvailable(fCommand.getContext(), true);
register(new String[] { IGDBSourceLookup.class.getName(), GDBSourceLookup.class.getName(),
IDebugSourceFiles.class.getName() }, new Hashtable<String, String>());
rm.done();
}
@ -120,10 +141,9 @@ public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup {
rm.done(false);
} else {
/*
* Issue the clear and set commands back to back so that the
* executor thread atomically changes the source lookup settings.
* Any commands to GDB issued after this call will get the new
* source substitute settings.
* Issue the clear and set commands back to back so that the executor thread
* atomically changes the source lookup settings. Any commands to GDB issued
* after this call will get the new source substitute settings.
*/
CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) {
@Override
@ -143,14 +163,19 @@ public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup {
fCachedEntries = entries;
CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) {
@Override
protected void handleFailure() {
/*
* We failed to apply the changes. Clear the cache as it does
* not represent the state of the backend. However we don't have
* a good recovery here, so on future sourceContainersChanged()
* calls we will simply reissue the substitutions.
*/
fCachedEntries = null;
protected void handleCompleted() {
// Reset the list of source files when source path substitutions change
fDebugSourceFilesCache.reset();
getSession().dispatchEvent(new DebugSourceFilesChangedEvent(sourceLookupCtx), getProperties());
if (!isSuccess()) {
/*
* We failed to apply the changes. Clear the cache as it does not represent the
* state of the backend. However we don't have a good recovery here, so on
* future sourceContainersChanged() calls we will simply reissue the
* substitutions.
*/
fCachedEntries = null;
}
rm.done();
}
};
@ -160,6 +185,100 @@ public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup {
fCommandFactory.createMISetSubstitutePath(sourceLookupCtx, entry.getKey(), entry.getValue()),
new DataRequestMonitor<MIInfo>(getExecutor(), countingRm));
}
}
private static final class DebugSourceFileInfo implements IDebugSourceFileInfo {
private final SourceFileInfo miInfo;
private DebugSourceFileInfo(SourceFileInfo miInfo) {
if (miInfo == null)
throw new IllegalArgumentException("The SourceFileInfo provided is null"); //$NON-NLS-1$
this.miInfo = miInfo;
}
@Override
public String getName() {
// we get the file name without the path
String name = miInfo != null ? miInfo.getFile() : null;
if (name == null)
return name;
try {
Path p = Paths.get(name);
name = p.getFileName() != null ? p.getFileName().toString() : ""; //$NON-NLS-1$
} catch (InvalidPathException e) {
// do nothing
}
return name;
}
@Override
public String getPath() {
// we get the file name without the path
String path = miInfo != null ? miInfo.getFullName() : null;
if (path == null)
return path;
try {
Path p = Paths.get(path);
path = p.toString();
} catch (InvalidPathException e) {
// do nothing
}
return path;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((miInfo == null) ? 0 : miInfo.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DebugSourceFileInfo other = (DebugSourceFileInfo) obj;
if (miInfo == null) {
if (other.miInfo != null)
return false;
} else if (!miInfo.equals(other.miInfo))
return false;
return true;
}
@Override
public String toString() {
return "DebugSourceFileInfo [miInfo=" + miInfo + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
@Override
public void getSources(final IDMContext dmc, final DataRequestMonitor<IDebugSourceFileInfo[]> rm) {
fDebugSourceFilesCache.execute(fCommandFactory.createMiFileListExecSourceFiles(dmc),
new DataRequestMonitor<MiSourceFilesInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
IDebugSourceFileInfo[] result = null;
MiSourceFilesInfo sourceFiles = getData();
SourceFileInfo[] info = sourceFiles.getSourceFiles();
result = Arrays.asList(info).stream().map(DebugSourceFileInfo::new)
.toArray(IDebugSourceFileInfo[]::new);
rm.setData(result);
rm.done();
}
});
}
@Override
public void flushCache(IDMContext context) {
fDebugSourceFilesCache.reset();
getSession().dispatchEvent(new DebugSourceFilesChangedEvent(fCommand.getContext()), getProperties());
}
}

View file

@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (c) 2017, 2018 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham (Kichwa Coders) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
import org.eclipse.cdt.dsf.service.IDsfService;
/**
* Provides the ability to obtain the list of source files for the given debug
* context. For GDB this is using the -file-list-exec-source-files command
*
* @since 5.8
*/
public interface IDebugSourceFiles extends IDsfService {
/**
* Data type for what is returned by
* {@link IDebugSourceFiles#getSources(IDMContext, DataRequestMonitor)}
*/
public interface IDebugSourceFileInfo {
/**
* The name of the source file as it appears in the debug information. This may
* be relative, just the name, or absolute. Use {@link #getPath()} for the
* absolute path to the file name.
*
* @return name of the file
*/
public String getName();
/**
* The absolute path of the the file.
*
* @return path to the file
*/
public String getPath();
}
/**
* Event indicating that the list of the files may have changed for the given context.
*/
public interface IDebugSourceFilesChangedEvent extends IDMEvent<IDMContext> {
}
/**
* Retrieves the list of sources data/files for the given context.
*
* @param context
* execution context
* @param rm
* Request completion monitor.
*/
void getSources(IDMContext context, DataRequestMonitor<IDebugSourceFileInfo[]> rm);
}

View file

@ -120,6 +120,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUncall;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIFileExecAndSymbols;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIFileExecFile;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIFileListExecSourceFiles;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIFileSymbolFile;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSet;
@ -254,6 +255,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIVarSetFormatInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarShowAttributesInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarShowFormatInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarUpdateInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MiSourceFilesInfo;
/**
* Factory to create MI/CLI commands.
@ -739,6 +741,11 @@ public class CommandFactory {
return new MIFileExecFile(dmc);
}
/** @since 5.8*/
public ICommand<MiSourceFilesInfo> createMiFileListExecSourceFiles(IDMContext ctx) {
return new MIFileListExecSourceFiles(ctx);
}
public ICommand<MIInfo> createMIFileSymbolFile(ICommandControlDMContext dmc, String file) {
return new MIFileSymbolFile(dmc, file);
}

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2017, 2018 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham (Kichwa Coders) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.mi.service.command.commands;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
import org.eclipse.cdt.dsf.mi.service.command.output.MiSourceFilesInfo;
/**
*
* -file-list-exec-source-files
*
* Returns the list of source files for the current execution context. It
* outputs both filename and full (absolute path) file name of a source file.
*
* @since 5.8
*/
public class MIFileListExecSourceFiles extends MICommand<MiSourceFilesInfo> {
public MIFileListExecSourceFiles(IDMContext ctx) {
super(ctx, "-file-list-exec-source-files"); //$NON-NLS-1$
}
@Override
public MiSourceFilesInfo getResult(MIOutput out) {
return new MiSourceFilesInfo(out);
}
}

View file

@ -0,0 +1,164 @@
/*******************************************************************************
* Copyright (c) 2017, 2018 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Jonah Graham (Kichwa Coders) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.mi.service.command.output;
import java.util.LinkedList;
import java.util.List;
/**
* Example output is:
*
* <pre>
* (gdb) -file-list-exec-source-files
* ^done,files=[{file=foo.c,fullname=/home/foo.c},
* {file=/home/bar.c,fullname=/home/bar.c},
* {file=gdb_could_not_find_fullpath.c}]
* </pre>
*
* @since 5.8
*/
public class MiSourceFilesInfo extends MIInfo {
private SourceFileInfo[] sourceFileInfos;
public MiSourceFilesInfo(MIOutput record) {
super(record);
parse();
if (sourceFileInfos == null) {
sourceFileInfos = new SourceFileInfo[0];
}
}
/**
* Returns array of source files infos
*
* @return
*/
public SourceFileInfo[] getSourceFiles() {
return sourceFileInfos;
}
private void parse() {
if (isDone()) {
MIOutput out = getMIOutput();
MIResultRecord rr = out.getMIResultRecord();
if (rr != null) {
MIResult[] results = rr.getMIResults();
for (int i = 0; i < results.length; i++) {
String var = results[i].getVariable();
if (var.equals("files")) { //$NON-NLS-1$
MIValue value = results[i].getMIValue();
if (value instanceof MIList) {
parseResults((MIList) value);
}
}
}
}
}
}
private void parseResults(MIList list) {
MIValue[] miValues = list.getMIValues();
List<SourceFileInfo> infos = new LinkedList<>();
if (miValues != null) {
for (MIValue miValue : miValues) {
if (miValue instanceof MITuple) {
MITuple miTuple = (MITuple) miValue;
SourceFileInfo info = new SourceFileInfo();
info.parse(miTuple.getMIResults());
infos.add(info);
}
}
}
sourceFileInfos = infos.toArray(new SourceFileInfo[infos.size()]);
}
public static class SourceFileInfo {
private String file;
private String fullname;
public void setFile(String file) {
this.file = file;
}
public String getFile() {
return file;
}
public void setFullName(String fullname) {
this.fullname = fullname;
}
public String getFullName() {
return fullname;
}
private void parse(MIResult[] results) {
for (MIResult result : results) {
String variable = result.getVariable();
MIValue miVal = result.getMIValue();
if (!(miVal instanceof MIConst)) {
continue;
}
String value = ((MIConst) miVal).getCString();
switch (variable) {
case "file": //$NON-NLS-1$
file = value;
break;
case "fullname": //$NON-NLS-1$
fullname = value;
break;
}
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((file == null) ? 0 : file.hashCode());
result = prime * result + ((fullname == null) ? 0 : fullname.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SourceFileInfo other = (SourceFileInfo) obj;
if (file == null) {
if (other.file != null)
return false;
} else if (!file.equals(other.file))
return false;
if (fullname == null) {
if (other.fullname != null)
return false;
} else if (!fullname.equals(other.fullname))
return false;
return true;
}
@Override
public String toString() {
return "SourceFileInfo [file=" + file + ", fullname=" + fullname + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2007, 2016 Ericsson and others.
* Copyright (c) 2007, 2018 Ericsson and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@ -67,6 +67,8 @@ import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData;
import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles.IDebugSourceFileInfo;
import org.eclipse.cdt.dsf.gdb.service.IGDBMemory2;
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
@ -107,6 +109,8 @@ public class SyncUtil {
private static ISourceLookup fSourceLookup;
private static IDebugSourceFiles fDebugSourceFiles;
// Static list of register names as obtained directly from GDB.
// We make it static, key'ed on each version of gdb, so it does not
// get re-set for every test.
@ -128,7 +132,7 @@ public class SyncUtil {
fMemory = tracker.getService(IMemory.class);
fCommandFactory = fGdbControl.getCommandFactory();
fSourceLookup = tracker.getService(ISourceLookup.class);
fDebugSourceFiles = tracker.getService(IDebugSourceFiles.class);
tracker.dispose();
};
fSession.getExecutor().submit(runnable).get();
@ -979,4 +983,21 @@ public class SyncUtil {
return query.get();
}
/**
* Get the sources from the debugger.
*
* Wrapper around
* {@link IDebugSourceFiles#getSources(IDMContext, DataRequestMonitor)}
*/
public static IDebugSourceFileInfo[] getSources(IDMContext ctx) throws Exception {
Query<IDebugSourceFileInfo[]> query = new Query<IDebugSourceFileInfo[]>() {
@Override
protected void execute(DataRequestMonitor<IDebugSourceFileInfo[]> rm) {
fDebugSourceFiles.getSources(ctx, rm);
}
};
fDebugSourceFiles.getExecutor().execute(query);
return query.get();
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Kichwa Coders and others.
* Copyright (c) 2015, 2018 Kichwa Coders and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@ -54,6 +54,8 @@ import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData;
import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles;
import org.eclipse.cdt.dsf.gdb.service.IDebugSourceFiles.IDebugSourceFileInfo;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
@ -1157,4 +1159,58 @@ public class SourceLookupTest extends BaseParametrizedTestCase {
*/
waitUntil("Timeout waiting for launches to terminate", () -> launch1.isTerminated() && launch2.isTerminated());
}
/**
* Helper method that actually performs the test/assertions for
* {@link IDebugSourceFiles#getSources(IDMContext, DataRequestMonitor)} tests.
*/
private void testGetSourcesListInner(String path) throws Throwable {
IDebugSourceFileInfo[] sources = SyncUtil.getSources(SyncUtil.getContainerContext());
String expectedPath = Paths.get(path, SOURCE_NAME).toString();
boolean anyMatch = Arrays.asList(sources).stream().anyMatch(source -> {
return source.getPath().equals(expectedPath);
});
assertTrue(anyMatch);
}
/**
* Test for {@link IDebugSourceFiles#getSources(IDMContext, DataRequestMonitor)}
* with source path substitution on. Therefore make sure there is an entry
* for the resolved source path of {@value #SOURCE_NAME}
*/
private void testGetSourcesList(String execName) throws Throwable {
doMappingAndLaunch(execName, true);
testGetSourcesListInner(SOURCE_ABSPATH);
}
/**
* Test for {@link IDebugSourceFiles#getSources(IDMContext, DataRequestMonitor)}
* with no source path substitution on. Therefore make sure there is an entry
* for the build path of {@value #SOURCE_NAME}
*/
@Test
public void testGetSourcesListNoSourceLookup() throws Throwable {
doLaunch(EXEC_PATH + EXEC_AC_NAME);
testGetSourcesListInner(BUILD_ABSPATH);
}
@Test
public void testGetSourcesListAC() throws Throwable {
testGetSourcesList(EXEC_AC_NAME);
}
@Test
public void testGetSourcesListAN() throws Throwable {
testGetSourcesList(EXEC_AN_NAME);
}
@Test
public void testGetSourcesListRC() throws Throwable {
testGetSourcesList(EXEC_RC_NAME);
}
@Test
public void testGetSourcesListRN() throws Throwable {
testGetSourcesList(EXEC_RN_NAME);
}
}