Beispiel #1
0
        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 */);
                    }
                }
            }
        }
Beispiel #2
0
    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 */);
          }
        }
      }
    }