<commit_before>package com.intellij.codeInsight.completion;

import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.AllClassesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.patterns.PsiJavaPatterns;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;

import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 * User: ik
 * Date: 02.12.2003
 * Time: 16:49:25
 * To change this template use Options | File Templates.
 */
public class AllClassesGetter {
  private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.AllClassesGetter");
  private final ElementFilter myFilter;
  private static final InsertHandler<JavaPsiClassReferenceElement> INSERT_HANDLER = new InsertHandler<JavaPsiClassReferenceElement>() {

    private void _handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
      final Editor editor = context.getEditor();
      final PsiClass psiClass = item.getObject();
      if (!psiClass.isValid()) return;

      int endOffset = editor.getCaretModel().getOffset();
      final String qname = psiClass.getQualifiedName();
      if (qname == null) return;

      if (endOffset == 0) return;

      final Document document = editor.getDocument();
      final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(psiClass.getProject());
      final PsiFile file = context.getFile();
      if (file.findElementAt(endOffset - 1) == null) return;

      boolean checkReference = true;
      if (file.getLanguage() == StdLanguages.JAVA) {
        if (PsiTreeUtil.findElementOfClassAtOffset(file, endOffset - 1, PsiImportStatementBase.class, false) != null) {
          checkReference = false;
        } else {
          final OffsetKey key = OffsetKey.create("endOffset", false);
          context.getOffsetMap().addOffset(key, endOffset);
          JavaPsiClassReferenceElement.JAVA_CLASS_INSERT_HANDLER.handleInsert(context, item);
          final int newOffset = context.getOffsetMap().getOffset(key);
          if (newOffset >= 0) {
            endOffset = newOffset;
          } else {
            LOG.error(endOffset + " became invalid: " + context.getOffsetMap() + "; inserting " + qname);
          }
        }
      }

      final RangeMarker toDelete = DefaultInsertHandler.insertSpace(endOffset, document);
      psiDocumentManager.commitAllDocuments();
      PsiReference psiReference = file.findReferenceAt(endOffset - 1);
      boolean insertFqn = true;
      if (checkReference && psiReference != null) {
        final PsiManager psiManager = file.getManager();
        if (psiManager.areElementsEquivalent(psiClass, DefaultInsertHandler.resolveReference(psiReference))) {
          insertFqn = false;
        }
        else if (psiClass.isValid()) {
          try {
            final PsiElement newUnderlying = psiReference.bindToElement(psiClass);
            if (newUnderlying != null) {
              final PsiElement psiElement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newUnderlying);
              if (psiElement != null) {
                for (final PsiReference reference : psiElement.getReferences()) {
                  if (psiManager.areElementsEquivalent(psiClass, DefaultInsertHandler.resolveReference(reference))) {
                    insertFqn = false;
                    endOffset = reference.getRangeInElement().getEndOffset() + reference.getElement().getTextRange().getStartOffset();
                    break;
                  }
                }
              }
            }
          }
          catch (IncorrectOperationException e) {
            //if it's empty we just insert fqn below
          }
        }
      }
      if (toDelete.isValid()) {
        document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
        if (insertFqn) {
          endOffset = toDelete.getStartOffset();
        }
      }

      if (insertFqn) {
        int i = endOffset - 1;
        while (i >= 0) {
          final char ch = document.getCharsSequence().charAt(i);
          if (!Character.isJavaIdentifierPart(ch) && ch != '.') break;
          i--;
        }
        document.replaceString(i + 1, endOffset, qname);
      }
    }

    public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
      context.setAddCompletionChar(false);
      _handleInsert(context, item);
      item.getTailType().processTail(context.getEditor(), context.getEditor().getCaretModel().getOffset());
    }

  };

  public AllClassesGetter(final ElementFilter filter) {
    myFilter = filter;
  }

  public void getClasses(final PsiElement context, final CompletionResultSet set, final int offset, final boolean filterByScope) {
    if(context == null || !context.isValid()) return;

    final String packagePrefix = getPackagePrefix(context, offset);

    final Set<String> qnames = new THashSet<String>();

    final GlobalSearchScope scope = filterByScope ? context.getContainingFile().getResolveScope() : GlobalSearchScope.allScope(context.getProject());
    final PrefixMatcher prefixMatcher = set.getPrefixMatcher();

    final boolean lookingForAnnotations = PsiJavaPatterns.psiElement().afterLeaf("@").accepts(context);

    AllClassesSearch.search(scope, context.getProject(), new Condition<String>() {
      public boolean value(String s) {
        return prefixMatcher.prefixMatches(s);
      }
    }).forEach(new Processor<PsiClass>() {
      public boolean process(PsiClass psiClass) {
        if (isSuitable(context, packagePrefix, qnames, lookingForAnnotations, psiClass, filterByScope)) {
          set.addElement(createLookupItem(psiClass));
        }
        return true;
      }
    });
  }

  private static String getPackagePrefix(final PsiElement context, final int offset) {
    final String fileText = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
      public String compute() {
        return context.getContainingFile().getText();
      }
    });
    int i = offset - 1;
    while (i >= 0) {
      final char c = fileText.charAt(i);
      if (!Character.isJavaIdentifierPart(c) && c != '.') break;
      i--;
    }
    String prefix = fileText.substring(i + 1, offset);
    final int j = prefix.lastIndexOf('.');
    return j > 0 ? prefix.substring(0, j) : "";
  }

  private boolean isSuitable(final PsiElement context, final String packagePrefix, final Set<String> qnames,
                             final boolean lookingForAnnotations,
                             final PsiClass psiClass,
                             final boolean filterByScope) {
    //noinspection AutoUnboxing<commit_msg>checkCanceled (IDEADEV-38159)<commit_after>package com.intellij.codeInsight.completion;

import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.AllClassesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.patterns.PsiJavaPatterns;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;

import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 * User: ik
 * Date: 02.12.2003
 * Time: 16:49:25
 * To change this template use Options | File Templates.
 */
public class AllClassesGetter {
  private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.AllClassesGetter");
  private final ElementFilter myFilter;
  private static final InsertHandler<JavaPsiClassReferenceElement> INSERT_HANDLER = new InsertHandler<JavaPsiClassReferenceElement>() {

    private void _handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
      final Editor editor = context.getEditor();
      final PsiClass psiClass = item.getObject();
      if (!psiClass.isValid()) return;

      int endOffset = editor.getCaretModel().getOffset();
      final String qname = psiClass.getQualifiedName();
      if (qname == null) return;

      if (endOffset == 0) return;

      final Document document = editor.getDocument();
      final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(psiClass.getProject());
      final PsiFile file = context.getFile();
      if (file.findElementAt(endOffset - 1) == null) return;

      boolean checkReference = true;
      if (file.getLanguage() == StdLanguages.JAVA) {
        if (PsiTreeUtil.findElementOfClassAtOffset(file, endOffset - 1, PsiImportStatementBase.class, false) != null) {
          checkReference = false;
        } else {
          final OffsetKey key = OffsetKey.create("endOffset", false);
          context.getOffsetMap().addOffset(key, endOffset);
          JavaPsiClassReferenceElement.JAVA_CLASS_INSERT_HANDLER.handleInsert(context, item);
          final int newOffset = context.getOffsetMap().getOffset(key);
          if (newOffset >= 0) {
            endOffset = newOffset;
          } else {
            LOG.error(endOffset + " became invalid: " + context.getOffsetMap() + "; inserting " + qname);
          }
        }
      }

      final RangeMarker toDelete = DefaultInsertHandler.insertSpace(endOffset, document);
      psiDocumentManager.commitAllDocuments();
      PsiReference psiReference = file.findReferenceAt(endOffset - 1);
      boolean insertFqn = true;
      if (checkReference && psiReference != null) {
        final PsiManager psiManager = file.getManager();
        if (psiManager.areElementsEquivalent(psiClass, DefaultInsertHandler.resolveReference(psiReference))) {
          insertFqn = false;
        }
        else if (psiClass.isValid()) {
          try {
            final PsiElement newUnderlying = psiReference.bindToElement(psiClass);
            if (newUnderlying != null) {
              final PsiElement psiElement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newUnderlying);
              if (psiElement != null) {
                for (final PsiReference reference : psiElement.getReferences()) {
                  if (psiManager.areElementsEquivalent(psiClass, DefaultInsertHandler.resolveReference(reference))) {
                    insertFqn = false;
                    endOffset = reference.getRangeInElement().getEndOffset() + reference.getElement().getTextRange().getStartOffset();
                    break;
                  }
                }
              }
            }
          }
          catch (IncorrectOperationException e) {
            //if it's empty we just insert fqn below
          }
        }
      }
      if (toDelete.isValid()) {
        document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
        if (insertFqn) {
          endOffset = toDelete.getStartOffset();
        }
      }

      if (insertFqn) {
        int i = endOffset - 1;
        while (i >= 0) {
          final char ch = document.getCharsSequence().charAt(i);
          if (!Character.isJavaIdentifierPart(ch) && ch != '.') break;
          i--;
        }
        document.replaceString(i + 1, endOffset, qname);
      }
    }

    public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
      context.setAddCompletionChar(false);
      _handleInsert(context, item);
      item.getTailType().processTail(context.getEditor(), context.getEditor().getCaretModel().getOffset());
    }

  };

  public AllClassesGetter(final ElementFilter filter) {
    myFilter = filter;
  }

  public void getClasses(final PsiElement context, final CompletionResultSet set, final int offset, final boolean filterByScope) {
    if(context == null || !context.isValid()) return;

    final String packagePrefix = getPackagePrefix(context, offset);

    final Set<String> qnames = new THashSet<String>();

    final GlobalSearchScope scope = filterByScope ? context.getContainingFile().getResolveScope() : GlobalSearchScope.allScope(context.getProject());
    final PrefixMatcher prefixMatcher = set.getPrefixMatcher();

    final boolean lookingForAnnotations = PsiJavaPatterns.psiElement().afterLeaf("@").accepts(context);

    AllClassesSearch.search(scope, context.getProject(), new Condition<String>() {
      public boolean value(String s) {
        return prefixMatcher.prefixMatches(s);
      }
    }).forEach(new Processor<PsiClass>() {
      public boolean process(PsiClass psiClass) {
        if (isSuitable(context, packagePrefix, qnames, lookingForAnnotations, psiClass, filterByScope)) {
          set.addElement(createLookupItem(psiClass));
        }
        return true;
      }
    });
  }

  private static String getPackagePrefix(final PsiElement context, final int offset) {
    final String fileText = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
      public String compute() {
        ProgressManager.getInstance().checkCanceled();
        return context.getContainingFile().getText();
      }
    });
    int i = offset - 1;
    while (i >= 0) {
      final char c = fileText.charAt(i);
      if (!Character.isJavaIdentifierPart(c) && c != '.') break;
      i--;
    }
    String prefix = fileText.substring(i + 1, offset);
    final int j = prefix.lastIndexOf('.');
    return j > 0 ? prefix.substring(0, j) : "";
  }

  private boolean isSuitable(final PsiElement context, final String packagePrefix, final Set<String> qnames,
                             final boolean lookingForAnnotations,
                             final PsiClass psiClass,
                             final boolean filterByScope) {
    //noinspection AutoUnboxing
{'fraction_non_alphanumeric': 0.10319227913882702, 'fraction_numerical': 0.003981912667881488, 'mean_word_length': 3.1092623405435384, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 27, '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': '420702', 'n_tokens_mistral': 4157, 'n_tokens_neox': 4032, 'n_words': 865}