From 3d9dc3938c7a80b5185238dd8ad7f1d6f259b3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=BBarnowski?= Date: Thu, 30 Apr 2020 19:30:57 +0200 Subject: [PATCH] Replace some deprecated components with listeners/services (#524) This migrates some of the existing project components to a more modern (and recommended) service/listener architecture. Also, some other minor deprecations were fixed along the way --- .../twitter/intellij/pants/PantsBundle.java | 4 +- .../intellij/pants/util/PantsUtil.java | 34 +++---- resources/META-INF/pants-python.xml | 10 +- resources/META-INF/plugin.xml | 5 +- .../pants/components/PantsProjectCache.java | 90 ++++++++++++++++- .../impl/PantsProjectCacheImpl.java | 98 ------------------- .../impl/PantsProjectComponentImpl.java | 20 ++-- .../pants/file/FileChangeTracker.java | 4 +- .../PantsProjectPaneSelectInTarget.java | 5 - .../PantsTreeStructureProvider.java | 84 +++++++++------- .../projectview/ProjectFilesViewPane.java | 15 ++- .../projectview/VirtualFileTreeNode.java | 5 +- .../PantsPythonProjectComponent.java | 9 -- .../PantsPythonRunConfigListener.java | 40 ++++++++ .../impl/PantsPythonProjectComponentImpl.java | 48 --------- .../pants/settings/PantsSettings.java | 3 +- .../impl/PantsProjectCacheTest.java | 31 +++--- ...ntsJvmRunConfigurationIntegrationTest.java | 7 +- ...PythonRunConfigurationIntegrationTest.java | 3 +- 19 files changed, 234 insertions(+), 281 deletions(-) delete mode 100644 src/com/twitter/intellij/pants/components/impl/PantsProjectCacheImpl.java delete mode 100644 src/com/twitter/intellij/pants/service/python/component/PantsPythonProjectComponent.java create mode 100644 src/com/twitter/intellij/pants/service/python/component/PantsPythonRunConfigListener.java delete mode 100644 src/com/twitter/intellij/pants/service/python/component/impl/PantsPythonProjectComponentImpl.java diff --git a/common/com/twitter/intellij/pants/PantsBundle.java b/common/com/twitter/intellij/pants/PantsBundle.java index 6b57963eb..3156308fe 100644 --- a/common/com/twitter/intellij/pants/PantsBundle.java +++ b/common/com/twitter/intellij/pants/PantsBundle.java @@ -3,7 +3,7 @@ package com.twitter.intellij.pants; -import com.intellij.CommonBundle; +import com.intellij.AbstractBundle; import com.intellij.reference.SoftReference; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.PropertyKey; @@ -18,7 +18,7 @@ public class PantsBundle { private static final String BUNDLE = "com.twitter.intellij.pants.PantsBundle"; public static String message(@PropertyKey(resourceBundle = BUNDLE) String key, Object... params) { - return CommonBundle.message(getBundle(), key, params); + return AbstractBundle.message(getBundle(), key, params); } private static ResourceBundle getBundle() { diff --git a/common/com/twitter/intellij/pants/util/PantsUtil.java b/common/com/twitter/intellij/pants/util/PantsUtil.java index 8299b562a..e7199feda 100644 --- a/common/com/twitter/intellij/pants/util/PantsUtil.java +++ b/common/com/twitter/intellij/pants/util/PantsUtil.java @@ -14,7 +14,7 @@ import com.intellij.execution.process.ProcessOutput; import com.intellij.ide.SaveAndSyncHandler; import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -24,16 +24,11 @@ import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager; -import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.ExternalSystemException; import com.intellij.openapi.externalSystem.model.Key; import com.intellij.openapi.externalSystem.model.ProjectSystemId; -import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode; -import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; -import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; -import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.Module; @@ -86,6 +81,7 @@ import java.lang.reflect.Type; import java.nio.charset.Charset; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -308,7 +304,8 @@ public static Optional findBuildRoot(@NotNull Module module) { Optional fromModuleFile = Optional.ofNullable(moduleFile).flatMap(PantsUtil::findBuildRoot); if (fromModuleFile.isPresent()) { return fromModuleFile; - } else { + } + else { final ModuleRootManager rootManager = ModuleRootManager.getInstance(module); for (VirtualFile contentRoot : rootManager.getContentRoots()) { final Optional buildRoot = findBuildRoot(contentRoot); @@ -538,7 +535,7 @@ public static boolean isPantsModule(@NotNull Module module) { @NotNull public static PantsSourceType getSourceTypeForTargetType(@Nullable String targetType, Boolean isSynthetic) { try { - if(isSynthetic && targetType != null) { + if (isSynthetic && targetType != null) { return PantsSourceType.SOURCE_GENERATED; } return targetType == null ? PantsSourceType.SOURCE : @@ -554,7 +551,9 @@ public static boolean isResource(PantsSourceType sourceType) { return sourceType == PantsSourceType.RESOURCE || sourceType == PantsSourceType.TEST_RESOURCE; } - public static Optional findModuleAddress(@NotNull Module module) { + public static Optional findModuleAddress(@Nullable Module module) { + if (module == null) return Optional.empty(); + ExternalSystemModulePropertyManager externalSystemModulePropertyManager = ExternalSystemModulePropertyManager.getInstance(module); String path = externalSystemModulePropertyManager.getLinkedProjectPath(); if (path == null) { @@ -615,7 +614,8 @@ public static Optional getRelativeProjectPath(@NotNull File workDirector public static void refreshAllProjects(@NotNull Project project) { if (isPantsProject(project) || isSeedPantsProject(project)) { ExternalProjectUtil.refresh(project, PantsConstants.SYSTEM_ID); - } else if (isBspProject(project)) { + } + else if (isBspProject(project)) { ExternalProjectUtil.refresh(project, ProjectSystemId.findById("BSP")); } } @@ -716,7 +716,7 @@ public static ProcessOutput getCmdOutput( public static boolean isPythonAvailable() { for (String pluginId : PYTHON_PLUGIN_IDS) { - final IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(pluginId)); + final IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(PluginId.getId(pluginId)); if (plugin != null && plugin.isEnabled()) { return true; } @@ -903,10 +903,7 @@ public static Optional findPantsExecutable(@NotNull String projectP public static Optional findPantsExecutable(@NotNull File file) { Optional vf = findPantsExecutable(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file)); - if (!vf.isPresent()) { - return Optional.empty(); - } - return Optional.of(new File(vf.get().getPath())); + return vf.map(virtualFile -> new File(virtualFile.getPath())); } private static Optional findPantsExecutable(@Nullable VirtualFile file) { @@ -961,11 +958,8 @@ public static void invalidatePluginCaches() { SimpleExportResult.clearCache(); } - /** - * Copy from {@link ExternalSystemExecuteTaskTask#parseCmdParameters} because it is private. - */ - public static List parseCmdParameters(Optional cmdArgsLine) { - return cmdArgsLine.map(ParametersListUtil::parse).orElse(ContainerUtil.newArrayList()); + public static List parseCmdParameters(@Nullable String cmdArgsLine) { + return Optional.ofNullable(cmdArgsLine).map(ParametersListUtil::parse).orElse(new ArrayList<>()); } public static void invokeLaterIfNeeded(Runnable task) { diff --git a/resources/META-INF/pants-python.xml b/resources/META-INF/pants-python.xml index 973a9a4bf..fabb59f47 100644 --- a/resources/META-INF/pants-python.xml +++ b/resources/META-INF/pants-python.xml @@ -2,12 +2,10 @@ - - - com.twitter.intellij.pants.service.python.component.PantsPythonProjectComponent - com.twitter.intellij.pants.service.python.component.impl.PantsPythonProjectComponentImpl - - + + + diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index f3fd35917..6fbd22063 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -37,10 +37,6 @@ com.twitter.intellij.pants.components.PantsProjectComponent com.twitter.intellij.pants.components.impl.PantsProjectComponentImpl - - com.twitter.intellij.pants.components.PantsProjectCache - com.twitter.intellij.pants.components.impl.PantsProjectCacheImpl - @@ -167,6 +163,7 @@ conditionClass="com.twitter.intellij.pants.ui.PantsToolWindowFactoryCondition"/> + VIRTUAL_FILE_COMPARATOR = (o1, o2) -> { + if (o1.isDirectory() && !o2.isDirectory()) { + return -1; + } + if (o2.isDirectory() && !o1.isDirectory()) { + return 1; + } + + return StringUtil.naturalCompare(o1.getPath().toLowerCase(), o2.getPath().toLowerCase()); + }; + + + private final Project myProject; + private volatile TreeSet myProjectRoots = null; + + public PantsProjectCache(Project project) { + myProject = project; + listenForRootsChange(); + } + + public static PantsProjectCache getInstance(@NotNull Project project) { + return ServiceManager.getService(project, PantsProjectCache.class); + } + + public boolean folderContainsSourceRoot(@NotNull VirtualFile file) { + if (!file.isDirectory()) { + return false; + } + final TreeSet 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 getProjectRoots() { + if (myProjectRoots == null) { + myProjectRoots = collectRoots(); + } + return myProjectRoots; + } + + @NotNull + private TreeSet collectRoots() { + final TreeSet result = new TreeSet<>(VIRTUAL_FILE_COMPARATOR); + final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject); + result.addAll(rootManager.getModuleSourceRoots(ContainerUtil.set(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE))); + return result; + } + + private void listenForRootsChange() { + if (myProject.isDefault() || !PantsUtil.isPantsProject(myProject)) { + return; + } + final MessageBusConnection connection = myProject.getMessageBus().connect(this); + connection.subscribe( + ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() { + @Override + public void rootsChanged(@NotNull ModuleRootEvent event) { + myProjectRoots = null; + } + } + ); + } + + @Override + public void dispose() { + myProjectRoots = null; + } } diff --git a/src/com/twitter/intellij/pants/components/impl/PantsProjectCacheImpl.java b/src/com/twitter/intellij/pants/components/impl/PantsProjectCacheImpl.java deleted file mode 100644 index 3cce80d9e..000000000 --- a/src/com/twitter/intellij/pants/components/impl/PantsProjectCacheImpl.java +++ /dev/null @@ -1,98 +0,0 @@ -// 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.ModuleRootEvent; -import com.intellij.openapi.roots.ModuleRootListener; -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 VIRTUAL_FILE_COMPARATOR = new Comparator() { - 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 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 ModuleRootListener() { - @Override - public void rootsChanged(ModuleRootEvent event) { - myProjectRoots = null; - } - } - ); - } - - @Override - public boolean folderContainsSourceRoot(@NotNull VirtualFile file) { - if (!file.isDirectory()) { - return false; - } - final TreeSet 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 getProjectRoots() { - if (myProjectRoots == null) { - myProjectRoots = collectRoots(); - } - return myProjectRoots; - } - - @NotNull - private TreeSet collectRoots() { - final TreeSet result = new TreeSet<>(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; - } -} diff --git a/src/com/twitter/intellij/pants/components/impl/PantsProjectComponentImpl.java b/src/com/twitter/intellij/pants/components/impl/PantsProjectComponentImpl.java index 4f8238876..8e3d7aae9 100644 --- a/src/com/twitter/intellij/pants/components/impl/PantsProjectComponentImpl.java +++ b/src/com/twitter/intellij/pants/components/impl/PantsProjectComponentImpl.java @@ -25,9 +25,9 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.twitter.intellij.pants.PantsBundle; -import com.twitter.intellij.pants.PantsException; import com.twitter.intellij.pants.components.PantsProjectComponent; import com.twitter.intellij.pants.execution.PantsMakeBeforeRun; import com.twitter.intellij.pants.file.FileChangeTracker; @@ -35,22 +35,20 @@ import com.twitter.intellij.pants.metrics.PantsExternalMetricsListenerManager; import com.twitter.intellij.pants.metrics.PantsMetrics; import com.twitter.intellij.pants.model.PantsOptions; -import com.twitter.intellij.pants.util.PantsSdkUtil; import com.twitter.intellij.pants.service.project.PantsResolver; import com.twitter.intellij.pants.settings.PantsProjectSettings; import com.twitter.intellij.pants.settings.PantsSettings; import com.twitter.intellij.pants.ui.PantsConsoleManager; import com.twitter.intellij.pants.util.PantsConstants; +import com.twitter.intellij.pants.util.PantsSdkUtil; import com.twitter.intellij.pants.util.PantsUtil; import icons.PantsIcons; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; public class PantsProjectComponentImpl extends AbstractProjectComponent implements PantsProjectComponent { protected PantsProjectComponentImpl(Project project) { @@ -86,7 +84,7 @@ public void projectOpened() { // projectOpened() is called on the dispatch thread, while // addPantsProjectIgnoreDirs() calls an external process, // so it cannot be run on the dispatch thread. - ApplicationManager.getApplication().executeOnPooledThread(() -> addPantsProjectIgnoredDirs()); + ApplicationManager.getApplication().executeOnPooledThread(this::addPantsProjectIgnoredDirs); } super.projectOpened(); @@ -186,8 +184,9 @@ public void moduleAdded(@NotNull Project project, @NotNull Module module) { */ private void prepareGuiComponents() { if (!ApplicationManager.getApplication().isUnitTestMode()) { - if (ToolWindowManager.getInstance(myProject).getToolWindow("Project") != null) { - ToolWindowManager.getInstance(myProject).getToolWindow("Project").show(null); + ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow("Project"); + if (toolWindow != null) { + toolWindow.show(null); } ExternalSystemUtil.ensureToolWindowInitialized(myProject, PantsConstants.SYSTEM_ID); } @@ -266,10 +265,9 @@ private void addPantsProjectIgnoredDirs() { String pathToIgnore = buildRoot.getPath() + File.separator + ".idea"; clm.addDirectoryToIgnoreImplicitly(pathToIgnore); - PantsOptions.getPantsOptions(myProject).map(optionObj -> optionObj.get(PantsConstants.PANTS_OPTION_PANTS_WORKDIR)) - .ifPresent(optionString -> optionString.ifPresent( - clm::addDirectoryToIgnoreImplicitly - )); + PantsOptions.getPantsOptions(myProject) + .flatMap(optionObj -> optionObj.get(PantsConstants.PANTS_OPTION_PANTS_WORKDIR)) + .ifPresent(clm::addDirectoryToIgnoreImplicitly); } ); } diff --git a/src/com/twitter/intellij/pants/file/FileChangeTracker.java b/src/com/twitter/intellij/pants/file/FileChangeTracker.java index 710c3d61d..2ad163fa7 100644 --- a/src/com/twitter/intellij/pants/file/FileChangeTracker.java +++ b/src/com/twitter/intellij/pants/file/FileChangeTracker.java @@ -8,7 +8,6 @@ import com.google.common.hash.Hashing; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -23,7 +22,6 @@ import com.twitter.intellij.pants.settings.PantsSettings; import com.twitter.intellij.pants.util.PantsConstants; import com.twitter.intellij.pants.util.PantsUtil; -import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -254,7 +252,7 @@ private static boolean isManifestJarValid(@NotNull Project project) { return false; } Manifest manifest = new Manifest(manifestInJar.getInputStream()); - List relPaths = PantsUtil.parseCmdParameters(Optional.of(manifest.getMainAttributes().getValue("Class-Path"))); + List relPaths = PantsUtil.parseCmdParameters(manifest.getMainAttributes().getValue("Class-Path")); for (String path : relPaths) { // All rel paths in META-INF/MANIFEST.MF is relative to the jar directory if (!new File(file.getParent().getPath(), path).exists()) { diff --git a/src/com/twitter/intellij/pants/projectview/PantsProjectPaneSelectInTarget.java b/src/com/twitter/intellij/pants/projectview/PantsProjectPaneSelectInTarget.java index a4b92a994..b7619ecf7 100644 --- a/src/com/twitter/intellij/pants/projectview/PantsProjectPaneSelectInTarget.java +++ b/src/com/twitter/intellij/pants/projectview/PantsProjectPaneSelectInTarget.java @@ -32,11 +32,6 @@ public float getWeight() { return StandardTargetWeights.PROJECT_WEIGHT; } - @Override - protected boolean canWorkWithCustomObjects() { - return false; - } - @Override public boolean canSelect(PsiFileSystemItem file) { if (!super.canSelect(file)) return false; diff --git a/src/com/twitter/intellij/pants/projectview/PantsTreeStructureProvider.java b/src/com/twitter/intellij/pants/projectview/PantsTreeStructureProvider.java index 9189289de..d036a2887 100644 --- a/src/com/twitter/intellij/pants/projectview/PantsTreeStructureProvider.java +++ b/src/com/twitter/intellij/pants/projectview/PantsTreeStructureProvider.java @@ -24,10 +24,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class PantsTreeStructureProvider implements TreeStructureProvider { @NotNull @@ -37,48 +39,56 @@ public Collection> modify( @NotNull Collection> collection, ViewSettings settings ) { - final Project project = node.getProject(); - if (node instanceof PsiDirectoryNode && project != null) { - final Module module = ModuleUtil.findModuleForPsiElement(((PsiDirectoryNode) node).getValue()); - final Optional buildPath = - module != null ? PantsUtil.findModuleAddress(module) : Optional.empty(); - if (buildPath.isPresent()) { - final Optional buildFile = PantsUtil.findFileRelativeToBuildRoot(project, buildPath.get()); + Project project = node.getProject(); + if (project == null || !(node instanceof PsiDirectoryNode)) return collection; + PsiDirectoryNode directory = (PsiDirectoryNode) node; - boolean isModuleRoot = - ArrayUtil.indexOf(ModuleRootManager.getInstance(module).getContentRoots(), ((PsiDirectoryNode) node).getVirtualFile()) >= 0; - if (buildFile.isPresent() && isModuleRoot) { - // Check if there's already a BUILD file in the directory; if so, we don't add another - final AbstractTreeNode existingBuildFile = ContainerUtil.find( - collection.iterator(), new Condition() { - @Override - public boolean value(AbstractTreeNode node) { - return node instanceof PsiFileNode && buildFile.get().equals(((PsiFileNode) node).getVirtualFile()); - } - } - ); - if (existingBuildFile == null) { - final PsiFile buildPsiFile = PsiManager.getInstance(project).findFile(buildFile.get()); - final PsiFileNode buildNode = new PsiFileNode(project, buildPsiFile, settings) { - @Override - protected void updateImpl(PresentationData data) { - super.updateImpl(data); - data.setIcon(PantsIcons.Icon); - } - }; - final List> modifiedCollection = new ArrayList<>(collection); - modifiedCollection.add(buildNode); - return modifiedCollection; - } - } + List newNodes = + Optional.ofNullable(getModuleOf(directory)) + .filter(module -> isModuleRoot(directory, module)) + .flatMap(PantsUtil::findModuleAddress) + .flatMap(buildPAth -> PantsUtil.findFileRelativeToBuildRoot(project, buildPAth)) + .filter(buildFile -> !alreadyExists(collection, buildFile)) + .map(buildFile -> createNode(settings, project, buildFile)) + .orElseGet(Collections::emptyList); + + if (newNodes.isEmpty()) return collection; + + return Stream.concat(collection.stream(), newNodes.stream()).collect(Collectors.toList()); + } + + private Module getModuleOf(@NotNull PsiDirectoryNode node) { + return ModuleUtil.findModuleForPsiElement(node.getValue()); + } + + private boolean alreadyExists(Collection> collection, VirtualFile buildFile) { + Condition> isBuildFile = + node -> node instanceof PsiFileNode && buildFile.equals(((PsiFileNode) node).getVirtualFile()); + return ContainerUtil.exists(collection, isBuildFile); + } + + @NotNull + private List createNode(ViewSettings settings, Project project, VirtualFile buildFile) { + final PsiFile buildPsiFile = PsiManager.getInstance(project).findFile(buildFile); + if(buildPsiFile == null) return Collections.emptyList(); + + PsiFileNode node = new PsiFileNode(project, buildPsiFile, settings) { + @Override + protected void updateImpl(@NotNull PresentationData data) { + super.updateImpl(data); + data.setIcon(PantsIcons.Icon); } - } - return collection; + }; + return Collections.singletonList(node); + } + + private boolean isModuleRoot(@NotNull PsiDirectoryNode node, Module module) { + return ArrayUtil.indexOf(ModuleRootManager.getInstance(module).getContentRoots(), node.getVirtualFile()) >= 0; } @Nullable @Override - public Object getData(Collection> collection, String s) { + public Object getData(@NotNull Collection> collection, @NotNull String s) { return null; } } diff --git a/src/com/twitter/intellij/pants/projectview/ProjectFilesViewPane.java b/src/com/twitter/intellij/pants/projectview/ProjectFilesViewPane.java index 8d68e967d..097510998 100644 --- a/src/com/twitter/intellij/pants/projectview/ProjectFilesViewPane.java +++ b/src/com/twitter/intellij/pants/projectview/ProjectFilesViewPane.java @@ -43,11 +43,13 @@ public ProjectFilesViewPane(Project project) { super(project); } + @NotNull @Override public String getTitle() { return PantsBundle.message("pants.title.project.files"); } + @NotNull @Override public Icon getIcon() { return AllIcons.General.ProjectTab; @@ -68,7 +70,7 @@ public int getWeight() { } @Override - public void readExternal(Element element) throws InvalidDataException { + public void readExternal(@NotNull Element element) throws InvalidDataException { super.readExternal(element); final String showExcludedOption = JDOMExternalizerUtil.readField(element, SHOW_EXCLUDED_FILES_OPTION); myShowExcludedFiles = showExcludedOption == null || Boolean.parseBoolean(showExcludedOption); @@ -90,11 +92,13 @@ public void addToolbarActions(DefaultActionGroup actionGroup) { actionGroup.addAction(new ShowOnlyLoadedFilesAction()).setAsSecondary(true); } + @NotNull @Override protected ProjectAbstractTreeStructureBase createStructure() { return new ProjectViewPaneTreeStructure(); } + @NotNull @Override protected ProjectViewTree createTree(DefaultTreeModel treeModel) { return new ProjectViewTree(myProject, treeModel) { @@ -110,6 +114,7 @@ protected AbstractTreeUpdater createTreeUpdater(AbstractTreeBuilder treeBuilder) return new AbstractTreeUpdater(treeBuilder); } + @NotNull @Override public SelectInTarget createSelectInTarget() { return new PantsProjectPaneSelectInTarget(myProject); @@ -146,12 +151,12 @@ private ShowExcludedFilesAction() { } @Override - public boolean isSelected(AnActionEvent event) { + public boolean isSelected(@NotNull AnActionEvent event) { return myShowExcludedFiles; } @Override - public void setSelected(AnActionEvent event, boolean flag) { + public void setSelected(@NotNull AnActionEvent event, boolean flag) { if (myShowExcludedFiles != flag) { myShowExcludedFiles = flag; updateFromRoot(true); @@ -177,12 +182,12 @@ private ShowOnlyLoadedFilesAction() { } @Override - public boolean isSelected(AnActionEvent event) { + public boolean isSelected(@NotNull AnActionEvent event) { return myShowOnlyLoadedFiles; } @Override - public void setSelected(AnActionEvent event, boolean flag) { + public void setSelected(@NotNull AnActionEvent event, boolean flag) { if (myShowOnlyLoadedFiles != flag) { myShowOnlyLoadedFiles = flag; updateFromRoot(true); diff --git a/src/com/twitter/intellij/pants/projectview/VirtualFileTreeNode.java b/src/com/twitter/intellij/pants/projectview/VirtualFileTreeNode.java index 6048a4012..ab4b3f31c 100644 --- a/src/com/twitter/intellij/pants/projectview/VirtualFileTreeNode.java +++ b/src/com/twitter/intellij/pants/projectview/VirtualFileTreeNode.java @@ -23,7 +23,6 @@ import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.twitter.intellij.pants.components.PantsProjectCache; -import com.twitter.intellij.pants.components.impl.PantsProjectCacheImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -58,7 +57,7 @@ public boolean contains(@NotNull VirtualFile file) { } @Override - protected void update(PresentationData presentation) { + protected void update(@NotNull PresentationData presentation) { final PsiManager psiManager = PsiManager.getInstance(myProject); final VirtualFile virtualFile = getValue(); final PsiFile psiElement = virtualFile.isValid() ? psiManager.findFile(virtualFile) : null; @@ -114,7 +113,7 @@ private boolean shouldShow(@NotNull VirtualFile file) { private boolean shouldIncludeDirectory(VirtualFile dir) { final ViewSettings viewSettings = getSettings(); if (viewSettings instanceof PantsViewSettings && ((PantsViewSettings)viewSettings).isShowOnlyLoadedFiles()) { - final PantsProjectCache projectCache = PantsProjectCacheImpl.getInstance(getProject()); + final PantsProjectCache projectCache = PantsProjectCache.getInstance(getProject()); return projectCache.folderContainsSourceRoot(dir); } return true; diff --git a/src/com/twitter/intellij/pants/service/python/component/PantsPythonProjectComponent.java b/src/com/twitter/intellij/pants/service/python/component/PantsPythonProjectComponent.java deleted file mode 100644 index d27b7ccfc..000000000 --- a/src/com/twitter/intellij/pants/service/python/component/PantsPythonProjectComponent.java +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). -// Licensed under the Apache License, Version 2.0 (see LICENSE). - -package com.twitter.intellij.pants.service.python.component; - -import com.intellij.openapi.components.ProjectComponent; - -public interface PantsPythonProjectComponent extends ProjectComponent { -} diff --git a/src/com/twitter/intellij/pants/service/python/component/PantsPythonRunConfigListener.java b/src/com/twitter/intellij/pants/service/python/component/PantsPythonRunConfigListener.java new file mode 100644 index 000000000..df42e1a2a --- /dev/null +++ b/src/com/twitter/intellij/pants/service/python/component/PantsPythonRunConfigListener.java @@ -0,0 +1,40 @@ +// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). +// Licensed under the Apache License, Version 2.0 (see LICENSE). + +package com.twitter.intellij.pants.service.python.component; + +import com.intellij.execution.RunManagerListener; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.python.run.AbstractPythonRunConfiguration; +import com.twitter.intellij.pants.util.PantsUtil; +import org.jetbrains.annotations.NotNull; + +public class PantsPythonRunConfigListener implements RunManagerListener { + private final Project myProject; + + public PantsPythonRunConfigListener(Project project) { + myProject = project; + } + + @Override + public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) { + if (myProject.isDefault() || !PantsUtil.isPantsProject(myProject)) { + return; + } + + final RunConfiguration runConfiguration = settings.getConfiguration(); + if (runConfiguration instanceof AbstractPythonRunConfiguration) { + AbstractPythonRunConfiguration configuration = (AbstractPythonRunConfiguration) runConfiguration; + String workingDirectory = configuration.getWorkingDirectory(); + if (StringUtil.isEmpty(workingDirectory)) { + PantsUtil.findBuildRoot(myProject) + .map(VirtualFile::getCanonicalPath) + .ifPresent(configuration::setWorkingDirectory); + } + } + } +} diff --git a/src/com/twitter/intellij/pants/service/python/component/impl/PantsPythonProjectComponentImpl.java b/src/com/twitter/intellij/pants/service/python/component/impl/PantsPythonProjectComponentImpl.java deleted file mode 100644 index 7268f093f..000000000 --- a/src/com/twitter/intellij/pants/service/python/component/impl/PantsPythonProjectComponentImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2017 Pants project contributors (see CONTRIBUTORS.md). -// Licensed under the Apache License, Version 2.0 (see LICENSE). - -package com.twitter.intellij.pants.service.python.component.impl; - -import com.intellij.execution.RunManagerListener; -import com.intellij.execution.RunnerAndConfigurationSettings; -import com.intellij.execution.configurations.RunConfiguration; -import com.intellij.openapi.components.AbstractProjectComponent; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.python.run.AbstractPythonRunConfiguration; -import com.twitter.intellij.pants.service.python.component.PantsPythonProjectComponent; -import com.twitter.intellij.pants.util.PantsUtil; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; - - -public class PantsPythonProjectComponentImpl extends AbstractProjectComponent implements PantsPythonProjectComponent { - protected PantsPythonProjectComponentImpl(Project project) { - super(project); - } - - @Override - public void projectOpened() { - super.projectOpened(); - if (myProject.isDefault()) { - return; - } - myProject.getMessageBus().connect().subscribe(RunManagerListener.TOPIC, new RunManagerListener() { - @Override - public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) { - final RunConfiguration runConfiguration = settings.getConfiguration(); - if (!(runConfiguration instanceof AbstractPythonRunConfiguration)) { - return; - } - final String workingDirectory = ((AbstractPythonRunConfiguration) runConfiguration).getWorkingDirectory(); - if (StringUtil.isEmpty(workingDirectory)) { - final Optional projectBuildRoot = PantsUtil.findBuildRoot(myProject); - projectBuildRoot - .ifPresent(file -> ((AbstractPythonRunConfiguration) runConfiguration).setWorkingDirectory(file.getCanonicalPath())); - } - } - }); - } -} diff --git a/src/com/twitter/intellij/pants/settings/PantsSettings.java b/src/com/twitter/intellij/pants/settings/PantsSettings.java index 1a92e7607..cd28bf34d 100644 --- a/src/com/twitter/intellij/pants/settings/PantsSettings.java +++ b/src/com/twitter/intellij/pants/settings/PantsSettings.java @@ -112,7 +112,6 @@ protected void copyExtraSettingsFrom(@NotNull PantsSettings settings) { protected void checkSettings(@NotNull PantsProjectSettings old, @NotNull PantsProjectSettings current) { } - @SuppressWarnings("unchecked") @Nullable @Override public MyState getState() { @@ -124,7 +123,7 @@ public MyState getState() { } @Override - public void loadState(MyState state) { + public void loadState(@NotNull MyState state) { super.loadState(state); setResolverVersion(state.getResolverVersion()); setUseIdeaProjectJdk(state.isUseIdeaProjectJdk()); diff --git a/tests/com/twitter/intellij/pants/components/impl/PantsProjectCacheTest.java b/tests/com/twitter/intellij/pants/components/impl/PantsProjectCacheTest.java index 5e577c003..c655b6d2a 100644 --- a/tests/com/twitter/intellij/pants/components/impl/PantsProjectCacheTest.java +++ b/tests/com/twitter/intellij/pants/components/impl/PantsProjectCacheTest.java @@ -23,7 +23,6 @@ public class PantsProjectCacheTest extends PantsCodeInsightFixtureTestCase { protected void setUp() throws Exception { super.setUp(); cleanUpsourceFolders(); - PantsProjectCacheImpl.getInstance(getProject()).projectOpened(); } private void cleanUpsourceFolders() { @@ -44,7 +43,6 @@ private void cleanUpsourceFolders() { @Override protected void tearDown() throws Exception { cleanUpsourceFolders(); - PantsProjectCacheImpl.getInstance(getProject()).projectClosed(); super.tearDown(); } @@ -56,34 +54,31 @@ public VirtualFile getMainContentRoot() { } public void testEmpty() { - final PantsProjectCache cache = PantsProjectCacheImpl.getInstance(myFixture.getProject()); + final PantsProjectCache cache = PantsProjectCache.getInstance(myFixture.getProject()); assertFalse(cache.folderContainsSourceRoot(myFixture.getProject().getBaseDir())); } public void testLastOne() { - final PantsProjectCache cache = PantsProjectCacheImpl.getInstance(myFixture.getProject()); + final PantsProjectCache cache = PantsProjectCache.getInstance(myFixture.getProject()); final VirtualFile root = getMainContentRoot(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - PsiTestUtil.addSourceRoot(getModule(), VfsUtil.createDirectoryIfMissing(root, "bar")); - PsiTestUtil.addSourceRoot(getModule(), VfsUtil.createDirectoryIfMissing(root, "baz")); - assertTrue(cache.folderContainsSourceRoot(VfsUtil.createDirectoryIfMissing(root, "baz"))); - assertFalse(cache.folderContainsSourceRoot(VfsUtil.createDirectoryIfMissing(root, "ba"))); - assertFalse(cache.folderContainsSourceRoot(VfsUtil.createDirectoryIfMissing(root, "bat"))); - } - catch (IOException e) { - fail(e.getMessage()); - } + ApplicationManager.getApplication().runWriteAction(() -> { + try { + PsiTestUtil.addSourceRoot(getModule(), VfsUtil.createDirectoryIfMissing(root, "bar")); + PsiTestUtil.addSourceRoot(getModule(), VfsUtil.createDirectoryIfMissing(root, "baz")); + assertTrue(cache.folderContainsSourceRoot(VfsUtil.createDirectoryIfMissing(root, "baz"))); + assertFalse(cache.folderContainsSourceRoot(VfsUtil.createDirectoryIfMissing(root, "ba"))); + assertFalse(cache.folderContainsSourceRoot(VfsUtil.createDirectoryIfMissing(root, "bat"))); + } + catch (IOException e) { + fail(e.getMessage()); } }); } public void testFirstOne() { - final PantsProjectCache cache = PantsProjectCacheImpl.getInstance(myFixture.getProject()); + final PantsProjectCache cache = PantsProjectCache.getInstance(myFixture.getProject()); final VirtualFile root = getMainContentRoot(); ApplicationManager.getApplication().runWriteAction(() -> { diff --git a/tests/com/twitter/intellij/pants/execution/OSSPantsJvmRunConfigurationIntegrationTest.java b/tests/com/twitter/intellij/pants/execution/OSSPantsJvmRunConfigurationIntegrationTest.java index ba11f1b95..a8249915f 100644 --- a/tests/com/twitter/intellij/pants/execution/OSSPantsJvmRunConfigurationIntegrationTest.java +++ b/tests/com/twitter/intellij/pants/execution/OSSPantsJvmRunConfigurationIntegrationTest.java @@ -35,7 +35,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.stream.Collectors; @@ -57,7 +56,7 @@ public void testClassRunConfiguration() throws Throwable { String testTask = "test"; assertEquals(Collections.singletonList(testTask), esc.getSettings().getTaskNames()); - List configScriptParameters = PantsUtil.parseCmdParameters(Optional.ofNullable(esc.getSettings().getScriptParameters())); + List configScriptParameters = PantsUtil.parseCmdParameters(esc.getSettings().getScriptParameters()); assertContains(configScriptParameters, "--test-junit-test=" + testClass.getQualifiedName()); // Make sure this configuration does not contain any task before running if added to the project. @@ -100,8 +99,8 @@ private GeneralCommandLine getFinalCommandline( OptionalInt debugPort ) throws InstantiationException, IllegalAccessException { PantsExecutionSettings settings = PantsExecutionSettings.createDefault(); - settings.withVmOptions(PantsUtil.parseCmdParameters(Optional.ofNullable(esc.getSettings().getVmOptions()))); - settings.withArguments(PantsUtil.parseCmdParameters(Optional.ofNullable(esc.getSettings().getScriptParameters()))); + settings.withVmOptions(PantsUtil.parseCmdParameters(esc.getSettings().getVmOptions())); + settings.withArguments(PantsUtil.parseCmdParameters(esc.getSettings().getScriptParameters())); debugPort.ifPresent(port -> settings.putUserData(BUILD_PROCESS_DEBUGGER_PORT_KEY, port)); GeneralCommandLine commandLine = ((PantsTaskManager) taskManagerClass.newInstance()).constructCommandLine( diff --git a/tests/com/twitter/intellij/pants/execution/OSSPantsPythonRunConfigurationIntegrationTest.java b/tests/com/twitter/intellij/pants/execution/OSSPantsPythonRunConfigurationIntegrationTest.java index 6eff6f086..8163212a3 100644 --- a/tests/com/twitter/intellij/pants/execution/OSSPantsPythonRunConfigurationIntegrationTest.java +++ b/tests/com/twitter/intellij/pants/execution/OSSPantsPythonRunConfigurationIntegrationTest.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; public class OSSPantsPythonRunConfigurationIntegrationTest extends OSSPantsIntegrationTest { @@ -51,7 +50,7 @@ public void testPyTestRunConfiguration() throws Throwable { PsiFile file = new PyTestFile(truClass.getContainingFile(), truClass); ExternalSystemRunConfiguration esc = getExternalSystemRunConfiguration(file); - List items = PantsUtil.parseCmdParameters(Optional.ofNullable(esc.getSettings().getScriptParameters())); + List items = PantsUtil.parseCmdParameters(esc.getSettings().getScriptParameters()); assertNotContainsSubstring(items, PantsConstants.PANTS_CLI_OPTION_JUNIT_TEST); assertContainsSubstring(items, PantsConstants.PANTS_CLI_OPTION_PYTEST);