Могу ли я вставить оператор в пустое тело функции, используя Roslyn DocumentEditor - PullRequest
0 голосов
/ 16 февраля 2019

В моем расширении Visual Studio я использую класс DocumentEditor (Microsoft.CodeAnalysis.Editing.DocumentEditor) для многократного обновления исходного файла.

Одно из изменений - добавление конструктора вучебный класс.Конструктор должен иметь определенный параметр, который передается путем внедрения зависимости, и должен сохранять параметр в переменной-члене.Если конструктор уже присутствует, он должен проверить параметр и проверить оператор присваивания.

Большая часть этого работает, но возникает проблема, если мне нужно добавить оператор в пустую функцию.

Если конструктор уже существует и содержит некоторые операторы, я могу добавить новый оператор с помощью

RoslynEditor.InsertBefore ( cons.Body.Statements.First(), assExpr ) ;

Если конструктор существует, но пуст, тогда это не будет работать.До сих пор я не нашел способа вставить оператор в пустое тело функции с помощью класса DocumentEditor.

В настоящее время я выбрал метод кувалды для замены завершенного конструктора с помощью DocumentEditor.ReplaceNode.

Так есть ли способ вставить инструкцию в тело функции - используя класс DocumentEditor - если тело пусто?

Вот часть моегокод.Функции в _RoslynUtilCS просто возвращают синтаксис Розлина.

// Look for the constructor
var cons = c.DescendantNodes().OfType<ConstructorDeclarationSyntax>().FirstOrDefault() ;
if ( cons == null )
{
  // There is no constructor.
  cons = _RoslynUtilCS.ControllerConstructor ( ShortClassName )
                      .WithAdditionalAnnotations ( Formatter.Annotation )
                      .WithTrailingTrivia ( _RoslynUtilCS.LineFeedSyntax() ) ;

  RoslynEditor.InsertBefore ( c.Members.First(), cons ) ;
}
else
{
  string parameterName = null ;

  // There is a constructor.
  // Does it already have a parameter of the generic IStringLocalizer type
  foreach ( var p in cons.ParameterList.Parameters )
  {
    var t  = p.Type ;
    var gt = t as GenericNameSyntax ;
    if ( gt != null )
    {
      if ( gt.Identifier.ToString() == "IStringLocalizer" )
      {
        parameterName = p.Identifier.ToString() ;
      }
    }
  }

  if ( string.IsNullOrEmpty ( parameterName ) )
  {
    // Add a parameter
    var param = _RoslynUtilCS.LocalizerParameter ( ShortClassName ) ;
    RoslynEditor.AddParameter ( cons, param ) ;

    // Add an assignment statement to assign the parameter to the localizer member variable.
    var assExpr = _RoslynUtilCS.LocalizerAsignment()
                               .WithAdditionalAnnotations ( Formatter.Annotation ) ;

    if ( cons.Body.Statements.Count == 0 )
    {
      // -------------------------------------------------------------------
      // Here I replace the complete constructor.
      // because I don't know how to insert a statement into
      // the empty constructor body.
      // -------------------------------------------------------------------
      var newcons = _RoslynUtilCS.ControllerConstructor ( ShortClassName )
                                 .WithAdditionalAnnotations ( Formatter.Annotation )
                                 .WithTrailingTrivia ( _RoslynUtilCS.LineFeedSyntax() ) ;

      RoslynEditor.ReplaceNode ( cons, newcons ) ;
    }
    else
    {
      RoslynEditor.InsertBefore ( cons.Body.Statements.First(), assExpr ) ;
    }
  }
  else
  {
    // NOT TESTED
    // It seems a bit over the top, but having found a parameter, we should look
    // for an assignment to the member variable and add it if it is missing.
    var count = cons.DescendantNodes()
                    .OfType<AssignmentExpressionSyntax>()
                    .Where(node => node.Kind() == SyntaxKind.SimpleAssignmentExpression)
                    .Where(node => node.Left.ToString() == "_localizer")
                    .Where(node => node.Right.ToString() == "localizer")
                    .Count() ;
    if ( count == 0 )
    {
      // Add an assignment statement to assign the parameter to the localizer member variable.
      var assExpr = _RoslynUtilCS.LocalizerAsignment() ;

      // -------------------------------------------------------------------
      // This is likely to have the same problem.
      // It won't work if the body contains no statements.
      // -------------------------------------------------------------------
      RoslynEditor.InsertBefore ( cons.Body.Statements.First(), assExpr ) ;
    }
  }
}

1 Ответ

0 голосов
/ 16 февраля 2019

Insert и Add метод редактора, наконец, InsertMembers из SyntaxGenerator, и они в любом случае заменяют исходный узел новым AKA Replace.

Проверьте источник код этого метода, и вы увидите, что вставка элементов в тело блока с 0 элементами (в случае ConstructorDeclarationSyntax) возвращает само объявление без изменений, поэтому вы не можете сделать простой editor.Insert\Add...

Вы можете определить новый метод расширения, который сделает это за вас, или написать что-то вроде этого, заменяя только тело:

editor.ReplaceNode(ctor.Body, SyntaxFactory.Block(newStatement));

Или

editor.ReplaceNode(ctor, ctor.WithBody(ctor.Body.AddStatements(newStatement)));
...