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>
|
@ -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,
|
||||
|
|
After Width: | Height: | Size: 131 B |
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 154 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 161 B |
After Width: | Height: | Size: 354 B |
After Width: | Height: | Size: 167 B |
After Width: | Height: | Size: 123 B |
After Width: | Height: | Size: 199 B |
After Width: | Height: | Size: 148 B |
After Width: | Height: | Size: 230 B |
After Width: | Height: | Size: 353 B |
BIN
dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/refresh.gif
Normal file
After Width: | Height: | Size: 385 B |
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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...
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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$
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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$
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|