File: Shared\Extensions\ITextSnapshotExtensions.cs
Web Access
Project: src\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions;
 
internal static partial class ITextSnapshotExtensions
{
    /// <summary>
    /// format given snapshot and apply text changes to buffer
    /// </summary>
    public static void FormatAndApplyToBuffer(
        this ITextBuffer textBuffer,
        TextSpan span,
        EditorOptionsService editorOptionsService,
        CancellationToken cancellationToken)
    {
        var document = textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
        if (document == null)
        {
            return;
        }
 
        var documentSyntax = ParsedDocument.CreateSynchronously(document, cancellationToken);
        var rules = FormattingRuleUtilities.GetFormattingRules(documentSyntax, span, additionalRules: null);
 
        var formatter = document.GetRequiredLanguageService<ISyntaxFormattingService>();
 
        var options = textBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false);
        var result = formatter.GetFormattingResult(documentSyntax.Root, [span], options, rules, cancellationToken);
        var changes = result.GetTextChanges(cancellationToken);
 
        using (Logger.LogBlock(FunctionId.Formatting_ApplyResultToBuffer, cancellationToken))
        {
            textBuffer.ApplyChanges(changes);
        }
    }
 
    /// <summary>
    /// Get <see cref="Document"/> from <see cref="Text.Extensions.GetOpenDocumentInCurrentContextWithChanges(ITextSnapshot)"/>
    /// once <see cref="IWorkspaceStatusService.WaitUntilFullyLoadedAsync(CancellationToken)"/> returns
    /// 
    /// for synchronous code path, make sure to use synchronous version 
    /// <see cref="GetFullyLoadedOpenDocumentInCurrentContextWithChanges(ITextSnapshot, IUIThreadOperationContext, IThreadingContext)"/>.
    /// otherwise, one can get into a deadlock
    /// </summary>
    public static async Task<Document?> GetFullyLoadedOpenDocumentInCurrentContextWithChangesAsync(
        this ITextSnapshot snapshot, IUIThreadOperationContext operationContext)
    {
        // just get a document from whatever we have
        var document = snapshot.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext();
        if (document == null)
        {
            // we don't know about this buffer yet
            return null;
        }
 
        // partial mode is always cancellable
        using (operationContext.AddScope(allowCancellation: true, EditorFeaturesResources.Waiting_for_background_work_to_finish))
        {
            var service = document.Project.Solution.Services.GetService<IWorkspaceStatusService>();
            if (service != null)
            {
                // TODO: decide for prototype, we don't do anything complex and just ask workspace whether it is fully loaded
                // later we might need to go and change all these with more specific info such as document/project/solution
                await service.WaitUntilFullyLoadedAsync(operationContext.UserCancellationToken).ConfigureAwait(false);
            }
 
            // get proper document
            return snapshot.GetOpenDocumentInCurrentContextWithChanges();
        }
    }
 
    /// <summary>
    /// Get <see cref="Document"/> from <see cref="Text.Extensions.GetOpenDocumentInCurrentContextWithChanges(ITextSnapshot)"/>
    /// once <see cref="IWorkspaceStatusService.WaitUntilFullyLoadedAsync(CancellationToken)"/> returns
    /// </summary>
    public static Document? GetFullyLoadedOpenDocumentInCurrentContextWithChanges(
        this ITextSnapshot snapshot, IUIThreadOperationContext operationContext, IThreadingContext threadingContext)
    {
        // make sure this is only called from UI thread
        threadingContext.ThrowIfNotOnUIThread();
 
        return threadingContext.JoinableTaskFactory.Run(() =>
            snapshot.GetFullyLoadedOpenDocumentInCurrentContextWithChangesAsync(operationContext));
    }
}