From 9f492437e98933bf677a4b492f51b094e9c0f81c Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:21:19 -0700 Subject: [PATCH 01/27] Add options --- .../InlineRename/InlineRenameSessionOptionsStorage.cs | 9 +++++++++ .../Impl/Options/AdvancedOptionPageControl.xaml.cs | 5 ++++- .../Core/Def/Options/VisualStudioOptionStorage.cs | 2 ++ .../Impl/Options/AdvancedOptionPageControl.xaml.vb | 5 ++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs index 7655b058f080c..6902b51bbad99 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs @@ -2,6 +2,7 @@ // 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; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.InlineRename; @@ -13,5 +14,13 @@ internal static class InlineRenameSessionOptionsStorage public static readonly Option2 RenameInComments = new("dotnet_rename_in_comments", defaultValue: false); public static readonly Option2 RenameFile = new("dotnet_rename_file", defaultValue: true); public static readonly Option2 PreviewChanges = new("dotnet_preview_inline_rename_changes", defaultValue: false); + + [Obsolete] public static readonly Option2 RenameAsynchronously = new("dotnet_rename_asynchronously", defaultValue: true); + + public static readonly Option2 CommitRenameAsynchronously = new("dotnet_commit_rename_asynchronously", defaultValue: null); + public static readonly Option2 CommitRenameAsynchronouslyFeatureFlag = new("dotnet_commit_rename_asynchronously_feature_flag", defaultValue: false); + + public static bool ShouldCommitAsynchronously(this IGlobalOptionService globalOptionService) + => globalOptionService.GetOption(CommitRenameAsynchronously) ?? globalOptionService.GetOption(CommitRenameAsynchronouslyFeatureFlag); } diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index e440ed8430a4b..8223907a25851 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -105,7 +105,10 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(Always_use_default_symbol_servers_for_navigation, MetadataAsSourceOptionsStorage.AlwaysUseDefaultSymbolServers); // Rename - BindToOption(Rename_asynchronously_exerimental, InlineRenameSessionOptionsStorage.RenameAsynchronously); + BindToOption(Rename_asynchronously_exerimental, InlineRenameSessionOptionsStorage.CommitRenameAsynchronously, () => + { + return optionStore.GetOption(InlineRenameSessionOptionsStorage.CommitRenameAsynchronouslyFeatureFlag); + }); BindToOption(Rename_UI_setting, InlineRenameUIOptionsStorage.UseInlineAdornment, label: Rename_UI_setting_label); // Using Directives diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 8ceae9cd6694d..532bc9517065c 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -344,6 +344,8 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_rename_use_inline_adornment", new RoamingProfileStorage("TextEditor.RenameUseInlineAdornment")}, {"dotnet_preview_inline_rename_changes", new RoamingProfileStorage("TextEditor.Specific.PreviewRename")}, {"dotnet_collapse_suggestions_in_inline_rename_ui", new RoamingProfileStorage("TextEditor.CollapseRenameSuggestionsUI")}, + {"dotnet_commit_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.CommitRenameAsynchronously")}, + {"dotnet_commit_rename_asynchronously_feature_flag", new FeatureFlagStorage("Roslyn.CommitRenameAsynchronously")}, {"dotnet_rename_get_suggestions_automatically", new FeatureFlagStorage(@"Editor.AutoSmartRenameSuggestions")}, {"dotnet_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.RenameAsynchronously")}, {"dotnet_rename_file", new RoamingProfileStorage("TextEditor.Specific.RenameFile")}, diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index f3383eddb4831..363c82418445a 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -98,7 +98,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options End Function) ' Rename - BindToOption(Rename_asynchronously_exerimental, InlineRenameSessionOptionsStorage.RenameAsynchronously) + BindToOption(Rename_asynchronously_exerimental, InlineRenameSessionOptionsStorage.CommitRenameAsynchronously, + Function() + Return optionStore.GetOption(InlineRenameSessionOptionsStorage.CommitRenameAsynchronouslyFeatureFlag) + End Function) BindToOption(Rename_UI_setting, InlineRenameUIOptionsStorage.UseInlineAdornment, label:=Rename_UI_setting_label) ' Import directives From 61ca7356fb63dfb81166b9b87524f5e1ee0cd133 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:23:55 -0700 Subject: [PATCH 02/27] Use the featureFlag in rename session --- .../Core/InlineRename/InlineRenameSession.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs index 7637bb6cef2a5..20197483bea12 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs @@ -56,6 +56,7 @@ internal partial class InlineRenameSession : IInlineRenameSession, IFeatureContr private bool _dismissed; private bool _isApplyingEdit; private string _replacementText; + private bool _applyingChangeToWorkspace = false; private readonly Dictionary _openTextBuffers = []; /// @@ -652,6 +653,11 @@ public void Cancel() { _threadingContext.ThrowIfNotOnUIThread(); + if (_applyingChangeToWorkspace) + { + return; + } + DismissUIAndRollbackEditsAndEndRenameSession_MustBeCalledOnUIThread( RenameLogMessage.UserActionOutcome.Canceled, previewChanges: false); } @@ -749,7 +755,7 @@ private bool CommitSynchronously(bool previewChanges, IUIThreadOperationContext /// public async Task CommitAsync(bool previewChanges, IUIThreadOperationContext editorOperationContext = null) { - if (this.RenameService.GlobalOptions.GetOption(InlineRenameSessionOptionsStorage.RenameAsynchronously)) + if (this.RenameService.GlobalOptions.ShouldCommitAsynchronously()) { await CommitWorkerAsync(previewChanges, canUseBackgroundWorkIndicator: true, editorOperationContext).ConfigureAwait(false); } @@ -782,6 +788,12 @@ private async Task CommitWorkerAsync(bool previewChanges, bool canUseBackg return false; } + // Don't dup commit. + if (this.IsCommitInProgress) + { + return false; + } + previewChanges = previewChanges || PreviewChanges; if (editorUIOperationContext is not null) @@ -886,6 +898,7 @@ private async Task CommitCoreAsync(IUIThreadOperationContext operationContext, b // We're about to make irrevocable changes to the workspace and UI. We're no longer cancellable at this point. using var _2 = operationContext.AddScope(allowCancellation: false, EditorFeaturesResources.Updating_files); cancellationToken = CancellationToken.None; + _applyingChangeToWorkspace = true; // Dismiss the rename UI and rollback any linked edits made. DismissUIAndRollbackEditsAndEndRenameSession_MustBeCalledOnUIThread( From 2c8a7427bbdf29691c84709acd107181711012d2 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:26:06 -0700 Subject: [PATCH 03/27] Async commit in view model --- .../InlineRename/UI/Adornment/RenameFlyoutViewModel.cs | 3 ++- .../InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs index 073bbc74a5850..6b9246cdb1032 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs @@ -24,6 +24,7 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.PlatformUI.OleComponentSupport; using Microsoft.VisualStudio.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename { @@ -228,7 +229,7 @@ public bool Submit() } SmartRenameViewModel?.Commit(IdentifierText); - Session.Commit(); + _ = Session.CommitAsync(previewChanges: false).ReportNonFatalErrorAsync(); return true; } diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index eab834a57bc04..c3b034ddf9128 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; @@ -243,7 +244,7 @@ protected override void OnAccessKey(AccessKeyEventArgs e) } else if (string.Equals(e.Key, RenameShortcutKey.Apply, StringComparison.OrdinalIgnoreCase)) { - this.Commit(); + _ = this.CommitAsync(); } } } @@ -318,13 +319,14 @@ private void CloseButton_Click(object sender, RoutedEventArgs e) } private void Apply_Click(object sender, RoutedEventArgs e) - => Commit(); + => _ = CommitAsync(); - private void Commit() + private async Task CommitAsync() { try { - _model.Session.Commit(); + //.ConfigureAwait(true) to make sure Exceptions could be shown in UI. + await _model.Session.CommitAsync(previewChanges: false).ConfigureAwait(true); _textView.VisualElement.Focus(); } catch (NotSupportedException ex) From 9aeb065c608fb9f44f6b7565917e78f7d777825c Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:26:40 -0700 Subject: [PATCH 04/27] Add comment --- src/EditorFeatures/Core/IInlineRenameSession.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/EditorFeatures/Core/IInlineRenameSession.cs b/src/EditorFeatures/Core/IInlineRenameSession.cs index f644a16a423b5..f758c652b4f75 100644 --- a/src/EditorFeatures/Core/IInlineRenameSession.cs +++ b/src/EditorFeatures/Core/IInlineRenameSession.cs @@ -52,5 +52,8 @@ internal interface IInlineRenameSession /// /// Dismisses the rename session, completing the rename operation across all files. /// + /// + /// It will only be async when InlineRenameUIOptionsStorage.CommitRenameAsynchronously is set to true. + /// Task CommitAsync(bool previewChanges, IUIThreadOperationContext editorOperationContext = null); } From 89cf25a44c4e51d71db7cbcb3cc5675c5d767dcd Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:38:18 -0700 Subject: [PATCH 05/27] Use primary constructor --- .../CommandHandlers/RenameCommandHandler.cs | 18 ++++----- .../AbstractRenameCommandHandler.cs | 37 +++++++------------ ...tractRenameCommandHandler_EscapeHandler.cs | 4 +- ...enameCommandHandler_LineStartEndHandler.cs | 4 +- ...tractRenameCommandHandler_RenameHandler.cs | 12 +++--- ...tractRenameCommandHandler_ReturnHandler.cs | 4 +- ...bstractRenameCommandHandler_SaveHandler.cs | 2 +- ...ctRenameCommandHandler_SelectAllHandler.cs | 4 +- ...actRenameCommandHandler_UndoRedoHandler.cs | 12 +++--- ...tRenameCommandHandler_WordDeleteHandler.cs | 4 +- 10 files changed, 43 insertions(+), 58 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs index 9283a50c0bf08..0b74fdaf53a24 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs @@ -28,18 +28,14 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename [Order(Before = PredefinedCommandHandlerNames.ChangeSignature)] [Order(Before = PredefinedCommandHandlerNames.ExtractInterface)] [Order(Before = PredefinedCommandHandlerNames.EncapsulateField)] - internal partial class RenameCommandHandler : AbstractRenameCommandHandler + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal partial class RenameCommandHandler( + IThreadingContext threadingContext, + InlineRenameService renameService, + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) + : AbstractRenameCommandHandler(threadingContext, renameService, asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Rename)) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RenameCommandHandler( - IThreadingContext threadingContext, - InlineRenameService renameService, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) - : base(threadingContext, renameService, asynchronousOperationListenerProvider) - { - } - protected override bool AdornmentShouldReceiveKeyboardNavigation(ITextView textView) => GetAdornment(textView) switch { diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs index 327254149e670..8db2b477e3a70 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs @@ -16,22 +16,11 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; -internal abstract partial class AbstractRenameCommandHandler +internal abstract partial class AbstractRenameCommandHandler( + IThreadingContext threadingContext, + InlineRenameService renameService, + IAsynchronousOperationListener listener) { - private readonly IThreadingContext _threadingContext; - private readonly InlineRenameService _renameService; - private readonly IAsynchronousOperationListener _listener; - - protected AbstractRenameCommandHandler( - IThreadingContext threadingContext, - InlineRenameService renameService, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) - { - _threadingContext = threadingContext; - _renameService = renameService; - _listener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Rename); - } - public string DisplayName => EditorFeaturesResources.Rename; protected abstract bool AdornmentShouldReceiveKeyboardNavigation(ITextView textView); @@ -46,7 +35,7 @@ protected AbstractRenameCommandHandler( private CommandState GetCommandState(Func nextHandler) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { return CommandState.Available; } @@ -55,12 +44,12 @@ private CommandState GetCommandState(Func nextHandler) } private CommandState GetCommandState() - => _renameService.ActiveSession != null ? CommandState.Available : CommandState.Unspecified; + => renameService.ActiveSession != null ? CommandState.Available : CommandState.Unspecified; private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, IUIThreadOperationContext operationContext, Action actionIfInsideActiveSpan) where TArgs : EditorCommandArgs { - if (_renameService.ActiveSession == null) + if (renameService.ActiveSession == null) { nextHandler(); return; @@ -77,12 +66,12 @@ private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, } var singleSpan = selectedSpans.Single(); - if (_renameService.ActiveSession.TryGetContainingEditableSpan(singleSpan.Start, out var containingSpan) && + if (renameService.ActiveSession.TryGetContainingEditableSpan(singleSpan.Start, out var containingSpan) && containingSpan.Contains(singleSpan)) { - actionIfInsideActiveSpan(_renameService.ActiveSession, operationContext, containingSpan); + actionIfInsideActiveSpan(renameService.ActiveSession, operationContext, containingSpan); } - else if (_renameService.ActiveSession.IsInOpenTextBuffer(singleSpan.Start)) + else if (renameService.ActiveSession.IsInOpenTextBuffer(singleSpan.Start)) { // It's in a read-only area that is open, so let's commit the rename // and then let the character go through @@ -98,7 +87,7 @@ private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, private void CommitIfActive(EditorCommandArgs args, IUIThreadOperationContext operationContext) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { var selection = args.TextView.Selection.VirtualSelectedSpans.First(); @@ -118,7 +107,7 @@ private void CommitIfActiveAndCallNextHandler(EditorCommandArgs args, Action nex private void Commit(IUIThreadOperationContext operationContext) { - RoslynDebug.AssertNotNull(_renameService.ActiveSession); - _renameService.ActiveSession.Commit(previewChanges: false, operationContext); + RoslynDebug.AssertNotNull(renameService.ActiveSession); + renameService.ActiveSession.Commit(previewChanges: false, operationContext); } } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs index 9d80929da596a..2a73834961c84 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs @@ -14,9 +14,9 @@ public CommandState GetCommandState(EscapeKeyCommandArgs args) public bool ExecuteCommand(EscapeKeyCommandArgs args, CommandExecutionContext context) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { - _renameService.ActiveSession.Cancel(); + renameService.ActiveSession.Cancel(); SetFocusToTextView(args.TextView); return true; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs index 6a78bc4b23ae0..1f853396cd729 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs @@ -40,7 +40,7 @@ public bool ExecuteCommand(LineEndExtendCommandArgs args, CommandExecutionContex private bool HandleLineStartOrLineEndCommand(ITextBuffer subjectBuffer, ITextView view, bool lineStart, bool extendSelection) { - if (_renameService.ActiveSession == null) + if (renameService.ActiveSession == null) { return false; } @@ -48,7 +48,7 @@ private bool HandleLineStartOrLineEndCommand(ITextBuffer subjectBuffer, ITextVie var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { - if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) + if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) { var newPoint = lineStart ? span.Start : span.End; if (newPoint == caretPoint.Value && (view.Selection.IsEmpty || extendSelection)) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs index 8fce77aa75c2b..6e02773342067 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs @@ -43,14 +43,14 @@ public bool ExecuteCommand(RenameCommandArgs args, CommandExecutionContext conte return false; } - var token = _listener.BeginAsyncOperation(nameof(ExecuteCommand)); + var token = listener.BeginAsyncOperation(nameof(ExecuteCommand)); _ = ExecuteCommandAsync(args, context.OperationContext).CompletesAsyncOperation(token); return true; } private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperationContext editorOperationContext) { - _threadingContext.ThrowIfNotOnUIThread(); + threadingContext.ThrowIfNotOnUIThread(); if (!args.SubjectBuffer.TryGetWorkspace(out var workspace)) { @@ -65,11 +65,11 @@ private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperatio } // If there is already an active session, commit it first - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { // Is the caret within any of the rename fields in this buffer? // If so, focus the dashboard - if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) + if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) { SetFocusToAdornment(args.TextView); return; @@ -111,7 +111,7 @@ private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperatio return; } - var sessionInfo = await _renameService.StartInlineSessionAsync(document, selectedSpans.Single().Span.ToTextSpan(), cancellationToken).ConfigureAwait(false); + var sessionInfo = await renameService.StartInlineSessionAsync(document, selectedSpans.Single().Span.ToTextSpan(), cancellationToken).ConfigureAwait(false); if (!sessionInfo.CanRename) { await ShowErrorDialogAsync(workspace, sessionInfo.LocalizedErrorMessage).ConfigureAwait(false); @@ -131,7 +131,7 @@ private static bool CanRename(RenameCommandArgs args) private async Task ShowErrorDialogAsync(Workspace workspace, string message) { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); var notificationService = workspace.Services.GetService(); notificationService.SendNotification(message, title: EditorFeaturesResources.Rename, severity: NotificationSeverity.Error); } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs index db18065f46759..e7d36e424cd9a 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs @@ -16,9 +16,9 @@ public CommandState GetCommandState(ReturnKeyCommandArgs args) public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext context) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { - CommitAndSetFocus(_renameService.ActiveSession, args.TextView, context.OperationContext); + CommitAndSetFocus(renameService.ActiveSession, args.TextView, context.OperationContext); return true; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs index 132c8d0f42a5d..6a1b2a8dfb6d5 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs @@ -14,7 +14,7 @@ public CommandState GetCommandState(SaveCommandArgs args) public bool ExecuteCommand(SaveCommandArgs args, CommandExecutionContext context) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { Commit(context.OperationContext); SetFocusToTextView(args.TextView); diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs index 037ebed9e5189..601adef3108c2 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs @@ -21,7 +21,7 @@ public bool ExecuteCommand(SelectAllCommandArgs args, CommandExecutionContext co private bool ExecuteSelectAll(ITextBuffer subjectBuffer, ITextView view) { - if (_renameService.ActiveSession == null) + if (renameService.ActiveSession == null) { return false; } @@ -29,7 +29,7 @@ private bool ExecuteSelectAll(ITextBuffer subjectBuffer, ITextView view) var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { - if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) + if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) { if (view.Selection.Start.Position != span.Start.Position || view.Selection.End.Position != span.End.Position) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs index 3dd653f02acba..5f665da1742a3 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs @@ -18,11 +18,11 @@ public CommandState GetCommandState(RedoCommandArgs args) public bool ExecuteCommand(UndoCommandArgs args, CommandExecutionContext context) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { - for (var i = 0; i < args.Count && _renameService.ActiveSession != null; i++) + for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) { - _renameService.ActiveSession.UndoManager.Undo(args.SubjectBuffer); + renameService.ActiveSession.UndoManager.Undo(args.SubjectBuffer); } return true; @@ -33,11 +33,11 @@ public bool ExecuteCommand(UndoCommandArgs args, CommandExecutionContext context public bool ExecuteCommand(RedoCommandArgs args, CommandExecutionContext context) { - if (_renameService.ActiveSession != null) + if (renameService.ActiveSession != null) { - for (var i = 0; i < args.Count && _renameService.ActiveSession != null; i++) + for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) { - _renameService.ActiveSession.UndoManager.Redo(args.SubjectBuffer); + renameService.ActiveSession.UndoManager.Redo(args.SubjectBuffer); } return true; diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs index af47ca63fc252..efbf835488bd9 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs @@ -29,7 +29,7 @@ public bool ExecuteCommand(WordDeleteToEndCommandArgs args, CommandExecutionCont private bool HandleWordDeleteCommand(ITextBuffer subjectBuffer, ITextView view, bool deleteToStart) { - if (_renameService.ActiveSession == null) + if (renameService.ActiveSession == null) { return false; } @@ -37,7 +37,7 @@ private bool HandleWordDeleteCommand(ITextBuffer subjectBuffer, ITextView view, var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { - if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) + if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) { int start = caretPoint.Value; int end = caretPoint.Value; From 0f0029ec4f915ac045b9f0b355aa6a9cd2279213 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:57:35 -0700 Subject: [PATCH 06/27] Prevent change when rename is in-progress --- .../AbstractRenameCommandHandler.cs | 5 +++ ...tractRenameCommandHandler_RenameHandler.cs | 10 ++++-- ...actRenameCommandHandler_UndoRedoHandler.cs | 36 ++++++++++++------- ...tRenameCommandHandler_WordDeleteHandler.cs | 6 ++++ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs index 8db2b477e3a70..cc5602b1c95a1 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs @@ -55,6 +55,11 @@ private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, return; } + if (renameService.ActiveSession.IsCommitInProgress) + { + return; + } + var selectedSpans = args.TextView.Selection.GetSnapshotSpansOnBuffer(args.SubjectBuffer); if (selectedSpans.Count > 1) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs index 6e02773342067..d50cde7541347 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs @@ -67,10 +67,14 @@ private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperatio // If there is already an active session, commit it first if (renameService.ActiveSession != null) { - // Is the caret within any of the rename fields in this buffer? - // If so, focus the dashboard - if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) + if (renameService.ActiveSession.IsCommitInProgress) { + return; + } + else if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) + { + // Is the caret within any of the rename fields in this buffer? + // If so, focus the dashboard SetFocusToAdornment(args.TextView); return; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs index 5f665da1742a3..04c9d2d2aaa32 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs @@ -18,31 +18,43 @@ public CommandState GetCommandState(RedoCommandArgs args) public bool ExecuteCommand(UndoCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession != null) + if (renameService.ActiveSession == null) { - for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) - { - renameService.ActiveSession.UndoManager.Undo(args.SubjectBuffer); - } + return false; + } + if (renameService.ActiveSession.IsCommitInProgress) + { + // When rename commit is in progress, handle the command so it won't change the workspace return true; } - return false; + for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) + { + renameService.ActiveSession.UndoManager.Undo(args.SubjectBuffer); + } + + return true; } public bool ExecuteCommand(RedoCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession != null) + if (renameService.ActiveSession == null) { - for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) - { - renameService.ActiveSession.UndoManager.Redo(args.SubjectBuffer); - } + return false; + } + if (renameService.ActiveSession.IsCommitInProgress) + { + // When rename commit is in progress, handle the command so it won't change the workspace return true; } - return false; + for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) + { + renameService.ActiveSession.UndoManager.Redo(args.SubjectBuffer); + } + + return true; } } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs index efbf835488bd9..91965d7d68f27 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs @@ -34,6 +34,12 @@ private bool HandleWordDeleteCommand(ITextBuffer subjectBuffer, ITextView view, return false; } + if (renameService.ActiveSession.IsCommitInProgress) + { + // When rename commit is in progress, handle the command so it won't change the workspace + return true; + } + var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { From 0478d09e49a6ab6aa74cd277b792dc78ab1d82b5 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 16:59:56 -0700 Subject: [PATCH 07/27] Move return key to async --- .../AbstractRenameCommandHandler_ReturnHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs index e7d36e424cd9a..a156c9615e781 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs @@ -6,6 +6,7 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; @@ -27,7 +28,7 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co protected virtual void CommitAndSetFocus(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) { - Commit(operationContext); + _ = activeSession.CommitAsync(previewChanges: false, operationContext).ReportNonFatalErrorAsync(); SetFocusToTextView(textView); } } From 76c722d2489813173773cb5c73d115b434b94bed Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 17:01:25 -0700 Subject: [PATCH 08/27] Reset one change --- .../Core/InlineRename/InlineRenameSession.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs index 20197483bea12..0548ab0375397 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs @@ -56,7 +56,6 @@ internal partial class InlineRenameSession : IInlineRenameSession, IFeatureContr private bool _dismissed; private bool _isApplyingEdit; private string _replacementText; - private bool _applyingChangeToWorkspace = false; private readonly Dictionary _openTextBuffers = []; /// @@ -653,11 +652,6 @@ public void Cancel() { _threadingContext.ThrowIfNotOnUIThread(); - if (_applyingChangeToWorkspace) - { - return; - } - DismissUIAndRollbackEditsAndEndRenameSession_MustBeCalledOnUIThread( RenameLogMessage.UserActionOutcome.Canceled, previewChanges: false); } @@ -898,7 +892,6 @@ private async Task CommitCoreAsync(IUIThreadOperationContext operationContext, b // We're about to make irrevocable changes to the workspace and UI. We're no longer cancellable at this point. using var _2 = operationContext.AddScope(allowCancellation: false, EditorFeaturesResources.Updating_files); cancellationToken = CancellationToken.None; - _applyingChangeToWorkspace = true; // Dismiss the rename UI and rollback any linked edits made. DismissUIAndRollbackEditsAndEndRenameSession_MustBeCalledOnUIThread( From bc379f8cfef80a610a56ab507be6d2c33e112450 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 17:08:20 -0700 Subject: [PATCH 09/27] guard more command handlers --- .../CommandHandlers/AbstractRenameCommandHandler.cs | 3 +++ ...tRenameCommandHandler_MoveSelectedLinesHandler.cs | 6 ++++++ ...meCommandHandler_RefactoringWithCommandHandler.cs | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs index cc5602b1c95a1..5964eca8e40c0 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs @@ -115,4 +115,7 @@ private void Commit(IUIThreadOperationContext operationContext) RoslynDebug.AssertNotNull(renameService.ActiveSession); renameService.ActiveSession.Commit(previewChanges: false, operationContext); } + + private bool IsRenameCommitInProgress() + => renameService.ActiveSession?.IsCommitInProgress is true; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_MoveSelectedLinesHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_MoveSelectedLinesHandler.cs index e9a140815a143..3e7e92b0f14c1 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_MoveSelectedLinesHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_MoveSelectedLinesHandler.cs @@ -15,6 +15,9 @@ public CommandState GetCommandState(MoveSelectedLinesUpCommandArgs args) public bool ExecuteCommand(MoveSelectedLinesUpCommandArgs args, CommandExecutionContext context) { + if (IsRenameCommitInProgress()) + return true; + CommitIfActive(args, context.OperationContext); return false; } @@ -24,6 +27,9 @@ public CommandState GetCommandState(MoveSelectedLinesDownCommandArgs args) public bool ExecuteCommand(MoveSelectedLinesDownCommandArgs args, CommandExecutionContext context) { + if (IsRenameCommitInProgress()) + return true; + CommitIfActive(args, context.OperationContext); return false; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs index a3413138c2e74..7d2a383d99b9a 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs @@ -18,6 +18,9 @@ public CommandState GetCommandState(ReorderParametersCommandArgs args) public bool ExecuteCommand(ReorderParametersCommandArgs args, CommandExecutionContext context) { + if (IsRenameCommitInProgress()) + return true; + CommitIfActive(args, context.OperationContext); return false; } @@ -27,6 +30,9 @@ public CommandState GetCommandState(RemoveParametersCommandArgs args) public bool ExecuteCommand(RemoveParametersCommandArgs args, CommandExecutionContext context) { + if (IsRenameCommitInProgress()) + return true; + CommitIfActive(args, context.OperationContext); return false; } @@ -36,6 +42,9 @@ public CommandState GetCommandState(ExtractInterfaceCommandArgs args) public bool ExecuteCommand(ExtractInterfaceCommandArgs args, CommandExecutionContext context) { + if (IsRenameCommitInProgress()) + return true; + CommitIfActive(args, context.OperationContext); return false; } @@ -45,6 +54,9 @@ public CommandState GetCommandState(EncapsulateFieldCommandArgs args) public bool ExecuteCommand(EncapsulateFieldCommandArgs args, CommandExecutionContext context) { + if (IsRenameCommitInProgress()) + return true; + CommitIfActive(args, context.OperationContext); return false; } From 4c4ec720cc76438c0b9e3d3c3890ebdf63b95caf Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 1 Oct 2024 17:12:37 -0700 Subject: [PATCH 10/27] Revert "Use primary constructor" This reverts commit 89cf25a44c4e51d71db7cbcb3cc5675c5d767dcd. --- .../CommandHandlers/RenameCommandHandler.cs | 18 ++++---- .../AbstractRenameCommandHandler.cs | 41 ++++++++++++------- ...tractRenameCommandHandler_EscapeHandler.cs | 4 +- ...enameCommandHandler_LineStartEndHandler.cs | 4 +- ...tractRenameCommandHandler_RenameHandler.cs | 14 +++---- ...tractRenameCommandHandler_ReturnHandler.cs | 4 +- ...bstractRenameCommandHandler_SaveHandler.cs | 2 +- ...ctRenameCommandHandler_SelectAllHandler.cs | 4 +- ...actRenameCommandHandler_UndoRedoHandler.cs | 16 ++++---- ...tRenameCommandHandler_WordDeleteHandler.cs | 6 +-- 10 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs index 0b74fdaf53a24..9283a50c0bf08 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs @@ -28,14 +28,18 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename [Order(Before = PredefinedCommandHandlerNames.ChangeSignature)] [Order(Before = PredefinedCommandHandlerNames.ExtractInterface)] [Order(Before = PredefinedCommandHandlerNames.EncapsulateField)] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - internal partial class RenameCommandHandler( - IThreadingContext threadingContext, - InlineRenameService renameService, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) - : AbstractRenameCommandHandler(threadingContext, renameService, asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Rename)) + internal partial class RenameCommandHandler : AbstractRenameCommandHandler { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RenameCommandHandler( + IThreadingContext threadingContext, + InlineRenameService renameService, + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) + : base(threadingContext, renameService, asynchronousOperationListenerProvider) + { + } + protected override bool AdornmentShouldReceiveKeyboardNavigation(ITextView textView) => GetAdornment(textView) switch { diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs index 5964eca8e40c0..d4bd65088e3a5 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler.cs @@ -16,11 +16,22 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; -internal abstract partial class AbstractRenameCommandHandler( - IThreadingContext threadingContext, - InlineRenameService renameService, - IAsynchronousOperationListener listener) +internal abstract partial class AbstractRenameCommandHandler { + private readonly IThreadingContext _threadingContext; + private readonly InlineRenameService _renameService; + private readonly IAsynchronousOperationListener _listener; + + protected AbstractRenameCommandHandler( + IThreadingContext threadingContext, + InlineRenameService renameService, + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) + { + _threadingContext = threadingContext; + _renameService = renameService; + _listener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Rename); + } + public string DisplayName => EditorFeaturesResources.Rename; protected abstract bool AdornmentShouldReceiveKeyboardNavigation(ITextView textView); @@ -35,7 +46,7 @@ internal abstract partial class AbstractRenameCommandHandler( private CommandState GetCommandState(Func nextHandler) { - if (renameService.ActiveSession != null) + if (_renameService.ActiveSession != null) { return CommandState.Available; } @@ -44,18 +55,18 @@ private CommandState GetCommandState(Func nextHandler) } private CommandState GetCommandState() - => renameService.ActiveSession != null ? CommandState.Available : CommandState.Unspecified; + => _renameService.ActiveSession != null ? CommandState.Available : CommandState.Unspecified; private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, IUIThreadOperationContext operationContext, Action actionIfInsideActiveSpan) where TArgs : EditorCommandArgs { - if (renameService.ActiveSession == null) + if (_renameService.ActiveSession == null) { nextHandler(); return; } - if (renameService.ActiveSession.IsCommitInProgress) + if (_renameService.ActiveSession.IsCommitInProgress) { return; } @@ -71,12 +82,12 @@ private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, } var singleSpan = selectedSpans.Single(); - if (renameService.ActiveSession.TryGetContainingEditableSpan(singleSpan.Start, out var containingSpan) && + if (_renameService.ActiveSession.TryGetContainingEditableSpan(singleSpan.Start, out var containingSpan) && containingSpan.Contains(singleSpan)) { - actionIfInsideActiveSpan(renameService.ActiveSession, operationContext, containingSpan); + actionIfInsideActiveSpan(_renameService.ActiveSession, operationContext, containingSpan); } - else if (renameService.ActiveSession.IsInOpenTextBuffer(singleSpan.Start)) + else if (_renameService.ActiveSession.IsInOpenTextBuffer(singleSpan.Start)) { // It's in a read-only area that is open, so let's commit the rename // and then let the character go through @@ -92,7 +103,7 @@ private void HandlePossibleTypingCommand(TArgs args, Action nextHandler, private void CommitIfActive(EditorCommandArgs args, IUIThreadOperationContext operationContext) { - if (renameService.ActiveSession != null) + if (_renameService.ActiveSession != null) { var selection = args.TextView.Selection.VirtualSelectedSpans.First(); @@ -112,10 +123,10 @@ private void CommitIfActiveAndCallNextHandler(EditorCommandArgs args, Action nex private void Commit(IUIThreadOperationContext operationContext) { - RoslynDebug.AssertNotNull(renameService.ActiveSession); - renameService.ActiveSession.Commit(previewChanges: false, operationContext); + RoslynDebug.AssertNotNull(_renameService.ActiveSession); + _renameService.ActiveSession.Commit(previewChanges: false, operationContext); } private bool IsRenameCommitInProgress() - => renameService.ActiveSession?.IsCommitInProgress is true; + => _renameService.ActiveSession?.IsCommitInProgress is true; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs index 2a73834961c84..9d80929da596a 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_EscapeHandler.cs @@ -14,9 +14,9 @@ public CommandState GetCommandState(EscapeKeyCommandArgs args) public bool ExecuteCommand(EscapeKeyCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession != null) + if (_renameService.ActiveSession != null) { - renameService.ActiveSession.Cancel(); + _renameService.ActiveSession.Cancel(); SetFocusToTextView(args.TextView); return true; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs index 1f853396cd729..6a78bc4b23ae0 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_LineStartEndHandler.cs @@ -40,7 +40,7 @@ public bool ExecuteCommand(LineEndExtendCommandArgs args, CommandExecutionContex private bool HandleLineStartOrLineEndCommand(ITextBuffer subjectBuffer, ITextView view, bool lineStart, bool extendSelection) { - if (renameService.ActiveSession == null) + if (_renameService.ActiveSession == null) { return false; } @@ -48,7 +48,7 @@ private bool HandleLineStartOrLineEndCommand(ITextBuffer subjectBuffer, ITextVie var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { - if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) + if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) { var newPoint = lineStart ? span.Start : span.End; if (newPoint == caretPoint.Value && (view.Selection.IsEmpty || extendSelection)) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs index d50cde7541347..3c02a60ab982d 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs @@ -43,14 +43,14 @@ public bool ExecuteCommand(RenameCommandArgs args, CommandExecutionContext conte return false; } - var token = listener.BeginAsyncOperation(nameof(ExecuteCommand)); + var token = _listener.BeginAsyncOperation(nameof(ExecuteCommand)); _ = ExecuteCommandAsync(args, context.OperationContext).CompletesAsyncOperation(token); return true; } private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperationContext editorOperationContext) { - threadingContext.ThrowIfNotOnUIThread(); + _threadingContext.ThrowIfNotOnUIThread(); if (!args.SubjectBuffer.TryGetWorkspace(out var workspace)) { @@ -65,13 +65,13 @@ private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperatio } // If there is already an active session, commit it first - if (renameService.ActiveSession != null) + if (_renameService.ActiveSession != null) { - if (renameService.ActiveSession.IsCommitInProgress) + if (_renameService.ActiveSession.IsCommitInProgress) { return; } - else if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) + else if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) { // Is the caret within any of the rename fields in this buffer? // If so, focus the dashboard @@ -115,7 +115,7 @@ private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperatio return; } - var sessionInfo = await renameService.StartInlineSessionAsync(document, selectedSpans.Single().Span.ToTextSpan(), cancellationToken).ConfigureAwait(false); + var sessionInfo = await _renameService.StartInlineSessionAsync(document, selectedSpans.Single().Span.ToTextSpan(), cancellationToken).ConfigureAwait(false); if (!sessionInfo.CanRename) { await ShowErrorDialogAsync(workspace, sessionInfo.LocalizedErrorMessage).ConfigureAwait(false); @@ -135,7 +135,7 @@ private static bool CanRename(RenameCommandArgs args) private async Task ShowErrorDialogAsync(Workspace workspace, string message) { - await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); var notificationService = workspace.Services.GetService(); notificationService.SendNotification(message, title: EditorFeaturesResources.Rename, severity: NotificationSeverity.Error); } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs index a156c9615e781..f932a5ea7f9d8 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs @@ -17,9 +17,9 @@ public CommandState GetCommandState(ReturnKeyCommandArgs args) public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession != null) + if (_renameService.ActiveSession != null) { - CommitAndSetFocus(renameService.ActiveSession, args.TextView, context.OperationContext); + CommitAndSetFocus(_renameService.ActiveSession, args.TextView, context.OperationContext); return true; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs index 6a1b2a8dfb6d5..132c8d0f42a5d 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SaveHandler.cs @@ -14,7 +14,7 @@ public CommandState GetCommandState(SaveCommandArgs args) public bool ExecuteCommand(SaveCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession != null) + if (_renameService.ActiveSession != null) { Commit(context.OperationContext); SetFocusToTextView(args.TextView); diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs index 601adef3108c2..037ebed9e5189 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_SelectAllHandler.cs @@ -21,7 +21,7 @@ public bool ExecuteCommand(SelectAllCommandArgs args, CommandExecutionContext co private bool ExecuteSelectAll(ITextBuffer subjectBuffer, ITextView view) { - if (renameService.ActiveSession == null) + if (_renameService.ActiveSession == null) { return false; } @@ -29,7 +29,7 @@ private bool ExecuteSelectAll(ITextBuffer subjectBuffer, ITextView view) var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { - if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) + if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) { if (view.Selection.Start.Position != span.Start.Position || view.Selection.End.Position != span.End.Position) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs index 04c9d2d2aaa32..cac99aa1a18e5 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs @@ -18,20 +18,20 @@ public CommandState GetCommandState(RedoCommandArgs args) public bool ExecuteCommand(UndoCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession == null) + if (_renameService.ActiveSession == null) { return false; } - if (renameService.ActiveSession.IsCommitInProgress) + if (_renameService.ActiveSession.IsCommitInProgress) { // When rename commit is in progress, handle the command so it won't change the workspace return true; } - for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) + for (var i = 0; i < args.Count && _renameService.ActiveSession != null; i++) { - renameService.ActiveSession.UndoManager.Undo(args.SubjectBuffer); + _renameService.ActiveSession.UndoManager.Undo(args.SubjectBuffer); } return true; @@ -39,20 +39,20 @@ public bool ExecuteCommand(UndoCommandArgs args, CommandExecutionContext context public bool ExecuteCommand(RedoCommandArgs args, CommandExecutionContext context) { - if (renameService.ActiveSession == null) + if (_renameService.ActiveSession == null) { return false; } - if (renameService.ActiveSession.IsCommitInProgress) + if (_renameService.ActiveSession.IsCommitInProgress) { // When rename commit is in progress, handle the command so it won't change the workspace return true; } - for (var i = 0; i < args.Count && renameService.ActiveSession != null; i++) + for (var i = 0; i < args.Count && _renameService.ActiveSession != null; i++) { - renameService.ActiveSession.UndoManager.Redo(args.SubjectBuffer); + _renameService.ActiveSession.UndoManager.Redo(args.SubjectBuffer); } return true; diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs index 91965d7d68f27..1e1a1354081b4 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs @@ -29,12 +29,12 @@ public bool ExecuteCommand(WordDeleteToEndCommandArgs args, CommandExecutionCont private bool HandleWordDeleteCommand(ITextBuffer subjectBuffer, ITextView view, bool deleteToStart) { - if (renameService.ActiveSession == null) + if (_renameService.ActiveSession == null) { return false; } - if (renameService.ActiveSession.IsCommitInProgress) + if (_renameService.ActiveSession.IsCommitInProgress) { // When rename commit is in progress, handle the command so it won't change the workspace return true; @@ -43,7 +43,7 @@ private bool HandleWordDeleteCommand(ITextBuffer subjectBuffer, ITextView view, var caretPoint = view.GetCaretPoint(subjectBuffer); if (caretPoint.HasValue) { - if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) + if (_renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out var span)) { int start = caretPoint.Value; int end = caretPoint.Value; From 2fb97f4df3908fc7c79a365d86fb7ea72b1be894 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 12:13:54 -0700 Subject: [PATCH 11/27] Remove not used options --- .../Core/InlineRename/InlineRenameSessionOptionsStorage.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs index 6902b51bbad99..7e9f406c018a3 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs @@ -15,9 +15,6 @@ internal static class InlineRenameSessionOptionsStorage public static readonly Option2 RenameFile = new("dotnet_rename_file", defaultValue: true); public static readonly Option2 PreviewChanges = new("dotnet_preview_inline_rename_changes", defaultValue: false); - [Obsolete] - public static readonly Option2 RenameAsynchronously = new("dotnet_rename_asynchronously", defaultValue: true); - public static readonly Option2 CommitRenameAsynchronously = new("dotnet_commit_rename_asynchronously", defaultValue: null); public static readonly Option2 CommitRenameAsynchronouslyFeatureFlag = new("dotnet_commit_rename_asynchronously_feature_flag", defaultValue: false); From b7647406c97c724ac7cea901c85ec68c1b905ba9 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 13:35:02 -0700 Subject: [PATCH 12/27] Add listener provider --- .../InlineRename/UI/Adornment/RenameFlyoutViewModel.cs | 6 +++++- .../InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 7 ++++++- .../InlineRename/UI/InlineRenameAdornmentManager.cs | 3 ++- .../AbstractRenameCommandHandler_UndoRedoHandler.cs | 1 - .../Core/InlineRename/InlineRenameSessionOptionsStorage.cs | 1 - src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb | 6 ++++-- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs index 6b9246cdb1032..7b6c4c64c506d 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs @@ -32,6 +32,7 @@ internal class RenameFlyoutViewModel : INotifyPropertyChanged, IDisposable { private readonly bool _registerOleComponent; private readonly IGlobalOptionService _globalOptionService; + private readonly IAsynchronousOperationListener _listener; private OleComponent? _oleComponent; private bool _disposedValue; private bool _isReplacementTextValid = true; @@ -57,6 +58,7 @@ public RenameFlyoutViewModel( Session.CommitStateChange += CommitStateChange; StartingSelection = selectionSpan; InitialTrackingSpan = session.TriggerSpan.CreateTrackingSpan(SpanTrackingMode.EdgeInclusive); + _listener = listenerProvider.GetListener(FeatureAttribute.Rename); var smartRenameSession = smartRenameSessionFactory?.Value.CreateSmartRenameSession(Session.TriggerSpan); if (smartRenameSession is not null) { @@ -229,7 +231,9 @@ public bool Submit() } SmartRenameViewModel?.Commit(IdentifierText); - _ = Session.CommitAsync(previewChanges: false).ReportNonFatalErrorAsync(); + + var token = _listener.BeginAsyncOperation(nameof(Submit)); + _ = Session.CommitAsync(previewChanges: false).ReportNonFatalErrorAsync().CompletesAsyncOperation(token); return true; } diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index c3b034ddf9128..f6b944028ea42 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename.HighlightTags; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; @@ -27,6 +28,7 @@ internal partial class RenameDashboard : InlineRenameAdornment private readonly RenameDashboardViewModel _model; private readonly IWpfTextView _textView; private readonly IAdornmentLayer _findAdornmentLayer; + private readonly IAsynchronousOperationListener _listener; private PresentationSource _presentationSource; private DependencyObject _rootDependencyObject; private IInputElement _rootInputElement; @@ -48,7 +50,8 @@ internal partial class RenameDashboard : InlineRenameAdornment public RenameDashboard( RenameDashboardViewModel model, IEditorFormatMapService editorFormatMapService, - IWpfTextView textView) + IWpfTextView textView, + IAsynchronousOperationListenerProvider listenerProvider) { _model = model; InitializeComponent(); @@ -56,6 +59,7 @@ public RenameDashboard( _tabNavigableChildren = [this.OverloadsCheckbox, this.CommentsCheckbox, this.StringsCheckbox, this.FileRenameCheckbox, this.PreviewChangesCheckbox, this.ApplyButton, this.CloseButton]; _textView = textView; + _listener = listenerProvider.GetListener(FeatureAttribute.Rename); this.DataContext = model; _textView.GotAggregateFocus += OnTextViewGotAggregateFocus; @@ -325,6 +329,7 @@ private async Task CommitAsync() { try { + using var token = _listener.BeginAsyncOperation(nameof(CommitAsync)); //.ConfigureAwait(true) to make sure Exceptions could be shown in UI. await _model.Session.CommitAsync(previewChanges: false).ConfigureAwait(true); _textView.VisualElement.Focus(); diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs index c0b3678fa7ec9..fbbf54f413cad 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs @@ -167,7 +167,8 @@ private void UpdateAdornments() var newAdornment = new RenameDashboard( (RenameDashboardViewModel)s_createdViewModels.GetValue(_renameService.ActiveSession, session => new RenameDashboardViewModel(session, _threadingContext, _textView)), _editorFormatMapService, - _textView); + _textView, + _listenerProvider); return newAdornment; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs index 65dd67563981a..04c9d2d2aaa32 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_UndoRedoHandler.cs @@ -44,7 +44,6 @@ public bool ExecuteCommand(RedoCommandArgs args, CommandExecutionContext context return false; } - if (renameService.ActiveSession.IsCommitInProgress) { // When rename commit is in progress, handle the command so it won't change the workspace diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs index 7e9f406c018a3..bb7197eaec312 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSessionOptionsStorage.cs @@ -2,7 +2,6 @@ // 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; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.InlineRename; diff --git a/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb b/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb index 16b38ba740a4e..6f1ecfe8ed22f 100644 --- a/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb +++ b/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb @@ -561,6 +561,8 @@ class D : B Dim configService = workspace.ExportProvider.GetExportedValue(Of TestWorkspaceConfigurationService) configService.Options = New WorkspaceConfigurationOptions(SourceGeneratorExecution:=executionPreference) + Dim listenerProvider = workspace.ExportProvider.GetExport(Of IAsynchronousOperationListenerProvider)().Value + Dim cursorDocument = workspace.Documents.Single(Function(d) d.CursorPosition.HasValue) Dim cursorPosition = cursorDocument.CursorPosition.Value @@ -591,7 +593,8 @@ class D : B Using dashboard = New RenameDashboard( New RenameDashboardViewModel(DirectCast(sessionInfo.Session, InlineRenameSession), threadingContext, textView), editorFormatMapService:=Nothing, - textView:=cursorDocument.GetTextView()) + textView:=cursorDocument.GetTextView(), + listenerProvider) Await WaitForRename(workspace) @@ -625,7 +628,6 @@ class D : B End Using Dim TestQuickInfoBroker = New TestQuickInfoBroker() - Dim listenerProvider = workspace.ExportProvider.GetExport(Of IAsynchronousOperationListenerProvider)().Value Dim editorFormatMapService = workspace.ExportProvider.GetExport(Of IEditorFormatMapService)().Value Using flyout = New RenameFlyout( From c731d9c79fb49890010b10e8f465eaa58fa30936 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 14:12:03 -0700 Subject: [PATCH 13/27] Add async rename tests --- .../CSharp/CSharpRename.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs index 537211b1d1250..99755d80fa74d 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs @@ -805,4 +805,51 @@ void Method() // Make sure the file is renamed. If the file is not found, this call would throw exception await TestServices.SolutionExplorer.GetProjectItemAsync(projectName, "MyTestClass.cs", HangMitigatingCancellationToken); } + + [CombinatorialData] + [IdeTheory] + public async Task VerifyAsyncRename(bool useInlineRename) + { + var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); + globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.CommitRenameAsynchronously, true); + + if (!useInlineRename) + globalOptions.SetGlobalOption(InlineRenameUIOptionsStorage.UseInlineAdornment, false); + + var markup = """ + class Program + { + static void Main(string[] args) + { + int x = 100; + Te$$stMethod(x); + } + + static void TestMethod(int y) + { + + } + } + """; + await SetUpEditorAsync(markup, HangMitigatingCancellationToken); + await TestServices.InlineRename.InvokeAsync(HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(["AsyncRenameMethod", VirtualKeyCode.RETURN], HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForRenameAsync(HangMitigatingCancellationToken); + await TestServices.EditorVerifier.TextEqualsAsync( + """ + class Program + { + static void Main(string[] args) + { + int x = 100; + AsyncRenameMethod$$(x); + } + + static void AsyncRenameMethod(int y) + { + + } + } + """, HangMitigatingCancellationToken); + } } From 7b30ea8c61bb4a27fa6a249742fc018d203fbd88 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 15:23:51 -0700 Subject: [PATCH 14/27] More async token --- .../CommandHandlers/RenameCommandHandler.cs | 6 ++++-- .../InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 4 ++++ .../AbstractRenameCommandHandler_ReturnHandler.cs | 11 ++++++++--- .../New.IntegrationTests/CSharp/CSharpRename.cs | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs index 0b74fdaf53a24..aa55626081976 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Shared.TestHooks; +using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename { @@ -90,11 +91,12 @@ protected override void SetAdornmentFocusToPreviousElement(ITextView textView) } } - protected override void CommitAndSetFocus(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) + protected override async Task CommitAndSetFocusAsync(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) { try { - base.CommitAndSetFocus(activeSession, textView, operationContext); + // ConfigureAwait(true) to show notification when error occurs. + await base.CommitAndSetFocusAsync(activeSession, textView, operationContext).ConfigureAwait(true); } catch (NotSupportedException ex) { diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index f6b944028ea42..13023e0876fd9 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -248,6 +248,7 @@ protected override void OnAccessKey(AccessKeyEventArgs e) } else if (string.Equals(e.Key, RenameShortcutKey.Apply, StringComparison.OrdinalIgnoreCase)) { + // CommitAsync catch & report all the exceptions. _ = this.CommitAsync(); } } @@ -325,6 +326,9 @@ private void CloseButton_Click(object sender, RoutedEventArgs e) private void Apply_Click(object sender, RoutedEventArgs e) => _ = CommitAsync(); + /// + /// CommitAsync is non-throwing. + /// private async Task CommitAsync() { try diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs index a156c9615e781..60fc448a07d31 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs @@ -2,6 +2,8 @@ // 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; @@ -19,16 +21,19 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co { if (renameService.ActiveSession != null) { - CommitAndSetFocus(renameService.ActiveSession, args.TextView, context.OperationContext); + var token = listener.BeginAsyncOperation(nameof(CommitAndSetFocusAsync)); + _ = CommitAndSetFocusAsync(renameService.ActiveSession, args.TextView, context.OperationContext) + .ReportNonFatalErrorAsync().CompletesAsyncOperation(token); return true; } return false; } - protected virtual void CommitAndSetFocus(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) + protected virtual async Task CommitAndSetFocusAsync(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) { - _ = activeSession.CommitAsync(previewChanges: false, operationContext).ReportNonFatalErrorAsync(); + // ConfigureAwait(true) because UI thread is needed to change the focus of text view. + await activeSession.CommitAsync(previewChanges: false, operationContext).ConfigureAwait(true); SetFocusToTextView(textView); } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs index 99755d80fa74d..c14dd8e64b5d3 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs @@ -748,7 +748,7 @@ public class Class2 """, HangMitigatingCancellationToken); } - [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/73630"), WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1903953/")] + [IdeFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1903953/")] public async Task VerifyRenameLinkedDocumentsAsync() { var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); From b12a1e3d81f5a8494990069987dd8dd1dad340b7 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 15:25:56 -0700 Subject: [PATCH 15/27] Reset a not needed change --- .../IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs index c14dd8e64b5d3..99755d80fa74d 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs @@ -748,7 +748,7 @@ public class Class2 """, HangMitigatingCancellationToken); } - [IdeFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1903953/")] + [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/73630"), WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1903953/")] public async Task VerifyRenameLinkedDocumentsAsync() { var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); From 67e1a6d98e88de912079d38c6dde7d9f4cb0d3b4 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 15:29:42 -0700 Subject: [PATCH 16/27] Move the token to caller --- .../InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index 13023e0876fd9..b758092e23a5a 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -248,8 +248,9 @@ protected override void OnAccessKey(AccessKeyEventArgs e) } else if (string.Equals(e.Key, RenameShortcutKey.Apply, StringComparison.OrdinalIgnoreCase)) { + var token = _listener.BeginAsyncOperation($"{nameof(OnAccessKey)}.{nameof(RenameShortcutKey.Apply)}"); // CommitAsync catch & report all the exceptions. - _ = this.CommitAsync(); + _ = this.CommitAsync().CompletesAsyncOperation(token); } } } @@ -324,7 +325,11 @@ private void CloseButton_Click(object sender, RoutedEventArgs e) } private void Apply_Click(object sender, RoutedEventArgs e) - => _ = CommitAsync(); + { + var token = _listener.BeginAsyncOperation(nameof(Apply_Click)); + // CommitAsync catch & report all the exceptions. + _ = CommitAsync().CompletesAsyncOperation(token); + } /// /// CommitAsync is non-throwing. @@ -333,7 +338,6 @@ private async Task CommitAsync() { try { - using var token = _listener.BeginAsyncOperation(nameof(CommitAsync)); //.ConfigureAwait(true) to make sure Exceptions could be shown in UI. await _model.Session.CommitAsync(previewChanges: false).ConfigureAwait(true); _textView.VisualElement.Focus(); From 362f502f52dec6800f6c90893e95b873f9a8ea4c Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 3 Oct 2024 15:32:41 -0700 Subject: [PATCH 17/27] Modify comment --- .../Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index b758092e23a5a..00a6406fa7393 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -338,7 +338,7 @@ private async Task CommitAsync() { try { - //.ConfigureAwait(true) to make sure Exceptions could be shown in UI. + //.ConfigureAwait(true) because UI thread is need to change focus await _model.Session.CommitAsync(previewChanges: false).ConfigureAwait(true); _textView.VisualElement.Focus(); } From f37bc826ed00226c5d6c0e4b4fc18a44658cdd98 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 4 Oct 2024 11:49:01 -0700 Subject: [PATCH 18/27] Delete the option entry in VS storage --- src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 532bc9517065c..e61110eb9eb6c 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -347,7 +347,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_commit_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.CommitRenameAsynchronously")}, {"dotnet_commit_rename_asynchronously_feature_flag", new FeatureFlagStorage("Roslyn.CommitRenameAsynchronously")}, {"dotnet_rename_get_suggestions_automatically", new FeatureFlagStorage(@"Editor.AutoSmartRenameSuggestions")}, - {"dotnet_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.RenameAsynchronously")}, {"dotnet_rename_file", new RoamingProfileStorage("TextEditor.Specific.RenameFile")}, {"dotnet_rename_in_comments", new RoamingProfileStorage("TextEditor.Specific.RenameInComments")}, {"dotnet_rename_in_strings", new RoamingProfileStorage("TextEditor.Specific.RenameInStrings")}, From f21f8432af00b6d94a4d185ad6aa08fe7c71708b Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 4 Oct 2024 11:54:54 -0700 Subject: [PATCH 19/27] Comment the storage --- src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index e61110eb9eb6c..2523cbaedf7af 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -347,6 +347,8 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_commit_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.CommitRenameAsynchronously")}, {"dotnet_commit_rename_asynchronously_feature_flag", new FeatureFlagStorage("Roslyn.CommitRenameAsynchronously")}, {"dotnet_rename_get_suggestions_automatically", new FeatureFlagStorage(@"Editor.AutoSmartRenameSuggestions")}, + // Deprecated option, don't use the same RoamingProfileStorage key + // {"dotnet_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.RenameAsynchronously")}, {"dotnet_rename_file", new RoamingProfileStorage("TextEditor.Specific.RenameFile")}, {"dotnet_rename_in_comments", new RoamingProfileStorage("TextEditor.Specific.RenameInComments")}, {"dotnet_rename_in_strings", new RoamingProfileStorage("TextEditor.Specific.RenameInStrings")}, From c716abdd1e5bbafee4c03d22dbdfa06192496935 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Fri, 4 Oct 2024 11:59:47 -0700 Subject: [PATCH 20/27] Modify the comment --- src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 2523cbaedf7af..5a2e148c15abc 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -347,7 +347,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_commit_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.CommitRenameAsynchronously")}, {"dotnet_commit_rename_asynchronously_feature_flag", new FeatureFlagStorage("Roslyn.CommitRenameAsynchronously")}, {"dotnet_rename_get_suggestions_automatically", new FeatureFlagStorage(@"Editor.AutoSmartRenameSuggestions")}, - // Deprecated option, don't use the same RoamingProfileStorage key + // Option is deprecated, don't use the same RoamingProfileStorage key // {"dotnet_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.RenameAsynchronously")}, {"dotnet_rename_file", new RoamingProfileStorage("TextEditor.Specific.RenameFile")}, {"dotnet_rename_in_comments", new RoamingProfileStorage("TextEditor.Specific.RenameInComments")}, From f0a2321763c7aaeb8231a22004db6e52e74c2e82 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Tue, 15 Oct 2024 15:41:57 -0700 Subject: [PATCH 21/27] Remove one else clause and change comment --- .../AbstractRenameCommandHandler_RenameHandler.cs | 3 ++- .../AbstractRenameCommandHandler_WordDeleteHandler.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs index d50cde7541347..e2b09c4c412a3 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs @@ -71,7 +71,8 @@ private async Task ExecuteCommandAsync(RenameCommandArgs args, IUIThreadOperatio { return; } - else if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) + + if (renameService.ActiveSession.TryGetContainingEditableSpan(caretPoint.Value, out _)) { // Is the caret within any of the rename fields in this buffer? // If so, focus the dashboard diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs index 91965d7d68f27..c11b6dddfe73d 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_WordDeleteHandler.cs @@ -36,7 +36,7 @@ private bool HandleWordDeleteCommand(ITextBuffer subjectBuffer, ITextView view, if (renameService.ActiveSession.IsCommitInProgress) { - // When rename commit is in progress, handle the command so it won't change the workspace + // When rename commit is in progress, swallow the command so it won't change the workspace return true; } From 05887bde2103316acd1acc8398b5be5f663cffcb Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 16 Oct 2024 15:20:32 -0700 Subject: [PATCH 22/27] Aggregate exception and async token handling --- .../CommandHandlers/RenameCommandHandler.cs | 5 ++--- .../UI/Adornment/RenameFlyoutViewModel.cs | 4 +--- .../UI/Dashboard/RenameDashboard.xaml.cs | 21 +++++-------------- .../UI/InlineRenameAdornmentManager.cs | 3 +-- ...tractRenameCommandHandler_ReturnHandler.cs | 12 +++-------- .../Core/InlineRename/InlineRenameSession.cs | 7 +++++++ .../Test2/Rename/RenameViewModelTests.vb | 3 +-- 7 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs index aa55626081976..b7f202ac39415 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs @@ -91,12 +91,11 @@ protected override void SetAdornmentFocusToPreviousElement(ITextView textView) } } - protected override async Task CommitAndSetFocusAsync(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) + protected override void CommitAndSetFocus(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) { try { - // ConfigureAwait(true) to show notification when error occurs. - await base.CommitAndSetFocusAsync(activeSession, textView, operationContext).ConfigureAwait(true); + base.CommitAndSetFocus(activeSession, textView, operationContext); } catch (NotSupportedException ex) { diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs index 7b6c4c64c506d..bb416065c20d2 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs @@ -231,9 +231,7 @@ public bool Submit() } SmartRenameViewModel?.Commit(IdentifierText); - - var token = _listener.BeginAsyncOperation(nameof(Submit)); - _ = Session.CommitAsync(previewChanges: false).ReportNonFatalErrorAsync().CompletesAsyncOperation(token); + Session.InitiateCommit(); return true; } diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index 00a6406fa7393..7f32db332b712 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; @@ -28,7 +27,6 @@ internal partial class RenameDashboard : InlineRenameAdornment private readonly RenameDashboardViewModel _model; private readonly IWpfTextView _textView; private readonly IAdornmentLayer _findAdornmentLayer; - private readonly IAsynchronousOperationListener _listener; private PresentationSource _presentationSource; private DependencyObject _rootDependencyObject; private IInputElement _rootInputElement; @@ -50,8 +48,7 @@ internal partial class RenameDashboard : InlineRenameAdornment public RenameDashboard( RenameDashboardViewModel model, IEditorFormatMapService editorFormatMapService, - IWpfTextView textView, - IAsynchronousOperationListenerProvider listenerProvider) + IWpfTextView textView) { _model = model; InitializeComponent(); @@ -59,7 +56,6 @@ public RenameDashboard( _tabNavigableChildren = [this.OverloadsCheckbox, this.CommentsCheckbox, this.StringsCheckbox, this.FileRenameCheckbox, this.PreviewChangesCheckbox, this.ApplyButton, this.CloseButton]; _textView = textView; - _listener = listenerProvider.GetListener(FeatureAttribute.Rename); this.DataContext = model; _textView.GotAggregateFocus += OnTextViewGotAggregateFocus; @@ -248,9 +244,7 @@ protected override void OnAccessKey(AccessKeyEventArgs e) } else if (string.Equals(e.Key, RenameShortcutKey.Apply, StringComparison.OrdinalIgnoreCase)) { - var token = _listener.BeginAsyncOperation($"{nameof(OnAccessKey)}.{nameof(RenameShortcutKey.Apply)}"); - // CommitAsync catch & report all the exceptions. - _ = this.CommitAsync().CompletesAsyncOperation(token); + this.Commit(); } } } @@ -325,21 +319,16 @@ private void CloseButton_Click(object sender, RoutedEventArgs e) } private void Apply_Click(object sender, RoutedEventArgs e) - { - var token = _listener.BeginAsyncOperation(nameof(Apply_Click)); - // CommitAsync catch & report all the exceptions. - _ = CommitAsync().CompletesAsyncOperation(token); - } + => Commit(); /// /// CommitAsync is non-throwing. /// - private async Task CommitAsync() + private void Commit() { try { - //.ConfigureAwait(true) because UI thread is need to change focus - await _model.Session.CommitAsync(previewChanges: false).ConfigureAwait(true); + _model.Session.InitiateCommit(); _textView.VisualElement.Focus(); } catch (NotSupportedException ex) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs index fbbf54f413cad..c0b3678fa7ec9 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/InlineRenameAdornmentManager.cs @@ -167,8 +167,7 @@ private void UpdateAdornments() var newAdornment = new RenameDashboard( (RenameDashboardViewModel)s_createdViewModels.GetValue(_renameService.ActiveSession, session => new RenameDashboardViewModel(session, _threadingContext, _textView)), _editorFormatMapService, - _textView, - _listenerProvider); + _textView); return newAdornment; } diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs index 60fc448a07d31..a38faccf534d4 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs @@ -2,13 +2,10 @@ // 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.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; @@ -21,19 +18,16 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co { if (renameService.ActiveSession != null) { - var token = listener.BeginAsyncOperation(nameof(CommitAndSetFocusAsync)); - _ = CommitAndSetFocusAsync(renameService.ActiveSession, args.TextView, context.OperationContext) - .ReportNonFatalErrorAsync().CompletesAsyncOperation(token); + CommitAndSetFocus(renameService.ActiveSession, args.TextView, context.OperationContext); return true; } return false; } - protected virtual async Task CommitAndSetFocusAsync(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) + protected virtual void CommitAndSetFocus(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) { - // ConfigureAwait(true) because UI thread is needed to change the focus of text view. - await activeSession.CommitAsync(previewChanges: false, operationContext).ConfigureAwait(true); + activeSession.InitiateCommit(); SetFocusToTextView(textView); } } diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs index 0548ab0375397..e7f61e644de50 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs @@ -744,6 +744,13 @@ private bool CommitSynchronously(bool previewChanges, IUIThreadOperationContext return _threadingContext.JoinableTaskFactory.Run(() => CommitWorkerAsync(previewChanges, canUseBackgroundWorkIndicator: false, operationContext)); } + public void InitiateCommit(IUIThreadOperationContext editorOperationContext = null) + { + var token = _asyncListener.BeginAsyncOperation(nameof(InitiateCommit)); + _ = CommitAsync(previewChanges: false, editorOperationContext) + .ReportNonFatalErrorAsync().CompletesAsyncOperation(token); + } + /// /// Caller should pass in the IUIThreadOperationContext if it is called from editor so rename commit operation could set up the its own context correctly. /// diff --git a/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb b/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb index 6f1ecfe8ed22f..e38a28bf024f3 100644 --- a/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb +++ b/src/EditorFeatures/Test2/Rename/RenameViewModelTests.vb @@ -593,8 +593,7 @@ class D : B Using dashboard = New RenameDashboard( New RenameDashboardViewModel(DirectCast(sessionInfo.Session, InlineRenameSession), threadingContext, textView), editorFormatMapService:=Nothing, - textView:=cursorDocument.GetTextView(), - listenerProvider) + textView:=cursorDocument.GetTextView()) Await WaitForRename(workspace) From 2eeead645e3877845a623f64286d4f2ae55e29ea Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 16 Oct 2024 15:25:28 -0700 Subject: [PATCH 23/27] Clean up unused listners --- .../InlineRename/CommandHandlers/RenameCommandHandler.cs | 1 - .../Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs index b7f202ac39415..0b74fdaf53a24 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/CommandHandlers/RenameCommandHandler.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Shared.TestHooks; -using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename { diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs index bb416065c20d2..fd3fa12848099 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs @@ -32,7 +32,6 @@ internal class RenameFlyoutViewModel : INotifyPropertyChanged, IDisposable { private readonly bool _registerOleComponent; private readonly IGlobalOptionService _globalOptionService; - private readonly IAsynchronousOperationListener _listener; private OleComponent? _oleComponent; private bool _disposedValue; private bool _isReplacementTextValid = true; @@ -58,7 +57,6 @@ public RenameFlyoutViewModel( Session.CommitStateChange += CommitStateChange; StartingSelection = selectionSpan; InitialTrackingSpan = session.TriggerSpan.CreateTrackingSpan(SpanTrackingMode.EdgeInclusive); - _listener = listenerProvider.GetListener(FeatureAttribute.Rename); var smartRenameSession = smartRenameSessionFactory?.Value.CreateSmartRenameSession(Session.TriggerSpan); if (smartRenameSession is not null) { From cb101d62663e18d9c43c1720fdc60838ab1b374c Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 16 Oct 2024 15:39:39 -0700 Subject: [PATCH 24/27] Add comments --- .../InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 3 --- src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index 7f32db332b712..f677088a9a477 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -321,9 +321,6 @@ private void CloseButton_Click(object sender, RoutedEventArgs e) private void Apply_Click(object sender, RoutedEventArgs e) => Commit(); - /// - /// CommitAsync is non-throwing. - /// private void Commit() { try diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs index e7f61e644de50..4ea243f8f2c37 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.cs @@ -744,6 +744,12 @@ private bool CommitSynchronously(bool previewChanges, IUIThreadOperationContext return _threadingContext.JoinableTaskFactory.Run(() => CommitWorkerAsync(previewChanges, canUseBackgroundWorkIndicator: false, operationContext)); } + /// + /// Start to commit the rename session. + /// Session might be committed sync or async, depends on the value of InlineRenameUIOptionsStorage.CommitRenameAsynchronously. + /// If it is committed async, method will only kick off the task. + /// + /// public void InitiateCommit(IUIThreadOperationContext editorOperationContext = null) { var token = _asyncListener.BeginAsyncOperation(nameof(InitiateCommit)); From 6a6894f3d5b6b52aa65f61073c6fd89785b68c5a Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 16 Oct 2024 15:43:54 -0700 Subject: [PATCH 25/27] Clean usings --- .../Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs index f677088a9a477..65752cc5d5b70 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/RenameDashboard.xaml.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename.HighlightTags; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; From 7aa8db2caf76293239648ff1a9317464325890bf Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 17 Oct 2024 14:38:05 -0700 Subject: [PATCH 26/27] Reset to default value in integration test --- .../IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs index 99755d80fa74d..a21f24aa2f944 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs @@ -43,6 +43,7 @@ public override async Task InitializeAsync() globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameOverloads, false); globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.RenameFile, true); globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.PreviewChanges, false); + globalOptions.SetGlobalOption(InlineRenameSessionOptionsStorage.CommitRenameAsynchronously, false); } [IdeFact] From 5a8ed41ca2784c647f5cc554ae9b1c6fec77754b Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 17 Oct 2024 16:18:30 -0700 Subject: [PATCH 27/27] Pass the missing context --- .../AbstractRenameCommandHandler_ReturnHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs index a38faccf534d4..cd51267b3d521 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_ReturnHandler.cs @@ -27,7 +27,7 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co protected virtual void CommitAndSetFocus(InlineRenameSession activeSession, ITextView textView, IUIThreadOperationContext operationContext) { - activeSession.InitiateCommit(); + activeSession.InitiateCommit(operationContext); SetFocusToTextView(textView); } }