Example #1
0
        internal static void ApplyChangesToOneFile(
            FileChange file, IVsTextLines textLines, bool createMarker, ChangeProposal changeToHighlight)
        {
            ArgumentValidation.CheckForNullReference(file, "file");
            ArgumentValidation.CheckForNullReference(textLines, "textLines");

            // Here we only work on text based ChangeProposal
            // Sort all ChangeProposal in one file by the offset.
            // After apply any change, the offset of next ChangeProposal will change.
            // We need to sort all the changes first, so it will be easy to recalculate
            // the new offset for next change.
            var sortedChanges = SortChanges(file);

            // Changes may cause the total line number of this file to be changed.
            // This variable is used to keep this line changes.
            var totalLineOffset = 0;

            // In one line, changes may cause column number of that line to be changed.
            // This variable is used to keep this column changes.
            var totalColumnOffset = 0;

            var pContent = IntPtr.Zero;

            // Store result TextSpan info
            var resultSpan = new[] { new TextSpan() };

            foreach (var sortedChange in sortedChanges)
            {
                var change = sortedChange.Value;
                // If user choose to apply this change
                if (change.Included)
                {
                    try
                    {
                        // If current change's StartLine is different from Last change's EndLine
                        // reset the columnOffset
                        if (resultSpan[0].iEndLine != change.StartLine + totalLineOffset)
                        {
                            totalColumnOffset = 0;
                        }

                        pContent = Marshal.StringToHGlobalAuto(change.NewValue);
                        // Calculate the current change's offset by applying lineOffset
                        // and columnOffset caused by previous changes
                        var startLine   = change.StartLine + totalLineOffset;
                        var startColumn = change.StartColumn + totalColumnOffset;
                        var endLine     = change.EndLine + totalLineOffset;
                        // Only apply columnOffset if current change's startLine is same as endLine
                        var endColumn = change.EndColumn + (startLine == endLine ? totalColumnOffset : 0);

                        textLines.ReplaceLines(
                            startLine, startColumn, endLine, endColumn,
                            pContent, change.NewValue.Length, resultSpan);

                        // Add the line offset caused by this change to total lineOffset;
                        var currentLineOffset = resultSpan[0].iEndLine - endLine;
                        totalLineOffset += currentLineOffset;
                        // Calculate the ColumnOffset after this change
                        totalColumnOffset = resultSpan[0].iEndIndex - change.EndColumn;
                    }
                    finally
                    {
                        if (pContent != IntPtr.Zero)
                        {
                            Marshal.FreeHGlobal(pContent);
                        }
                    }
                }

                // Create Marker for this change's textspan.
                // If this change is the change to be highlighted, highlight it.
                if (createMarker)
                {
                    var textChangeToHighlight = changeToHighlight as TextChangeProposal;
                    var highlight             = ((textChangeToHighlight != null) &&
                                                 (change.StartLine == textChangeToHighlight.StartLine) &&
                                                 (change.StartColumn == textChangeToHighlight.StartColumn));
                    CreateMarker(textLines, resultSpan[0], highlight);
                }
            }
        }
Example #2
0
        /// <summary>
        ///     Display the refactoring preview for a selected preview changes node.
        /// </summary>
        /// <param name="vsTextView">The text view to show the file contents.</param>
        /// <param name="fileChange">All changes in one file.</param>
        /// <param name="node">The selected PreviewChangesNode.</param>
        public void DisplayPreview(IVsTextView vsTextView, FileChange fileChange, PreviewChangesNode node)
        {
            ArgumentValidation.CheckForNullReference(vsTextView, "vsTextView");
            ArgumentValidation.CheckForNullReference(node, "previewChangeNode");

            if (fileChange != null)
            {
                // Get the temp file for this file extension, and copy all file content to the temp file text buffer
                PreviewTempFile tempFile = null;
                try
                {
                    tempFile = GetPreviewTempFile(Path.GetExtension(fileChange.FileName));
                }
                catch (InvalidOperationException)
                {
                    // Failed to get text buffer, just set the text view to nothing.
                    NativeMethods.ThrowOnFailure(vsTextView.SetBuffer(_bufferForNoChanges));
                    return;
                }

                // Copy the content of source file to that temp file text buffer
                CopyFileToBuffer(fileChange.FileName, tempFile.TextBuffer);

                // Create text markers on all changes on this file
                RefactoringOperationBase.ApplyChangesToOneFile(fileChange, tempFile.TextBuffer, true, node.ChangeProposal);

                // Set language service ID
                // Get Language service ID on this change node
                var languageServiceID = node.LanguageServiceID;
                if (languageServiceID == Guid.Empty &&
                    node.ChildList != null &&
                    node.ChildList.Count > 0)
                {
                    // If can not get the language service ID, check if it has child nodes
                    // if so, get the language service ID for first change in this file node.
                    languageServiceID = node.ChildList[0].LanguageServiceID;
                }
                if (languageServiceID != Guid.Empty)
                {
                    NativeMethods.ThrowOnFailure(tempFile.TextBuffer.SetLanguageServiceID(ref languageServiceID));
                }

                // Set the vsTextView with textBuffer
                NativeMethods.ThrowOnFailure(vsTextView.SetBuffer(tempFile.TextBuffer));

                // Ensure visible of first line and set the caret to position (0,0)
                ScrollInView(vsTextView, 0, 0, 0, 1);

                // If there is ChangeProposal, make sure that change is visible in the text view.
                // If ChangeProposal is null, that might be file node, make the first chagne visible.
                // Here we will only work with Text based change proposal.
                var visibleChange = node.ChangeProposal as TextChangeProposal;
                if (visibleChange == null)
                {
                    // Try to get first change in this file
                    if (node.ChildList != null &&
                        node.ChildList.Count > 0)
                    {
                        visibleChange = node.ChildList[0].ChangeProposal as TextChangeProposal;
                    }
                }

                if (visibleChange != null)
                {
                    // There are some changes, create TextSpan for first change,
                    // and make the cursor position to that TextSpan.
                    ScrollInView(
                        vsTextView, visibleChange.StartLine, visibleChange.StartColumn,
                        visibleChange.EndLine, visibleChange.EndColumn);
                }

                // Save the state, this will be used to refresh the text view when preview request
                // changed, such as check/uncheck
                _lastTextView            = vsTextView;
                _lastDisplayedFileChange = fileChange;
                _lastDisplayedNode       = node;
            }
            else
            {
                // No related file to this node, set nothing for the text view.
                NativeMethods.ThrowOnFailure(vsTextView.SetBuffer(_bufferForNoChanges));
            }
        }
        protected override IList<FileChange> GetFileChanges()
        {
            var fileChangeMap = new Dictionary<string, FileChange>();

            using (WaitCursorHelper.NewWaitCursor())
            {
                var artifact = _contributorInput.ObjectToBeRenamed.Artifact;
                var artifactProjectItem = VsUtils.GetProjectItemForDocument(artifact.Uri.LocalPath, Services.ServiceProvider);

                if (artifactProjectItem != null)
                {
                    // Run the custom tool to ensure the generated code is up-to-date
                    VsUtils.RunCustomTool(artifactProjectItem);
                    var generatedCodeProjectItem = VsUtils.GetGeneratedCodeProjectItem(artifactProjectItem);

                    if (generatedCodeProjectItem != null)
                    {
                        var generatedItemPath = generatedCodeProjectItem.get_FileNames(1);
                        var objectSearchLanguage = FileExtensions.VbExt.Equals(
                            Path.GetExtension(generatedItemPath), StringComparison.OrdinalIgnoreCase)
                                                       ? ObjectSearchLanguage.VB
                                                       : ObjectSearchLanguage.CSharp;

                        var codeElements = generatedCodeProjectItem.FileCodeModel.CodeElements.OfType<CodeElement2>();
                        CodeElementRenameData renameData = null;

                        // Check if we're refactoring an EntityType or Property
                        var entityType = _contributorInput.ObjectToBeRenamed as EntityType;
                        var property = _contributorInput.ObjectToBeRenamed as Property;

                        if (entityType != null)
                        {
                            var newEntitySetName = _contributorInput.NewName;
                            var pluralize = ModelHelper.GetDesignerPropertyValueFromArtifactAsBool(
                                OptionsDesignerInfo.ElementName, OptionsDesignerInfo.AttributeEnablePluralization,
                                OptionsDesignerInfo.EnablePluralizationDefault, artifact);

                            // Pluralize the entity set name if the setting it turned on
                            if (pluralize)
                            {
                                var pluralizationService = DependencyResolver.GetService<IPluralizationService>();
                                newEntitySetName = pluralizationService.Pluralize(_contributorInput.NewName);
                            }

                            renameData = new CodeElementRenameData(
                                _contributorInput.NewName, newEntitySetName, _contributorInput.OldName, entityType.EntitySet.Name.Value);
                        }
                        else if (property != null)
                        {
                            if (property.EntityType != null)
                            {
                                renameData = new CodeElementRenameData(
                                    _contributorInput.NewName, _contributorInput.OldName, property.EntityType.Name.Value);
                            }
                        }

                        if (renameData != null)
                        {
                            var codeElementsToRename = new Dictionary<CodeElement2, Tuple<string, string>>();
                            CodeElementUtilities.FindRootCodeElementsToRename(
                                codeElements, renameData, generatedItemPath, objectSearchLanguage, ref codeElementsToRename);
                            var changeProposals = new List<ChangeProposal>();

                            // We may need to rename more than one object, as type names affect functions and entity set properties. This means we need to loop through and
                            // process each root item change in the generated code designer file.
                            foreach (var codeElementToRename in codeElementsToRename.Keys)
                            {
                                var nameTuple = codeElementsToRename[codeElementToRename];

                                CodeElementUtilities.CreateChangeProposals(
                                    codeElementToRename, nameTuple.Item1, nameTuple.Item2, generatedItemPath,
                                    changeProposals, objectSearchLanguage);
                            }

                            // Now sort the change proposals by filename so that we can return a list of file changes
                            foreach (var changeProposal in changeProposals)
                            {
                                FileChange fileChange;
                                HashSet<ChangeProposal> fileChangeProposals;

                                if (fileChangeMap.TryGetValue(changeProposal.FileName, out fileChange))
                                {
                                    fileChangeProposals = fileChange.ChangeList.Values.First();
                                }
                                else
                                {
                                    fileChange = new FileChange(changeProposal.FileName);
                                    fileChangeProposals = new HashSet<ChangeProposal>();
                                    fileChange.ChangeList.Add(
                                        new KeyValuePair<RefactoringPreviewGroup, HashSet<ChangeProposal>>(
                                            new RefactoringPreviewGroup(Resources.RefactorPreviewGroupName), fileChangeProposals));
                                    fileChangeMap.Add(changeProposal.FileName, fileChange);
                                }

                                if (!fileChangeProposals.Contains(changeProposal))
                                {
                                    fileChangeProposals.Add(changeProposal);
                                }
                            }
                        }
                    }
                }
            }

            return fileChangeMap.Values.ToList();
        }
        /// <summary>
        ///     Display the refactoring preview for a selected preview changes node.
        /// </summary>
        /// <param name="vsTextView">The text view to show the file contents.</param>
        /// <param name="fileChange">All changes in one file.</param>
        /// <param name="node">The selected PreviewChangesNode.</param>
        public void DisplayPreview(IVsTextView vsTextView, FileChange fileChange, PreviewChangesNode node)
        {
            ArgumentValidation.CheckForNullReference(vsTextView, "vsTextView");
            ArgumentValidation.CheckForNullReference(node, "previewChangeNode");

            if (fileChange != null)
            {
                // Get the temp file for this file extension, and copy all file content to the temp file text buffer
                PreviewTempFile tempFile = null;
                try
                {
                    tempFile = GetPreviewTempFile(Path.GetExtension(fileChange.FileName));
                }
                catch (InvalidOperationException)
                {
                    // Failed to get text buffer, just set the text view to nothing.
                    NativeMethods.ThrowOnFailure(vsTextView.SetBuffer(_bufferForNoChanges));
                    return;
                }

                // Copy the content of source file to that temp file text buffer
                CopyFileToBuffer(fileChange.FileName, tempFile.TextBuffer);

                // Create text markers on all changes on this file
                RefactoringOperationBase.ApplyChangesToOneFile(fileChange, tempFile.TextBuffer, true, node.ChangeProposal);

                // Set language service ID
                // Get Language service ID on this change node
                var languageServiceID = node.LanguageServiceID;
                if (languageServiceID == Guid.Empty
                    && node.ChildList != null
                    && node.ChildList.Count > 0)
                {
                    // If can not get the language service ID, check if it has child nodes
                    // if so, get the language service ID for first change in this file node.
                    languageServiceID = node.ChildList[0].LanguageServiceID;
                }
                if (languageServiceID != Guid.Empty)
                {
                    NativeMethods.ThrowOnFailure(tempFile.TextBuffer.SetLanguageServiceID(ref languageServiceID));
                }

                // Set the vsTextView with textBuffer
                NativeMethods.ThrowOnFailure(vsTextView.SetBuffer(tempFile.TextBuffer));

                // Ensure visible of first line and set the caret to position (0,0)
                ScrollInView(vsTextView, 0, 0, 0, 1);

                // If there is ChangeProposal, make sure that change is visible in the text view.
                // If ChangeProposal is null, that might be file node, make the first chagne visible.
                // Here we will only work with Text based change proposal.
                var visibleChange = node.ChangeProposal as TextChangeProposal;
                if (visibleChange == null)
                {
                    // Try to get first change in this file
                    if (node.ChildList != null
                        && node.ChildList.Count > 0)
                    {
                        visibleChange = node.ChildList[0].ChangeProposal as TextChangeProposal;
                    }
                }

                if (visibleChange != null)
                {
                    // There are some changes, create TextSpan for first change, 
                    // and make the cursor position to that TextSpan.
                    ScrollInView(
                        vsTextView, visibleChange.StartLine, visibleChange.StartColumn,
                        visibleChange.EndLine, visibleChange.EndColumn);
                }

                // Save the state, this will be used to refresh the text view when preview request
                // changed, such as check/uncheck
                _lastTextView = vsTextView;
                _lastDisplayedFileChange = fileChange;
                _lastDisplayedNode = node;
            }
            else
            {
                // No related file to this node, set nothing for the text view.
                NativeMethods.ThrowOnFailure(vsTextView.SetBuffer(_bufferForNoChanges));
            }
        }