private void EndRenameSession()
 {
     _debuggingWorkspaceService.BeforeDebuggingStateChanged -= OnBeforeDebuggingStateChanged;
     CancelAllOpenDocumentTrackingTasks();
     RenameTrackingDismisser.DismissRenameTracking(_workspace, _workspace.GetOpenDocumentIds());
     _inlineRenameSessionDurationLogBlock.Dispose();
 }
Example #2
0
        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));
        }
Example #4
0
        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);
            }
        }
Example #7
0
        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;
            }