使用Roslyn删除C#中的外来分号 - (替换w空琐事)

 丹丹2502857623 发布于 2023-01-30 15:52

我已经想出了如何打开解决方案然后遍历项目然后迭代文档.我一直在寻找如何查找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();
        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

小智.. 10

这是一个小程序,它在解决方案中的所有类,结构,接口和枚举声明之后删除可选分号.程序循环遍历解决方案中的文档,并使用a SyntaxWriter来重写syntaxtree.如果进行了任何更改,则使用新语法覆盖原始代码文件.

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 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;
      }
    }
  }
}

希望它与您所寻找的一致.

2 个回答
  • 这是一个小程序,它在解决方案中的所有类,结构,接口和枚举声明之后删除可选分号.程序循环遍历解决方案中的文档,并使用a SyntaxWriter来重写syntaxtree.如果进行了任何更改,则使用新语法覆盖原始代码文件.

    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;
          }
        }
      }
    }
    

    希望它与您所寻找的一致.

    2023-01-30 15:53 回答
  • 此信息必须存储在ClassDeclaration节点中 - 因为根据C#规范,分号是其产生结束时的可选标记:

    class-declaration:attributes opt class-modifiers opt partial opt class identifier type-parameter-list opt class-base opt type-parameter-constraints-clauses opt class-body ; 选择

    UPDATE

    根据Roslyn的文档,您实际上无法更改语法树 - 因为它们是不可变结构.这可能kind是readonly 的原因.但是,您可以使用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);
    

    为了将您的新树再次转换为代码(也就是漂亮的打印),您可以使用GetText()编译单元节点中的方法(在我们的示例中,root变量).

    您还可以扩展SyntaxRewriter类以执行代码转换.罗斯林官方网站上有一个广泛的例子; 看一下这个特别的演练.以下命令将转换后的树写回原始文件:

    SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
    if (newSource != sourceTree.GetRoot())
    {
        File.WriteAllText(sourceTree.FilePath, newSource.GetFullText());
    }
    

    其中rewriter是一个实例SyntaxRewriter.

    2023-01-30 15:54 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有