How it works...

In this recipe, we wrote a C# console application based on Roslyn Workspaces API to format and simplify all the source files in a solution. These operations are very similar to what a Visual Studio IDE would do when you apply formatting and simplification quick fixes after setting the corresponding tools options. Let's walk through the code and understand how we implemented these operations:

public static void Main(string[] args)
{
// Parse arguments to get solution.
var slnPath = ParseArguments(args);
if (slnPath == null)
{
return;
}

// Create workspace.
MSBuildWorkspace workspace = MSBuildWorkspace.Create();

// Open solution within the workspace.
Console.WriteLine($"Loading solution '{slnPath}'...");
Solution solution = workspace.OpenSolutionAsync(slnPath).Result;

// Format the solution.
solution = FormatSolution(solution, workspace.Options);

// Simplify the solution.
solution = SimplifySolution(solution, workspace.Options);

// Apply changes.
ApplyChanges(workspace, solution);
}

The Main method invokes individual methods to perform the following operations:

  • ParseArguments: To scan for the input file to parse and transform.
  • MSBuildWorkspace.Create: To create a workspace and Workspace.OpenSolutionAsync to load the given solution in the workspace.
  • FormatSolution: To format all the documents in the solution.
  • SimplifySolution: To simplify all the documents in the solution.
  • ApplyChanges: To apply the formatting and simplification changes to the workspace and persist these to the disk.

Implementation of ParseArguments is identical to the one in the recipe, Writing an application based on Compiler Syntax API to parse and transform source files. Refer to the How it works..., section of that recipe for further explanation on this method.

MSBuildWorkspace (http://source.roslyn.io/#q=MSBuildWorkspace) is a custom implementation of the core Roslyn Workspace, which uses the MSBuild (https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild) project model for loading the solution/project files and allows reading and writing individual documents in the projects.

The FormatSolution method formats all documents in the solution. First, it modifies the options to prefer whitespace over tabs, with an indentation size of 2 (default is 4):

  • FormattingOptions.UseTabs: Value set to false.
  • FormattingOptions.IndentationSize: Value set to 2.
private static Solution FormatSolution(Solution originalSolution, OptionSet options)
{
Console.WriteLine("Formatting solution...");

// Prefer whitespaces over tabs, with an indentation size of 2.
options = options
.WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, false)
.WithChangedOption(FormattingOptions.IndentationSize, LanguageNames.CSharp, 2);

Solution newSolution = originalSolution;
foreach (var documentId in originalSolution.Projects.SelectMany(p => p.DocumentIds))
{
Document document = newSolution.GetDocument(documentId);

// Format the document.
Document newDocument = Formatter.FormatAsync(document, options).Result;

// Update the current solution.
newSolution = newDocument.Project.Solution;
}

return newSolution;
}

It keeps track of the current solution snapshot in newSolution, which is initialized to originalSolution. It then iterates over all the documents in the solution and does the following:

Note how we can't simply iterate over originalSolution.Projects or project.Documents because it will return objects from the unmodified originalSolution, not from the newSolution. We need to use the ProjectId/DocumentIds (that don't change) to look up the corresponding snapshots in the newSolution.

Finally, it returns the newSolution after all documents have been formatted.

The SimplifySolution method simplifies all documents in the solution. First, it modifies the options to prefer implicitly type local declaration, that is, user var over explicit type specification, by setting SimplificationOptions.PreferImplicitTypeInLocalDeclaration to true:

private static Solution SimplifySolution(Solution originalSolution, OptionSet options)
{
Console.WriteLine("Simplifying solution...");

// Prefer 'var' over explicit type specification.
options = options.WithChangedOption(SimplificationOptions.PreferImplicitTypeInLocalDeclaration, true);

Solution newSolution = originalSolution;
foreach (var documentId in originalSolution.Projects.SelectMany(p => p.DocumentIds))
{
Document document = newSolution.GetDocument(documentId);

// Add simplification annotation to the root.
var newRoot = document.GetSyntaxRootAsync().Result.WithAdditionalAnnotations(Simplifier.Annotation);

// Simplify the document.
Document newDocument = Simplifier.ReduceAsync(document.WithSyntaxRoot(newRoot), options).Result;

// Update the current solution.
newSolution = newDocument.Project.Solution;
}

return newSolution;
}

SimplifySolution has very identical implementation to the FormatSolution method for iterating the documents, simplifying them by invoking Simplifier.ReduceAsync public API (http://source.roslyn.io/#q=Simplifier.ReduceAsync), storing the latest snapshot in newSolution after processing each document, and finally returning the new solution snapshot at the end. It has one important difference though. Simplifier.ReduceAsync only processes nodes with a special syntax annotation: Simplifier.Annotation (http://source.roslyn.io/#q=Simplifier.Annotation). Hence, before invoking this API, we add this syntax annotation to the root of the document.

The ApplyChanges method invokes the Workspace.TryApplyChanges public API (http://source.roslyn.io/#q=Workspace.TryApplyChanges) with the new solution snapshot to apply the changes in the solution snapshot to the workspace. This also causes the MSBuildWorkspace to persist these changes onto the disk:

private static void ApplyChanges(Workspace workspace, Solution solution)
{
// Apply solution changes to the workspace.
// This persists the in-memory changes into the disk.
if (workspace.TryApplyChanges(solution))
{
Console.WriteLine("Solution updated.");
}
else
{
Console.WriteLine("Update failed!");
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset