2014-01-29 1 views
21

Я выяснил, как открыть решение, а затем выполнить итерацию через проекты, а затем документы. Я зациклился на том, как искать классы C#, Enums, Structs и Interfaces, которые могут иметь постороннюю точку с запятой в конце объявления (стиль C++). Я хотел бы удалить их и сохранить файлы .cs обратно на диск. В моей нынешней компании написано около 25 решений, с которыми я бы справился. Примечание. Причина, по которой мы это делаем, - продвижение вперед с лучшим набором стандартов кодирования. (И я хотел бы узнать, как использовать Рослин, чтобы сделать эти 'простой' корректировки)Удалите лишние точки с запятой в C# с помощью Roslyn - (замените w пустым пустяком)

Пример (ОБНОВЛЕНО):

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

Пример кода я хочу, чтобы Transform

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 
+0

Я думаю, я могу найти узел, используя TypeOf (), но я не знаю, как сказать «следующий токен» оттуда. У меня есть кодовая база у меня дома, я обновлю вопрос с тем, что у меня есть сегодня. Благодарю. Мне в основном нужна помощь с «следующий токен, если с запятой преобразовать, чтобы удалить точку с запятой», а затем сохранить обратно на диск. – BuddyJoe

+2

Точка с запятой не будет токеном NEXT, это будет последний токен класса. Вы можете вызвать GetLastToken и проверить его вид, или есть именованное свойство, которое будет либо по умолчанию (SyntaxToken), либо действительным токеном. Я думаю, что это будет только SelicolonToken. –

+0

Можете ли вы изменить свой вопрос на простом примере, чтобы я был на 100% уверен, что вы пытаетесь снять. Благодаря! –

ответ

10

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

Надеюсь, это нечто похожее на то, что вы искали.

8

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

класса декларация: атрибуты OPT класс модификаторов неавтоматического частичная неавтоматического идентификатор класса типа список параметров неавтоматического класс база неавтоматического типа параметров-ограничений-положения opt класс-корпус ; выбрать

UPDATE

Согласно документации Рослин, вы не можете реально изменить синтаксических деревьев - как они неизменные структуры. Вероятно, это причина, по которой kind - только для чтения. Однако вы можете создать новое дерево, используя методы 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; взгляните на this particular walkthrough. Следующие команды написать преобразованное дерево обратно в исходный файл:

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

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

+4

Синтаксические деревья Roslyn не являются абстрактными. Они полны верности и включают все токены и «мелочи» (пробелы, комментарии, предпроцессорные директивы и т. Д.). Вы правы в том, что токен является частью декларации класса. –

+0

Не знал этого; благодаря! – rla4

+0

Я дошел до того, что нашел вчерашнюю мелочь. Опубликует обновление для кода позже сегодня. Просто не понимаю, как связать все это вместе. Спасибо rla4 и Кевин – BuddyJoe