|
// 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));
}
}
|