Roslyn - 将编辑后的文档保存到物理解决方案
Roslyn - save edited document to physical solution
我正在尝试遍历所有文档,更改一些代码,然后使用新语法重写分析过的文档。我遇到的唯一问题是我不知道如何通过工作区保存更改的文档。在我的代码执行后没有任何反应。
var manager = new AnalyzerManager(slnPath);
foreach (var project in manager.Projects.Values)
{
var workspace = project.GetWorkspace();
var sln = workspace.CurrentSolution;
foreach (var msBuildProject in sln.Projects)
{
foreach (var document in msBuildProject.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
编辑:在 Buildalyzer 包的帮助下获取工作区。
Buildalyzer
使用不持久更改的临时工作区。您想使用 MSBuildWorkspace。
你应该有一个看起来像这样的项目文件
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.3.1" />
</ItemGroup>
</Project>
然后这就是我加载您的解决方案和运行您的重写器
的方式
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.MSBuild;
class Program {
static async Task Main(string[] args) {
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances.Length == 1
// If there is only one instance of MSBuild on this machine, set that as the one to use.
? visualStudioInstances[0]
// Handle selecting the version of MSBuild you want to use.
: SelectVisualStudioInstance(visualStudioInstances);
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
using (var workspace = MSBuildWorkspace.Create()) {
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
var solutionPath = args[0];
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// Run your custome re-writer on the loaded solution
foreach (var msBuildProject in solution.Projects) {
foreach (var document in msBuildProject.Documents) {
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
}
private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
Console.WriteLine("Multiple installs of MSBuild detected please select one:");
for (int i = 0; i < visualStudioInstances.Length; i++) {
Console.WriteLine($"Instance {i + 1}");
Console.WriteLine($" Name: {visualStudioInstances[i].Name}");
Console.WriteLine($" Version: {visualStudioInstances[i].Version}");
Console.WriteLine($" MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
}
while (true) {
var userResponse = Console.ReadLine();
if (int.TryParse(userResponse, out int instanceNumber) &&
instanceNumber > 0 &&
instanceNumber <= visualStudioInstances.Length) {
return visualStudioInstances[instanceNumber - 1];
}
Console.WriteLine("Input not accepted, try again.");
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
public void Report(ProjectLoadProgress loadProgress) {
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null) {
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\:ss\.fffffff} {projectDisplay}");
}
}
}
我正在尝试遍历所有文档,更改一些代码,然后使用新语法重写分析过的文档。我遇到的唯一问题是我不知道如何通过工作区保存更改的文档。在我的代码执行后没有任何反应。
var manager = new AnalyzerManager(slnPath);
foreach (var project in manager.Projects.Values)
{
var workspace = project.GetWorkspace();
var sln = workspace.CurrentSolution;
foreach (var msBuildProject in sln.Projects)
{
foreach (var document in msBuildProject.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
编辑:在 Buildalyzer 包的帮助下获取工作区。
Buildalyzer
使用不持久更改的临时工作区。您想使用 MSBuildWorkspace。
你应该有一个看起来像这样的项目文件
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.3.1" />
</ItemGroup>
</Project>
然后这就是我加载您的解决方案和运行您的重写器
的方式using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.MSBuild;
class Program {
static async Task Main(string[] args) {
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances.Length == 1
// If there is only one instance of MSBuild on this machine, set that as the one to use.
? visualStudioInstances[0]
// Handle selecting the version of MSBuild you want to use.
: SelectVisualStudioInstance(visualStudioInstances);
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
using (var workspace = MSBuildWorkspace.Create()) {
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
var solutionPath = args[0];
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// Run your custome re-writer on the loaded solution
foreach (var msBuildProject in solution.Projects) {
foreach (var document in msBuildProject.Documents) {
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
}
private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
Console.WriteLine("Multiple installs of MSBuild detected please select one:");
for (int i = 0; i < visualStudioInstances.Length; i++) {
Console.WriteLine($"Instance {i + 1}");
Console.WriteLine($" Name: {visualStudioInstances[i].Name}");
Console.WriteLine($" Version: {visualStudioInstances[i].Version}");
Console.WriteLine($" MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
}
while (true) {
var userResponse = Console.ReadLine();
if (int.TryParse(userResponse, out int instanceNumber) &&
instanceNumber > 0 &&
instanceNumber <= visualStudioInstances.Length) {
return visualStudioInstances[instanceNumber - 1];
}
Console.WriteLine("Input not accepted, try again.");
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
public void Report(ProjectLoadProgress loadProgress) {
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null) {
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\:ss\.fffffff} {projectDisplay}");
}
}
}