@NotNull
 private Set<VcsRef> readBranches(@NotNull GitRepository repository) {
   StopWatch sw = StopWatch.start("readBranches in " + repository.getRoot().getName());
   VirtualFile root = repository.getRoot();
   repository.update();
   Collection<GitLocalBranch> localBranches = repository.getBranches().getLocalBranches();
   Collection<GitRemoteBranch> remoteBranches = repository.getBranches().getRemoteBranches();
   Set<VcsRef> refs = new THashSet<VcsRef>(localBranches.size() + remoteBranches.size());
   for (GitLocalBranch localBranch : localBranches) {
     refs.add(
         myVcsObjectsFactory.createRef(
             localBranch.getHash(), localBranch.getName(), GitRefManager.LOCAL_BRANCH, root));
   }
   for (GitRemoteBranch remoteBranch : remoteBranches) {
     refs.add(
         myVcsObjectsFactory.createRef(
             remoteBranch.getHash(),
             remoteBranch.getNameForLocalOperations(),
             GitRefManager.REMOTE_BRANCH,
             root));
   }
   String currentRevision = repository.getCurrentRevision();
   if (currentRevision != null) { // null => fresh repository
     refs.add(
         myVcsObjectsFactory.createRef(
             HashImpl.build(currentRevision), "HEAD", GitRefManager.HEAD, root));
   }
   sw.report();
   return refs;
 }
 private void handleBranchingInstruction(
     ProblemsHolder holder,
     StandardInstructionVisitor visitor,
     Set<Instruction> trueSet,
     Set<Instruction> falseSet,
     HashSet<PsiElement> reportedAnchors,
     BranchingInstruction instruction,
     final boolean onTheFly) {
   PsiElement psiAnchor = instruction.getPsiAnchor();
   boolean underBinary = isAtRHSOfBooleanAnd(psiAnchor);
   if (instruction instanceof InstanceofInstruction
       && visitor.isInstanceofRedundant((InstanceofInstruction) instruction)) {
     if (visitor.canBeNull((BinopInstruction) instruction)) {
       holder.registerProblem(
           psiAnchor,
           InspectionsBundle.message("dataflow.message.redundant.instanceof"),
           new RedundantInstanceofFix());
     } else {
       final LocalQuickFix localQuickFix = createSimplifyBooleanExpressionFix(psiAnchor, true);
       holder.registerProblem(
           psiAnchor,
           InspectionsBundle.message(
               underBinary
                   ? "dataflow.message.constant.condition.when.reached"
                   : "dataflow.message.constant.condition",
               Boolean.toString(true)),
           localQuickFix == null ? null : new LocalQuickFix[] {localQuickFix});
     }
   } else if (psiAnchor instanceof PsiSwitchLabelStatement) {
     if (falseSet.contains(instruction)) {
       holder.registerProblem(
           psiAnchor, InspectionsBundle.message("dataflow.message.unreachable.switch.label"));
     }
   } else if (psiAnchor != null
       && !reportedAnchors.contains(psiAnchor)
       && !isFlagCheck(psiAnchor)) {
     boolean evaluatesToTrue = trueSet.contains(instruction);
     final PsiElement parent = psiAnchor.getParent();
     if (parent instanceof PsiAssignmentExpression
         && ((PsiAssignmentExpression) parent).getLExpression() == psiAnchor) {
       holder.registerProblem(
           psiAnchor,
           InspectionsBundle.message(
               "dataflow.message.pointless.assignment.expression",
               Boolean.toString(evaluatesToTrue)),
           createConditionalAssignmentFixes(
               evaluatesToTrue, (PsiAssignmentExpression) parent, onTheFly));
     } else if (!skipReportingConstantCondition(visitor, psiAnchor, evaluatesToTrue)) {
       final LocalQuickFix fix = createSimplifyBooleanExpressionFix(psiAnchor, evaluatesToTrue);
       String message =
           InspectionsBundle.message(
               underBinary
                   ? "dataflow.message.constant.condition.when.reached"
                   : "dataflow.message.constant.condition",
               Boolean.toString(evaluatesToTrue));
       holder.registerProblem(psiAnchor, message, fix == null ? null : new LocalQuickFix[] {fix});
     }
     reportedAnchors.add(psiAnchor);
   }
 }
 /**
  * Gets the array of common ancestors for passed files.
  *
  * @param files array of files
  * @return array of common ancestors for passed files
  */
 @NotNull
 public static VirtualFile[] getCommonAncestors(@NotNull VirtualFile[] files) {
   // Separate files by first component in the path.
   HashMap<VirtualFile, Set<VirtualFile>> map = new HashMap<VirtualFile, Set<VirtualFile>>();
   for (VirtualFile aFile : files) {
     VirtualFile directory = aFile.isDirectory() ? aFile : aFile.getParent();
     if (directory == null) return VirtualFile.EMPTY_ARRAY;
     VirtualFile[] path = getPathComponents(directory);
     Set<VirtualFile> filesSet;
     final VirtualFile firstPart = path[0];
     if (map.containsKey(firstPart)) {
       filesSet = map.get(firstPart);
     } else {
       filesSet = new THashSet<VirtualFile>();
       map.put(firstPart, filesSet);
     }
     filesSet.add(directory);
   }
   // Find common ancestor for each set of files.
   ArrayList<VirtualFile> ancestorsList = new ArrayList<VirtualFile>();
   for (Set<VirtualFile> filesSet : map.values()) {
     VirtualFile ancestor = null;
     for (VirtualFile file : filesSet) {
       if (ancestor == null) {
         ancestor = file;
         continue;
       }
       ancestor = getCommonAncestor(ancestor, file);
       // assertTrue(ancestor != null);
     }
     ancestorsList.add(ancestor);
     filesSet.clear();
   }
   return toVirtualFileArray(ancestorsList);
 }
  private static void reportOptionalOfNullableImprovements(
      ProblemsHolder holder, Set<PsiElement> reportedAnchors, Instruction[] instructions) {
    for (Instruction instruction : instructions) {
      if (instruction instanceof MethodCallInstruction) {
        final PsiExpression[] args = ((MethodCallInstruction) instruction).getArgs();
        if (args.length != 1) continue;

        final PsiExpression expr = args[0];

        if (((MethodCallInstruction) instruction).isOptionalAlwaysNullProblem()) {
          if (!reportedAnchors.add(expr)) continue;
          holder.registerProblem(
              expr,
              "Passing <code>null</code> argument to <code>Optional</code>",
              DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(expr));
        } else if (((MethodCallInstruction) instruction).isOptionalAlwaysNotNullProblem()) {
          if (!reportedAnchors.add(expr)) continue;
          holder.registerProblem(
              expr,
              "Passing a non-null argument to <code>Optional</code>",
              DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix());
        }
      }
    }
  }
 private void updateRightTreeModel() {
   Set<PsiFile> deps = new HashSet<PsiFile>();
   Set<PsiFile> scope = getSelectedScope(myLeftTree);
   myIllegalsInRightTree = new HashSet<PsiFile>();
   for (PsiFile psiFile : scope) {
     Map<DependencyRule, Set<PsiFile>> illegalDeps = myIllegalDependencies.get(psiFile);
     if (illegalDeps != null) {
       for (final DependencyRule rule : illegalDeps.keySet()) {
         myIllegalsInRightTree.addAll(illegalDeps.get(rule));
       }
     }
     final Set<PsiFile> psiFiles = myDependencies.get(psiFile);
     if (psiFiles != null) {
       for (PsiFile file : psiFiles) {
         if (file != null && file.isValid()) {
           deps.add(file);
         }
       }
     }
   }
   deps.removeAll(scope);
   myRightTreeExpansionMonitor.freeze();
   myRightTree.setModel(buildTreeModel(deps, myRightTreeMarker));
   myRightTreeExpansionMonitor.restore();
   expandFirstLevel(myRightTree);
 }
 @NotNull
 private static <T> Set<T> remove(@NotNull Set<T> original, @NotNull Set<T>... toRemove) {
   Set<T> result = newHashSet(original);
   for (Set<T> set : toRemove) {
     result.removeAll(set);
   }
   return result;
 }
 @Override
 @NotNull
 public Document[] getUncommittedDocuments() {
   ApplicationManager.getApplication().assertIsDispatchThread();
   Document[] documents =
       myUncommittedDocuments.toArray(new Document[myUncommittedDocuments.size()]);
   return ArrayUtil.stripTrailingNulls(documents);
 }
 private static void addOldStillExistingTags(
     @NotNull Set<VcsRef> allRefs,
     @NotNull Set<String> currentTags,
     @NotNull Set<VcsRef> previousRefs) {
   for (VcsRef ref : previousRefs) {
     if (!allRefs.contains(ref) && currentTags.contains(ref.getName())) {
       allRefs.add(ref);
     }
   }
 }
 @Nullable
 public static Set<PsiType> getExpectedTypes(final CompletionParameters parameters) {
   final PsiExpression expr =
       PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
   if (expr != null) {
     final Set<PsiType> set = new THashSet<PsiType>();
     for (final ExpectedTypeInfo expectedInfo :
         JavaSmartCompletionContributor.getExpectedTypes(parameters)) {
       set.add(expectedInfo.getType());
     }
     return set;
   }
   return null;
 }
 private static void reportUnboxedNullables(
     DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
   for (PsiElement expr : visitor.getProblems(NullabilityProblem.unboxingNullable)) {
     if (!reportedAnchors.add(expr)) continue;
     holder.registerProblem(expr, InspectionsBundle.message("dataflow.message.unboxing"));
   }
 }
  private void reportNullableReturns(
      DataFlowInstructionVisitor visitor,
      ProblemsHolder holder,
      Set<PsiElement> reportedAnchors,
      @NotNull PsiElement block) {
    final PsiMethod method = getScopeMethod(block);
    if (method == null || NullableStuffInspectionBase.isNullableNotInferred(method, true)) return;

    boolean notNullRequired = NullableNotNullManager.isNotNull(method);
    if (!notNullRequired && !SUGGEST_NULLABLE_ANNOTATIONS) return;

    PsiType returnType = method.getReturnType();
    // no warnings in void lambdas, where the expression is not returned anyway
    if (block instanceof PsiExpression
        && block.getParent() instanceof PsiLambdaExpression
        && returnType == PsiType.VOID) return;

    // no warnings for Void methods, where only null can be possibly returned
    if (returnType == null || returnType.equalsToText(CommonClassNames.JAVA_LANG_VOID)) return;

    for (PsiElement statement : visitor.getProblems(NullabilityProblem.nullableReturn)) {
      assert statement instanceof PsiExpression;
      final PsiExpression expr = (PsiExpression) statement;
      if (!reportedAnchors.add(expr)) continue;

      if (notNullRequired) {
        final String text =
            isNullLiteralExpression(expr)
                ? InspectionsBundle.message("dataflow.message.return.null.from.notnull")
                : InspectionsBundle.message("dataflow.message.return.nullable.from.notnull");
        holder.registerProblem(expr, text);
      } else if (AnnotationUtil.isAnnotatingApplicable(statement)) {
        final NullableNotNullManager manager =
            NullableNotNullManager.getInstance(expr.getProject());
        final String defaultNullable = manager.getDefaultNullable();
        final String presentableNullable = StringUtil.getShortName(defaultNullable);
        final String text =
            isNullLiteralExpression(expr)
                ? InspectionsBundle.message(
                    "dataflow.message.return.null.from.notnullable", presentableNullable)
                : InspectionsBundle.message(
                    "dataflow.message.return.nullable.from.notnullable", presentableNullable);
        final LocalQuickFix[] fixes =
            PsiTreeUtil.getParentOfType(expr, PsiMethod.class, PsiLambdaExpression.class)
                    instanceof PsiLambdaExpression
                ? LocalQuickFix.EMPTY_ARRAY
                : new LocalQuickFix[] {
                  new AnnotateMethodFix(
                      defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls())) {
                    @Override
                    public int shouldAnnotateBaseMethod(
                        PsiMethod method, PsiMethod superMethod, Project project) {
                      return 1;
                    }
                  }
                };
        holder.registerProblem(expr, text, fixes);
      }
    }
  }
Esempio n. 12
0
 public static void cleanupDeleteOnExitHookList()
     throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
   // try to reduce file set retained by java.io.DeleteOnExitHook
   List<String> list;
   synchronized (DELETE_ON_EXIT_HOOK_CLASS) {
     if (DELETE_ON_EXIT_HOOK_DOT_FILES.isEmpty()) return;
     list = new ArrayList<String>(DELETE_ON_EXIT_HOOK_DOT_FILES);
   }
   for (int i = list.size() - 1; i >= 0; i--) {
     String path = list.get(i);
     if (FileSystemUtil.getAttributes(path) == null || new File(path).delete()) {
       synchronized (DELETE_ON_EXIT_HOOK_CLASS) {
         DELETE_ON_EXIT_HOOK_DOT_FILES.remove(path);
       }
     }
   }
 }
Esempio n. 13
0
  @NotNull
  private static List<VFileEvent> validateEvents(@NotNull List<VFileEvent> events) {
    final List<EventWrapper> deletionEvents = ContainerUtil.newArrayList();
    for (int i = 0, size = events.size(); i < size; i++) {
      final VFileEvent event = events.get(i);
      if (event instanceof VFileDeleteEvent && event.isValid()) {
        deletionEvents.add(new EventWrapper((VFileDeleteEvent) event, i));
      }
    }

    final TIntHashSet invalidIDs;
    if (deletionEvents.isEmpty()) {
      invalidIDs = EmptyIntHashSet.INSTANCE;
    } else {
      ContainerUtil.quickSort(deletionEvents, DEPTH_COMPARATOR);

      invalidIDs = new TIntHashSet(deletionEvents.size());
      final Set<VirtualFile> dirsToBeDeleted = new THashSet<VirtualFile>(deletionEvents.size());
      nextEvent:
      for (EventWrapper wrapper : deletionEvents) {
        final VirtualFile candidate = wrapper.event.getFile();
        VirtualFile parent = candidate;
        while (parent != null) {
          if (dirsToBeDeleted.contains(parent)) {
            invalidIDs.add(wrapper.id);
            continue nextEvent;
          }
          parent = parent.getParent();
        }

        if (candidate.isDirectory()) {
          dirsToBeDeleted.add(candidate);
        }
      }
    }

    final List<VFileEvent> filtered = new ArrayList<VFileEvent>(events.size() - invalidIDs.size());
    for (int i = 0, size = events.size(); i < size; i++) {
      final VFileEvent event = events.get(i);
      if (event.isValid() && !(event instanceof VFileDeleteEvent && invalidIDs.contains(i))) {
        filtered.add(event);
      }
    }
    return filtered;
  }
  @TestOnly
  private static void assertAccessInTests(
      @NotNull VirtualFileSystemEntry child, @NotNull NewVirtualFileSystem delegate) {
    final Application application = ApplicationManager.getApplication();
    if (IS_UNDER_TEAMCITY
        && SHOULD_PERFORM_ACCESS_CHECK
        && application.isUnitTestMode()
        && application instanceof ApplicationImpl
        && ((ApplicationImpl) application).isComponentsCreated()) {
      if (delegate != LocalFileSystem.getInstance() && delegate != JarFileSystem.getInstance()) {
        return;
      }
      // root' children are loaded always
      if (child.getParent() == null || child.getParent().getParent() == null) return;

      Set<String> allowed = allowedRoots();
      boolean isUnder = allowed == null;
      if (!isUnder) {
        String childPath = child.getPath();
        if (delegate == JarFileSystem.getInstance()) {
          VirtualFile local = JarFileSystem.getInstance().getVirtualFileForJar(child);
          assert local != null : child;
          childPath = local.getPath();
        }
        for (String root : allowed) {
          if (FileUtil.startsWith(childPath, root)) {
            isUnder = true;
            break;
          }
          if (root.startsWith(JarFileSystem.PROTOCOL_PREFIX)) {
            String rootLocalPath =
                FileUtil.toSystemIndependentName(PathUtil.toPresentableUrl(root));
            isUnder = FileUtil.startsWith(childPath, rootLocalPath);
            if (isUnder) break;
          }
        }
      }

      assert isUnder || allowed.isEmpty()
          : "File accessed outside allowed roots: "
              + child
              + ";\nAllowed roots: "
              + new ArrayList<String>(allowed);
    }
  }
 public static Set<String> getAllLookupStrings(@NotNull PsiMember member) {
   Set<String> allLookupStrings = ContainerUtil.newLinkedHashSet();
   String name = member.getName();
   allLookupStrings.add(name);
   PsiClass containingClass = member.getContainingClass();
   while (containingClass != null) {
     final String className = containingClass.getName();
     if (className == null) {
       break;
     }
     name = className + "." + name;
     allLookupStrings.add(name);
     final PsiElement parent = containingClass.getParent();
     if (!(parent instanceof PsiClass)) {
       break;
     }
     containingClass = (PsiClass) parent;
   }
   return allLookupStrings;
 }
  @Override
  public void commitAllDocuments() {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (myUncommittedDocuments.isEmpty()) return;

    final Document[] documents = getUncommittedDocuments();
    for (Document document : documents) {
      commitDocument(document);
    }

    LOG.assertTrue(!hasUncommitedDocuments(), myUncommittedDocuments);
  }
  private static void reportNullableAssignments(
      DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
    for (PsiElement expr : visitor.getProblems(NullabilityProblem.assigningToNotNull)) {
      if (!reportedAnchors.add(expr)) continue;

      final String text =
          isNullLiteralExpression(expr)
              ? InspectionsBundle.message("dataflow.message.assigning.null")
              : InspectionsBundle.message("dataflow.message.assigning.nullable");
      holder.registerProblem(expr, text);
    }
  }
  private void reportNullableArgumentsPassedToNonAnnotated(
      DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
    for (PsiElement expr :
        visitor.getProblems(NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter)) {
      if (reportedAnchors.contains(expr)) continue;

      final String text =
          isNullLiteralExpression(expr)
              ? "Passing <code>null</code> argument to non annotated parameter"
              : "Argument <code>#ref</code> #loc might be null but passed to non annotated parameter";
      LocalQuickFix[] fixes =
          createNPEFixes((PsiExpression) expr, (PsiExpression) expr, holder.isOnTheFly());
      final PsiElement parent = expr.getParent();
      if (parent instanceof PsiExpressionList) {
        final int idx = ArrayUtilRt.find(((PsiExpressionList) parent).getExpressions(), expr);
        if (idx > -1) {
          final PsiElement gParent = parent.getParent();
          if (gParent instanceof PsiCallExpression) {
            final PsiMethod psiMethod = ((PsiCallExpression) gParent).resolveMethod();
            if (psiMethod != null
                && psiMethod.getManager().isInProject(psiMethod)
                && AnnotationUtil.isAnnotatingApplicable(psiMethod)) {
              final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
              if (idx < parameters.length) {
                final AddNullableAnnotationFix addNullableAnnotationFix =
                    new AddNullableAnnotationFix(parameters[idx]);
                fixes =
                    fixes == null
                        ? new LocalQuickFix[] {addNullableAnnotationFix}
                        : ArrayUtil.append(fixes, addNullableAnnotationFix);
                holder.registerProblem(expr, text, fixes);
                reportedAnchors.add(expr);
              }
            }
          }
        }
      }
    }
  }
 @Nullable
 private AnalysisScope getScope() {
   final Set<PsiFile> selectedScope = getSelectedScope(myRightTree);
   Set<PsiFile> result = new HashSet<PsiFile>();
   ((PackageDependenciesNode) myLeftTree.getModel().getRoot())
       .fillFiles(result, !mySettings.UI_FLATTEN_PACKAGES);
   selectedScope.removeAll(result);
   if (selectedScope.isEmpty()) return null;
   List<VirtualFile> files = new ArrayList<VirtualFile>();
   final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
   for (PsiFile psiFile : selectedScope) {
     final VirtualFile file = psiFile.getVirtualFile();
     LOG.assertTrue(file != null);
     if (fileIndex.isInContent(file)) {
       files.add(file);
     }
   }
   if (!files.isEmpty()) {
     return new AnalysisScope(myProject, files);
   }
   return null;
 }
  @NotNull
  private FSRecords.NameId[] persistAllChildren(
      @NotNull final VirtualFile file, final int id, @NotNull FSRecords.NameId[] current) {
    assert file != mySuperRoot;
    final NewVirtualFileSystem fs = replaceWithNativeFS(getDelegate(file));

    String[] delegateNames = VfsUtil.filterNames(fs.list(file));
    if (delegateNames.length == 0 && current.length > 0) {
      return current;
    }

    Set<String> toAdd = ContainerUtil.newHashSet(delegateNames);
    for (FSRecords.NameId nameId : current) {
      toAdd.remove(nameId.name);
    }

    final TIntArrayList childrenIds = new TIntArrayList(current.length + toAdd.size());
    final List<FSRecords.NameId> nameIds =
        ContainerUtil.newArrayListWithExpectedSize(current.length + toAdd.size());
    for (FSRecords.NameId nameId : current) {
      childrenIds.add(nameId.id);
      nameIds.add(nameId);
    }
    for (String newName : toAdd) {
      FakeVirtualFile child = new FakeVirtualFile(file, newName);
      FileAttributes attributes = fs.getAttributes(child);
      if (attributes != null) {
        int childId = createAndFillRecord(fs, child, id, attributes);
        childrenIds.add(childId);
        nameIds.add(new FSRecords.NameId(childId, FileNameCache.storeName(newName), newName));
      }
    }

    FSRecords.updateList(id, childrenIds.toNativeArray());
    setChildrenCached(id);

    return nameIds.toArray(new FSRecords.NameId[nameIds.size()]);
  }
  private void reportNullableArguments(
      DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
    for (PsiElement expr :
        visitor.getProblems(NullabilityProblem.passingNullableToNotNullParameter)) {
      if (!reportedAnchors.add(expr)) continue;

      final String text =
          isNullLiteralExpression(expr)
              ? InspectionsBundle.message("dataflow.message.passing.null.argument")
              : InspectionsBundle.message("dataflow.message.passing.nullable.argument");
      LocalQuickFix[] fixes =
          createNPEFixes((PsiExpression) expr, (PsiExpression) expr, holder.isOnTheFly());
      holder.registerProblem(expr, text, fixes);
    }
  }
 /**
  * Cancel previously registered action and schedules (new) action to be executed when all
  * documents are committed.
  *
  * @param key the (unique) id of the action.
  * @param action The action to be executed after automatic commit. This action will overwrite any
  *     action which was registered under this key earlier. The action will be executed in EDT.
  * @return true if action has been run immediately, or false if action was scheduled for execution
  *     later.
  */
 public boolean cancelAndRunWhenAllCommitted(
     @NonNls @NotNull Object key, @NotNull final Runnable action) {
   ApplicationManager.getApplication().assertIsDispatchThread();
   if (myProject.isDisposed()) {
     action.run();
     return true;
   }
   if (myUncommittedDocuments.isEmpty()) {
     action.run();
     if (!hasUncommitedDocuments()) {
       assert actionsWhenAllDocumentsAreCommitted.isEmpty() : actionsWhenAllDocumentsAreCommitted;
     }
     return true;
   }
   actionsWhenAllDocumentsAreCommitted.put(key, action);
   return false;
 }
  void handleCommitWithoutPsi(@NotNull Document document) {
    final Pair<CharSequence, Long> prevPair = myLastCommittedTexts.remove(document);
    if (prevPair == null) {
      return;
    }

    if (!myProject.isInitialized() || myProject.isDisposed()) {
      return;
    }

    myUncommittedDocuments.remove(document);

    VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
    if (virtualFile == null || !FileIndexFacade.getInstance(myProject).isInContent(virtualFile)) {
      return;
    }

    final PsiFile psiFile = getPsiFile(document);
    if (psiFile == null) {
      return;
    }

    // we can end up outside write action here if the document has forUseInNonAWTThread=true
    ApplicationManager.getApplication()
        .runWriteAction(
            new ExternalChangeAction() {
              @Override
              public void run() {
                psiFile.getViewProvider().beforeContentsSynchronized();
                synchronized (PsiLock.LOCK) {
                  final int oldLength = prevPair.first.length();
                  PsiManagerImpl manager = (PsiManagerImpl) psiFile.getManager();
                  BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, true);
                  BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, false);
                  if (psiFile instanceof PsiFileImpl) {
                    ((PsiFileImpl) psiFile).onContentReload();
                  }
                  BlockSupportImpl.sendAfterChildrenChangedEvent(
                      manager, psiFile, oldLength, false);
                  BlockSupportImpl.sendAfterChildrenChangedEvent(manager, psiFile, oldLength, true);
                }
                psiFile.getViewProvider().contentsSynchronized();
              }
            });
  }
 /**
  * Schedules action to be executed when all documents are committed.
  *
  * @return true if action has been run immediately, or false if action was scheduled for execution
  *     later.
  */
 @Override
 public boolean performWhenAllCommitted(@NotNull final Runnable action) {
   ApplicationManager.getApplication().assertIsDispatchThread();
   assert !myProject.isDisposed() : "Already disposed: " + myProject;
   if (myUncommittedDocuments.isEmpty()) {
     action.run();
     return true;
   }
   CompositeRunnable actions =
       (CompositeRunnable) actionsWhenAllDocumentsAreCommitted.get(PERFORM_ALWAYS_KEY);
   if (actions == null) {
     actions = new CompositeRunnable();
     actionsWhenAllDocumentsAreCommitted.put(PERFORM_ALWAYS_KEY, actions);
   }
   actions.add(action);
   myDocumentCommitProcessor.log(
       "PDI: added performWhenAllCommitted", null, false, action, myUncommittedDocuments);
   return false;
 }
  protected boolean finishCommitInWriteAction(
      @NotNull final Document document,
      @NotNull final List<Processor<Document>> finishProcessors,
      final boolean synchronously) {
    if (myProject.isDisposed()) return false;
    assert !(document instanceof DocumentWindow);
    myIsCommitInProgress = true;
    boolean success = true;
    try {
      final FileViewProvider viewProvider = getCachedViewProvider(document);
      if (viewProvider != null) {
        for (Processor<Document> finishRunnable : finishProcessors) {
          success = finishRunnable.process(document);
          if (synchronously) {
            assert success : finishRunnable + " in " + finishProcessors;
          }
          if (!success) {
            break;
          }
        }
        if (success) {
          myLastCommittedTexts.remove(document);
          viewProvider.contentsSynchronized();
        }
      } else {
        handleCommitWithoutPsi(document);
      }
    } finally {
      myDocumentCommitProcessor.log(
          "in PDI.finishDoc: ", null, synchronously, success, myUncommittedDocuments);
      if (success) {
        myUncommittedDocuments.remove(document);
        myDocumentCommitProcessor.log(
            "in PDI.finishDoc: removed doc", null, synchronously, success, myUncommittedDocuments);
      }
      myIsCommitInProgress = false;
      myDocumentCommitProcessor.log(
          "in PDI.finishDoc: exit", null, synchronously, success, myUncommittedDocuments);
    }

    return success;
  }
  // null means we were unable to get roots, so do not check access
  @Nullable
  @TestOnly
  private static Set<String> allowedRoots() {
    if (insideGettingRoots) return null;

    Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
    if (openProjects.length == 0) return null;

    final Set<String> allowed = new THashSet<String>();
    allowed.add(FileUtil.toSystemIndependentName(PathManager.getHomePath()));

    try {
      URL outUrl = Application.class.getResource("/");
      String output = new File(outUrl.toURI()).getParentFile().getParentFile().getPath();
      allowed.add(FileUtil.toSystemIndependentName(output));
    } catch (URISyntaxException ignored) {
    }

    allowed.add(FileUtil.toSystemIndependentName(SystemProperties.getJavaHome()));
    allowed.add(
        FileUtil.toSystemIndependentName(new File(FileUtil.getTempDirectory()).getParent()));
    allowed.add(FileUtil.toSystemIndependentName(System.getProperty("java.io.tmpdir")));
    allowed.add(FileUtil.toSystemIndependentName(SystemProperties.getUserHome()));

    for (Project project : openProjects) {
      if (!project.isInitialized()) {
        return null; // all is allowed
      }
      for (VirtualFile root : ProjectRootManager.getInstance(project).getContentRoots()) {
        allowed.add(root.getPath());
      }
      for (VirtualFile root : getAllRoots(project)) {
        allowed.add(StringUtil.trimEnd(root.getPath(), JarFileSystem.JAR_SEPARATOR));
      }
      String location = project.getBasePath();
      assert location != null : project;
      allowed.add(FileUtil.toSystemIndependentName(location));
    }

    allowed.addAll(ourAdditionalRoots);

    return allowed;
  }
 @Override
 public boolean hasUncommitedDocuments() {
   return !myIsCommitInProgress && !myUncommittedDocuments.isEmpty();
 }
  @NotNull
  @Override
  public DetailedLogData readFirstBlock(
      @NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException {
    if (!isRepositoryReady(root)) {
      return LogDataImpl.empty();
    }
    GitRepository repository =
        ObjectUtils.assertNotNull(myRepositoryManager.getRepositoryForRoot(root));

    // need to query more to sort them manually; this doesn't affect performance: it is equal for
    // -1000 and -2000
    int commitCount = requirements.getCommitCount() * 2;

    String[] params =
        new String[] {"HEAD", "--branches", "--remotes", "--max-count=" + commitCount};
    // NB: not specifying --tags, because it introduces great slowdown if there are many tags,
    // but makes sense only if there are heads without branch or HEAD labels (rare case). Such cases
    // are partially handled below.

    boolean refresh =
        requirements instanceof VcsLogProviderRequirementsEx
            && ((VcsLogProviderRequirementsEx) requirements).isRefresh();

    DetailedLogData data = GitHistoryUtils.loadMetadata(myProject, root, true, params);

    Set<VcsRef> safeRefs = data.getRefs();
    Set<VcsRef> allRefs = new OpenTHashSet<VcsRef>(safeRefs, DONT_CONSIDER_SHA);
    Set<VcsRef> branches = readBranches(repository);
    addNewElements(allRefs, branches);

    Collection<VcsCommitMetadata> allDetails;
    Set<String> currentTagNames = null;
    DetailedLogData commitsFromTags = null;
    if (!refresh) {
      allDetails = data.getCommits();
    } else {
      // on refresh: get new tags, which point to commits not from the first block; then get
      // history, walking down just from these tags
      // on init: just ignore such tagged-only branches. The price for speed-up.
      VcsLogProviderRequirementsEx rex = (VcsLogProviderRequirementsEx) requirements;

      currentTagNames = readCurrentTagNames(root);
      addOldStillExistingTags(allRefs, currentTagNames, rex.getPreviousRefs());

      allDetails = newHashSet(data.getCommits());

      Set<String> previousTags =
          newHashSet(ContainerUtil.mapNotNull(rex.getPreviousRefs(), GET_TAG_NAME));
      Set<String> safeTags = newHashSet(ContainerUtil.mapNotNull(safeRefs, GET_TAG_NAME));
      Set<String> newUnmatchedTags = remove(currentTagNames, previousTags, safeTags);

      if (!newUnmatchedTags.isEmpty()) {
        commitsFromTags = loadSomeCommitsOnTaggedBranches(root, commitCount, newUnmatchedTags);
        addNewElements(allDetails, commitsFromTags.getCommits());
        addNewElements(allRefs, commitsFromTags.getRefs());
      }
    }

    StopWatch sw = StopWatch.start("sorting commits in " + root.getName());
    List<VcsCommitMetadata> sortedCommits = VcsLogSorter.sortByDateTopoOrder(allDetails);
    sortedCommits =
        sortedCommits.subList(0, Math.min(sortedCommits.size(), requirements.getCommitCount()));
    sw.report();

    if (LOG.isDebugEnabled()) {
      validateDataAndReportError(
          root, allRefs, sortedCommits, data, branches, currentTagNames, commitsFromTags);
    }

    return new LogDataImpl(allRefs, sortedCommits);
  }
  @Override
  public void documentChanged(DocumentEvent event) {
    if (myStopTrackingDocuments) return;

    final Document document = event.getDocument();
    VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
    boolean isRelevant = virtualFile != null && isRelevant(virtualFile);

    final FileViewProvider viewProvider = getCachedViewProvider(document);
    if (viewProvider == null) {
      handleCommitWithoutPsi(document);
      return;
    }
    boolean inMyProject = viewProvider.getManager() == myPsiManager;
    if (!isRelevant || !inMyProject) {
      myLastCommittedTexts.remove(document);
      return;
    }

    ApplicationManager.getApplication().assertWriteAccessAllowed();
    final List<PsiFile> files = viewProvider.getAllFiles();
    boolean commitNecessary = true;
    for (PsiFile file : files) {

      if (mySynchronizer.isInsideAtomicChange(file)) {
        commitNecessary = false;
        continue;
      }

      assert file instanceof PsiFileImpl
              || "mock.file".equals(file.getName())
                  && ApplicationManager.getApplication().isUnitTestMode()
          : event + "; file=" + file + "; allFiles=" + files + "; viewProvider=" + viewProvider;
    }

    boolean forceCommit =
        ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction.class)
            && (SystemProperties.getBooleanProperty("idea.force.commit.on.external.change", false)
                || ApplicationManager.getApplication().isHeadlessEnvironment()
                    && !ApplicationManager.getApplication().isUnitTestMode());

    // Consider that it's worth to perform complete re-parse instead of merge if the whole document
    // text is replaced and
    // current document lines number is roughly above 5000. This makes sense in situations when
    // external change is performed
    // for the huge file (that causes the whole document to be reloaded and 'merge' way takes a
    // while to complete).
    if (event.isWholeTextReplaced() && document.getTextLength() > 100000) {
      document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE);
    }

    if (commitNecessary) {
      assert !(document instanceof DocumentWindow);
      myUncommittedDocuments.add(document);
      myDocumentCommitProcessor.log(
          "added uncommitted doc",
          null,
          false,
          myProject,
          document,
          ((DocumentEx) document).isInBulkUpdate());
      if (forceCommit) {
        commitDocument(document);
      } else if (!((DocumentEx) document).isInBulkUpdate() && myPerformBackgroundCommit) {
        myDocumentCommitProcessor.commitAsynchronously(myProject, document, event);
      }
    } else {
      myLastCommittedTexts.remove(document);
    }
  }
 @TestOnly
 public void clearUncommittedDocuments() {
   myLastCommittedTexts.clear();
   myUncommittedDocuments.clear();
   mySynchronizer.cleanupForNextTest();
 }