/// <summary> /// Add the necessary edits to uncomment out a single span. /// </summary> private void UncommentSpan( Document document, ICommentSelectionService service, SnapshotSpan span, ArrayBuilder <TextChange> textChanges, ArrayBuilder <CommentTrackingSpan> spansToSelect, CancellationToken cancellationToken) { var info = service.GetInfoAsync(document, span.Span.ToTextSpan(), cancellationToken).WaitAndGetResult(cancellationToken); // If the selection is exactly a block comment, use it as priority over single line comments. if (info.SupportsBlockComment && TryUncommentExactlyBlockComment(info, span, textChanges, spansToSelect)) { return; } if (info.SupportsSingleLineComment && TryUncommentSingleLineComments(info, span, textChanges, spansToSelect)) { return; } // 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 (info.SupportsBlockComment) { UncommentContainingBlockComment(info, span, textChanges, spansToSelect); } }
private static void UncommentSpan( ICommentSelectionService service, SnapshotSpan span, ArrayBuilder <TextChange> textChanges, ArrayBuilder <CommentTrackingSpan> spansToSelect) { var info = service.GetInfo(); // If the selection is exactly a block comment, use it as priority over single line comments. if (info.SupportsBlockComment && TryUncommentExactlyBlockComment(info, span, textChanges, spansToSelect)) { return; } if (info.SupportsSingleLineComment && TryUncommentSingleLineComments(info, span, textChanges, spansToSelect)) { return; } // 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 (info.SupportsBlockComment) { UncommentContainingBlockComment(info, span, textChanges, spansToSelect); } }
internal abstract Task <CommentSelectionResult> CollectEditsAsync( Document document, ICommentSelectionService service, ITextBuffer textBuffer, NormalizedSnapshotSpanCollection selectedSpans, TCommand command, CancellationToken cancellationToken );
private static Document Format(ICommentSelectionService service, ITextSnapshot snapshot, IEnumerable <SnapshotSpan> changes, CancellationToken cancellationToken) { var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return(null); } var textSpans = changes.SelectAsArray(change => change.Span.ToTextSpan()); return(service.FormatAsync(document, textSpans, cancellationToken).WaitAndGetResult(cancellationToken)); }
private void Format(ICommentSelectionService service, ITextSnapshot snapshot, IEnumerable <ITrackingSpan> changes, CancellationToken cancellationToken) { var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return; } var textSpans = changes.Select(s => s.GetSpan(snapshot).Span.ToTextSpan()).ToImmutableArray(); var newDocument = service.FormatAsync(document, textSpans, cancellationToken).WaitAndGetResult(cancellationToken); newDocument.Project.Solution.Workspace.ApplyDocumentChanges(newDocument, cancellationToken); }
/// <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( Document document, ICommentSelectionService service, NormalizedSnapshotSpanCollection selectedSpans, List <TextChange> textChanges, List <ITrackingSpan> trackingSpans, Operation operation, CancellationToken cancellationToken) { foreach (var span in selectedSpans) { if (operation == Operation.Comment) { CommentSpan(document, service, span, textChanges, trackingSpans, cancellationToken); } else { UncommentSpan(document, service, span, textChanges, trackingSpans, cancellationToken); } } }
internal override Task <CommentSelectionResult> CollectEditsAsync( Document document, ICommentSelectionService service, ITextBuffer subjectBuffer, NormalizedSnapshotSpanCollection selectedSpans, Operation operation, CancellationToken cancellationToken ) { var spanTrackingList = ArrayBuilder <CommentTrackingSpan> .GetInstance(); var textChanges = ArrayBuilder <TextChange> .GetInstance(); foreach (var span in selectedSpans) { if (operation == Operation.Comment) { CommentSpan( document, service, span, textChanges, spanTrackingList, cancellationToken ); } else { UncommentSpan( document, service, span, textChanges, spanTrackingList, cancellationToken ); } } return(Task.FromResult( new CommentSelectionResult( textChanges.ToArrayAndFree(), spanTrackingList.ToArrayAndFree(), operation ) )); }
/// <summary> /// Add the necessary edits to uncomment out a single span. /// </summary> private void UncommentSpan( Document document, ICommentSelectionService service, SnapshotSpan span, List <TextChange> textChanges, List <ITrackingSpan> spansToSelect, CancellationToken cancellationToken) { var info = service.GetInfoAsync(document, span.Span.ToTextSpan(), cancellationToken).WaitAndGetResult(cancellationToken); if (info.SupportsSingleLineComment && TryUncommentSingleLineComments(info, span, textChanges, spansToSelect)) { return; } if (info.SupportsBlockComment) { UncommentContainingBlockComment(info, span, textChanges, spansToSelect); } }
/// <summary> /// Applies the requested edits and sets the selection. /// This operation is not cancellable. /// </summary> private void ApplyEdits(Document document, ITextView textView, ITextBuffer subjectBuffer, ICommentSelectionService service, string title, CommentSelectionResult edits) { // Create tracking spans to track the text changes. var currentSnapshot = subjectBuffer.CurrentSnapshot; var trackingSpans = edits.TrackingSpans .SelectAsArray(textSpan => (originalSpan: textSpan, trackingSpan: CreateTrackingSpan(edits.ResultOperation, currentSnapshot, textSpan.TrackingTextSpan))); // Apply the text changes. using (var transaction = new CaretPreservingEditTransaction(title, textView, _undoHistoryRegistry, _editorOperationsFactoryService)) { document.Project.Solution.Workspace.ApplyTextChanges(document.Id, edits.TextChanges.Distinct(), CancellationToken.None); transaction.Complete(); } // Convert the tracking spans into snapshot spans for formatting and selection. var trackingSnapshotSpans = trackingSpans.Select(s => CreateSnapshotSpan(subjectBuffer.CurrentSnapshot, s.trackingSpan, s.originalSpan)); if (trackingSnapshotSpans.Any()) { if (edits.ResultOperation == Operation.Uncomment) { // Format the document only during uncomment operations. Use second transaction so it can be undone. using var transaction = new CaretPreservingEditTransaction(title, textView, _undoHistoryRegistry, _editorOperationsFactoryService); var formattedDocument = Format(service, subjectBuffer.CurrentSnapshot, trackingSnapshotSpans, CancellationToken.None); if (formattedDocument != null) { formattedDocument.Project.Solution.Workspace.ApplyDocumentChanges(formattedDocument, CancellationToken.None); transaction.Complete(); } } // Set the multi selection after edits have been applied. textView.SetMultiSelection(trackingSnapshotSpans); } }
internal async override Task <CommentSelectionResult> CollectEditsAsync(Document document, ICommentSelectionService service, ITextBuffer subjectBuffer, NormalizedSnapshotSpanCollection selectedSpans, ValueTuple command, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CommandHandler_ToggleLineComment, KeyValueLogMessage.Create(LogType.UserAction, m => { m[LanguageNameString] = document.Project.Language; m[LengthString] = subjectBuffer.CurrentSnapshot.Length; }), cancellationToken)) { var commentInfo = await service.GetInfoAsync(document, selectedSpans.First().Span.ToTextSpan(), cancellationToken).ConfigureAwait(false); if (commentInfo.SupportsSingleLineComment) { return(ToggleLineComment(commentInfo, selectedSpans)); } return(s_emptyCommentSelectionResult); } }
/// <summary> /// Add the necessary edits to comment out a single span. /// </summary> private void CommentSpan( Document document, ICommentSelectionService service, SnapshotSpan span, ArrayBuilder <TextChange> textChanges, ArrayBuilder <CommentTrackingSpan> trackingSpans, CancellationToken cancellationToken) { var(firstLine, lastLine) = DetermineFirstAndLastLine(span); if (span.IsEmpty && firstLine.IsEmptyOrWhitespace()) { // No selection, and on an empty line, don't do anything. return; } if (!span.IsEmpty && string.IsNullOrWhiteSpace(span.GetText())) { // Just whitespace selected, don't do anything. return; } // Get the information from the language as to how they'd like to comment this region. var commentInfo = service.GetInfoAsync(document, span.Span.ToTextSpan(), cancellationToken).WaitAndGetResult(cancellationToken); if (!commentInfo.SupportsBlockComment && !commentInfo.SupportsSingleLineComment) { // Neither type of comment supported. return; } if (commentInfo.SupportsBlockComment && !commentInfo.SupportsSingleLineComment) { // Only block comments supported here. If there is a span, just surround that // span with a block comment. If tehre is no span then surround the entire line // with a block comment. if (span.IsEmpty) { var firstNonWhitespaceOnLine = firstLine.GetFirstNonWhitespacePosition(); var insertPosition = firstNonWhitespaceOnLine ?? firstLine.Start; span = new SnapshotSpan(span.Snapshot, Span.FromBounds(insertPosition, firstLine.End)); } AddBlockComment(span, textChanges, trackingSpans, commentInfo); } else if (!commentInfo.SupportsBlockComment && commentInfo.SupportsSingleLineComment) { // Only single line comments supported here. AddSingleLineComments(span, textChanges, trackingSpans, firstLine, lastLine, commentInfo); } else { // both comment forms supported. Do a block comment only if a portion of code is // selected on a single line, otherwise comment out all the lines using single-line // comments. if (!span.IsEmpty && !SpanIncludesAllTextOnIncludedLines(span) && firstLine.LineNumber == lastLine.LineNumber) { AddBlockComment(span, textChanges, trackingSpans, commentInfo); } else { AddSingleLineComments(span, textChanges, trackingSpans, firstLine, lastLine, commentInfo); } } }