<commit_before>﻿// Copyright 2014 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;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Controls;
using Microsoft.VisualStudio.Text;
using VsChromium.Core.Configuration;
using VsChromium.Core.Ipc;
using VsChromium.Core.Ipc.TypedMessages;
using VsChromium.Core.Linq;
using VsChromium.Core.Threads;
using VsChromium.Features.SourceExplorerHierarchy;
using VsChromium.Package;
using VsChromium.Settings;
using VsChromium.Threads;
using VsChromium.Views;
using VsChromium.Wpf;
using TreeView = System.Windows.Controls.TreeView;

namespace VsChromium.Features.ToolWindows.CodeSearch {
  public class CodeSearchController : ICodeSearchController {
    private static class OperationsIds {
      public const string SearchCode = "files-contents-search";
      public const string SearchFilePaths = "file-names-search";
    }

    private readonly CodeSearchControl _control;
    private readonly IUIRequestProcessor _uiRequestProcessor;
    private readonly IProgressBarTracker _progressBarTracker;
    private readonly IStandarImageSourceFactory _standarImageSourceFactory;
    private readonly IWindowsExplorer _windowsExplorer;
    private readonly IClipboard _clipboard;
    private readonly ISynchronizationContextProvider _synchronizationContextProvider;
    private readonly IOpenDocumentHelper _openDocumentHelper;
    private readonly IEventBus _eventBus;
    private readonly IGlobalSettingsProvider _globalSettingsProvider;
    private readonly TaskCancellation _taskCancellation;

    /// <summary>
    /// For generating unique id n progress bar tracker.
    /// </summary>
    private int _operationSequenceId;

    public CodeSearchController(
      CodeSearchControl control,
      IUIRequestProcessor uiRequestProcessor,
      IProgressBarTracker progressBarTracker,
      IStandarImageSourceFactory standarImageSourceFactory,
      IWindowsExplorer windowsExplorer,
      IClipboard clipboard,
      ISynchronizationContextProvider synchronizationContextProvider,
      IOpenDocumentHelper openDocumentHelper,
      IEventBus eventBus,
      IGlobalSettingsProvider globalSettingsProvider) {
      _control = control;
      _uiRequestProcessor = uiRequestProcessor;
      _progressBarTracker = progressBarTracker;
      _standarImageSourceFactory = standarImageSourceFactory;
      _windowsExplorer = windowsExplorer;
      _clipboard = clipboard;
      _synchronizationContextProvider = synchronizationContextProvider;
      _openDocumentHelper = openDocumentHelper;
      _eventBus = eventBus;
      _globalSettingsProvider = globalSettingsProvider;
      _taskCancellation = new TaskCancellation();

      // Ensure initial values are in sync.
      GlobalSettingsOnPropertyChanged(null, null);

      // Ensure changes to ViewModel are synchronized to global settings
      ViewModel.PropertyChanged += ViewModelOnPropertyChanged;

      // Ensure changes to global settings are synchronized to ViewModel
      _globalSettingsProvider.GlobalSettings.PropertyChanged += GlobalSettingsOnPropertyChanged;
    }

    private void GlobalSettingsOnPropertyChanged(object sender, PropertyChangedEventArgs args) {
      var setting = _globalSettingsProvider.GlobalSettings;
      ViewModel.MatchCase = setting.SearchMatchCase;
      ViewModel.MatchWholeWord = setting.SearchMatchWholeWord;
      ViewModel.UseRegex = setting.SearchUseRegEx;
      ViewModel.IncludeSymLinks = setting.SearchIncludeSymLinks;
    }

    private void ViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs args) {
      var settings = _globalSettingsProvider.GlobalSettings;
      var model = (CodeSearchViewModel)sender;
      settings.SearchMatchCase = model.MatchCase;
      settings.SearchMatchWholeWord = model.MatchWholeWord;
      settings.SearchUseRegEx = model.UseRegex;
      settings.SearchIncludeSymLinks = model.IncludeSymLinks;
    }

    public CodeSearchViewModel ViewModel { get { return _control.ViewModel; } }
    public IUIRequestProcessor UIRequestProcessor { get { return _uiRequestProcessor; } }
    public IStandarImageSourceFactory StandarImageSourceFactory { get { return _standarImageSourceFactory; } }
    public IClipboard Clipboard { get { return _clipboard; } }
    public IWindowsExplorer WindowsExplorer { get { return _windowsExplorer; } }
    public GlobalSettings GlobalSettings { get { return _globalSettingsProvider.GlobalSettings; } }
    public ISynchronizationContextProvider SynchronizationContextProvider { get { return _synchronizationContextProvider; } }
    public IOpenDocumentHelper OpenDocumentHelper { get { return _openDocumentHelper; } }

    public void SetFileSystemTreeComputing() {
      var items = CreateInfromationMessages("Loading files from VS Chromium projects...)");
      ViewModel.SetInformationMessages(items);

      if (!ViewModel.FileSystemTreeAvailable || ViewModel.ActiveDisplay == CodeSearchViewModel.DisplayKind.InformationMessages) {
        ViewModel.SwitchToInformationMessages();
      }
    }

    public void SetFileSystemTreeComputed(FileSystemTree tree) {
      ViewModel.FileSystemTreeAvailable = (tree.Root.Entries.Count > 0);

      if (ViewModel.FileSystemTreeAvailable) {
        var items = CreateInfromationMessages(
          "No search results available - Type text to search for in \"Search Code\" and/or \"File Path\"");
        ViewModel.SetInformationMessages(items);
        if (ViewModel.ActiveDisplay == CodeSearchViewModel.DisplayKind.InformationMessages) {
          ViewModel.SwitchToInformationMessages();
        }

        // Add top level nodes in search results explaining results may be outdated
        AddResultsOutdateMessage();
      } else {
        var items = CreateInfromationMessages(
          string.Format("Open a source file from a local Chromium enlistment or") + "\r\n" +
          string.Format("from a directory containing a \"{0}\" file.", ConfigurationFileNames.ProjectFileName));
        ViewModel.SetInformationMessages(items);
        ViewModel.SwitchToInformationMessages();
      }

      FetchDatabaseStatistics();
    }

    public void SetFileSystemTreeError(ErrorResponse error) {
      var viewModel = CreateErrorResponseViewModel(error);
      ViewModel.SetInformationMessages(viewModel);
    }

    public void FilesLoaded() {
      // Add top level nodes in search results explaining results may be outdated
      AddResultsOutdateMessage();
      FetchDatabaseStatistics();
    }

    public void CancelSearch() {
      ViewModel.SwitchToInformationMessages();
    }

    private void AddResultsOutdateMessage() {
      // Add only if we have search results currently displayed.
      if (ViewModel.ActiveDisplay == CodeSearchViewModel.DisplayKind.InformationMessages) {
        return;
      }

      // Add message only once. 
      if (ViewModel.RootNodes.Count > 0 &&
          ViewModel.RootNodes[0] is TextWarningItemViewModel) {
        return;
      }

      // Add message
      var parent = ViewModel.RootNodes[0].ParentViewModel;
      var item = new TextWarningItemViewModel(
        StandarImageSourceFactory,
        parent,
        "Search results may be out of date as index has been updated. Run search again to display up-to-date results.");
      ViewModel.RootNodes.Insert(0, item);
    }

    public void RefreshFileSystemTree() {
      var uiRequest = new UIRequest {
        Request = new RefreshFileSystemTreeRequest(),
        Id = "RefreshFileSystemTreeRequest",
        Delay = TimeSpan.FromSeconds(0.0),
      };

      _uiRequestProcessor.Post(uiRequest);
    }

    public void OpenFileInEditor(FileEntryViewModel fileEntry, Span? span) {
      // Using "Post" is important: it allows the newly opened document to
      // receive the focus.
      SynchronizationContextProvider.UIContext.Post(() =>
        OpenDocumentHelper.OpenDocument(fileEntry.Path, _ => span));
    }

    private List<TreeViewItemViewModel> CreateInfromationMessages(params string[] messages) {
      var result = new List<TreeViewItemViewModel>();
      var rootNode = new RootTreeViewItemViewModel(StandarImageSourceFactory);
      foreach (var text in messages) {
        result.Add(new TextItemViewModel(StandarImageSourceFactory, rootNode, text));<commit_msg>Remove "search results out of date" message.<commit_after>﻿// Copyright 2014 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;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Controls;
using Microsoft.VisualStudio.Text;
using VsChromium.Core.Configuration;
using VsChromium.Core.Ipc;
using VsChromium.Core.Ipc.TypedMessages;
using VsChromium.Core.Linq;
using VsChromium.Core.Threads;
using VsChromium.Features.SourceExplorerHierarchy;
using VsChromium.Package;
using VsChromium.Settings;
using VsChromium.Threads;
using VsChromium.Views;
using VsChromium.Wpf;
using TreeView = System.Windows.Controls.TreeView;

namespace VsChromium.Features.ToolWindows.CodeSearch {
  public class CodeSearchController : ICodeSearchController {
    private static class OperationsIds {
      public const string SearchCode = "files-contents-search";
      public const string SearchFilePaths = "file-names-search";
    }

    private readonly CodeSearchControl _control;
    private readonly IUIRequestProcessor _uiRequestProcessor;
    private readonly IProgressBarTracker _progressBarTracker;
    private readonly IStandarImageSourceFactory _standarImageSourceFactory;
    private readonly IWindowsExplorer _windowsExplorer;
    private readonly IClipboard _clipboard;
    private readonly ISynchronizationContextProvider _synchronizationContextProvider;
    private readonly IOpenDocumentHelper _openDocumentHelper;
    private readonly IEventBus _eventBus;
    private readonly IGlobalSettingsProvider _globalSettingsProvider;
    private readonly TaskCancellation _taskCancellation;

    /// <summary>
    /// For generating unique id n progress bar tracker.
    /// </summary>
    private int _operationSequenceId;

    public CodeSearchController(
      CodeSearchControl control,
      IUIRequestProcessor uiRequestProcessor,
      IProgressBarTracker progressBarTracker,
      IStandarImageSourceFactory standarImageSourceFactory,
      IWindowsExplorer windowsExplorer,
      IClipboard clipboard,
      ISynchronizationContextProvider synchronizationContextProvider,
      IOpenDocumentHelper openDocumentHelper,
      IEventBus eventBus,
      IGlobalSettingsProvider globalSettingsProvider) {
      _control = control;
      _uiRequestProcessor = uiRequestProcessor;
      _progressBarTracker = progressBarTracker;
      _standarImageSourceFactory = standarImageSourceFactory;
      _windowsExplorer = windowsExplorer;
      _clipboard = clipboard;
      _synchronizationContextProvider = synchronizationContextProvider;
      _openDocumentHelper = openDocumentHelper;
      _eventBus = eventBus;
      _globalSettingsProvider = globalSettingsProvider;
      _taskCancellation = new TaskCancellation();

      // Ensure initial values are in sync.
      GlobalSettingsOnPropertyChanged(null, null);

      // Ensure changes to ViewModel are synchronized to global settings
      ViewModel.PropertyChanged += ViewModelOnPropertyChanged;

      // Ensure changes to global settings are synchronized to ViewModel
      _globalSettingsProvider.GlobalSettings.PropertyChanged += GlobalSettingsOnPropertyChanged;
    }

    private void GlobalSettingsOnPropertyChanged(object sender, PropertyChangedEventArgs args) {
      var setting = _globalSettingsProvider.GlobalSettings;
      ViewModel.MatchCase = setting.SearchMatchCase;
      ViewModel.MatchWholeWord = setting.SearchMatchWholeWord;
      ViewModel.UseRegex = setting.SearchUseRegEx;
      ViewModel.IncludeSymLinks = setting.SearchIncludeSymLinks;
    }

    private void ViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs args) {
      var settings = _globalSettingsProvider.GlobalSettings;
      var model = (CodeSearchViewModel)sender;
      settings.SearchMatchCase = model.MatchCase;
      settings.SearchMatchWholeWord = model.MatchWholeWord;
      settings.SearchUseRegEx = model.UseRegex;
      settings.SearchIncludeSymLinks = model.IncludeSymLinks;
    }

    public CodeSearchViewModel ViewModel { get { return _control.ViewModel; } }
    public IUIRequestProcessor UIRequestProcessor { get { return _uiRequestProcessor; } }
    public IStandarImageSourceFactory StandarImageSourceFactory { get { return _standarImageSourceFactory; } }
    public IClipboard Clipboard { get { return _clipboard; } }
    public IWindowsExplorer WindowsExplorer { get { return _windowsExplorer; } }
    public GlobalSettings GlobalSettings { get { return _globalSettingsProvider.GlobalSettings; } }
    public ISynchronizationContextProvider SynchronizationContextProvider { get { return _synchronizationContextProvider; } }
    public IOpenDocumentHelper OpenDocumentHelper { get { return _openDocumentHelper; } }

    public void SetFileSystemTreeComputing() {
      var items = CreateInfromationMessages("Loading files from VS Chromium projects...)");
      ViewModel.SetInformationMessages(items);

      if (!ViewModel.FileSystemTreeAvailable || ViewModel.ActiveDisplay == CodeSearchViewModel.DisplayKind.InformationMessages) {
        ViewModel.SwitchToInformationMessages();
      }
    }

    public void SetFileSystemTreeComputed(FileSystemTree tree) {
      ViewModel.FileSystemTreeAvailable = (tree.Root.Entries.Count > 0);

      if (ViewModel.FileSystemTreeAvailable) {
        var items = CreateInfromationMessages(
          "No search results available - Type text to search for in \"Search Code\" and/or \"File Path\"");
        ViewModel.SetInformationMessages(items);
        if (ViewModel.ActiveDisplay == CodeSearchViewModel.DisplayKind.InformationMessages) {
          ViewModel.SwitchToInformationMessages();
        }

        // Add top level nodes in search results explaining results may be outdated
        AddResultsOutdatedMessage();
      } else {
        var items = CreateInfromationMessages(
          string.Format("Open a source file from a local Chromium enlistment or") + "\r\n" +
          string.Format("from a directory containing a \"{0}\" file.", ConfigurationFileNames.ProjectFileName));
        ViewModel.SetInformationMessages(items);
        ViewModel.SwitchToInformationMessages();
      }

      FetchDatabaseStatistics();
    }

    public void SetFileSystemTreeError(ErrorResponse error) {
      var viewModel = CreateErrorResponseViewModel(error);
      ViewModel.SetInformationMessages(viewModel);
    }

    public void FilesLoaded() {
      // Add top level nodes in search results explaining results may be outdated
      AddResultsOutdatedMessage();
      FetchDatabaseStatistics();
    }

    public void CancelSearch() {
      ViewModel.SwitchToInformationMessages();
    }

    private void AddResultsOutdatedMessage() {
      // Don't do this for now because
      // 1) the message tends to be annoying
      // 2) there is an issue with "BringObjectToView" that assumes nodes list
      //    have the same # of children, and we get an error 
      //    "TreeView item data context is not the right data object."
#if false
      // Add only if we have search results currently displayed.
      if (ViewModel.ActiveDisplay == CodeSearchViewModel.DisplayKind.InformationMessages) {
        return;
      }

      // Add message only once. 
      if (ViewModel.RootNodes.Count > 0 &&
          ViewModel.RootNodes[0] is TextWarningItemViewModel) {
        return;
      }

      // Add message
      var parent = ViewModel.RootNodes[0].ParentViewModel;
      var item = new TextWarningItemViewModel(
        StandarImageSourceFactory,
        parent,
        "Search results may be out of date as index has been updated. Run search again to display up-to-date results.");
      ViewModel.RootNodes.Insert(0, item);
#endif
    }

    public void RefreshFileSystemTree() {
      var uiRequest = new UIRequest {
        Request = new RefreshFileSystemTreeRequest(),
        Id = "RefreshFileSystemTreeRequest",
        Delay = TimeSpan.FromSeconds(0.0),
      };

      _uiRequestProcessor.Post(uiRequest);
    }

    public void OpenFileInEditor(FileEntryViewModel fileEntry, Span? span) {
      // Using "Post" is important: it allows the newly opened document to
      // receive the focus.
      SynchronizationContextProvider.UIContext.Post(() =>
        OpenDocumentHelper.OpenDocument(fileEntry.Path, _ => span));
    }

    private List<TreeViewItemViewModel> CreateInfromationMessages(params string[] messages) {
      var result = new List<TreeViewItemViewModel>();
      var rootNode = new RootTreeViewItemViewModel(StandarImageSourceFactory);
      foreach (var text in messages) {
        result.Add(new TextItemViewModel(StandarImageSourceFactory, rootNode, text));
{'fraction_non_alphanumeric': 0.06764654823216248, 'fraction_numerical': 0.0015045425611943752, 'mean_word_length': 3.8558583871874124, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 19, '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': '2144768', 'n_tokens_mistral': 4371, 'n_tokens_neox': 4197, 'n_words': 1136}