Skip to content

Commit

Permalink
Add GUI for faspass amend command (#528)
Browse files Browse the repository at this point in the history
Allows user to include (or remove) new pants targets into already
imported project
  • Loading branch information
Tomasz Pasternak authored May 18, 2020
1 parent 6b782c3 commit a35f379
Show file tree
Hide file tree
Showing 20 changed files with 1,171 additions and 34 deletions.
18 changes: 18 additions & 0 deletions common/com/twitter/intellij/pants/PantsBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,21 @@ pants.resolve.incremental.import.unsupported=No target root found for constructi
pants.settings.text.use.intellij.compiler=Use the IntelliJ compiler [Experimental] This aims to reduce the iteration overhead by interacting with IntelliJ's compiler directly.
pants.settings.text.use.intellij.compiler.help.messasge=If your project does not work with IntelliJ compiler out of the box, please try to conform the code structure to 1:1:1.
pants.settings.text.use.intellij.compiler.help.messasge.link=https://www.pantsbuild.org/build_files.html#target-granularity

pants.bsp.select.targets=Amend BSP Project
pants.bsp.error.failed.to.fetch.targets=Failed to fetch imported targets list, please look at the logs for more details
pants.bsp.error.action.not.supported.title=Action not Supported
pants.bsp.error.failed.more.than.one.bsp.project.not.supported.message=Amending targets not supported for multi-BSP root projects
pants.bsp.error.failed.not.a.bsp.pants.project.message=Amending targets is not supported for non-BSP projects
pants.bsp.error.no.project.found=No project found
pants.bsp.unknown.error=Unknown error, please look at idea.log for details
pants.bsp.invalid.targets.list=Found no matching Pants targets
pants.bsp.loading=Running "pants list" to find matching targets...
pants.bsp.preview.title.plural={0} matching Pants targets
pants.bsp.preview.title.singular=1 matching Pants target
pants.bsp.preview.title.loading=0 matching Pants targets
pants.bsp.editor.title=Targets specs (example src/main/scala::)
pants.bsp.warn.no.targets=WARNING: no targets match the specs
pants.bsp.msg.box.empty.list.content=Target specs cannot be empty
pants.bsp.msg.box.amend.title=BSP Amend
pants.bsp.msg.box.specs.unchanged.content=Target specs same as before. Nothing happens.
8 changes: 8 additions & 0 deletions resources/META-INF/pants-scala.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
<!-- Licensed under the Apache License, Version 2.0 (see LICENSE). -->

<idea-plugin version="2">
<actions>
<action id="com.twitter.intellij.pants.compiler.actions.BspAmendProjectAction"
class="com.twitter.intellij.pants.bsp.FastpassBspAmendAction"
text="Amend BSP project">
<add-to-group group-id="Pants.Menu"/>
</action>
</actions>

<extensions defaultExtensionNs="com.intellij">
<externalProjectDataService implementation="com.twitter.intellij.pants.service.scala.PantsScalaDataService" order="last"/>
<highlightVisitor implementation="com.twitter.intellij.pants.highlight.PantsScalaHighlightVisitor"/>
Expand Down
18 changes: 18 additions & 0 deletions src/com/twitter/intellij/pants/bsp/BloopConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package com.twitter.intellij.pants.bsp;

import java.util.List;

class BloopConfig {
private final List<String> pantsTargets;

public List<String> getPantsTargets() {
return pantsTargets;
}

BloopConfig(List<String> pantsTargets) {
this.pantsTargets = pantsTargets;
}
}
128 changes: 128 additions & 0 deletions src/com/twitter/intellij/pants/bsp/FastpassBspAmendAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package com.twitter.intellij.pants.bsp;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.twitter.intellij.pants.PantsBundle;
import com.twitter.intellij.pants.bsp.ui.FastpassManagerDialog;
import com.twitter.intellij.pants.util.ExternalProjectUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.bsp.BSP;
import org.jetbrains.bsp.BspUtil;

import javax.swing.SwingUtilities;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

public class FastpassBspAmendAction extends AnAction {

private final Logger logger = Logger.getInstance(FastpassBspAmendAction.class);

@Override
public void update(@NotNull AnActionEvent e) {
super.update(e);
boolean isBsp = e.getProject() != null && BspUtil.isBspProject(e.getProject());
e.getPresentation().setEnabledAndVisible(isBsp);
}

@Override
public void actionPerformed(@NotNull AnActionEvent event) {
try {
Project project = event.getProject();
if (project != null) {
Set<PantsBspData> linkedProjects = PantsBspData.importsFor(project);
if (linkedProjects.size() > 1) {
Messages.showErrorDialog(
PantsBundle.message("pants.bsp.error.failed.more.than.one.bsp.project.not.supported.message"),
PantsBundle.message("pants.bsp.error.action.not.supported.title")
);
}
else if (linkedProjects.size() < 1) {
Messages.showErrorDialog(
PantsBundle.message("pants.bsp.error.failed.not.a.bsp.pants.project.message"),
PantsBundle.message("pants.bsp.error.action.not.supported.title")
);
}
else {
PantsBspData importData = linkedProjects.stream().findFirst().get();
startAmendProcedure(project, importData);
}
}
else {
Messages.showErrorDialog(
PantsBundle.message("pants.bsp.error.no.project.found"),
PantsBundle.message("pants.bsp.error.action.not.supported.title")
);
}
} catch (Throwable e) {
logger.error(e);
}
}

private void startAmendProcedure(Project project, PantsBspData firstProject) {
CompletableFuture<Set<String>> oldTargets = FastpassUtils.selectedTargets(firstProject);

FastpassTargetListCache targetsListCache = new FastpassTargetListCache(Paths.get(firstProject.getPantsRoot().getPath()));
PantsTargetsRepository getPreview = targets -> FastpassUtils.validateAndGetPreview(firstProject.getPantsRoot(), targets,
targetsListCache::getTargetsList
);
Optional<Set<String>> newTargets = FastpassManagerDialog
.promptForTargetsToImport(project, oldTargets, getPreview);
amendAndRefreshIfNeeded(project, firstProject, oldTargets, newTargets);
}

private void amendAndRefreshIfNeeded(
@NotNull Project project,
@NotNull PantsBspData basePath,
@NotNull CompletableFuture<Set<String>> oldTargets,
@NotNull Optional<Set<String>> newTargets
) {
oldTargets.thenAccept(
oldTargetsVal -> newTargets.ifPresent(newTargetsVal -> {
if(newTargetsVal.isEmpty()) {
SwingUtilities.invokeLater(() -> {
Messages.showErrorDialog(PantsBundle.message("pants.bsp.msg.box.empty.list.content"),
PantsBundle.message("pants.bsp.msg.box.amend.title"));
});
} else if (!newTargetsVal.equals(oldTargetsVal)) {
try {
refreshProjectsWithNewTargetsList(project, newTargets.get(), basePath);
}
catch (Throwable e) {
logger.error(e);
}
} else {
SwingUtilities.invokeLater(() -> {
Messages.showInfoMessage(PantsBundle.message("pants.bsp.msg.box.specs.unchanged.content"),
PantsBundle.message("pants.bsp.msg.box.amend.title"));
});
}
})
);
}

private void refreshProjectsWithNewTargetsList(
@NotNull Project project,
Collection<String> newTargets,
PantsBspData basePath
) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
try {
FastpassUtils.amendAll(basePath, new ArrayList<>(newTargets), project).get();
ExternalProjectUtil.refresh(project, BSP.ProjectSystemId());
} catch (Throwable e){
logger.error(e);
}
},"Amending", false, project );
}
}
26 changes: 26 additions & 0 deletions src/com/twitter/intellij/pants/bsp/FastpassTargetListCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package com.twitter.intellij.pants.bsp;

import java.nio.file.Path;
import java.util.Collection;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

final public class FastpassTargetListCache {

private final Path myPantsRoot;

public FastpassTargetListCache(Path pantsRoot) {

myPantsRoot = pantsRoot;
}

final ConcurrentHashMap<Path, CompletableFuture<Collection<PantsTargetAddress>>> cache = new ConcurrentHashMap<>();

public CompletableFuture<Collection<PantsTargetAddress>> getTargetsList(Path path) {
return cache.computeIfAbsent(path, path1 -> FastpassUtils.availableTargetsIn(myPantsRoot.resolve(path1)));
}
}
Loading

0 comments on commit a35f379

Please sign in to comment.