/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/Core/IWpfDifferenceViewerExtensions.cs
129 строк
6 KB
Cyrus Najmabadi
Move files
25 апр 2025, 01:10
25 апр 2025, 01:10
46f9987
Код
Авторство
О чём код?
// 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. #nullable disable using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Differencing; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Projection; using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions; internal static class IWpfDifferenceViewerExtensions { private sealed class SizeToFitHelper(IThreadingContext threadingContext, IWpfDifferenceViewer diffViewer, double minWidth) { private readonly IThreadingContext _threadingContext = threadingContext; private readonly IWpfDifferenceViewer _diffViewer = diffViewer; private readonly double _minWidth = minWidth; private double _width; private double _height; public async Task SizeToFitAsync(CancellationToken cancellationToken) { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) await CalculateSizeAsync(cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task // We have the height and width required to display the inline diff snapshot now. // Set the height and width of the IWpfDifferenceViewer accordingly. _diffViewer.VisualElement.Width = _width; _diffViewer.VisualElement.Height = _height; } private async Task<IProjectionSnapshot> GetInlineBufferSnapshotAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (_diffViewer.DifferenceBuffer.CurrentInlineBufferSnapshot is { } snapshot) { return snapshot; } var completionSource = new TaskCompletionSource<IProjectionSnapshot>(TaskCreationOptions.RunContinuationsAsynchronously); _diffViewer.DifferenceBuffer.SnapshotDifferenceChanged += HandleSnapshotDifferenceChanged; // Handle cases where the snapshot was set between the previous check and the event registration if (_diffViewer.DifferenceBuffer.CurrentInlineBufferSnapshot is { } snapshot2) completionSource.SetResult(snapshot2); try { return await completionSource.Task.WithCancellation(cancellationToken).ConfigureAwaitRunInline(); } finally { _diffViewer.DifferenceBuffer.SnapshotDifferenceChanged -= HandleSnapshotDifferenceChanged; } // Local function void HandleSnapshotDifferenceChanged(object sender, SnapshotDifferenceChangeEventArgs e) { // This event handler will only be called when the inline diff snapshot computation is complete. Contract.ThrowIfNull(_diffViewer.DifferenceBuffer.CurrentInlineBufferSnapshot); completionSource.SetResult(_diffViewer.DifferenceBuffer.CurrentInlineBufferSnapshot); } } private async Task CalculateSizeAsync(CancellationToken cancellationToken) { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); IWpfTextView textView; ITextSnapshot snapshot; if (_diffViewer.ViewMode == DifferenceViewMode.RightViewOnly) { textView = _diffViewer.RightView; snapshot = _diffViewer.DifferenceBuffer.RightBuffer.CurrentSnapshot; } else if (_diffViewer.ViewMode == DifferenceViewMode.LeftViewOnly) { textView = _diffViewer.LeftView; snapshot = _diffViewer.DifferenceBuffer.LeftBuffer.CurrentSnapshot; } else { textView = _diffViewer.InlineView; #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) snapshot = await GetInlineBufferSnapshotAsync(cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task } // Perform a layout without actually rendering the content on the screen so that // we can calculate the exact height and width required to render the content on // the screen before actually rendering it. This helps us avoiding the flickering // effect that would be caused otherwise when the UI is rendered twice with // different sizes. textView.DisplayTextLineContainingBufferPosition( new SnapshotPoint(snapshot, 0), 0.0, ViewRelativePosition.Top, double.MaxValue, double.MaxValue); _width = Math.Max(textView.MaxTextRightCoordinate * (textView.ZoomLevel / 100), _minWidth); // Width of the widest line. Contract.ThrowIfFalse(IsNormal(_width)); _height = textView.LineHeight * (textView.ZoomLevel / 100) * // Height of each line. snapshot.LineCount; // Number of lines. Contract.ThrowIfFalse(IsNormal(_height)); } private static bool IsNormal(double value) => !double.IsNaN(value) && !double.IsInfinity(value) && value > 0.0; } public static Task SizeToFitAsync(this IWpfDifferenceViewer diffViewer, IThreadingContext threadingContext, double minWidth = 400.0, CancellationToken cancellationToken = default) { var helper = new SizeToFitHelper(threadingContext, diffViewer, minWidth); return helper.SizeToFitAsync(cancellationToken); } }