private static PsiFile createFileCopy(PsiFile file, long caret, long selEnd) {
    final VirtualFile virtualFile = file.getVirtualFile();
    boolean mayCacheCopy =
        file.isPhysical()
            &&
            // we don't want to cache code fragment copies even if they appear to be physical
            virtualFile != null
            && virtualFile.isInLocalFileSystem();
    long combinedOffsets = caret + (selEnd << 32);
    if (mayCacheCopy) {
      final Trinity<PsiFile, Document, Long> cached =
          SoftReference.dereference(file.getUserData(FILE_COPY_KEY));
      if (cached != null
          && cached.first.getClass().equals(file.getClass())
          && isCopyUpToDate(cached.second, cached.first)) {
        final PsiFile copy = cached.first;
        if (copy.getViewProvider().getModificationStamp()
                > file.getViewProvider().getModificationStamp()
            && cached.third.longValue() != combinedOffsets) {
          // the copy PSI might have some caches that are not cleared on its modification because
          // there are no events in the copy
          //   so, clear all the caches
          // hopefully it's a rare situation that the user invokes completion in different parts of
          // the file
          //   without modifying anything physical in between
          ((PsiModificationTrackerImpl) file.getManager().getModificationTracker()).incCounter();
        }
        final Document document = cached.second;
        assert document != null;
        file.putUserData(
            FILE_COPY_KEY,
            new SoftReference<Trinity<PsiFile, Document, Long>>(
                Trinity.create(copy, document, combinedOffsets)));

        Document originalDocument = file.getViewProvider().getDocument();
        assert originalDocument != null;
        assert originalDocument.getTextLength() == file.getTextLength() : originalDocument;
        document.setText(originalDocument.getImmutableCharSequence());
        return copy;
      }
    }

    final PsiFile copy = (PsiFile) file.copy();
    if (mayCacheCopy) {
      final Document document = copy.getViewProvider().getDocument();
      assert document != null;
      file.putUserData(
          FILE_COPY_KEY,
          new SoftReference<Trinity<PsiFile, Document, Long>>(
              Trinity.create(copy, document, combinedOffsets)));
    }
    return copy;
  }
  public static void invoke(
      final Project project, PsiFile file, final Editor editor, PsiElement element) {
    if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;

    final PsiJavaCodeReferenceElement refExpr = (PsiJavaCodeReferenceElement) element.getParent();
    final PsiClass aClass = (PsiClass) refExpr.resolve();
    if (aClass == null) {
      return;
    }
    final PsiClass containingClass = PsiUtil.getTopLevelClass(refExpr);
    if (aClass != containingClass) {
      PsiImportList importList = ((PsiJavaFile) file).getImportList();
      if (importList == null) {
        return;
      }
      boolean alreadyImported = false;
      for (PsiImportStaticStatement statement : importList.getImportStaticStatements()) {
        if (!statement.isOnDemand()) continue;
        PsiClass staticResolve = statement.resolveTargetClass();
        if (aClass == staticResolve) {
          alreadyImported = true;
          break;
        }
      }
      if (!alreadyImported) {
        PsiImportStaticStatement importStaticStatement =
            JavaPsiFacade.getInstance(file.getProject())
                .getElementFactory()
                .createImportStaticStatement(aClass, "*");
        importList.add(importStaticStatement);
      }
    }

    List<PsiFile> roots = file.getViewProvider().getAllFiles();
    for (final PsiFile root : roots) {
      PsiElement copy = root.copy();
      final PsiManager manager = root.getManager();

      final TIntArrayList expressionToDequalifyOffsets = new TIntArrayList();
      copy.accept(
          new JavaRecursiveElementWalkingVisitor() {
            int delta;

            @Override
            public void visitReferenceElement(PsiJavaCodeReferenceElement expression) {
              if (isParameterizedReference(expression)) return;
              PsiElement qualifierExpression = expression.getQualifier();
              if (qualifierExpression instanceof PsiJavaCodeReferenceElement
                  && ((PsiJavaCodeReferenceElement) qualifierExpression).isReferenceTo(aClass)) {
                try {
                  PsiElement resolved = expression.resolve();
                  int end = expression.getTextRange().getEndOffset();
                  qualifierExpression.delete();
                  delta += end - expression.getTextRange().getEndOffset();
                  PsiElement after = expression.resolve();
                  if (manager.areElementsEquivalent(after, resolved)) {
                    expressionToDequalifyOffsets.add(
                        expression.getTextRange().getStartOffset() + delta);
                  }
                } catch (IncorrectOperationException e) {
                  LOG.error(e);
                }
              }
              super.visitElement(expression);
            }
          });

      expressionToDequalifyOffsets.forEachDescending(
          new TIntProcedure() {
            @Override
            public boolean execute(int offset) {
              PsiJavaCodeReferenceElement expression =
                  PsiTreeUtil.findElementOfClassAtOffset(
                      root, offset, PsiJavaCodeReferenceElement.class, false);
              if (expression == null) {
                return false;
              }
              PsiElement qualifierExpression = expression.getQualifier();
              if (qualifierExpression instanceof PsiJavaCodeReferenceElement
                  && ((PsiJavaCodeReferenceElement) qualifierExpression).isReferenceTo(aClass)) {
                qualifierExpression.delete();
                if (editor != null) {
                  HighlightManager.getInstance(project)
                      .addRangeHighlight(
                          editor,
                          expression.getTextRange().getStartOffset(),
                          expression.getTextRange().getEndOffset(),
                          EditorColorsManager.getInstance()
                              .getGlobalScheme()
                              .getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES),
                          false,
                          null);
                }
              }

              return true;
            }
          });
    }
  }