I'm implementing a Visual Studio Language Service for a custom scripting language. I've managed to implement syntax highlighting, error checking, code completion, and "Go To Definition". I am having trouble figuring out how to hook in to the "Find All References" menu option (or even get it to display at this point).
Can anyone point me to a useful resource for implementing "Find All References" functionality in Visual Studio for a custom language? I've tried Googling for any information on it, but I can't seem to find anything.
First of all, there are multiple locations where Find All References can be invoked. The primary ones are:
Others include:
In the ideal implementation, you'll have an implementation of IVsSimpleLibrary2
which integrates support for your language into the Class View and Object Browser windows. The implementation of Find All References centers around the IVsFindSymbol
interface, which is provided by Visual Studio. Your code handles the relevant searches in the implementation of IVsSimpleLibrary2.GetList2
.
Make sure your library capabilities includes _LIB_FLAGS2.LF_SUPPORTSLISTREFERENCES
.
In your handler for IVsSimpleLibrary2.GetList2
, you are interested in the case where all of the following are true.
pobSrch
is a non-null array of length 1. I'll assume the first element is assigned to the local variable criteria
for the remainder of these conditions.criteria.eSrchType == VSOBSEARCHTYPE.SO_ENTIREWORD
criteria.grfOptions
has the flag _VSOBSEARCHOPTIONS.VSOBSO_LOOKINREFS
criteria.grfOptions
has the flag _VSOBSEARCHOPTIONS.VSOBSO_CASESENSITIVE
When the above conditions are met, return an IVsSimpleObjectList2
implementation whose children are lazily computed results of a Find All References command.
In your ViewFilter.QueryCommandStatus
implementation, when guidCmdGroup == VSConstants.GUID_VSStandardCommandSet97
and nCmdId == VSStd97CmdID.FindReferences
you need to return OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_ENABLED
.
nCmdId
will be VSStd2KCmdID.FindReferences
but the guidCmdGroup
will be the same as mentioned before. This mismatch was corrected starting in Visual Studio 2008, after which point VSStd2KCmdID.FindReferences
was no longer used.Override ViewFilter.HandlePreExec
for the case of the command GUID and ID listed above, and execute the following code for that case:
HandleFindReferences();
return true;
Add the following extension method class:
public static class IVsFindSymbolExtensions
{
public static void DoSearch(this IVsFindSymbol findSymbol, Guid symbolScope, VSOBSEARCHCRITERIA2 criteria)
{
if (findSymbol == null)
throw new ArgumentNullException("findSymbol");
VSOBSEARCHCRITERIA2[] criteriaArray = { criteria };
ErrorHandler.ThrowOnFailure(findSymbol.DoSearch(ref symbolScope, criteriaArray));
}
}
Add the following method to your ViewFilter
class:
public virtual void HandleFindReferences()
{
int line;
int col;
// Get the caret position
ErrorHandler.ThrowOnFailure( TextView.GetCaretPos( out line, out col ) );
// Get the tip text at that location.
Source.BeginParse(line, col, new TokenInfo(), ParseReason.Autos, TextView, HandleFindReferencesResponse);
}
// this can be any constant value, it's just used in the next step.
public const int FindReferencesResults = 100;
void HandleFindReferencesResponse( ParseRequest req )
{
if ( req == null )
return;
// make sure the caret hasn't moved
int line;
int col;
ErrorHandler.ThrowOnFailure( TextView.GetCaretPos( out line, out col ) );
if ( req.Line != line || req.Col != col )
return;
IVsFindSymbol findSymbol = CodeWindowManager.LanguageService.GetService(typeof(SVsObjectSearch)) as IVsFindSymbol;
if ( findSymbol == null )
return;
// TODO: calculate references as an IEnumerable<IVsSimpleObjectList2>
// TODO: set the results on the IVsSimpleLibrary2 (used as described below)
VSOBSEARCHCRITERIA2 criteria =
new VSOBSEARCHCRITERIA2()
{
dwCustom = FindReferencesResults,
eSrchType = VSOBSEARCHTYPE.SO_ENTIREWORD,
grfOptions = (uint)_VSOBSEARCHOPTIONS2.VSOBSO_LISTREFERENCES,
pIVsNavInfo = null,
szName = "Find All References"
};
findSymbol.DoSearch(new Guid(SymbolScopeGuids80.All), criteria);
}
Update your implementation of IVsSimpleLibrary2.GetList2
. When the search criteria's dwCustom
value is set to FindReferencesResults
, rather than compute the results of a Find All References command on a Class View or Object Browser node, you only need to return an IVsSimpleObjectList2
that wraps the results previously calculated by your HandleFindReferencesResponse
method.