private async Task <CommentSelectionResult> ToggleBlockCommentsAsync(Document document, CommentSelectionInfo commentInfo, ITextStructureNavigator navigator, NormalizedSnapshotSpanCollection selectedSpans, CancellationToken cancellationToken) { var firstLineAroundSelection = selectedSpans.First().Start.GetContainingLine().Start; var lastLineAroundSelection = selectedSpans.Last().End.GetContainingLine().End; var linesContainingSelection = TextSpan.FromBounds(firstLineAroundSelection, lastLineAroundSelection); var blockCommentedSpans = await GetBlockCommentsInDocumentAsync( document, selectedSpans.First().Snapshot, linesContainingSelection, commentInfo, cancellationToken).ConfigureAwait(false); var blockCommentSelections = selectedSpans.SelectAsArray(span => new BlockCommentSelectionHelper(blockCommentedSpans, span)); var returnOperation = Operation.Uncomment; var textChanges = ArrayBuilder <TextChange> .GetInstance(); var trackingSpans = ArrayBuilder <CommentTrackingSpan> .GetInstance(); // Try to uncomment until an already uncommented span is found. foreach (var blockCommentSelection in blockCommentSelections) { // If any selection does not have comments to remove, then the operation should be comment. if (!TryUncommentBlockComment(blockCommentedSpans, blockCommentSelection, textChanges, trackingSpans, commentInfo)) { returnOperation = Operation.Comment; break; } } if (returnOperation == Operation.Comment) { textChanges.Clear(); trackingSpans.Clear(); foreach (var blockCommentSelection in blockCommentSelections) { BlockCommentSpan(blockCommentSelection, navigator, textChanges, trackingSpans, commentInfo); } } return(new CommentSelectionResult(textChanges.ToArrayAndFree(), trackingSpans.ToArrayAndFree(), returnOperation)); }