/// <summary>
        /// True if this service would like to format the document based on the user typing the
        /// provided character.
        /// </summary>
        public static bool SupportsFormattingOnTypedCharacter(Document document, char ch)
        {
            Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp);
            var formattingService = document.GetRequiredLanguageService <IFormattingInteractionService>();
            var options           = AutoFormattingOptions.From(document.Project);

            return(formattingService.SupportsFormattingOnTypedCharacter(document, options, ch));
        }
예제 #2
0
        private void ExecuteReturnOrTypeCommandWorker(EditorCommandArgs args, CancellationToken cancellationToken)
        {
            var textView      = args.TextView;
            var subjectBuffer = args.SubjectBuffer;

            if (!CanExecuteCommand(subjectBuffer))
            {
                return;
            }

            var caretPosition = textView.GetCaretPoint(args.SubjectBuffer);

            if (!caretPosition.HasValue)
            {
                return;
            }

            var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return;
            }

            var service = document.GetLanguageService <IFormattingInteractionService>();

            if (service == null)
            {
                return;
            }

            IList <TextChange>?textChanges;

            // save current caret position
            if (args is ReturnKeyCommandArgs)
            {
                if (!service.SupportsFormatOnReturn)
                {
                    return;
                }

                textChanges = service.GetFormattingChangesOnReturnAsync(
                    document, caretPosition.Value, documentOptions: null, cancellationToken).WaitAndGetResult(cancellationToken);
            }
            else if (args is TypeCharCommandArgs typeCharArgs)
            {
                var options = AutoFormattingOptions.From(document.Project);
                if (!service.SupportsFormattingOnTypedCharacter(document, options, typeCharArgs.TypedChar))
                {
                    return;
                }

                textChanges = service.GetFormattingChangesAsync(
                    document, typeCharArgs.TypedChar, caretPosition.Value, documentOptions: null, cancellationToken).WaitAndGetResult(cancellationToken);
            }
            else
            {
                throw ExceptionUtilities.UnexpectedValue(args);
            }

            if (textChanges == null || textChanges.Count == 0)
            {
                return;
            }

            using (var transaction = CreateEditTransaction(textView, EditorFeaturesResources.Automatic_Formatting))
            {
                transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance;
                document.Project.Solution.Workspace.ApplyTextChanges(document.Id, textChanges, cancellationToken);
                transaction.Complete();
            }

            // get new caret position after formatting
            var newCaretPositionMarker = args.TextView.GetCaretPoint(args.SubjectBuffer);

            if (!newCaretPositionMarker.HasValue)
            {
                return;
            }

            var snapshotAfterFormatting = args.SubjectBuffer.CurrentSnapshot;

            var oldCaretPosition = caretPosition.Value.TranslateTo(snapshotAfterFormatting, PointTrackingMode.Negative);
            var newCaretPosition = newCaretPositionMarker.Value.TranslateTo(snapshotAfterFormatting, PointTrackingMode.Negative);

            if (oldCaretPosition.Position == newCaretPosition.Position)
            {
                return;
            }

            // caret has moved to wrong position, move it back to correct position
            args.TextView.TryMoveCaretToAndEnsureVisible(oldCaretPosition);
        }