private bool TryUncommentSingleLineComments(ICommentUncommentService service, SnapshotSpan span, List <TextChange> textChanges, List <ITrackingSpan> spansToSelect) { // First see if we're selecting any lines that have the single-line comment prefix. // If so, then we'll just remove the single-line comment prefix from those lines. var firstAndLastLine = DetermineFirstAndLastLine(span); for (int lineNumber = firstAndLastLine.Item1.LineNumber; lineNumber <= firstAndLastLine.Item2.LineNumber; ++lineNumber) { var line = span.Snapshot.GetLineFromLineNumber(lineNumber); var lineText = line.GetText(); if (lineText.Trim().StartsWith(service.SingleLineCommentString, StringComparison.Ordinal)) { DeleteText(textChanges, new TextSpan(line.Start.Position + lineText.IndexOf(service.SingleLineCommentString, StringComparison.Ordinal), service.SingleLineCommentString.Length)); } } // If we made any changes, select the entirety of the lines we change, so that subsequent invocations will // affect the same lines. if (!textChanges.Any()) { return(false); } spansToSelect.Add(span.Snapshot.CreateTrackingSpan(Span.FromBounds(firstAndLastLine.Item1.Start.Position, firstAndLastLine.Item2.End.Position), SpanTrackingMode.EdgeExclusive)); return(true); }
/// <summary> /// Add the necessary edits to uncomment out a single span. /// </summary> private void UncommentSpan(ICommentUncommentService service, SnapshotSpan span, List <TextChange> textChanges, List <ITrackingSpan> spansToSelect) { if (TryUncommentSingleLineComments(service, span, textChanges, spansToSelect)) { return; } TryUncommentContainingBlockComment(service, span, textChanges, spansToSelect); }
private bool TryUncommentContainingBlockComment(ICommentUncommentService service, SnapshotSpan span, List <TextChange> textChanges, List <ITrackingSpan> spansToSelect) { // We didn't make any single line changes. If the language supports block comments, see // if we're inside a containing block comment and uncomment that. if (!service.SupportsBlockComment) { return(false); } var positionOfStart = -1; var positionOfEnd = -1; var spanText = span.GetText(); var trimmedSpanText = spanText.Trim(); // See if the selection includes just a block comment (plus whitespace) if (trimmedSpanText.StartsWith(service.BlockCommentStartString, StringComparison.Ordinal) && trimmedSpanText.EndsWith(service.BlockCommentEndString, StringComparison.Ordinal)) { positionOfStart = span.Start + spanText.IndexOf(service.BlockCommentStartString, StringComparison.Ordinal); positionOfEnd = span.Start + spanText.LastIndexOf(service.BlockCommentEndString, StringComparison.Ordinal); } else { // See if we are (textually) contained in a block comment. // This could allow a selection that spans multiple block comments to uncomment the beginning of // the first and end of the last. Oh well. var text = span.Snapshot.AsText(); positionOfStart = text.LastIndexOf(service.BlockCommentStartString, span.Start, caseSensitive: true); // If we found a start comment marker, make sure there isn't an end comment marker after it but before our span. if (positionOfStart >= 0) { var lastEnd = text.LastIndexOf(service.BlockCommentEndString, span.Start, caseSensitive: true); if (lastEnd < positionOfStart) { positionOfEnd = text.IndexOf(service.BlockCommentEndString, span.End, caseSensitive: true); } else if (lastEnd + service.BlockCommentEndString.Length > span.End) { // The end of the span is *inside* the end marker, so searching backwards found it. positionOfEnd = lastEnd; } } } if (positionOfStart < 0 || positionOfEnd < 0) { return(false); } spansToSelect.Add(span.Snapshot.CreateTrackingSpan(Span.FromBounds(positionOfStart, positionOfEnd + service.BlockCommentEndString.Length), SpanTrackingMode.EdgeExclusive)); DeleteText(textChanges, new TextSpan(positionOfStart, service.BlockCommentStartString.Length)); DeleteText(textChanges, new TextSpan(positionOfEnd, service.BlockCommentEndString.Length)); return(true); }
/// <summary> /// Adds edits to comment out each non-blank line, at the given indent. /// </summary> private void ApplyCommentToNonBlankLines(ICommentUncommentService service, List <TextChange> textChanges, Tuple <ITextSnapshotLine, ITextSnapshotLine> firstAndLastLine, int indentToCommentAt) { for (int lineNumber = firstAndLastLine.Item1.LineNumber; lineNumber <= firstAndLastLine.Item2.LineNumber; ++lineNumber) { var line = firstAndLastLine.Item1.Snapshot.GetLineFromLineNumber(lineNumber); if (!line.IsEmptyOrWhitespace()) { InsertText(textChanges, line.Start + indentToCommentAt, service.SingleLineCommentString); } } }
/// <summary> /// Add the necessary edits to the given spans. Also collect tracking spans over each span. /// /// Internal so that it can be called by unit tests. /// </summary> internal void CollectEdits(ICommentUncommentService service, NormalizedSnapshotSpanCollection selectedSpans, List <TextChange> textChanges, List <ITrackingSpan> trackingSpans, Operation operation) { foreach (var span in selectedSpans) { if (operation == Operation.Comment) { CommentSpan(service, span, textChanges, trackingSpans); } else { UncommentSpan(service, span, textChanges, trackingSpans); } } }
private void Format(ICommentUncommentService service, ITextSnapshot snapshot, IEnumerable <ITrackingSpan> changes, CancellationToken cancellationToken) { var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return; } var textSpans = changes.Select(s => s.GetSpan(snapshot)).Select(s => s.Span.ToTextSpan()).ToList(); var newDocument = service.Format(document, textSpans, cancellationToken); newDocument.Project.Solution.Workspace.ApplyDocumentChanges(newDocument, cancellationToken); }
/// <summary> /// Add the necessary edits to comment out a single span. /// </summary> private void CommentSpan(ICommentUncommentService service, SnapshotSpan span, List <TextChange> textChanges, List <ITrackingSpan> trackingSpans) { var firstAndLastLine = DetermineFirstAndLastLine(span); if (span.IsEmpty && firstAndLastLine.Item1.IsEmptyOrWhitespace()) { return; } if (!span.IsEmpty && string.IsNullOrWhiteSpace(span.GetText())) { return; } if (span.IsEmpty || string.IsNullOrWhiteSpace(span.GetText())) { var firstNonWhitespaceOnLine = firstAndLastLine.Item1.GetFirstNonWhitespacePosition(); var insertPosition = firstNonWhitespaceOnLine.HasValue ? firstNonWhitespaceOnLine.Value : firstAndLastLine.Item1.Start; // If there isn't a selection, we select the whole line trackingSpans.Add(span.Snapshot.CreateTrackingSpan(Span.FromBounds(firstAndLastLine.Item1.Start, firstAndLastLine.Item1.End), SpanTrackingMode.EdgeInclusive)); InsertText(textChanges, insertPosition, service.SingleLineCommentString); } else { if (service.SupportsBlockComment && !SpanIncludesAllTextOnIncludedLines(span) && firstAndLastLine.Item1.LineNumber == firstAndLastLine.Item2.LineNumber) { trackingSpans.Add(span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive)); InsertText(textChanges, span.Start, service.BlockCommentStartString); InsertText(textChanges, span.End, service.BlockCommentEndString); } else { // Select the entirety of the lines, so that another comment operation will add more comments, not insert block comments. trackingSpans.Add(span.Snapshot.CreateTrackingSpan(Span.FromBounds(firstAndLastLine.Item1.Start.Position, firstAndLastLine.Item2.End.Position), SpanTrackingMode.EdgeInclusive)); var indentToCommentAt = DetermineSmallestIndent(span, firstAndLastLine); ApplyCommentToNonBlankLines(service, textChanges, firstAndLastLine, indentToCommentAt); } } }
/// <summary> /// Add the necessary edits to uncomment out a single span. /// </summary> private void UncommentSpan(ICommentUncommentService service, SnapshotSpan span, List <TextChange> textChanges, List <ITrackingSpan> spansToSelect) { if (service.SupportsBlockComment) { var positionOfStart = -1; var positionOfEnd = -1; var spanText = span.GetText(); var trimmedSpanText = spanText.Trim(); // See if the selection includes just a block comment (plus whitespace) if (trimmedSpanText.StartsWith(service.BlockCommentStartString, StringComparison.Ordinal) && trimmedSpanText.EndsWith(service.BlockCommentEndString, StringComparison.Ordinal)) { positionOfStart = span.Start + spanText.IndexOf(service.BlockCommentStartString, StringComparison.Ordinal); positionOfEnd = span.Start + spanText.LastIndexOf(service.BlockCommentEndString, StringComparison.Ordinal); } else { // See if we are (textually) contained in a block comment. // This could allow a selection that spans multiple block comments to uncomment the beginning of // the first and end of the last. Oh well. var text = span.Snapshot.AsText(); positionOfStart = text.LastIndexOf(service.BlockCommentStartString, span.Start, caseSensitive: true); // If we found a start comment marker, make sure there isn't an end comment marker after it but before our span. if (positionOfStart >= 0) { var lastEnd = text.LastIndexOf(service.BlockCommentEndString, span.Start, caseSensitive: true); if (lastEnd < positionOfStart) { positionOfEnd = text.IndexOf(service.BlockCommentEndString, span.End, caseSensitive: true); } else if (lastEnd + service.BlockCommentEndString.Length > span.End) { // The end of the span is *inside* the end marker, so searching backwards found it. positionOfEnd = lastEnd; } } } if (positionOfStart >= 0 && positionOfEnd >= 0) { spansToSelect.Add(span.Snapshot.CreateTrackingSpan(Span.FromBounds(positionOfStart, positionOfEnd + service.BlockCommentEndString.Length), SpanTrackingMode.EdgeExclusive)); DeleteText(textChanges, new TextSpan(positionOfStart, service.BlockCommentStartString.Length)); DeleteText(textChanges, new TextSpan(positionOfEnd, service.BlockCommentEndString.Length)); return; } } var firstAndLastLine = DetermineFirstAndLastLine(span); for (int lineNumber = firstAndLastLine.Item1.LineNumber; lineNumber <= firstAndLastLine.Item2.LineNumber; ++lineNumber) { var line = span.Snapshot.GetLineFromLineNumber(lineNumber); var lineText = line.GetText(); if (lineText.Trim().StartsWith(service.SingleLineCommentString, StringComparison.Ordinal)) { DeleteText(textChanges, new TextSpan(line.Start.Position + lineText.IndexOf(service.SingleLineCommentString, StringComparison.Ordinal), service.SingleLineCommentString.Length)); } } // If we made any changes, select the entirety of the lines we change, so that subsequent invocations will // affect the same lines. if (textChanges.Any()) { spansToSelect.Add(span.Snapshot.CreateTrackingSpan(Span.FromBounds(firstAndLastLine.Item1.Start.Position, firstAndLastLine.Item2.End.Position), SpanTrackingMode.EdgeExclusive)); } }
public CommentSelectionServiceProxy(ICommentUncommentService commentUncommentService) { _commentUncommentService = commentUncommentService; }