<commit_before>﻿// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using VsChromium.Commands;
using VsChromium.Core.Logging;
using VsChromium.Core.Utility;
using VsChromium.Threads;
using Constants = Microsoft.VisualStudio.OLE.Interop.Constants;

namespace VsChromium.Features.SourceExplorerHierarchy {
  /// <summary>
  /// Implementation of <see cref="IVsUIHierarchy"/> for the virtual project.
  /// </summary>
  public class VsHierarchy : IVsHierarchyImpl, IVsUIHierarchy, IVsProject3, IDisposable {
    private readonly System.IServiceProvider _serviceProvider;
    private readonly IVsGlyphService _vsGlyphService;
    private readonly EventSinkCollection _eventSinks = new EventSinkCollection();
    private readonly VsHierarchyLogger _logger;
    private readonly Dictionary<CommandID, VsHierarchyCommandHandler> _commandHandlers = new Dictionary<CommandID, VsHierarchyCommandHandler>();
    private readonly int _threadId;
    private VsHierarchyNodes _nodes = new VsHierarchyNodes();
    private int _nodesVersion = 0;
    private Microsoft.VisualStudio.OLE.Interop.IServiceProvider _site;
    private uint _selectionEventsCookie;
    private bool _vsHierarchyActive;

    public VsHierarchy(System.IServiceProvider serviceProvider, IVsGlyphService vsGlyphService, IUIThread uiThread) {
      _serviceProvider = serviceProvider;
      _vsGlyphService = vsGlyphService;
      _threadId = uiThread.ManagedThreadId;
      _logger = new VsHierarchyLogger(this);
    }

    public EventSinkCollection EventSinks {
      get {
        return _eventSinks;
      }
    }

    public IntPtr ImageListPtr {
      get { return _vsGlyphService.ImageListPtr; }
    }

    public VsHierarchyNodes Nodes {
      get { return _nodes; }
    }

    public int Version {
      get { return _nodesVersion; }
    }

    public bool IsEmpty {
      get { return Nodes.IsEmpty; }
    }

    public void SelectNodeByFilePath(string path) {
      // TODO(rpaquay): Make this more efficient?
      NodeViewModel node;
      if (Nodes.RootNode.FindNodeByMoniker(path, out node)) {
        SelectNode(node);
      }
    }

    public void AddCommandHandler(VsHierarchyCommandHandler handler) {
      _commandHandlers.Add(handler.CommandId, handler);
    }

    public void Dispose() {
      CheckOnUIThread();
      Close();
    }

    public void Disable() {
      CheckOnUIThread();
      // Reset nodes to avoid holding onto memory if we had an active hierarchy.
      SetNodes(new VsHierarchyNodes(), null);
    }

    public void Disconnect() {
      CheckOnUIThread();
      CloseVsHierarchy();
    }

    public void Reconnect() {
      CheckOnUIThread();
      CloseVsHierarchy();
      SetNodes(_nodes, null);
    }

    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();
          return;
        }<commit_msg>Re-enable "Expand By Default" after previous perf. workaround fix.<commit_after>﻿// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using VsChromium.Commands;
using VsChromium.Core.Logging;
using VsChromium.Core.Utility;
using VsChromium.Threads;
using Constants = Microsoft.VisualStudio.OLE.Interop.Constants;

namespace VsChromium.Features.SourceExplorerHierarchy {
  /// <summary>
  /// Implementation of <see cref="IVsUIHierarchy"/> for the virtual project.
  /// </summary>
  public class VsHierarchy : IVsHierarchyImpl, IVsUIHierarchy, IVsProject3, IDisposable {
    private readonly System.IServiceProvider _serviceProvider;
    private readonly IVsGlyphService _vsGlyphService;
    private readonly EventSinkCollection _eventSinks = new EventSinkCollection();
    private readonly VsHierarchyLogger _logger;
    private readonly Dictionary<CommandID, VsHierarchyCommandHandler> _commandHandlers = new Dictionary<CommandID, VsHierarchyCommandHandler>();
    private readonly int _threadId;
    private VsHierarchyNodes _nodes = new VsHierarchyNodes();
    private int _nodesVersion = 0;
    private Microsoft.VisualStudio.OLE.Interop.IServiceProvider _site;
    private uint _selectionEventsCookie;
    private bool _vsHierarchyActive;

    public VsHierarchy(System.IServiceProvider serviceProvider, IVsGlyphService vsGlyphService, IUIThread uiThread) {
      _serviceProvider = serviceProvider;
      _vsGlyphService = vsGlyphService;
      _threadId = uiThread.ManagedThreadId;
      _logger = new VsHierarchyLogger(this);
    }

    public EventSinkCollection EventSinks {
      get {
        return _eventSinks;
      }
    }

    public IntPtr ImageListPtr {
      get { return _vsGlyphService.ImageListPtr; }
    }

    public VsHierarchyNodes Nodes {
      get { return _nodes; }
    }

    public int Version {
      get { return _nodesVersion; }
    }

    public bool IsEmpty {
      get { return Nodes.IsEmpty; }
    }

    public void SelectNodeByFilePath(string path) {
      // TODO(rpaquay): Make this more efficient?
      NodeViewModel node;
      if (Nodes.RootNode.FindNodeByMoniker(path, out node)) {
        SelectNode(node);
      }
    }

    public void AddCommandHandler(VsHierarchyCommandHandler handler) {
      _commandHandlers.Add(handler.CommandId, handler);
    }

    public void Dispose() {
      CheckOnUIThread();
      Close();
    }

    public void Disable() {
      CheckOnUIThread();
      // Reset nodes to avoid holding onto memory if we had an active hierarchy.
      SetNodes(new VsHierarchyNodes(), null);
    }

    public void Disconnect() {
      CheckOnUIThread();
      CloseVsHierarchy();
    }

    public void Reconnect() {
      CheckOnUIThread();
      CloseVsHierarchy();
      SetNodes(_nodes, null);
    }

    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;
        }
{'fraction_non_alphanumeric': 0.08425079305840642, 'fraction_numerical': 0.003732039559619332, 'mean_word_length': 2.644678680720843, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '373485', 'n_tokens_mistral': 3050, 'n_tokens_neox': 2929, 'n_words': 861}