2015-07-09 10 views
6

Sto tentando di sostituire un paio di nodi in un albero di sintassi utilizzando roslyn. Ma la natura immutabile sembra mettersi sulla mia strada.Sostituzione di più nodi nell'albero di sintassi di Roslyn

public static string Rewrite(string content) 
    { 
     var tree = CSharpSyntaxTree.ParseText(content); 
     var root = tree.GetRoot(); 

     var methods =root 
      .DescendantNodes(node=>true) 
      .OfType<MethodDeclarationSyntax>() 
      .ToList(); 

     foreach(var method in methods) 
     { 
      var returnActions = method 
       .DescendantNodes(node => true) 
       .OfType<BinaryExpressionSyntax>() 
       //Ok this is cheating 
       .Where(node => node.OperatorToken.ValueText == "==") 
       .Where(node => node.Right.ToString() == "\"#exit#\"" || node.Right.ToString() == "\"#break#\"") 
       .Select(node => node.Parent as IfStatementSyntax) 
       .ToList(); 

      var lookup = new Dictionary<StatementSyntax,StatementSyntax>(); 

      if (returnActions.Count > 0) 
      { 
       foreach(var ifStatement in returnActions) 
       { 
        var mainCall = ifStatement.GetPrevious() as ExpressionStatementSyntax;       
        var newIfStatement = ifStatement.WithCondition(mainCall.Expression.WithoutTrivia()); 

        lookup[mainCall] = null; 
        lookup[ifStatement] = newIfStatement; 
       } 

       //this only replace some of the nodes 
       root = root.ReplaceNodes(lookup.Keys, (s, d) => lookup[s]); 
      } 
     } 

     return root.ToFullString(); 
    } 

Il problema è che quando chiamo root.ReplaceNodes solo alcuni dei nodi viene sostituito.

Immagino che la sostituzione cambi l'albero in modo che gli altri nodi non corrispondano più all'albero originale e quindi non possono essere sostituiti.

Ma qual è il modo migliore per affrontare questo?

Looping sul processo più e più volte fino a non più il cambiamento si verifica sente zoppo :)

I cambiamenti possono avvenire annidati, e penso che è ciò che provoca i problemi qui. Posso ordinare il changeset in qualche modo per aggirare questo o c'è un modo idiomatico di fare le cose qui?

risposta

10

Immagino che la sostituzione cambi l'albero in modo che gli altri nodi non corrispondano più all'albero originale e quindi non possa essere sostituito.

Hai ragione. La sostituzione dei nodi crea alberi di sintassi completamente nuovi. I nodi degli alberi di sintassi precedenti non possono essere confrontati con questi nuovi alberi di sintassi.

Ci sono quattro modi per applicare più modifiche ad un albero di sintassi:

  1. Usare il DocumentEditor - Vedi: https://stackoverflow.com/a/30563669/300908
  2. Usa Annotations (linee 235 e 239)
  3. Usa .TrackNodes()
  4. Crea a CSharpSyntaxRewriter che sostituisce i nodi in un approccio dal basso verso l'alto. Ho scritto su questo sul mio blog.

Di queste opzioni, credo che il DocumentEditor abbia la reputazione di essere il più semplice da usare. Potrebbe benissimo essere il modo idiomatico di applicare più cambiamenti in futuro.

Problemi correlati