diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF index d8a7d4c4ef6..b41fa97f10c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF @@ -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, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/flatLayout.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/flatLayout.png new file mode 100644 index 00000000000..061b593caa2 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/flatLayout.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/flatLayout@2x.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/flatLayout@2x.png new file mode 100644 index 00000000000..23540f72b63 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/flatLayout@2x.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/hierarchicalLayout.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/hierarchicalLayout.png new file mode 100644 index 00000000000..64822eaa0d8 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/hierarchicalLayout.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/hierarchicalLayout@2x.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/hierarchicalLayout@2x.png new file mode 100644 index 00000000000..b84fb01bf15 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/dlcl16/hierarchicalLayout@2x.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/collapse_all.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/collapse_all.gif new file mode 100644 index 00000000000..b004e7c3374 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/collapse_all.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/existingFiles.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/existingFiles.gif new file mode 100644 index 00000000000..b226e41c527 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/existingFiles.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/expand_all.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/expand_all.gif new file mode 100644 index 00000000000..d66de6d100e Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/expand_all.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/flatLayout.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/flatLayout.png new file mode 100644 index 00000000000..13f83a23741 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/flatLayout.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/flatLayout@2x.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/flatLayout@2x.png new file mode 100644 index 00000000000..280561aeb4c Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/flatLayout@2x.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/hierarchicalLayout.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/hierarchicalLayout.png new file mode 100644 index 00000000000..665aa5cee3a Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/hierarchicalLayout.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/hierarchicalLayout@2x.png b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/hierarchicalLayout@2x.png new file mode 100644 index 00000000000..84039be3a93 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/elcl16/hierarchicalLayout@2x.png differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/debugsources_view.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/debugsources_view.gif new file mode 100644 index 00000000000..37b47054630 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/debugsources_view.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/refresh.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/refresh.gif new file mode 100644 index 00000000000..b6b8dc6836d Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/refresh.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties index 14cee021f4b..bb0e517600e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties @@ -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 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml index 75ae7454c99..bc74c689b2f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml @@ -296,6 +296,14 @@ name="%view.osresources.name" icon="icons/full/view16/osresources_view.gif"> + + @@ -327,6 +335,18 @@ id="org.eclipse.cdt.debug.ui.debuggerConsoleView"> + + + + + + @@ -630,4 +650,25 @@ name="GDB Remote Serial"> + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesLabelProvider.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesLabelProvider.java new file mode 100644 index 00000000000..87ce3a47094 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesLabelProvider.java @@ -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 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); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesMessages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesMessages.java new file mode 100644 index 00000000000..d1502b7967f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesMessages.java @@ -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() { + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesMessages.properties new file mode 100644 index 00000000000..f90c7c064ce --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesMessages.properties @@ -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= + +GdbDebugSourcesPreferences_name=Preferences... diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesTreeContentProvider.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesTreeContentProvider.java new file mode 100644 index 00000000000..f11a37ed80b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesTreeContentProvider.java @@ -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 children = tree.getChildren(showExistingFilesOnly); + return children.toArray(); + } + return null; + } + + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof DebugSourcesTreeElement) { + DebugSourcesTreeElement tree = (DebugSourcesTreeElement) parentElement; + Set 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 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; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesTreeElement.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesTreeElement.java new file mode 100644 index 00000000000..a5ccee1ed61 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesTreeElement.java @@ -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 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 getChildren() { + return children; + } + + /** + * + * @param filesThatMayExistOnly only include files that may exist + * @return list of children of the node + */ + public Set 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; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesView.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesView.java new file mode 100644 index 00000000000..4a76461cb9a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesView.java @@ -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 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(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 getAdapter(Class 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 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); + } + } + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesViewComparator.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesViewComparator.java new file mode 100644 index 00000000000..b15e836b71b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/DebugSourcesViewComparator.java @@ -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 extends ViewerComparator { + + private Function> 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> 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> 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; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/IDebugSourcesImagesConst.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/IDebugSourcesImagesConst.java new file mode 100644 index 00000000000..bcd29f864e1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/IDebugSourcesImagesConst.java @@ -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$ +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesCollapseAction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesCollapseAction.java new file mode 100644 index 00000000000..77a0c16617d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesCollapseAction.java @@ -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 descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID, + IDebugSourcesImagesConst.IMG_COLLAPSE_DEBUG_SOURCES); + descriptor.ifPresent(this::setImageDescriptor); + } + + @Override + public void run() { + if (viewer != null) { + viewer.collapseAll(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesExpandAction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesExpandAction.java new file mode 100644 index 00000000000..d02a1f95905 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesExpandAction.java @@ -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 descriptor = ResourceLocator.imageDescriptorFromBundle(GdbUIPlugin.PLUGIN_ID, + IDebugSourcesImagesConst.IMG_EXPAND_DEBUG_SOURCES); + descriptor.ifPresent(this::setImageDescriptor); + } + + @Override + public void run() { + if (viewer != null) { + viewer.expandAll(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesFlattendedTree.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesFlattendedTree.java new file mode 100644 index 00000000000..efced7519f9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesFlattendedTree.java @@ -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 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); + } + } + +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesNormalTree.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesNormalTree.java new file mode 100644 index 00000000000..3ec7609394c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesNormalTree.java @@ -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 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); + } + } + +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesShowExistingFilesOnly.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesShowExistingFilesOnly.java new file mode 100644 index 00000000000..5501325cc33 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesShowExistingFilesOnly.java @@ -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 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); + } + +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesViewRefresh.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesViewRefresh.java new file mode 100644 index 00000000000..4fe13968771 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/debugsources/actions/DebugSourcesViewRefresh.java @@ -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(); + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index f040489bedb..9d97873b2bc 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -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, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java index 3a292ea7953..739f4e9f1e5 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java @@ -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 + implements IDebugSourceFilesChangedEvent { + public DebugSourceFilesChangedEvent(IDMContext context) { + super(context); + } + } + + private ICommandControlService fCommand; private CommandFactory fCommandFactory; private Map fDirectors = new HashMap<>(); /** @@ -50,6 +67,8 @@ public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup { */ private Map 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()); + fDebugSourceFilesCache = new CommandCache(getSession(), fCommand); + fDebugSourceFilesCache.setContextAvailable(fCommand.getContext(), true); + register(new String[] { IGDBSourceLookup.class.getName(), GDBSourceLookup.class.getName(), + IDebugSourceFiles.class.getName() }, new Hashtable()); 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(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 rm) { + fDebugSourceFilesCache.execute(fCommandFactory.createMiFileListExecSourceFiles(dmc), + new DataRequestMonitor(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()); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IDebugSourceFiles.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IDebugSourceFiles.java new file mode 100644 index 00000000000..0bd066cc433 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IDebugSourceFiles.java @@ -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 { + } + + /** + * 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 rm); +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java index a1aef544584..7e1287d02db 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java @@ -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 createMiFileListExecSourceFiles(IDMContext ctx) { + return new MIFileListExecSourceFiles(ctx); + } + public ICommand createMIFileSymbolFile(ICommandControlDMContext dmc, String file) { return new MIFileSymbolFile(dmc, file); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileListExecSourceFiles.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileListExecSourceFiles.java new file mode 100644 index 00000000000..c58b4e41e6a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileListExecSourceFiles.java @@ -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 { + + public MIFileListExecSourceFiles(IDMContext ctx) { + super(ctx, "-file-list-exec-source-files"); //$NON-NLS-1$ + } + + @Override + public MiSourceFilesInfo getResult(MIOutput out) { + return new MiSourceFilesInfo(out); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MiSourceFilesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MiSourceFilesInfo.java new file mode 100644 index 00000000000..f1441cfde0a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MiSourceFilesInfo.java @@ -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: + * + *
+ *    (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}]
+ * 
+ * + * @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 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$ + } + + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java index 0a97f39caf7..43af9dbbc2f 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java @@ -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 query = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fDebugSourceFiles.getSources(ctx, rm); + } + }; + fDebugSourceFiles.getExecutor().execute(query); + return query.get(); + } } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java index c4bb8c8d648..94c20e34cbc 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java @@ -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); + } }