/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/CommonControls/MemberSelectionViewModel.cs
192 строки
7 KB
Cyrus Najmabadi
Where+Select
05 авг 2025, 13:31
05 авг 2025, 13:31
1d162fe
Код
Авторство
О чём код?
// 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.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.LanguageServices.Utilities; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.CommonControls; internal sealed class MemberSelectionViewModel : AbstractNotifyPropertyChanged { private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor; private readonly ImmutableDictionary<ISymbol, Task<ImmutableArray<ISymbol>>>? _symbolToDependentsMap; private readonly ImmutableDictionary<ISymbol, MemberSymbolViewModel> _symbolToMemberViewMap; public MemberSelectionViewModel( IUIThreadOperationExecutor uiThreadOperationExecutor, ImmutableArray<MemberSymbolViewModel> members, ImmutableDictionary<ISymbol, Task<ImmutableArray<ISymbol>>>? dependentsMap, TypeKind destinationTypeKind = TypeKind.Class, bool showDependentsButton = true, bool showPublicButton = true) { _uiThreadOperationExecutor = uiThreadOperationExecutor; // Use public property to hook property change events up Members = [.. members.OrderBy(s => s.SymbolName)]; _symbolToDependentsMap = dependentsMap; _symbolToMemberViewMap = members.ToImmutableDictionary(memberViewModel => memberViewModel.Symbol); UpdateMembersBasedOnDestinationKind(destinationTypeKind); ShowCheckDependentsButton = showDependentsButton; ShowPublicButton = showPublicButton; } public bool ShowCheckDependentsButton { get; } public bool ShowPublicButton { get; } public bool ShowMakeAbstract => Members.Any(m => m.IsMakeAbstractCheckable); public ImmutableArray<MemberSymbolViewModel> CheckedMembers => Members.WhereAsArray(m => m.IsChecked && m.IsCheckable); public ImmutableArray<MemberSymbolViewModel> Members { get; set { var oldMembers = field; if (SetProperty(ref field, value)) { // If we have registered for events before, remove the handlers // to be a good citizen in the world if (!oldMembers.IsDefaultOrEmpty) { foreach (var oldMember in oldMembers) { oldMember.PropertyChanged -= MemberPropertyChangedHandler; } } foreach (var member in field) { member.PropertyChanged += MemberPropertyChangedHandler; } NotifyPropertyChanged(nameof(ShowMakeAbstract)); } } } private void MemberPropertyChangedHandler(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(MemberSymbolViewModel.IsChecked)) { // Hook the CheckedMembers property change to each individual member checked status change NotifyPropertyChanged(nameof(CheckedMembers)); } if (e.PropertyName == nameof(MemberSymbolViewModel.IsMakeAbstractCheckable)) { NotifyPropertyChanged(nameof(ShowMakeAbstract)); } } public void SelectPublic() => SelectMembers(Members.WhereAsArray(v => v.Symbol.DeclaredAccessibility == Accessibility.Public)); public void SelectAll() => SelectMembers(Members); internal void DeselectAll() => SelectMembers(Members, isChecked: false); public void SelectDependents() { Contract.ThrowIfFalse(ShowCheckDependentsButton); var checkedMembers = Members .WhereAsArray(member => member.IsChecked && member.IsCheckable); var result = _uiThreadOperationExecutor.Execute( title: ServicesVSResources.Pull_Members_Up, defaultDescription: ServicesVSResources.Calculating_dependents, allowCancellation: true, showProgress: true, context => { if (_symbolToDependentsMap != null) { foreach (var member in Members) _symbolToDependentsMap[member.Symbol].Wait(context.UserCancellationToken); } }); if (result == UIThreadOperationStatus.Completed) { foreach (var member in checkedMembers) { var membersToSelected = FindDependentsRecursively(member.Symbol).SelectAsArray(symbol => _symbolToMemberViewMap[symbol]); SelectMembers(membersToSelected); } } } public ImmutableArray<(ISymbol member, bool makeAbstract)> GetSelectedMembers() => Members. SelectAsArray(memberSymbolView => memberSymbolView.IsChecked && memberSymbolView.IsCheckable, memberViewModel => (member: memberViewModel.Symbol, makeAbstract: memberViewModel.IsMakeAbstractCheckable && memberViewModel.MakeAbstract)); public void UpdateMembersBasedOnDestinationKind(TypeKind destinationType) { var fields = Members.WhereAsArray(memberViewModel => memberViewModel.Symbol.IsKind(SymbolKind.Field)); var makeAbstractEnabledCheckboxes = Members. WhereAsArray(memberViewModel => !memberViewModel.Symbol.IsKind(SymbolKind.Field) && !memberViewModel.Symbol.IsAbstract); var isInterface = destinationType == TypeKind.Interface; // Disable field check box and make abstract if destination is interface foreach (var member in fields) { member.IsCheckable = !isInterface; member.TooltipText = isInterface ? ServicesVSResources.Interface_cannot_have_field : string.Empty; } foreach (var member in makeAbstractEnabledCheckboxes) { member.IsMakeAbstractCheckable = !isInterface; } } private static void SelectMembers(ImmutableArray<MemberSymbolViewModel> members, bool isChecked = true) { foreach (var member in members.Where(viewModel => viewModel.IsCheckable)) { member.IsChecked = isChecked; } } private ImmutableHashSet<ISymbol> FindDependentsRecursively(ISymbol member) { var queue = new Queue<ISymbol>(); // Under situation like two methods call each other, this hashset is used to // prevent the infinity loop. var visited = new HashSet<ISymbol>(); var result = new HashSet<ISymbol>(); queue.Enqueue(member); visited.Add(member); while (!queue.IsEmpty()) { var currentMember = queue.Dequeue(); result.Add(currentMember); visited.Add(currentMember); if (_symbolToDependentsMap != null) { foreach (var dependent in _symbolToDependentsMap[currentMember].Result) { if (!visited.Contains(dependent)) queue.Enqueue(dependent); } } } return [.. result]; } }