@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); } } }
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); } } } }
@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(); }