Удалите лишние точки с запятой в C# с помощью Roslyn - (заменить W пустые мелочи)

я выяснил, как открыть решение, а затем повторить проекты, а затем документы. Я застрял в том, как искать классы C#, перечисления, структуры и интерфейсы, которые могут иметь постороннюю точку с запятой в конце объявления (стиль c++). Я хотел бы удалить их и сохранить .CS файлы обратно на диск. В моей текущей компании есть примерно 25 решений, которые я бы использовал. Примечание: причина, по которой мы делаем это, чтобы двигаться вперед с большим набором стандартов кодирования. (И я хотел бы узнать, как использовать Roslyn для этих "простых" настроек)

пример (обновлено):

class Program
{
    static void Main(string[] args)
    {
        string solutionFile = @"S:sourcedotnetSimpleAppSimpleApp.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

2 ответов


вот небольшая программа, которая удаляет необязательную точку с запятой после всех объявлений класса, структуры, интерфейса и перечисления в решении. Программа перебирает документы в решении, и использует 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;
      }
    }
  }
}

надеюсь, это что-то вроде того, что вы искали.


эта информация должна храниться в узле ClassDeclaration-поскольку, согласно спецификации C#, двоеточие является необязательным маркером в конце его производства:

класс-декларации: атрибуты opt класс-модификаторы opt частичная opt тип идентификатора класса-параметр-список opt класс-База opt Type-parameter-constraints-clauses opt класс-тела ; opt

обновление

согласно документации Roslyn, вы не можете фактически изменить синтаксические деревья-поскольку они являются неизменяемыми структурами. Вероятно, именно поэтому kind находится в состоянии readonly. Однако, вы можете создать новое дерево, используя With* методы, определенные для каждого изменяемого свойства дерева и использующие ReplaceNode. Есть хороший пример на Документация Рослин:

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 для выполнения преобразований кода. Существует обширный пример для этого на официальном сайте Roslyn; взгляните на это конкретное пошаговое руководство. Следующие команды пишут преобразованное дерево обратно в исходный файл:

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

где rewriter является экземпляром SyntaxRewriter.