private void EndRenameSession() { _debuggingWorkspaceService.BeforeDebuggingStateChanged -= OnBeforeDebuggingStateChanged; CancelAllOpenDocumentTrackingTasks(); RenameTrackingDismisser.DismissRenameTracking(_workspace, _workspace.GetOpenDocumentIds()); _inlineRenameSessionDurationLogBlock.Dispose(); }
private void EndRenameSession() { CancelAllOpenDocumentTrackingTasks(); RenameTrackingDismisser.DismissRenameTracking(_workspace, _workspace.GetOpenDocumentIds()); _inlineRenameSessionDurationLogBlock.Dispose(); }
public bool ExecuteCommand(EscapeKeyCommandArgs args, CommandExecutionContext context) { var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); return(document != null && RenameTrackingDismisser.DismissVisibleRenameTracking(document.Project.Solution.Workspace, document.Id)); }
private void InitializeOpenBuffers(SnapshotSpan triggerSpan) { using (Logger.LogBlock(FunctionId.Rename_CreateOpenTextBufferManagerForAllOpenDocs, CancellationToken.None)) { var openBuffers = new HashSet <ITextBuffer>(); foreach (var d in _workspace.GetOpenDocumentIds()) { var document = _baseSolution.GetDocument(d); if (document == null) { continue; } Contract.ThrowIfFalse(document.TryGetText(out var text)); Contract.ThrowIfNull(text); var textSnapshot = text.FindCorrespondingEditorTextSnapshot(); if (textSnapshot == null) { FatalError.ReportAndCatch(new NullTextBufferException(document, text)); continue; } Contract.ThrowIfNull(textSnapshot.TextBuffer); openBuffers.Add(textSnapshot.TextBuffer); } foreach (var buffer in openBuffers) { TryPopulateOpenTextBufferManagerForBuffer(buffer); } } var startingSpan = triggerSpan.Span; // Select this span if we didn't already have something selected var selections = _triggerView.Selection.GetSnapshotSpansOnBuffer(triggerSpan.Snapshot.TextBuffer); if (!selections.Any() || selections.First().IsEmpty || !startingSpan.Contains(selections.First())) { _triggerView.SetSelection(new SnapshotSpan(triggerSpan.Snapshot, startingSpan)); } this.UndoManager.CreateInitialState(this.ReplacementText, _triggerView.Selection, new SnapshotSpan(triggerSpan.Snapshot, startingSpan)); _openTextBuffers[triggerSpan.Snapshot.TextBuffer].SetReferenceSpans(SpecializedCollections.SingletonEnumerable(startingSpan.ToTextSpan())); UpdateReferenceLocationsTask(ThreadingContext.JoinableTaskFactory.RunAsync( () => _renameInfo.FindRenameLocationsAsync(_optionSet, _cancellationTokenSource.Token))); RenameTrackingDismisser.DismissRenameTracking(_workspace, _workspace.GetOpenDocumentIds()); }
public void ExecuteCommand(EscapeKeyCommandArgs args, Action nextHandler) { var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null && RenameTrackingDismisser.DismissVisibleRenameTracking(document.Project.Solution.Workspace, document.Id)) { return; } nextHandler(); }
/// <summary> /// Try to do a symbolic rename the specified symbol. /// </summary> /// <returns>False ONLY if it can't resolve the name. Other errors result in the normal /// exception being propagated.</returns> public static bool TryRenameElement( Document document, ContainedLanguageRenameType clrt, string oldFullyQualifiedName, string newFullyQualifiedName, IEnumerable <IRefactorNotifyService> refactorNotifyServices, CancellationToken cancellationToken) { var symbol = FindSymbol(document, clrt, oldFullyQualifiedName, cancellationToken); if (symbol == null) { return(false); } Workspace workspace; if (Workspace.TryGetWorkspace(document.GetTextAsync(cancellationToken).WaitAndGetResult_Venus(cancellationToken).Container, out workspace)) { var newName = newFullyQualifiedName.Substring(newFullyQualifiedName.LastIndexOf('.') + 1); var optionSet = document.Project.Solution.Workspace.Options; var newSolution = Renamer.RenameSymbolAsync(document.Project.Solution, symbol, newName, optionSet, cancellationToken).WaitAndGetResult_Venus(cancellationToken); var changedDocuments = newSolution.GetChangedDocuments(document.Project.Solution); var undoTitle = string.Format(EditorFeaturesResources.Rename_0_to_1, symbol.Name, newName); using (var workspaceUndoTransaction = workspace.OpenGlobalUndoTransaction(undoTitle)) { // Notify third parties about the coming rename operation on the workspace, and let // any exceptions propagate through refactorNotifyServices.TryOnBeforeGlobalSymbolRenamed(workspace, changedDocuments, symbol, newName, throwOnFailure: true); if (!workspace.TryApplyChanges(newSolution)) { Exceptions.ThrowEFail(); } // Notify third parties about the completed rename operation on the workspace, and // let any exceptions propagate through refactorNotifyServices.TryOnAfterGlobalSymbolRenamed(workspace, changedDocuments, symbol, newName, throwOnFailure: true); workspaceUndoTransaction.Commit(); } RenameTrackingDismisser.DismissRenameTracking(workspace, changedDocuments); return(true); } else { return(false); } }
private void CommitCore(IWaitContext waitContext, bool previewChanges) { using (Logger.LogBlock(previewChanges ? FunctionId.Rename_CommitCoreWithPreview : FunctionId.Rename_CommitCore, waitContext.CancellationToken)) { _conflictResolutionTask.Wait(waitContext.CancellationToken); waitContext.AllowCancel = false; Solution newSolution = _conflictResolutionTask.Result.NewSolution; if (previewChanges) { var previewService = _workspace.Services.GetService <IPreviewDialogService>(); newSolution = previewService.PreviewChanges( string.Format(EditorFeaturesResources.PreviewChangesOf, EditorFeaturesResources.Rename), "vs.csharp.refactoring.rename", string.Format(EditorFeaturesResources.RenameToTitle, this.OriginalSymbolName, _renameInfo.GetFinalSymbolName(this.ReplacementText)), _renameInfo.FullDisplayName, _renameInfo.Glyph, _conflictResolutionTask.Result.NewSolution, _triggerDocument.Project.Solution); if (newSolution == null) { // User clicked cancel. return; } } // The user hasn't cancelled by now, so we're done waiting for them. Off to // rename! waitContext.Message = EditorFeaturesResources.UpdatingFiles; DismissAndRollbackTemporaryEdits(); CancelAllOpenDocumentTrackingTasks(); ApplyRename(newSolution, waitContext); LogRenameSession(RenameLogMessage.UserActionOutcome.Committed, previewChanges); RenameTrackingDismisser.DismissRenameTracking(_workspace, _workspace.GetOpenDocumentIds()); _inlineRenameSessionDurationLogBlock.Dispose(); } }
private void ApplyChangesToWorkspace(CancellationToken cancellationToken) { _stateMachine.ThreadingContext.ThrowIfNotOnUIThread(); // Now that the necessary work has been done to create the intermediate and final // solutions during PreparePreview, check one more time for cancellation before making all of the // workspace changes. cancellationToken.ThrowIfCancellationRequested(); // Undo must backtrack to the state with the original identifier before the state // with the user-edited identifier. For example, // // 1. Original: void M() { M(); } // 2. User types: void Method() { M(); } // 3. Invoke rename: void Method() { Method(); } // // The undo process should be as follows // 1. Back to original name everywhere: void M() { M(); } // No tracking session // 2. Back to state 2 above: void Method() { M(); } // Resume tracking session // 3. Finally, start undoing typing: void M() { M(); } // // As far as the user can see, undo state 1 never actually existed so we must insert // a state here to facilitate the undo. Do the work to obtain the intermediate and // final solution without updating the workspace, and then finally disallow // cancellation and update the workspace twice. var renameTrackingSolutionSet = RenameSymbolAsync(cancellationToken).WaitAndGetResult(cancellationToken); var document = _snapshotSpan.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); var newName = _snapshotSpan.GetText(); var workspace = document.Project.Solution.Workspace; // Since the state machine is only watching buffer changes, it will interpret the // text changes caused by undo and redo actions as potential renames, so carefully // update the state machine after undo/redo actions. var changedDocuments = renameTrackingSolutionSet.RenamedSolution.GetChangedDocuments(renameTrackingSolutionSet.OriginalSolution); // When this action is undone (the user has undone twice), restore the state // machine to so that they can continue their original rename tracking session. var trackingSessionId = _stateMachine.StoreCurrentTrackingSessionAndGenerateId(); UpdateWorkspaceForResetOfTypedIdentifier(workspace, renameTrackingSolutionSet.OriginalSolution, trackingSessionId); // Now that the solution is back in its original state, notify third parties about // the coming rename operation. if (!_refactorNotifyServices.TryOnBeforeGlobalSymbolRenamed(workspace, changedDocuments, renameTrackingSolutionSet.Symbol, newName, throwOnFailure: false)) { var notificationService = workspace.Services.GetService <INotificationService>(); notificationService.SendNotification( EditorFeaturesResources.Rename_operation_was_cancelled_or_is_not_valid, EditorFeaturesResources.Rename_Symbol, NotificationSeverity.Error); return; } // move all changes to final solution based on the workspace's current solution, since the current solution // got updated when we reset it above. var finalSolution = workspace.CurrentSolution; foreach (var docId in changedDocuments) { // because changes have already been made to the workspace (UpdateWorkspaceForResetOfTypedIdentifier() above), // these calls can't be cancelled and must be allowed to complete. var root = renameTrackingSolutionSet.RenamedSolution.GetDocument(docId).GetSyntaxRootSynchronously(CancellationToken.None); finalSolution = finalSolution.WithDocumentSyntaxRoot(docId, root); } // Undo/redo on this action must always clear the state machine UpdateWorkspaceForGlobalIdentifierRename( workspace, finalSolution, _displayText, changedDocuments, renameTrackingSolutionSet.Symbol, newName, trackingSessionId); RenameTrackingDismisser.DismissRenameTracking(workspace, changedDocuments); }
private void RenameSymbolAndApplyChanges(CancellationToken cancellationToken, out bool clearTrackingSession) { AssertIsForeground(); // Undo must backtrack to the state with the original identifier before the state // with the user-edited identifier. For example, // // 1. Original: void M() { M(); } // 2. User types: void Method() { M(); } // 3. Invoke rename: void Method() { Method(); } // // The undo process should be as follows // 1. Back to original name everywhere: void M() { M(); } // No tracking session // 2. Back to state 2 above: void Method() { M(); } // Resume tracking session // 3. Finally, start undoing typing: void M() { M(); } // // As far as the user can see, undo state 1 never actually existed so we must insert // a state here to facilitate the undo. Do the work to obtain the intermediate and // final solution without updating the workspace, and then finally disallow // cancellation and update the workspace twice. Document document = _snapshotSpan.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { Contract.Fail("Invoked rename tracking smart tag but cannot find the document for the snapshot span."); } // Get copy of solution with the original name in the place of the renamed name var solutionWithOriginalName = CreateSolutionWithOriginalName(document, cancellationToken); // Get the symbol for the identifier we're renaming (which has now been reverted to // its original name) and invoke the rename service. ISymbol symbol; if (!TryGetSymbol(solutionWithOriginalName, document.Id, cancellationToken, out symbol)) { Contract.Fail("Invoked rename tracking smart tag but cannot find the symbol"); } var newName = _snapshotSpan.GetText(); var optionSet = document.Project.Solution.Workspace.Options; if (_stateMachine.TrackingSession.ForceRenameOverloads) { optionSet = optionSet.WithChangedOption(RenameOptions.RenameOverloads, true); } var renamedSolution = Renamer.RenameSymbolAsync(solutionWithOriginalName, symbol, newName, optionSet, cancellationToken).WaitAndGetResult(cancellationToken); // Now that the necessary work has been done to create the intermediate and final // solutions, check one more time for cancellation before making all of the // workspace changes. cancellationToken.ThrowIfCancellationRequested(); if (_showPreview) { var previewService = renamedSolution.Workspace.Services.GetService <IPreviewDialogService>(); renamedSolution = previewService.PreviewChanges( string.Format(EditorFeaturesResources.PreviewChangesOf, EditorFeaturesResources.Rename), "vs.csharp.refactoring.rename", string.Format( EditorFeaturesResources.RenameToTitle, _stateMachine.TrackingSession.OriginalName, newName), symbol.ToDisplayString(), symbol.GetGlyph(), renamedSolution, solutionWithOriginalName); if (renamedSolution == null) { // User clicked cancel. clearTrackingSession = false; return; } } var workspace = document.Project.Solution.Workspace; // Since the state machine is only watching buffer changes, it will interpret the // text changes caused by undo and redo actions as potential renames, so carefully // update the state machine after undo/redo actions. var changedDocuments = renamedSolution.GetChangedDocuments(solutionWithOriginalName); // When this action is undone (the user has undone twice), restore the state // machine to so that they can continue their original rename tracking session. UpdateWorkspaceForResetOfTypedIdentifier(workspace, solutionWithOriginalName); // Now that the solution is back in its original state, notify third parties about // the coming rename operation. if (!_refactorNotifyServices.TryOnBeforeGlobalSymbolRenamed(workspace, changedDocuments, symbol, newName, throwOnFailure: false)) { var notificationService = workspace.Services.GetService <INotificationService>(); notificationService.SendNotification( EditorFeaturesResources.RenameOperationWasCancelled, EditorFeaturesResources.RenameSymbol, NotificationSeverity.Error); clearTrackingSession = true; return; } // move all changes to final solution based on the workspace's current solution, since the current solution // got updated when we reset it above. var finalSolution = workspace.CurrentSolution; foreach (var docId in changedDocuments) { // because changes have already been made to the workspace (UpdateWorkspaceForResetOfTypedIdentifier() above), // these calls can't be cancelled and must be allowed to complete. var root = renamedSolution.GetDocument(docId).GetSyntaxRootAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None); finalSolution = finalSolution.WithDocumentSyntaxRoot(docId, root); } // Undo/redo on this action must always clear the state machine UpdateWorkspaceForGlobalIdentifierRename(workspace, finalSolution, workspace.CurrentSolution, _displayText, changedDocuments, symbol, newName); RenameTrackingDismisser.DismissRenameTracking(workspace, changedDocuments); clearTrackingSession = true; }