Supprimer les points-virgules superflus en C # en utilisant Roslyn - (remplacer W trivia vide)

J'ai compris comment ouvrir une solution, puis parcourir les projets, puis les Documents. Je suis coincé avec la façon de rechercher des Classes C#, des énumérations, des structures et des Interfaces qui peuvent avoir un point-virgule étranger à la fin de la déclaration (style c++). Je voudrais supprimer et enregistrer la .fichiers cs sur le disque. Il y a environ 25 solutions écrites dans mon entreprise actuelle contre lesquelles je courrais. Remarque: la raison pour laquelle nous faisons cela est d'aller de l'avant avec un meilleur ensemble de les normes de codage. (Et je voudrais apprendre à utiliser Roslyn pour faire ces ajustements "simples")

Exemple (mise à jour):

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

Exemple de Code que je Veux Transformer

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
21
demandé sur Zword 2014-01-29 19:38:23

2 réponses

Voici un petit programme qui supprime le point-virgule facultatif après toutes les déclarations class, struct, interface et enum dans une solution. Le programme parcourt les documents de la solution et utilise un SyntaxWriter pour réécrire l'arbre syntaxique. Si des modifications ont été apportées, les fichiers de code d'origine sont remplacés par la nouvelle syntaxe.

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

J'espère que c'est quelque chose comme ce que vous cherchiez.

10
répondu Daniel Brixen 2014-02-09 16:07:51

Cette information devrait être stockée dans le noeud ClassDeclaration-car, selon la spécification C#, le point-virgule est un jeton facultatif à la fin de ses productions:

Classe-déclaration: attributsopt classe-modificateursopt partielleopt identificateur de classe de type de paramètre de la listeopt la classe de baseopt type de paramètre-les contraintes-les clausesopt classe-corps ;opt

Mise à JOUR

Selon la documentation de Roslyn, vous ne pouvez pas réellement changer les arbres de syntaxe-car ce sont des structures immuables. C'est probablement la raison pour laquelle kind est en lecture seule. Vous pouvez toutefois créer un nouvel arbre en utilisant les méthodes With*, définies pour chaque propriété d'arbre modifiable, et en utilisant ReplaceNode. Il y a un bon exemple sur la documentation de 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);

Pour convertir à nouveau votre nouvel arbre en code (aka pretty printing), vous pourrait utiliser la méthode GetText() du nœud de l'Unité de compilation (dans notre exemple, la variable root).

Vous pouvez également étendre une classe SyntaxRewriter pour effectuer des transformations de code. Il y a un exemple complet pour le faire sur le site officiel de Roslyn; jetez un oeil à Cette procédure pas à pas particulière. Les commandes suivantes écrivent l'arborescence transformée dans le fichier d'origine:

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

Où rewriter est une instance de SyntaxRewriter.

8
répondu rla4 2014-02-06 09:15:43