File: InlineRename\UI\Dashboard\RenameDashboardViewModel.cs
Web Access
Project: src\src\EditorFeatures\Core.Wpf\Microsoft.CodeAnalysis.EditorFeatures.Wpf_s3pc0hsu_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures.Wpf)
// 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.Immutable;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.InlineRename;
using Microsoft.VisualStudio.Text.Editor;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename
{
    internal class RenameDashboardViewModel : INotifyPropertyChanged, IDisposable
    {
        private readonly IThreadingContext _threadingContext;
        private readonly IWpfTextView _textView;
        private RenameDashboardSeverity _severity = RenameDashboardSeverity.None;
        private int _resolvableConflictCount;
        private int _unresolvableConflictCount;
        private bool _isReplacementTextValid;
        private Visibility _visibility;
 
        public RenameDashboardViewModel(InlineRenameSession session, IThreadingContext threadingContext, IWpfTextView wpfTextView)
        {
            Session = session;
            SearchText = EditorFeaturesResources.Searching;
            _textView = wpfTextView;
 
            Session.ReferenceLocationsChanged += OnReferenceLocationsChanged;
            Session.ReplacementsComputed += OnReplacementsComputed;
            Session.ReplacementTextChanged += OnReplacementTextChanged;
            Session.CommitStateChange += CommitStateChange;
 
            _textView.GotAggregateFocus += GotAggregateFocus;
            _textView.LostAggregateFocus += LostAggregateFocus;
 
            // Set the flag to true by default if we're showing the option.
            _isReplacementTextValid = true;
            _threadingContext = threadingContext;
            RefreshVisibility();
        }
 
        private void LostAggregateFocus(object sender, EventArgs _)
            => RefreshVisibility();
 
        private void GotAggregateFocus(object sender, EventArgs _)
            => RefreshVisibility();
 
        private void CommitStateChange(object sender, EventArgs _)
            => RefreshVisibility();
 
        private void RefreshVisibility()
            => Visibility = !Session.IsCommitInProgress && _textView.HasAggregateFocus
            ? Visibility.Visible : Visibility.Collapsed;
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        private void OnReferenceLocationsChanged(object sender, ImmutableArray<InlineRenameLocation> renameLocations)
        {
            var totalFilesCount = renameLocations.GroupBy(s => s.Document).Count();
            var totalSpansCount = renameLocations.Length;
 
            UpdateSearchText(totalSpansCount, totalFilesCount);
        }
 
        private void OnIsReplacementTextValidChanged(bool isReplacementTextValid)
        {
            if (isReplacementTextValid == _isReplacementTextValid)
            {
                return;
            }
 
            _isReplacementTextValid = isReplacementTextValid;
            NotifyPropertyChanged(nameof(AllowFileRename));
        }
 
        private void OnReplacementsComputed(object sender, IInlineRenameReplacementInfo result)
        {
            var session = (InlineRenameSession)sender;
            _resolvableConflictCount = 0;
            _unresolvableConflictCount = 0;
            OnIsReplacementTextValidChanged(result.ReplacementTextValid);
            if (result.ReplacementTextValid)
            {
                ErrorText = null;
                foreach (var resolution in result.GetAllReplacementKinds())
                {
                    switch (resolution)
                    {
                        case InlineRenameReplacementKind.ResolvedReferenceConflict:
                        case InlineRenameReplacementKind.ResolvedNonReferenceConflict:
                            _resolvableConflictCount++;
                            break;
                        case InlineRenameReplacementKind.UnresolvedConflict:
                            _unresolvableConflictCount++;
                            break;
                    }
                }
            }
            else
            {
                ErrorText = string.IsNullOrEmpty(session.ReplacementText)
                    ? null
                    : EditorFeaturesResources.The_new_name_is_not_a_valid_identifier;
            }
 
            UpdateSeverity();
            AllPropertiesChanged();
        }
 
        private void OnReplacementTextChanged(object sender, EventArgs args)
        {
            // When the new name changes, we need to update the display of the new name or the
            // instructional text, depending on whether the new name and the original name are
            // distinct.
            NotifyPropertyChanged(nameof(ShouldShowInstructions));
            NotifyPropertyChanged(nameof(ShouldShowNewName));
            NotifyPropertyChanged(nameof(NewNameDescription));
        }
 
        private void NotifyPropertyChanged([CallerMemberName] string name = null)
            => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
 
        private void AllPropertiesChanged()
            => NotifyPropertyChanged(string.Empty);
 
        private void UpdateSearchText(int referenceCount, int fileCount)
        {
            if (referenceCount == 1 && fileCount == 1)
            {
                SearchText = EditorFeaturesResources.Rename_will_update_1_reference_in_1_file;
            }
            else if (fileCount == 1)
            {
                SearchText = string.Format(EditorFeaturesResources.Rename_will_update_0_references_in_1_file, referenceCount);
            }
            else
            {
                SearchText = string.Format(EditorFeaturesResources.Rename_will_update_0_references_in_1_files, referenceCount, fileCount);
            }
 
            NotifyPropertyChanged("SearchText");
        }
 
        private void UpdateSeverity()
        {
            if (ErrorText != null ||
                _unresolvableConflictCount > 0)
            {
                _severity = RenameDashboardSeverity.Error;
            }
            else if (_resolvableConflictCount > 0)
            {
                _severity = RenameDashboardSeverity.Info;
            }
            else
            {
                _severity = RenameDashboardSeverity.None;
            }
        }
 
        public InlineRenameSession Session { get; }
 
        public RenameDashboardSeverity Severity => _severity;
 
        public bool AllowFileRename => Session.FileRenameInfo == InlineRenameFileRenameInfo.Allowed && _isReplacementTextValid;
        public bool ShowFileRename => Session.FileRenameInfo != InlineRenameFileRenameInfo.NotAllowed;
        public string FileRenameString => Session.FileRenameInfo switch
        {
            InlineRenameFileRenameInfo.TypeDoesNotMatchFileName => EditorFeaturesResources.Rename_file_name_doesnt_match,
            InlineRenameFileRenameInfo.TypeWithMultipleLocations => EditorFeaturesResources.Rename_file_partial_type,
            _ => EditorFeaturesResources.Rename_symbols_file
        };
 
        public Visibility Visibility
        {
            get
            {
                _threadingContext.ThrowIfNotOnUIThread();
                return _visibility;
            }
            set
            {
                _threadingContext.ThrowIfNotOnUIThread();
                if (_visibility != value)
                {
                    _visibility = value;
                    NotifyPropertyChanged();
                }
            }
        }
 
        public string HeaderText
        {
            get
            {
                return string.Format(EditorFeaturesResources.Rename_colon_0, Session.OriginalSymbolName);
            }
        }
 
        public string NewNameDescription
        {
            get
            {
                return string.Format(EditorFeaturesResources.New_name_colon_0, Session.ReplacementText);
            }
        }
 
        public bool ShouldShowInstructions
        {
            get
            {
                return Session.OriginalSymbolName == Session.ReplacementText;
            }
        }
 
        public bool ShouldShowNewName
        {
            get
            {
                return !ShouldShowInstructions;
            }
        }
 
        public string SearchText { get; private set; }
 
        public bool HasResolvableConflicts
        {
            get { return _resolvableConflictCount > 0; }
        }
 
        public string ResolvableConflictText
        {
            get
            {
                return _resolvableConflictCount >= 1
                    ? string.Format(EditorFeaturesResources._0_conflict_s_will_be_resolved, _resolvableConflictCount)
                    : null;
            }
        }
 
        public bool HasUnresolvableConflicts
        {
            get { return _unresolvableConflictCount > 0; }
        }
 
        public string UnresolvableConflictText
        {
            get
            {
                return _unresolvableConflictCount >= 1
                   ? string.Format(EditorFeaturesResources._0_unresolvable_conflict_s, _unresolvableConflictCount)
                   : null;
            }
        }
 
        public bool HasError
            => ErrorText != null;
 
        public string ErrorText { get; private set; }
 
        public Visibility RenameOverloadsVisibility
            => Session.HasRenameOverloads ? Visibility.Visible : Visibility.Collapsed;
 
        public bool IsRenameOverloadsEditable
            => !Session.MustRenameOverloads;
 
        public bool DefaultRenameOverloadFlag
        {
            get => Session.Options.RenameOverloads;
 
            set
            {
                if (IsRenameOverloadsEditable)
                {
                    Session.RenameService.GlobalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameOverloads, value);
                    Session.RefreshRenameSessionWithOptionsChanged(Session.Options with { RenameOverloads = value });
                }
            }
        }
 
        public bool DefaultRenameInStringsFlag
        {
            get => Session.Options.RenameInStrings;
 
            set
            {
                Session.RenameService.GlobalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameInStrings, value);
                Session.RefreshRenameSessionWithOptionsChanged(Session.Options with { RenameInStrings = value });
            }
        }
 
        public bool DefaultRenameInCommentsFlag
        {
            get => Session.Options.RenameInComments;
 
            set
            {
                Session.RenameService.GlobalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameInComments, value);
                Session.RefreshRenameSessionWithOptionsChanged(Session.Options with { RenameInComments = value });
            }
        }
 
        public bool DefaultRenameFileFlag
        {
            get => Session.Options.RenameFile;
            set
            {
                Session.RenameService.GlobalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameFile, value);
                Session.RefreshRenameSessionWithOptionsChanged(Session.Options with { RenameFile = value });
            }
        }
 
        public bool DefaultPreviewChangesFlag
        {
            get => Session.PreviewChanges;
 
            set
            {
                Session.RenameService.GlobalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.PreviewChanges, value);
                Session.SetPreviewChanges(value);
            }
        }
 
        public string OriginalName => Session.OriginalSymbolName;
 
        public void Dispose()
        {
            Session.ReplacementTextChanged -= OnReplacementTextChanged;
            Session.ReferenceLocationsChanged -= OnReferenceLocationsChanged;
            Session.ReplacementsComputed -= OnReplacementsComputed;
            Session.CommitStateChange -= CommitStateChange;
            _textView.GotAggregateFocus -= GotAggregateFocus;
            _textView.LostAggregateFocus -= LostAggregateFocus;
        }
    }
}