public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var node = root.FindNode(context.Span); var memberDecl = node as MemberDeclarationSyntax; if (memberDecl == null) { return; } var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); if (semanticModel == null) { return; } var symbol = semanticModel.GetDeclaredSymbol(memberDecl, context.CancellationToken); if (symbol == null) { return; } var doc = symbol.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken); XElement xdoc; try { xdoc = string.IsNullOrEmpty(doc) ? GenerateEmptyDocComment() : XElement.Parse(doc); } catch { return; } var docContext = new DocCommentRefactoringContext(memberDecl, symbol, xdoc, semanticModel, context.CancellationToken); var previousCanComment = false; for (var i = 0; i < _strategies.Count; i++) { if (await _strategies[i].CanCommentAsync(previousCanComment, docContext)) { var ii = i; var action = CodeAction.Create(Resources.Title, c => GenerateDocCommentAsync(context.Document, docContext, ii, c), nameof(DocCommentRefactoring)); context.RegisterRefactoring(action); previousCanComment = true; } } }
public DocCommentRefactoringContext( DocCommentRefactoringContext context, MemberDeclarationSyntax syntax, ISymbol symbol, XElement docComment, SemanticModel semanticModel, CancellationToken cancellationToken) { Syntax = syntax; Symbol = symbol; DocComment = docComment; SemanticModel = semanticModel; CancellationToken = cancellationToken; _cache = context._cache; }
private async Task <Document> GenerateDocCommentAsync(Document document, DocCommentRefactoringContext docContext, int startIndex, CancellationToken cancellationToken) { docContext = new DocCommentRefactoringContext( docContext, docContext.Syntax, docContext.Symbol, docContext.DocComment, await document.GetSemanticModelAsync(cancellationToken), cancellationToken); // Allow all strategies to try generate, some may mutate an existing DocComment. var previousCanComment = false; for (var i = startIndex; i < _strategies.Count; i++) { if (await _strategies[i].CanCommentAsync(previousCanComment, docContext)) { await _strategies[i].CommentAsync(docContext); previousCanComment = true; } } var root = await document.GetSyntaxRootAsync(docContext.CancellationToken); var trivia = docContext.Syntax.GetLeadingTrivia(); // Remove existing DocComments for (var i = 0; i < trivia.Count; i++) { if (trivia[i].IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) || trivia[i].IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia)) { trivia.RemoveAt(i--); } } var insertionIndex = 0; var newLine = NewLineTrivia(root); if (trivia.Count < 2) { // Empty file, insert a linebreak after where the trivia // will be inserted. trivia = trivia.Insert(0, newLine); insertionIndex = 0; } else { // Probably a newline followed by a whitespace. Insert the // trivia before the newline. insertionIndex = trivia.Count - 1; trivia = trivia.Insert(insertionIndex, newLine); } var whitespace = string.Empty; if (trivia[trivia.Count - 1].IsKind(SyntaxKind.WhitespaceTrivia)) { whitespace = trivia[trivia.Count - 1].ToString(); } trivia = trivia.InsertRange(insertionIndex, SyntaxFactory.ParseLeadingTrivia(GetTriviaText(docContext.DocComment.Nodes(), whitespace, newLine.ToString()))); var newMember = docContext.Syntax.WithLeadingTrivia(trivia); root = root.ReplaceNode(docContext.Syntax, newMember); return(document.WithSyntaxRoot(root)); }