Skip to content

Instantly share code, notes, and snippets.

@OmerRaviv
Created September 6, 2015 08:47
Show Gist options
  • Save OmerRaviv/fc3ce6fbd5b6af501f01 to your computer and use it in GitHub Desktop.
Save OmerRaviv/fc3ce6fbd5b6af501f01 to your computer and use it in GitHub Desktop.
[Obsolete("Not supported on VS2015, Use INavigationEngine implementation instead")]
internal static class LegacyGoToDefinitionEngine
{
/// <summary>
/// If Visual Studio's recognizes the given member and knows where its source code is, goes to the source code.
/// Otherwise, opens the "Find Symbols" ToolWindow.
/// </summary>
public static void GoToMemberDefinition(string memberName, uint sreachOptions = (uint)_VSOBSEARCHOPTIONS.VSOBSO_LOOKINREFS)
{
GoToDefinition(memberName, _LIB_LISTTYPE.LLT_MEMBERS, sreachOptions);
}
public static void GoToClassDefinition(string typeName, uint sreachOptions = (uint)_VSOBSEARCHOPTIONS.VSOBSO_LOOKINREFS)
{
GoToDefinition(typeName, _LIB_LISTTYPE.LLT_CLASSES, sreachOptions);
}
private static void GoToDefinition(string memberName, _LIB_LISTTYPE libListtype, uint sreachOptions)
{
if (GoToDefinitionInternal(memberName, libListtype, sreachOptions) == false)
{
// There was an ambiguity (more than one item found) or no items found at all.
if (LegacyLocationFinder.CanFindSymbols(memberName, sreachOptions) == false)
{
Logger.Error("Failed to FindSymbol for symbol " + memberName);
}
}
}
private static bool GoToDefinitionInternal(string typeOrMemberName, _LIB_LISTTYPE symbolType, uint sreachOptions)
{
IVsSimpleObjectList2 list;
if (LegacyLocationFinder.TryFindSymbol(typeOrMemberName, out list, symbolType, sreachOptions))
{
int ok;
const VSOBJGOTOSRCTYPE whereToGo = VSOBJGOTOSRCTYPE.GS_DEFINITION;
DataTipService.CloseDataTipIfOpen();
return HResult.Succeeded(list.CanGoToSource(0, whereToGo, out ok)) &&
HResult.Succeeded(ok) &&
HResult.Succeeded(list.GoToSource(0, whereToGo));
}
return false;
}
}
public static class LibraryGetter
{
public static readonly Guid CSharpLibrary = new Guid("58f1bad0-2288-45b9-ac3a-d56398f7781d");
public static readonly Guid VBLibrary = new Guid("414AC972-9829-4B6A-A8D7-A08152FEB8AA");
/// <summary>
/// Get Visual Studio's library of metadata for C#.
/// </summary>
public static IVsSimpleLibrary2 GetCSharpLibrary()
{
var objManager = VisualStudioServices.GetService<SVsObjectManager, IVsObjectManager2>();
IVsLibrary2 csharpLib;
var cSharpLibrary = CSharpLibrary;
objManager.FindLibrary(ref cSharpLibrary, out csharpLib);
return (IVsSimpleLibrary2)csharpLib;
}
}
[Obsolete("Not supported on vs 2015, Use FileLocationFinder implementation instead")]
internal static class LegacyLocationFinder
{
public static bool TryGetSourceLocation(string memberName, out string fileName, out uint line, uint sreachOptions)
{
IVsSimpleObjectList2 list;
if (LegacyLocationFinder.TryFindSymbol(memberName, out list, _LIB_LISTTYPE.LLT_MEMBERS, sreachOptions))
{
return HResult.Succeeded(list.GetSourceContextWithOwnership(0, out fileName, out line));
}
fileName = null;
line = 0;
return false;
}
/// <summary>
/// Tries to find a member (field/property/event/methods/etc).
/// </summary>
/// <param name="typeOrMemberName">The type or member we are searching for</param>
/// <param name="resultList">An IVsSimpleObjectList2 which contains a single result.</param>
/// <param name="symbolType">The type of symbol we are looking for (member/class/etc)</param>
/// <returns>
/// True if a unique match was found. False if the member does not exist or there was an ambiguity
/// (more than one member matched the search term).
/// </returns>
public static bool TryFindSymbol(string typeOrMemberName,
out IVsSimpleObjectList2 resultList,
_LIB_LISTTYPE symbolType,
uint sreachOptions)
{
try
{
// The Visual Studio API we're using here breaks with superfulous spaces
typeOrMemberName = typeOrMemberName.Replace(" ", "");
var library = LibraryGetter.GetCSharpLibrary();
IVsSimpleObjectList2 list;
var searchSucceed = HResult.Succeeded(library.GetList2((uint)symbolType,
(uint)_LIB_LISTFLAGS.LLF_USESEARCHFILTER,
CreateSearchCriteria(typeOrMemberName, sreachOptions),
out list));
if (searchSucceed && list != null)
{
// Check if there is an ambiguity (where there is more than one symbol that matches)
if (GetSymbolNames(list).Distinct().Count() == 1)
{
uint count;
list.GetItemCount(out count);
if (count > 1)
{
int ok;
list.CanDelete((uint)1, out ok);
}
resultList = list;
return true;
}
}
}
catch (AccessViolationException e)
{
/* eat this type of exception (ripped from original implementation) */
}
resultList = null;
return false;
}
public static bool CanFindSymbols(string memberName, uint sreachOptions)
{
var searcher = VisualStudioServices.GetService<SVsObjectSearch, IVsFindSymbol>();
var guidSymbolScope = LibraryGetter.CSharpLibrary;
return HResult.Succeeded(searcher.DoSearch(ref guidSymbolScope, CreateSearchCriteria(memberName, sreachOptions)));
}
private static VSOBSEARCHCRITERIA2[] CreateSearchCriteria(string typeOrMemberName, uint sreachOptions)
{
return new[]
{
new VSOBSEARCHCRITERIA2
{
eSrchType = VSOBSEARCHTYPE.SO_PRESTRING,
//grfOptions = (uint)_VSOBSEARCHOPTIONS.VSOBSO_LOOKINREFS,
grfOptions = sreachOptions,
szName = typeOrMemberName
}
};
}
private static IEnumerable<string> GetSymbolNames(IVsSimpleObjectList2 list)
{
uint count;
if (HResult.Succeeded(list.GetItemCount(out count)))
{
for (uint i = 0; i < count; i++)
{
object symbol;
if (HResult.Succeeded(list.GetProperty(i,
(int)_VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_FULLNAME,
out symbol)))
{
yield return (string)symbol;
}
}
}
}
}
@JoanComasFdz
Copy link

Hi Omar,

Thanks a lot for this gist! Just a question:

What does the "searcher.DoSearch()" method do? Looks like a key method for the GoToDefinitionEngine.

@OmerRaviv
Copy link
Author

That's this method: https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.shell.interop.ivsfindsymbol.dosearch.aspx

My specific implementation just opens the Find All References window if there is an ambiguity (more than one item was found and we don't know which one to Go To)

@JoanComasFdz
Copy link

Ah yes of course, sorry, I adapted the code slightly and the "var searcher = " just disappeared.

Its working like a charm!

@PeterMacej
Copy link

Thank you for the code! Just one question. What is the following code in TryFindSymbol for?

if (count > 1)
{
    int ok;
    list.CanDelete((uint)1, out ok);
}

It seems it does nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment