static void Main(string[] args) { if (args.Length != 3) { Console.WriteLine("Usage:"); Console.WriteLine("{0} <IntelliSenseDirectory> <SourceDirectory> <SolutionFilePath>", AppDomain.CurrentDomain.FriendlyName); Console.WriteLine("Press ENTER to exit;"); Console.ReadLine(); return; } ParseIntelliSense p = new ParseIntelliSense(args[0]); //Check parameters if (!Directory.Exists(p.IntelliSenseDirectory)) { Console.WriteLine($"Directory not found: {p.IntelliSenseDirectory}"); Console.WriteLine("Press ENTER to exit;"); Console.ReadLine(); return; } if (!Directory.Exists(args[1])) { Console.WriteLine($"Directory not found: {args[1]}"); Console.WriteLine("Press ENTER to exit;"); Console.ReadLine(); return; } if (!File.Exists(args[2])) { Console.WriteLine($"Solution file not found: {args[2]}"); Console.WriteLine("Press ENTER to exit;"); Console.ReadLine(); return; } p.ParseIntelliSenseFiles(); var workspace = MSBuildWorkspace.Create(); var solution = workspace.OpenSolutionAsync(args[2]).Result; var projects = solution.Projects.ToList(); var metadataReferences = projects.Where(proj => !proj.Name.Contains("test")) // Filter out test projects I guess? .SelectMany(proj => proj.MetadataReferences) .Distinct(); // Does it matter if they're distinct or not? Project project = null; var projectsMatchingDir = projects.Where(proj => proj.FilePath.Contains(args[1])).ToList(); if (projects.Count > 1) { // Picking the first one is fine because this pattern emerged in the following case: // // System.LibraryWeCareAbout // System.LibraryWeCareAbout.Other // // In this case, we just use the first. We can always run the tool again on the second one if it's necessary. project = projects.FirstOrDefault(proj => proj.FilePath.Contains(args[1])); } else if (projectsMatchingDir.Count == 0) { var path = GetPathToProject(args[1]); project = workspace.OpenProjectAsync(path).Result; } else { project = projectsMatchingDir.First(); } foreach (var document in project.Documents) { SourceText text; try { using (var stream = File.OpenRead(document.FilePath)) { text = SourceText.From(stream); } } catch (DirectoryNotFoundException) { continue; } catch (FileNotFoundException) { continue; } SyntaxTree initialTree = (SyntaxTree)CSharpSyntaxTree.ParseText(text); var compilation = CSharpCompilation.Create( "test", syntaxTrees: new[] { initialTree }, references: metadataReferences); var rewriter = new Rewriter(compilation.GetSemanticModel(initialTree), p.MembersDictionary); var treeWithFormattedTrivia = rewriter.Visit(initialTree.GetRoot()).SyntaxTree; var options = SetOptions(workspace.Options); var simplifiedTree = Simplifier.ReduceAsync(document.WithSyntaxRoot(treeWithFormattedTrivia.GetRoot()), options).Result .GetSyntaxTreeAsync().Result; var simplifiedTriviaLookup = GetSimplifiedCommentLookup(simplifiedTree, metadataReferences); var rw = new MoveCommentsRewriter(compilation.GetSemanticModel(initialTree), simplifiedTree.GetRoot(), simplifiedTriviaLookup); var finalTree = rw.Visit(initialTree.GetRoot()).SyntaxTree; if (initialTree != finalTree) { // Need to call format here because comments are inserted at the 0th column when rewriting the syntax tree. var formattedRootNode = Formatter.Format(finalTree.GetRoot(), workspace, options); Console.WriteLine($"Saving file: {document.FilePath}"); SourceText newText = formattedRootNode.GetText(); using (var writer = new StreamWriter(document.FilePath, append: false, encoding: text.Encoding)) { newText.Write(writer); } } } Console.WriteLine("Press ENTER to exit;"); Console.ReadLine(); }