// Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). // Licensed under the Apache License, Version 2.0 (see LICENSE). package com.twitter.intellij.pants.components.impl; import com.intellij.ProjectTopics; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootAdapter; import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBusConnection; import com.twitter.intellij.pants.components.PantsProjectCache; import com.twitter.intellij.pants.util.PantsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.java.JavaSourceRootType; import java.util.Comparator; import java.util.TreeSet; public class PantsProjectCacheImpl extends AbstractProjectComponent implements PantsProjectCache, Disposable { private static final Comparator<VirtualFile> VIRTUAL_FILE_COMPARATOR = new Comparator<VirtualFile>() { public int compare(VirtualFile o1, VirtualFile o2) { if (o1.isDirectory() && !o2.isDirectory()) { return -1; } if (o2.isDirectory() && !o1.isDirectory()) { return 1; } return StringUtil.naturalCompare(o1.getPath().toLowerCase(), o2.getPath().toLowerCase()); } }; @NotNull public static PantsProjectCache getInstance(final Project project) { return project.getComponent(PantsProjectCache.class); } private volatile TreeSet<VirtualFile> myProjectRoots = null; protected PantsProjectCacheImpl(Project project) { super(project); } @Override public void projectOpened() { super.projectOpened(); if (myProject.isDefault() || !PantsUtil.isPantsProject(myProject)) { return; } final MessageBusConnection connection = myProject.getMessageBus().connect(this); connection.subscribe( ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { @Override public void rootsChanged(ModuleRootEvent event) { super.rootsChanged(event); myProjectRoots = null; } } ); } @Override public boolean folderContainsSourceRoot(@NotNull VirtualFile file) { if (!file.isDirectory()) { return false; } final TreeSet<VirtualFile> allRoots = getProjectRoots(); // find this file or the next one in natural order final VirtualFile candidate = allRoots.ceiling(file); return candidate != null && VfsUtil.isAncestor(file, candidate, false); } private synchronized TreeSet<VirtualFile> getProjectRoots() { if (myProjectRoots == null) { myProjectRoots = collectRoots(); } return myProjectRoots; } @NotNull private TreeSet<VirtualFile> collectRoots() { final TreeSet<VirtualFile> result = new TreeSet<VirtualFile>(VIRTUAL_FILE_COMPARATOR); final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject); result.addAll(rootManager.getModuleSourceRoots(ContainerUtil.set(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE))); return result; } @Override public void dispose() { myProjectRoots = null; } }