<commit_before>package com.intellij.codeInsight.daemon.impl;

import com.intellij.lang.properties.parsing.PropertiesTokenTypes;
import com.intellij.lang.xml.XmlFileViewProvider;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorMarkupModel;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;

public class PsiChangeHandler extends PsiTreeChangeAdapter {
  private final Project myProject;
  private final DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;

  public PsiChangeHandler(Project project, DaemonCodeAnalyzerImpl daemonCodeAnalyzer) {
    myProject = project;
    myDaemonCodeAnalyzer = daemonCodeAnalyzer;
  }

  public void childAdded(PsiTreeChangeEvent event) {
    updateByChange(event.getParent(), true);
  }

  public void childRemoved(PsiTreeChangeEvent event) {
    updateByChange(event.getParent(), true);
  }

  public void childReplaced(PsiTreeChangeEvent event) {
    updateByChange(event.getNewChild(), typesEqual(event.getNewChild(), event.getOldChild()));
  }

  private static boolean typesEqual(final PsiElement newChild, final PsiElement oldChild) {
    return newChild != null && oldChild != null && newChild.getClass() == oldChild.getClass();
  }

  public void childrenChanged(PsiTreeChangeEvent event) {
    updateByChange(event.getParent(), true);
  }

  public void beforeChildMovement(PsiTreeChangeEvent event) {
    updateByChange(event.getOldParent(), true);
    updateByChange(event.getNewParent(), true);
  }

  public void beforeChildrenChange(PsiTreeChangeEvent event) {
    // this event sent always before every PSI change, even not significant one (like after quick typing/backspacing char)
    // mark file dirty just in case
    PsiFile psiFile = event.getFile();
    if (psiFile != null) {
      Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(psiFile);
      if (document != null) {
        myDaemonCodeAnalyzer.getFileStatusMap().markFileScopeDirtyDefensively(psiFile);
        myDaemonCodeAnalyzer.stopProcess(true);
      }
    }
  }

  public void propertyChanged(PsiTreeChangeEvent event) {
    String propertyName = event.getPropertyName();
    if (!propertyName.equals(PsiTreeChangeEvent.PROP_WRITABLE)) {
      myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
      myDaemonCodeAnalyzer.stopProcess(true);
    }
  }

  private void updateByChange(PsiElement child, final boolean whitespaceOptimizationAllowed) {
    final Editor editor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
    if (editor != null) {
      ApplicationManager.getApplication().invokeLater(new Runnable() {
        public void run() {
          EditorMarkupModel markupModel = (EditorMarkupModel)editor.getMarkupModel();
          markupModel.setErrorStripeRenderer(markupModel.getErrorStripeRenderer());
        }
      }, ModalityState.stateForComponent(editor.getComponent()));
    }

    PsiFile file = child.getContainingFile();
    if (file == null) {
      myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
      return;
    }
    Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
    if (document == null) return;

    // optimization
    if (whitespaceOptimizationAllowed && UpdateHighlightersUtil.isWhitespaceOptimizationAllowed(document)) {
      final ASTNode node = child.getNode();
      if (child instanceof PsiWhiteSpace || child instanceof PsiComment || child instanceof PsiDocToken ||
          node != null && PropertiesTokenTypes.PROPERTIES_TYPES_TO_IGNORE.contains(node.getElementType())) {
        return;
      }
    }

    if (file instanceof XmlFile) {
      // TODO: Hackery. Need an API for language plugin developers to define dirty scope for their changes.
      final FileViewProvider provider = file.getViewProvider();
      if (provider instanceof XmlFileViewProvider && ((XmlFileViewProvider)provider).getLanguageExtensions().length > 0) {
        myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
        return;
      }
    }

    // optimization:
    PsiElement parent = child;
    while (true) {
      if (parent instanceof PsiFile || parent instanceof PsiDirectory) {
        myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
        return;
      }
      PsiElement pparent = parent.getParent();

      if (parent instanceof XmlTag) {
        PsiElement dirtyScope = pparent;

        if (pparent instanceof XmlTag && "head".equals(((XmlTag)pparent).getLocalName())) {
          final PsiFile containingFile = parent.getContainingFile();
          final FileType fileType = containingFile == null ? null : containingFile.getFileType();

          if (fileType == StdFileTypes.JSP || fileType == StdFileTypes.JSPX || fileType == StdFileTypes.HTML ||
              fileType == StdFileTypes.XHTML) {<commit_msg>IDEADEV-8818<commit_after>package com.intellij.codeInsight.daemon.impl;

import com.intellij.lang.ASTNode;
import com.intellij.lang.StdLanguages;
import com.intellij.lang.Language;
import com.intellij.lang.properties.parsing.PropertiesTokenTypes;
import com.intellij.lang.xml.XmlFileViewProvider;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorMarkupModel;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;

public class PsiChangeHandler extends PsiTreeChangeAdapter {
  private final Project myProject;
  private final DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;

  public PsiChangeHandler(Project project, DaemonCodeAnalyzerImpl daemonCodeAnalyzer) {
    myProject = project;
    myDaemonCodeAnalyzer = daemonCodeAnalyzer;
  }

  public void childAdded(PsiTreeChangeEvent event) {
    updateByChange(event.getParent(), true);
  }

  public void childRemoved(PsiTreeChangeEvent event) {
    updateByChange(event.getParent(), true);
  }

  public void childReplaced(PsiTreeChangeEvent event) {
    updateByChange(event.getNewChild(), typesEqual(event.getNewChild(), event.getOldChild()));
  }

  private static boolean typesEqual(final PsiElement newChild, final PsiElement oldChild) {
    return newChild != null && oldChild != null && newChild.getClass() == oldChild.getClass();
  }

  public void childrenChanged(PsiTreeChangeEvent event) {
    updateByChange(event.getParent(), true);
  }

  public void beforeChildMovement(PsiTreeChangeEvent event) {
    updateByChange(event.getOldParent(), true);
    updateByChange(event.getNewParent(), true);
  }

  public void beforeChildrenChange(PsiTreeChangeEvent event) {
    // this event sent always before every PSI change, even not significant one (like after quick typing/backspacing char)
    // mark file dirty just in case
    PsiFile psiFile = event.getFile();
    if (psiFile != null) {
      Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(psiFile);
      if (document != null) {
        myDaemonCodeAnalyzer.getFileStatusMap().markFileScopeDirtyDefensively(psiFile);
        myDaemonCodeAnalyzer.stopProcess(true);
      }
    }
  }

  public void propertyChanged(PsiTreeChangeEvent event) {
    String propertyName = event.getPropertyName();
    if (!propertyName.equals(PsiTreeChangeEvent.PROP_WRITABLE)) {
      myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
      myDaemonCodeAnalyzer.stopProcess(true);
    }
  }

  private void updateByChange(PsiElement child, final boolean whitespaceOptimizationAllowed) {
    final Editor editor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
    if (editor != null) {
      ApplicationManager.getApplication().invokeLater(new Runnable() {
        public void run() {
          EditorMarkupModel markupModel = (EditorMarkupModel)editor.getMarkupModel();
          markupModel.setErrorStripeRenderer(markupModel.getErrorStripeRenderer());
        }
      }, ModalityState.stateForComponent(editor.getComponent()));
    }

    PsiFile file = child.getContainingFile();
    if (file == null) {
      myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
      return;
    }
    Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
    if (document == null) return;

    // optimization
    if (whitespaceOptimizationAllowed && UpdateHighlightersUtil.isWhitespaceOptimizationAllowed(document)) {
      final ASTNode node = child.getNode();
      if (child instanceof PsiWhiteSpace || child instanceof PsiComment || child instanceof PsiDocToken ||
          node != null && PropertiesTokenTypes.PROPERTIES_TYPES_TO_IGNORE.contains(node.getElementType())) {
        return;
      }
    }

    if (file instanceof XmlFile) {
      // TODO: Hackery. Need an API for language plugin developers to define dirty scope for their changes.
      final FileViewProvider provider = file.getViewProvider();
      if (provider instanceof XmlFileViewProvider && ((XmlFileViewProvider)provider).getLanguageExtensions().length > 0) {
        myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
        return;
      }
    }
    // change in e.g. sciptlet may lead to error in any other place
    Language language = file.getLanguage();
    if (language == StdLanguages.JSPX || language == StdLanguages.JSP) {
      myDaemonCodeAnalyzer.getFileStatusMap().markFileScopeDirty(document, file);
      return;
    }

    // optimization:
    PsiElement parent = child;
    while (true) {
      if (parent instanceof PsiFile || parent instanceof PsiDirectory) {
        myDaemonCodeAnalyzer.getFileStatusMap().markAllFilesDirty();
        return;
      }
      PsiElement pparent = parent.getParent();

      if (parent instanceof XmlTag) {
        PsiElement dirtyScope = pparent;

        if (pparent instanceof XmlTag && "head".equals(((XmlTag)pparent).getLocalName())) {
          final PsiFile containingFile = parent.getContainingFile();
          final FileType fileType = containingFile == null ? null : containingFile.getFileType();

          if (fileType == StdFileTypes.JSP || fileType == StdFileTypes.JSPX || fileType == StdFileTypes.HTML ||
              fileType == StdFileTypes.XHTML) {
{'fraction_non_alphanumeric': 0.09713404679847633, 'fraction_numerical': 0.0005441683294032287, 'mean_word_length': 4.2484531175630655, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, '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': '4557748', 'n_tokens_mistral': 3175, 'n_tokens_neox': 3106, 'n_words': 627}