public void SetNodes(VsHierarchyNodes nodes, VsHierarchyChanges changes) { CheckOnUIThread(); var description = string.Format("SetNodes(node count={0}, added={1}, deleted={2})", nodes.Count, (changes == null ? -1 : changes.AddedItems.Count), (changes == null ? -1 : changes.DeletedItems.Count)); using (new TimeElapsedLogger(description, InfoLogger.Instance)) { // Simple case: empty hiererchy if (nodes.RootNode.GetChildrenCount() == 0) { if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } CloseVsHierarchy(); return; } // Simple case of unknwon changes or hierarchy is not active. if (changes == null || !_vsHierarchyActive) { // PERF: WE first open the hierarchy with only a single root node, // then we assign the nodes and refresh the root node children. // // This is to workaround a performance issue in Resharper: Resharper // listens to "OnOpenProjects" event and scans the entire hierarchy // (on the UI thread), which can "hang" Visual Studio for seconds. For // example, when opening a Chromium enlistement with about 180,000 // files in the VsHierarchy, Resharper takes up to 20 seconds to scan // all elements. // // One side effect is that Resharper won't know of any of the files in // the hierarchy, so they won't show up in various "Navigate To" // windows. OpenVsHierarchy(); if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } RefreshAll(); return; } Invariants.Assert(_vsHierarchyActive); // PERF: Simple case of one of the collection empty, refresh all is // faster than individual operations if (nodes.IsEmpty || _nodes.IsEmpty) { if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } RefreshAll(); if (_nodes.RootNode.ExpandByDefault) { ExpandNode(_nodes.RootNode); } return; } // Incremental case: Notify of add/remove items. Invariants.Assert(changes != null); // Note: We want to avoid calling "OnItemAdded" in "IVsHierarchyEvents" // if possible, because it implies making the item visible. // "IVsHierarchyEvents" supports a version of "OnItemAdded" with a // "ensureVisible" flag. var events1 = EventSinks.OfType <IVsHierarchyEvents>().ToList(); var events2 = events1.OfType <IVsHierarchyEvents2>().ToList(); var events1Only = events1.Except(events2.OfType <IVsHierarchyEvents>()).ToList(); // Pass 1: Notify deletion of old items as long as we have the old node // collection active. This is safe because the hierarchy host at this // point knows only about current nodes, and does not know anything about // new nodes. In return, we have not updated out "_nodes" member, so any // GetProperty call will return info about current node only. foreach (var deleted in changes.DeletedItems) { var deletedNode = _nodes.GetNode(deleted); Invariants.Assert(deletedNode != null); Invariants.Assert(deletedNode.Parent != null); if (_logger.LogNodeChangesActivity) { _logger.Log("Deleting node {0,7}-\"{1}\"", deletedNode.ItemId, deletedNode.FullPath); } // PERF: avoid allocation for (var i = 0; i < events1.Count; i++) { events1[i].OnItemDeleted(deletedNode.ItemId); } } // Pass 2: Notify of node additions. We first need to switch our "_nodes" // field to the new node collection, so that any query made by the // hierarchy host as a result of add events will be answered with the // right set of nodes (the new ones). if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } foreach (var added in changes.AddedItems) { var addedNode = nodes.GetNode(added); var previousSiblingItemId = addedNode.GetPreviousSiblingItemId(); Invariants.Assert(addedNode != null); Invariants.Assert(addedNode.Parent != null); if (_logger.LogNodeChangesActivity) { _logger.Log("Adding node {0,7}-\"{1}\"", addedNode.ItemId, addedNode.FullPath); _logger.Log(" child of {0,7}-\"{1}\"", addedNode.Parent.ItemId, addedNode.Parent.FullPath); _logger.Log( " next to {0,7}-\"{1}\"", previousSiblingItemId, (previousSiblingItemId != VSConstants.VSITEMID_NIL ? nodes.GetNode(previousSiblingItemId).FullPath : "nil")); } // PERF: avoid allocation for (var i = 0; i < events1Only.Count; i++) { events1Only[i].OnItemAdded( addedNode.Parent.ItemId, previousSiblingItemId, addedNode.ItemId); } // PERF: avoid allocation for (var i = 0; i < events2.Count; i++) { events2[i].OnItemAdded( addedNode.Parent.ItemId, previousSiblingItemId, addedNode.ItemId, false /* ensure visible */); } } } }
public void SetNodes(VsHierarchyNodes nodes, VsHierarchyChanges changes) { CheckOnUIThread(); var description = string.Format("SetNodes(node count={0}, added={1}, deleted={2})", nodes.Count, (changes == null ? -1 : changes.AddedItems.Count), (changes == null ? -1 : changes.DeletedItems.Count)); using (new TimeElapsedLogger(description)) { // Simple case: empty hiererchy if (nodes.RootNode.GetChildrenCount() == 0) { if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } CloseVsHierarchy(); return; } // Simple case of unknwon changes or hierarchy is not active. if (changes == null || !_vsHierarchyActive) { // PERF: WE first open the hierarchy with only a single root node, // then we assign the nodes and refresh the root node children. // // This is to workaround a performance issue in Resharper: Resharper // listens to "OnOpenProjects" event and scans the entire hierarchy // (on the UI thread), which can "hang" Visual Studio for seconds. For // example, when opening a Chromium enlistement with about 180,000 // files in the VsHierarchy, Resharper takes up to 20 seconds to scan // all elements. // // One side effect is that Resharper won't know of any of the files in // the hierarchy, so they won't show up in various "Navigate To" // windows. OpenVsHierarchy(); if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } RefreshAll(); return; } Debug.Assert(_vsHierarchyActive); // PERF: Simple case of one of the collection empty, refresh all is // faster than individual operations if (nodes.IsEmpty || _nodes.IsEmpty) { if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } RefreshAll(); if (_nodes.RootNode.ExpandByDefault) { ExpandNode(_nodes.RootNode); } return; } // Incremental case: Notify of add/remove items. Debug.Assert(changes != null); // Note: We want to avoid calling "OnItemAdded" in "IVsHierarchyEvents" // if possible, because it implies making the item visible. // "IVsHierarchyEvents" supports a version of "OnItemAdded" with a // "ensureVisible" flag. var events1 = EventSinks.OfType<IVsHierarchyEvents>().ToList(); var events2 = events1.OfType<IVsHierarchyEvents2>().ToList(); var events1Only = events1.Except(events2.OfType<IVsHierarchyEvents>()).ToList(); // Pass 1: Notify deletion of old items as long as we have the old node // collection active. This is safe because the hierarchy host at this // point knows only about current nodes, and does not know anything about // new nodes. In return, we have not updated out "_nodes" member, so any // GetProperty call will return info about current node only. foreach (var deleted in changes.DeletedItems) { var deletedNode = _nodes.GetNode(deleted); Debug.Assert(deletedNode != null); Debug.Assert(deletedNode.Parent != null); if (_logger.LogNodeChangesActivity) { _logger.Log("Deleting node {0,7}-\"{1}\"", deletedNode.ItemId, deletedNode.FullPath); } // PERF: avoid allocation for (var i = 0; i < events1.Count; i++) { events1[i].OnItemDeleted(deletedNode.ItemId); } } // Pass 2: Notify of node additions. We first need to switch our "_nodes" // field to the new node collection, so that any query made by the // hierarchy host as a result of add events will be answered with the // right set of nodes (the new ones). if (!ReferenceEquals(nodes, _nodes)) { _nodes = nodes; _nodesVersion++; } foreach (var added in changes.AddedItems) { var addedNode = nodes.GetNode(added); var previousSiblingItemId = addedNode.GetPreviousSiblingItemId(); Debug.Assert(addedNode != null); Debug.Assert(addedNode.Parent != null); if (_logger.LogNodeChangesActivity) { _logger.Log("Adding node {0,7}-\"{1}\"", addedNode.ItemId, addedNode.FullPath); _logger.Log(" child of {0,7}-\"{1}\"", addedNode.Parent.ItemId, addedNode.Parent.FullPath); _logger.Log( " next to {0,7}-\"{1}\"", previousSiblingItemId, (previousSiblingItemId != VSConstants.VSITEMID_NIL ? nodes.GetNode(previousSiblingItemId).FullPath : "nil")); } // PERF: avoid allocation for (var i = 0; i < events1Only.Count; i++) { events1Only[i].OnItemAdded( addedNode.Parent.ItemId, previousSiblingItemId, addedNode.ItemId); } // PERF: avoid allocation for (var i = 0; i < events2.Count; i++) { events2[i].OnItemAdded( addedNode.Parent.ItemId, previousSiblingItemId, addedNode.ItemId, false /* ensure visible */); } } } }