Question

I'm trying to make an extension for Visual Studio that changes some syntax in the code. Actually, I've done the first step which was to change the name of a variable if this name was not okey with the rules we use in the firm. For example:

int newVariable;
double test;

will be changed into:

int iNewVariable;
double dblTest;

Now I have to change this type of comment: (SingleLineComment)

//this is a single line Comment

Into a MultiLineComment

/*Here it's a MultiLine one*/

I use the Roslyn Syntax Visualiser to find the type and kind to make a correct code, but nothing works. Here it's what I've done for the Diagnostic:

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"));
            }
        }
    }

}

Here is the CodeFix:

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

}

The user has to click on the advertisement that my program gives and the comment will be changed.

But my program never enters where I have to call the method.

I'm doing my first step with Roslyn, so I don't already know a lot of things, but I'm learning about it..

Edit:

All the code added

Was it helpful?

Solution

I've put together an example using a CodeFix with a Diagnostic that replaces single line comments with multiline ones. Here's some of the code. Alot of it is based on Dustin Campbell's Roslyn demo from Build which can be seen at http://channel9.msdn.com/Events/Build/2014/2-577

AnalyzeSyntaxTree implementation from ISyntaxTreeAnalyzer to find single line comments:

[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 implementation from 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;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top