Recently, I needed to introduce symbol navigation through a Visual Studio extension, using the already loaded solution.
This task is easy to accomplish in a Visual Studio Code extension, by relying on the C# extension being installed and using some built in APIs that harness the Langaue Server that the C# extension brings in. In the past, this has been OmniSharp, though that will be changing with newer releases of the C# extension and the C# Dev Kit extension.
For example, in VSCode with a C# solution loaded up, if you have a fully qualified method name (and by fully qualified, I mean namespace, class name, and method name, something like CodingWithCalvin.OpenBinFolder.Vsix.Commands.OpenBinFolderCommand.OpenPath
, where CodingWithCalvin.OpenBinFolder.Vsix.Commands
is the namespace, OpenBinFolderCommand
is the class name, and OpenPath
is the method name), you can execute a command to search for matching symbols in the workspace -
const symbols = await commands.executeCommand<SymbolInformation[]>("vscode.executeWorkspaceSymbolProvider", `CodingWithCalvin.OpenBinFolder.Vsix.Commands.OpenBinFolderCommand.OpenPath`);
That will return a list of matching symbols. In the example above, there would only ever be one matching symbol, but there could be more. In the case of a single symbol, we can tell VSCode to open the corresponding file and navigate right to the symbol (the method in this case).
And, as an extensibility enthusiast, that methodology is nice and succint. But, I needed to do this in a Visual Studio extension, and those types of commands and APIs don’t exist in Visual Studio.
I’ll be honest, I struggled with this for quite a bit and eventually plead for help on Twitter. Luckily, Gérald Barré came to the rescue on Twitter and pointed me in the right direction by writing a blog post to answer my question!
Now, his example was attempting to find a type, but I was trying to find a symbol (in this case, a method). So, I had to make some changes to his code, but the overall idea is the same.
- Use the
VisualStudioWorkspace
to get the current solution i. This is available in theMicrosoft.VisualStudio.LanguageServices
NuGet package - Use the
CurrentSolution
to get theCompilation
for the solution loaded in the IDE - Loop over the
Projects
in theCurrentSolution
i. Make sure to check if the project supports compliation, as not all projects do - Use the
SymbolFinder
class to perform the search against the current project (of the loop) i. This is available in theMicrosoft.CodeAnalysis
NuGet package i. One caveat here is that you need to search for the method name and can’t just use the fully qualified method name (that includes namespace and class name), so we’ll need to perform an additional check to make sure we have the correct method - If we get to this point with a single symbol, then we can utilize the
Workspace
again to try and navigate to the symbol
Here is the final code I ended up with -
private readonly VisualStudioWorkspace _workspace;
[ImportingConstructor]
public SymbolService(VisualStudioWorkspace workspace)
{
_workspace = workspace;
}
if (_workspace?.CurrentSolution != null)
{
foreach (var project in _workspace.CurrentSolution.Projects)
{
if (project.SupportsCompilation)
{
var symbols = (await SymbolFinder
.FindDeclarationsAsync(project, "OpenPath", true)) // Just the method name here (true is to make the search case-insensitive)
.Where(x => x.ToDisplayString().EqualsIgnoreCase($"CodingWithCalvin.OpenBinFolder.Vsix.Commands.OpenBinFolderCommand.OpenPath()")); // Note that ToDisplayString() includes the parenthesis at the end of the method name, hence why they are in the equals check
if (symbols != null && symbols.Any())
{
var symbol = symbols.FirstOrDefault();
await _workspace.TryGoToDefinitionAsync(symbol, project, cancellationToken);
break;
}
}
}
}
And that’s it! Now, I can search for a symbol in the currently loaded solution and navigate my developer friend directly to it. Yes, its a little more involved than VSCode, but after finally figuring it out, its not THAT terrible.
Huge thanks to Gérald for pointing me in the right direction!
This post, "Finding Symbols in Your C# Projects Using Roslyn", first appeared on https://www.codingwithcalvin.net/finding-symbols-in-your-c-projects-using-roslyn/