2016-07-17 156 views
6

Per esempio, ho il seguente commento di documentazione nel mio file di codice C#:Sostituire trivia con il nodo a Roslyn

/// add k+5 

voglio SOSTITUIRE con il nodo

_tst.AddElement(k+5); 

Come può Lo faccio usando C#/Roslyn? Ho trovato come aggiungere questa linea, ma non ho trovato come sostituire. Il mio codice che aggiunge il nodo:

public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax method) 
{ 
    var newmethod = method; 
    var TestEntryArgName = "_tst"; 

    /* Adding _tst.AddElement(i); */ 
    foreach (var s in newmethod.Body.DescendantNodes()) 
    { 
     SyntaxTrivia st = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " "); 
     bool fl = false; 
     bool before = true; 
     var lt = s.GetLeadingTrivia(); 

     foreach (var triviaEntry in lt) 
     { 
      if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) 
      { 
       fl = true; 
       st = triviaEntry; 
       break; 
      } 
     } 

     if (!fl) 
     { 
      lt = s.GetTrailingTrivia(); 
      before = false; 
      foreach (var triviaEntry in lt) 
      { 
       if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) 
       { 
        fl = true; 
        st = triviaEntry; 
        break; 
       } 
      } 
      if (!fl) continue; 
     } 

     var commentContents = st.ToString(); 
     char[] delim = { ' ', '\n', '\t', '\r' }; 
     var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries); 
     if (ar.Length != 2 || ar[0] != "add") continue; 

     var lineToAdd = TestEntryArgName + ".AddElement(" + ar[1] + ")"; 
     var linelist = new List<ExpressionStatementSyntax>(); 
     linelist.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(lineToAdd))); 

     var childlist = s.Parent.ChildNodes(); 

     foreach (var si in childlist) 
     { 
      if (s != si) continue; 
      if (before) newmethod = newmethod.InsertNodesBefore(si, linelist); 
      else newmethod = newmethod.InsertNodesAfter(si, linelist); 
      break; 
     } 

     break; 
    } 

    return newmethod; 
} 

Ho bisogno di sostituire tutti questi commenti nel mio metodo. Questa funzione inserisce solo il nodo e lo fa solo una volta.

Modifica. In questo momento, ho la seguente soluzione, ma sembra troppo complicato e non ovvio ...

public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax method) 
    { 
     var TestEntryArgName = "__tst"; 
     /* Adding last param */ 
     var parlist = method.ChildNodes().OfType<ParameterListSyntax>().First(); 
     var newparlist = parlist.AddParameters(SyntaxFactory.Parameter(
                   SyntaxFactory.Identifier(TestEntryArgName)) 
                       .WithType(SyntaxFactory.ParseTypeName("Heap "))); 
     var newmethod = method.ReplaceNode(parlist, newparlist); 

     /* Adding __tst.AddElement(i); */ 
     while (true) { 
      IEnumerable<SyntaxNode> desc; 
      bool triviaFound; 
      desc = newmethod.Body.DescendantNodes(); 
      triviaFound = false; 
      foreach (var s in desc) 
      { 
       SyntaxTrivia st = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " "); 
       bool fl = false; 
       bool before = true; 
       var lt = s.GetLeadingTrivia(); 

       foreach (var triviaEntry in lt) 
       { 
        if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) 
        { 
         fl = true; 
         st = triviaEntry; 
         break; 
        } 
       } 

       if (!fl) 
       { 
        lt = s.GetTrailingTrivia(); 
        before = false; 
        foreach (var triviaEntry in lt) 
        { 
         if (triviaEntry.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) 
         { 
          fl = true; 
          st = triviaEntry; 
          break; 
         } 
        } 
        if (!fl) continue; 
       } 

       var commentContents = st.ToString(); 
       char[] delim = { ' ', '\n', '\t', '\r' }; 
       var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries); 
       if (ar.Length != 2 || ar[0] != "add") continue; 

       var lineToAdd = TestEntryArgName + ".AddElement(" + ar[1] + ")"; 
       var linelist = new List<ExpressionStatementSyntax>(); 
       linelist.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.ParseExpression(lineToAdd))); 

       var childlist = s.Parent.ChildNodes(); 

       foreach (var si in childlist) 
       { 
        if (s != si) continue; 
        if (before) newmethod = newmethod.InsertNodesBefore(si, linelist); 
        else newmethod = newmethod.InsertNodesAfter(si, linelist); 
        break; 
       } 

       var newTrvias = newmethod.DescendantTrivia().Where((t) => 
           { 
            if (t.Kind() != SyntaxKind.SingleLineDocumentationCommentTrivia) 
             return false; 
            var arr = t.ToString().Split(delim, StringSplitOptions.RemoveEmptyEntries); 
            return arr.Length == 2 && arr[0] == "add"; 
           }); 

       newmethod = newmethod.ReplaceTrivia(newTrvias.First(), SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")); 
       triviaFound = true; 
       break; 
      } 
      if (!triviaFound) break; 
     } 
     return newmethod; 
    } 

risposta

3

quello che stai cercando è probabilmente CSharpSyntaxRewriter.This è una classe a Roslyn che visita ogni nodo , gettone e curiosità sul modello sintattico del tuo codice. È possibile rendere il proprio masterizzatore, che sostituisce VisitTrivia e restituisce l'espressione Volete get.For esempio:

public class MyRewriter : CSharpSyntaxRewriter 
{ 
public MyRewriter(): base(true) 
{ 
} 
public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia) 
{ 
    if(trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) 
    { 
     string xml = trivia.ToFullString(); 

     var TestEntryArgName = "__tst"; 

     char[] delim = { ' ', '\n', '\t', '\r' }; 
     var ar = xml.Split(delim, StringSplitOptions.RemoveEmptyEntries); 
     if (ar.Length != 3 || ar[1] != "add") 
     { 
      return base.VisitTrivia(trivia); 
     } 
     var lineToAdd = TestEntryArgName + ".AddElement(" + ar[2] + ")"; 

     var expression = SyntaxFactory.SyntaxTrivia(SyntaxKind.SingleLineCommentTrivia, lineToAdd); 

     return expression; 
    } 
    return base.VisitTrivia(trivia); 
    } 
} 

uso del campione:

var myRewriter = new MyRewriter(); 

    string code = ""; 

    using (StreamReader sr = new StreamReader("Program.cs")) 
    { 
     code = sr.ReadToEnd(); 
    } 
    var tree = CSharpSyntaxTree.ParseText(code); 
    var node = tree.GetRoot(); 


    using(StreamWriter sw = new StreamWriter("Program.cs")) 
    { 
     sw.Write(myRewriter.Visit(node)); 
    } 
+0

Oh, è bellissimo! Grazie! –

+0

L'ho provato, e non funziona: D Questo metodo sovrascritto non è chiamato affatto :(Dove potrebbe essere il problema? –

+0

Devi definire un costruttore per il tuo 'CSharpSyntaxRewriter' e chiamare il costruttore di base() con un parametro che dice di visitare la curiosità – JoshVarty

1

ho trovato una soluzione semplice, e penso, che mi va bene, ma, lo so, non può essere definito una soluzione "giusta", perché riguarda il codice sorgente. Ad ogni modo:

public static MethodDeclarationSyntax getChangedNode(MethodDeclarationSyntax newmethod) 
{ 
    /* Adding __tst.AddElement(i); */ 
    while (true) 
    { 
     var desc = newmethod.Body.DescendantTrivia().Where((t) => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)); 
     var triviaFound = false; 

     foreach (var s in desc) 
     { 
      var commentContents = s.ToString(); 
      char[] delim = { ' ', '\n', '\t', '\r' }; 
      var ar = commentContents.Split(delim, StringSplitOptions.RemoveEmptyEntries); 
      if (ar.Length != 2 || ar[0] != "add") continue; 

      var lineToAdd = "\r\n__tst.AddElement(" + ar[1] + ");\r\n"; 
      newmethod = CSharpSyntaxTree.ParseText(newmethod.GetText().Replace(s.FullSpan, lineToAdd)) 
       .GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First(); 
      triviaFound = true; 
      break; 
     } 
     if (!triviaFound) break; 
    } 
    return newmethod; 
}