Exemple #1
0
        /// <summary>
        /// Performs a SaveAs operation of an open document. Called from SaveItem after the running document table has been updated with the new doc data.
        /// </summary>
        /// <param name="docData">A pointer to the document in the rdt</param>
        /// <param name="newFilePath">The new file path to the document</param>
        /// <returns></returns>
        internal override int AfterSaveItemAs(IntPtr docData, string newFilePath)
        {
            Utilities.ArgumentNotNullOrEmpty(nameof(newFilePath), newFilePath);
            newFilePath = newFilePath.Trim();

            //Identify if Path or FileName are the same for old and new file
            var newDirectoryName = CommonUtils.NormalizeDirectoryPath(Path.GetDirectoryName(newFilePath));
            var oldDirectoryName = CommonUtils.NormalizeDirectoryPath(Path.GetDirectoryName(this.GetMkDocument()));
            var isSamePath       = CommonUtils.IsSameDirectory(newDirectoryName, oldDirectoryName);
            var isSameFile       = CommonUtils.IsSamePath(newFilePath, this.Url);

            //Get target container
            HierarchyNode targetContainer = null;
            var           isLink          = false;

            if (isSamePath)
            {
                targetContainer = this.Parent;
            }
            else if (!CommonUtils.IsSubpathOf(this.ProjectMgr.ProjectHome, newDirectoryName))
            {
                targetContainer = this.Parent;
                isLink          = true;
            }
            else if (CommonUtils.IsSameDirectory(this.ProjectMgr.ProjectHome, newDirectoryName))
            {
                //the projectnode is the target container
                targetContainer = this.ProjectMgr;
            }
            else
            {
                //search for the target container among existing child nodes
                targetContainer = this.ProjectMgr.FindNodeByFullPath(newDirectoryName);
                if (targetContainer != null && (targetContainer is FileNode))
                {
                    // We already have a file node with this name in the hierarchy.
                    throw new InvalidOperationException(SR.GetString(SR.FileAlreadyExistsAndCannotBeRenamed, Path.GetFileName(newFilePath)));
                }
            }

            if (targetContainer == null)
            {
                // Add a chain of subdirectories to the project.
                var relativeUri = CommonUtils.GetRelativeDirectoryPath(this.ProjectMgr.ProjectHome, newDirectoryName);
                targetContainer = this.ProjectMgr.CreateFolderNodes(relativeUri);
            }
            Utilities.CheckNotNull(targetContainer, "Could not find a target container");

            //Suspend file changes while we rename the document
            var oldrelPath = this.ItemNode.GetMetadata(ProjectFileConstants.Include);
            var oldName    = CommonUtils.GetAbsoluteFilePath(this.ProjectMgr.ProjectHome, oldrelPath);
            var sfc        = new SuspendFileChanges(this.ProjectMgr.Site, oldName);

            sfc.Suspend();

            try
            {
                // Rename the node.
                DocumentManager.UpdateCaption(this.ProjectMgr.Site, Path.GetFileName(newFilePath), docData);
                // Check if the file name was actually changed.
                // In same cases (e.g. if the item is a file and the user has changed its encoding) this function
                // is called even if there is no real rename.
                if (!isSameFile || (this.Parent.ID != targetContainer.ID))
                {
                    // The path of the file is changed or its parent is changed; in both cases we have
                    // to rename the item.
                    if (isLink != this.IsLinkFile)
                    {
                        if (isLink)
                        {
                            var newPath = CommonUtils.GetRelativeFilePath(
                                this.ProjectMgr.ProjectHome,
                                Path.Combine(Path.GetDirectoryName(this.Url), Path.GetFileName(newFilePath))
                                );

                            this.ItemNode.SetMetadata(ProjectFileConstants.Link, newPath);
                        }
                        else
                        {
                            this.ItemNode.SetMetadata(ProjectFileConstants.Link, null);
                        }
                        SetIsLinkFile(isLink);
                    }

                    RenameFileNode(oldName, newFilePath, targetContainer);
                    this.ProjectMgr.OnInvalidateItems(this.Parent);
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine("Exception : " + e.Message);
                this.RecoverFromRenameFailure(newFilePath, oldrelPath);
                throw;
            }
            finally
            {
                sfc.Resume();
            }

            return(VSConstants.S_OK);
        }
Exemple #2
0
        /// <summary>
        /// Gets called to rename the eventually running document this hierarchyitem points to
        /// </summary>
        /// returns FALSE if the doc can not be renamed
        internal bool RenameDocument(string oldName, string newName)
        {
            var pRDT = this.GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable;

            if (pRDT == null)
            {
                return(false);
            }

            var docData = IntPtr.Zero;

            SuspendFileChanges sfc = null;

            if (File.Exists(oldName))
            {
                sfc = new SuspendFileChanges(this.ProjectMgr.Site, oldName);
                sfc.Suspend();
            }

            try
            {
                // Suspend ms build since during a rename operation no msbuild re-evaluation should be performed until we have finished.
                // Scenario that could fail if we do not suspend.
                // We have a project system relying on MPF that triggers a Compile target build (re-evaluates itself) whenever the project changes. (example: a file is added, property changed.)
                // 1. User renames a file in  the above project sytem relying on MPF
                // 2. Our rename funstionality implemented in this method removes and readds the file and as a post step copies all msbuild entries from the removed file to the added file.
                // 3. The project system mentioned will trigger an msbuild re-evaluate with the new item, because it was listening to OnItemAdded.
                //    The problem is that the item at the "add" time is only partly added to the project, since the msbuild part has not yet been copied over as mentioned in part 2 of the last step of the rename process.
                //    The result is that the project re-evaluates itself wrongly.
                var renameflag = VSRENAMEFILEFLAGS.VSRENAMEFILEFLAGS_NoFlags;
                ErrorHandler.ThrowOnFailure(pRDT.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldName, out var pIVsHierarchy, out var itemId, out docData, out var uiVsDocCookie));

                if (pIVsHierarchy != null && !Utilities.IsSameComObject(pIVsHierarchy, this.ProjectMgr))
                {
                    // Don't rename it if it wasn't opened by us.
                    return(false);
                }

                // ask other potentially running packages
                if (!this.ProjectMgr.Tracker.CanRenameItem(oldName, newName, renameflag))
                {
                    return(false);
                }

                if (IsFileOnDisk(oldName))
                {
                    RenameInStorage(oldName, newName);
                }

                // For some reason when ignoreFileChanges is called in Resume, we get an ArgumentException because
                // Somewhere a required fileWatcher is null.  This issue only occurs when you copy and rename a typescript file,
                // Calling Resume here prevents said fileWatcher from being null. Don't know why it works, but it does.
                // Also fun! This is the only location it can go (between RenameInStorage and RenameFileNode)
                // So presumably there is some condition that is no longer met once both of these methods are called with a ts file.
                // https://nodejstools.codeplex.com/workitem/1510
                if (sfc != null)
                {
                    sfc.Resume();
                    sfc.Suspend();
                }

                if (!CommonUtils.IsSamePath(oldName, newName))
                {
                    // Check out the project file if necessary.
                    if (!this.ProjectMgr.QueryEditProjectFile(false))
                    {
                        throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
                    }

                    this.RenameFileNode(oldName, newName);
                }
                else
                {
                    this.RenameCaseOnlyChange(oldName, newName);
                }

                DocumentManager.UpdateCaption(this.ProjectMgr.Site, this.Caption, docData);

                // changed from MPFProj:
                // http://mpfproj10.codeplex.com/WorkItem/View.aspx?WorkItemId=8231
                this.ProjectMgr.Tracker.OnItemRenamed(oldName, newName, renameflag);
            }
            finally
            {
                if (sfc != null)
                {
                    sfc.Resume();
                }
                if (docData != IntPtr.Zero)
                {
                    Marshal.Release(docData);
                }
            }

            return(true);
        }
Exemple #3
0
        /// <summary>
        /// Renames a file node.
        /// </summary>
        /// <param name="label">The new name.</param>
        /// <returns>An errorcode for failure or S_OK.</returns>
        /// <exception cref="InvalidOperationException" if the file cannot be validated>
        /// <devremark>
        /// We are going to throw instead of showing messageboxes, since this method is called from various places where a dialog box does not make sense.
        /// For example the FileNodeProperties are also calling this method. That should not show directly a messagebox.
        /// Also the automation methods are also calling SetEditLabel
        /// </devremark>

        public override int SetEditLabel(string label)
        {
            // IMPORTANT NOTE: This code will be called when a parent folder is renamed. As such, it is
            //                 expected that we can be called with a label which is the same as the current
            //                 label and this should not be considered a NO-OP.
            if (this.ProjectMgr == null || this.ProjectMgr.IsClosed)
            {
                return(VSConstants.E_FAIL);
            }

            // Validate the filename.
            if (String.IsNullOrEmpty(label))
            {
                throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, label));
            }
            else if (label.Length > NativeMethods.MAX_PATH)
            {
                throw new InvalidOperationException(SR.GetString(SR.PathTooLong, label));
            }
            else if (Utilities.IsFileNameInvalid(label))
            {
                throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, label));
            }

            for (HierarchyNode n = this.Parent.FirstChild; n != null; n = n.NextSibling)
            {
                // TODO: Distinguish between real Urls and fake ones (eg. "References")
                if (n != this && String.Equals(n.Caption, label, StringComparison.OrdinalIgnoreCase))
                {
                    if (File.Exists(n.Url))
                    {
                        //A file or folder with the name '{0}' already exists on disk at this location. Please choose another name.
                        //If this file or folder does not appear in the Solution Explorer, then it is not currently part of your project. To view files which exist on disk, but are not in the project, select Show All Files from the Project menu.
                        throw new InvalidOperationException(SR.GetString(SR.FileOrFolderAlreadyExists, label));
                    }
                    else
                    {
                        // Check if the file is open in the editor, if so, we need to close it, and if it's dirty
                        // let the user save it.
                        DocumentManager manager = n.GetDocumentManager();
                        if (manager != null)
                        {
                            int close = manager.Close(__FRAMECLOSE.FRAMECLOSE_PromptSave);
                            if (close == VSConstants.E_ABORT || close == VSConstants.S_FALSE)
                            {
                                // User cancelled operation in message box.
                                throw new InvalidOperationException(SR.GetString(SR.FileOpenDoesNotExist, label));
                            }
                        }

                        if (File.Exists(n.Url))
                        {
                            // The file was dirty and the user saved it.
                            throw new InvalidOperationException(SR.GetString(SR.FileOrFolderAlreadyExists, label));
                        }

                        // The file is no longer open in the editor and isn't on disk.  We can try removing it now.
                        if (!n.Remove(false))
                        {
                            throw new InvalidOperationException(SR.GetString(SR.UnableToRemoveFile, label));
                        }
                    }
                }
            }

            string fileName = Path.GetFileNameWithoutExtension(label);

            // Verify that the file extension is unchanged
            string strRelPath = Path.GetFileName(this.ItemNode.GetMetadata(ProjectFileConstants.Include));

            if (!Utilities.IsInAutomationFunction(this.ProjectMgr.Site) &&
                !String.Equals(Path.GetExtension(strRelPath), Path.GetExtension(label), StringComparison.OrdinalIgnoreCase))
            {
                // Prompt to confirm that they really want to change the extension of the file
                string     message = SR.GetString(SR.ConfirmExtensionChange, label);
                IVsUIShell shell   = this.ProjectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;

                Utilities.CheckNotNull(shell, "Could not get the UI shell from the project");

                if (!VsShellUtilities.PromptYesNo(message, null, OLEMSGICON.OLEMSGICON_INFO, shell))
                {
                    // The user cancelled the confirmation for changing the extension.
                    // Return S_OK in order not to show any extra dialog box
                    return(VSConstants.S_OK);
                }
            }


            // Build the relative path by looking at folder names above us as one scenarios
            // where we get called is when a folder above us gets renamed (in which case our path is invalid)
            HierarchyNode parent = this.Parent;

            while (parent != null && (parent is FolderNode))
            {
                strRelPath = Path.Combine(parent.Caption, strRelPath);
                parent     = parent.Parent;
            }

            return(SetEditLabel(label, strRelPath));
        }