<commit_before>// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.spellchecker;

import com.google.common.collect.Maps;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.ide.SaveAndSyncHandler;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.spellchecker.dictionary.Dictionary;
import com.intellij.spellchecker.dictionary.*;
import com.intellij.spellchecker.engine.SpellCheckerEngine;
import com.intellij.spellchecker.engine.SuggestionProvider;
import com.intellij.spellchecker.grazie.GrazieSpellCheckerEngine;
import com.intellij.spellchecker.grazie.GrazieSuggestionProvider;
import com.intellij.spellchecker.settings.SpellCheckerSettings;
import com.intellij.spellchecker.state.CachedDictionaryState;
import com.intellij.spellchecker.state.DictionaryStateListener;
import com.intellij.spellchecker.state.ProjectDictionaryState;
import com.intellij.spellchecker.util.SpellCheckerBundle;
import com.intellij.util.EventDispatcher;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.InputStream;
import java.util.*;

import static com.intellij.openapi.application.PathManager.getOptionsPath;
import static com.intellij.openapi.util.io.FileUtil.isAncestor;
import static com.intellij.openapi.util.io.FileUtilRt.extensionEquals;
import static com.intellij.openapi.util.io.FileUtilRt.toSystemDependentName;
import static com.intellij.openapi.vfs.VfsUtilCore.visitChildrenRecursively;
import static com.intellij.project.ProjectKt.getProjectStoreDirectory;

public class SpellCheckerManager implements Disposable {
  private static final Logger LOG = Logger.getInstance(SpellCheckerManager.class);

  private static final int MAX_METRICS = 1;
  public static final String PROJECT = "project";
  public static final String APP = "application";
  private final Project project;
  private ProjectDictionary myProjectDictionary;
  private EditableDictionary myAppDictionary;
  private final SpellCheckerSettings settings;
  private final VirtualFileListener myCustomDictFileListener;
  private final String myProjectDictionaryPath;
  private final String myAppDictionaryPath;
  public static final String PROJECT_DICTIONARY_PATH =
    "dictionaries" + File.separator + System.getProperty("user.name").replace('.', '_') + ".xml";
  public static final String CACHED_DICTIONARY_FILE = "cachedDictionary.xml";

  private final EventDispatcher<DictionaryStateListener> myUserDictionaryListenerEventDispatcher =
    EventDispatcher.create(DictionaryStateListener.class);

  private SpellCheckerEngine mySpellChecker;
  private SuggestionProvider mySuggestionProvider;


  public static SpellCheckerManager getInstance(Project project) {
    return ServiceManager.getService(project, SpellCheckerManager.class);
  }

  public SpellCheckerManager(Project project) {
    this.project = project;
    this.settings = SpellCheckerSettings.getInstance(project);

    this.mySpellChecker = new GrazieSpellCheckerEngine(project);
    this.mySuggestionProvider = new GrazieSuggestionProvider(mySpellChecker);

    fullConfigurationReload();

    Disposer.register(project, this);
    final VirtualFile projectStoreDir = project.getBaseDir() != null ? getProjectStoreDirectory(project.getBaseDir()) : null;
    myProjectDictionaryPath = projectStoreDir != null ? projectStoreDir.getPath() + File.separator + PROJECT_DICTIONARY_PATH : "";
    myAppDictionaryPath = getOptionsPath() + File.separator + CACHED_DICTIONARY_FILE;
    myCustomDictFileListener = new CustomDictFileListener(settings);
    LocalFileSystem.getInstance().addVirtualFileListener(myCustomDictFileListener);
    BundledDictionaryProvider.EP_NAME.addChangeListener(this::fillEngineDictionary, this);
    RuntimeDictionaryProvider.EP_NAME.addChangeListener(this::fillEngineDictionary, this);
    CustomDictionaryProvider.EP_NAME.addChangeListener(this::fillEngineDictionary, this);
  }

  @SuppressWarnings("unused")  // used in Rider
  public SpellCheckerEngine getSpellChecker() {
    return mySpellChecker;
  }

  public void fullConfigurationReload() {
    mySpellChecker = new GrazieSpellCheckerEngine(project);
    mySuggestionProvider = new GrazieSuggestionProvider(mySpellChecker);
    fillEngineDictionary();
  }

  public void updateBundledDictionaries(final List<String> removedDictionaries) {
    for (BundledDictionaryProvider provider : BundledDictionaryProvider.EP_NAME.getExtensionList()) {
      for (String dictionary : provider.getBundledDictionaries()) {<commit_msg>Spellchecker: remove unnecessary parent disposable registration<commit_after>// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.spellchecker;

import com.google.common.collect.Maps;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.ide.SaveAndSyncHandler;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.spellchecker.dictionary.Dictionary;
import com.intellij.spellchecker.dictionary.*;
import com.intellij.spellchecker.engine.SpellCheckerEngine;
import com.intellij.spellchecker.engine.SuggestionProvider;
import com.intellij.spellchecker.grazie.GrazieSpellCheckerEngine;
import com.intellij.spellchecker.grazie.GrazieSuggestionProvider;
import com.intellij.spellchecker.settings.SpellCheckerSettings;
import com.intellij.spellchecker.state.CachedDictionaryState;
import com.intellij.spellchecker.state.DictionaryStateListener;
import com.intellij.spellchecker.state.ProjectDictionaryState;
import com.intellij.spellchecker.util.SpellCheckerBundle;
import com.intellij.util.EventDispatcher;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.InputStream;
import java.util.*;

import static com.intellij.openapi.application.PathManager.getOptionsPath;
import static com.intellij.openapi.util.io.FileUtil.isAncestor;
import static com.intellij.openapi.util.io.FileUtilRt.extensionEquals;
import static com.intellij.openapi.util.io.FileUtilRt.toSystemDependentName;
import static com.intellij.openapi.vfs.VfsUtilCore.visitChildrenRecursively;
import static com.intellij.project.ProjectKt.getProjectStoreDirectory;

public class SpellCheckerManager implements Disposable {
  private static final Logger LOG = Logger.getInstance(SpellCheckerManager.class);

  private static final int MAX_METRICS = 1;
  public static final String PROJECT = "project";
  public static final String APP = "application";
  private final Project project;
  private ProjectDictionary myProjectDictionary;
  private EditableDictionary myAppDictionary;
  private final SpellCheckerSettings settings;
  private final VirtualFileListener myCustomDictFileListener;
  private final String myProjectDictionaryPath;
  private final String myAppDictionaryPath;
  public static final String PROJECT_DICTIONARY_PATH =
    "dictionaries" + File.separator + System.getProperty("user.name").replace('.', '_') + ".xml";
  public static final String CACHED_DICTIONARY_FILE = "cachedDictionary.xml";

  private final EventDispatcher<DictionaryStateListener> myUserDictionaryListenerEventDispatcher =
    EventDispatcher.create(DictionaryStateListener.class);

  private SpellCheckerEngine mySpellChecker;
  private SuggestionProvider mySuggestionProvider;


  public static SpellCheckerManager getInstance(Project project) {
    return ServiceManager.getService(project, SpellCheckerManager.class);
  }

  public SpellCheckerManager(Project project) {
    this.project = project;
    this.settings = SpellCheckerSettings.getInstance(project);

    this.mySpellChecker = new GrazieSpellCheckerEngine(project);
    this.mySuggestionProvider = new GrazieSuggestionProvider(mySpellChecker);

    fullConfigurationReload();

    final VirtualFile projectStoreDir = project.getBaseDir() != null ? getProjectStoreDirectory(project.getBaseDir()) : null;
    myProjectDictionaryPath = projectStoreDir != null ? projectStoreDir.getPath() + File.separator + PROJECT_DICTIONARY_PATH : "";
    myAppDictionaryPath = getOptionsPath() + File.separator + CACHED_DICTIONARY_FILE;
    myCustomDictFileListener = new CustomDictFileListener(settings);
    LocalFileSystem.getInstance().addVirtualFileListener(myCustomDictFileListener);
    BundledDictionaryProvider.EP_NAME.addChangeListener(this::fillEngineDictionary, this);
    RuntimeDictionaryProvider.EP_NAME.addChangeListener(this::fillEngineDictionary, this);
    CustomDictionaryProvider.EP_NAME.addChangeListener(this::fillEngineDictionary, this);
  }

  @SuppressWarnings("unused")  // used in Rider
  public SpellCheckerEngine getSpellChecker() {
    return mySpellChecker;
  }

  public void fullConfigurationReload() {
    mySpellChecker = new GrazieSpellCheckerEngine(project);
    mySuggestionProvider = new GrazieSuggestionProvider(mySpellChecker);
    fillEngineDictionary();
  }

  public void updateBundledDictionaries(final List<String> removedDictionaries) {
    for (BundledDictionaryProvider provider : BundledDictionaryProvider.EP_NAME.getExtensionList()) {
      for (String dictionary : provider.getBundledDictionaries()) {
{'fraction_non_alphanumeric': 0.08290251107828656, 'fraction_numerical': 0.0020310192023633676, 'mean_word_length': 6.6775336640680365, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, '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': '3633769', 'n_tokens_mistral': 3109, 'n_tokens_neox': 3124, 'n_words': 565}