С# Roslyn изменить тип комментария

Я пытаюсь сделать расширение для Visual Studio, которое меняет некоторый синтаксис в коде. На самом деле, я сделал первый шаг, который состоял в том, чтобы изменить имя переменной, если это имя не соответствовало правилам, которые мы используем в фирме. Например:

int newVariable;
double test;

будет изменено на:

int iNewVariable;
double dblTest;

Теперь мне нужно изменить этот тип комментария: (SingleLineComment)

//this is a single line Comment

В многострочный комментарий

/*Here it's a MultiLine one*/

Я использую визуализатор синтаксиса Roslyn, чтобы найти тип и вид для создания правильного кода, но ничего не работает. Вот что я сделал для диагностики:

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace CodeFix
{
    [DiagnosticAnalyzer]
    [ExportDiagnosticAnalyzer(DiagnosticId, LanguageNames.CSharp)]
    public class DiagnosticAnalyzer : ISyntaxNodeAnalyzer<SyntaxKind>
    {
        internal const string DiagnosticId = "CodeFix";
        internal const string Description = "Mauvais formattage";
        internal const string MessageFormat = "'{0}'";
        internal const string Category = "Correction";

        internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Description, MessageFormat, Category, DiagnosticSeverity.Warning);

        public ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get { return ImmutableArray.Create(Rule); }
        }
        public ImmutableArray<SyntaxKind> SyntaxKindsOfInterest //Ce qui nous intéresse
        {
            get
            {
                return ImmutableArray.Create(SyntaxKind.IfStatement, SyntaxKind.ElseClause, SyntaxKind.LocalDeclarationStatement, SyntaxKind.ConstKeyword, SyntaxKind.SingleLineCommentTrivia, SyntaxKind.SimpleAssignmentExpression);
            }
        }
        public void AnalyzeNode(SyntaxNode node, SemanticModel model, Action<Diagnostic> addDiagnostic, CancellationToken cancellationToken) //Analyse des Nodes
        {

            var ifStatement = node as IfStatementSyntax; //Récupération des IfStatement parmis tous les nodes
            if (ifStatement != null &&
                ifStatement.Statement != null &&
                !ifStatement.Statement.IsKind(SyntaxKind.Block))
            {
                addDiagnostic(Diagnostic.Create(Rule, ifStatement.IfKeyword.GetLocation(), "Le if require des crochets"));
            }

            var elseClause = node as ElseClauseSyntax; //Récupération des Else parmis tous les nodes
            if (elseClause != null &&
                elseClause.Statement != null &&
                !elseClause.Statement.IsKind(SyntaxKind.Block) && //Pas que ce soit déjà un block avec {}
                !elseClause.Statement.IsKind(SyntaxKind.IfStatement)) //A cause des else if
            {
                addDiagnostic(Diagnostic.Create(Rule, elseClause.ElseKeyword.GetLocation(), "le else require des crochets"));
            }

        }
    }
    internal class IDiagnosticAnalyzer : ISyntaxTreeAnalyzer
    {
        internal const string DiagnosticIdComment = "CommentChanger";
        internal const string DescriptionComment = "Les commentaires doivent être en format /* */";
        internal const string MessageFormatComment = "'{0}' doit être en multiline";
        internal const string CategoryComment = "Renommage";

        internal static DiagnosticDescriptor RuleComment = new DiagnosticDescriptor(DiagnosticIdComment, DescriptionComment, MessageFormatComment, CategoryComment, DiagnosticSeverity.Warning);

        public ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get { return ImmutableArray.Create(RuleComment); }
        }

        public void AnalyzeSyntaxTree(SyntaxTree tree, Action<Diagnostic> addDiagnostic, CancellationToken cancellationToken)
        {
            var root = tree.GetRoot();
            var trivia = root.DescendantTrivia();
            var a = trivia.Where(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia)).ToList();

            foreach (var b in a)
            {
                addDiagnostic(Diagnostic.Create(RuleComment, b.GetLocation(), "Commentaire sur une ligne"));
            }
        }
    }

}

Вот кодфикс:

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Formatting;
using System;

namespace CodeFix
{
    [ExportCodeFixProvider(DiagnosticAnalyzer.DiagnosticId, LanguageNames.CSharp)]
    internal class CodeFixProvider : ICodeFixProvider
    {
        public IEnumerable<string> GetFixableDiagnosticIds()
        {
            return new[] { DiagnosticAnalyzer.DiagnosticId };
        }

        public async Task<IEnumerable<CodeAction>> GetFixesAsync(Document document, TextSpan span, IEnumerable<Diagnostic> diagnostics, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken); //Document à utiliser (root)
            var token = root.FindToken(span.Start); //


            if (token.IsKind(SyntaxKind.IfKeyword))
            {
                var ifStatement = (IfStatementSyntax)token.Parent;
                var newIfStatement = ifStatement
                    .WithStatement(SyntaxFactory.Block(ifStatement.Statement))
                    .WithAdditionalAnnotations(Formatter.Annotation); //Pour que ce soit indenté juste

                var newRoot = root.ReplaceNode(ifStatement, newIfStatement); 

                return new[] { CodeAction.Create("Ajouter des crochets", document.WithSyntaxRoot(newRoot)) };
            }

            if (token.IsKind(SyntaxKind.ElseKeyword))
            {
                var elseClause = (ElseClauseSyntax)token.Parent;
                var newElseClause = elseClause
                    .WithStatement(SyntaxFactory.Block(elseClause.Statement))
                    .WithAdditionalAnnotations(Formatter.Annotation);

                var newRoot = root.ReplaceNode(elseClause, newElseClause);

                return new[] { CodeAction.Create("Ajouter des crochets", document.WithSyntaxRoot(newRoot)) };
            }

            if (token.IsKind(SyntaxKind.SingleLineCommentTrivia))
            {

                var root1 = await document.GetSyntaxRootAsync(cancellationToken);
                var token1 = root1.FindToken(span.Start);
                var allTrivia = token1.GetAllTrivia();
                foreach (var singleTrivia in allTrivia)
                {
                    if (singleTrivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
                    {
                        var commentContent = singleTrivia.ToString().Replace("//", string.Empty);
                        var newComment = SyntaxFactory.Comment(string.Format("/*{0}*/", commentContent));
                        var newRoot = root.ReplaceTrivia(singleTrivia, newComment);
                        return new[] { CodeAction.Create("Convert to multiline", document.WithSyntaxRoot(newRoot)) };
                    }
                }

            }    

            return null;
        }    
    }

}

Пользователь должен нажать на рекламу, которую дает моя программа, и комментарий будет изменен.

Но моя программа никогда не входит туда, где я должен вызвать метод.

Я делаю свой первый шаг с Рослин, так что я еще многого не знаю, но я узнаю об этом..

Изменить:

Весь код добавлен


person Maloz    schedule 22.04.2014    source источник


Ответы (1)


Я собрал пример, используя CodeFix с диагностикой, которая заменяет однострочные комментарии многострочными. Вот часть кода. Многое из этого основано на демо-версии Roslyn Дастина Кэмпбелла из Build, которую можно увидеть по адресу http://channel9.msdn.com/Events/Build/2014/2-577

Реализация AnalyzeSyntaxTree от ISyntaxTreeAnalyzer для поиска однострочных комментариев:

[DiagnosticAnalyzer]
[ExportDiagnosticAnalyzer(DiagnosticId, LanguageNames.CSharp)]
internal class DiagnosticAnalyzer : ISyntaxTreeAnalyzer
{
    internal const string DiagnosticId = "CommentChanger";
    internal const string Description = "Single comments should be multiline comments";
    internal const string MessageFormat = "'{0}' should be multiline";
    internal const string Category = "Naming";

    internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Description, MessageFormat, Category, DiagnosticSeverity.Warning);

    public ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
    {
        get { return ImmutableArray.Create(Rule); }
    }

    public void AnalyzeSyntaxTree(SyntaxTree tree, Action<Diagnostic> addDiagnostic, CancellationToken cancellationToken)
    {
        var root = tree.GetRoot();
        var trivia = root.DescendantTrivia();
        var a = trivia.Where(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia)).ToList();

        foreach(var b in a)
        {
            addDiagnostic(Diagnostic.Create(Rule, b.GetLocation(), "Single comment"));
        }
    }
}

Реализация GetFixesAsync от ICodeFixProvider:

public async Task<IEnumerable<CodeAction>> GetFixesAsync(Document document, TextSpan span, IEnumerable<Diagnostic> diagnostics, CancellationToken cancellationToken)
    {
        var root = await document.GetSyntaxRootAsync(cancellationToken);
        var token = root.FindToken(span.Start);
        var allTrivia = token.GetAllTrivia();
        foreach(var singleTrivia in allTrivia)
        {
            if (singleTrivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
            {
                var commentContent = singleTrivia.ToString().Replace("//", string.Empty);
                var newComment = SyntaxFactory.Comment(string.Format("/*{0}*/", commentContent));
                var newRoot = root.ReplaceTrivia(singleTrivia, newComment);
                return new[] { CodeAction.Create("Convert to multiline", document.WithSyntaxRoot(newRoot)) };
            }
        }           
        return null;
    }
person rh072005    schedule 22.04.2014
comment
Спасибо! Но у меня есть вопрос. Должен ли я поместить код в: открытый класс DiagnosticAnalyzer: ISyntaxNodeAnalyzer‹SyntaxKind› {} Или в другой класс, подобный этому, открытый класс IDiagnosticAnalyzer: ISyntaxTreeAnalyzer {} - person Maloz; 25.04.2014
comment
извинения за медленный ответ. В этом примере используется ISyntaxTreeAnalyzer — я добавил остальную часть кода для класса к приведенному выше. Вы обнаружите, что метод AnaylzeSyntaxTree реализован интерфейсом ISyntaxTreeAnalyzer. - person rh072005; 28.04.2014
comment
Итак, это то, что я уже пробовал. Но, может быть, наличие 2 класса в DiagnosticAnalyzer - это проблема? Потому что никакой диагностики у меня нет... Даже если я возьму твой код, тот самый, копипаст. Нет ошибки, но нет диагностики. Единственное отличие состоит в том, что у меня есть другой класс в моем DiagnosticAnalyzer.cs, который используется для анализа некоторых узлов: открытый класс DiagnosticAnalyzer : ISyntaxNodeAnalyzer‹SyntaxKind› {} - person Maloz; 28.04.2014
comment
Я бы попробовал это с двумя отдельными файлами для двух классов. Также, если вы опубликуете весь свой код, я посмотрю, почему два класса конфликтуют. - person rh072005; 28.04.2014
comment
Я скопировал ваш код, и он собрался и запустился без каких-либо проблем. И диагностика, и исправления сработали, поэтому похоже, что проблема не в вашем коде. У меня были проблемы с несколькими диагностиками, конфликтующими друг с другом. Попробуйте запустить экземпляр отладки Visual Studio и удалить все другие средства диагностики, затем очистите и перестройте приведенный выше код и повторите попытку. - person rh072005; 28.04.2014
comment
Спасибо! Теперь это работает! Я понял, почему раньше не было. В моем CodeFix в верхней части кода я использовал не IDiagnosticAnalyzer, а IDiagnosticAnalyzer , поэтому класс с мелочами не использовался. - person Maloz; 29.04.2014