/// <summary> /// Get FileChange for a previewNode. /// After user clicks a preview node, the preview dialog will show /// content of the file this change will be applied to. And the content /// of that file will contains all the selected changes to that file and /// will show user the version with changes already applied. /// </summary> /// <param name="previewNode"></param> /// <returns> /// FileChange related to this node. If this change is atomic change, /// or the node is file node, it will return the FileChange object for /// the file it is changing. /// If the node is a group node, it will return null. /// </returns> public FileChange GetFileChange(PreviewChangesNode previewNode) { ArgumentValidation.CheckForNullReference(previewNode, "previewNode"); string fileName = null; var changeProposal = previewNode.ChangeProposal; if (changeProposal != null) { // Preview Node for atomic change fileName = changeProposal.FileName; } else { // Look at direct child, if that child is an atomic change, // then get filename for it, otherwise, just leave the filename as null if (previewNode.ChildList != null && previewNode.ChildList.Count > 0) { var proposal = previewNode.ChildList[0].ChangeProposal; if (proposal != null) { fileName = proposal.FileName; } } } FileChange fileChange = null; if (fileName != null) { _fileChanges.TryGetValue(fileName, out fileChange); } return(fileChange); }
/// <summary> /// Add child node to this node. /// </summary> /// <param name="node"></param> public void AddChildNode(PreviewChangesNode node) { if (_childList == null) { _childList = new List <PreviewChangesNode>(); } node._parent = this; _childList.Add(node); ComputeCheckedState(); }
protected static void AddCheckBoxToPreviewNode(PreviewChangesNode previewNode, bool setCheckState, bool check) { ArgumentValidation.CheckForNullReference(previewNode, "previewNode"); previewNode.ShowCheckBox = true; if (setCheckState) { if (check) { previewNode.CheckState = __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked; } else { previewNode.CheckState = __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked; } } }
protected static PreviewChangesNode CreatePreviewNode( string displayText, ushort icon, IntPtr imageList, TextChangeProposal proposal, bool forceSelection, bool enableChangeUncheck, bool isChecked) { var displayData = new VSTREEDISPLAYDATA(); if (forceSelection) { Debug.Assert(!string.IsNullOrEmpty(displayText), "display text is null or empty"); Debug.Assert(proposal != null, "proposal is null"); var length = displayText.Length; displayText = displayText.TrimStart(); var spaceLength = length - displayText.Length; displayData.State = (uint)_VSTREEDISPLAYSTATE.TDS_FORCESELECT; displayData.ForceSelectStart = (ushort)(proposal.StartColumn - spaceLength); displayData.ForceSelectLength = (ushort)(proposal.EndColumn - proposal.StartColumn); } if (imageList != IntPtr.Zero) { displayData.hImageList = imageList; } displayData.Image = displayData.SelectedImage = icon; var node = new PreviewChangesNode(displayText, displayData, displayText, null, proposal); if (enableChangeUncheck) { AddCheckBoxToPreviewNode(node, true, isChecked); } return(node); }
protected static List <PreviewChangesNode> CreatePreviewNodesForVsLang( IList <FileChange> fileChanges, bool placeNodesUnderSingleRoot = false) { var vsLangObjectNodes = new List <PreviewChangesNode>(); var rootToFileToChangesMap = new Dictionary <string, Dictionary <string, List <PreviewChangesNode> > >(); var isCSharpChange = true; foreach (var fileChange in fileChanges) { if (fileChange.ChangeList != null) { if (fileChange.ChangeList.Count > 0) { using (var textBuffer = VsTextLinesFromFile.Load(fileChange.FileName)) { if (textBuffer != null) { foreach ( var vsLangTextChange in fileChange.ChangeList.SelectMany(cl => cl.Value).OfType <VsLangTextChangeProposal>()) { if (vsLangTextChange.IsRootChange) { // Create object definition node var rootNode = new PreviewChangesNode( vsLangTextChange.ObjectDefinitionFullName , new VSTREEDISPLAYDATA() , vsLangTextChange.ObjectDefinitionFullName , new List <PreviewChangesNode>() , vsLangTextChange); rootNode.CheckState = vsLangTextChange.Included ? __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked : __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked; vsLangObjectNodes.Add(rootNode); // It's possible that a non-root change was processed first, in which case the dictionary will already be updated // with the root node name. This means we need to check the dictionary before adding to it. if (!rootToFileToChangesMap.ContainsKey(vsLangTextChange.ObjectDefinitionFullName)) { rootToFileToChangesMap.Add( vsLangTextChange.ObjectDefinitionFullName, new Dictionary <string, List <PreviewChangesNode> >()); } } else { // Get display text, trim the leading space of the text for display purpose. var displayText = string.Empty; int lineLength; if (ErrorHandler.Succeeded(textBuffer.GetLengthOfLine(vsLangTextChange.StartLine, out lineLength))) { if ( ErrorHandler.Succeeded( textBuffer.GetLineText( vsLangTextChange.StartLine, 0, vsLangTextChange.StartLine, lineLength, out displayText))) { var length = displayText.Length; displayText = displayText.TrimStart(); var spaceLength = length - displayText.Length; Debug.Assert( spaceLength <= vsLangTextChange.StartColumn, "Start column of selection is negative, HydratedVsLangRefactor.CreatePreviewNodeForChanges()"); if (spaceLength <= vsLangTextChange.StartColumn) { var changeNodeDisplayData = new VSTREEDISPLAYDATA(); changeNodeDisplayData.State = (uint)_VSTREEDISPLAYSTATE.TDS_FORCESELECT; changeNodeDisplayData.ForceSelectStart = (ushort)(vsLangTextChange.StartColumn - spaceLength); changeNodeDisplayData.ForceSelectLength = (ushort)(vsLangTextChange.Length); var changeNode = new PreviewChangesNode( displayText, changeNodeDisplayData, displayText, null, vsLangTextChange); // Add checked checkbox changeNode.ShowCheckBox = true; changeNode.CheckState = vsLangTextChange.Included ? __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked : __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked; // Apply the language service to this change. Guid languageServiceId; textBuffer.GetLanguageServiceID(out languageServiceId); changeNode.LanguageServiceID = languageServiceId; Dictionary <string, List <PreviewChangesNode> > fileToChangesMap; if (rootToFileToChangesMap.TryGetValue( vsLangTextChange.ObjectDefinitionFullName, out fileToChangesMap)) { List <PreviewChangesNode> changeNodes; if (fileToChangesMap.TryGetValue(fileChange.FileName, out changeNodes)) { changeNodes.Add(changeNode); } else { // There are no changes for the file listed under this root node, so we need to create it changeNodes = new List <PreviewChangesNode> { changeNode }; fileToChangesMap.Add(fileChange.FileName, changeNodes); } } else { // There are no changes processed yet for this object name, so we need to update our dictionary with // markers for creating a new root node, a new file node, and a new change node. fileToChangesMap = new Dictionary <string, List <PreviewChangesNode> >(); fileToChangesMap.Add( fileChange.FileName, new List <PreviewChangesNode> { changeNode }); rootToFileToChangesMap.Add( vsLangTextChange.ObjectDefinitionFullName, fileToChangesMap); } } } } } } } } } } } // Now that all the changes have been sorted under the correct root nodes in our dictionary, create the File nodes that connect // the root nodes to the change nodes. foreach (var rootNode in vsLangObjectNodes) { Dictionary <string, List <PreviewChangesNode> > fileToChangesMap; if (rootToFileToChangesMap.TryGetValue(rootNode.DisplayText, out fileToChangesMap)) { if (fileToChangesMap != null) { foreach (var fileName in fileToChangesMap.Keys) { var fileNodeDisplayData = new VSTREEDISPLAYDATA(); if (FileExtensions.VbExt.Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase)) { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_VBPROJECT; isCSharpChange = false; } else { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_CSHARPFILE; } var shortFileName = Path.GetFileName(fileName); var checkState = DetermineCheckState(fileToChangesMap[fileName]); var fileNode = new PreviewChangesNode(shortFileName, fileNodeDisplayData, shortFileName, null, null); fileNode.AddChildNodes(fileToChangesMap[fileName]); // Add checked checkbox fileNode.ShowCheckBox = true; fileNode.CheckState = checkState; rootNode.AddChildNode(fileNode); // Update root check state if (rootNode.ChildList.Count == 1) { // This is the first child, so the root should match the child check state rootNode.CheckState = checkState; } else { switch (rootNode.CheckState) { case __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked: { if (checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked || checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked) { rootNode.CheckState = __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked; } break; } case __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked: { if (checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked || checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked) { rootNode.CheckState = __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked; } break; } } } } } } } if (placeNodesUnderSingleRoot) { PreviewChangesNode rootNode; var fileNodeDisplayData = new VSTREEDISPLAYDATA(); if (isCSharpChange) { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_CSHARPFILE; rootNode = new PreviewChangesNode(CSharpRootNodeText, fileNodeDisplayData, null, null, null); } else { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_VBPROJECT; rootNode = new PreviewChangesNode(VBRootNodeText, fileNodeDisplayData, null, null, null); } rootNode.ShowCheckBox = true; rootNode.CheckState = DetermineCheckState(vsLangObjectNodes); rootNode.AddChildNodes(vsLangObjectNodes); return(new List <PreviewChangesNode> { rootNode }); } else { return(vsLangObjectNodes); } }
/// <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)); } }
/// <summary> /// Get FileChange for a previewNode. /// After user clicks a preview node, the preview dialog will show /// content of the file this change will be applied to. And the content /// of that file will contains all the selected changes to that file and /// will show user the version with changes already applied. /// </summary> /// <param name="previewNode"></param> /// <returns> /// FileChange related to this node. If this change is atomic change, /// or the node is file node, it will return the FileChange object for /// the file it is changing. /// If the node is a group node, it will return null. /// </returns> public FileChange GetFileChange(PreviewChangesNode previewNode) { ArgumentValidation.CheckForNullReference(previewNode, "previewNode"); string fileName = null; var changeProposal = previewNode.ChangeProposal; if (changeProposal != null) { // Preview Node for atomic change fileName = changeProposal.FileName; } else { // Look at direct child, if that child is an atomic change, // then get filename for it, otherwise, just leave the filename as null if (previewNode.ChildList != null && previewNode.ChildList.Count > 0) { var proposal = previewNode.ChildList[0].ChangeProposal; if (proposal != null) { fileName = proposal.FileName; } } } FileChange fileChange = null; if (fileName != null) { _fileChanges.TryGetValue(fileName, out fileChange); } return fileChange; }
protected static List<PreviewChangesNode> CreatePreviewNodesForVsLang( IList<FileChange> fileChanges, bool placeNodesUnderSingleRoot = false) { var vsLangObjectNodes = new List<PreviewChangesNode>(); var rootToFileToChangesMap = new Dictionary<string, Dictionary<string, List<PreviewChangesNode>>>(); var isCSharpChange = true; foreach (var fileChange in fileChanges) { if (fileChange.ChangeList != null) { if (fileChange.ChangeList.Count > 0) { using (var textBuffer = VsTextLinesFromFile.Load(fileChange.FileName)) { if (textBuffer != null) { foreach ( var vsLangTextChange in fileChange.ChangeList.SelectMany(cl => cl.Value).OfType<VsLangTextChangeProposal>()) { if (vsLangTextChange.IsRootChange) { // Create object definition node var rootNode = new PreviewChangesNode( vsLangTextChange.ObjectDefinitionFullName , new VSTREEDISPLAYDATA() , vsLangTextChange.ObjectDefinitionFullName , new List<PreviewChangesNode>() , vsLangTextChange); rootNode.CheckState = vsLangTextChange.Included ? __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked : __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked; vsLangObjectNodes.Add(rootNode); // It's possible that a non-root change was processed first, in which case the dictionary will already be updated // with the root node name. This means we need to check the dictionary before adding to it. if (!rootToFileToChangesMap.ContainsKey(vsLangTextChange.ObjectDefinitionFullName)) { rootToFileToChangesMap.Add( vsLangTextChange.ObjectDefinitionFullName, new Dictionary<string, List<PreviewChangesNode>>()); } } else { // Get display text, trim the leading space of the text for display purpose. var displayText = string.Empty; int lineLength; if (ErrorHandler.Succeeded(textBuffer.GetLengthOfLine(vsLangTextChange.StartLine, out lineLength))) { if ( ErrorHandler.Succeeded( textBuffer.GetLineText( vsLangTextChange.StartLine, 0, vsLangTextChange.StartLine, lineLength, out displayText))) { var length = displayText.Length; displayText = displayText.TrimStart(); var spaceLength = length - displayText.Length; Debug.Assert( spaceLength <= vsLangTextChange.StartColumn, "Start column of selection is negative, HydratedVsLangRefactor.CreatePreviewNodeForChanges()"); if (spaceLength <= vsLangTextChange.StartColumn) { var changeNodeDisplayData = new VSTREEDISPLAYDATA(); changeNodeDisplayData.State = (uint)_VSTREEDISPLAYSTATE.TDS_FORCESELECT; changeNodeDisplayData.ForceSelectStart = (ushort)(vsLangTextChange.StartColumn - spaceLength); changeNodeDisplayData.ForceSelectLength = (ushort)(vsLangTextChange.Length); var changeNode = new PreviewChangesNode( displayText, changeNodeDisplayData, displayText, null, vsLangTextChange); // Add checked checkbox changeNode.ShowCheckBox = true; changeNode.CheckState = vsLangTextChange.Included ? __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked : __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked; // Apply the language service to this change. Guid languageServiceId; textBuffer.GetLanguageServiceID(out languageServiceId); changeNode.LanguageServiceID = languageServiceId; Dictionary<string, List<PreviewChangesNode>> fileToChangesMap; if (rootToFileToChangesMap.TryGetValue( vsLangTextChange.ObjectDefinitionFullName, out fileToChangesMap)) { List<PreviewChangesNode> changeNodes; if (fileToChangesMap.TryGetValue(fileChange.FileName, out changeNodes)) { changeNodes.Add(changeNode); } else { // There are no changes for the file listed under this root node, so we need to create it changeNodes = new List<PreviewChangesNode> { changeNode }; fileToChangesMap.Add(fileChange.FileName, changeNodes); } } else { // There are no changes processed yet for this object name, so we need to update our dictionary with // markers for creating a new root node, a new file node, and a new change node. fileToChangesMap = new Dictionary<string, List<PreviewChangesNode>>(); fileToChangesMap.Add( fileChange.FileName, new List<PreviewChangesNode> { changeNode }); rootToFileToChangesMap.Add( vsLangTextChange.ObjectDefinitionFullName, fileToChangesMap); } } } } } } } } } } } // Now that all the changes have been sorted under the correct root nodes in our dictionary, create the File nodes that connect // the root nodes to the change nodes. foreach (var rootNode in vsLangObjectNodes) { Dictionary<string, List<PreviewChangesNode>> fileToChangesMap; if (rootToFileToChangesMap.TryGetValue(rootNode.DisplayText, out fileToChangesMap)) { if (fileToChangesMap != null) { foreach (var fileName in fileToChangesMap.Keys) { var fileNodeDisplayData = new VSTREEDISPLAYDATA(); if (FileExtensions.VbExt.Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase)) { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_VBPROJECT; isCSharpChange = false; } else { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_CSHARPFILE; } var shortFileName = Path.GetFileName(fileName); var checkState = DetermineCheckState(fileToChangesMap[fileName]); var fileNode = new PreviewChangesNode(shortFileName, fileNodeDisplayData, shortFileName, null, null); fileNode.AddChildNodes(fileToChangesMap[fileName]); // Add checked checkbox fileNode.ShowCheckBox = true; fileNode.CheckState = checkState; rootNode.AddChildNode(fileNode); // Update root check state if (rootNode.ChildList.Count == 1) { // This is the first child, so the root should match the child check state rootNode.CheckState = checkState; } else { switch (rootNode.CheckState) { case __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked: { if (checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked || checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked) { rootNode.CheckState = __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked; } break; } case __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Unchecked: { if (checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked || checkState == __PREVIEWCHANGESITEMCHECKSTATE.PCCS_Checked) { rootNode.CheckState = __PREVIEWCHANGESITEMCHECKSTATE.PCCS_PartiallyChecked; } break; } } } } } } } if (placeNodesUnderSingleRoot) { PreviewChangesNode rootNode; var fileNodeDisplayData = new VSTREEDISPLAYDATA(); if (isCSharpChange) { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_CSHARPFILE; rootNode = new PreviewChangesNode(CSharpRootNodeText, fileNodeDisplayData, null, null, null); } else { fileNodeDisplayData.Image = fileNodeDisplayData.SelectedImage = CommonConstants.OM_GLYPH_VBPROJECT; rootNode = new PreviewChangesNode(VBRootNodeText, fileNodeDisplayData, null, null, null); } rootNode.ShowCheckBox = true; rootNode.CheckState = DetermineCheckState(vsLangObjectNodes); rootNode.AddChildNodes(vsLangObjectNodes); return new List<PreviewChangesNode> { rootNode }; } else { return vsLangObjectNodes; } }
protected static PreviewChangesNode CreatePreviewNode( string displayText, ushort icon, IntPtr imageList, TextChangeProposal proposal, bool forceSelection, bool enableChangeUncheck, bool isChecked) { var displayData = new VSTREEDISPLAYDATA(); if (forceSelection) { Debug.Assert(!string.IsNullOrEmpty(displayText), "display text is null or empty"); Debug.Assert(proposal != null, "proposal is null"); var length = displayText.Length; displayText = displayText.TrimStart(); var spaceLength = length - displayText.Length; displayData.State = (uint)_VSTREEDISPLAYSTATE.TDS_FORCESELECT; displayData.ForceSelectStart = (ushort)(proposal.StartColumn - spaceLength); displayData.ForceSelectLength = (ushort)(proposal.EndColumn - proposal.StartColumn); } if (imageList != IntPtr.Zero) { displayData.hImageList = imageList; } displayData.Image = displayData.SelectedImage = icon; var node = new PreviewChangesNode(displayText, displayData, displayText, null, proposal); if (enableChangeUncheck) { AddCheckBoxToPreviewNode(node, true, isChecked); } return node; }
/// <summary> /// Add child node to this node. /// </summary> /// <param name="node"></param> public void AddChildNode(PreviewChangesNode node) { if (_childList == null) { _childList = new List<PreviewChangesNode>(); } node._parent = this; _childList.Add(node); ComputeCheckedState(); }