c# - tutorial - roslyn version
Roslynを使ってC#で余分なセミコロンを削除する-(空のトリビアに置き換えて) (2)
ここには、ソリューション内のすべてのクラス、構造体、インタフェース、および列挙型宣言の後にオプションのセミコロンを削除する小さなプログラムがあります。 プログラムは、ソリューション内のドキュメントをループし、 SyntaxWriter
を書き換えるためにSyntaxWriterを使用します。 変更が加えられた場合、元のコードファイルは新しい構文で上書きされます。
using System;
using System.IO;
using System.Linq;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
namespace TrailingSemicolon
{
class Program
{
static void Main(string[] args)
{
string solutionfile = @"c:\temp\mysolution.sln";
var workspace = Workspace.LoadSolution(solutionfile);
var solution = workspace.CurrentSolution;
var rewriter = new TrailingSemicolonRewriter();
foreach (var project in solution.Projects)
{
foreach (var document in project.Documents)
{
SyntaxTree tree = (SyntaxTree)document.GetSyntaxTree();
var newSource = rewriter.Visit(tree.GetRoot());
if (newSource != tree.GetRoot())
{
File.WriteAllText(tree.FilePath, newSource.GetText().ToString());
}
}
}
}
class TrailingSemicolonRewriter : SyntaxRewriter
{
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
return RemoveSemicolon(node, node.SemicolonToken, t => node.WithSemicolonToken(t));
}
public override SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
{
return RemoveSemicolon(node, node.SemicolonToken, t => node.WithSemicolonToken(t));
}
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
{
return RemoveSemicolon(node, node.SemicolonToken, t => node.WithSemicolonToken(t));
}
public override SyntaxNode VisitEnumDeclaration(EnumDeclarationSyntax node)
{
return RemoveSemicolon(node, node.SemicolonToken, t => node.WithSemicolonToken(t));
}
private SyntaxNode RemoveSemicolon(SyntaxNode node,
SyntaxToken semicolonToken,
Func<SyntaxToken, SyntaxNode> withSemicolonToken)
{
if (semicolonToken.Kind != SyntaxKind.None)
{
var leadingTrivia = semicolonToken.LeadingTrivia;
var trailingTrivia = semicolonToken.TrailingTrivia;
SyntaxToken newToken = Syntax.Token(
leadingTrivia,
SyntaxKind.None,
trailingTrivia);
bool addNewline = semicolonToken.HasTrailingTrivia
&& trailingTrivia.Count() == 1
&& trailingTrivia.First().Kind == SyntaxKind.EndOfLineTrivia;
var newNode = withSemicolonToken(newToken);
if (addNewline)
return newNode.WithTrailingTrivia(Syntax.Whitespace(Environment.NewLine));
else
return newNode;
}
return node;
}
}
}
}
うまくいけば、それはあなたが探していたもののラインに沿ったものです。
私はソリューションを開き、プロジェクトとドキュメントを繰り返し処理する方法を理解しました。 私はC#クラス、列挙型、構造体、および宣言(C ++スタイル)の終わりに無関係なセミコロンを持つ可能性があるインタフェースを探す方法についています。 私はそれらを削除し、ディスクに.csファイルを保存したいと思います。 現在の会社には約25の解決策が書かれています。 注:私たちがこれをやっている理由は、より良いコーディング標準セットを進めることです。 (そして、私はRoslynを使ってこれらの「単純な」調整を行う方法を学びたいと思っています)
例(更新済み) :
class Program
{
static void Main(string[] args)
{
string solutionFile = @"S:\source\dotnet\SimpleApp\SimpleApp.sln";
IWorkspace workspace = Workspace.LoadSolution(solutionFile);
var proj = workspace.CurrentSolution.Projects.First();
var doc = proj.Documents.First();
var root = (CompilationUnitSyntax)doc.GetSyntaxRoot();
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var decl in classes)
{
ProcessClass(decl);
}
Console.ReadKey();
}
private static SyntaxNode ProcessClass(ClassDeclarationSyntax node)
{
ClassDeclarationSyntax newNode;
if (node.HasTrailingTrivia)
{
foreach (var t in node.GetTrailingTrivia())
{
var es = new SyntaxTrivia();
es.Kind = SyntaxKind.EmptyStatement;
// kind is readonly - what is the right way to create
// the right SyntaxTrivia?
if (t.Kind == SyntaxKind.EndOfLineTrivia)
{
node.ReplaceTrivia(t, es);
}
}
return // unsure how to do transform and return it
}
}
変換したいコードの例
using System;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
};
// note: the semicolon at the end of the Person class
この情報はClassDeclarationノードに格納する必要があります.C#仕様では、セミコロンはプロダクションの終わりにオプションのトークンであるため、
class-declaration:属性opt class-modifiers opt部分的なクラスの識別子type-parameter-list optのクラスベースoptのタイプ-param -constraints-clauses opt class-body ; オプト
更新
Roslynの文書によると、構文木は実際には変更できません。変更可能な構造であるからです。 それはおそらくkind
が読み込み専用である理由です。 ただし、 With*
メソッドを使用して、変更可能なツリープロパティごとに定義し、 ReplaceNode
を使用して、新しいツリーを作成することもできます。 Roslynのドキュメントには良い例があります:
var root = (CompilationUnitSyntax)tree.GetRoot();
var oldUsing = root.Usings[1];
var newUsing = oldUsing.WithName(name); //changes the name property of a Using statement
root = root.ReplaceNode(oldUsing, newUsing);
新しいツリーをもう一度コードに変換するには、コンパイル単位ノード(この例ではroot
変数)からGetText()
メソッドを使用できます。
コード変換を実行するためのSyntaxRewriterクラスを拡張することもできます。 公式のRoslynのウェブサイトでこれを行うための広範な例があります。 この特定のウォークスルーを見てください。 次のコマンドは、変換されたツリーを元のファイルに書き戻します。
SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
if (newSource != sourceTree.GetRoot())
{
File.WriteAllText(sourceTree.FilePath, newSource.GetFullText());
}
SyntaxRewriter
はSyntaxRewriter
インスタンスです。