diff --git a/base/src/com/google/idea/blaze/base/dependencies/AddSourceToProjectHelper.java b/base/src/com/google/idea/blaze/base/dependencies/AddSourceToProjectHelper.java index 2e2780aaaa5..c075b11d0f6 100644 --- a/base/src/com/google/idea/blaze/base/dependencies/AddSourceToProjectHelper.java +++ b/base/src/com/google/idea/blaze/base/dependencies/AddSourceToProjectHelper.java @@ -267,8 +267,7 @@ static boolean sourceCoveredByProjectViewTargets(LocationContext context) { } private static Collection fromTargetInfo(Collection targetInfos) { - return targetInfos - .stream() + return targetInfos.stream() .map(t -> TargetKey.forPlainTarget(t.label)) .collect(toImmutableList()); } @@ -334,10 +333,7 @@ private static WorkspacePath findBlazePackagePath(Project project, WorkspacePath @Nullable static LocationContext getContext(Project project, VirtualFile file) { if (Blaze.getProjectType(project).equals(ProjectType.QUERY_SYNC)) { - // TODO(b/260643753) understand usages of this, and implement using BlazeProject instead - // Note the return type LocationContext includes BlazeProjectData to callers will need to - // change too. - return null; + throw new UnsupportedOperationException("AddSourceToProjectHelper#getContext"); } BlazeProjectData syncData = BlazeProjectDataManager.getInstance(project).getBlazeProjectData(); if (syncData == null) { diff --git a/base/src/com/google/idea/blaze/base/dependencies/ExternalFileProjectManagementHelper.java b/base/src/com/google/idea/blaze/base/dependencies/ExternalFileProjectManagementHelper.java index cd14efd9907..036b5381c2b 100644 --- a/base/src/com/google/idea/blaze/base/dependencies/ExternalFileProjectManagementHelper.java +++ b/base/src/com/google/idea/blaze/base/dependencies/ExternalFileProjectManagementHelper.java @@ -24,10 +24,14 @@ import com.google.idea.blaze.base.lang.buildfile.language.BuildFileType; import com.google.idea.blaze.base.model.BlazeProjectData; import com.google.idea.blaze.base.model.primitives.LanguageClass; +import com.google.idea.blaze.base.model.primitives.WorkspaceRoot; import com.google.idea.blaze.base.projectview.ProjectViewSet; +import com.google.idea.blaze.base.qsync.QuerySyncManager; +import com.google.idea.blaze.base.qsync.QuerySyncProject; import com.google.idea.blaze.base.scope.BlazeContext; import com.google.idea.blaze.base.settings.Blaze; import com.google.idea.blaze.base.settings.BlazeImportSettings; +import com.google.idea.blaze.base.settings.BlazeImportSettings.ProjectType; import com.google.idea.blaze.base.settings.BlazeUserSettings; import com.google.idea.blaze.base.settings.ui.BlazeUserSettingsCompositeConfigurable; import com.google.idea.blaze.base.settings.ui.BlazeUserSettingsConfigurable; @@ -49,11 +53,14 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotifications; +import com.intellij.ui.HyperlinkLabel; import java.io.File; +import java.nio.file.Path; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; import javax.annotation.Nullable; /** @@ -69,7 +76,7 @@ public class ExternalFileProjectManagementHelper private static final Key KEY = Key.create("add.source.to.project"); - private final Set suppressedFiles = new HashSet<>(); + private final Set suppressedFiles = new HashSet<>(); private static final ImmutableList IGNORED_FILE_TYPE_NAMES = ImmutableList.of( @@ -116,13 +123,33 @@ public EditorNotificationPanel createNotificationPanel(VirtualFile vf, FileEdito return null; } if (!BlazeUserSettings.getInstance().getShowAddFileToProjectNotification() - || suppressedFiles.contains(new File(vf.getPath()))) { + || suppressedFiles.contains(vf)) { return null; } File file = new File(vf.getPath()); if (!supportedFileType(file)) { return null; } + return createNotificationPanelByProjectType(vf); + } + + @Nullable + private EditorNotificationPanel createNotificationPanelByProjectType(VirtualFile vf) { + ProjectType projectType = Blaze.getProjectType(project); + switch (projectType) { + case QUERY_SYNC: + return createNotificationPanelForQuerySync(vf); + case ASPECT_SYNC: + return createNotificationPanelForLegacySync(vf); + case UNKNOWN: + return null; + } + throw new AssertionError(projectType); + } + + @Nullable + public EditorNotificationPanel createNotificationPanelForLegacySync(VirtualFile vf) { + LocationContext context = AddSourceToProjectHelper.getContext(project, vf); if (context == null) { return null; @@ -142,22 +169,71 @@ public EditorNotificationPanel createNotificationPanel(VirtualFile vf, FileEdito return null; } - EditorNotificationPanel panel = new EditorNotificationPanel(); + EditorNotificationPanel panel = + createPanel( + vf, + p -> + p.createActionLabel( + "Add file to project", + () -> { + AddSourceToProjectHelper.addSourceToProject( + project, context.workspacePath, inProjectDirectories, targetsFuture); + EditorNotifications.getInstance(project).updateNotifications(vf); + })); panel.setVisible(false); // starts off not visible until we get the query results - panel.setText("Do you want to add this file to your project sources?"); - panel.createActionLabel( - "Add file to project", + + targetsFuture.addListener( () -> { - AddSourceToProjectHelper.addSourceToProject( - project, context.workspacePath, inProjectDirectories, targetsFuture); - EditorNotifications.getInstance(project).updateNotifications(vf); + try { + List targets = targetsFuture.get(); + if (!targets.isEmpty() || !inProjectDirectories) { + panel.setVisible(true); + } + } catch (InterruptedException | ExecutionException e) { + // ignore + } + }, + MoreExecutors.directExecutor()); + return panel; + } + + @Nullable + private EditorNotificationPanel createNotificationPanelForQuerySync(VirtualFile virtualFile) { + QuerySyncProject querySyncProject = + QuerySyncManager.getInstance(project).getLoadedProject().orElse(null); + if (querySyncProject == null) { + return null; + } + if (!WorkspaceRoot.fromProject(project).isInWorkspace(virtualFile)) { + return null; + } + + Path path = virtualFile.toNioPath(); + // Project views do not support overriding a directory exclude, and the excluded directory may + // be imported from another file and so cannot be removed from the top-level file. + if (querySyncProject.containsPath(path) || querySyncProject.explicitlyExcludesPath(path)) { + return null; + } + + return createPanel( + virtualFile, + p -> { + HyperlinkLabel unused = + p.createActionLabel("Add file to project", "Blaze.AddToQuerySyncProjectView"); }); + } + + private EditorNotificationPanel createPanel( + VirtualFile virtualFile, Consumer mainActionAdder) { + EditorNotificationPanel panel = new EditorNotificationPanel(); + panel.setText("Do you want to add this file to your project sources?"); + mainActionAdder.accept(panel); panel.createActionLabel( "Hide notification", () -> { // suppressed for this file until the editor is restarted - suppressedFiles.add(file); - EditorNotifications.getInstance(project).updateNotifications(vf); + suppressedFiles.add(virtualFile); + EditorNotifications.getInstance(project).updateNotifications(virtualFile); }); panel.createActionLabel( "Don't show again", @@ -170,20 +246,6 @@ public EditorNotificationPanel createNotificationPanel(VirtualFile vf, FileEdito BlazeUserSettingsCompositeConfigurable.ID, BlazeUserSettingsConfigurable.SHOW_ADD_FILE_TO_PROJECT.label()); }); - - targetsFuture.addListener( - () -> { - try { - List targets = targetsFuture.get(); - if (!targets.isEmpty() || !inProjectDirectories) { - panel.setVisible(true); - } - } catch (InterruptedException | ExecutionException e) { - // ignore - } - }, - MoreExecutors.directExecutor()); - return panel; } diff --git a/base/src/com/google/idea/blaze/base/qsync/QuerySyncProject.java b/base/src/com/google/idea/blaze/base/qsync/QuerySyncProject.java index 7693b10c724..3a0485b57b3 100644 --- a/base/src/com/google/idea/blaze/base/qsync/QuerySyncProject.java +++ b/base/src/com/google/idea/blaze/base/qsync/QuerySyncProject.java @@ -427,6 +427,20 @@ public boolean containsPath(Path absolutePath) { return projectDefinition.isIncluded(workspaceRelative); } + /** + * Returns true if {@code absolutePath} is specified in a project exclude. + * + *

A path not added or excluded the project definition will return false for both {@code + * containsPath} and {@code explicitlyExcludesPath} + */ + public boolean explicitlyExcludesPath(Path absolutePath) { + if (!workspaceRoot.isInWorkspace(absolutePath.toFile())) { + return false; + } + Path workspaceRelative = workspaceRoot.path().relativize(absolutePath); + return projectDefinition.isExcluded(workspaceRelative); + } + /** Returns all external dependencies of a given label */ public ImmutableSet