File: Options\Style\NamingPreferences\NamingStyleOptionPageViewModel.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.LanguageServices.Implementation.Options.Style.NamingPreferences;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Style;
 
internal sealed class NamingStyleOptionPageViewModel : AbstractNotifyPropertyChanged
{
    public string ManageSpecificationsButtonText => ServicesVSResources.Manage_specifications;
    public string ManageStylesButtonText => ServicesVSResources.Manage_naming_styles;
 
    private readonly NotificationOptionViewModel[] _notifications =
    [
        new NotificationOptionViewModel(NotificationOption2.Silent, KnownMonikers.None),
        new NotificationOptionViewModel(NotificationOption2.Suggestion, KnownMonikers.StatusInformation),
        new NotificationOptionViewModel(NotificationOption2.Warning, KnownMonikers.StatusWarning),
        new NotificationOptionViewModel(NotificationOption2.Error, KnownMonikers.StatusError)
    ];
 
    public string CodeStyleMembersAutomationText => ServicesVSResources.Naming_rules;
 
    public ObservableCollection<NamingRuleViewModel> CodeStyleItems { get; set; }
    public ObservableCollection<SymbolSpecification> Specifications { get; set; }
    public ObservableCollection<MutableNamingStyle> NamingStyles { get; set; }
 
    public NamingStyleOptionPageViewModel(NamingStylePreferences info)
    {
        var viewModels = new List<NamingRuleViewModel>();
        foreach (var namingRule in info.Rules.NamingRules)
        {
            var viewModel = new NamingRuleViewModel()
            {
                NamingStyles = [.. info.NamingStyles.Select(n => new MutableNamingStyle(n))],
                Specifications = [.. info.SymbolSpecifications],
                NotificationPreferences = [.. _notifications]
            };
 
            viewModel.SelectedSpecification = viewModel.Specifications.Single(s => s.ID == namingRule.SymbolSpecification.ID);
            viewModel.SelectedStyle = viewModel.NamingStyles.Single(s => s.ID == namingRule.NamingStyle.ID);
            viewModel.SelectedNotificationPreference = viewModel.NotificationPreferences.Single(n => n.Notification.Severity == namingRule.EnforcementLevel);
 
            viewModels.Add(viewModel);
        }
 
        CodeStyleItems = new ObservableCollection<NamingRuleViewModel>(viewModels);
        Specifications = [.. info.SymbolSpecifications];
        NamingStyles = [.. info.NamingStyles.Select(n => new MutableNamingStyle(n))];
 
        SetMoveArrowStatuses();
    }
 
    private int _selectedIndex;
    public int SelectedIndex
    {
        get
        {
            return _selectedIndex;
        }
 
        set
        {
            if (value == _selectedIndex)
            {
                return;
            }
 
            _selectedIndex = value;
        }
    }
 
    internal void AddItem(NamingRuleViewModel namingRuleViewModel)
    {
        CodeStyleItems.Add(namingRuleViewModel);
        SetMoveArrowStatuses();
    }
 
    internal void RemoveItem(NamingRuleViewModel namingRuleViewModel)
    {
        CodeStyleItems.Remove(namingRuleViewModel);
        SetMoveArrowStatuses();
    }
 
    internal void UpdateSpecificationList(ManageSymbolSpecificationsDialogViewModel viewModel)
    {
        var symbolSpecifications = viewModel.Items.Cast<SymbolSpecificationViewModel>().Select(n => new SymbolSpecification(
            n.ID,
            n.ItemName,
            [.. n.SymbolKindList.Where(s => s.IsChecked).Select(k => k.CreateSymbolOrTypeOrMethodKind())],
            [.. n.AccessibilityList.Where(s => s.IsChecked).Select(a => a._accessibility)],
            [.. n.ModifierList.Where(s => s.IsChecked).Select(m => new SymbolSpecification.ModifierKind(m._modifier.Modifiers))]));
 
        Specifications.Clear();
        foreach (var specification in symbolSpecifications)
        {
            Specifications.Add(specification);
        }
 
        // The existing rules have had their Specifications pulled out from underneath them, so
        // this goes through and resets them.
 
        foreach (var rule in CodeStyleItems)
        {
            var selectedSpecification = rule.SelectedSpecification;
 
            rule.Specifications.Clear();
            foreach (var specification in symbolSpecifications)
            {
                rule.Specifications.Add(specification);
            }
 
            // Set the SelectedSpecification to null and then back to the actual selected 
            // specification to trigger the INotifyPropertyChanged event.
 
            rule.SelectedSpecification = null;
 
            if (selectedSpecification != null)
            {
                rule.SelectedSpecification = rule.Specifications.Single(s => s.ID == selectedSpecification.ID);
            }
        }
    }
 
    internal void MoveItem(int oldSelectedIndex, int newSelectedIndex)
    {
        CodeStyleItems.Move(oldSelectedIndex, newSelectedIndex);
        SetMoveArrowStatuses();
    }
 
    private void SetMoveArrowStatuses()
    {
        for (var i = 0; i < CodeStyleItems.Count; i++)
        {
            CodeStyleItems[i].CanMoveUp = true;
            CodeStyleItems[i].CanMoveDown = true;
 
            if (i == 0)
            {
                CodeStyleItems[i].CanMoveUp = false;
            }
 
            if (i == CodeStyleItems.Count - 1)
            {
                CodeStyleItems[i].CanMoveDown = false;
            }
        }
    }
 
    internal void UpdateStyleList(ManageNamingStylesDialogViewModel viewModel)
    {
        var namingStyles = viewModel.Items.Cast<NamingStyleViewModel>().Select(n => new MutableNamingStyle(
            new NamingStyle(
                id: n.ID,
                name: n.ItemName,
                prefix: n.RequiredPrefix,
                suffix: n.RequiredSuffix,
                wordSeparator: n.WordSeparator,
                capitalizationScheme: n.CapitalizationSchemes[n.CapitalizationSchemeIndex].Capitalization)));
 
        NamingStyles.Clear();
        foreach (var style in namingStyles)
        {
            NamingStyles.Add(style);
        }
 
        // The existing rules have had their Styles pulled out from underneath them, so
        // this goes through and resets them.
 
        foreach (var rule in CodeStyleItems)
        {
            var selectedStyle = rule.SelectedStyle;
 
            rule.NamingStyles.Clear();
            foreach (var style in namingStyles)
            {
                rule.NamingStyles.Add(style);
            }
 
            // Set the SelectedStyle to null and then back to the actual selected 
            // style to trigger the INotifyPropertyChanged event.
 
            rule.SelectedStyle = null;
 
            if (selectedStyle != null)
            {
                rule.SelectedStyle = rule.NamingStyles.Single(n => n.ID == selectedStyle.ID);
            }
        }
    }
 
    internal sealed class NamingRuleViewModel : AbstractNotifyPropertyChanged
    {
        public NamingRuleViewModel()
        {
            Specifications = [];
            NamingStyles = [];
            NotificationPreferences = [];
        }
 
        private SymbolSpecification _selectedSpecification;
        private MutableNamingStyle _selectedNamingStyle;
        private NotificationOptionViewModel _selectedNotification;
 
        public ObservableCollection<SymbolSpecification> Specifications { get; set; }
        public ObservableCollection<MutableNamingStyle> NamingStyles { get; set; }
        public IEnumerable<NotificationOptionViewModel> NotificationPreferences { get; set; }
 
        public SymbolSpecification SelectedSpecification
        {
            get
            {
                return _selectedSpecification;
            }
            set
            {
                SetProperty(ref _selectedSpecification, value);
            }
        }
 
        public MutableNamingStyle SelectedStyle
        {
            get
            {
                return _selectedNamingStyle;
            }
            set
            {
                SetProperty(ref _selectedNamingStyle, value);
            }
        }
        public NotificationOptionViewModel SelectedNotificationPreference
        {
            get
            {
                return _selectedNotification;
            }
 
            set
            {
                SetProperty(ref _selectedNotification, value);
            }
        }
 
        private bool _canMoveUp;
        public bool CanMoveUp
        {
            get
            {
                return _canMoveUp;
            }
 
            set
            {
                SetProperty(ref _canMoveUp, value);
            }
        }
 
        private bool _canMoveDown;
        public bool CanMoveDown
        {
            get
            {
                return _canMoveDown;
            }
 
            set
            {
                SetProperty(ref _canMoveDown, value);
            }
        }
 
        public string MoveUpAutomationText => ServicesVSResources.Move_up;
        public string MoveDownAutomationText => ServicesVSResources.Move_down;
 
        public string RemoveAutomationText => ServicesVSResources.Remove;
 
        public bool IsComplete()
            => SelectedSpecification != null && SelectedStyle != null && SelectedNotificationPreference != null;
 
        // For screen readers
        public override string ToString() => ServicesVSResources.Naming_Rule;
    }
}