如何在Roslyn中的region指令后插入变量声明?我希望能够做到这样的事情:
class MyClass
{
#region myRegion
#endregion
}
到此:
class MyClass
{
#region myRegion
private const string myData = "somedata";
#endregion
}
我似乎找不到任何以这种方式处理琐事的例子。
答案 0 :(得分:3)
使用CSharpSyntaxRewriter非常棘手,因为#region <name>
和#endregion
最终都在同一个SyntaxTriviaList
中,而你{&#39}必须拆分并找出要创建的内容。不打扰所有错综复杂的最简单方法是创建相应的TextChange
并修改SourceText
。
var tree = SyntaxFactory.ParseSyntaxTree(
@"class MyClass
{
#region myRegion
#endregion
}");
// get the region trivia
var region = tree.GetRoot()
.DescendantNodes(descendIntoTrivia: true)
.OfType<RegionDirectiveTriviaSyntax>()
.Single();
// modify the source text
tree = tree.WithChangedText(
tree.GetText().WithChanges(
new TextChange(
region.GetLocation().SourceSpan,
region.ToFullString() + "private const string myData = \"somedata\";")));
之后,tree
是:
class MyClass
{
#region myRegion
private const string myData = "somedata";
#endregion
}
答案 1 :(得分:0)
m0sa的答案适用于空白区域,但不能替换现有代码,这可能是需要这样做的原因,即重新运行代码生成工具。
要实现这一目标,需要找到整个区域。由于目标文件可能包含多个嵌套区域,因此这也很困难。为此,我处理所有指令并建立区域层次结构:
public class RegionInfo
{
public RegionDirectiveTriviaSyntax Begin;
public EndRegionDirectiveTriviaSyntax End;
public RegionInfo Parent;
public List<RegionInfo> Children = new List<RegionInfo>();
public string Name => this.Begin.EndOfDirectiveToken.ToFullString().Trim();
}
public static class CodeMutator
{
public static string ReplaceRegion(string existingCode, string regionName, string newCode)
{
var syntaxTree = CSharpSyntaxTree.ParseText(existingCode);
var region = CodeMutator.GetRegion(syntaxTree, regionName);
if (region == null)
{
throw new Exception($"Cannot find region named {regionName}");
}
return
existingCode
.Substring(0, region.Begin.FullSpan.End) +
newCode +
Environment.NewLine +
existingCode
.Substring(region.End.FullSpan.Start);
}
static RegionInfo GetRegion(SyntaxTree syntaxTree, string regionName) =>
CodeMutator.GetRegions(syntaxTree)
.FirstOrDefault(x => x.Name == regionName);
static List<RegionInfo> GetRegions(SyntaxTree syntaxTree)
{
var directives = syntaxTree
.GetRoot()
.DescendantNodes(descendIntoTrivia: true)
.OfType<DirectiveTriviaSyntax>()
.Select(x => (x.GetLocation().SourceSpan.Start, x))
.OrderBy(x => x.Item1)
.ToList();
var allRegions = new List<RegionInfo>();
RegionInfo parent = null;
foreach (var directive in directives)
{
if (directive.Item2 is RegionDirectiveTriviaSyntax begin)
{
var next = new RegionInfo() {Begin = begin, Parent = parent};
allRegions.Add(next);
parent?.Children.Add(next);
parent = next;
}
else if (directive.Item2 is EndRegionDirectiveTriviaSyntax end)
{
if (parent == null)
{
Log.Error("Unmatched end region");
}
else
{
parent.End = end;
parent = parent.Parent;
}
}
}
return allRegions;
}
}