From 7eeecab0209a14240dd23e2258c3357a0c6c3d6d Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Thu, 7 Jan 2016 00:59:38 -0500 Subject: [PATCH] Bug 244434 - When the target file of "Open Declaration" is reachable via multiple workspace paths, open it under the path that most closely matches the originating file Change-Id: I616804c6ffb9900e5df2f918a409f46c3cbb7a3a Signed-off-by: Nathan Ridge --- .../search/actions/OpenDeclarationsJob.java | 12 ++- .../cdt/internal/ui/viewsupport/IndexUI.java | 76 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java index 326d577d1b0..a9ba7c4098a 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/search/actions/OpenDeclarationsJob.java @@ -496,7 +496,17 @@ class OpenDeclarationsJob extends Job implements ASTRunnable { private ICElementHandle getCElementForName(ICProject project, IIndex index, IName declName) throws CoreException { if (declName instanceof IIndexName) { - return IndexUI.getCElementForName(project, index, (IIndexName) declName); + IIndexName indexName = (IIndexName) declName; + ITranslationUnit tu= IndexUI.getTranslationUnit(project, indexName); + if (tu != null) { + // If the file containing the target name is accessible via multiple + // workspace paths, choose the one that most closely matches the + // workspace path of the file originating the action. + tu = IndexUI.getPreferredTranslationUnit(tu, fTranslationUnit); + + return IndexUI.getCElementForName(tu, index, indexName); + } + return null; } if (declName instanceof IASTName) { IASTName astName = (IASTName) declName; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/IndexUI.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/IndexUI.java index 8339e414667..6c15a7a5c0d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/IndexUI.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/IndexUI.java @@ -13,7 +13,9 @@ package org.eclipse.cdt.internal.ui.viewsupport; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Properties; @@ -92,6 +94,7 @@ import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable; import org.eclipse.cdt.internal.core.model.ext.CElementHandleFactory; import org.eclipse.cdt.internal.core.model.ext.ICElementHandle; import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences; +import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.ui.editor.ASTProvider; @@ -359,6 +362,79 @@ public class IndexUI { } return null; } + + /** + * Given a 'source' and a 'target' translation unit, return a translation unit + * that resolves to the same file as 'target' and has a workspace path that + * matches the workspace path of 'source' as closely as possible. + * + * Most commonly, 'target' will be the only trasnlation unit that resolves to + * the file in question, and 'target' is returned. In the presence of linked + * folders, however, multiple workspace paths can refer to the same file, and + * this function chooses the one that's closest to 'source'. + */ + public static ITranslationUnit getPreferredTranslationUnit(ITranslationUnit target, + ITranslationUnit source) { + // Get the files corresponding to the source and target translation units. + // These files encode the workspace paths. + IFile sourceFile = source.getFile(); + IFile targetFile = target.getFile(); + if (sourceFile == null || targetFile == null) { + return target; + } + + // Resolve the location of the target in the filesystem. + IPath targetLocation = targetFile.getLocation(); + if (targetLocation == null) { + return target; + } + + // Find all files that resolve to the same location. + IFile[] candidates = ResourceLookup.findFilesForLocation(targetLocation); + + // In the common case that there is one only file that resolves to that + // location, or if the search found no results, return the original target. + if (candidates.length <= 1) { + return target; + } + + // Get the workspace path of the source translation unit's file. + final IPath sourcePath = sourceFile.getFullPath(); + + // Sort the candidates files by how closely they match 'sourcePath'. + Arrays.sort(candidates, new Comparator() { + @Override + public int compare(IFile f1, IFile f2) { + // Get the workspace paths of the files being compared. + IPath p1 = f1.getFullPath(); + IPath p2 = f2.getFullPath(); + + // Closeness of the match is defined by how many segments of + // the candidate's workspace path match 'sourcePath'. + int s1 = p1.matchingFirstSegments(sourcePath); + int s2 = p2.matchingFirstSegments(sourcePath); + if (s1 > s2) + return -1; + if (s1 < s2) + return 1; + + // Fall back on alphabetical comparison. + return p1.toString().compareTo(p2.toString()); + } + }); + + // Processing in the sorted order, return the first file for which + // a translation unit can be found. + for (IFile candidate : candidates) { + ITranslationUnit tu = CoreModelUtil.findTranslationUnit(candidate); + if (tu != null) { + return tu; + } + } + + // Fall back on returning the original target. + return target; + } public static ICElementHandle getCElementForName(ICProject preferProject, IIndex index, IIndexName declName) throws CoreException {