/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/Preview/PreviewEngine.cs
328 строк
12 KB
Tomáš Matoušek
Refactoring of extension methods in source packages (#78620) - take 2 (#78894)
26 июн 2025, 18:42
Не верифицирован
26 июн 2025, 18:42
05fc006
Код
Авторство
О чём код?
// 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.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; internal sealed class PreviewEngine : IVsPreviewChangesEngine { private readonly IVsEditorAdaptersFactoryService _editorFactory; private readonly Solution _newSolution; private readonly Solution _oldSolution; private readonly string _topLevelName; private readonly Glyph _topLevelGlyph; private readonly string _helpString; private readonly string _description; private readonly string _title; private readonly IComponentModel _componentModel; private readonly IVsImageService2 _imageService; private TopLevelChange _topLevelChange; private PreviewUpdater _updater; public Solution FinalSolution { get; private set; } public bool ShowCheckBoxes { get; private set; } public PreviewEngine(string title, string helpString, string description, string topLevelItemName, Glyph topLevelGlyph, Solution newSolution, Solution oldSolution, IComponentModel componentModel, bool showCheckBoxes = true) : this(title, helpString, description, topLevelItemName, topLevelGlyph, newSolution, oldSolution, componentModel, null, showCheckBoxes) { } public PreviewEngine( string title, string helpString, string description, string topLevelItemName, Glyph topLevelGlyph, Solution newSolution, Solution oldSolution, IComponentModel componentModel, IVsImageService2 imageService, bool showCheckBoxes = true) { _topLevelName = topLevelItemName; _topLevelGlyph = topLevelGlyph; _title = title ?? throw new ArgumentNullException(nameof(title)); _helpString = helpString ?? throw new ArgumentNullException(nameof(helpString)); _description = description ?? throw new ArgumentNullException(nameof(description)); _newSolution = newSolution.WithMergedLinkedFileChangesAsync(oldSolution, cancellationToken: CancellationToken.None).Result; _oldSolution = oldSolution; _editorFactory = componentModel.GetService<IVsEditorAdaptersFactoryService>(); _componentModel = componentModel; this.ShowCheckBoxes = showCheckBoxes; _imageService = imageService; } public void CloseWorkspace() { _updater?.CloseWorkspace(); } public int ApplyChanges() { FinalSolution = _topLevelChange.GetUpdatedSolution(applyingChanges: true); return VSConstants.S_OK; } public int GetConfirmation(out string pbstrConfirmation) { pbstrConfirmation = EditorFeaturesResources.Apply2; return VSConstants.S_OK; } public int GetDescription(out string pbstrDescription) { pbstrDescription = _description; return VSConstants.S_OK; } public int GetHelpContext(out string pbstrHelpContext) { pbstrHelpContext = _helpString; return VSConstants.S_OK; } public int GetRootChangesList(out object ppIUnknownPreviewChangesList) { var changes = _newSolution.GetChanges(_oldSolution); var projectChanges = changes.GetProjectChanges(); _topLevelChange = new TopLevelChange(_topLevelName, _topLevelGlyph, _newSolution, this); var builder = ArrayBuilder<AbstractChange>.GetInstance(); // Documents // (exclude unchangeable ones if they will be ignored when applied to workspace.) var changedDocuments = projectChanges.SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true, _oldSolution.Workspace.IgnoreUnchangeableDocumentsWhenApplyingChanges)); var addedDocuments = projectChanges.SelectMany(p => p.GetAddedDocuments()); var removedDocuments = projectChanges.SelectMany(p => p.GetRemovedDocuments()); var allDocumentsWithChanges = new List<DocumentId>(); allDocumentsWithChanges.AddRange(changedDocuments); allDocumentsWithChanges.AddRange(addedDocuments); allDocumentsWithChanges.AddRange(removedDocuments); // Additional Documents var changedAdditionalDocuments = projectChanges.SelectMany(p => p.GetChangedAdditionalDocuments()); var addedAdditionalDocuments = projectChanges.SelectMany(p => p.GetAddedAdditionalDocuments()); var removedAdditionalDocuments = projectChanges.SelectMany(p => p.GetRemovedAdditionalDocuments()); allDocumentsWithChanges.AddRange(changedAdditionalDocuments); allDocumentsWithChanges.AddRange(addedAdditionalDocuments); allDocumentsWithChanges.AddRange(removedAdditionalDocuments); // AnalyzerConfig Documents var changedAnalyzerConfigDocuments = projectChanges.SelectMany(p => p.GetChangedAnalyzerConfigDocuments()); var addedAnalyzerConfigDocuments = projectChanges.SelectMany(p => p.GetAddedAnalyzerConfigDocuments()); var removedAnalyzerConfigDocuments = projectChanges.SelectMany(p => p.GetRemovedAnalyzerConfigDocuments()); allDocumentsWithChanges.AddRange(changedAnalyzerConfigDocuments); allDocumentsWithChanges.AddRange(addedAnalyzerConfigDocuments); allDocumentsWithChanges.AddRange(removedAnalyzerConfigDocuments); AppendFileChanges(allDocumentsWithChanges, builder); // References (metadata/project/analyzer) ReferenceChange.AppendReferenceChanges(projectChanges, this, builder); _topLevelChange.Children = builder.Count == 0 ? ChangeList.Empty : new ChangeList(builder.ToArray()); ppIUnknownPreviewChangesList = _topLevelChange.Children.Changes.Length == 0 ? new ChangeList(new[] { new NoChange(this) }) : new ChangeList(new[] { _topLevelChange }); if (_topLevelChange.Children.Changes.Length == 0) { this.ShowCheckBoxes = false; } return VSConstants.S_OK; } private void AppendFileChanges(IEnumerable<DocumentId> changedDocuments, ArrayBuilder<AbstractChange> builder) { // Avoid showing linked changes to linked files multiple times. var linkedDocumentIds = new HashSet<DocumentId>(); var orderedChangedDocuments = changedDocuments.GroupBy(d => d.ProjectId).OrderByDescending(g => g.Count()).Flatten(); foreach (var documentId in orderedChangedDocuments) { if (linkedDocumentIds.Contains(documentId)) { continue; } var left = _oldSolution.GetTextDocument(documentId); var right = _newSolution.GetTextDocument(documentId); if (left is Document leftDocument) { linkedDocumentIds.AddRange(leftDocument.GetLinkedDocumentIds()); } else if (right is Document rightDocument) { // Added document. linkedDocumentIds.AddRange(rightDocument.GetLinkedDocumentIds()); } var fileChange = new FileChange(left, right, _componentModel, _topLevelChange, this, _imageService); if (fileChange.Children.Changes.Length > 0) { builder.Add(fileChange); } } } public int GetTextViewDescription(out string pbstrTextViewDescription) { pbstrTextViewDescription = EditorFeaturesResources.Preview_Code_Changes_colon; return VSConstants.S_OK; } public int GetTitle(out string pbstrTitle) { pbstrTitle = _title; return VSConstants.S_OK; } public int GetWarning(out string pbstrWarning, out int ppcwlWarningLevel) { pbstrWarning = null; ppcwlWarningLevel = 0; return VSConstants.E_NOTIMPL; } public void UpdatePreview(DocumentId documentId, SpanChange spanSource) { var updatedSolution = _topLevelChange.GetUpdatedSolution(applyingChanges: false); var document = updatedSolution.GetTextDocument(documentId); if (document != null) { _updater.UpdateView(document, spanSource); } } // We don't get a TexView until they call OnRequestChanges on a child. // However, once they've called it once, it's always the same TextView. public void SetTextView(object textView) { _updater ??= new PreviewUpdater(EnsureTextViewIsInitialized(textView)); } private ITextView EnsureTextViewIsInitialized(object previewTextView) { // We pass in a regular ITextView in tests if (previewTextView is not null and ITextView) { return (ITextView)previewTextView; } var adapter = (IVsTextView)previewTextView; var textView = _editorFactory.GetWpfTextView(adapter); if (textView == null) { EditBufferToInitialize(adapter); textView = _editorFactory.GetWpfTextView(adapter); } UpdateTextViewOptions(textView); return textView; } private static void UpdateTextViewOptions(IWpfTextView textView) { // Do not show the IndentationCharacterMargin, which controls spaces vs. tabs etc. textView.Options.SetOptionValue(DefaultTextViewHostOptions.IndentationCharacterMarginOptionId, false); // Do not show LineEndingMargin, which determines EOL and EOF settings. textView.Options.SetOptionValue(DefaultTextViewHostOptions.LineEndingMarginOptionId, false); // Do not show the "no issues found" health indicator for previews. textView.Options.SetOptionValue(DefaultTextViewHostOptions.EnableFileHealthIndicatorOptionId, false); } // When the dialog is first instantiated, the IVsTextView it contains may // not have been initialized, which will prevent us from using an // EditorAdaptersFactoryService to get the ITextView. If we edit the IVsTextView, // it will initialize and we can proceed. private static void EditBufferToInitialize(IVsTextView adapter) { if (adapter == null) { return; } var newText = ""; var newTextPtr = Marshal.StringToHGlobalAuto(newText); try { Marshal.ThrowExceptionForHR(adapter.GetBuffer(out var lines)); Marshal.ThrowExceptionForHR(lines.GetLastLineIndex(out _, out var piLineIndex)); Marshal.ThrowExceptionForHR(lines.GetLengthOfLine(piLineIndex, out var piLineLength)); Microsoft.VisualStudio.TextManager.Interop.TextSpan[] changes = null; piLineLength = piLineLength > 0 ? piLineLength - 1 : 0; Marshal.ThrowExceptionForHR(lines.ReplaceLines(0, 0, piLineIndex, piLineLength, newTextPtr, newText.Length, changes)); } finally { Marshal.FreeHGlobal(newTextPtr); } } private sealed class NoChange : AbstractChange { public NoChange(PreviewEngine engine) : base(engine) { } public override int GetText(out VSTREETEXTOPTIONS tto, out string ppszText) { tto = VSTREETEXTOPTIONS.TTO_DEFAULT; ppszText = ServicesVSResources.No_Changes; return VSConstants.S_OK; } public override int GetTipText(out VSTREETOOLTIPTYPE eTipType, out string ppszText) { eTipType = VSTREETOOLTIPTYPE.TIPTYPE_DEFAULT; ppszText = null; return VSConstants.E_FAIL; } public override int CanRecurse => 0; public override int IsExpandable => 0; internal override void GetDisplayData(VSTREEDISPLAYDATA[] pData) => pData[0].Image = pData[0].SelectedImage = (ushort)StandardGlyphGroup.GlyphInformation; public override int OnRequestSource(object pIUnknownTextView) => VSConstants.S_OK; public override void UpdatePreview() { } } }