Import of bazel plugin using copybara
PiperOrigin-RevId: 142767644
diff --git a/BUILD b/BUILD
index 1f6ee16..f87b1dc 100644
--- a/BUILD
+++ b/BUILD
@@ -10,6 +10,7 @@
tests = [
"//base:integration_tests",
"//base:unit_tests",
+ "//ijwb:integration_tests",
"//ijwb:unit_tests",
"//java:integration_tests",
"//java:unit_tests",
diff --git a/WORKSPACE b/WORKSPACE
index c29d33a..edb340b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,17 +1,19 @@
workspace(name = "intellij_with_bazel")
-# The plugin api for IntelliJ 2016.2.5. This is required to build IJwB,
+# Long-lived download links available at: https://www.jetbrains.com/intellij-repository/releases
+
+# The plugin api for IntelliJ 2016.2.4. This is required to build IJwB,
# and run integration tests.
new_http_archive(
- name = "intellij_latest",
+ name = "IC_162_2032_8",
build_file = "intellij_platform_sdk/BUILD.idea",
- url = "https://download.jetbrains.com/idea/ideaIC-2016.2.5.tar.gz",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2016.2.4/ideaIC-2016.2.4.zip",
)
# The plugin api for CLion 2016.2.2. This is required to build CLwB,
# and run integration tests.
new_http_archive(
- name = "clion_latest",
+ name = "CL_162_1967_7",
build_file = "intellij_platform_sdk/BUILD.clion",
url = "https://download.jetbrains.com/cpp/CLion-2016.2.2.tar.gz",
)
@@ -19,7 +21,7 @@
# The plugin api for Android Studio 2.2 stable. This is required to build ASwB,
# and run integration tests.
new_http_archive(
- name = "android_studio_latest",
+ name = "AI_145_1617_8",
build_file = "intellij_platform_sdk/BUILD.android_studio",
url = "https://dl.google.com/dl/android/studio/ide-zips/2.2.0.12/android-studio-ide-145.3276617-linux.zip",
)
diff --git a/aswb/src/META-INF/aswb.xml b/aswb/src/META-INF/aswb.xml
index ab228a2..ca22494 100644
--- a/aswb/src/META-INF/aswb.xml
+++ b/aswb/src/META-INF/aswb.xml
@@ -36,7 +36,6 @@
<programRunner implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryProgramRunner" order="first"/>
<executor implementation="com.google.idea.blaze.android.run.binary.mobileinstall.IncrementalInstallRunExecutor" order="last"/>
<executor implementation="com.google.idea.blaze.android.run.binary.mobileinstall.IncrementalInstallDebugExecutor" order="last"/>
- <applicationService serviceImplementation="com.google.idea.blaze.android.settings.AswbGlobalSettings"/>
<applicationService serviceInterface="com.google.idea.blaze.base.plugin.BlazePluginId"
serviceImplementation="com.google.idea.blaze.android.plugin.AswbPlugin"/>
<projectService serviceImplementation="com.google.idea.blaze.android.manifest.ManifestParser"/>
@@ -47,6 +46,10 @@
implementation="com.google.idea.blaze.android.resources.actions.BlazeNewResourceCreationHandler" />
</extensions>
+ <extensionPoints>
+ <extensionPoint qualifiedName="com.google.idea.blaze.BuildSystemAndroidJdkProvider" interface="com.google.idea.blaze.android.sync.BuildSystemAndroidJdkProvider"/>
+ </extensionPoints>
+
<extensions defaultExtensionNs="com.google.idea.blaze">
<SyncPlugin implementation="com.google.idea.blaze.android.sync.BlazeAndroidSyncPlugin"/>
<SyncListener implementation="com.google.idea.blaze.android.sync.BlazeAndroidSyncListener"/>
@@ -57,6 +60,7 @@
<PrefetchFileSource implementation="com.google.idea.blaze.android.sync.AndroidPrefetchFileSource"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryRunConfigurationHandlerProvider"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.android.run.test.BlazeAndroidTestRunConfigurationHandlerProvider"/>
+ <BuildSystemAndroidJdkProvider implementation="com.google.idea.blaze.android.sync.BazelAndroidJdkProvider"/>
</extensions>
<extensions defaultExtensionNs="com.android.ide">
diff --git a/aswb/src/com/google/idea/blaze/android/cppapi/NdkSupport.java b/aswb/src/com/google/idea/blaze/android/cppapi/NdkSupport.java
index 506dfe6..8456626 100644
--- a/aswb/src/com/google/idea/blaze/android/cppapi/NdkSupport.java
+++ b/aswb/src/com/google/idea/blaze/android/cppapi/NdkSupport.java
@@ -19,5 +19,5 @@
/** Contains the experiment that turns on NDK support */
public class NdkSupport {
- public static final BoolExperiment NDK_SUPPORT = new BoolExperiment("ndk.support", false);
+ public static final BoolExperiment NDK_SUPPORT = new BoolExperiment("ndk.support", true);
}
diff --git a/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java b/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java
index 3087e9a..f6b50ae 100644
--- a/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java
+++ b/aswb/src/com/google/idea/blaze/android/cppimpl/BlazeNdkSupportEnabler.java
@@ -23,6 +23,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.google.idea.blaze.cpp.BlazeCWorkspace;
import com.intellij.openapi.application.ApplicationManager;
@@ -39,6 +40,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
boolean enabled = blazeProjectData.workspaceLanguageSettings.isLanguageActive(LanguageClass.C);
enableCSupportInIde(project, enabled);
diff --git a/aswb/src/com/google/idea/blaze/android/manifest/ManifestParser.java b/aswb/src/com/google/idea/blaze/android/manifest/ManifestParser.java
index becaa66..ddd7d49 100644
--- a/aswb/src/com/google/idea/blaze/android/manifest/ManifestParser.java
+++ b/aswb/src/com/google/idea/blaze/android/manifest/ManifestParser.java
@@ -20,6 +20,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
@@ -105,6 +106,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
getInstance(project).manifestFileMap.clear();
}
diff --git a/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java b/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java
index b2c7177..056f692 100644
--- a/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java
+++ b/aswb/src/com/google/idea/blaze/android/projectview/AndroidSdkPlatformSection.java
@@ -15,13 +15,20 @@
*/
package com.google.idea.blaze.android.projectview;
+import com.google.idea.blaze.android.sync.sdk.AndroidSdkFromProjectView;
+import com.google.idea.blaze.base.projectview.ProjectView;
import com.google.idea.blaze.base.projectview.parser.ParseContext;
import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
import com.google.idea.blaze.base.projectview.section.ScalarSection;
import com.google.idea.blaze.base.projectview.section.ScalarSectionParser;
import com.google.idea.blaze.base.projectview.section.SectionKey;
import com.google.idea.blaze.base.projectview.section.SectionParser;
+import com.google.idea.blaze.base.projectview.section.sections.TextBlock;
+import com.google.idea.blaze.base.projectview.section.sections.TextBlockSection;
+import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.text.StringUtil;
+import java.util.Collection;
+import org.jetbrains.android.sdk.AndroidSdkUtils;
import org.jetbrains.annotations.Nullable;
/** Allows manual override of the android sdk. */
@@ -50,5 +57,25 @@
public ItemType getItemType() {
return ItemType.Other;
}
+
+ @Override
+ public ProjectView addProjectViewDefaultValue(ProjectView projectView) {
+ if (!projectView.getSectionsOfType(KEY).isEmpty()) {
+ return projectView;
+ }
+ Collection<Sdk> sdks = AndroidSdkUtils.getAllAndroidSdks();
+ return ProjectView.builder(projectView)
+ .add(TextBlockSection.of(TextBlock.newLine()))
+ .add(TextBlockSection.of(TextBlock.of("# Please set to an android SDK platform")))
+ .add(
+ TextBlockSection.of(
+ TextBlock.of(
+ sdks.isEmpty()
+ ? "# You currently have no SDKs. Please use the SDK manager first."
+ : "# Available SDKs are: "
+ + AndroidSdkFromProjectView.getAvailableSdkPlatforms(sdks))))
+ .add(ScalarSection.builder(KEY).set("<android sdk platform>"))
+ .build();
+ }
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/projectview/GeneratedAndroidResourcesSection.java b/aswb/src/com/google/idea/blaze/android/projectview/GeneratedAndroidResourcesSection.java
new file mode 100644
index 0000000..a93d218
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/projectview/GeneratedAndroidResourcesSection.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.projectview;
+
+import com.google.idea.blaze.base.projectview.parser.ParseContext;
+import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
+import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.google.idea.blaze.base.projectview.section.ListSectionParser;
+import com.google.idea.blaze.base.projectview.section.SectionKey;
+import com.google.idea.blaze.base.projectview.section.SectionParser;
+import com.google.idea.blaze.base.ui.BlazeValidationError;
+import com.intellij.util.PathUtil;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/** Project view section white-listing generated resource directories */
+public class GeneratedAndroidResourcesSection {
+ public static final SectionKey<GenfilesPath, ListSection<GenfilesPath>> KEY =
+ SectionKey.of("generated_android_resource_directories");
+
+ public static final SectionParser PARSER = new Parser();
+
+ private GeneratedAndroidResourcesSection() {}
+
+ private static class Parser extends ListSectionParser<GenfilesPath> {
+ Parser() {
+ super(KEY);
+ }
+
+ @Nullable
+ @Override
+ protected GenfilesPath parseItem(ProjectViewParser parser, ParseContext parseContext) {
+ String canonicalPath = PathUtil.getCanonicalPath(parseContext.current().text);
+
+ List<BlazeValidationError> errors = new ArrayList<>();
+ if (!GenfilesPath.validate(canonicalPath, errors)) {
+ parseContext.addErrors(errors);
+ return null;
+ }
+ return new GenfilesPath(canonicalPath);
+ }
+
+ @Override
+ protected void printItem(GenfilesPath item, StringBuilder sb) {
+ sb.append(item.relativePath);
+ }
+
+ @Override
+ public ItemType getItemType() {
+ return ItemType.FileSystemItem;
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/projectview/GenfilesPath.java b/aswb/src/com/google/idea/blaze/android/projectview/GenfilesPath.java
new file mode 100644
index 0000000..90960f9
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/projectview/GenfilesPath.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.projectview;
+
+import com.google.common.base.Objects;
+import com.google.idea.blaze.base.ui.BlazeValidationError;
+import java.io.Serializable;
+import java.util.List;
+
+/** Project view entry data for {@link GeneratedAndroidResourcesSection}. */
+public class GenfilesPath implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final String relativePath;
+
+ public GenfilesPath(String relativePath) {
+ this.relativePath = relativePath;
+ }
+
+ static boolean validate(String path, List<BlazeValidationError> errors) {
+ if (path.startsWith("/")) {
+ BlazeValidationError.collect(
+ errors,
+ new BlazeValidationError(
+ "Genfiles path must be relative; cannot start with '/': " + path));
+ return false;
+ }
+
+ if (path.endsWith("/")) {
+ BlazeValidationError.collect(
+ errors, new BlazeValidationError("Genfiles path may not end with '/': " + path));
+ return false;
+ }
+
+ if (path.indexOf(':') >= 0) {
+ BlazeValidationError.collect(
+ errors, new BlazeValidationError("Genfiles path may not contain ':': " + path));
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ GenfilesPath that = (GenfilesPath) o;
+ return Objects.equal(relativePath, that.relativePath);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(relativePath);
+ }
+
+ @Override
+ public String toString() {
+ return relativePath;
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/settings/AswbGlobalSettings.java b/aswb/src/com/google/idea/blaze/android/settings/AswbGlobalSettings.java
deleted file mode 100644
index 53577f5..0000000
--- a/aswb/src/com/google/idea/blaze/android/settings/AswbGlobalSettings.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.android.settings;
-
-import com.intellij.openapi.components.PersistentStateComponent;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.components.State;
-import com.intellij.openapi.components.Storage;
-import com.intellij.util.xmlb.XmlSerializerUtil;
-import org.jetbrains.annotations.Nullable;
-
-/** Stores aswb global settings. */
-@State(name = "AswbGlobalSettings", storages = @Storage("aswb.global.xml"))
-public class AswbGlobalSettings implements PersistentStateComponent<AswbGlobalSettings> {
-
- @Deprecated private String localSdkLocation;
-
- public static AswbGlobalSettings getInstance() {
- return ServiceManager.getService(AswbGlobalSettings.class);
- }
-
- @Nullable
- @Override
- public AswbGlobalSettings getState() {
- return this;
- }
-
- @Override
- public void loadState(AswbGlobalSettings state) {
- XmlSerializerUtil.copyBean(state, this);
- }
-
- @Deprecated
- public void setLocalSdkLocation(String localSdkLocation) {
- this.localSdkLocation = localSdkLocation;
- }
-
- @Deprecated
- public String getLocalSdkLocation() {
- return localSdkLocation;
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java b/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java
new file mode 100644
index 0000000..f45b368
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/BazelAndroidJdkProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync;
+
+import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
+import com.intellij.openapi.project.Project;
+import com.intellij.pom.java.LanguageLevel;
+import javax.annotation.Nullable;
+
+/** The highest JDK language level supported by bazel. */
+public class BazelAndroidJdkProvider implements BuildSystemAndroidJdkProvider {
+
+ @Nullable
+ @Override
+ public LanguageLevel getLanguageLevel(Project project) {
+ BuildSystem buildSystem = Blaze.getBuildSystem(project);
+ return buildSystem == BuildSystem.Bazel ? LanguageLevel.JDK_1_7 : null;
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidJavaSyncAugmenter.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidJavaSyncAugmenter.java
index e6240bd..084aec6 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidJavaSyncAugmenter.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidJavaSyncAugmenter.java
@@ -15,22 +15,27 @@
*/
package com.google.idea.blaze.android.sync;
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
import com.google.idea.blaze.android.sync.importer.BlazeAndroidWorkspaceImporter;
import com.google.idea.blaze.base.ideinfo.AndroidIdeInfo;
import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.java.sync.BlazeJavaSyncAugmenter;
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
/** Augments the java sync process with Android support. */
public class BlazeAndroidJavaSyncAugmenter implements BlazeJavaSyncAugmenter {
@Override
public void addJarsForSourceTarget(
WorkspaceLanguageSettings workspaceLanguageSettings,
+ ProjectViewSet projectViewSet,
TargetIdeInfo target,
Collection<BlazeJarLibrary> jars,
Collection<BlazeJarLibrary> genJars) {
@@ -45,9 +50,15 @@
if (idlJar != null) {
genJars.add(new BlazeJarLibrary(idlJar, target.key));
}
-
+ Set<String> whitelistedGenResourcePaths =
+ projectViewSet
+ .listItems(GeneratedAndroidResourcesSection.KEY)
+ .stream()
+ .map(genfilesPath -> genfilesPath.relativePath)
+ .collect(Collectors.toSet());
if (BlazeAndroidWorkspaceImporter.shouldGenerateResources(androidIdeInfo)
- && !BlazeAndroidWorkspaceImporter.shouldGenerateResourceModule(androidIdeInfo)) {
+ && !BlazeAndroidWorkspaceImporter.shouldGenerateResourceModule(
+ androidIdeInfo, whitelistedGenResourcePaths)) {
// Add blaze's output unless it's a top level rule.
// In these cases the resource jar contains the entire
// transitive closure of R classes. It's unlikely this is wanted to resolve in the IDE.
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java
index fda9123..815c4c9 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncListener.java
@@ -17,6 +17,7 @@
import com.android.tools.idea.res.ResourceFolderRegistry;
import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
@@ -24,7 +25,8 @@
/** Android-specific hooks to run after a blaze sync. */
public class BlazeAndroidSyncListener extends SyncListener.Adapter {
@Override
- public void afterSync(Project project, BlazeContext context, SyncResult syncResult) {
+ public void afterSync(
+ Project project, BlazeContext context, SyncMode syncMode, SyncResult syncResult) {
if (syncResult == SyncResult.SUCCESS || syncResult == SyncResult.PARTIAL_SUCCESS) {
DumbService dumbService = DumbService.getInstance(project);
dumbService.queueTask(new ResourceFolderRegistry.PopulateCachesTask(project));
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
index 52c21b2..72b352d 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/BlazeAndroidSyncPlugin.java
@@ -16,20 +16,17 @@
package com.google.idea.blaze.android.sync;
import com.android.tools.idea.sdk.IdeSdks;
-import com.google.common.base.Joiner;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.android.cppapi.NdkSupport;
import com.google.idea.blaze.android.projectview.AndroidSdkPlatformSection;
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
import com.google.idea.blaze.android.sync.importer.BlazeAndroidWorkspaceImporter;
import com.google.idea.blaze.android.sync.model.AndroidSdkPlatform;
import com.google.idea.blaze.android.sync.model.BlazeAndroidImportResult;
import com.google.idea.blaze.android.sync.model.BlazeAndroidSyncData;
import com.google.idea.blaze.android.sync.projectstructure.BlazeAndroidProjectStructureSyncer;
import com.google.idea.blaze.android.sync.sdk.AndroidSdkFromProjectView;
-import com.google.idea.blaze.android.sync.sdk.SdkExperiment;
-import com.google.idea.blaze.android.sync.sdklegacy.AndroidSdkPlatformSyncer;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.SyncState;
@@ -43,9 +40,8 @@
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.output.StatusOutput;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.base.sync.libraries.LibrarySource;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
@@ -53,6 +49,8 @@
import com.google.idea.blaze.base.sync.workspace.WorkingSet;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.blaze.java.projectview.JavaLanguageLevelSection;
+import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
+import com.google.idea.blaze.java.sync.projectstructure.JavaSourceFolderProvider;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.module.Module;
@@ -78,7 +76,7 @@
@Override
public ImmutableList<WorkspaceType> getSupportedWorkspaceTypes() {
- return ImmutableList.of(WorkspaceType.ANDROID, WorkspaceType.ANDROID_NDK);
+ return ImmutableList.of(WorkspaceType.ANDROID);
}
@Nullable
@@ -90,7 +88,7 @@
@Nullable
@Override
public ModuleType getWorkspaceModuleType(WorkspaceType workspaceType) {
- if (workspaceType == WorkspaceType.ANDROID || workspaceType == WorkspaceType.ANDROID_NDK) {
+ if (workspaceType == WorkspaceType.ANDROID) {
return StdModuleTypes.JAVA;
}
return null;
@@ -98,17 +96,13 @@
@Override
public Set<LanguageClass> getSupportedLanguagesInWorkspace(WorkspaceType workspaceType) {
- switch (workspaceType) {
- case ANDROID:
- return ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA);
- case ANDROID_NDK:
- if (NdkSupport.NDK_SUPPORT.getValue()) {
- return ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA, LanguageClass.C);
- } else {
- return ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA);
- }
- default:
- return ImmutableSet.of();
+ if (workspaceType != WorkspaceType.ANDROID) {
+ return ImmutableSet.of();
+ }
+ if (NdkSupport.NDK_SUPPORT.getValue()) {
+ return ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA, LanguageClass.C);
+ } else {
+ return ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA);
}
}
@@ -144,16 +138,12 @@
return;
}
- final AndroidSdkPlatform androidSdkPlatform;
- if (SdkExperiment.useStandardSdkManager()) {
- androidSdkPlatform = AndroidSdkFromProjectView.getAndroidSdkPlatform(context, projectViewSet);
- } else {
- androidSdkPlatform = AndroidSdkPlatformSyncer.getAndroidSdkPlatform(project, context);
- }
+ AndroidSdkPlatform androidSdkPlatform =
+ AndroidSdkFromProjectView.getAndroidSdkPlatform(context, projectViewSet);
BlazeAndroidWorkspaceImporter workspaceImporter =
new BlazeAndroidWorkspaceImporter(
- project, context, workspaceRoot, projectViewSet, targetMap);
+ project, context, workspaceRoot, projectViewSet, targetMap, artifactLocationDecoder);
BlazeAndroidImportResult importResult =
Scope.push(
context,
@@ -190,10 +180,7 @@
return;
}
- LanguageLevel defaultLanguageLevel =
- Blaze.getBuildSystem(project) == BuildSystem.Blaze
- ? LanguageLevel.JDK_1_8
- : LanguageLevel.JDK_1_7;
+ LanguageLevel defaultLanguageLevel = BuildSystemAndroidJdkProvider.languageLevel(project);
LanguageLevel javaLanguageLevel =
JavaLanguageLevelSection.getLanguageLevel(projectViewSet, defaultLanguageLevel);
setProjectSdkAndLanguageLevel(project, sdk, javaLanguageLevel);
@@ -222,6 +209,15 @@
isAndroidWorkspace(blazeProjectData.workspaceLanguageSettings));
}
+ @Nullable
+ @Override
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ if (!projectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.ANDROID)) {
+ return null;
+ }
+ return new JavaSourceFolderProvider(projectData.syncState.get(BlazeJavaSyncData.class));
+ }
+
@Override
public boolean validate(
Project project, BlazeContext context, BlazeProjectData blazeProjectData) {
@@ -249,33 +245,15 @@
return true;
}
- if (workspaceLanguageSettings.isWorkspaceType(WorkspaceType.ANDROID_NDK)
+ if (workspaceLanguageSettings.isLanguageActive(LanguageClass.C)
&& !NdkSupport.NDK_SUPPORT.getValue()) {
IssueOutput.error("Android NDK is not supported yet.").submit(context);
return false;
}
- if (SdkExperiment.useStandardSdkManager()) {
- if (AndroidSdkFromProjectView.getAndroidSdkPlatform(context, projectViewSet) == null) {
- return false;
- }
- } else {
- String androidSdkPlatform = projectViewSet.getScalarValue(AndroidSdkPlatformSection.KEY);
- if (Strings.isNullOrEmpty(androidSdkPlatform)) {
- String error =
- Joiner.on('\n')
- .join(
- "No android_sdk_platform set.",
- "You should specify the android SDK platform in your '.blazeproject' file.",
- "To set this add an 'android_sdk_platform' line to your .blazeproject file,",
- "e.g. 'android_sdk_platform: \"android-N\"', where 'android-N' is a",
- "platform directory name in your local SDK directory.");
- IssueOutput.error(error)
- .inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
- .submit(context);
- }
+ if (AndroidSdkFromProjectView.getAndroidSdkPlatform(context, projectViewSet) == null) {
+ return false;
}
-
return true;
}
@@ -298,7 +276,8 @@
@Override
public Collection<SectionParser> getSections() {
- return ImmutableList.of(AndroidSdkPlatformSection.PARSER);
+ return ImmutableList.of(
+ AndroidSdkPlatformSection.PARSER, GeneratedAndroidResourcesSection.PARSER);
}
@Nullable
@@ -311,7 +290,6 @@
}
private static boolean isAndroidWorkspace(WorkspaceLanguageSettings workspaceLanguageSettings) {
- return workspaceLanguageSettings.isWorkspaceType(
- WorkspaceType.ANDROID, WorkspaceType.ANDROID_NDK);
+ return workspaceLanguageSettings.isWorkspaceType(WorkspaceType.ANDROID);
}
}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java b/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java
new file mode 100644
index 0000000..d9b53bf
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/BuildSystemAndroidJdkProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.pom.java.LanguageLevel;
+import javax.annotation.Nullable;
+
+/** Provides the highest JDK language level supported by the build system. */
+public interface BuildSystemAndroidJdkProvider {
+
+ ExtensionPointName<BuildSystemAndroidJdkProvider> EP_NAME =
+ ExtensionPointName.create("com.google.idea.blaze.BuildSystemAndroidJdkProvider");
+
+ LanguageLevel DEFAULT_LANGUAGE_LEVEL = LanguageLevel.JDK_1_7;
+
+ static LanguageLevel languageLevel(Project project) {
+ for (BuildSystemAndroidJdkProvider provider : EP_NAME.getExtensions()) {
+ LanguageLevel level = provider.getLanguageLevel(project);
+ if (level != null) {
+ return level;
+ }
+ }
+ return DEFAULT_LANGUAGE_LEVEL;
+ }
+
+ /**
+ * Returns the highest JDK language level supported for this project, or null if the provider is
+ * unable to determine it.
+ */
+ @Nullable
+ LanguageLevel getLanguageLevel(Project project);
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java b/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java
index 7a3651b..3cb5351 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java
@@ -18,10 +18,13 @@
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
import com.google.idea.blaze.android.sync.importer.aggregators.TransitiveResourceMap;
+import com.google.idea.blaze.android.sync.importer.problems.GeneratedResourceWarnings;
import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
import com.google.idea.blaze.android.sync.model.BlazeAndroidImportResult;
import com.google.idea.blaze.android.sync.model.BlazeResourceLibrary;
@@ -37,6 +40,7 @@
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.output.PerformanceWarning;
import com.google.idea.blaze.base.sync.projectview.ProjectViewTargetImportFilter;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.intellij.openapi.project.Project;
import java.util.Collection;
import java.util.Collections;
@@ -44,25 +48,39 @@
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** Builds a BlazeWorkspace. */
public final class BlazeAndroidWorkspaceImporter {
+ private final Project project;
private final BlazeContext context;
private final TargetMap targetMap;
private final ProjectViewTargetImportFilter importFilter;
+ private final ProjectViewSet projectViewSet;
+ private final ArtifactLocationDecoder artifactLocationDecoder;
+ private final ImmutableSet<String> whitelistedGenResourcePaths;
public BlazeAndroidWorkspaceImporter(
Project project,
BlazeContext context,
WorkspaceRoot workspaceRoot,
ProjectViewSet projectViewSet,
- TargetMap targetMap) {
+ TargetMap targetMap,
+ ArtifactLocationDecoder artifactLocationDecoder) {
this.context = context;
this.targetMap = targetMap;
this.importFilter = new ProjectViewTargetImportFilter(project, workspaceRoot, projectViewSet);
+ this.projectViewSet = projectViewSet;
+ this.artifactLocationDecoder = artifactLocationDecoder;
+ this.project = project;
+ this.whitelistedGenResourcePaths =
+ ImmutableSet.copyOf(
+ projectViewSet
+ .listItems(GeneratedAndroidResourcesSection.KEY)
+ .stream()
+ .map(genfilesPath -> genfilesPath.relativePath)
+ .collect(Collectors.toSet()));
}
public BlazeAndroidImportResult importWorkspace() {
@@ -84,7 +102,13 @@
addSourceTarget(workspaceBuilder, transitiveResourceMap, target);
}
- warnAboutGeneratedResources(workspaceBuilder.generatedResourceLocations);
+ GeneratedResourceWarnings.submit(
+ project,
+ context,
+ projectViewSet,
+ artifactLocationDecoder,
+ workspaceBuilder.generatedResourceLocations,
+ whitelistedGenResourcePaths);
ImmutableList<AndroidResourceModule> androidResourceModules =
buildAndroidResourceModules(workspaceBuilder);
@@ -99,7 +123,8 @@
TargetIdeInfo target) {
AndroidIdeInfo androidIdeInfo = target.androidIdeInfo;
assert androidIdeInfo != null;
- if (shouldGenerateResources(androidIdeInfo) && shouldGenerateResourceModule(androidIdeInfo)) {
+ if (shouldGenerateResources(androidIdeInfo)
+ && shouldGenerateResourceModule(androidIdeInfo, whitelistedGenResourcePaths)) {
AndroidResourceModule.Builder builder = new AndroidResourceModule.Builder(target.key);
workspaceBuilder.androidResourceModules.add(builder);
@@ -108,6 +133,11 @@
builder.addResource(artifactLocation);
} else {
workspaceBuilder.generatedResourceLocations.add(artifactLocation);
+ if (whitelistedGenResourcePaths.contains(artifactLocation.relativePath)) {
+ // Still track location in generatedResourceLocations, so that we can warn if a
+ // whitelist entry goes unused and can be removed.
+ builder.addResource(artifactLocation);
+ }
}
}
@@ -118,6 +148,9 @@
builder.addTransitiveResource(artifactLocation);
} else {
workspaceBuilder.generatedResourceLocations.add(artifactLocation);
+ if (whitelistedGenResourcePaths.contains(artifactLocation.relativePath)) {
+ builder.addTransitiveResource(artifactLocation);
+ }
}
}
for (TargetKey resourceDependency : transitiveResourceInfo.transitiveResourceTargets) {
@@ -137,19 +170,18 @@
return androidIdeInfo.generateResourceClass && androidIdeInfo.legacyResources == null;
}
- public static boolean shouldGenerateResourceModule(AndroidIdeInfo androidIdeInfo) {
- return androidIdeInfo.resources.stream().anyMatch(ArtifactLocation::isSource);
+ public static boolean shouldGenerateResourceModule(
+ AndroidIdeInfo androidIdeInfo, Set<String> whitelistedGenResourcePaths) {
+ return androidIdeInfo
+ .resources
+ .stream()
+ .anyMatch(location -> isSourceOrWhitelistedGenPath(location, whitelistedGenResourcePaths));
}
- private void warnAboutGeneratedResources(Set<ArtifactLocation> generatedResourceLocations) {
- for (ArtifactLocation artifactLocation : generatedResourceLocations) {
- IssueOutput.warn(
- String.format(
- "Dropping generated resource directory '%s', "
- + "R classes will not contain resources from this directory",
- artifactLocation.getExecutionRootRelativePath()))
- .submit(context);
- }
+ private static boolean isSourceOrWhitelistedGenPath(
+ ArtifactLocation artifactLocation, Set<String> whitelistedGenResourcePaths) {
+ return artifactLocation.isSource()
+ || whitelistedGenResourcePaths.contains(artifactLocation.getRelativePath());
}
@Nullable
@@ -169,7 +201,6 @@
return null;
}
- @NotNull
private ImmutableList<AndroidResourceModule> buildAndroidResourceModules(
WorkspaceBuilder workspaceBuilder) {
// Filter empty resource modules
diff --git a/aswb/src/com/google/idea/blaze/android/sync/importer/problems/AddGeneratedResourceDirectoryNavigatable.java b/aswb/src/com/google/idea/blaze/android/sync/importer/problems/AddGeneratedResourceDirectoryNavigatable.java
new file mode 100644
index 0000000..59839bf
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/importer/problems/AddGeneratedResourceDirectoryNavigatable.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync.importer.problems;
+
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
+import com.google.idea.blaze.android.projectview.GenfilesPath;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+import com.google.idea.blaze.base.projectview.ProjectViewEdit;
+import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import java.io.File;
+
+/**
+ * Action to whitelist a generated resource directory in the local project view file.
+ *
+ * <p>This is unfortunately wrapped in a navigatable, because the IntelliJ ProblemsView class
+ * doesn't have an extension point or action group ID for extending the right-click actions.
+ *
+ * <p>Ideally, we would have a quick fix in the java R.type.foo and xml @type/foo references, but to
+ * know which generated directory contains type.foo, we would need to parse the resources, which
+ * would be expensive.
+ */
+class AddGeneratedResourceDirectoryNavigatable implements Navigatable {
+
+ private final Project project;
+ private final File projectViewFile;
+ private final ArtifactLocation generatedResDir;
+
+ AddGeneratedResourceDirectoryNavigatable(
+ Project project, File projectViewFile, ArtifactLocation generatedResDir) {
+ this.project = project;
+ this.projectViewFile = projectViewFile;
+ this.generatedResDir = generatedResDir;
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ int addToProjectView =
+ Messages.showYesNoDialog(
+ String.format(
+ "Whitelist generated resource directory \"%s\" in project view?",
+ generatedResDir.getRelativePath()),
+ "Whitelist generated resource",
+ null);
+ if (addToProjectView == Messages.YES) {
+ addDirectoryToProjectView(project, projectViewFile, generatedResDir);
+ }
+ }
+
+ @Override
+ public boolean canNavigate() {
+ return true;
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return false;
+ }
+
+ private static void addDirectoryToProjectView(
+ Project project, File projectViewFile, ArtifactLocation generatedResDir) {
+ ProjectViewEdit edit =
+ ProjectViewEdit.editLocalProjectView(
+ project,
+ builder -> {
+ ListSection<GenfilesPath> existingSection =
+ builder.getLast(GeneratedAndroidResourcesSection.KEY);
+ ListSection.Builder<GenfilesPath> directoryBuilder =
+ ListSection.update(GeneratedAndroidResourcesSection.KEY, existingSection);
+ directoryBuilder.add(new GenfilesPath(generatedResDir.getRelativePath()));
+ builder.replace(existingSection, directoryBuilder);
+ return true;
+ });
+ if (edit == null) {
+ Messages.showErrorDialog(
+ "Could not modify project view. Check for errors in your project view and try again",
+ "Error");
+ return;
+ }
+ edit.apply();
+ VirtualFile projectView = VfsUtil.findFileByIoFile(projectViewFile, false);
+ if (projectView != null) {
+ FileEditorManager.getInstance(project)
+ .openEditor(new OpenFileDescriptor(project, projectView), true);
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceClassifier.java b/aswb/src/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceClassifier.java
new file mode 100644
index 0000000..10ec342
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceClassifier.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync.importer.problems;
+
+import com.android.SdkConstants;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.LocaleQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.resources.ResourceFolderType;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+import com.google.idea.blaze.base.io.FileAttributeProvider;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.intellij.openapi.diagnostic.Logger;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+/**
+ * Classifies generated resource directories as "interesting" or not.
+ *
+ * <p>Uninteresting resources: It is very common to have string translations as the only generated
+ * resources for a rule. If that's the case, we can ignore them since we likely already have a
+ * source variant of the same resource as a baseline for resolving references in Java or XML.
+ *
+ * <p>Other generated resources are interesting.
+ */
+class GeneratedResourceClassifier {
+
+ private static final Logger logger = Logger.getInstance(GeneratedResourceClassifier.class);
+
+ private final ImmutableSortedMap<ArtifactLocation, Integer> interestingDirectories;
+
+ GeneratedResourceClassifier(
+ Collection<ArtifactLocation> generatedResourceLocations,
+ ArtifactLocationDecoder artifactLocationDecoder,
+ ListeningExecutorService executorService) {
+ FileAttributeProvider fileAttributeProvider = FileAttributeProvider.getInstance();
+ List<ListenableFuture<GenResourceClassification>> jobs =
+ generatedResourceLocations
+ .stream()
+ .map(
+ location ->
+ executorService.submit(
+ () ->
+ classifyLocation(
+ location, artifactLocationDecoder, fileAttributeProvider)))
+ .collect(Collectors.toList());
+
+ ImmutableSortedMap.Builder<ArtifactLocation, Integer> interesting =
+ ImmutableSortedMap.naturalOrder();
+ try {
+ for (GenResourceClassification classification : Futures.allAsList(jobs).get()) {
+ if (classification.isInteresting) {
+ interesting.put(classification.artifactLocation, classification.numSubDirs);
+ }
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ interesting = considerAllInteresting(generatedResourceLocations);
+ } catch (ExecutionException e) {
+ logger.error(e);
+ interesting = considerAllInteresting(generatedResourceLocations);
+ } finally {
+ interestingDirectories = interesting.build();
+ }
+ }
+
+ /**
+ * Returns a collection of interesting directories as a sorted map from directory to estimated
+ * size of subdirectory (to estimate cost of indexing).
+ *
+ * @return map of interesting directories and associated sizes
+ */
+ public ImmutableSortedMap<ArtifactLocation, Integer> getInterestingDirectories() {
+ return interestingDirectories;
+ }
+
+ private static ImmutableSortedMap.Builder<ArtifactLocation, Integer> considerAllInteresting(
+ Collection<ArtifactLocation> generatedResourceLocations) {
+ ImmutableSortedMap.Builder<ArtifactLocation, Integer> builder =
+ ImmutableSortedMap.naturalOrder();
+ for (ArtifactLocation location : generatedResourceLocations) {
+ builder.put(location, -1);
+ }
+ return builder;
+ }
+
+ private static class GenResourceClassification {
+ final boolean isInteresting;
+ final ArtifactLocation artifactLocation;
+ final int numSubDirs;
+
+ GenResourceClassification(
+ boolean isInteresting, ArtifactLocation artifactLocation, int numSubDirs) {
+ this.isInteresting = isInteresting;
+ this.artifactLocation = artifactLocation;
+ this.numSubDirs = numSubDirs;
+ }
+
+ static GenResourceClassification uninteresting(
+ ArtifactLocation artifactLocation, int numSubDirs) {
+ return new GenResourceClassification(false, artifactLocation, numSubDirs);
+ }
+
+ static GenResourceClassification interesting(
+ ArtifactLocation artifactLocation, int numSubDirs) {
+ return new GenResourceClassification(true, artifactLocation, numSubDirs);
+ }
+ }
+
+ private static GenResourceClassification classifyLocation(
+ ArtifactLocation artifactLocation,
+ ArtifactLocationDecoder artifactLocationDecoder,
+ FileAttributeProvider fileAttributeProvider) {
+ File resDirectory = artifactLocationDecoder.decode(artifactLocation);
+ File[] children = fileAttributeProvider.listFiles(resDirectory);
+ if (children == null) {
+ return GenResourceClassification.uninteresting(artifactLocation, 0);
+ }
+ if (mayHaveNonStringTranslations(children)) {
+ return GenResourceClassification.interesting(artifactLocation, children.length);
+ } else {
+ return GenResourceClassification.uninteresting(artifactLocation, children.length);
+ }
+ }
+
+ @VisibleForTesting
+ static boolean mayHaveNonStringTranslations(File[] resDirectoryChildren) {
+ return Arrays.stream(resDirectoryChildren)
+ .anyMatch(child -> mayHaveNonStringTranslations(child.getName()));
+ }
+
+ private static boolean mayHaveNonStringTranslations(String dirName) {
+ // String translations only sit in the values-xx-rYY directories, so we can rule out other
+ // directories quickly.
+ if (!dirName.contains(SdkConstants.RES_QUALIFIER_SEP)) {
+ return true;
+ }
+ if (ResourceFolderType.getFolderType(dirName) != ResourceFolderType.VALUES) {
+ return true;
+ }
+ FolderConfiguration config = FolderConfiguration.getConfigForFolder(dirName);
+ // Conservatively say it's interesting if there is an unrecognized configuration.
+ if (config == null) {
+ return true;
+ }
+ // If this is a translation mixed with something else, consider it a translation directory.
+ boolean hasTranslation = false;
+ for (ResourceQualifier qualifier : config.getQualifiers()) {
+ if (qualifier instanceof LocaleQualifier) {
+ hasTranslation = true;
+ }
+ }
+ return !hasTranslation;
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceWarnings.java b/aswb/src/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceWarnings.java
new file mode 100644
index 0000000..1e6afae
--- /dev/null
+++ b/aswb/src/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceWarnings.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync.importer.problems;
+
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
+import com.google.idea.blaze.base.async.executor.BlazeExecutor;
+import com.google.idea.blaze.base.ideinfo.ArtifactLocation;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.scope.output.IssueOutput;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.intellij.openapi.project.Project;
+import java.io.File;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/** Submits generated resource warnings and potential fixes to the problems view. */
+public class GeneratedResourceWarnings {
+
+ private GeneratedResourceWarnings() {}
+
+ public static void submit(
+ Project project,
+ BlazeContext context,
+ ProjectViewSet projectViewSet,
+ ArtifactLocationDecoder artifactLocationDecoder,
+ Set<ArtifactLocation> generatedResourceLocations,
+ Set<String> whitelistedLocations) {
+ if (generatedResourceLocations.isEmpty()) {
+ return;
+ }
+ Set<ArtifactLocation> nonWhitelistedLocations = new HashSet<>();
+ Set<String> unusedWhitelistEntries = new HashSet<>();
+ filterWhitelistedEntries(
+ generatedResourceLocations,
+ whitelistedLocations,
+ nonWhitelistedLocations,
+ unusedWhitelistEntries);
+ // Tag any warnings with the project view file.
+ File projectViewFile = projectViewSet.getTopLevelProjectViewFile().projectViewFile;
+ if (!nonWhitelistedLocations.isEmpty()) {
+ GeneratedResourceClassifier classifier =
+ new GeneratedResourceClassifier(
+ nonWhitelistedLocations,
+ artifactLocationDecoder,
+ BlazeExecutor.getInstance().getExecutor());
+ ImmutableSortedMap<ArtifactLocation, Integer> interestingDirectories =
+ classifier.getInterestingDirectories();
+ if (!interestingDirectories.isEmpty()) {
+ IssueOutput.warn(
+ String.format(
+ "Dropping %d generated resource directories.\n"
+ + "R classes will not contain resources from these directories.\n"
+ + "Double-click to add to project view if needed to resolve references.",
+ interestingDirectories.size()))
+ .inFile(projectViewFile)
+ .onLine(1)
+ .inColumn(1)
+ .submit(context);
+ for (Map.Entry<ArtifactLocation, Integer> entry : interestingDirectories.entrySet()) {
+ IssueOutput.warn(
+ String.format(
+ "Dropping generated resource directory '%s' w/ %d subdirs",
+ entry.getKey(), entry.getValue()))
+ .inFile(projectViewFile)
+ .navigatable(
+ new AddGeneratedResourceDirectoryNavigatable(
+ project, projectViewFile, entry.getKey()))
+ .submit(context);
+ }
+ }
+ }
+ // Warn about unused parts of the whitelist.
+ if (!unusedWhitelistEntries.isEmpty()) {
+ IssueOutput.warn(
+ String.format(
+ "%d unused entries in project view section \"%s\":\n%s",
+ unusedWhitelistEntries.size(),
+ GeneratedAndroidResourcesSection.KEY.getName(),
+ String.join("\n ", unusedWhitelistEntries)))
+ .inFile(projectViewFile)
+ .submit(context);
+ }
+ }
+
+ private static void filterWhitelistedEntries(
+ Set<ArtifactLocation> generatedResourceLocations,
+ Set<String> whitelistedLocations,
+ Set<ArtifactLocation> nonWhitelistedLocations,
+ Set<String> unusedWhitelistEntries) {
+ unusedWhitelistEntries.addAll(whitelistedLocations);
+ for (ArtifactLocation location : generatedResourceLocations) {
+ if (whitelistedLocations.contains(location.getRelativePath())) {
+ unusedWhitelistEntries.remove(location.getRelativePath());
+ } else {
+ nonWhitelistedLocations.add(location);
+ }
+ }
+ }
+}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
index 76d4767..20d5462 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
import com.google.idea.blaze.android.resources.LightResourceClassService;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationHandler;
import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
@@ -190,13 +191,17 @@
++totalRunConfigurationModules;
}
+ int whitelistedGenResources =
+ projectViewSet.listItems(GeneratedAndroidResourcesSection.KEY).size();
context.output(
PrintOutput.log(
String.format(
- "Android resource module count: %d, run config modules: %d, order entries: %d",
+ "Android resource module count: %d, run config modules: %d, order entries: %d, "
+ + "generated resources: %d",
syncData.importResult.androidResourceModules.size(),
totalRunConfigurationModules,
- totalOrderEntries)));
+ totalOrderEntries,
+ whitelistedGenResources)));
}
} else {
AndroidFacetModuleCustomizer.removeAndroidFacet(workspaceModule);
diff --git a/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java b/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java
index 2740352..05b52e4 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/sdk/AndroidSdkFromProjectView.java
@@ -94,7 +94,7 @@
return new AndroidSdkPlatform(androidSdk, androidSdkApiLevel);
}
- private static String getAvailableSdkPlatforms(Collection<Sdk> sdks) {
+ public static String getAvailableSdkPlatforms(Collection<Sdk> sdks) {
List<String> names = Lists.newArrayList();
for (Sdk sdk : sdks) {
AndroidSdkAdditionalData additionalData = AndroidSdkUtils.getAndroidSdkAdditionalData(sdk);
diff --git a/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkExperiment.java b/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkExperiment.java
deleted file mode 100644
index 1ad1842..0000000
--- a/aswb/src/com/google/idea/blaze/android/sync/sdk/SdkExperiment.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.android.sync.sdk;
-
-import com.android.tools.idea.startup.AndroidStudioInitializer;
-
-/**
- * Wrapper class to keep track of "experiment" for later deletion.
- *
- * <p>The experiment is actually controlled by the JVM property, and we can't use a "normal"
- * experiment for that since the JVM property also affects upstream Android Studio.
- */
-public class SdkExperiment {
- public static boolean useStandardSdkManager() {
- return AndroidStudioInitializer.isAndroidSdkManagerEnabled();
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/sdklegacy/AndroidSdkPlatformSyncer.java b/aswb/src/com/google/idea/blaze/android/sync/sdklegacy/AndroidSdkPlatformSyncer.java
deleted file mode 100644
index aa0fcd0..0000000
--- a/aswb/src/com/google/idea/blaze/android/sync/sdklegacy/AndroidSdkPlatformSyncer.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.android.sync.sdklegacy;
-
-import com.android.tools.idea.startup.AndroidStudioInitializer;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.idea.blaze.android.projectview.AndroidSdkPlatformSection;
-import com.google.idea.blaze.android.settings.AswbGlobalSettings;
-import com.google.idea.blaze.android.sync.model.AndroidSdkPlatform;
-import com.google.idea.blaze.base.projectview.ProjectViewManager;
-import com.google.idea.blaze.base.projectview.ProjectViewSet;
-import com.google.idea.blaze.base.scope.BlazeContext;
-import com.google.idea.blaze.base.scope.output.IssueOutput;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.Sdk;
-import java.io.File;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.jetbrains.android.sdk.AndroidPlatform;
-import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
-import org.jetbrains.android.sdk.AndroidSdkUtils;
-
-/** Calculates AndroidSdkPlatform. */
-@Deprecated
-public class AndroidSdkPlatformSyncer {
- @Nullable
- public static AndroidSdkPlatform getAndroidSdkPlatform(Project project, BlazeContext context) {
-
- final String localSdkLocation;
- if (AndroidStudioInitializer.isAndroidSdkManagerEnabled()) {
- Sdk sdk = Iterables.getFirst(AndroidSdkUtils.getAllAndroidSdks(), null);
- if (sdk == null) {
- IssueOutput.error(
- "Error: No Android SDK configured. Please use the SDK manager to configure.")
- .submit(context);
- return null;
- }
- localSdkLocation = sdk.getHomePath();
- } else {
- localSdkLocation = AswbGlobalSettings.getInstance().getLocalSdkLocation();
- if (localSdkLocation == null) {
- IssueOutput.error(
- "Error: No Android SDK synced yet."
- + (Blaze.defaultBuildSystem() == BuildSystem.Blaze
- ? " Please sync SDK following go/aswb-sdk."
- : ""))
- .submit(context);
- return null;
- }
- }
-
- String androidSdkPlatform = null;
- ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
- if (projectViewSet != null) {
- androidSdkPlatform = projectViewSet.getScalarValue(AndroidSdkPlatformSection.KEY);
- }
-
- // This is verified in the project view verification step, but double-check here
- if (androidSdkPlatform == null) {
- IssueOutput.error(
- "No android_sdk_platform set. Please ensure this is set to a platform SDK directory.")
- .submit(context);
- return null;
- }
-
- String androidSdk =
- BlazeAndroidSdk.getAndroidSdkLevelFromLocalChannel(localSdkLocation, androidSdkPlatform);
-
- if (androidSdk == null) {
- IssueOutput.error(
- Joiner.on("\n")
- .join(
- "No such android_sdk_platform: " + androidSdkPlatform,
- "Available android_sdk_platforms are: "
- + getAvailableSdkPlatforms(localSdkLocation)))
- .inFile(projectViewSet.getTopLevelProjectViewFile().projectViewFile)
- .submit(context);
- return null;
- }
-
- Sdk sdk = AndroidSdkUtils.findSuitableAndroidSdk(androidSdk);
- if (sdk == null) {
- ImmutableList.Builder<String> error =
- ImmutableList.<String>builder()
- .add(
- String.format(
- "Can't find a matching SDK "
- + "(was looking for '%s' in the '%s' platform directory).",
- androidSdk, androidSdkPlatform),
- "Available android_sdk_platforms are: "
- + getAvailableSdkPlatforms(localSdkLocation));
- if (Blaze.defaultBuildSystem() == BuildSystem.Blaze) {
- error.add(
- "If you have no SDK, please sync your SDK by following go/aswb-sdk and try again. ",
- "If you have done everything correctly, this can be due to an SDK sync manager bug.",
- "To workaround, please delete ~/.AndroidStudioWithBlazeXX/system and restart");
- }
-
- IssueOutput.error(String.join("\n", error.build())).submit(context);
- return null;
- }
-
- int androidSdkApiLevel = getAndroidSdkApiLevel(androidSdk);
- return new AndroidSdkPlatform(androidSdk, androidSdkApiLevel);
- }
-
- private static String getAvailableSdkPlatforms(String localSdkDirectoryString) {
- File localSdkDirectory = new File(localSdkDirectoryString);
- if (localSdkDirectory.exists()) {
- File platformDirectory = new File(localSdkDirectory, "platforms");
- if (platformDirectory.exists()) {
- File[] children = platformDirectory.listFiles();
- if (children != null) {
- List<String> names = Lists.newArrayList();
- for (File child : children) {
- if (child.isDirectory()) {
- names.add('"' + child.getName() + '"');
- }
- }
- return "{" + Joiner.on(", ").join(names) + "}";
- }
- }
- }
- return "<No platforms found>";
- }
-
- private static int getAndroidSdkApiLevel(String androidSdk) {
- int androidSdkApiLevel = 1;
- Sdk sdk = AndroidSdkUtils.findSuitableAndroidSdk(androidSdk);
- if (sdk != null) {
- AndroidSdkAdditionalData additionalData =
- (AndroidSdkAdditionalData) sdk.getSdkAdditionalData();
- if (additionalData != null) {
- AndroidPlatform androidPlatform = additionalData.getAndroidPlatform();
- if (androidPlatform != null) {
- androidSdkApiLevel = androidPlatform.getApiLevel();
- }
- }
- }
- return androidSdkApiLevel;
- }
-}
diff --git a/aswb/src/com/google/idea/blaze/android/sync/sdklegacy/BlazeAndroidSdk.java b/aswb/src/com/google/idea/blaze/android/sync/sdklegacy/BlazeAndroidSdk.java
deleted file mode 100644
index bee2b3b..0000000
--- a/aswb/src/com/google/idea/blaze/android/sync/sdklegacy/BlazeAndroidSdk.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.android.sync.sdklegacy;
-
-import com.android.SdkConstants;
-import com.android.sdklib.AndroidTargetHash;
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.AndroidVersionHelper;
-import com.intellij.openapi.diagnostic.Logger;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-import javax.annotation.Nullable;
-
-/** Utility methods for handling the android sdk. */
-@Deprecated
-final class BlazeAndroidSdk {
- private static final Logger LOG = Logger.getInstance(BlazeAndroidSdk.class);
-
- private BlazeAndroidSdk() {}
-
- /** Reads the android sdk level from your local SDK directory. */
- public static String getAndroidSdkLevelFromLocalChannel(
- String localSdkLocation, String androidSdkPlatform) {
- File androidSdkPlatformsDir =
- new File(new File(new File(localSdkLocation), "platforms"), androidSdkPlatform);
- File sourcePropertiesFile = new File(androidSdkPlatformsDir, SdkConstants.FN_SOURCE_PROP);
- return getAndroidSdkLevelFromSourceProperties(sourcePropertiesFile);
- }
-
- @Nullable
- public static String getAndroidSdkLevelFromSourceProperties(File sourcePropertiesFile) {
- if (!sourcePropertiesFile.exists()) {
- return null;
- }
-
- AndroidVersion androidVersion =
- readAndroidVersionFromSourcePropertiesFile(sourcePropertiesFile);
- if (androidVersion == null) {
- LOG.warn("Could not read source.properties from: " + sourcePropertiesFile);
- return null;
- }
- return AndroidTargetHash.getPlatformHashString(androidVersion);
- }
-
- @Nullable
- private static AndroidVersion readAndroidVersionFromSourcePropertiesFile(
- File sourcePropertiesFile) {
- Properties props = parseProperties(sourcePropertiesFile);
- if (props == null) {
- return null;
- }
- try {
- return AndroidVersionHelper.create(props);
- } catch (AndroidVersion.AndroidVersionException e) {
- return null;
- }
- }
-
- /**
- * Parses the given file as properties file if it exists. Returns null if the file does not exist,
- * cannot be parsed or has no properties.
- */
- @Nullable
- private static Properties parseProperties(File propsFile) {
- if (!propsFile.exists()) {
- return null;
- }
- try (InputStream fis = new FileInputStream(propsFile)) {
- Properties props = new Properties();
- props.load(fis);
-
- // To be valid, there must be at least one property in it.
- if (props.size() > 0) {
- return props;
- }
- } catch (IOException e) {
- // Ignore
- }
- return null;
- }
-}
diff --git a/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporterTest.java b/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporterTest.java
index 1f16d2d..c839293 100644
--- a/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporterTest.java
+++ b/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporterTest.java
@@ -19,6 +19,8 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection;
+import com.google.idea.blaze.android.projectview.GenfilesPath;
import com.google.idea.blaze.android.sync.BlazeAndroidJavaSyncAugmenter;
import com.google.idea.blaze.android.sync.model.AndroidResourceModule;
import com.google.idea.blaze.android.sync.model.BlazeAndroidImportResult;
@@ -34,6 +36,7 @@
import com.google.idea.blaze.base.ideinfo.TargetKey;
import com.google.idea.blaze.base.ideinfo.TargetMap;
import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
+import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
@@ -52,6 +55,7 @@
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
import com.google.idea.common.experiments.ExperimentService;
import com.google.idea.common.experiments.MockExperimentService;
@@ -72,6 +76,10 @@
private static final String FAKE_GEN_ROOT_EXECUTION_PATH_FRAGMENT =
"blaze-out/gcc-4.X.Y-crosstool-v17-hybrid-grtev3-k8-fastbuild/bin";
+ private static final ArtifactLocationDecoder FAKE_ARTIFACT_DECODER =
+ (ArtifactLocationDecoder)
+ artifactLocation -> new File("/", artifactLocation.getRelativePath());
+
private static final BlazeImportSettings DUMMY_IMPORT_SETTINGS =
new BlazeImportSettings("", "", "", "", "", BuildSystem.Blaze);
@@ -91,6 +99,9 @@
BlazeImportSettingsManager.class, new BlazeImportSettingsManager(project));
BlazeImportSettingsManager.getInstance(getProject()).setImportSettings(DUMMY_IMPORT_SETTINGS);
+ MockFileAttributeProvider mockFileAttributeProvider = new MockFileAttributeProvider();
+ applicationServices.register(FileAttributeProvider.class, mockFileAttributeProvider);
+
context = new BlazeContext();
context.addOutputSink(IssueOutput.class, errorCollector);
}
@@ -102,7 +113,12 @@
BlazeAndroidWorkspaceImporter workspaceImporter =
new BlazeAndroidWorkspaceImporter(
- project, context, workspaceRoot, projectViewSet, targetMapBuilder.build());
+ project,
+ context,
+ workspaceRoot,
+ projectViewSet,
+ targetMapBuilder.build(),
+ FAKE_ARTIFACT_DECODER);
return workspaceImporter.importWorkspace();
}
@@ -364,9 +380,11 @@
WorkspaceLanguageSettings workspaceLanguageSettings =
new WorkspaceLanguageSettings(
WorkspaceType.ANDROID, ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA));
+ ProjectViewSet projectViewSet = ProjectViewSet.builder().add(projectView).build();
for (TargetIdeInfo target : targetMap.targets()) {
if (importRoots.importAsSource(target.key.label)) {
- syncAugmenter.addJarsForSourceTarget(workspaceLanguageSettings, target, jars, genJars);
+ syncAugmenter.addJarsForSourceTarget(
+ workspaceLanguageSettings, projectViewSet, target, jars, genJars);
}
}
@@ -416,9 +434,11 @@
WorkspaceLanguageSettings workspaceLanguageSettings =
new WorkspaceLanguageSettings(
WorkspaceType.ANDROID, ImmutableSet.of(LanguageClass.ANDROID, LanguageClass.JAVA));
+ ProjectViewSet projectViewSet = ProjectViewSet.builder().add(projectView).build();
for (TargetIdeInfo target : targetMap.targets()) {
if (importRoots.importAsSource(target.key.label)) {
- syncAugmenter.addJarsForSourceTarget(workspaceLanguageSettings, target, jars, genJars);
+ syncAugmenter.addJarsForSourceTarget(
+ workspaceLanguageSettings, projectViewSet, target, jars, genJars);
}
}
assertThat(
@@ -599,7 +619,182 @@
.build());
importWorkspace(workspaceRoot, targetMapBuilder, projectView);
- errorCollector.assertIssueContaining("Dropping generated resource");
+ errorCollector.assertIssueContaining("Dropping 1 generated resource");
+ }
+
+ @Test
+ public void testMixingGeneratedAndNonGeneratedSourcesWhitelisted() {
+ ProjectView projectView =
+ ProjectView.builder()
+ .add(
+ ListSection.builder(DirectorySection.KEY)
+ .add(DirectoryEntry.include(new WorkspacePath("java/example"))))
+ .add(
+ ListSection.builder(GeneratedAndroidResourcesSection.KEY)
+ .add(new GenfilesPath("java/example/res")))
+ .build();
+
+ TargetMapBuilder targetMapBuilder =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//java/example:lib")
+ .setBuildFile(source("java/example/BUILD"))
+ .setKind("android_library")
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setManifestFile(source("java/example/AndroidManifest.xml"))
+ .addResource(source("java/example/res"))
+ .addResource(gen("java/example/res"))
+ .setGenerateResourceClass(true)
+ .setResourceJavaPackage("com.google.android.example"))
+ .build());
+
+ BlazeAndroidImportResult result = importWorkspace(workspaceRoot, targetMapBuilder, projectView);
+ errorCollector.assertNoIssues();
+ assertThat(result.androidResourceModules)
+ .containsExactly(
+ AndroidResourceModule.builder(TargetKey.forPlainTarget(new Label("//java/example:lib")))
+ .addResourceAndTransitiveResource(source("java/example/res"))
+ .addResourceAndTransitiveResource(gen("java/example/res"))
+ .build());
+ }
+
+ @Test
+ public void testMixingGeneratedAndNonGeneratedSourcesPartlyWhitelisted() {
+ ProjectView projectView =
+ ProjectView.builder()
+ .add(
+ ListSection.builder(DirectorySection.KEY)
+ .add(DirectoryEntry.include(new WorkspacePath("java/example")))
+ .add(DirectoryEntry.include(new WorkspacePath("java/example2")))
+ .add(DirectoryEntry.include(new WorkspacePath("java/uninterestingdir"))))
+ .add(
+ ListSection.builder(GeneratedAndroidResourcesSection.KEY)
+ .add(new GenfilesPath("java/example/res"))
+ .add(new GenfilesPath("unused/whitelisted/path/res")))
+ .build();
+
+ TargetMapBuilder targetMapBuilder =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//java/example:lib")
+ .setBuildFile(source("java/example/BUILD"))
+ .setKind("android_library")
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setManifestFile(source("java/example/AndroidManifest.xml"))
+ .addResource(source("java/example/res"))
+ .addResource(gen("java/example/res"))
+ .setGenerateResourceClass(true)
+ .setResourceJavaPackage("com.google.android.example"))
+ .build())
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//java/example2:lib")
+ .setBuildFile(source("java/example2/BUILD"))
+ .setKind("android_library")
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setManifestFile(source("java/example2/AndroidManifest.xml"))
+ .addResource(source("java/example2/res"))
+ .addResource(gen("java/example2/res"))
+ .setGenerateResourceClass(true)
+ .setResourceJavaPackage("com.google.android.example2"))
+ .build())
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//java/uninterestingdir:lib")
+ .setBuildFile(source("java/uninterestingdir/BUILD"))
+ .setKind("android_library")
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setManifestFile(source("java/uninterestingdir/AndroidManifest.xml"))
+ .addResource(source("java/uninterestingdir/res"))
+ .addResource(gen("java/uninterestingdir/res"))
+ .setGenerateResourceClass(true)
+ .setResourceJavaPackage("com.google.android.uninterestingdir"))
+ .build());
+
+ importWorkspace(workspaceRoot, targetMapBuilder, projectView);
+ errorCollector.assertIssues(
+ "Dropping 1 generated resource directories.\n"
+ + "R classes will not contain resources from these directories.\n"
+ + "Double-click to add to project view if needed to resolve references.",
+ "Dropping generated resource directory "
+ + String.format("'%s/java/example2/res'", FAKE_GEN_ROOT_EXECUTION_PATH_FRAGMENT)
+ + " w/ 2 subdirs",
+ "1 unused entries in project view section \"generated_android_resource_directories\":\n"
+ + "unused/whitelisted/path/res");
+ }
+
+ @Test
+ public void testMixingGeneratedAndNonGeneratedSourcesNoInterestingDirectories() {
+ ProjectView projectView =
+ ProjectView.builder()
+ .add(
+ ListSection.builder(DirectorySection.KEY)
+ .add(DirectoryEntry.include(new WorkspacePath("java/uninterestingdir"))))
+ .build();
+
+ TargetMapBuilder targetMapBuilder =
+ TargetMapBuilder.builder()
+ .addTarget(
+ TargetIdeInfo.builder()
+ .setLabel("//java/uninterestingdir:lib")
+ .setBuildFile(source("java/uninterestingdir/BUILD"))
+ .setKind("android_library")
+ .setAndroidInfo(
+ AndroidIdeInfo.builder()
+ .setManifestFile(source("java/uninterestingdir/AndroidManifest.xml"))
+ .addResource(source("java/uninterestingdir/res"))
+ .addResource(gen("java/uninterestingdir/res"))
+ .setGenerateResourceClass(true)
+ .setResourceJavaPackage("com.google.android.uninterestingdir"))
+ .build());
+
+ BlazeAndroidImportResult result = importWorkspace(workspaceRoot, targetMapBuilder, projectView);
+ errorCollector.assertNoIssues();
+ assertThat(result.androidResourceModules)
+ .containsExactly(
+ AndroidResourceModule.builder(
+ TargetKey.forPlainTarget(new Label("//java/uninterestingdir:lib")))
+ .addResourceAndTransitiveResource(source("java/uninterestingdir/res"))
+ .build());
+ }
+
+ /**
+ * Mock provider to satisfy directory listing queries from {@link
+ * com.google.idea.blaze.android.sync.importer.problems.GeneratedResourceClassifier}.
+ */
+ private static class MockFileAttributeProvider extends FileAttributeProvider {
+
+ // Return a few non-translation directories so that directories are considered interesting,
+ // or return only-translation directories so that it's considered uninteresting.
+ @Override
+ public File[] listFiles(File directory) {
+ File interestingResDir1 = FAKE_ARTIFACT_DECODER.decode(gen("java/example/res"));
+ if (directory.equals(interestingResDir1)) {
+ return new File[] {
+ new File("java/example/res/raw"), new File("java/example/res/values-es"),
+ };
+ }
+ File interestingResDir2 = FAKE_ARTIFACT_DECODER.decode(gen("java/example2/res"));
+ if (directory.equals(interestingResDir2)) {
+ return new File[] {
+ new File("java/example2/res/layout"), new File("java/example2/res/values-ar"),
+ };
+ }
+ File uninterestingResDir = FAKE_ARTIFACT_DECODER.decode(gen("java/uninterestingdir/res"));
+ if (directory.equals(uninterestingResDir)) {
+ return new File[] {
+ new File("java/uninterestingdir/res/values-ar"),
+ new File("java/uninterestingdir/res/values-es"),
+ };
+ }
+ return new File[0];
+ }
}
private ArtifactLocation source(String relativePath) {
diff --git a/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceClassifierTest.java b/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceClassifierTest.java
new file mode 100644
index 0000000..ab9d124
--- /dev/null
+++ b/aswb/tests/unittests/com/google/idea/blaze/android/sync/importer/problems/GeneratedResourceClassifierTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.android.sync.importer.problems;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.base.BlazeTestCase;
+import java.io.File;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link GeneratedResourceClassifier}. */
+@RunWith(JUnit4.class)
+public class GeneratedResourceClassifierTest extends BlazeTestCase {
+
+ @Test
+ public void rankEmpty() {
+ assertThat(GeneratedResourceClassifier.mayHaveNonStringTranslations(files())).isFalse();
+ }
+
+ @Test
+ public void rankOneValues() {
+ assertThat(GeneratedResourceClassifier.mayHaveNonStringTranslations(files("foo/res/values")))
+ .isTrue();
+ }
+
+ @Test
+ public void rankOneTranslation() {
+ assertThat(GeneratedResourceClassifier.mayHaveNonStringTranslations(files("foo/res/values-af")))
+ .isFalse();
+ }
+
+ @Test
+ public void rankOneOtherType() {
+ assertThat(GeneratedResourceClassifier.mayHaveNonStringTranslations(files("foo/res/raw")))
+ .isTrue();
+ }
+
+ @Test
+ public void onlyTranslations() {
+ assertThat(
+ GeneratedResourceClassifier.mayHaveNonStringTranslations(
+ files("foo/res/values-af", "foo/res/values-ar", "foo/res/values-b+sr+Latn")))
+ .isFalse();
+ }
+
+ @Test
+ public void onlyTranslationsWithOther() {
+ assertThat(
+ GeneratedResourceClassifier.mayHaveNonStringTranslations(
+ files(
+ "foo/res/values-af",
+ "foo/res/values-fr-rCA",
+ "foo/res/values-b+sr+Latn",
+ "foo/res/values-af-sw600dp",
+ "foo/res/values-fr-rCA-sw600dp",
+ "foo/res/values-b+sr+Latn-sw600dp")))
+ .isFalse();
+ }
+
+ @Test
+ public void mixedWithTranslationsAndDefaultValues() {
+ assertThat(
+ GeneratedResourceClassifier.mayHaveNonStringTranslations(
+ files("foo/res/values", "foo/res/values-af", "foo/res/values-fr-rCA")))
+ .isTrue();
+ }
+
+ @Test
+ public void mixedWithTranslationsDrawableXml() {
+ assertThat(
+ GeneratedResourceClassifier.mayHaveNonStringTranslations(
+ files(
+ "foo/res/drawable-xxhdpi",
+ "foo/res/values-af",
+ "foo/res/values-fr-rCA",
+ "foo/res/xml")))
+ .isTrue();
+ }
+
+ @Test
+ public void mixedWithIncorrectConfig() {
+ assertThat(
+ GeneratedResourceClassifier.mayHaveNonStringTranslations(
+ files(
+ "foo/res/values-notaqualifier", "foo/res/values-af", "foo/res/values-fr-rCA")))
+ .isTrue();
+ }
+
+ @Test
+ public void mixedWithIncorrectFolder() {
+ assertThat(
+ GeneratedResourceClassifier.mayHaveNonStringTranslations(
+ files("foo/res/php_scripts", "foo/res/values-af", "foo/res/values-fr-rCA")))
+ .isTrue();
+ }
+
+ private static File[] files(String... paths) {
+ return Arrays.stream(paths).map(File::new).toArray(File[]::new);
+ }
+}
diff --git a/base/BUILD b/base/BUILD
index 2921507..6902fd5 100644
--- a/base/BUILD
+++ b/base/BUILD
@@ -8,6 +8,7 @@
deps = [
"//common/binaryhelper",
"//common/experiments",
+ "//common/formatter",
"//intellij_platform_sdk:plugin_api",
"//proto_deps",
"@jsr305_annotations//jar",
diff --git a/base/src/META-INF/blaze-base.xml b/base/src/META-INF/blaze-base.xml
index ba5bbc3..1e12599 100644
--- a/base/src/META-INF/blaze-base.xml
+++ b/base/src/META-INF/blaze-base.xml
@@ -161,6 +161,7 @@
serviceImplementation="com.google.idea.blaze.base.help.BlazeHelpHandlerImpl"/>
<additionalTextAttributes scheme="Default" file="base/resources/colorSchemes/BuildDefault.xml"/>
+ <typedHandler implementation="com.google.idea.blaze.base.lang.buildfile.completion.BuildCompletionAutoPopupHandler"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
@@ -194,12 +195,14 @@
<enterHandlerDelegate implementation="com.google.idea.blaze.base.lang.buildfile.editor.BuildEnterHandler" order="after EnterBetweenBracesHandler"/>
<completion.contributor language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.completion.ParameterCompletionContributor"/>
<completion.contributor language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.completion.BuiltInFunctionCompletionContributor"/>
+ <completion.contributor language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.completion.BuiltInSymbolCompletionContributor"/>
<completion.contributor language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.completion.BuiltInFunctionAttributeCompletionContributor"/>
<completion.contributor language="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.completion.ArgumentCompletionContributor"/>
<langCodeStyleSettingsProvider implementation="com.google.idea.blaze.base.lang.buildfile.formatting.BuildLanguageCodeStyleSettingsProvider"/>
<codeStyleSettingsProvider implementation="com.google.idea.blaze.base.lang.buildfile.formatting.BuildCodeStyleSettingsProvider"/>
<editor.backspaceModeOverride language="BUILD" implementationClass="com.intellij.codeInsight.editorActions.SmartBackspaceDisabler"/>
<filetype.stubBuilder filetype="BUILD" implementationClass="com.google.idea.blaze.base.lang.buildfile.stubs.BuildFileStubBuilder"/>
+ <editorNotificationProvider implementation="com.google.idea.blaze.base.lang.AdditionalLanguagesHelper"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.lang">
@@ -233,6 +236,13 @@
</component>
</application-components>
+ <project-components>
+ <component>
+ <implementation-class>com.google.idea.blaze.base.buildmodifier.BuildifierFormatter</implementation-class>
+ <loadForDefaultProject/>
+ </component>
+ </project-components>
+
<extensionPoints>
<extensionPoint qualifiedName="com.google.idea.blaze.SyncListener" interface="com.google.idea.blaze.base.sync.SyncListener"/>
<extensionPoint qualifiedName="com.google.idea.blaze.SyncPlugin" interface="com.google.idea.blaze.base.sync.BlazeSyncPlugin"/>
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java b/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java
new file mode 100644
index 0000000..cf4574a
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierDelegatingCodeStyleManager.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.buildmodifier;
+
+import static java.util.Comparator.comparing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile.BlazeFileType;
+import com.google.idea.common.formatter.DelegatingCodeStyleManager;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.impl.CheckUtil;
+import com.intellij.util.IncorrectOperationException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A {@link CodeStyleManager} implementation which runs buildifier on BUILD files, and otherwise
+ * delegates to the existing formatter.
+ */
+public class BuildifierDelegatingCodeStyleManager extends DelegatingCodeStyleManager {
+
+ public BuildifierDelegatingCodeStyleManager(CodeStyleManager original) {
+ super(original);
+ }
+
+ @Override
+ public void reformatText(PsiFile file, int startOffset, int endOffset)
+ throws IncorrectOperationException {
+ if (overrideFormatterForFile(file)) {
+ formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset)));
+ } else {
+ super.reformatText(file, startOffset, endOffset);
+ }
+ }
+
+ @Override
+ public void reformatText(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ if (overrideFormatterForFile(file)) {
+ formatInternal(file, ranges);
+ } else {
+ super.reformatText(file, ranges);
+ }
+ }
+
+ @Override
+ public void reformatTextWithContext(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ if (overrideFormatterForFile(file)) {
+ formatInternal(file, ranges);
+ } else {
+ super.reformatTextWithContext(file, ranges);
+ }
+ }
+
+ private static boolean overrideFormatterForFile(PsiFile file) {
+ // don't format skylark extensions
+ return file instanceof BuildFile
+ && ((BuildFile) file).getBlazeFileType() == BlazeFileType.BuildPackage;
+ }
+
+ private void formatInternal(PsiFile file, Collection<TextRange> ranges) {
+ ApplicationManager.getApplication().assertWriteAccessAllowed();
+ PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
+ CheckUtil.checkWritable(file);
+
+ Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
+ if (document == null) {
+ return;
+ }
+ Map<TextRange, String> replacements = getFormatReplacements(document.getText(), ranges);
+ TreeMap<TextRange, String> sortedReplacements =
+ new TreeMap<>(comparing(TextRange::getStartOffset));
+ sortedReplacements.putAll(replacements);
+ performReplacements(document, sortedReplacements);
+ }
+
+ private static Map<TextRange, String> getFormatReplacements(
+ String text, Collection<TextRange> ranges) {
+ ImmutableMap.Builder<TextRange, String> output = ImmutableMap.builder();
+ for (TextRange range : ranges) {
+ String result = BuildFileFormatter.formatText(range.substring(text));
+ if (result == null) {
+ return ImmutableMap.of();
+ }
+ output.put(range, result);
+ }
+ return output.build();
+ }
+
+ private void performReplacements(
+ final Document document, final Map<TextRange, String> reverseSortedReplacements) {
+ WriteCommandAction.runWriteCommandAction(
+ getProject(),
+ () -> {
+ for (Map.Entry<TextRange, String> replacement : reverseSortedReplacements.entrySet()) {
+ TextRange range = replacement.getKey();
+ document.replaceString(
+ range.getStartOffset(), range.getEndOffset(), replacement.getValue());
+ }
+ PsiDocumentManager.getInstance(getProject()).commitDocument(document);
+ });
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierFormatter.java b/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierFormatter.java
new file mode 100644
index 0000000..ce786b3
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/buildmodifier/BuildifierFormatter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.buildmodifier;
+
+import com.google.idea.common.formatter.FormatterInstaller;
+import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.project.Project;
+
+/** Integrates buildifier with IntelliJ's formatter. */
+public class BuildifierFormatter extends AbstractProjectComponent {
+
+ protected BuildifierFormatter(Project project) {
+ super(project);
+ }
+
+ @Override
+ public void projectOpened() {
+ FormatterInstaller.replaceFormatter(myProject, BuildifierDelegatingCodeStyleManager::new);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java b/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java
index df5e61a..daeae84 100644
--- a/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java
+++ b/base/src/com/google/idea/blaze/base/buildmodifier/FileSaveHandler.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.base.buildmodifier;
import com.google.idea.blaze.base.bazel.BuildSystemProvider;
+import com.google.idea.blaze.base.settings.BlazeUserSettings;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
@@ -29,7 +30,7 @@
@Override
public void beforeDocumentSaving(final Document document) {
- if (!document.isWritable()) {
+ if (!BlazeUserSettings.getInstance().getFormatBuildFilesOnSave() || !document.isWritable()) {
return;
}
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
diff --git a/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java b/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java
index 69f8465..aeb0f24 100644
--- a/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java
+++ b/base/src/com/google/idea/blaze/base/io/FileAttributeProvider.java
@@ -44,4 +44,8 @@
public long getFileSize(File file) {
return file.length();
}
+
+ public File[] listFiles(File file) {
+ return file.listFiles();
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java b/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java
index ef4337a..2ebd868 100644
--- a/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java
+++ b/base/src/com/google/idea/blaze/base/issueparser/BlazeIssueParser.java
@@ -17,27 +17,25 @@
import static com.google.common.base.Preconditions.checkState;
-import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
+import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
-import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
-import com.google.idea.blaze.base.projectview.section.ListSection;
import com.google.idea.blaze.base.projectview.section.Section;
import com.google.idea.blaze.base.projectview.section.SectionKey;
import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
import com.google.idea.blaze.base.scope.output.IssueOutput;
-import com.intellij.openapi.project.Project;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import javax.annotation.Nullable;
+
/** Parses blaze output for compile errors. */
public class BlazeIssueParser {
@@ -69,27 +67,27 @@
}
}
- interface Parser {
- @NotNull
- ParseResult parse(@NotNull String currentLine, @NotNull List<String> previousLines);
+ /** Used by BlazeIssueParser. Generally implemented by subclassing SingleLineParser */
+ public interface Parser {
+ ParseResult parse(String currentLine, List<String> previousLines);
}
- abstract static class SingleLineParser implements Parser {
- @NotNull Pattern pattern;
+ /** Base for a Parser that consumes a single contextless line at a time, matched via regex */
+ public abstract static class SingleLineParser implements Parser {
+ Pattern pattern;
- SingleLineParser(@NotNull String regex) {
+ public SingleLineParser(String regex) {
pattern = Pattern.compile(regex);
}
@Override
- public ParseResult parse(
- @NotNull String currentLine, @NotNull List<String> multilineMatchResult) {
+ public ParseResult parse(String currentLine, List<String> multilineMatchResult) {
checkState(
multilineMatchResult.isEmpty(), "SingleLineParser recieved multiple lines of input");
return parse(currentLine);
}
- ParseResult parse(@NotNull String line) {
+ ParseResult parse(String line) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
return ParseResult.output(createIssue(matcher));
@@ -98,36 +96,61 @@
}
@Nullable
- protected abstract IssueOutput createIssue(@NotNull Matcher matcher);
+ protected abstract IssueOutput createIssue(Matcher matcher);
+ }
+
+ @Nullable
+ public static File fileFromAbsolutePath(String absolutePath) {
+ return new File(absolutePath);
+ }
+
+ @Nullable
+ public static File fileFromRelativePath(WorkspaceRoot workspaceRoot, String relativePath) {
+ try {
+ final WorkspacePath workspacePath = new WorkspacePath(relativePath);
+ return workspaceRoot.fileForPath(workspacePath);
+ } catch (IllegalArgumentException e) {
+ // Ignore -- malformed error message
+ return null;
+ }
+ }
+
+ /** Returns the file referenced by the target */
+ @Nullable
+ public static File fileFromTarget(WorkspaceRoot workspaceRoot, String targetString) {
+ Label label = Label.createIfValid(targetString);
+ if (label == null) {
+ return null;
+ }
+ try {
+ final WorkspacePath combined =
+ new WorkspacePath(label.blazePackage(), label.targetName().toString());
+ return workspaceRoot.fileForPath(combined);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /** Falls back to returning -1 if no integer can be parsed. */
+ public static int parseOptionalInt(String intString) {
+ try {
+ return Integer.parseInt(intString);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
}
static class CompileParser extends SingleLineParser {
- @NotNull private final WorkspaceRoot workspaceRoot;
+ private final WorkspaceRoot workspaceRoot;
- public CompileParser(@NotNull WorkspaceRoot workspaceRoot) {
- super("(.*?):([0-9]+):([0-9]+:)? (error|warning): (.*)");
+ CompileParser(WorkspaceRoot workspaceRoot) {
+ super("^([^/].*?):([0-9]+):(?:([0-9]+):)? (error|warning): (.*)$");
this.workspaceRoot = workspaceRoot;
}
@Override
- protected IssueOutput createIssue(@NotNull Matcher matcher) {
- final File file;
- try {
- String fileName = matcher.group(1);
- final WorkspacePath workspacePath;
- if (fileName.startsWith("//depot/google3/")) {
- workspacePath = new WorkspacePath(fileName.substring("//depot/google3/".length()));
- } else if (fileName.startsWith("/")) {
- workspacePath = workspaceRoot.workspacePathFor(new File(fileName));
- } else {
- workspacePath = new WorkspacePath(fileName);
- }
- file = workspaceRoot.fileForPath(workspacePath);
- } catch (IllegalArgumentException e) {
- // Ignore -- malformed error message
- return null;
- }
-
+ protected IssueOutput createIssue(Matcher matcher) {
+ final File file = fileFromRelativePath(workspaceRoot, matcher.group(1));
IssueOutput.Category type =
matcher.group(4).equals("error")
? IssueOutput.Category.ERROR
@@ -135,7 +158,7 @@
return IssueOutput.issue(type, matcher.group(5))
.inFile(file)
.onLine(Integer.parseInt(matcher.group(2)))
- .inColumn(parseOptionalInt(matcher.group(4)))
+ .inColumn(parseOptionalInt(matcher.group(3)))
.build();
}
}
@@ -145,9 +168,8 @@
Pattern.compile(
"(ERROR): (.*?):([0-9]+):([0-9]+): (Traceback \\(most recent call last\\):)");
- @NotNull
@Override
- public ParseResult parse(@NotNull String currentLine, @NotNull List<String> previousLines) {
+ public ParseResult parse(String currentLine, List<String> previousLines) {
if (previousLines.isEmpty()) {
if (PATTERN.matcher(currentLine).find()) {
return ParseResult.needsMoreInput();
@@ -179,36 +201,43 @@
static class BuildParser extends SingleLineParser {
BuildParser() {
- super("(ERROR): (.*?):([0-9]+):([0-9]+): (.*)");
+ super("^ERROR: (/.*?BUILD):([0-9]+):([0-9]+): (.*)$");
}
@Override
- protected IssueOutput createIssue(@NotNull Matcher matcher) {
- return IssueOutput.error(matcher.group(5))
- .inFile(new File(matcher.group(2)))
- .onLine(Integer.parseInt(matcher.group(3)))
- .inColumn(parseOptionalInt(matcher.group(4)))
+ protected IssueOutput createIssue(Matcher matcher) {
+ File file = fileFromAbsolutePath(matcher.group(1));
+ return IssueOutput.error(matcher.group(4))
+ .inFile(file)
+ .onLine(Integer.parseInt(matcher.group(2)))
+ .inColumn(parseOptionalInt(matcher.group(3)))
.build();
}
}
- /** Falls back to returning -1 if no integer can be parsed. */
- private static int parseOptionalInt(String intString) {
- try {
- return Integer.parseInt(intString);
- } catch (NumberFormatException e) {
- return -1;
- }
- }
-
static class LinelessBuildParser extends SingleLineParser {
LinelessBuildParser() {
- super("(ERROR): (.*?):char offsets [0-9]+--[0-9]+: (.*)");
+ super("^ERROR: (.*?):char offsets [0-9]+--[0-9]+: (.*)$");
}
@Override
- protected IssueOutput createIssue(@NotNull Matcher matcher) {
- return IssueOutput.error(matcher.group(3)).inFile(new File(matcher.group(2))).build();
+ protected IssueOutput createIssue(Matcher matcher) {
+ return IssueOutput.error(matcher.group(2)).inFile(new File(matcher.group(1))).build();
+ }
+ }
+
+ static class FileNotFoundBuildParser extends SingleLineParser {
+ private final WorkspaceRoot workspaceRoot;
+
+ FileNotFoundBuildParser(WorkspaceRoot workspaceRoot) {
+ super("^ERROR: .*? Unable to load file '(.*?)': (.*)$");
+ this.workspaceRoot = workspaceRoot;
+ }
+
+ @Override
+ protected IssueOutput createIssue(Matcher matcher) {
+ File file = fileFromTarget(workspaceRoot, matcher.group(1));
+ return IssueOutput.error(matcher.group(2)).inFile(file).build();
}
}
@@ -222,7 +251,7 @@
}
@Override
- protected IssueOutput createIssue(@NotNull Matcher matcher) {
+ protected IssueOutput createIssue(Matcher matcher) {
File file = null;
if (projectViewSet != null) {
String targetString = matcher.group(1);
@@ -231,12 +260,7 @@
projectViewFileWithSection(
projectViewSet,
TargetSection.KEY,
- new Predicate<ListSection<TargetExpression>>() {
- @Override
- public boolean apply(@NotNull ListSection<TargetExpression> targetSection) {
- return targetSection.items().contains(targetExpression);
- }
- });
+ targetSection -> targetSection.items().contains(targetExpression));
}
return IssueOutput.error(matcher.group(0)).inFile(file).build();
@@ -252,7 +276,7 @@
}
@Override
- protected IssueOutput createIssue(@NotNull Matcher matcher) {
+ protected IssueOutput createIssue(Matcher matcher) {
File file = null;
if (projectViewSet != null) {
final String packageString = matcher.group(1);
@@ -276,13 +300,13 @@
@Nullable
private static <T, SectionType extends Section<T>> File projectViewFileWithSection(
- @NotNull ProjectViewSet projectViewSet,
- @NotNull SectionKey<T, SectionType> key,
- @NotNull Predicate<SectionType> predicate) {
+ ProjectViewSet projectViewSet,
+ SectionKey<T, SectionType> key,
+ Predicate<SectionType> predicate) {
for (ProjectViewSet.ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
ImmutableList<SectionType> sections = projectViewFile.projectView.getSectionsOfType(key);
for (SectionType section : sections) {
- if (predicate.apply(section)) {
+ if (predicate.test(section)) {
return projectViewFile.projectViewFile;
}
}
@@ -290,34 +314,17 @@
return null;
}
- @NotNull private List<Parser> parsers = Lists.newArrayList();
+ private ImmutableList<Parser> parsers;
/**
* The parser that requested more lines of input during the last call to {@link
* #parseIssue(String)}.
*/
@Nullable private Parser multilineMatchingParser;
- @NotNull private List<String> multilineMatchResult = new ArrayList<>();
+ private List<String> multilineMatchResult = new ArrayList<>();
- public BlazeIssueParser(@Nullable Project project, @NotNull WorkspaceRoot workspaceRoot) {
-
- ProjectViewSet projectViewSet =
- project != null ? ProjectViewManager.getInstance(project).getProjectViewSet() : null;
-
- parsers.add(new CompileParser(workspaceRoot));
- parsers.add(new TracebackParser());
- parsers.add(new BuildParser());
- parsers.add(new LinelessBuildParser());
- parsers.add(new ProjectViewLabelParser(projectViewSet));
- parsers.add(
- new InvalidTargetProjectViewPackageParser(
- projectViewSet, "no such package '(.*)': BUILD file not found on package path"));
- parsers.add(
- new InvalidTargetProjectViewPackageParser(
- projectViewSet, "no targets found beneath '(.*)'"));
- parsers.add(
- new InvalidTargetProjectViewPackageParser(
- projectViewSet, "ERROR: invalid target format '(.*)'"));
+ public BlazeIssueParser(ImmutableList<Parser> parsers) {
+ this.parsers = parsers;
}
@Nullable
diff --git a/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java b/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java
index dbb313e..c3c5de2 100644
--- a/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java
+++ b/base/src/com/google/idea/blaze/base/issueparser/IssueOutputLineProcessor.java
@@ -15,15 +15,17 @@
*/
package com.google.idea.blaze.base.issueparser;
+import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.async.process.LineProcessingOutputStream;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
+import com.google.idea.blaze.base.projectview.ProjectViewManager;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.scope.output.PrintOutput.OutputType;
import com.intellij.openapi.project.Project;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import javax.annotation.Nullable;
/**
* Forwards output to PrintOutputs, colored by whether or not an issue is found per-line.
@@ -32,20 +34,41 @@
*/
public class IssueOutputLineProcessor implements LineProcessingOutputStream.LineProcessor {
- @NotNull private final BlazeContext context;
+ private final BlazeContext context;
- @NotNull private final BlazeIssueParser blazeIssueParser;
+ private final BlazeIssueParser blazeIssueParser;
public IssueOutputLineProcessor(
- @Nullable Project project,
- @NotNull BlazeContext context,
- @NotNull WorkspaceRoot workspaceRoot) {
+ @Nullable Project project, BlazeContext context, WorkspaceRoot workspaceRoot) {
this.context = context;
- this.blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ ProjectViewSet projectViewSet =
+ project != null ? ProjectViewManager.getInstance(project).getProjectViewSet() : null;
+
+ ImmutableList<BlazeIssueParser.Parser> parsers =
+ ImmutableList.of(
+ new BlazeIssueParser.CompileParser(workspaceRoot),
+ new BlazeIssueParser.TracebackParser(),
+ new BlazeIssueParser.BuildParser(),
+ new BlazeIssueParser.LinelessBuildParser(),
+ new BlazeIssueParser.ProjectViewLabelParser(projectViewSet),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "no such package '(.*)': BUILD file not found on package path"),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "no targets found beneath '(.*)'"),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "ERROR: invalid target format '(.*)'"),
+ new BlazeIssueParser.FileNotFoundBuildParser(workspaceRoot));
+ this.blazeIssueParser = new BlazeIssueParser(parsers);
+ }
+
+ public IssueOutputLineProcessor(
+ BlazeContext context, ImmutableList<BlazeIssueParser.Parser> parsers) {
+ this.context = context;
+ this.blazeIssueParser = new BlazeIssueParser(parsers);
}
@Override
- public boolean processLine(@NotNull String line) {
+ public boolean processLine(String line) {
IssueOutput issue = blazeIssueParser.parseIssue(line);
if (issue != null) {
if (issue.getCategory() == IssueOutput.Category.ERROR) {
diff --git a/base/src/com/google/idea/blaze/base/lang/AdditionalLanguagesHelper.java b/base/src/com/google/idea/blaze/base/lang/AdditionalLanguagesHelper.java
new file mode 100644
index 0000000..384e35b
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/AdditionalLanguagesHelper.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.lang;
+
+import com.google.common.collect.Sets;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.primitives.LanguageClass;
+import com.google.idea.blaze.base.projectview.ProjectViewEdit;
+import com.google.idea.blaze.base.projectview.section.ListSection;
+import com.google.idea.blaze.base.projectview.section.sections.AdditionalLanguagesSection;
+import com.google.idea.blaze.base.settings.BlazeUserSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncManager;
+import com.google.idea.blaze.base.sync.BlazeSyncParams;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.projectview.LanguageSupport;
+import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.EditorNotificationPanel;
+import com.intellij.ui.EditorNotifications;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+/**
+ * Detects usage of files in supported but inactive languages, and offers to add them to the project
+ * view.
+ */
+public class AdditionalLanguagesHelper
+ extends EditorNotifications.Provider<EditorNotificationPanel> {
+
+ private static final Key<EditorNotificationPanel> KEY = Key.create("add additional language");
+
+ // avoid notifying more than once per project per language.
+ private final Set<LanguageClass> notifiedLanguages = Sets.newHashSet();
+ private final Project project;
+ private final EditorNotifications notifications;
+
+ public AdditionalLanguagesHelper(Project project, final EditorNotifications notifications) {
+ this.project = project;
+ this.notifications = notifications;
+
+ for (LanguageClass langauge : LanguageClass.values()) {
+ if (PropertiesComponent.getInstance(project).getBoolean(propertyKey(langauge))) {
+ notifiedLanguages.add(langauge);
+ }
+ }
+ }
+
+ private void suppressNotifications(LanguageClass language) {
+ PropertiesComponent.getInstance(project).setValue(propertyKey(language), true);
+ notifiedLanguages.add(language);
+ notifications.updateAllNotifications();
+ }
+
+ private static String propertyKey(LanguageClass language) {
+ return "additional_languages_helper_suppressed_" + language.getName();
+ }
+
+ @Override
+ public Key<EditorNotificationPanel> getKey() {
+ return KEY;
+ }
+
+ @Nullable
+ @Override
+ public EditorNotificationPanel createNotificationPanel(VirtualFile file, FileEditor fileEditor) {
+ String ext = file.getExtension();
+ if (ext == null) {
+ return null;
+ }
+ LanguageClass language = LanguageClass.fromExtension(ext);
+ if (language == null || notifiedLanguages.contains(language)) {
+ return null;
+ }
+ BlazeProjectData projectData =
+ BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
+ if (projectData == null) {
+ return null;
+ }
+ WorkspaceLanguageSettings settings = projectData.workspaceLanguageSettings;
+ if (settings.isLanguageActive(language)) {
+ return null;
+ }
+ if (!LanguageSupport.supportedLanguagesForWorkspaceType(settings.getWorkspaceType())
+ .contains(language)) {
+ return null;
+ }
+
+ String langName = language.getName();
+ String message =
+ String.format("Do you want to enable %s support in your project view file?", langName);
+
+ EditorNotificationPanel panel = new EditorNotificationPanel();
+ panel.setText(message);
+ panel.createActionLabel(
+ String.format("Enable %s support", langName),
+ () -> enableLanguageSupport(project, language));
+ panel.createActionLabel("Don't show again", () -> suppressNotifications(language));
+ return panel;
+ }
+
+ private void enableLanguageSupport(Project project, LanguageClass language) {
+ ProjectViewEdit edit =
+ ProjectViewEdit.editLocalProjectView(
+ project,
+ builder -> {
+ ListSection<LanguageClass> existingSection =
+ builder.getLast(AdditionalLanguagesSection.KEY);
+ builder.replace(
+ existingSection,
+ ListSection.update(AdditionalLanguagesSection.KEY, existingSection)
+ .add(language));
+ return true;
+ });
+ if (edit == null) {
+ Messages.showErrorDialog(
+ "Could not modify project view. Check for errors in your project view and try again",
+ "Error");
+ return;
+ }
+ edit.apply();
+
+ suppressNotifications(language);
+
+ BlazeSyncManager.getInstance(project)
+ .requestProjectSync(
+ new BlazeSyncParams.Builder("Sync", BlazeSyncParams.SyncMode.INCREMENTAL)
+ .addProjectViewTargets(true)
+ .addWorkingSet(BlazeUserSettings.getInstance().getExpandSyncToWorkingSet())
+ .build());
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuildCompletionAutoPopupHandler.java b/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuildCompletionAutoPopupHandler.java
new file mode 100644
index 0000000..8be2b0d
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuildCompletionAutoPopupHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.lang.buildfile.completion;
+
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
+import com.intellij.codeInsight.AutoPopupController;
+import com.intellij.codeInsight.editorActions.TypedHandlerDelegate;
+import com.intellij.codeInsight.lookup.LookupManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+
+/**
+ * Supplements {@link com.intellij.codeInsight.editorActions.CompletionAutoPopupHandler}, triggering
+ * auto-complete pop-up on some non-letter characters when typing in build files.
+ */
+public class BuildCompletionAutoPopupHandler extends TypedHandlerDelegate {
+
+ @Override
+ public Result checkAutoPopup(
+ char charTyped, final Project project, final Editor editor, final PsiFile file) {
+ if (!(file instanceof BuildFile)) {
+ return Result.CONTINUE;
+ }
+ if (LookupManager.getActiveLookup(editor) != null) {
+ return Result.CONTINUE;
+ }
+
+ if (charTyped != '/' && charTyped != ':') {
+ return Result.CONTINUE;
+ }
+ PsiElement psi = file.findElementAt(editor.getCaretModel().getOffset());
+ if (psi != null && psi.getParent() instanceof StringLiteral) {
+ AutoPopupController.getInstance(project).scheduleAutoPopup(editor);
+ return Result.STOP;
+ }
+ return Result.CONTINUE;
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributor.java b/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributor.java
index bf37676..4cecbca 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributor.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributor.java
@@ -18,9 +18,9 @@
import static com.intellij.patterns.PlatformPatterns.psiComment;
import static com.intellij.patterns.PlatformPatterns.psiElement;
+import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.lang.buildfile.language.BuildFileLanguage;
-import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpec;
-import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpecProvider;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuiltInNamesProvider;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.lang.buildfile.psi.FunctionStatement;
import com.google.idea.blaze.base.lang.buildfile.psi.ReferenceExpression;
@@ -77,13 +77,9 @@
CompletionParameters parameters,
ProcessingContext context,
CompletionResultSet result) {
- BuildLanguageSpec spec =
- BuildLanguageSpecProvider.getInstance()
- .getLanguageSpec(parameters.getPosition().getProject());
- if (spec == null) {
- return;
- }
- for (String ruleName : spec.getKnownRuleNames()) {
+ ImmutableSet<String> builtInNames =
+ BuiltInNamesProvider.getBuiltInFunctionNames(parameters.getPosition().getProject());
+ for (String ruleName : builtInNames) {
result.addElement(
LookupElementBuilder.create(ruleName)
.withIcon(BlazeIcons.BuildRule)
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInSymbolCompletionContributor.java b/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInSymbolCompletionContributor.java
new file mode 100644
index 0000000..0ffe41a
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInSymbolCompletionContributor.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.lang.buildfile.completion;
+
+import static com.intellij.patterns.PlatformPatterns.psiComment;
+import static com.intellij.patterns.PlatformPatterns.psiElement;
+
+import com.google.idea.blaze.base.lang.buildfile.language.BuildFileLanguage;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuiltInNamesProvider;
+import com.google.idea.blaze.base.lang.buildfile.psi.ReferenceExpression;
+import com.intellij.codeInsight.completion.AutoCompletionContext;
+import com.intellij.codeInsight.completion.AutoCompletionDecision;
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionParameters;
+import com.intellij.codeInsight.completion.CompletionProvider;
+import com.intellij.codeInsight.completion.CompletionResultSet;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.util.ProcessingContext;
+
+/** Completes built-in blaze global symbols. */
+public class BuiltInSymbolCompletionContributor extends CompletionContributor {
+
+ @Override
+ public AutoCompletionDecision handleAutoCompletionPossibility(AutoCompletionContext context) {
+ // auto-insert the obvious only case; else show other cases.
+ final LookupElement[] items = context.getItems();
+ if (items.length == 1) {
+ return AutoCompletionDecision.insertItem(items[0]);
+ }
+ return AutoCompletionDecision.SHOW_LOOKUP;
+ }
+
+ public BuiltInSymbolCompletionContributor() {
+ extend(
+ CompletionType.BASIC,
+ psiElement()
+ .withLanguage(BuildFileLanguage.INSTANCE)
+ .andNot(psiComment())
+ .withParent(ReferenceExpression.class),
+ new CompletionProvider<CompletionParameters>() {
+ @Override
+ protected void addCompletions(
+ CompletionParameters parameters,
+ ProcessingContext context,
+ CompletionResultSet result) {
+ for (String symbol : BuiltInNamesProvider.GLOBALS) {
+ result.addElement(LookupElementBuilder.create(symbol));
+ }
+ }
+ });
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java
index 8c7dcb0..c57a138 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuildLanguageSpecProviderImpl.java
@@ -21,6 +21,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.intellij.openapi.project.Project;
import java.util.Map;
@@ -44,6 +45,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
LanguageSpecResult spec = blazeProjectData.syncState.get(LanguageSpecResult.class);
if (spec != null) {
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java
index 7f12f08..7b90bc2 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java
@@ -25,7 +25,7 @@
public class BuiltInNamesProvider {
// https://www.bazel.io/versions/master/docs/skylark/lib/globals.html
- private static final ImmutableSet<String> GLOBALS =
+ public static final ImmutableSet<String> GLOBALS =
ImmutableSet.of(
"Actions",
"DATA_CFG",
@@ -66,11 +66,36 @@
"type",
"zip");
+ // https://www.bazel.io/versions/master/docs/be/functions.html
+ private static final ImmutableSet<String> FUNCTIONS =
+ ImmutableSet.of(
+ "load",
+ "package",
+ "pacakge_group",
+ "licenses",
+ "exports_files",
+ "glob",
+ "select",
+ "workspace");
+
+ /** Returns all built-in global symbols and function names. */
public static ImmutableSet<String> getBuiltInNames(Project project) {
+ ImmutableSet.Builder<String> builder =
+ ImmutableSet.<String>builder().addAll(GLOBALS).addAll(FUNCTIONS);
BuildLanguageSpec spec = BuildLanguageSpecProvider.getInstance().getLanguageSpec(project);
- if (spec == null) {
- return GLOBALS;
+ if (spec != null) {
+ builder = builder.addAll(spec.getKnownRuleNames());
}
- return ImmutableSet.<String>builder().addAll(GLOBALS).addAll(spec.getKnownRuleNames()).build();
+ return builder.build();
+ }
+
+ /** Returns all built-in rules and function names. */
+ public static ImmutableSet<String> getBuiltInFunctionNames(Project project) {
+ ImmutableSet.Builder<String> builder = ImmutableSet.<String>builder().addAll(FUNCTIONS);
+ BuildLanguageSpec spec = BuildLanguageSpecProvider.getInstance().getLanguageSpec(project);
+ if (spec != null) {
+ builder = builder.addAll(spec.getKnownRuleNames());
+ }
+ return builder.build();
}
}
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java b/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java
index 5b4517c..4887369 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java
@@ -111,8 +111,8 @@
public static final ImmutableSet<TokenKind> KEYWORDS =
ImmutableSet.of(
AND, AS, ASSERT, BREAK, CLASS, CONTINUE, DEF, DEL, ELIF, ELSE, EXCEPT, FINALLY, FOR, FROM,
- GLOBAL, IF, IMPORT, IN, IS, LAMBDA, LOAD, NONLOCAL, NOT, OR, PASS, RAISE, RETURN, TRY,
- WHILE, WITH, YIELD);
+ GLOBAL, IF, IMPORT, IN, IS, LAMBDA, NONLOCAL, NOT, OR, PASS, RAISE, RETURN, TRY, WHILE,
+ WITH, YIELD);
public static final ImmutableSet<TokenKind> OPERATIONS =
ImmutableSet.of(
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/parser/StatementParsing.java b/base/src/com/google/idea/blaze/base/lang/buildfile/parser/StatementParsing.java
index e22e0ba..fdc8da9 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/parser/StatementParsing.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/parser/StatementParsing.java
@@ -45,7 +45,7 @@
// Unlike in Python grammar, 'load' and 'def' are only allowed as a top-level statement
public void parseTopLevelStatement() {
- if (currentToken() == TokenKind.LOAD) {
+ if (currentToken() == TokenKind.IDENTIFIER && "load".equals(builder.getTokenText())) {
parseLoadStatement();
} else if (currentToken() == TokenKind.DEF) {
parseFunctionDefStatement();
@@ -89,7 +89,7 @@
// load '(' STRING (',' [IDENTIFIER '='] STRING)* [','] ')'
private void parseLoadStatement() {
PsiBuilder.Marker marker = builder.mark();
- expect(TokenKind.LOAD);
+ expect(TokenKind.IDENTIFIER);
expect(TokenKind.LPAREN);
parseStringLiteral(false);
// Not implementing [IDENTIFIER EQUALS] option -- not a documented feature,
diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java
index 154d5d9..e59d9a7 100644
--- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java
+++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FuncallExpression.java
@@ -129,6 +129,7 @@
return getKeywordArgument("name");
}
+ @Nullable
public Argument.Keyword getKeywordArgument(String name) {
ArgumentList argList = getArgList();
return argList != null ? argList.getKeywordArgument(name) : null;
diff --git a/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java b/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java
index 6a227ce..d0cbe97 100644
--- a/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java
+++ b/base/src/com/google/idea/blaze/base/model/BlazeLibrary.java
@@ -17,12 +17,16 @@
import com.google.common.base.Objects;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.util.io.URLUtil;
import java.io.File;
import java.io.Serializable;
@@ -73,7 +77,7 @@
FileUtilRt.extensionEquals(name, "jar") || FileUtilRt.extensionEquals(name, "zip");
// .jar files require an URL with "jar" protocol.
String protocol =
- isJarFile ? StandardFileSystems.JAR_PROTOCOL : StandardFileSystems.FILE_PROTOCOL;
+ isJarFile ? StandardFileSystems.JAR_PROTOCOL : defaultFileSystem().getProtocol();
String filePath = FileUtil.toSystemIndependentName(path.getPath());
String url = VirtualFileManager.constructUrl(protocol, filePath);
if (isJarFile) {
@@ -81,4 +85,11 @@
}
return url;
}
+
+ private static VirtualFileSystem defaultFileSystem() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return TempFileSystem.getInstance();
+ }
+ return LocalFileSystem.getInstance();
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/Kind.java b/base/src/com/google/idea/blaze/base/model/primitives/Kind.java
index afe9302..88d5199 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/Kind.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/Kind.java
@@ -48,6 +48,7 @@
PY_BINARY("py_binary", LanguageClass.PYTHON),
PY_TEST("py_test", LanguageClass.PYTHON),
PY_APPENGINE_BINARY("py_appengine_binary", LanguageClass.PYTHON),
+ PY_WRAP_CC("py_wrap_cc", LanguageClass.PYTHON),
;
static final ImmutableMap<String, Kind> STRING_TO_KIND = makeStringToKindMap();
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java b/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java
index f174a4f..9addd48 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/LanguageClass.java
@@ -15,21 +15,40 @@
*/
package com.google.idea.blaze.base.model.primitives;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import javax.annotation.Nullable;
+
/** Language classes. */
public enum LanguageClass {
- GENERIC("generic"),
- C("c"),
- JAVA("java"),
- ANDROID("android"),
- JAVASCRIPT("javascript"),
- TYPESCRIPT("typescript"),
- DART("dart"),
- PYTHON("python");
+ GENERIC("generic", ImmutableSet.of()),
+ C("c", ImmutableSet.of("c", "cc", "cpp", "h", "hh", "hpp")),
+ JAVA("java", ImmutableSet.of("java")),
+ ANDROID("android", ImmutableSet.of("aidl")),
+ JAVASCRIPT("javascript", ImmutableSet.of("js", "applejs")),
+ TYPESCRIPT("typescript", ImmutableSet.of("ts", "ats")),
+ DART("dart", ImmutableSet.of("dart")),
+ PYTHON("python", ImmutableSet.of("py", "pyw"));
+
+ private static final ImmutableMap<String, LanguageClass> RECOGNIZED_EXTENSIONS =
+ extensionToClassMap();
+
+ private static ImmutableMap<String, LanguageClass> extensionToClassMap() {
+ ImmutableMap.Builder<String, LanguageClass> result = ImmutableMap.builder();
+ for (LanguageClass lang : LanguageClass.values()) {
+ for (String ext : lang.recognizedFilenameExtensions) {
+ result.put(ext, lang);
+ }
+ }
+ return result.build();
+ }
private final String name;
+ private final ImmutableSet<String> recognizedFilenameExtensions;
- LanguageClass(String name) {
+ LanguageClass(String name, ImmutableSet<String> recognizedFilenameExtensions) {
this.name = name;
+ this.recognizedFilenameExtensions = recognizedFilenameExtensions;
}
public String getName() {
@@ -44,4 +63,10 @@
}
return null;
}
+
+ /** Returns the LanguageClass associated with the given filename extension, if it's recognized. */
+ @Nullable
+ public static LanguageClass fromExtension(String filenameExtension) {
+ return RECOGNIZED_EXTENSIONS.get(filenameExtension);
+ }
}
diff --git a/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java b/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java
index 293aeb0..290118b 100644
--- a/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java
+++ b/base/src/com/google/idea/blaze/base/model/primitives/WorkspaceType.java
@@ -22,7 +22,6 @@
* enum ordinal.
*/
public enum WorkspaceType {
- ANDROID_NDK("android_ndk", LanguageClass.ANDROID, LanguageClass.JAVA, LanguageClass.C),
ANDROID("android", LanguageClass.ANDROID, LanguageClass.JAVA),
C("c", LanguageClass.C),
JAVA("java", LanguageClass.JAVA),
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java b/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java
index 6d1164b..ddfa57e 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/SectionParser.java
@@ -15,6 +15,7 @@
*/
package com.google.idea.blaze.base.projectview.section;
+import com.google.idea.blaze.base.projectview.ProjectView;
import com.google.idea.blaze.base.projectview.parser.ParseContext;
import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
import javax.annotation.Nullable;
@@ -46,6 +47,11 @@
return false;
}
+ /** Allows the section to add a default value. Used during the wizard. */
+ public ProjectView addProjectViewDefaultValue(ProjectView projectView) {
+ return projectView;
+ }
+
/** The type of item(s) in this section. */
public abstract ItemType getItemType();
}
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/DirectorySection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/DirectorySection.java
index 0163e24..cc1cb23 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/sections/DirectorySection.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/DirectorySection.java
@@ -17,6 +17,7 @@
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.projectview.ProjectView;
import com.google.idea.blaze.base.projectview.parser.ParseContext;
import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
import com.google.idea.blaze.base.projectview.section.ListSection;
@@ -66,5 +67,18 @@
public ItemType getItemType() {
return ItemType.FileSystemItem;
}
+
+ @Override
+ public ProjectView addProjectViewDefaultValue(ProjectView projectView) {
+ if (!projectView.getSectionsOfType(KEY).isEmpty()) {
+ return projectView;
+ }
+ return ProjectView.builder(projectView)
+ .add(
+ ListSection.builder(KEY)
+ .add(TextBlock.of(" # Add the directories you want added as source here"))
+ .add(TextBlock.newLine()))
+ .build();
+ }
}
}
diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetSection.java
index 5bd3361..cd69179 100644
--- a/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetSection.java
+++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/TargetSection.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.base.projectview.section.sections;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
+import com.google.idea.blaze.base.projectview.ProjectView;
import com.google.idea.blaze.base.projectview.parser.ParseContext;
import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
import com.google.idea.blaze.base.projectview.section.ListSection;
@@ -49,5 +50,21 @@
public ItemType getItemType() {
return ItemType.Label;
}
+
+ @Override
+ public ProjectView addProjectViewDefaultValue(ProjectView projectView) {
+ if (!projectView.getSectionsOfType(KEY).isEmpty()) {
+ return projectView;
+ }
+ return ProjectView.builder(projectView)
+ .add(
+ ListSection.builder(KEY)
+ .add(
+ TextBlock.of(
+ " # Add targets that reach the source code "
+ + "that you want to resolve here"))
+ .add(TextBlock.newLine()))
+ .build();
+ }
}
}
diff --git a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
old mode 100755
new mode 100644
index 80ae557..74a3812
--- a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
+++ b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationSyncListener.java
@@ -23,6 +23,7 @@
import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
@@ -42,6 +43,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
UIUtil.invokeAndWaitIfNeeded(
diff --git a/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java b/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java
index c0d9f1a..0b336f4 100644
--- a/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java
+++ b/base/src/com/google/idea/blaze/base/run/testmap/TestTargetFilterImpl.java
@@ -34,6 +34,7 @@
import com.google.idea.blaze.base.run.TestTargetFinder;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
@@ -180,6 +181,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
TestTargetFinder testTargetFinder = TestTargetFinder.getInstance(project);
((TestTargetFilterImpl) testTargetFinder).clearMapData();
diff --git a/base/src/com/google/idea/blaze/base/scope/BlazeContext.java b/base/src/com/google/idea/blaze/base/scope/BlazeContext.java
index 82568ae..316c05b 100644
--- a/base/src/com/google/idea/blaze/base/scope/BlazeContext.java
+++ b/base/src/com/google/idea/blaze/base/scope/BlazeContext.java
@@ -17,6 +17,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import java.util.List;
import org.jetbrains.annotations.NotNull;
@@ -29,7 +30,7 @@
@NotNull private final List<BlazeScope> scopes = Lists.newArrayList();
@NotNull
- private final ArrayListMultimap<Class<? extends Output>, OutputSink<?>> outputSinks =
+ private final ListMultimap<Class<? extends Output>, OutputSink<?>> outputSinks =
ArrayListMultimap.create();
boolean isEnding;
diff --git a/base/src/com/google/idea/blaze/base/settings/BlazeUserSettings.java b/base/src/com/google/idea/blaze/base/settings/BlazeUserSettings.java
index 1e74ef4..6b7298e 100644
--- a/base/src/com/google/idea/blaze/base/settings/BlazeUserSettings.java
+++ b/base/src/com/google/idea/blaze/base/settings/BlazeUserSettings.java
@@ -36,7 +36,7 @@
)
public class BlazeUserSettings implements PersistentStateComponent<BlazeUserSettings> {
- public boolean suppressConsoleForRunAction = false;
+ private boolean suppressConsoleForRunAction = false;
private boolean resyncAutomatically = false;
private boolean syncStatusPopupShown = false;
private boolean expandSyncToWorkingSet = true;
@@ -44,6 +44,7 @@
private boolean attachSourcesByDefault = false;
private boolean attachSourcesOnDemand = false;
private boolean collapseProjectView = true;
+ private boolean formatBuildFilesOnSave = true;
private String blazeBinaryPath = "/usr/bin/blaze";
@Nullable private String bazelBinaryPath;
@@ -141,6 +142,14 @@
this.collapseProjectView = collapseProjectView;
}
+ public boolean getFormatBuildFilesOnSave() {
+ return formatBuildFilesOnSave;
+ }
+
+ public void setFormatBuildFilesOnSave(boolean formatBuildFilesOnSave) {
+ this.formatBuildFilesOnSave = formatBuildFilesOnSave;
+ }
+
// Deprecated -- use BlazeJavaUserSettings
@Deprecated
@SuppressWarnings("unused") // Used by bean serialization
diff --git a/base/src/com/google/idea/blaze/base/settings/ui/BlazeUserSettingsConfigurable.java b/base/src/com/google/idea/blaze/base/settings/ui/BlazeUserSettingsConfigurable.java
index 82e45fa..f8a7210 100644
--- a/base/src/com/google/idea/blaze/base/settings/ui/BlazeUserSettingsConfigurable.java
+++ b/base/src/com/google/idea/blaze/base/settings/ui/BlazeUserSettingsConfigurable.java
@@ -52,6 +52,7 @@
private JCheckBox suppressConsoleForRunAction;
private JCheckBox resyncAutomatically;
private JCheckBox collapseProjectView;
+ private JCheckBox formatBuildFilesOnSave;
private FileSelectorWithStoredHistory blazeBinaryPathField;
private FileSelectorWithStoredHistory bazelBinaryPathField;
@@ -83,6 +84,7 @@
settings.setSuppressConsoleForRunAction(suppressConsoleForRunAction.isSelected());
settings.setResyncAutomatically(resyncAutomatically.isSelected());
settings.setCollapseProjectView(collapseProjectView.isSelected());
+ settings.setFormatBuildFilesOnSave(formatBuildFilesOnSave.isSelected());
settings.setBlazeBinaryPath(Strings.nullToEmpty(blazeBinaryPathField.getText()));
settings.setBazelBinaryPath(Strings.nullToEmpty(bazelBinaryPathField.getText()));
@@ -97,6 +99,7 @@
suppressConsoleForRunAction.setSelected(settings.getSuppressConsoleForRunAction());
resyncAutomatically.setSelected(settings.getResyncAutomatically());
collapseProjectView.setSelected(settings.getCollapseProjectView());
+ formatBuildFilesOnSave.setSelected(settings.getFormatBuildFilesOnSave());
blazeBinaryPathField.setTextWithHistory(settings.getBlazeBinaryPath());
bazelBinaryPathField.setTextWithHistory(settings.getBazelBinaryPath());
@@ -115,10 +118,10 @@
public boolean isModified() {
BlazeUserSettings settings = BlazeUserSettings.getInstance();
boolean isModified =
- !Objects.equal(
- suppressConsoleForRunAction.isSelected(), settings.getSuppressConsoleForRunAction())
- || !Objects.equal(resyncAutomatically.isSelected(), settings.getResyncAutomatically())
- || !Objects.equal(collapseProjectView.isSelected(), settings.getCollapseProjectView())
+ suppressConsoleForRunAction.isSelected() != settings.getSuppressConsoleForRunAction()
+ || resyncAutomatically.isSelected() != settings.getResyncAutomatically()
+ || collapseProjectView.isSelected() != settings.getCollapseProjectView()
+ || formatBuildFilesOnSave.isSelected() != settings.getFormatBuildFilesOnSave()
|| !Objects.equal(
Strings.nullToEmpty(blazeBinaryPathField.getText()),
Strings.nullToEmpty(settings.getBlazeBinaryPath()))
@@ -153,7 +156,7 @@
contributorRowCount += contributor.getRowCount();
}
- final int totalRowSize = 6 + contributorRowCount;
+ final int totalRowSize = 7 + contributorRowCount;
int rowi = 0;
myMainPanel = new JPanel();
@@ -216,6 +219,25 @@
null,
0,
false));
+ formatBuildFilesOnSave = new JCheckBox();
+ formatBuildFilesOnSave.setSelected(false);
+ formatBuildFilesOnSave.setText("Automatically format BUILD files on file save");
+ myMainPanel.add(
+ formatBuildFilesOnSave,
+ new GridConstraints(
+ rowi++,
+ 0,
+ 1,
+ 2,
+ GridConstraints.ANCHOR_NORTHWEST,
+ GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ null,
+ null,
+ 0,
+ false));
for (BlazeUserSettingsContributor contributor : settingsContributors) {
rowi = contributor.addComponents(myMainPanel, rowi);
}
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
index b29ce17..0b55343 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncPlugin.java
@@ -36,7 +36,6 @@
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import java.util.Collection;
import java.util.Set;
@@ -122,17 +121,8 @@
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData);
- /**
- * Modify the project content entries. There will be one content entry per project directory from
- * the project view set.
- */
- void updateContentEntries(
- Project project,
- BlazeContext context,
- WorkspaceRoot workspaceRoot,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- Collection<ContentEntry> contentEntries);
+ @Nullable
+ SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData);
/** Modifies the IDE project structure in accordance with the sync data. */
void updateProjectStructure(
@@ -215,14 +205,11 @@
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData) {}
+ @Nullable
@Override
- public void updateContentEntries(
- Project project,
- BlazeContext context,
- WorkspaceRoot workspaceRoot,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- Collection<ContentEntry> contentEntries) {}
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ return null;
+ }
@Override
public void updateProjectStructure(
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
index 68ee51d..3d4e3ec 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
@@ -84,6 +84,7 @@
import com.google.idea.blaze.base.targetmaps.ReverseDependencyMap;
import com.google.idea.blaze.base.util.SaveUtil;
import com.google.idea.blaze.base.vcs.BlazeVcsHandler;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
@@ -94,8 +95,10 @@
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.StandardFileSystems;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
@@ -157,37 +160,34 @@
@VisibleForTesting
boolean syncProject(BlazeContext context) {
SyncResult syncResult = SyncResult.FAILURE;
+ SyncMode syncMode = syncParams.syncMode;
try {
SaveUtil.saveAllFiles();
- onSyncStart(project, context);
- syncResult = doSyncProject(context);
+ BlazeProjectData oldBlazeProjectData =
+ syncMode != SyncMode.FULL
+ ? BlazeProjectDataManagerImpl.getImpl(project)
+ .loadProjectRoot(context, importSettings)
+ : null;
+ if (oldBlazeProjectData == null) {
+ syncMode = SyncMode.FULL;
+ }
+
+ onSyncStart(project, context, syncMode);
+ syncResult = doSyncProject(context, syncMode, oldBlazeProjectData);
} catch (AssertionError | Exception e) {
LOG.error(e);
IssueOutput.error("Internal error: " + e.getMessage()).submit(context);
} finally {
- afterSync(project, context, syncResult);
+ afterSync(project, context, syncMode, syncResult);
}
return syncResult == SyncResult.SUCCESS || syncResult == SyncResult.PARTIAL_SUCCESS;
}
/** @return true if sync successfully completed */
- private SyncResult doSyncProject(final BlazeContext context) {
+ private SyncResult doSyncProject(
+ BlazeContext context, SyncMode syncMode, @Nullable BlazeProjectData oldBlazeProjectData) {
this.syncStartTime = System.currentTimeMillis();
- if (importSettings.getProjectViewFile() == null) {
- IssueOutput.error(
- "This project looks like it's been opened from an old version of ASwB. "
- + "That is unfortunately not supported. Please reimport your project.")
- .submit(context);
- return SyncResult.FAILURE;
- }
-
- @Nullable BlazeProjectData oldBlazeProjectData = null;
- if (syncParams.syncMode != SyncMode.FULL) {
- oldBlazeProjectData =
- BlazeProjectDataManagerImpl.getImpl(project).loadProjectRoot(context, importSettings);
- }
-
BlazeVcsHandler vcsHandler = null;
for (BlazeVcsHandler candidate : BlazeVcsHandler.EP_NAME.getExtensions()) {
if (candidate.handlesProject(importSettings.getBuildSystem(), workspaceRoot)) {
@@ -271,7 +271,7 @@
BuildResult ideInfoResult = BuildResult.SUCCESS;
BuildResult ideResolveResult = BuildResult.SUCCESS;
- if (syncParams.syncMode != SyncMode.RESTORE_EPHEMERAL_STATE || oldBlazeProjectData == null) {
+ if (syncMode != SyncMode.RESTORE_EPHEMERAL_STATE || oldBlazeProjectData == null) {
SyncState.Builder syncStateBuilder = new SyncState.Builder();
SyncState previousSyncState =
oldBlazeProjectData != null ? oldBlazeProjectData.syncState : null;
@@ -384,7 +384,7 @@
newBlazeProjectData = oldBlazeProjectData;
}
- FileCaches.onSync(project, context, projectViewSet, newBlazeProjectData, syncParams.syncMode);
+ FileCaches.onSync(project, context, projectViewSet, newBlazeProjectData, syncMode);
ListenableFuture<?> prefetch =
PrefetchService.getInstance().prefetchProjectFiles(project, newBlazeProjectData);
FutureUtil.waitForFuture(context, prefetch)
@@ -417,7 +417,7 @@
syncResult = SyncResult.PARTIAL_SUCCESS;
}
- onSyncComplete(project, context, projectViewSet, newBlazeProjectData, syncResult);
+ onSyncComplete(project, context, projectViewSet, newBlazeProjectData, syncMode, syncResult);
return syncResult;
}
@@ -681,7 +681,8 @@
IssueOutput.warn("Could not set module type for workspace module.").submit(context);
}
- Module workspaceModule = moduleEditor.createModule(".workspace", workspaceModuleType);
+ Module workspaceModule =
+ moduleEditor.createModule(BlazeDataStorage.WORKSPACE_MODULE_NAME, workspaceModuleType);
ModifiableRootModel workspaceModifiableModel = moduleEditor.editModule(workspaceModule);
ContentEntryEditor.createContentEntries(
@@ -734,20 +735,28 @@
private static String pathToUrl(File path) {
String filePath = FileUtil.toSystemIndependentName(path.getPath());
- return VirtualFileManager.constructUrl(StandardFileSystems.FILE_PROTOCOL, filePath);
+ return VirtualFileManager.constructUrl(defaultFileSystem().getProtocol(), filePath);
}
- private static void onSyncStart(Project project, BlazeContext context) {
+ private static VirtualFileSystem defaultFileSystem() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return TempFileSystem.getInstance();
+ }
+ return LocalFileSystem.getInstance();
+ }
+
+ private static void onSyncStart(Project project, BlazeContext context, SyncMode syncMode) {
final SyncListener[] syncListeners = SyncListener.EP_NAME.getExtensions();
for (SyncListener syncListener : syncListeners) {
- syncListener.onSyncStart(project, context);
+ syncListener.onSyncStart(project, context, syncMode);
}
}
- private static void afterSync(Project project, BlazeContext context, SyncResult syncResult) {
+ private static void afterSync(
+ Project project, BlazeContext context, SyncMode syncMode, SyncResult syncResult) {
final SyncListener[] syncListeners = SyncListener.EP_NAME.getExtensions();
for (SyncListener syncListener : syncListeners) {
- syncListener.afterSync(project, context, syncResult);
+ syncListener.afterSync(project, context, syncMode, syncResult);
}
}
@@ -756,13 +765,14 @@
BlazeContext context,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
validate(project, context, blazeProjectData);
final SyncListener[] syncListeners = SyncListener.EP_NAME.getExtensions();
for (SyncListener syncListener : syncListeners) {
syncListener.onSyncComplete(
- project, context, importSettings, projectViewSet, blazeProjectData, syncResult);
+ project, context, importSettings, projectViewSet, blazeProjectData, syncMode, syncResult);
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java b/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java
new file mode 100644
index 0000000..1810e9c
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/sync/GenericSourceFolderProvider.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.sync;
+
+import com.google.common.collect.ImmutableMap;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.vfs.VirtualFile;
+
+/** An implementation of {@link SourceFolderProvider} with no language-specific settings. */
+public class GenericSourceFolderProvider implements SourceFolderProvider {
+
+ public static final GenericSourceFolderProvider INSTANCE = new GenericSourceFolderProvider();
+
+ private GenericSourceFolderProvider() {}
+
+ @Override
+ public ImmutableMap<VirtualFile, SourceFolder> initializeSourceFolders(
+ ContentEntry contentEntry) {
+ ImmutableMap.Builder<VirtualFile, SourceFolder> output = ImmutableMap.builder();
+ VirtualFile file = contentEntry.getFile();
+ if (file != null) {
+ output.put(file, contentEntry.addSourceFolder(file, false));
+ }
+ return output.build();
+ }
+
+ @Override
+ public SourceFolder setSourceFolderForLocation(
+ ContentEntry contentEntry,
+ SourceFolder parentFolder,
+ VirtualFile file,
+ boolean isTestSource) {
+ return contentEntry.addSourceFolder(file, isTestSource);
+ }
+}
diff --git a/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java b/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java
new file mode 100644
index 0000000..6514a82
--- /dev/null
+++ b/base/src/com/google/idea/blaze/base/sync/SourceFolderProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.sync;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.vfs.VirtualFile;
+
+/** Provides source folders for each content entry during sync. */
+public interface SourceFolderProvider {
+
+ /** Iterates over the available sync plugins, requesting a SourceFolderProvider. */
+ static SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
+ SourceFolderProvider provider = syncPlugin.getSourceFolderProvider(projectData);
+ if (provider != null) {
+ return provider;
+ }
+ }
+ throw new RuntimeException(
+ "No SourceFolderProvider available for workspace type: "
+ + projectData.workspaceLanguageSettings.getWorkspaceType());
+ }
+
+ /**
+ * Creates the initial source folders for the given {@link ContentEntry}. These source folders are
+ * 'initial' because the 'is test' property (and potentially additional test source folders) are
+ * added later.
+ */
+ ImmutableMap<VirtualFile, SourceFolder> initializeSourceFolders(ContentEntry contentEntry);
+
+ /**
+ * Sets the source folder for the given file, incorporating the test information as appropriate.
+ */
+ SourceFolder setSourceFolderForLocation(
+ ContentEntry contentEntry, SourceFolder parentFolder, VirtualFile file, boolean isTestSource);
+}
diff --git a/base/src/com/google/idea/blaze/base/sync/SyncListener.java b/base/src/com/google/idea/blaze/base/sync/SyncListener.java
index 28ba5fc..a3512cb 100644
--- a/base/src/com/google/idea/blaze/base/sync/SyncListener.java
+++ b/base/src/com/google/idea/blaze/base/sync/SyncListener.java
@@ -19,6 +19,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
@@ -40,7 +41,7 @@
}
/** Called after open documents have been saved, prior to starting the blaze sync. */
- void onSyncStart(Project project, BlazeContext context);
+ void onSyncStart(Project project, BlazeContext context, SyncMode syncMode);
/** Called on successful (or partially successful) completion of a sync */
void onSyncComplete(
@@ -49,16 +50,17 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult);
/** Guaranteed to be called once per sync, regardless of whether it successfully completed */
- void afterSync(Project project, BlazeContext context, SyncResult syncResult);
+ void afterSync(Project project, BlazeContext context, SyncMode syncMode, SyncResult syncResult);
/** Convenience adapter class. */
abstract class Adapter implements SyncListener {
@Override
- public void onSyncStart(Project project, BlazeContext context) {}
+ public void onSyncStart(Project project, BlazeContext context, SyncMode syncMode) {}
@Override
public void onSyncComplete(
@@ -67,9 +69,11 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {}
@Override
- public void afterSync(Project project, BlazeContext context, SyncResult syncResult) {}
+ public void afterSync(
+ Project project, BlazeContext context, SyncMode syncMode, SyncResult syncResult) {}
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java b/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java
index fdc584d..f44f200 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/BlazeIdeInterfaceAspectsImpl.java
@@ -402,7 +402,7 @@
return BuildResult.fromExitCode(retVal);
}
- private AspectStrategy getAspectStrategy(Project project) {
+ private static AspectStrategy getAspectStrategy(Project project) {
for (AspectStrategyProvider provider : AspectStrategyProvider.EP_NAME.getExtensions()) {
AspectStrategy aspectStrategy = provider.getAspectStrategy(project);
if (aspectStrategy != null) {
diff --git a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java
index 960a79c..0fdd2b5 100644
--- a/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java
+++ b/base/src/com/google/idea/blaze/base/sync/aspects/strategy/AspectStrategyProvider.java
@@ -17,11 +17,13 @@
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
+import javax.annotation.Nullable;
/** Extension point for providing an aspect strategy */
public interface AspectStrategyProvider {
ExtensionPointName<AspectStrategyProvider> EP_NAME =
ExtensionPointName.create("com.google.idea.blaze.AspectStrategyProvider");
+ @Nullable
AspectStrategy getAspectStrategy(Project project);
}
diff --git a/base/src/com/google/idea/blaze/base/sync/data/BlazeDataStorage.java b/base/src/com/google/idea/blaze/base/sync/data/BlazeDataStorage.java
index 2a9fca8..b86e4f7 100644
--- a/base/src/com/google/idea/blaze/base/sync/data/BlazeDataStorage.java
+++ b/base/src/com/google/idea/blaze/base/sync/data/BlazeDataStorage.java
@@ -24,6 +24,7 @@
/** Defines where we store our blaze project data. */
public class BlazeDataStorage {
private static final String DATA_SUBDIRECTORY = ".blaze";
+ public static final String WORKSPACE_MODULE_NAME = ".workspace";
public static File getProjectDataDir(BlazeImportSettings importSettings) {
return new File(importSettings.getProjectDataDirectory(), DATA_SUBDIRECTORY);
diff --git a/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java b/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java
index e3e4357..871274e 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.base.sync.projectstructure;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.idea.blaze.base.model.BlazeProjectData;
@@ -24,20 +25,26 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
+import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
-import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.util.io.URLUtil;
import java.io.File;
import java.util.Collection;
import java.util.List;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
+import javax.annotation.Nullable;
/** Modifies content entries based on project data. */
public class ContentEntryEditor {
@@ -58,6 +65,9 @@
Multimap<WorkspacePath, WorkspacePath> excludesByRootDirectory =
sortExcludesByRootDirectory(rootDirectories, excludeDirectories);
+ SourceTestConfig testConfig = new SourceTestConfig(projectViewSet);
+ SourceFolderProvider provider = SourceFolderProvider.getSourceFolderProvider(blazeProjectData);
+
List<ContentEntry> contentEntries = Lists.newArrayList();
for (WorkspacePath rootDirectory : rootDirectories) {
File root = workspaceRoot.fileForPath(rootDirectory);
@@ -68,14 +78,80 @@
File excludeFolder = workspaceRoot.fileForPath(exclude);
contentEntry.addExcludeFolder(pathToIdeaUrl(excludeFolder));
}
- }
- for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
- syncPlugin.updateContentEntries(
- project, context, workspaceRoot, projectViewSet, blazeProjectData, contentEntries);
+ ImmutableMap<VirtualFile, SourceFolder> sourceFolders =
+ provider.initializeSourceFolders(contentEntry);
+ VirtualFile rootFile = getVirtualFile(root);
+ SourceFolder rootSource = sourceFolders.get(rootFile);
+ walkFileSystem(
+ workspaceRoot,
+ testConfig,
+ excludesByRootDirectory.get(rootDirectory),
+ contentEntry,
+ provider,
+ sourceFolders,
+ rootSource,
+ rootFile);
}
}
+ private static void walkFileSystem(
+ WorkspaceRoot workspaceRoot,
+ SourceTestConfig testConfig,
+ Collection<WorkspacePath> excludedDirectories,
+ ContentEntry contentEntry,
+ SourceFolderProvider provider,
+ ImmutableMap<VirtualFile, SourceFolder> sourceFolders,
+ SourceFolder parent,
+ VirtualFile file) {
+ if (!file.isDirectory()) {
+ return;
+ }
+ WorkspacePath workspacePath;
+ try {
+ workspacePath = workspaceRoot.workspacePathFor(file);
+ } catch (IllegalArgumentException e) {
+ // stop at directories with unhandled characters.
+ return;
+ }
+ if (excludedDirectories.contains(workspacePath)) {
+ return;
+ }
+ boolean isTest = testConfig.isTestSource(workspacePath.relativePath());
+ SourceFolder current = sourceFolders.get(file);
+ SourceFolder currentOrParent = current != null ? current : parent;
+ if (isTest != currentOrParent.isTestSource()) {
+ if (current != null) {
+ contentEntry.removeSourceFolder(current);
+ }
+ currentOrParent =
+ provider.setSourceFolderForLocation(contentEntry, currentOrParent, file, isTest);
+ }
+ for (VirtualFile child : file.getChildren()) {
+ walkFileSystem(
+ workspaceRoot,
+ testConfig,
+ excludedDirectories,
+ contentEntry,
+ provider,
+ sourceFolders,
+ currentOrParent,
+ child);
+ }
+ }
+
+ @Nullable
+ private static VirtualFile getVirtualFile(File file) {
+ return getFileSystem().findFileByPath(file.getPath());
+ }
+
+ private static VirtualFileSystem getFileSystem() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return TempFileSystem.getInstance();
+ }
+ return LocalFileSystem.getInstance();
+ }
+
private static Multimap<WorkspacePath, WorkspacePath> sortExcludesByRootDirectory(
Collection<WorkspacePath> rootDirectories, Collection<WorkspacePath> excludedDirectories) {
@@ -104,25 +180,29 @@
|| (relativePath.charAt(rootDirectoryString.length()) == '/'));
}
- @NotNull
- private static String pathToUrl(@NotNull String filePath) {
+ private static String pathToUrl(String filePath) {
filePath = FileUtil.toSystemIndependentName(filePath);
if (filePath.endsWith(".srcjar") || filePath.endsWith(".jar")) {
return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath + URLUtil.JAR_SEPARATOR;
} else if (filePath.contains("src.jar!")) {
return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath;
} else {
- return VfsUtilCore.pathToUrl(filePath);
+ return VirtualFileManager.constructUrl(defaultFileSystem().getProtocol(), filePath);
}
}
- @NotNull
- private static String pathToIdeaUrl(@NotNull File path) {
+ private static VirtualFileSystem defaultFileSystem() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return TempFileSystem.getInstance();
+ }
+ return LocalFileSystem.getInstance();
+ }
+
+ private static String pathToIdeaUrl(File path) {
return pathToUrl(toSystemIndependentName(path.getPath()));
}
- @NotNull
- private static String toSystemIndependentName(@NonNls @NotNull String aFileName) {
+ private static String toSystemIndependentName(String aFileName) {
return FileUtilRt.toSystemIndependentName(aFileName);
}
}
diff --git a/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java b/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java
index 8e5b39f..b01fbdc 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectview/LanguageSupport.java
@@ -101,7 +101,7 @@
}
/** @return The set of {@link LanguageClass}'s supported for this {@link WorkspaceType}s. */
- private static Set<LanguageClass> supportedLanguagesForWorkspaceType(WorkspaceType type) {
+ public static Set<LanguageClass> supportedLanguagesForWorkspaceType(WorkspaceType type) {
Set<LanguageClass> supportedLanguages = EnumSet.noneOf(LanguageClass.class);
for (BlazeSyncPlugin syncPlugin : BlazeSyncPlugin.EP_NAME.getExtensions()) {
supportedLanguages.addAll(syncPlugin.getSupportedLanguagesInWorkspace(type));
diff --git a/base/src/com/google/idea/blaze/base/sync/projectview/SourceTestConfig.java b/base/src/com/google/idea/blaze/base/sync/projectview/SourceTestConfig.java
index 018193e..4af730c 100644
--- a/base/src/com/google/idea/blaze/base/sync/projectview/SourceTestConfig.java
+++ b/base/src/com/google/idea/blaze/base/sync/projectview/SourceTestConfig.java
@@ -15,16 +15,46 @@
*/
package com.google.idea.blaze.base.sync.projectview;
+import com.google.common.annotations.VisibleForTesting;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.projectview.section.Glob;
import com.google.idea.blaze.base.projectview.section.sections.TestSourceSection;
+import com.intellij.openapi.util.text.StringUtil;
+import java.io.File;
+import java.util.stream.Collectors;
/** Affects the way sources are imported. */
public class SourceTestConfig {
private final Glob.GlobSet testSources;
public SourceTestConfig(ProjectViewSet projectViewSet) {
- this.testSources = new Glob.GlobSet(projectViewSet.listItems(TestSourceSection.KEY));
+ this.testSources =
+ new Glob.GlobSet(
+ projectViewSet
+ .listItems(TestSourceSection.KEY)
+ .stream()
+ .map(SourceTestConfig::modifyGlob)
+ .collect(Collectors.toList()));
+ }
+
+ private static Glob modifyGlob(Glob glob) {
+ return new Glob(modifyPattern(glob.toString()));
+ }
+
+ /**
+ * We modify the glob patterns provided by the user, so that their behavior more closely matches
+ * what is expected.
+ *
+ * <p>Rules:
+ * <li>path/ => path*
+ * <li>path/* => path*
+ * <li>path => path*
+ */
+ @VisibleForTesting
+ static String modifyPattern(String pattern) {
+ pattern = StringUtil.trimEnd(pattern, '*');
+ pattern = StringUtil.trimEnd(pattern, File.separatorChar);
+ return pattern + "*";
}
/** Returns true if this artifact is a test artifact. */
diff --git a/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusListener.java b/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusListener.java
index e843332..cb7ca1f 100644
--- a/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusListener.java
+++ b/base/src/com/google/idea/blaze/base/sync/status/BlazeSyncStatusListener.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.base.sync.status;
import com.google.idea.blaze.base.scope.BlazeContext;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.intellij.openapi.project.Project;
@@ -26,12 +27,13 @@
public class BlazeSyncStatusListener extends SyncListener.Adapter {
@Override
- public void onSyncStart(Project project, BlazeContext context) {
+ public void onSyncStart(Project project, BlazeContext context, SyncMode syncMode) {
BlazeSyncStatusImpl.getImpl(project).syncStarted();
}
@Override
- public void afterSync(Project project, BlazeContext context, SyncResult syncResult) {
+ public void afterSync(
+ Project project, BlazeContext context, SyncMode syncMode, SyncResult syncResult) {
BlazeSyncStatusImpl.getImpl(project).syncEnded(syncResult);
}
}
diff --git a/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java b/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java
index af965aa..5cf22c6 100644
--- a/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java
+++ b/base/src/com/google/idea/blaze/base/targetmaps/SourceToTargetMapImpl.java
@@ -26,6 +26,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
@@ -115,6 +116,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
getImpl(project).clearSourceToTargetMap();
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java
index 348d657..2f7a6b5 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/BlazeSelectProjectViewOption.java
@@ -20,11 +20,22 @@
/** Provides an option on the "Select .blazeproject" screen */
public interface BlazeSelectProjectViewOption extends BlazeWizardOption {
+ /** Returns a shared project view to use */
@Nullable
- WorkspacePath getSharedProjectView();
+ default WorkspacePath getSharedProjectView() {
+ return null;
+ }
+ /** Returns an initial local project view to use */
@Nullable
- String getInitialProjectViewText();
+ default String getInitialProjectViewText() {
+ return null;
+ }
+
+ /** Whether to allow the sections to add default values for the project view */
+ default boolean allowAddDefaultProjectViewValues() {
+ return false;
+ }
void commit();
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java
index 2d94db8..62db2c5 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/CopyExternalProjectViewOption.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.base.wizard2;
-import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.projectview.ProjectViewStorageManager;
import com.google.idea.blaze.base.ui.BlazeValidationResult;
import com.google.idea.blaze.base.ui.UiUtil;
@@ -91,12 +90,6 @@
@Nullable
@Override
- public WorkspacePath getSharedProjectView() {
- return null;
- }
-
- @Nullable
- @Override
public String getInitialProjectViewText() {
try {
byte[] bytes = Files.readAllBytes(Paths.get(getProjectViewPath()));
diff --git a/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java
index d886175..366e424 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/CreateFromScratchProjectViewOption.java
@@ -15,7 +15,6 @@
*/
package com.google.idea.blaze.base.wizard2;
-import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.google.idea.blaze.base.ui.BlazeValidationResult;
import javax.annotation.Nullable;
import javax.swing.JComponent;
@@ -38,17 +37,16 @@
@Nullable
@Override
- public WorkspacePath getSharedProjectView() {
- return null;
- }
-
- @Nullable
- @Override
public String getInitialProjectViewText() {
return "";
}
@Override
+ public boolean allowAddDefaultProjectViewValues() {
+ return true;
+ }
+
+ @Override
public void commit() {}
@Override
diff --git a/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
index b65059b..e2e41d9 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/GenerateFromBuildFileSelectProjectViewOption.java
@@ -104,12 +104,6 @@
@Nullable
@Override
- public WorkspacePath getSharedProjectView() {
- return null;
- }
-
- @Nullable
- @Override
public String getInitialProjectViewText() {
WorkspacePathResolver workspacePathResolver =
builder.getWorkspaceOption().getWorkspacePathResolver();
@@ -119,6 +113,11 @@
}
@Override
+ public boolean allowAddDefaultProjectViewValues() {
+ return true;
+ }
+
+ @Override
public void commit() {
userSettings.put(LAST_WORKSPACE_PATH, getBuildFilePath());
buildFilePathField.addCurrentTextToHistory();
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java b/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
index e4803be..c2d47e1 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ImportFromWorkspaceProjectViewOption.java
@@ -107,12 +107,6 @@
return new WorkspacePath(getProjectViewPath());
}
- @Nullable
- @Override
- public String getInitialProjectViewText() {
- return null;
- }
-
@Override
public void commit() {
userSettings.put(LAST_WORKSPACE_PATH, getProjectViewPath());
diff --git a/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java b/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java
index 0b96d06..2b8c058 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/UseExistingBazelWorkspaceOption.java
@@ -16,18 +16,51 @@
package com.google.idea.blaze.base.wizard2;
import com.google.idea.blaze.base.bazel.BuildSystemProvider;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.google.idea.blaze.base.ui.BlazeValidationResult;
+import com.google.idea.blaze.base.ui.UiUtil;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDialog;
+import com.intellij.openapi.fileChooser.FileChooserFactory;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.TextFieldWithHistory;
import icons.BlazeIcons;
+import java.awt.Dimension;
import java.io.File;
-import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
-class UseExistingBazelWorkspaceOption extends UseExistingWorkspaceOption {
+class UseExistingBazelWorkspaceOption implements BlazeSelectWorkspaceOption {
+
+ private final JComponent component;
+ private final TextFieldWithHistory directoryField;
UseExistingBazelWorkspaceOption(BlazeNewProjectBuilder builder) {
- super(builder, BuildSystem.Bazel);
+ this.directoryField = new TextFieldWithHistory();
+ this.directoryField.setHistory(builder.getWorkspaceHistory(BuildSystem.Bazel));
+ this.directoryField.setHistorySize(BlazeNewProjectBuilder.HISTORY_SIZE);
+ this.directoryField.setText(builder.getLastImportedWorkspace(BuildSystem.Bazel));
+
+ JButton button = new JButton("...");
+ button.addActionListener(action -> this.chooseDirectory());
+ int buttonSize = this.directoryField.getPreferredSize().height;
+ button.setPreferredSize(new Dimension(buttonSize, buttonSize));
+
+ JComponent box =
+ UiUtil.createHorizontalBox(
+ HORIZONTAL_LAYOUT_GAP,
+ getIconComponent(),
+ new JLabel("Workspace:"),
+ this.directoryField,
+ button);
+ UiUtil.setPreferredWidth(box, PREFERRED_COMPONENT_WIDTH);
+ this.component = box;
}
@Override
@@ -36,21 +69,6 @@
}
@Override
- protected boolean isWorkspaceRoot(File file) {
- return BuildSystemProvider.getWorkspaceRootProvider(BuildSystem.Bazel).isWorkspaceRoot(file);
- }
-
- @Override
- protected BlazeValidationResult validateWorkspaceRoot(File workspaceRoot) {
- if (!isWorkspaceRoot(workspaceRoot)) {
- return BlazeValidationResult.failure(
- "Invalid workspace root: choose a bazel workspace directory "
- + "(containing a WORKSPACE file)");
- }
- return BlazeValidationResult.success();
- }
-
- @Override
public String getOptionName() {
return "use-existing-bazel-workspace";
}
@@ -60,18 +78,105 @@
return "Use existing bazel workspace";
}
+ private static boolean isWorkspaceRoot(File file) {
+ return BuildSystemProvider.getWorkspaceRootProvider(BuildSystem.Bazel).isWorkspaceRoot(file);
+ }
+
+ private static boolean isWorkspaceRoot(VirtualFile file) {
+ return isWorkspaceRoot(new File(file.getPath()));
+ }
+
@Override
- protected String getWorkspaceName(File workspaceRoot) {
+ public BuildSystem getBuildSystemForWorkspace() {
+ return BuildSystem.Bazel;
+ }
+
+ @Override
+ public JComponent getUiComponent() {
+ return component;
+ }
+
+ @Override
+ public void commit() throws BlazeProjectCommitException {}
+
+ @Override
+ public WorkspaceRoot getWorkspaceRoot() {
+ return new WorkspaceRoot(new File(getDirectory()));
+ }
+
+ @Override
+ public File getFileBrowserRoot() {
+ return new File(getDirectory());
+ }
+
+ @Override
+ public String getWorkspaceName() {
+ File workspaceRoot = new File(getDirectory());
return workspaceRoot.getName();
}
@Override
- protected String fileChooserDescription() {
- return "Select the directory of the workspace you want to use.";
+ public BlazeValidationResult validate() {
+ if (getDirectory().isEmpty()) {
+ return BlazeValidationResult.failure("Please select a workspace");
+ }
+ File workspaceRootFile = new File(getDirectory());
+ if (!workspaceRootFile.exists()) {
+ return BlazeValidationResult.failure("Workspace does not exist");
+ }
+ if (!isWorkspaceRoot(workspaceRootFile)) {
+ return BlazeValidationResult.failure(
+ "Invalid workspace root: choose a bazel workspace directory "
+ + "(containing a WORKSPACE file)");
+ }
+ return BlazeValidationResult.success();
}
- @Override
- protected Icon getBuildSystemIcon() {
- return BlazeIcons.BazelLeaf;
+ private String getDirectory() {
+ return directoryField.getText().trim();
+ }
+
+ private void chooseDirectory() {
+ FileChooserDescriptor descriptor =
+ new FileChooserDescriptor(false, true, false, false, false, false) {
+ @Override
+ public boolean isFileSelectable(VirtualFile file) {
+ // Default implementation doesn't filter directories,
+ // we want to make sure only workspace roots are selectable
+ return super.isFileSelectable(file) && isWorkspaceRoot(file);
+ }
+ }.withHideIgnored(false)
+ .withTitle("Select Workspace Root")
+ .withDescription("Select the directory of the workspace you want to use.")
+ .withFileFilter(UseExistingBazelWorkspaceOption::isWorkspaceRoot);
+ FileChooserDialog chooser =
+ FileChooserFactory.getInstance().createFileChooser(descriptor, null, null);
+
+ final VirtualFile[] files;
+ File existingLocation = new File(getDirectory());
+ if (existingLocation.exists()) {
+ VirtualFile toSelect =
+ LocalFileSystem.getInstance().refreshAndFindFileByPath(existingLocation.getPath());
+ files = chooser.choose(null, toSelect);
+ } else {
+ files = chooser.choose(null);
+ }
+ if (files.length == 0) {
+ return;
+ }
+ VirtualFile file = files[0];
+ directoryField.setText(file.getPath());
+ }
+
+ private static JComponent getIconComponent() {
+ JLabel iconPanel =
+ new JLabel(IconLoader.getIconSnapshot(BlazeIcons.BazelLeaf)) {
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+ };
+ UiUtil.setPreferredWidth(iconPanel, 16);
+ return iconPanel;
}
}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/UseExistingWorkspaceOption.java b/base/src/com/google/idea/blaze/base/wizard2/UseExistingWorkspaceOption.java
deleted file mode 100644
index ed4e227..0000000
--- a/base/src/com/google/idea/blaze/base/wizard2/UseExistingWorkspaceOption.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.base.wizard2;
-
-import com.google.common.base.Joiner;
-import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.google.idea.blaze.base.ui.BlazeValidationResult;
-import com.google.idea.blaze.base.ui.UiUtil;
-import com.google.idea.blaze.base.vcs.BlazeVcsHandler;
-import com.intellij.icons.AllIcons;
-import com.intellij.openapi.fileChooser.FileChooserDescriptor;
-import com.intellij.openapi.fileChooser.FileChooserDialog;
-import com.intellij.openapi.fileChooser.FileChooserFactory;
-import com.intellij.openapi.util.IconLoader;
-import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.ui.TextFieldWithHistory;
-import java.awt.Dimension;
-import java.io.File;
-import java.util.Arrays;
-import java.util.stream.Collectors;
-import javax.swing.Icon;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-
-/** Option to use an existing workspace */
-public abstract class UseExistingWorkspaceOption implements BlazeSelectWorkspaceOption {
-
- private final JComponent component;
- private final TextFieldWithHistory directoryField;
- private final BuildSystem buildSystem;
-
- protected UseExistingWorkspaceOption(BlazeNewProjectBuilder builder, BuildSystem buildSystem) {
- this.buildSystem = buildSystem;
-
- this.directoryField = new TextFieldWithHistory();
- directoryField.setHistory(builder.getWorkspaceHistory(buildSystem));
- directoryField.setHistorySize(BlazeNewProjectBuilder.HISTORY_SIZE);
- directoryField.setText(builder.getLastImportedWorkspace(buildSystem));
-
- JButton button = new JButton("...");
- button.addActionListener(action -> chooseDirectory());
- int buttonSize = directoryField.getPreferredSize().height;
- button.setPreferredSize(new Dimension(buttonSize, buttonSize));
-
- JComponent box =
- UiUtil.createHorizontalBox(
- HORIZONTAL_LAYOUT_GAP,
- getIconComponent(),
- new JLabel("Workspace:"),
- directoryField,
- button);
- UiUtil.setPreferredWidth(box, PREFERRED_COMPONENT_WIDTH);
- this.component = box;
- }
-
- protected abstract boolean isWorkspaceRoot(File file);
-
- protected abstract BlazeValidationResult validateWorkspaceRoot(File workspaceRoot);
-
- private boolean isWorkspaceRoot(VirtualFile file) {
- return isWorkspaceRoot(new File(file.getPath()));
- }
-
- protected abstract String fileChooserDescription();
-
- protected abstract Icon getBuildSystemIcon();
-
- protected abstract String getWorkspaceName(File workspaceRoot);
-
- @Override
- public BuildSystem getBuildSystemForWorkspace() {
- return buildSystem;
- }
-
- @Override
- public JComponent getUiComponent() {
- return component;
- }
-
- @Override
- public void commit() throws BlazeProjectCommitException {}
-
- @Override
- public WorkspaceRoot getWorkspaceRoot() {
- return new WorkspaceRoot(new File(getDirectory()));
- }
-
- @Override
- public File getFileBrowserRoot() {
- return new File(getDirectory());
- }
-
- @Override
- public String getWorkspaceName() {
- File workspaceRoot = new File(getDirectory());
- return getWorkspaceName(workspaceRoot);
- }
-
- @Override
- public BlazeValidationResult validate() {
- if (getDirectory().isEmpty()) {
- return BlazeValidationResult.failure("Please select a workspace");
- }
-
- File workspaceRootFile = new File(getDirectory());
- if (!workspaceRootFile.exists()) {
- return BlazeValidationResult.failure("Workspace does not exist");
- }
-
- WorkspaceRoot workspaceRoot = new WorkspaceRoot(workspaceRootFile);
- boolean hasVcsHandler =
- Arrays.stream(BlazeVcsHandler.EP_NAME.getExtensions())
- .anyMatch(vcsHandler -> vcsHandler.handlesProject(buildSystem, workspaceRoot));
- if (!hasVcsHandler) {
- StringBuilder sb = new StringBuilder();
- sb.append("Workspace is not of a supported VCS type. ");
- sb.append("VCS types considered were: ");
- Joiner.on(", ")
- .appendTo(
- sb,
- Arrays.stream(BlazeVcsHandler.EP_NAME.getExtensions())
- .map(BlazeVcsHandler::getVcsName)
- .collect(Collectors.toList()));
- return BlazeValidationResult.failure(sb.toString());
- }
-
- return validateWorkspaceRoot(workspaceRootFile);
- }
-
- private String getDirectory() {
- return directoryField.getText().trim();
- }
-
- private void chooseDirectory() {
- FileChooserDescriptor descriptor =
- new FileChooserDescriptor(false, true, false, false, false, false) {
- @Override
- public boolean isFileSelectable(VirtualFile file) {
- // Default implementation doesn't filter directories,
- // we want to make sure only workspace roots are selectable
- return super.isFileSelectable(file) && isWorkspaceRoot(file);
- }
-
- @Override
- public Icon getIcon(VirtualFile file) {
- if (buildSystem == BuildSystem.Bazel) {
- // isWorkspaceRoot requires file system calls -- it's too expensive
- return super.getIcon(file);
- }
- if (isWorkspaceRoot(file)) {
- return AllIcons.Nodes.SourceFolder;
- }
- return super.getIcon(file);
- }
- }.withHideIgnored(false)
- .withTitle("Select Workspace Root")
- .withDescription(fileChooserDescription())
- .withFileFilter(this::isWorkspaceRoot);
- FileChooserDialog chooser =
- FileChooserFactory.getInstance().createFileChooser(descriptor, null, null);
-
- final VirtualFile[] files;
- File existingLocation = new File(getDirectory());
- if (existingLocation.exists()) {
- VirtualFile toSelect =
- LocalFileSystem.getInstance().refreshAndFindFileByPath(existingLocation.getPath());
- files = chooser.choose(null, toSelect);
- } else {
- files = chooser.choose(null);
- }
- if (files.length == 0) {
- return;
- }
- VirtualFile file = files[0];
- directoryField.setText(file.getPath());
- }
-
- private JComponent getIconComponent() {
- JLabel iconPanel =
- new JLabel(IconLoader.getIconSnapshot(getBuildSystemIcon())) {
- @Override
- public boolean isEnabled() {
- return true;
- }
- };
- UiUtil.setPreferredWidth(iconPanel, 16);
- return iconPanel;
- }
-}
diff --git a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java
index 594d652..0307b5a 100644
--- a/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java
+++ b/base/src/com/google/idea/blaze/base/wizard2/ui/BlazeEditProjectViewControl.java
@@ -22,10 +22,14 @@
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectView;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
+import com.google.idea.blaze.base.projectview.ProjectViewSet.ProjectViewFile;
import com.google.idea.blaze.base.projectview.ProjectViewStorageManager;
import com.google.idea.blaze.base.projectview.ProjectViewVerifier;
+import com.google.idea.blaze.base.projectview.parser.ProjectViewParser;
import com.google.idea.blaze.base.projectview.section.ScalarSection;
+import com.google.idea.blaze.base.projectview.section.SectionParser;
import com.google.idea.blaze.base.projectview.section.sections.ImportSection;
+import com.google.idea.blaze.base.projectview.section.sections.Sections;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.OutputSink.Propagation;
import com.google.idea.blaze.base.scope.Scope;
@@ -44,6 +48,7 @@
import com.google.idea.blaze.base.wizard2.BlazeSelectProjectViewOption;
import com.google.idea.blaze.base.wizard2.BlazeSelectWorkspaceOption;
import com.google.idea.blaze.base.wizard2.ProjectDataDirectoryValidator;
+import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.ide.RecentProjectsManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationNamesInfo;
@@ -76,6 +81,9 @@
new FileChooserDescriptor(false, true, false, false, false, false);
private static final Logger LOG = Logger.getInstance(BlazeEditProjectViewControl.class);
+ private static final BoolExperiment allowAddprojectViewDefaultValues =
+ new BoolExperiment("allow.add.project.view.default.values", true);
+
private final JPanel component;
private final String buildSystemName;
private final ProjectViewUi projectViewUi;
@@ -143,6 +151,10 @@
WorkspaceRoot workspaceRoot = workspaceOption.getWorkspaceRoot();
WorkspacePath workspacePath = projectViewOption.getSharedProjectView();
String initialProjectViewText = projectViewOption.getInitialProjectViewText();
+ boolean allowAddDefaultValues =
+ projectViewOption.allowAddDefaultProjectViewValues()
+ && allowAddprojectViewDefaultValues.getValue();
+ WorkspacePathResolver workspacePathResolver = workspaceOption.getWorkspacePathResolver();
HashCode hashCode =
Hashing.md5()
@@ -151,6 +163,7 @@
.putUnencodedChars(workspaceRoot.toString())
.putUnencodedChars(workspacePath != null ? workspacePath.toString() : "")
.putUnencodedChars(initialProjectViewText != null ? initialProjectViewText : "")
+ .putBoolean(allowAddDefaultValues)
.hash();
// If any params have changed, reinit the control
@@ -159,18 +172,42 @@
init(
workspaceName,
workspaceRoot,
- workspaceOption.getWorkspacePathResolver(),
+ workspacePathResolver,
workspacePath,
- initialProjectViewText);
+ initialProjectViewText,
+ allowAddDefaultValues);
}
}
+ private static String modifyInitialProjectView(
+ String initialProjectViewText, WorkspacePathResolver workspacePathResolver) {
+ BlazeContext context = new BlazeContext();
+ ProjectViewParser projectViewParser = new ProjectViewParser(context, workspacePathResolver);
+ projectViewParser.parseProjectView(initialProjectViewText);
+ ProjectViewSet projectViewSet = projectViewParser.getResult();
+ ProjectViewFile projectViewFile = projectViewSet.getTopLevelProjectViewFile();
+ if (projectViewFile == null) {
+ return initialProjectViewText;
+ }
+ ProjectView projectView = projectViewFile.projectView;
+ for (SectionParser sectionParser : Sections.getParsers()) {
+ projectView = sectionParser.addProjectViewDefaultValue(projectView);
+ }
+ return ProjectViewParser.projectViewToString(projectView);
+ }
+
private void init(
String workspaceName,
WorkspaceRoot workspaceRoot,
WorkspacePathResolver workspacePathResolver,
@Nullable WorkspacePath sharedProjectView,
- @Nullable String initialProjectViewText) {
+ @Nullable String initialProjectViewText,
+ boolean allowAddDefaultValues) {
+ if (allowAddDefaultValues && initialProjectViewText != null) {
+ initialProjectViewText =
+ modifyInitialProjectView(initialProjectViewText, workspacePathResolver);
+ }
+
this.workspaceRoot = workspaceRoot;
projectNameField.setText(workspaceName);
String defaultDataDir = getDefaultProjectDataDirectory(workspaceName);
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/ArgumentCompletionContributorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/ArgumentCompletionContributorTest.java
index aeff26f..d91307f 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/ArgumentCompletionContributorTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/ArgumentCompletionContributorTest.java
@@ -54,10 +54,10 @@
new WorkspacePath("BUILD"),
"def function(name, deps, srcs):",
" # empty function",
- "function(d");
+ "function(dep");
Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
- editorTest.setCaretPosition(editor, 2, "function(n".length());
+ editorTest.setCaretPosition(editor, 2, "function(dep".length());
LookupElement[] completionItems = testFixture.completeBasic();
assertThat(completionItems).isNull();
@@ -82,7 +82,6 @@
editorTest.setCaretPosition(editor, 2, "function(".length());
String[] completionItems = editorTest.getCompletionItemsAsStrings();
- assertThat(completionItems).hasLength(4);
assertThat(completionItems).asList().containsAllOf("name", "deps", "srcs", "function");
});
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuildLabelAutoCompletionTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuildLabelAutoCompletionTest.java
new file mode 100644
index 0000000..bfeb7aa
--- /dev/null
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuildLabelAutoCompletionTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.lang.buildfile.completion;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.impl.LookupImpl;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.testFramework.fixtures.CompletionAutoPopupTester;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for auto-popup code completion in BUILD labels. */
+@RunWith(JUnit4.class)
+public class BuildLabelAutoCompletionTest extends BuildFileIntegrationTestCase {
+
+ private CompletionAutoPopupTester completionTester;
+
+ @Before
+ public final void before() {
+ completionTester = new CompletionAutoPopupTester(testFixture);
+ }
+
+ /** Completion UI testing can't be run on the EDT. */
+ @Override
+ protected boolean runTestsOnEdt() {
+ return false;
+ }
+
+ @Test
+ public void testPopupAutocompleteAfterSlash() {
+ completionTester.runWithAutoPopupEnabled(
+ () -> {
+ createBuildFile(new WorkspacePath("java/com/foo/BUILD"));
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("BUILD"),
+ "java_library(",
+ " name = 'lib',",
+ " srcs = [''],");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 2, " srcs = ['".length());
+
+ completionTester.typeWithPauses("/");
+ assertThat(currentLookupStrings()).containsExactly("'//java/com/foo'");
+ });
+ }
+
+ @Test
+ public void testPopupAutocompleteAfterColon() {
+ completionTester.runWithAutoPopupEnabled(
+ () -> {
+ createBuildFile(new WorkspacePath("java/com/foo/BUILD"), "java_library(name = 'target')");
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("BUILD"),
+ "java_library(",
+ " name = 'lib',",
+ " srcs = ['//java/com/foo'],");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 2, " srcs = ['//java/com/foo".length());
+
+ completionTester.typeWithPauses(":");
+ assertThat(currentLookupStrings()).containsExactly("'//java/com/foo:target'");
+ });
+ }
+
+ @Test
+ public void testPopupAutocompleteAfterLetter() {
+ // test for IntelliJ's standard autocomplete popup trigger
+ completionTester.runWithAutoPopupEnabled(
+ () -> {
+ createBuildFile(new WorkspacePath("java/com/foo/BUILD"), "java_library(name = 'target')");
+ BuildFile file =
+ createBuildFile(
+ new WorkspacePath("BUILD"),
+ "java_library(",
+ " name = 'lib',",
+ " srcs = ['//'],");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 2, " srcs = ['//".length());
+
+ completionTester.typeWithPauses("j");
+ assertThat(currentLookupStrings()).containsExactly("'//java/com/foo'");
+ });
+ }
+
+ private List<String> currentLookupStrings() {
+ LookupImpl lookup = completionTester.getLookup();
+ if (lookup == null) {
+ return ImmutableList.of();
+ }
+ return lookup
+ .getItems()
+ .stream()
+ .map(LookupElement::getLookupString)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionAttributeCompletionContributorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionAttributeCompletionContributorTest.java
index a6c6b9e..8199591 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionAttributeCompletionContributorTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionAttributeCompletionContributorTest.java
@@ -25,6 +25,7 @@
import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.testing.ServiceHelper;
import com.google.repackaged.devtools.build.lib.query2.proto.proto2api.Build;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.editor.Editor;
@@ -35,7 +36,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/** Tests for BuiltInFunctionAttributeCompletionContributor. */
+/** Tests for {@link BuiltInFunctionAttributeCompletionContributor}. */
@RunWith(JUnit4.class)
public class BuiltInFunctionAttributeCompletionContributorTest
extends BuildFileIntegrationTestCase {
@@ -65,10 +66,10 @@
public void testSimpleSingleCompletion() {
setRuleAndAttributes("sh_binary", "name", "deps", "srcs", "data");
- BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "sh_binary(", " n");
+ BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "sh_binary(", " nam");
Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
- editorTest.setCaretPosition(editor, 1, " n".length());
+ editorTest.setCaretPosition(editor, 1, " nam".length());
String[] completionItems = editorTest.getCompletionItemsAsStrings();
assertThat(completionItems).isNull();
@@ -77,6 +78,15 @@
@Test
public void testNoCompletionInUnknownRule() {
+ ServiceHelper.unregisterLanguageExtensionPoint(
+ "com.intellij.completion.contributor",
+ BuiltInSymbolCompletionContributor.class,
+ getTestRootDisposable());
+ ServiceHelper.unregisterLanguageExtensionPoint(
+ "com.intellij.completion.contributor",
+ BuiltInFunctionCompletionContributor.class,
+ getTestRootDisposable());
+
setRuleAndAttributes("sh_binary", "name", "deps", "srcs", "data");
BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "java_binary(");
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributorTest.java
index 6402fc2..6b19f7b 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributorTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInFunctionCompletionContributorTest.java
@@ -24,6 +24,7 @@
import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.testing.ServiceHelper;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
@@ -33,7 +34,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/** Tests BuiltInFunctionCompletionContributor */
+/** Tests {@link BuiltInFunctionCompletionContributor} */
@RunWith(JUnit4.class)
public class BuiltInFunctionCompletionContributorTest extends BuildFileIntegrationTestCase {
@@ -54,11 +55,8 @@
Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
editorTest.setCaretPosition(editor, 0, 0);
- LookupElement[] completionItems = testFixture.completeBasic();
- assertThat(completionItems).hasLength(2);
- assertThat(completionItems[0].getLookupString()).isEqualTo("android_binary");
- assertThat(completionItems[1].getLookupString()).isEqualTo("java_library");
-
+ String[] completionItems = editorTest.getCompletionItemsAsStrings();
+ assertThat(completionItems).asList().containsAllOf("android_binary", "java_library");
assertFileContents(file, "");
}
@@ -66,10 +64,10 @@
public void testUniqueTopLevelCompletion() {
setRules("java_library", "android_binary");
- BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "ja");
+ BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "java_libra");
Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
- editorTest.setCaretPosition(editor, 0, 2);
+ editorTest.setCaretPosition(editor, 0, "java_libra".length());
LookupElement[] completionItems = testFixture.completeBasic();
assertThat(completionItems).isNull();
@@ -83,10 +81,11 @@
setRules("java_library", "android_binary");
BuildFile file =
- createBuildFile(new WorkspacePath("build_defs.bzl"), "def function():", " native.j");
+ createBuildFile(
+ new WorkspacePath("build_defs.bzl"), "def function():", " native.java_libra");
Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
- editorTest.setCaretPosition(editor, 1, " native.j".length());
+ editorTest.setCaretPosition(editor, 1, " native.java_libra".length());
LookupElement[] completionItems = testFixture.completeBasic();
assertThat(completionItems).isNull();
@@ -97,6 +96,11 @@
@Test
public void testNoCompletionInsideRule() {
+ ServiceHelper.unregisterLanguageExtensionPoint(
+ "com.intellij.completion.contributor",
+ BuiltInSymbolCompletionContributor.class,
+ getTestRootDisposable());
+
setRules("java_library", "android_binary");
String[] contents = {"java_library(", " name = \"lib\"", ""};
@@ -123,6 +127,20 @@
assertThat(editorTest.getCompletionItemsAsStrings()).isEmpty();
}
+ @Test
+ public void testGlobalFunctions() {
+ BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "licen");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 0, 5);
+
+ LookupElement[] completionItems = testFixture.completeBasic();
+ assertThat(completionItems).isNull();
+
+ assertFileContents(file, "licenses()");
+ editorTest.assertCaretPosition(editor, 0, "licenses(".length());
+ }
+
private void setRules(String... ruleNames) {
ImmutableMap.Builder<String, RuleDefinition> rules = ImmutableMap.builder();
for (String name : ruleNames) {
@@ -133,7 +151,7 @@
private static class MockBuildLanguageSpecProvider implements BuildLanguageSpecProvider {
- BuildLanguageSpec languageSpec;
+ BuildLanguageSpec languageSpec = new BuildLanguageSpec(ImmutableMap.of());
void setRules(ImmutableMap<String, RuleDefinition> rules) {
languageSpec = new BuildLanguageSpec(rules);
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInSymbolCompletionContributorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInSymbolCompletionContributorTest.java
new file mode 100644
index 0000000..c48cfd5
--- /dev/null
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/BuiltInSymbolCompletionContributorTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.lang.buildfile.completion;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpec;
+import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpecProvider;
+import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests {@link BuiltInSymbolCompletionContributor} */
+@RunWith(JUnit4.class)
+public class BuiltInSymbolCompletionContributorTest extends BuildFileIntegrationTestCase {
+
+ @Before
+ public final void before() {
+ registerApplicationService(
+ BuildLanguageSpecProvider.class,
+ new BuildLanguageSpecProvider() {
+ @Nullable
+ @Override
+ public BuildLanguageSpec getLanguageSpec(Project project) {
+ return null;
+ }
+ });
+ }
+
+ @Test
+ public void testSimpleTopLevelCompletion() {
+ BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 0, 0);
+
+ String[] completionItems = editorTest.getCompletionItemsAsStrings();
+ assertThat(completionItems).asList().containsAnyOf("PACKAGE_NAME", "len", "dict", "struct");
+ assertFileContents(file, "");
+ }
+
+ @Test
+ public void testUniqueTopLevelCompletion() {
+ BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "PACKAGE_N");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 0, "PACKAGE_N".length());
+
+ LookupElement[] completionItems = testFixture.completeBasic();
+ assertThat(completionItems).isNull();
+
+ assertFileContents(file, "PACKAGE_NAME");
+ editorTest.assertCaretPosition(editor, 0, "PACKAGE_NAME".length());
+ }
+
+ @Test
+ public void testNoCompletionInComment() {
+ BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "#PACK");
+
+ Editor editor = editorTest.openFileInEditor(file.getVirtualFile());
+ editorTest.setCaretPosition(editor, 0, "#PACK".length());
+
+ assertThat(editorTest.getCompletionItemsAsStrings()).isEmpty();
+ }
+}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/LocalSymbolCompletionTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/LocalSymbolCompletionTest.java
index d29676a..a4147d8 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/LocalSymbolCompletionTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/completion/LocalSymbolCompletionTest.java
@@ -39,11 +39,11 @@
@Test
public void testLocalVariable() {
- setInput("var = [a, b]", "def function(name, deps, srcs):", " v<caret>");
+ setInput("var_name = [a, b]", "def function(name, deps, srcs):", " var_n<caret>");
editorTest.completeIfUnique();
- assertResult("var = [a, b]", "def function(name, deps, srcs):", " var<caret>");
+ assertResult("var_name = [a, b]", "def function(name, deps, srcs):", " var_name<caret>");
}
@Test
@@ -57,7 +57,7 @@
@Test
public void testNoCompletionAfterDot() {
- setInput("var = [a, b]", "def function(name, deps, srcs):", " ext.v<caret>");
+ setInput("var_name = [a, b]", "def function(name, deps, srcs):", " ext.var_na<caret>");
String[] completionItems = editorTest.getCompletionItemsAsStrings();
assertThat(completionItems).isEmpty();
@@ -65,41 +65,41 @@
@Test
public void testFunctionParam() {
- setInput("def test(var):", " v<caret>");
+ setInput("def test(var_name):", " var_na<caret>");
editorTest.completeIfUnique();
- assertResult("def test(var):", " var<caret>");
+ assertResult("def test(var_name):", " var_name<caret>");
}
// b/28912523: when symbol is present in multiple assignment statements, should only be
// included once in the code-completion dialog
@Test
public void testSymbolAssignedMultipleTimes() {
- setInput("var = 1", "var = 2", "var = 3", "<caret>");
+ setInput("var_name = 1", "var_name = 2", "var_name = 3", "var_na<caret>");
editorTest.completeIfUnique();
- assertResult("var = 1", "var = 2", "var = 3", "var<caret>");
+ assertResult("var_name = 1", "var_name = 2", "var_name = 3", "var_name<caret>");
}
@Test
public void testSymbolDefinedOutsideScope() {
- setInput("<caret>", "var = 1");
+ setInput("var_na<caret>", "var_name = 1");
assertThat(editorTest.getCompletionItemsAsStrings()).isEmpty();
}
@Test
public void testSymbolDefinedOutsideScope2() {
- setInput("def fn():", " var = 1", "v<caret>");
+ setInput("def fn():", " var_name = 1", "var_na<caret>");
assertThat(testFixture.completeBasic()).isEmpty();
}
@Test
public void testSymbolDefinedOutsideScope3() {
- setInput("for var in (1, 2, 3): print var", "v<caret>");
+ setInput("for var_name in (1, 2, 3): print var_name", "var_na<caret>");
assertThat(testFixture.completeBasic()).isEmpty();
}
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/findusages/ExternalFileUsagesTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/findusages/ExternalFileUsagesTest.java
index b7a8392..f50dbf2 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/findusages/ExternalFileUsagesTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/buildfile/findusages/ExternalFileUsagesTest.java
@@ -57,6 +57,7 @@
Argument.Keyword arg =
buildFile.findChildByClass(FuncallExpression.class).getKeywordArgument("srcs");
+ assertThat(arg).isNotNull();
PsiElement ref = references[0].getElement();
assertThat(ref).isInstanceOf(StringLiteral.class);
diff --git a/base/tests/unittests/com/google/idea/blaze/base/issueparser/BlazeIssueParserTest.java b/base/tests/unittests/com/google/idea/blaze/base/issueparser/BlazeIssueParserTest.java
index 322754d..437822a 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/issueparser/BlazeIssueParserTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/issueparser/BlazeIssueParserTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.BlazeTestCase;
import com.google.idea.blaze.base.model.primitives.TargetExpression;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
@@ -30,10 +31,11 @@
import com.google.idea.blaze.base.projectview.section.ListSection;
import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
import com.google.idea.blaze.base.scope.output.IssueOutput;
+import com.google.idea.blaze.base.scope.output.IssueOutput.Category;
import com.google.idea.common.experiments.ExperimentService;
import com.google.idea.common.experiments.MockExperimentService;
import java.io.File;
-import org.jetbrains.annotations.NotNull;
+import java.util.regex.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -44,10 +46,10 @@
private ProjectViewManager projectViewManager;
private WorkspaceRoot workspaceRoot;
+ private ImmutableList<BlazeIssueParser.Parser> parsers;
@Override
- protected void initTest(
- @NotNull Container applicationServices, @NotNull Container projectServices) {
+ protected void initTest(Container applicationServices, Container projectServices) {
super.initTest(applicationServices, projectServices);
applicationServices.register(ExperimentService.class, new MockExperimentService());
@@ -55,12 +57,40 @@
projectViewManager = mock(ProjectViewManager.class);
projectServices.register(ProjectViewManager.class, projectViewManager);
+ ProjectViewSet projectViewSet =
+ ProjectViewSet.builder()
+ .add(
+ new File(".blazeproject"),
+ ProjectView.builder()
+ .add(
+ ListSection.builder(TargetSection.KEY)
+ .add(TargetExpression.fromString("//tests/com/google/a/b/c/d/baz:baz"))
+ .add(TargetExpression.fromString("//package/path:hello4")))
+ .build())
+ .build();
+ when(projectViewManager.getProjectViewSet()).thenReturn(projectViewSet);
+
workspaceRoot = new WorkspaceRoot(new File("/root"));
+
+ parsers =
+ ImmutableList.of(
+ new BlazeIssueParser.CompileParser(workspaceRoot),
+ new BlazeIssueParser.TracebackParser(),
+ new BlazeIssueParser.BuildParser(),
+ new BlazeIssueParser.LinelessBuildParser(),
+ new BlazeIssueParser.ProjectViewLabelParser(projectViewSet),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "no such package '(.*)': BUILD file not found on package path"),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "no targets found beneath '(.*)'"),
+ new BlazeIssueParser.InvalidTargetProjectViewPackageParser(
+ projectViewSet, "ERROR: invalid target format '(.*)'"),
+ new BlazeIssueParser.FileNotFoundBuildParser(workspaceRoot));
}
@Test
public void testParseTargetError() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"ERROR: invalid target format "
@@ -74,7 +104,7 @@
@Test
public void testParseCompileError() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"java/com/google/android/samples/helloroot/math/DivideMath.java:17: error: "
@@ -83,6 +113,7 @@
assertThat(issue.getFile().getPath())
.isEqualTo("/root/java/com/google/android/samples/helloroot/math/DivideMath.java");
assertThat(issue.getLine()).isEqualTo(17);
+ assertThat(issue.getColumn()).isEqualTo(-1);
assertThat(issue.getMessage())
.isEqualTo("non-static variable this cannot be referenced from a static context");
assertThat(issue.getCategory()).isEqualTo(IssueOutput.Category.ERROR);
@@ -90,50 +121,29 @@
@Test
public void testParseCompileErrorWithColumn() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"java/com/google/devtools/aswb/pluginrepo/googleplex/PluginsEndpoint.java:33:26: "
+ "error: '|' is not preceded with whitespace.");
assertNotNull(issue);
assertThat(issue.getLine()).isEqualTo(33);
+ assertThat(issue.getColumn()).isEqualTo(26);
assertThat(issue.getMessage()).isEqualTo("'|' is not preceded with whitespace.");
assertThat(issue.getCategory()).isEqualTo(IssueOutput.Category.ERROR);
}
@Test
- public void testParseCompileErrorWithAbsolutePath() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
- IssueOutput issue =
- blazeIssueParser.parseIssue(
- "/root/java/com/google/android/samples/helloroot/math/DivideMath.java:17: error: "
- + "non-static variable this cannot be referenced from a static context");
- assertNotNull(issue);
- assertThat(issue.getFile().getPath())
- .isEqualTo("/root/java/com/google/android/samples/helloroot/math/DivideMath.java");
- }
-
- @Test
- public void testParseCompileErrorWithDepotPath() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
- IssueOutput issue =
- blazeIssueParser.parseIssue(
- "//depot/google3/package_path/DivideMath.java:17: error: "
- + "non-static variable this cannot be referenced from a static context");
- assertNotNull(issue);
- assertThat(issue.getFile().getPath()).isEqualTo("/root/package_path/DivideMath.java");
- }
-
- @Test
public void testParseBuildError() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
- "ERROR: /path/to/root/javatests/package_path/BUILD:42:12: "
+ "ERROR: /root/javatests/package_path/BUILD:42:12: "
+ "Target '//java/package_path:helloroot_visibility' failed");
assertNotNull(issue);
- assertThat(issue.getFile().getPath()).isEqualTo("/path/to/root/javatests/package_path/BUILD");
+ assertThat(issue.getFile().getPath()).isEqualTo("/root/javatests/package_path/BUILD");
assertThat(issue.getLine()).isEqualTo(42);
+ assertThat(issue.getColumn()).isEqualTo(12);
assertThat(issue.getMessage())
.isEqualTo("Target '//java/package_path:helloroot_visibility' failed");
assertThat(issue.getCategory()).isEqualTo(IssueOutput.Category.ERROR);
@@ -141,7 +151,7 @@
@Test
public void testParseLinelessBuildError() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"ERROR: /path/to/root/java/package_path/BUILD:char offsets 1222--1229: "
@@ -153,20 +163,37 @@
}
@Test
- public void testLabelProjectViewParser() {
- ProjectViewSet projectViewSet =
- ProjectViewSet.builder()
- .add(
- new File(".blazeproject"),
- ProjectView.builder()
- .add(
- ListSection.builder(TargetSection.KEY)
- .add(TargetExpression.fromString("//package/path:hello4")))
- .build())
- .build();
- when(projectViewManager.getProjectViewSet()).thenReturn(projectViewSet);
+ public void testParseFileNotFoundError() {
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
+ IssueOutput issue =
+ blazeIssueParser.parseIssue(
+ "ERROR: Extension file not found. Unable to load file '//third_party/bazel:tools/ide/"
+ + "intellij_info.bzl': file doesn't exist or isn't a file");
+ assertNotNull(issue);
+ assertThat(issue.getFile().getPath())
+ .isEqualTo("/root/third_party/bazel/tools/ide/intellij_info.bzl");
+ assertThat(issue.getMessage()).isEqualTo("file doesn't exist or isn't a file");
+ assertThat(issue.getCategory()).isEqualTo(IssueOutput.Category.ERROR);
+ }
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ @Test
+ public void testParseFileNotFoundErrorWithPackage() {
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
+ IssueOutput issue =
+ blazeIssueParser.parseIssue(
+ "ERROR: error loading package 'path/to/package': Extension file not found. Unable to"
+ + " load file '//third_party/bazel:tools/ide/intellij_info.bzl': file doesn't exist"
+ + " or isn't a file");
+ assertNotNull(issue);
+ assertThat(issue.getFile().getPath())
+ .isEqualTo("/root/third_party/bazel/tools/ide/intellij_info.bzl");
+ assertThat(issue.getMessage()).isEqualTo("file doesn't exist or isn't a file");
+ assertThat(issue.getCategory()).isEqualTo(IssueOutput.Category.ERROR);
+ }
+
+ @Test
+ public void testLabelProjectViewParser() {
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"no such target '//package/path:hello4': "
@@ -179,19 +206,7 @@
@Test
public void testPackageProjectViewParser() {
- ProjectViewSet projectViewSet =
- ProjectViewSet.builder()
- .add(
- new File(".blazeproject"),
- ProjectView.builder()
- .add(
- ListSection.builder(TargetSection.KEY)
- .add(TargetExpression.fromString("//package/path:hello4")))
- .build())
- .build();
- when(projectViewManager.getProjectViewSet()).thenReturn(projectViewSet);
-
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"no such package 'package/path': BUILD file not found on package path");
@@ -202,19 +217,7 @@
@Test
public void testDeletedBUILDFileButLeftPackageInLocalTargets() {
- ProjectViewSet projectViewSet =
- ProjectViewSet.builder()
- .add(
- new File(".blazeproject"),
- ProjectView.builder()
- .add(
- ListSection.builder(TargetSection.KEY)
- .add(TargetExpression.fromString("//tests/com/google/a/b/c/d/baz:baz")))
- .build())
- .build();
- when(projectViewManager.getProjectViewSet()).thenReturn(projectViewSet);
-
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"Error:com.google.a.b.Exception exception in Bar: no targets found beneath "
@@ -240,7 +243,7 @@
"name 'BAD_FUNCTION' is not defined."
};
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
for (int i = 0; i < lines.length - 1; ++i) {
IssueOutput issue = blazeIssueParser.parseIssue(lines[i]);
assertNull(issue);
@@ -266,7 +269,7 @@
"name 'BAD_FUNCTION' is not defined."
};
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
for (int i = 0; i < lines.length; ++i) {
blazeIssueParser.parseIssue(lines[i]);
}
@@ -282,7 +285,7 @@
@Test
public void testMultipleIssues() {
- BlazeIssueParser blazeIssueParser = new BlazeIssueParser(project, workspaceRoot);
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(parsers);
IssueOutput issue =
blazeIssueParser.parseIssue(
"ERROR: /home/plumpy/whatever:char offsets 1222--1229: name 'grubber' is not defined");
@@ -295,5 +298,32 @@
blazeIssueParser.parseIssue(
"ERROR: /home/plumpy/whatever:char offsets 1222--1229: name 'grubber' is not defined");
assertNotNull(issue);
+
+ }
+
+ @Test
+ public void testExtraParserMatch() {
+ BlazeIssueParser blazeIssueParser = new BlazeIssueParser(ImmutableList.of(new TestParser()));
+ IssueOutput issue =
+ blazeIssueParser.parseIssue("TEST This is a test message for our test parser.");
+ assertNotNull(issue);
+ assertThat(issue.getMessage()).isEqualTo("This is a test message for our test parser.");
+ assertThat(issue.getLine()).isEqualTo(-1);
+ assertThat(issue.getColumn()).isEqualTo(-1);
+ assertThat(issue.getCategory()).isEqualTo(Category.WARNING);
+ assertNull(issue.getFile());
+ }
+
+ /** Simple Parser for testing */
+ private static class TestParser extends BlazeIssueParser.SingleLineParser {
+
+ public TestParser() {
+ super("^TEST (.*)$");
+ }
+
+ @Override
+ protected IssueOutput createIssue(Matcher matcher) {
+ return IssueOutput.warn(matcher.group(1)).build();
+ }
}
}
diff --git a/base/tests/unittests/com/google/idea/blaze/base/sync/projectview/SourceTestConfigTest.java b/base/tests/unittests/com/google/idea/blaze/base/sync/projectview/SourceTestConfigTest.java
new file mode 100644
index 0000000..5f2051f
--- /dev/null
+++ b/base/tests/unittests/com/google/idea/blaze/base/sync/projectview/SourceTestConfigTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.sync.projectview;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link SourceTestConfig} */
+@RunWith(JUnit4.class)
+public class SourceTestConfigTest {
+
+ @Test
+ public void testGlobModification() {
+ assertThat(SourceTestConfig.modifyPattern("path/to/file/*")).isEqualTo("path/to/file*");
+ assertThat(SourceTestConfig.modifyPattern("path/to/file/")).isEqualTo("path/to/file*");
+ assertThat(SourceTestConfig.modifyPattern("path/to/file")).isEqualTo("path/to/file*");
+ assertThat(SourceTestConfig.modifyPattern("path/to/file*")).isEqualTo("path/to/file*");
+ }
+}
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java b/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java
index 1424a0d..b197413 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/sync/BlazeSyncIntegrationTestCase.java
@@ -44,6 +44,7 @@
import com.google.idea.blaze.base.settings.BlazeImportSettings;
import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
import com.google.idea.blaze.base.sync.aspects.BlazeIdeInterface;
+import com.google.idea.blaze.base.sync.data.BlazeDataStorage;
import com.google.idea.blaze.base.sync.projectstructure.ModuleEditorImpl;
import com.google.idea.blaze.base.sync.projectstructure.ModuleEditorProvider;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
@@ -53,8 +54,10 @@
import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
import com.google.idea.blaze.base.vcs.BlazeVcsHandler;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@@ -79,6 +82,8 @@
protected ErrorCollector errorCollector;
protected BlazeContext context;
+ private ImmutableList<ContentEntry> workspaceContentEntries = ImmutableList.of();
+
@Before
public void doSetup() throws Exception {
projectViewManager = new MockProjectViewManager();
@@ -99,9 +104,12 @@
@Override
public void commit() {
// don't commit module changes,
- // but make sure they're properly disposed when the test is finished
+ // and make sure they're properly disposed when the test is finished
for (ModifiableRootModel model : modifiableModels) {
Disposer.register(getTestRootDisposable(), model::dispose);
+ if (model.getModule().getName().equals(BlazeDataStorage.WORKSPACE_MODULE_NAME)) {
+ workspaceContentEntries = ImmutableList.copyOf(model.getContentEntries());
+ }
}
}
};
@@ -128,6 +136,22 @@
workspaceRoot.toString()));
}
+ /** The workspace content entries created during sync */
+ protected ImmutableList<ContentEntry> getWorkspaceContentEntries() {
+ return workspaceContentEntries;
+ }
+
+ /** Search the workspace module's {@link ContentEntry}s for one with the given file. */
+ @Nullable
+ protected ContentEntry findContentEntry(VirtualFile root) {
+ for (ContentEntry entry : workspaceContentEntries) {
+ if (root.equals(entry.getFile())) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
protected static ArtifactLocation sourceRoot(String relativePath) {
return ArtifactLocation.builder().setRelativePath(relativePath).setIsSource(true).build();
}
diff --git a/clwb/src/com/google/idea/blaze/clwb/sync/BlazeCLionSyncPlugin.java b/clwb/src/com/google/idea/blaze/clwb/sync/BlazeCLionSyncPlugin.java
index 00888bc..64b1059 100644
--- a/clwb/src/com/google/idea/blaze/clwb/sync/BlazeCLionSyncPlugin.java
+++ b/clwb/src/com/google/idea/blaze/clwb/sync/BlazeCLionSyncPlugin.java
@@ -17,17 +17,12 @@
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.model.primitives.WorkspaceType;
-import com.google.idea.blaze.base.projectview.ProjectViewSet;
-import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.GenericSourceFolderProvider;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.intellij.openapi.module.ModuleType;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ContentEntry;
-import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.cidr.cpp.CPPModuleType;
-import java.util.Collection;
import javax.annotation.Nullable;
class BlazeCLionSyncPlugin extends BlazeSyncPlugin.Adapter {
@@ -52,20 +47,13 @@
return null;
}
+ @Nullable
@Override
- public void updateContentEntries(
- Project project,
- BlazeContext context,
- WorkspaceRoot workspaceRoot,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- Collection<ContentEntry> contentEntries) {
-
- for (ContentEntry entry : contentEntries) {
- VirtualFile file = entry.getFile();
- if (file != null) {
- entry.addSourceFolder(file, false);
- }
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ if (!projectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.C)) {
+ return null;
}
+ return GenericSourceFolderProvider.INSTANCE;
}
+
}
diff --git a/common/formatter/BUILD b/common/formatter/BUILD
new file mode 100644
index 0000000..839a469
--- /dev/null
+++ b/common/formatter/BUILD
@@ -0,0 +1,11 @@
+licenses(["notice"]) # Apache 2.0
+
+java_library(
+ name = "formatter",
+ srcs = glob(["src/**/*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//intellij_platform_sdk:plugin_api",
+ "@jsr305_annotations//jar",
+ ],
+)
diff --git a/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java b/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java
new file mode 100644
index 0000000..aa2460f
--- /dev/null
+++ b/common/formatter/src/com/google/idea/common/formatter/DelegatingCodeStyleManager.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.common.formatter;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.codeStyle.Indent;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.ThrowableRunnable;
+import java.util.Collection;
+import javax.annotation.Nullable;
+
+/** A delegating {@link CodeStyleManager}. */
+public abstract class DelegatingCodeStyleManager extends CodeStyleManager {
+
+ private final CodeStyleManager delegate;
+
+ public DelegatingCodeStyleManager(CodeStyleManager delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Project getProject() {
+ return delegate.getProject();
+ }
+
+ @Override
+ public PsiElement reformat(PsiElement element) throws IncorrectOperationException {
+ return delegate.reformat(element);
+ }
+
+ @Override
+ public PsiElement reformat(PsiElement element, boolean canChangeWhiteSpacesOnly)
+ throws IncorrectOperationException {
+ return delegate.reformat(element, canChangeWhiteSpacesOnly);
+ }
+
+ @Override
+ public PsiElement reformatRange(PsiElement element, int startOffset, int endOffset)
+ throws IncorrectOperationException {
+ return delegate.reformatRange(element, startOffset, endOffset);
+ }
+
+ @Override
+ public PsiElement reformatRange(
+ PsiElement element, int startOffset, int endOffset, boolean canChangeWhiteSpacesOnly)
+ throws IncorrectOperationException {
+ return delegate.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly);
+ }
+
+ @Override
+ public void reformatText(PsiFile file, int startOffset, int endOffset)
+ throws IncorrectOperationException {
+ delegate.reformatText(file, startOffset, endOffset);
+ }
+
+ @Override
+ public void reformatText(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ delegate.reformatText(file, ranges);
+ }
+
+ @Override
+ public void reformatTextWithContext(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ delegate.reformatTextWithContext(file, ranges);
+ }
+
+ @Override
+ public void adjustLineIndent(PsiFile file, TextRange rangeToAdjust)
+ throws IncorrectOperationException {
+ delegate.adjustLineIndent(file, rangeToAdjust);
+ }
+
+ @Override
+ public int adjustLineIndent(PsiFile file, int offset) throws IncorrectOperationException {
+ return delegate.adjustLineIndent(file, offset);
+ }
+
+ @Override
+ public int adjustLineIndent(Document document, int offset) {
+ return delegate.adjustLineIndent(document, offset);
+ }
+
+ @Override
+ public boolean isLineToBeIndented(PsiFile file, int offset) {
+ return delegate.isLineToBeIndented(file, offset);
+ }
+
+ @Override
+ @Nullable
+ public String getLineIndent(PsiFile file, int offset) {
+ return delegate.getLineIndent(file, offset);
+ }
+
+ @Override
+ @Nullable
+ public String getLineIndent(Document document, int offset) {
+ return delegate.getLineIndent(document, offset);
+ }
+
+ @Override
+ public Indent getIndent(String text, FileType fileType) {
+ return delegate.getIndent(text, fileType);
+ }
+
+ @Override
+ public String fillIndent(Indent indent, FileType fileType) {
+ return delegate.fillIndent(indent, fileType);
+ }
+
+ @Override
+ public Indent zeroIndent() {
+ return delegate.zeroIndent();
+ }
+
+ @Override
+ public void reformatNewlyAddedElement(ASTNode block, ASTNode addedElement)
+ throws IncorrectOperationException {
+ delegate.reformatNewlyAddedElement(block, addedElement);
+ }
+
+ @Override
+ public boolean isSequentialProcessingAllowed() {
+ return delegate.isSequentialProcessingAllowed();
+ }
+
+ @Override
+ public void performActionWithFormatterDisabled(Runnable r) {
+ delegate.performActionWithFormatterDisabled(r);
+ }
+
+ @Override
+ public <T extends Throwable> void performActionWithFormatterDisabled(ThrowableRunnable<T> r)
+ throws T {
+ delegate.performActionWithFormatterDisabled(r);
+ }
+
+ @Override
+ public <T> T performActionWithFormatterDisabled(Computable<T> r) {
+ return delegate.performActionWithFormatterDisabled(r);
+ }
+}
diff --git a/common/formatter/src/com/google/idea/common/formatter/FormatterInstaller.java b/common/formatter/src/com/google/idea/common/formatter/FormatterInstaller.java
new file mode 100644
index 0000000..390dfb4
--- /dev/null
+++ b/common/formatter/src/com/google/idea/common/formatter/FormatterInstaller.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.common.formatter;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import org.picocontainer.MutablePicoContainer;
+
+/** A utility class to replace the default IntelliJ {@link CodeStyleManager}. */
+public final class FormatterInstaller {
+
+ /** A factory for constructing a {@link CodeStyleManager} given the current instance. */
+ public interface CodeStyleManagerFactory {
+ CodeStyleManager createFormatter(CodeStyleManager delegate);
+ }
+
+ private static final String CODE_STYLE_MANAGER_KEY = CodeStyleManager.class.getName();
+
+ /**
+ * Replace the existing formatter with one produced from the given {@link CodeStyleManagerFactory}
+ */
+ public static void replaceFormatter(Project project, CodeStyleManagerFactory newFormatter) {
+ CodeStyleManager currentManager = CodeStyleManager.getInstance(project);
+ MutablePicoContainer container = (MutablePicoContainer) project.getPicoContainer();
+ container.unregisterComponent(CODE_STYLE_MANAGER_KEY);
+ container.registerComponentInstance(
+ CODE_STYLE_MANAGER_KEY, newFormatter.createFormatter(currentManager));
+ }
+}
diff --git a/cpp/BUILD b/cpp/BUILD
index f38d357..d5361bf 100644
--- a/cpp/BUILD
+++ b/cpp/BUILD
@@ -1,5 +1,7 @@
licenses(["notice"]) # Apache 2.0
+load("//intellij_platform_sdk:build_defs.bzl", "select_for_plugin_api")
+
java_library(
name = "cpp",
srcs = glob(
@@ -7,13 +9,14 @@
exclude = [
"src/com/google/idea/blaze/cpp/versioned/**",
],
- ) + select({
- "//intellij_platform_sdk:android-studio-latest": [":api_v145_sources"],
- "//conditions:default": [":api_v162_sources"],
+ ) + select_for_plugin_api({
+ "android-studio-145.1617.8": [":api_v145_sources"],
+ "default": [":api_v162_sources"],
}),
visibility = ["//visibility:public"],
deps = [
"//base",
+ "//common/experiments",
"//intellij_platform_sdk:plugin_api",
"@jsr305_annotations//jar",
],
diff --git a/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java b/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java
index 2795722..1b4937b 100644
--- a/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java
+++ b/cpp/src/com/google/idea/blaze/cpp/BlazeCWorkspace.java
@@ -20,12 +20,15 @@
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.Blaze;
+import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.jetbrains.cidr.lang.symbols.OCSymbol;
import com.jetbrains.cidr.lang.workspace.OCResolveConfiguration;
import com.jetbrains.cidr.lang.workspace.OCWorkspace;
@@ -38,6 +41,9 @@
public final class BlazeCWorkspace implements OCWorkspace {
private static final Logger LOG = Logger.getInstance(BlazeCWorkspace.class);
+ private static final BoolExperiment refreshExecRoot =
+ new BoolExperiment("refresh.exec.root.cpp", true);
+
@Nullable private final Project project;
@Nullable private final OCWorkspaceModificationTrackers modTrackers;
@@ -65,13 +71,9 @@
long start = System.currentTimeMillis();
- // non-recursive refresh of the blaze-out directory. This essentially invalidates the cache for
- // all files below this directory.
- ApplicationManager.getApplication()
- .runWriteAction(
- () ->
- LocalFileSystem.getInstance()
- .refreshIoFiles(ImmutableList.of(blazeProjectData.blazeRoots.executionRoot)));
+ if (refreshExecRoot.getValue()) {
+ refreshExecRoot(blazeProjectData);
+ }
// Non-incremental update to our c configurations.
configurationResolver.update(context, blazeProjectData);
@@ -93,6 +95,19 @@
});
}
+ private static void refreshExecRoot(BlazeProjectData blazeProjectData) {
+ // recursive refresh of the blaze execution root. This is required because:
+ // <li>Our blaze aspect can't tell us exactly which genfiles are required to resolve the project
+ // <li>Cidr caches the directory contents as part of symbol building, so we need to do this work
+ // up front.
+ VirtualFile execRoot =
+ getFileSystem().findFileByIoFile(blazeProjectData.blazeRoots.executionRoot);
+ if (execRoot != null) {
+ ApplicationManager.getApplication()
+ .runWriteAction(() -> VfsUtil.markDirtyAndRefresh(false, true, true, execRoot));
+ }
+ }
+
@Override
public Collection<VirtualFile> getLibraryFilesToBuildSymbols() {
// This method should return all the header files themselves, not the head file directories.
@@ -148,4 +163,11 @@
OCResolveConfiguration config = configurationResolver.getConfigurationForFile(sourceFile);
return config == null ? ImmutableList.of() : ImmutableList.of(config);
}
+
+ private static LocalFileSystem getFileSystem() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return TempFileSystem.getInstance();
+ }
+ return LocalFileSystem.getInstance();
+ }
}
diff --git a/ijwb/BUILD b/ijwb/BUILD
index d327898..0ec5aad 100644
--- a/ijwb/BUILD
+++ b/ijwb/BUILD
@@ -66,6 +66,7 @@
load(
"//testing:test_defs.bzl",
+ "intellij_integration_test_suite",
"intellij_unit_test_suite",
)
@@ -84,3 +85,22 @@
"@junit//jar",
],
)
+
+intellij_integration_test_suite(
+ name = "integration_tests",
+ srcs = glob(["tests/integrationtests/**/*.java"]),
+ required_plugins = "com.google.idea.blaze.ijwb",
+ test_package_root = "com.google.idea.blaze.ijwb",
+ runtime_deps = [
+ ":ijwb_bazel",
+ ],
+ deps = [
+ ":ijwb_lib",
+ "//base",
+ "//base:integration_test_utils",
+ "//base:unit_test_utils",
+ "//intellij_platform_sdk:plugin_api_for_tests",
+ "@jsr305_annotations//jar",
+ "@junit//jar",
+ ],
+)
diff --git a/ijwb/src/META-INF/ijwb_bazel.xml b/ijwb/src/META-INF/ijwb_bazel.xml
index 00430e9..3d7fbc2 100644
--- a/ijwb/src/META-INF/ijwb_bazel.xml
+++ b/ijwb/src/META-INF/ijwb_bazel.xml
@@ -30,10 +30,4 @@
]]>
</description>
- <application-components>
- <component>
- <implementation-class>com.google.idea.blaze.ijwb.plugin.MigrateBazelPluginDependency</implementation-class>
- </component>
- </application-components>
-
</idea-plugin>
diff --git a/ijwb/src/com/google/idea/blaze/ijwb/android/BlazeAndroidLiteJavaSyncAugmenter.java b/ijwb/src/com/google/idea/blaze/ijwb/android/BlazeAndroidLiteJavaSyncAugmenter.java
index 6a2b844..69ec1d8 100644
--- a/ijwb/src/com/google/idea/blaze/ijwb/android/BlazeAndroidLiteJavaSyncAugmenter.java
+++ b/ijwb/src/com/google/idea/blaze/ijwb/android/BlazeAndroidLiteJavaSyncAugmenter.java
@@ -19,6 +19,7 @@
import com.google.idea.blaze.base.ideinfo.LibraryArtifact;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.java.sync.BlazeJavaSyncAugmenter;
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
@@ -30,6 +31,7 @@
@Override
public void addJarsForSourceTarget(
WorkspaceLanguageSettings workspaceLanguageSettings,
+ ProjectViewSet projectViewSet,
TargetIdeInfo target,
Collection<BlazeJarLibrary> jars,
Collection<BlazeJarLibrary> genJars) {
diff --git a/ijwb/src/com/google/idea/blaze/ijwb/javascript/BlazeJavascriptSyncPlugin.java b/ijwb/src/com/google/idea/blaze/ijwb/javascript/BlazeJavascriptSyncPlugin.java
index 4d19ffc..c684726 100644
--- a/ijwb/src/com/google/idea/blaze/ijwb/javascript/BlazeJavascriptSyncPlugin.java
+++ b/ijwb/src/com/google/idea/blaze/ijwb/javascript/BlazeJavascriptSyncPlugin.java
@@ -20,27 +20,25 @@
import com.google.common.collect.Lists;
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.WorkspacePath;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.model.primitives.WorkspaceType;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.GenericSourceFolderProvider;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.base.sync.libraries.LibrarySource;
-import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.WebModuleType;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.PlatformUtils;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
@@ -67,31 +65,13 @@
return ImmutableSet.of(LanguageClass.JAVASCRIPT);
}
+ @Nullable
@Override
- public void updateContentEntries(
- Project project,
- BlazeContext context,
- WorkspaceRoot workspaceRoot,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- Collection<ContentEntry> contentEntries) {
- if (!blazeProjectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.JAVASCRIPT)) {
- return;
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ if (!projectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.JAVASCRIPT)) {
+ return null;
}
-
- SourceTestConfig testConfig = new SourceTestConfig(projectViewSet);
- for (ContentEntry contentEntry : contentEntries) {
- VirtualFile virtualFile = contentEntry.getFile();
- if (virtualFile == null) {
- continue;
- }
- if (!workspaceRoot.isInWorkspace(virtualFile)) {
- continue;
- }
- WorkspacePath workspacePath = workspaceRoot.workspacePathFor(virtualFile);
- boolean isTestSource = testConfig.isTestSource(workspacePath.relativePath());
- contentEntry.addSourceFolder(virtualFile, isTestSource);
- }
+ return GenericSourceFolderProvider.INSTANCE;
}
@Override
@@ -149,7 +129,7 @@
if (!workspaceLanguageSettings.isLanguageActive(LanguageClass.JAVASCRIPT)) {
return true;
}
- if (!PlatformUtils.isIdeaUltimate()) {
+ if (!ApplicationManager.getApplication().isUnitTestMode() && !PlatformUtils.isIdeaUltimate()) {
IssueOutput.error("IntelliJ Ultimate needed for Javascript support.").submit(context);
return false;
}
diff --git a/ijwb/src/com/google/idea/blaze/ijwb/plugin/MigrateBazelPluginDependency.java b/ijwb/src/com/google/idea/blaze/ijwb/plugin/MigrateBazelPluginDependency.java
deleted file mode 100644
index 4231e03..0000000
--- a/ijwb/src/com/google/idea/blaze/ijwb/plugin/MigrateBazelPluginDependency.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.ijwb.plugin;
-
-import com.google.idea.blaze.base.plugin.dependency.PluginDependencyHelper;
-import com.google.idea.blaze.base.settings.Blaze;
-import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
-import com.intellij.openapi.components.ApplicationComponent;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.project.ProjectManager;
-import com.intellij.openapi.project.ProjectManagerAdapter;
-
-/**
- * Temporary migration code following the IntelliJ-with-Bazel plugin ID change. We can't prevent an
- * initial, spurious error that a required plugin is missing, however this will at least prevent the
- * error on subsequent project loads.
- */
-public class MigrateBazelPluginDependency extends ApplicationComponent.Adapter {
-
- @Override
- public void initComponent() {
- ProjectManager projectManager = ProjectManager.getInstance();
- projectManager.addProjectManagerListener(
- new ProjectManagerAdapter() {
- @Override
- public void projectOpened(Project project) {
- if (Blaze.isBlazeProject(project)
- && Blaze.getBuildSystem(project) == BuildSystem.Bazel) {
- PluginDependencyHelper.removeDependencyOnOldPlugin(
- project, IjwbPluginId.BLAZE_PLUGIN_ID);
- }
- }
- });
- }
-}
diff --git a/ijwb/tests/integrationtests/com/google/idea/blaze/ijwb/javascript/JavascriptSyncTest.java b/ijwb/tests/integrationtests/com/google/idea/blaze/ijwb/javascript/JavascriptSyncTest.java
new file mode 100644
index 0000000..5174f96
--- /dev/null
+++ b/ijwb/tests/integrationtests/com/google/idea/blaze/ijwb/javascript/JavascriptSyncTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.ijwb.javascript;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.sync.BlazeSyncIntegrationTestCase;
+import com.google.idea.blaze.base.sync.BlazeSyncParams;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Sync integration tests for projects containing javascript. */
+@RunWith(JUnit4.class)
+public class JavascriptSyncTest extends BlazeSyncIntegrationTestCase {
+
+ @Test
+ public void testSimpleTestSourcesIdentified() {
+ setProjectView(
+ "directories:",
+ " common/jslayout/calendar",
+ " common/jslayout/tests",
+ "targets:",
+ " //common/jslayout/...:all",
+ "test_sources:",
+ " common/jslayout/tests",
+ "workspace_type: javascript");
+
+ VirtualFile rootFile = workspace.createDirectory(new WorkspacePath("common/jslayout/calendar"));
+
+ VirtualFile testFile =
+ workspace.createFile(new WorkspacePath("common/jslayout/tests/date_formatter_test.js"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ assertThat(getWorkspaceContentEntries()).hasSize(2);
+
+ ContentEntry sourceEntry = findContentEntry(rootFile);
+ assertThat(sourceEntry.getSourceFolders()).hasLength(1);
+
+ SourceFolder nonTestSource = findSourceFolder(sourceEntry, rootFile);
+ assertThat(nonTestSource.isTestSource()).isFalse();
+
+ ContentEntry testEntry = findContentEntry(testFile.getParent());
+ assertThat(testEntry.getSourceFolders()).hasLength(1);
+
+ SourceFolder testSource = findSourceFolder(testEntry, testFile.getParent());
+ assertThat(testSource.isTestSource()).isTrue();
+ }
+
+ @Test
+ public void testTestSourcesMissingFromDirectoriesSectionAreAdded() {
+ setProjectView(
+ "directories:",
+ " common/jslayout",
+ "targets:",
+ " //common/jslayout/...:all",
+ "test_sources:",
+ " */tests",
+ "workspace_type: javascript");
+
+ VirtualFile testDir = workspace.createDirectory(new WorkspacePath("common/jslayout/tests"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ImmutableList<ContentEntry> contentEntries = getWorkspaceContentEntries();
+ assertThat(contentEntries).hasSize(1);
+
+ SourceFolder root = findSourceFolder(contentEntries.get(0), testDir.getParent());
+ assertThat(root.isTestSource()).isFalse();
+
+ SourceFolder testRoot = findSourceFolder(contentEntries.get(0), testDir);
+ assertThat(testRoot).isNotNull();
+ assertThat(testRoot.isTestSource()).isTrue();
+ }
+
+ @Test
+ public void testTestSourceChildrenAreNotAddedAsSourceFolders() {
+ // child directories of test sources are always test sources, so they should never
+ // appear as separate SourceFolders.
+ setProjectView(
+ "directories:",
+ " common/jslayout",
+ "targets:",
+ " //common/jslayout/...:all",
+ "test_sources:",
+ " */tests/*",
+ "workspace_type: javascript");
+
+ VirtualFile rootDir = workspace.createDirectory(new WorkspacePath("common/jslayout"));
+ VirtualFile nestedTestDir =
+ workspace.createDirectory(new WorkspacePath("common/jslayout/tests/foo"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ImmutableList<ContentEntry> contentEntries = getWorkspaceContentEntries();
+ assertThat(contentEntries).hasSize(1);
+
+ SourceFolder root = findSourceFolder(contentEntries.get(0), rootDir);
+ assertThat(root.isTestSource()).isFalse();
+
+ SourceFolder child = findSourceFolder(contentEntries.get(0), nestedTestDir);
+ assertThat(child).isNull();
+
+ SourceFolder testRoot = findSourceFolder(contentEntries.get(0), nestedTestDir.getParent());
+ assertThat(testRoot).isNotNull();
+ assertThat(testRoot.isTestSource()).isTrue();
+ }
+
+ @Nullable
+ private static SourceFolder findSourceFolder(ContentEntry entry, VirtualFile file) {
+ for (SourceFolder sourceFolder : entry.getSourceFolders()) {
+ if (file.equals(sourceFolder.getFile())) {
+ return sourceFolder;
+ }
+ }
+ return null;
+ }
+}
diff --git a/intellij_platform_sdk/BUILD b/intellij_platform_sdk/BUILD
index a0aad73..2f68436 100644
--- a/intellij_platform_sdk/BUILD
+++ b/intellij_platform_sdk/BUILD
@@ -1,6 +1,11 @@
+# Copyright 2011 Google Inc. All rights reserved.
#
-# Description: IntelliJ plugin SDKs required to build the plugin jars.
-#
+# Description:
+# Defines a package group that restricts access to the JetBrains
+# plugin apis to known packages that build plugins. Only packages
+# listed here may depend on these libraries.
+
+licenses(["notice"]) # Apache2
package(default_visibility = ["//visibility:public"])
@@ -11,10 +16,19 @@
},
)
+# IntelliJ CE 2016.3.1
config_setting(
- name = "clion-latest",
+ name = "intellij-2016.3.1",
values = {
- "define": "ij_product=clion-latest",
+ "define": "ij_product=intellij-2016.3.1",
+ },
+)
+
+# IntelliJ CE 2016.2.4
+config_setting(
+ name = "intellij-162.2032.8",
+ values = {
+ "define": "ij_product=intellij-162.2032.8",
},
)
@@ -25,18 +39,67 @@
},
)
+# Android Studio 2.2.0.7
+config_setting(
+ name = "android-studio-145.1617.8",
+ values = {
+ "define": "ij_product=android-studio-145.1617.8",
+ },
+)
+
+config_setting(
+ name = "android-studio-beta",
+ values = {
+ "define": "ij_product=android-studio-beta",
+ },
+)
+
+# Android Studio 2.3.0.3
+config_setting(
+ name = "android-studio-2.3.0.3",
+ values = {
+ "define": "ij_product=android-studio-2.3.0.3",
+ },
+)
+
+config_setting(
+ name = "clion-latest",
+ values = {
+ "define": "ij_product=clion-latest",
+ },
+)
+
+# CLion 2016.2.2
+config_setting(
+ name = "clion-162.1967.7",
+ values = {
+ "define": "ij_product=clion-162.1967.7",
+ },
+)
+
+# CLion 16 (2016.2.1)
+config_setting(
+ name = "clion-162.1628.20",
+ values = {
+ "define": "ij_product=clion-162.1628.20",
+ },
+)
+
+load(":build_defs.bzl", "select_from_plugin_api_directory", "select_for_ide")
+
+# The purpose of this rule is to hide the versioning
+# complexity from users of this api.
+# There will be additional versions added in the future
java_library(
name = "plugin_api_internal",
- visibility = ["//visibility:private"],
- exports = select({
- ":intellij-latest": ["@intellij_latest//:plugin_api"],
- ":clion-latest": ["@clion_latest//:plugin_api"],
- ":android-studio-latest": [
- "@android_studio_latest//:plugin_api",
- "@android_studio_latest//:android_plugin",
+ exports = select_from_plugin_api_directory(
+ android_studio = [
+ ":sdk",
+ ":android_plugin",
],
- "//conditions:default": ["@intellij_latest//:plugin_api"],
- }),
+ clion = [":sdk"],
+ intellij = [":sdk"],
+ ),
)
# The outward facing plugin api
@@ -59,57 +122,73 @@
],
)
+# For the grammar-kit binary only (a build time tool). It needs the IJ API for runtime.
+# The clion.jar doesn't work (missing MockEditorFactory, MockProjectEx), so just use
+# an idea.jar. It doesn't affect the generated code.
java_library(
- name = "junit",
- neverlink = 1,
- exports = select({
- ":intellij-latest": ["@intellij_latest//:junit"],
- ":android-studio-latest": ["@android_studio_latest//:junit"],
- ":clion-latest": [],
- "//conditions:default": ["@intellij_latest//:junit"],
- }),
+ name = "plugin_api_for_grammar_kit",
+ visibility = ["//third_party/java/jetbrains/grammar_kit:__pkg__"],
+ exports = ["//intellij_platform_sdk/IC_162_2032_8:sdk"],
)
-# The dev kit is only for IntelliJ since you only develop plugins in Java.
+# Used to support IntelliJ plugin development in our plugin
java_library(
name = "devkit",
neverlink = 1,
- exports = select({
- ":intellij-latest": ["@intellij_latest//:devkit"],
- ":android-studio-latest": [],
- ":clion-latest": [],
- "//conditions:default": ["@intellij_latest//:devkit"],
- }),
+ exports = select_from_plugin_api_directory(
+ android_studio = [],
+ clion = [],
+ intellij = [":devkit"],
+ ),
+)
+
+# IntelliJ Mercurial plugin
+java_library(
+ name = "hg4idea",
+ neverlink = 1,
+ exports = select_from_plugin_api_directory(
+ android_studio = [":hg4idea"],
+ clion = [":hg4idea"],
+ intellij = [":hg4idea"],
+ ),
+)
+
+# IntelliJ JUnit plugin
+java_library(
+ name = "junit",
+ neverlink = 1,
+ exports = select_from_plugin_api_directory(
+ android_studio = [":junit"],
+ clion = [],
+ intellij = [":junit"],
+ ),
)
# Bundled plugins required by integration tests
java_library(
name = "bundled_plugins",
testonly = 1,
- runtime_deps = select({
- ":intellij-latest": ["@intellij_latest//:bundled_plugins"],
- ":clion-latest": ["@clion_latest//:bundled_plugins"],
- ":android-studio-latest": ["@android_studio_latest//:bundled_plugins"],
- "//conditions:default": ["@intellij_latest//:bundled_plugins"],
- }),
+ runtime_deps = select_from_plugin_api_directory(
+ android_studio = [":bundled_plugins"],
+ clion = [":bundled_plugins"],
+ intellij = [":bundled_plugins"],
+ ),
)
filegroup(
name = "application_info_jar",
- srcs = select({
- ":intellij-latest": ["@intellij_latest//:application_info_jar"],
- ":clion-latest": ["@clion_latest//:application_info_jar"],
- ":android-studio-latest": ["@android_studio_latest//:application_info_jar"],
- "//conditions:default": ["@intellij_latest//:application_info_jar"],
- }),
+ srcs = select_from_plugin_api_directory(
+ android_studio = [":application_info_jar"],
+ clion = [":application_info_jar"],
+ intellij = [":application_info_jar"],
+ ),
)
filegroup(
name = "application_info_name",
- srcs = select({
- ":intellij-latest": ["intellij_application_info_name.txt"],
- ":clion-latest": ["clion_application_info_name.txt"],
- ":android-studio-latest": ["android_studio_application_info_name.txt"],
- "//conditions:default": ["intellij_application_info_name.txt"],
- }),
+ srcs = select_for_ide(
+ android_studio = ["android_studio_application_info_name.txt"],
+ clion = ["clion_application_info_name.txt"],
+ intellij = ["intellij_application_info_name.txt"],
+ ),
)
diff --git a/intellij_platform_sdk/BUILD.android_studio b/intellij_platform_sdk/BUILD.android_studio
index 77c0f55..f768640 100644
--- a/intellij_platform_sdk/BUILD.android_studio
+++ b/intellij_platform_sdk/BUILD.android_studio
@@ -5,7 +5,7 @@
package(default_visibility = ["//visibility:public"])
java_import(
- name = "plugin_api",
+ name = "sdk",
jars = glob([
"android-studio/lib/*.jar",
]),
diff --git a/intellij_platform_sdk/BUILD.clion b/intellij_platform_sdk/BUILD.clion
index c32194f..e395d09 100644
--- a/intellij_platform_sdk/BUILD.clion
+++ b/intellij_platform_sdk/BUILD.clion
@@ -5,11 +5,16 @@
package(default_visibility = ["//visibility:public"])
java_import(
- name = "plugin_api",
+ name = "sdk",
jars = glob(["clion-*/lib/*.jar"]),
tags = ["intellij-provided-by-sdk"],
)
+java_import(
+ name = "hg4idea",
+ jars = glob(["clion-*/plugins/hg4idea/lib/hg4idea.jar"]),
+)
+
# The plugins required by CLwB. Presumably there will be some, when we write
# some integration tests.
java_import(
@@ -21,4 +26,4 @@
filegroup(
name = "application_info_jar",
srcs = glob(["clion-*/lib/clion.jar"]),
-)
\ No newline at end of file
+)
diff --git a/intellij_platform_sdk/BUILD.idea b/intellij_platform_sdk/BUILD.idea
index 22082d2..32a92ad 100644
--- a/intellij_platform_sdk/BUILD.idea
+++ b/intellij_platform_sdk/BUILD.idea
@@ -5,19 +5,24 @@
package(default_visibility = ["//visibility:public"])
java_import(
- name = "plugin_api",
- jars = glob(["idea-IC-*/lib/*.jar"]),
+ name = "sdk",
+ jars = glob(["lib/*.jar"]),
tags = ["intellij-provided-by-sdk"],
)
java_import(
name = "devkit",
- jars = glob(["idea-IC-*/plugins/devkit/lib/devkit.jar"]),
+ jars = glob(["plugins/devkit/lib/devkit.jar"]),
+)
+
+java_import(
+ name = "hg4idea",
+ jars = ["plugins/hg4idea/lib/hg4idea.jar"],
)
java_import(
name = "junit",
- jars = glob(["idea-IC-*/plugins/junit/lib/*.jar"]),
+ jars = glob(["plugins/junit/lib/*.jar"]),
)
# The plugins required by IJwB. We need to include them
@@ -25,15 +30,15 @@
java_import(
name = "bundled_plugins",
jars = glob([
- "idea-IC-*/plugins/devkit/lib/*.jar",
- "idea-IC-*/plugins/java-i18n/lib/*.jar",
- "idea-IC-*/plugins/junit/lib/*.jar",
- "idea-IC-*/plugins/properties/lib/*.jar",
+ "plugins/devkit/lib/*.jar",
+ "plugins/java-i18n/lib/*.jar",
+ "plugins/junit/lib/*.jar",
+ "plugins/properties/lib/*.jar",
]),
tags = ["intellij-provided-by-sdk"],
)
filegroup(
name = "application_info_jar",
- srcs = glob(["idea-IC-*/lib/resources.jar"]),
+ srcs = glob(["lib/resources.jar"]),
)
diff --git a/intellij_platform_sdk/build_defs.bzl b/intellij_platform_sdk/build_defs.bzl
new file mode 100644
index 0000000..57dbe76
--- /dev/null
+++ b/intellij_platform_sdk/build_defs.bzl
@@ -0,0 +1,169 @@
+"""Convenience methods for plugin_api."""
+
+# The current indirect ij_product mapping (eg. "intellij-latest")
+INDIRECT_IJ_PRODUCTS = {
+ "intellij-latest": "intellij-162.2032.8",
+ "android-studio-latest": "android-studio-145.1617.8",
+ "android-studio-beta": "android-studio-2.3.0.3",
+ "clion-latest": "clion-162.1967.7",
+}
+
+DIRECT_IJ_PRODUCTS = {
+ "intellij-2016.3.1": struct(
+ ide="intellij",
+ directory="intellij_ce_2016_3_1",
+ ),
+ "intellij-162.2032.8": struct(
+ ide="intellij",
+ directory="IC_162_2032_8",
+ ),
+ "android-studio-145.1617.8": struct(
+ ide="android-studio",
+ directory="AI_145_1617_8",
+ ),
+ "android-studio-2.3.0.3": struct(
+ ide="android-studio",
+ directory="android_studio_2_3_0_3",
+ ),
+ "clion-162.1628.20": struct(
+ ide="clion",
+ directory="CL_162_1628_20",
+ ),
+ "clion-162.1967.7": struct(
+ ide="clion",
+ directory="CL_162_1967_7",
+ ),
+}
+
+# BUILD_VARS for each IDE corresponding to indirect ij_products, eg. "intellij-latest"
+
+
+
+
+
+
+def select_for_plugin_api(params):
+ """Selects for a plugin_api.
+
+ Args:
+ params: A dict with ij_product -> value.
+ You may only include direct ij_products here,
+ not indirects (eg. intellij-latest).
+ Returns:
+ A select statement on all plugin_apis. Unless you include a "default"
+ clause any other matched plugin_api will return "None".
+
+ A build without an ij_product is considered equivalent to building with
+ "intellij-latest".
+
+ Example:
+ java_library(
+ name = "foo",
+ srcs = select_for_plugin_api({
+ "intellij-2016.3.1": [...my intellij 2016.3 sources ....],
+ "intellij-2012.2.4": [...my intellij 2016.2 sources ...],
+ }),
+ )
+ """
+ for indirect_ij_product in INDIRECT_IJ_PRODUCTS:
+ if indirect_ij_product in params:
+ error_message = "".join([
+ "Do not select on indirect ij_product %s. " % indirect_ij_product,
+ "Instead, select on an exact ij_product."])
+ fail(error_message)
+
+ # To make the select work with "intellij-latest" and friends,
+ # we find if the user is currently selecting on what intellij-latest
+ # is resolving to, and copy that. Example:
+ #
+ # {"intellij-2016.3.1": "stuff"} ->
+ # {"intellij-2016.3.1": "stuff", "intellij-latest": "stuff"}
+ params = dict(**params)
+ for indirect_ij_product, resolved_plugin_api in INDIRECT_IJ_PRODUCTS.items():
+ if resolved_plugin_api in params:
+ params[indirect_ij_product] = params[resolved_plugin_api]
+
+ if "default" not in params:
+ # If "intellij-latest" is supported, we set "default" to that
+ # This supports building with an empty command line. Example:
+ #
+ # {"intellij-2016.3.1": "stuff", "intellij-latest": "stuff"} ->
+ # {"intellij-2016.3.1": "stuff", "intellij-latest": "stuff", "default": "stuff"}
+ if "intellij-latest" in params:
+ params["default"] = params["intellij-latest"]
+
+ # Add the other indirect ij_products returning None rather than default
+ for ij_product in INDIRECT_IJ_PRODUCTS:
+ if ij_product not in params:
+ params[ij_product] = None
+ for ij_product in DIRECT_IJ_PRODUCTS:
+ if ij_product not in params:
+ params[ij_product] = None
+
+ # Map to the actual targets
+ # This makes it more convenient so the user doesn't have to
+ # fully specify the path to the plugin_apis
+ select_params = dict()
+ for ij_product, value in params.items():
+ if ij_product == "default":
+ select_params["//conditions:default"] = value
+ else:
+ select_params["//intellij_platform_sdk:" + ij_product] = value
+ return select(select_params)
+
+def select_for_ide(intellij=None, android_studio=None, clion=None, default=None):
+ """Selects for the supported IDEs.
+
+ Args:
+ intellij: Files to use for IntelliJ. If None, will use default.
+ android_studio: Files to use for Android Studio. If None will use default.
+ clion: Files to use for CLion. If None will use default.
+ default: Files to use for any IDEs not passed.
+ Returns:
+ A select statement on all plugin_apis, sorted into IDEs.
+
+ Example:
+ java_library(
+ name = "foo",
+ srcs = select_for_ide(
+ clion = [":cpp_only_sources"],
+ default = [":java_only_sources"],
+ ),
+ )
+ """
+ intellij = intellij or default
+ android_studio = android_studio or default
+ clion = clion or default
+ default = default or intellij
+
+ ide_to_value = {
+ "intellij" : intellij,
+ "android-studio": android_studio,
+ "clion": clion,
+ }
+
+ # Map (direct ij_product) -> corresponding ide value
+ params = dict()
+ for ij_product, value in DIRECT_IJ_PRODUCTS.items():
+ params[ij_product] = ide_to_value[value.ide]
+ params["default"] = default
+
+ return select_for_plugin_api(params)
+
+def _plugin_api_directory(value):
+ return "@" + value.directory + "//"
+
+def select_from_plugin_api_directory(intellij, android_studio, clion):
+ """Internal convenience method to generate select statement from the IDE's plugin_api directories."""
+
+ ide_to_value = {
+ "intellij" : intellij,
+ "android-studio": android_studio,
+ "clion": clion,
+ }
+
+ # Map (direct ij_product) -> corresponding product directory
+ params = dict()
+ for ij_product, value in DIRECT_IJ_PRODUCTS.items():
+ params[ij_product] = [_plugin_api_directory(value) + item for item in ide_to_value[value.ide]]
+ return select_for_plugin_api(params)
diff --git a/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncAugmenter.java b/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncAugmenter.java
index 5b5b23c..7c4aa21 100644
--- a/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncAugmenter.java
+++ b/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncAugmenter.java
@@ -16,6 +16,7 @@
package com.google.idea.blaze.java.sync;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
+import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
import com.intellij.openapi.extensions.ExtensionPointName;
@@ -34,6 +35,7 @@
*/
void addJarsForSourceTarget(
WorkspaceLanguageSettings workspaceLanguageSettings,
+ ProjectViewSet projectViewSet,
TargetIdeInfo target,
Collection<BlazeJarLibrary> jars,
Collection<BlazeJarLibrary> genJars);
diff --git a/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java b/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java
index dfca78e..65151a6 100644
--- a/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java
+++ b/java/src/com/google/idea/blaze/java/sync/BlazeJavaSyncPlugin.java
@@ -36,6 +36,7 @@
import com.google.idea.blaze.base.scope.scopes.TimingScope;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.base.sync.libraries.LibrarySource;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
@@ -52,15 +53,14 @@
import com.google.idea.blaze.java.sync.model.BlazeJarLibrary;
import com.google.idea.blaze.java.sync.model.BlazeJavaImportResult;
import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
+import com.google.idea.blaze.java.sync.projectstructure.JavaSourceFolderProvider;
import com.google.idea.blaze.java.sync.projectstructure.Jdks;
-import com.google.idea.blaze.java.sync.projectstructure.SourceFolderEditor;
import com.google.idea.blaze.java.sync.workingset.JavaWorkingSet;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
import com.intellij.pom.java.LanguageLevel;
@@ -177,23 +177,13 @@
updateJdk(project, context, projectViewSet, blazeProjectData);
}
+ @Nullable
@Override
- public void updateContentEntries(
- Project project,
- BlazeContext context,
- WorkspaceRoot workspaceRoot,
- ProjectViewSet projectViewSet,
- BlazeProjectData blazeProjectData,
- Collection<ContentEntry> contentEntries) {
- if (!blazeProjectData.workspaceLanguageSettings.isLanguageActive(LanguageClass.JAVA)) {
- return;
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ if (!projectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.JAVA)) {
+ return null;
}
- BlazeJavaSyncData syncData = blazeProjectData.syncState.get(BlazeJavaSyncData.class);
- if (syncData == null) {
- return;
- }
-
- SourceFolderEditor.modifyContentEntries(syncData.importResult, contentEntries);
+ return new JavaSourceFolderProvider(projectData.syncState.get(BlazeJavaSyncData.class));
}
private static void updateJdk(
diff --git a/java/src/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporter.java b/java/src/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporter.java
index 80d7d83..2e8df7f 100644
--- a/java/src/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporter.java
+++ b/java/src/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporter.java
@@ -40,7 +40,6 @@
import com.google.idea.blaze.base.scope.output.PrintOutput;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.sync.projectview.ImportRoots;
-import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.java.sync.BlazeJavaSyncAugmenter;
@@ -67,7 +66,6 @@
private final WorkspaceRoot workspaceRoot;
private final ImportRoots importRoots;
private final TargetMap targetMap;
- private final SourceTestConfig sourceTestConfig;
private final JdepsMap jdepsMap;
@Nullable private final JavaWorkingSet workingSet;
private final ArtifactLocationDecoder artifactLocationDecoder;
@@ -75,6 +73,7 @@
private final JavaSourceFilter sourceFilter;
private final WorkspaceLanguageSettings workspaceLanguageSettings;
private final List<BlazeJavaSyncAugmenter> augmenters;
+ private final ProjectViewSet projectViewSet;
public BlazeJavaWorkspaceImporter(
Project project,
@@ -97,9 +96,9 @@
this.jdepsMap = jdepsMap;
this.workingSet = workingSet;
this.artifactLocationDecoder = artifactLocationDecoder;
- this.sourceTestConfig = new SourceTestConfig(projectViewSet);
this.workspaceLanguageSettings = workspaceLanguageSettings;
this.augmenters = Arrays.asList(BlazeJavaSyncAugmenter.EP_NAME.getExtensions());
+ this.projectViewSet = projectViewSet;
}
public BlazeJavaImportResult importWorkspace(BlazeContext context) {
@@ -114,7 +113,6 @@
project,
context,
workspaceRoot,
- sourceTestConfig,
artifactLocationDecoder,
importRoots.rootDirectories(),
workspaceBuilder.sourceArtifacts,
@@ -389,6 +387,7 @@
for (BlazeJavaSyncAugmenter augmenter : augmenters) {
augmenter.addJarsForSourceTarget(
workspaceLanguageSettings,
+ projectViewSet,
target,
workspaceBuilder.outputJarsFromSourceTargets.get(targetKey),
workspaceBuilder.generatedJarsFromSourceTargets);
diff --git a/java/src/com/google/idea/blaze/java/sync/model/BlazeSourceDirectory.java b/java/src/com/google/idea/blaze/java/sync/model/BlazeSourceDirectory.java
index f39a2e0..dd7db2b 100644
--- a/java/src/com/google/idea/blaze/java/sync/model/BlazeSourceDirectory.java
+++ b/java/src/com/google/idea/blaze/java/sync/model/BlazeSourceDirectory.java
@@ -20,46 +20,38 @@
import java.io.Serializable;
import java.util.Comparator;
import javax.annotation.concurrent.Immutable;
-import org.jetbrains.annotations.NotNull;
/** A source directory. */
@Immutable
public final class BlazeSourceDirectory implements Serializable {
- private static final long serialVersionUID = 2L;
+ private static final long serialVersionUID = 3L;
public static final Comparator<BlazeSourceDirectory> COMPARATOR =
(o1, o2) ->
String.CASE_INSENSITIVE_ORDER.compare(
o1.getDirectory().getPath(), o2.getDirectory().getPath());
- @NotNull private final File directory;
- private final boolean isTest;
+ private final File directory;
private final boolean isGenerated;
private final boolean isResource;
- @NotNull private final String packagePrefix;
+ private final String packagePrefix;
/** Bulider for source directory */
public static class Builder {
- @NotNull private final File directory;
- @NotNull private String packagePrefix = "";
- private boolean isTest;
+ private final File directory;
+ private String packagePrefix = "";
private boolean isResource;
private boolean isGenerated;
- private Builder(@NotNull File directory) {
+ private Builder(File directory) {
this.directory = directory;
}
- public Builder setPackagePrefix(@NotNull String packagePrefix) {
+ public Builder setPackagePrefix(String packagePrefix) {
this.packagePrefix = packagePrefix;
return this;
}
- public Builder setTest(boolean isTest) {
- this.isTest = isTest;
- return this;
- }
-
public Builder setResource(boolean isResource) {
this.isResource = isResource;
return this;
@@ -71,44 +63,31 @@
}
public BlazeSourceDirectory build() {
- return new BlazeSourceDirectory(directory, isTest, isResource, isGenerated, packagePrefix);
+ return new BlazeSourceDirectory(directory, isResource, isGenerated, packagePrefix);
}
}
- @NotNull
- public static Builder builder(@NotNull String directory) {
+ public static Builder builder(String directory) {
return new Builder(new File(directory));
}
- @NotNull
- public static Builder builder(@NotNull File directory) {
+ public static Builder builder(File directory) {
return new Builder(directory);
}
private BlazeSourceDirectory(
- @NotNull File directory,
- boolean isTest,
- boolean isResource,
- boolean isGenerated,
- @NotNull String packagePrefix) {
+ File directory, boolean isResource, boolean isGenerated, String packagePrefix) {
this.directory = directory;
- this.isTest = isTest;
this.isResource = isResource;
this.isGenerated = isGenerated;
this.packagePrefix = packagePrefix;
}
/** Returns the full path name of the root of a source directory. */
- @NotNull
public File getDirectory() {
return directory;
}
- /** Returns {@code true} if the directory contains test sources. */
- public boolean getIsTest() {
- return isTest;
- }
-
/** Returns {@code true} if the directory contains resources. */
public boolean getIsResource() {
return isResource;
@@ -123,14 +102,13 @@
* Returns the package prefix for the directory. If the directory is a source root, such as a
* "src" directory, then this returns an empty string.
*/
- @NotNull
public String getPackagePrefix() {
return packagePrefix;
}
@Override
public int hashCode() {
- return Objects.hashCode(directory, isTest, isResource, packagePrefix, isGenerated);
+ return Objects.hashCode(directory, isResource, packagePrefix, isGenerated);
}
@Override
@@ -145,7 +123,6 @@
return directory.equals(that.directory)
&& packagePrefix.equals(that.packagePrefix)
&& isResource == that.isResource
- && isTest == that.isTest
&& isGenerated == that.isGenerated;
}
@@ -155,9 +132,6 @@
+ " directory: "
+ directory
+ "\n"
- + " isTest: "
- + isTest
- + "\n"
+ " isGenerated: "
+ isGenerated
+ "\n"
diff --git a/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java b/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java
new file mode 100644
index 0000000..5855655
--- /dev/null
+++ b/java/src/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProvider.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.java.sync.projectstructure;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
+import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
+import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
+import com.google.idea.blaze.java.sync.model.BlazeSourceDirectory;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
+import com.intellij.util.io.URLUtil;
+import java.io.File;
+import javax.annotation.Nullable;
+import org.jetbrains.jps.model.JpsElement;
+import org.jetbrains.jps.model.java.JavaResourceRootType;
+import org.jetbrains.jps.model.java.JavaSourceRootProperties;
+import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
+
+/** Edits source folders in IntelliJ content entries */
+public class JavaSourceFolderProvider implements SourceFolderProvider {
+
+ private final ImmutableMap<File, BlazeContentEntry> blazeContentEntries;
+
+ public JavaSourceFolderProvider(@Nullable BlazeJavaSyncData syncData) {
+ this.blazeContentEntries = blazeContentEntries(syncData);
+ }
+
+ private static ImmutableMap<File, BlazeContentEntry> blazeContentEntries(
+ @Nullable BlazeJavaSyncData syncData) {
+ if (syncData == null) {
+ return ImmutableMap.of();
+ }
+ ImmutableMap.Builder<File, BlazeContentEntry> builder = ImmutableMap.builder();
+ for (BlazeContentEntry blazeContentEntry : syncData.importResult.contentEntries) {
+ builder.put(blazeContentEntry.contentRoot, blazeContentEntry);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public ImmutableMap<VirtualFile, SourceFolder> initializeSourceFolders(
+ ContentEntry contentEntry) {
+ ImmutableMap.Builder<VirtualFile, SourceFolder> output = ImmutableMap.builder();
+ VirtualFile virtualFile = contentEntry.getFile();
+ if (virtualFile == null) {
+ return output.build();
+ }
+
+ File contentRoot = new File(virtualFile.getPath());
+ BlazeContentEntry javaContentEntry = blazeContentEntries.get(contentRoot);
+ if (javaContentEntry != null) {
+ for (BlazeSourceDirectory sourceDirectory : javaContentEntry.sources) {
+ SourceFolder sourceFolder = addSourceFolderToContentEntry(contentEntry, sourceDirectory);
+ output.put(sourceFolder.getFile(), sourceFolder);
+ }
+ }
+ return output.build();
+ }
+
+ @Override
+ public SourceFolder setSourceFolderForLocation(
+ ContentEntry contentEntry,
+ SourceFolder parentFolder,
+ VirtualFile file,
+ boolean isTestSource) {
+ SourceFolder sourceFolder;
+ if (isResource(parentFolder)) {
+ JavaResourceRootType resourceRootType =
+ isTestSource ? JavaResourceRootType.TEST_RESOURCE : JavaResourceRootType.RESOURCE;
+ sourceFolder = contentEntry.addSourceFolder(pathToUrl(file.getPath()), resourceRootType);
+ } else {
+ sourceFolder = contentEntry.addSourceFolder(pathToUrl(file.getPath()), isTestSource);
+ }
+ sourceFolder.setPackagePrefix(derivePackagePrefix(file, parentFolder));
+ JpsModuleSourceRoot sourceRoot = sourceFolder.getJpsElement();
+ JpsElement properties = sourceRoot.getProperties();
+ if (properties instanceof JavaSourceRootProperties) {
+ ((JavaSourceRootProperties) properties).setForGeneratedSources(isGenerated(parentFolder));
+ }
+ return sourceFolder;
+ }
+
+ private static String derivePackagePrefix(VirtualFile file, SourceFolder parentFolder) {
+ String parentPackagePrefix = parentFolder.getPackagePrefix();
+ String relativePath = VfsUtilCore.getRelativePath(file, parentFolder.getFile(), '.');
+ if (Strings.isNullOrEmpty(relativePath)) {
+ return parentPackagePrefix;
+ }
+ return parentPackagePrefix + "." + relativePath;
+ }
+
+ @VisibleForTesting
+ static boolean isResource(SourceFolder folder) {
+ return folder.getRootType() instanceof JavaResourceRootType;
+ }
+
+ @VisibleForTesting
+ static boolean isGenerated(SourceFolder folder) {
+ JpsElement properties = folder.getJpsElement().getProperties();
+ return properties instanceof JavaSourceRootProperties
+ && ((JavaSourceRootProperties) properties).isForGeneratedSources();
+ }
+
+ private static SourceFolder addSourceFolderToContentEntry(
+ ContentEntry contentEntry, BlazeSourceDirectory sourceDirectory) {
+ File sourceDir = sourceDirectory.getDirectory();
+
+ // Create the source folder
+ SourceFolder sourceFolder;
+ if (sourceDirectory.getIsResource()) {
+ sourceFolder =
+ contentEntry.addSourceFolder(
+ pathToUrl(sourceDir.getPath()), JavaResourceRootType.RESOURCE);
+ } else {
+ sourceFolder = contentEntry.addSourceFolder(pathToUrl(sourceDir.getPath()), false);
+ }
+ JpsModuleSourceRoot sourceRoot = sourceFolder.getJpsElement();
+ JpsElement properties = sourceRoot.getProperties();
+ if (properties instanceof JavaSourceRootProperties) {
+ JavaSourceRootProperties rootProperties = (JavaSourceRootProperties) properties;
+ if (sourceDirectory.getIsGenerated()) {
+ rootProperties.setForGeneratedSources(true);
+ }
+ }
+ String packagePrefix = sourceDirectory.getPackagePrefix();
+ if (!Strings.isNullOrEmpty(packagePrefix)) {
+ sourceFolder.setPackagePrefix(packagePrefix);
+ }
+ return sourceFolder;
+ }
+
+ private static String pathToUrl(String filePath) {
+ filePath = FileUtil.toSystemIndependentName(filePath);
+ if (filePath.endsWith(".srcjar") || filePath.endsWith(".jar")) {
+ return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath + URLUtil.JAR_SEPARATOR;
+ } else if (filePath.contains("src.jar!")) {
+ return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath;
+ } else {
+ return VirtualFileManager.constructUrl(defaultFileSystem().getProtocol(), filePath);
+ }
+ }
+
+ private static VirtualFileSystem defaultFileSystem() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return TempFileSystem.getInstance();
+ }
+ return LocalFileSystem.getInstance();
+ }
+}
diff --git a/java/src/com/google/idea/blaze/java/sync/projectstructure/SourceFolderEditor.java b/java/src/com/google/idea/blaze/java/sync/projectstructure/SourceFolderEditor.java
deleted file mode 100644
index 0d88c4e..0000000
--- a/java/src/com/google/idea/blaze/java/sync/projectstructure/SourceFolderEditor.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2016 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.idea.blaze.java.sync.projectstructure;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
-import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
-import com.google.idea.blaze.java.sync.model.BlazeJavaImportResult;
-import com.google.idea.blaze.java.sync.model.BlazeSourceDirectory;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.roots.ContentEntry;
-import com.intellij.openapi.roots.SourceFolder;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.VfsUtilCore;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.io.URLUtil;
-import java.io.File;
-import java.util.Collection;
-import java.util.Map;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.jps.model.JpsElement;
-import org.jetbrains.jps.model.java.JavaResourceRootType;
-import org.jetbrains.jps.model.java.JavaSourceRootProperties;
-import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
-
-/** Edits source folders in IntelliJ content entries */
-public class SourceFolderEditor {
- private static final Logger LOG = Logger.getInstance(SourceFolderEditor.class);
-
- public static void modifyContentEntries(
- BlazeJavaImportResult importResult, Collection<ContentEntry> contentEntries) {
-
- Map<File, BlazeContentEntry> contentEntryMap = Maps.newHashMap();
- for (BlazeContentEntry contentEntry : importResult.contentEntries) {
- contentEntryMap.put(contentEntry.contentRoot, contentEntry);
- }
-
- for (ContentEntry contentEntry : contentEntries) {
- VirtualFile virtualFile = contentEntry.getFile();
- if (virtualFile == null) {
- continue;
- }
-
- File contentRoot = new File(virtualFile.getPath());
- BlazeContentEntry javaContentEntry = contentEntryMap.get(contentRoot);
- if (javaContentEntry != null) {
- for (BlazeSourceDirectory sourceDirectory : javaContentEntry.sources) {
- addSourceFolderToContentEntry(contentEntry, sourceDirectory);
- }
- }
- }
- }
-
- private static void addSourceFolderToContentEntry(
- ContentEntry contentEntry, BlazeSourceDirectory sourceDirectory) {
- File sourceDir = sourceDirectory.getDirectory();
-
- // Create the source folder
- SourceFolder sourceFolder;
- if (sourceDirectory.getIsResource()) {
- JavaResourceRootType resourceRootType =
- sourceDirectory.getIsTest()
- ? JavaResourceRootType.TEST_RESOURCE
- : JavaResourceRootType.RESOURCE;
- sourceFolder = contentEntry.addSourceFolder(pathToUrl(sourceDir.getPath()), resourceRootType);
- } else {
- sourceFolder =
- contentEntry.addSourceFolder(pathToUrl(sourceDir.getPath()), sourceDirectory.getIsTest());
- }
- JpsModuleSourceRoot sourceRoot = sourceFolder.getJpsElement();
- JpsElement properties = sourceRoot.getProperties();
- if (properties instanceof JavaSourceRootProperties) {
- JavaSourceRootProperties rootProperties = (JavaSourceRootProperties) properties;
- if (sourceDirectory.getIsGenerated()) {
- rootProperties.setForGeneratedSources(true);
- }
- String packagePrefix = sourceDirectory.getPackagePrefix();
- if (!Strings.isNullOrEmpty(packagePrefix)) {
- rootProperties.setPackagePrefix(packagePrefix);
- }
- }
- }
-
- @NotNull
- private static String pathToUrl(@NotNull String filePath) {
- filePath = FileUtil.toSystemIndependentName(filePath);
- if (filePath.endsWith(".srcjar") || filePath.endsWith(".jar")) {
- return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath + URLUtil.JAR_SEPARATOR;
- } else if (filePath.contains("src.jar!")) {
- return URLUtil.JAR_PROTOCOL + URLUtil.SCHEME_SEPARATOR + filePath;
- } else {
- return VfsUtilCore.pathToUrl(filePath);
- }
- }
-}
diff --git a/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java b/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java
index 805dc21..133625e 100644
--- a/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java
+++ b/java/src/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculator.java
@@ -41,7 +41,6 @@
import com.google.idea.blaze.base.scope.Scope;
import com.google.idea.blaze.base.scope.output.IssueOutput;
import com.google.idea.blaze.base.scope.scopes.TimingScope;
-import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.util.PackagePrefixCalculator;
import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
@@ -81,7 +80,6 @@
Project project,
BlazeContext context,
WorkspaceRoot workspaceRoot,
- SourceTestConfig sourceTestConfig,
ArtifactLocationDecoder artifactLocationDecoder,
Collection<WorkspacePath> rootDirectories,
Collection<SourceArtifact> sources,
@@ -127,7 +125,6 @@
context,
workspaceRoot,
artifactLocationDecoder,
- sourceTestConfig,
workspacePath,
sourcesUnderDirectoryRoot.get(workspacePath),
javaPackageReaders);
@@ -196,7 +193,6 @@
BlazeContext context,
WorkspaceRoot workspaceRoot,
ArtifactLocationDecoder artifactLocationDecoder,
- SourceTestConfig sourceTestConfig,
WorkspacePath directoryRoot,
Collection<SourceArtifact> sourceArtifacts,
Collection<JavaPackageReader> javaPackageReaders) {
@@ -217,7 +213,6 @@
workspaceRoot,
artifactLocationDecoder,
directoryRoot,
- sourceTestConfig,
javaArtifacts,
javaPackageReaders,
result);
@@ -232,7 +227,6 @@
WorkspaceRoot workspaceRoot,
ArtifactLocationDecoder artifactLocationDecoder,
WorkspacePath directoryRoot,
- SourceTestConfig sourceTestConfig,
Collection<SourceArtifact> javaArtifacts,
Collection<JavaPackageReader> javaPackageReaders,
Collection<BlazeSourceDirectory> result) {
@@ -368,7 +362,6 @@
result.add(
BlazeSourceDirectory.builder(workspaceRoot.fileForPath(sourceRoot.workspacePath))
.setPackagePrefix(sourceRoot.packagePrefix)
- .setTest(sourceTestConfig.isTestSource(sourceRoot.workspacePath.relativePath()))
.setGenerated(false)
.build());
}
diff --git a/java/src/com/google/idea/blaze/java/syncstatus/SyncStatusHelper.java b/java/src/com/google/idea/blaze/java/syncstatus/SyncStatusHelper.java
index 352553b..ff6365c 100644
--- a/java/src/com/google/idea/blaze/java/syncstatus/SyncStatusHelper.java
+++ b/java/src/com/google/idea/blaze/java/syncstatus/SyncStatusHelper.java
@@ -20,6 +20,7 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.settings.BlazeImportSettings;
+import com.google.idea.blaze.base.sync.BlazeSyncParams.SyncMode;
import com.google.idea.blaze.base.sync.SyncListener;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
@@ -85,6 +86,7 @@
BlazeImportSettings importSettings,
ProjectViewSet projectViewSet,
BlazeProjectData blazeProjectData,
+ SyncMode syncMode,
SyncResult syncResult) {
getInstance(project).syncedJavaFiles = null;
}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java
index 074cd27..a87565e 100644
--- a/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/sync/JavaSyncTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.ideinfo.JavaIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetIdeInfo;
import com.google.idea.blaze.base.ideinfo.TargetMap;
@@ -31,7 +32,11 @@
import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
import com.google.idea.blaze.java.sync.model.BlazeSourceDirectory;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.vfs.VirtualFile;
import java.util.List;
+import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -42,12 +47,7 @@
@Test
public void testJavaClassesPresentInClassPath() throws Exception {
- setProjectView(
- "directories:",
- " java/com/google",
- "targets:",
- " //java/com/google:lib",
- "workspace_type: java");
+ setProjectView("directories:", " java/com/google", "targets:", " //java/com/google:lib");
workspace.createFile(
new WorkspacePath("java/com/google/ClassWithUniqueName1.java"),
@@ -105,12 +105,7 @@
@Test
public void testSimpleSync() throws Exception {
- setProjectView(
- "directories:",
- " java/com/google",
- "targets:",
- " //java/com/google:lib",
- "workspace_type: java");
+ setProjectView("directories:", " java/com/google", "targets:", " //java/com/google:lib");
workspace.createFile(
new WorkspacePath("java/com/google/Source.java"),
@@ -149,4 +144,195 @@
assertThat(blazeProjectData.workspaceLanguageSettings.getWorkspaceType())
.isEqualTo(WorkspaceType.JAVA);
}
+
+ @Test
+ public void testSimpleTestSourcesIdentified() {
+ setProjectView(
+ "directories:",
+ " java/com/google",
+ " javatests/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "test_sources:",
+ " javatests/*");
+
+ VirtualFile javaRoot = workspace.createDirectory(new WorkspacePath("java/com/google"));
+ VirtualFile javatestsRoot =
+ workspace.createDirectory(new WorkspacePath("javatests/com/google"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ImmutableList<ContentEntry> contentEntries = getWorkspaceContentEntries();
+ assertThat(contentEntries).hasSize(2);
+
+ assertThat(findContentEntry(javaRoot)).isNotNull();
+ assertThat(findContentEntry(javaRoot).getSourceFolders()).hasLength(1);
+ assertThat(findContentEntry(javaRoot).getSourceFolders()[0].isTestSource()).isFalse();
+
+ assertThat(findContentEntry(javatestsRoot)).isNotNull();
+ assertThat(findContentEntry(javatestsRoot).getSourceFolders()).hasLength(1);
+ assertThat(findContentEntry(javatestsRoot).getSourceFolders()[0].isTestSource()).isTrue();
+ }
+
+ @Test
+ public void testNestedTestSourcesAreAdded() {
+ setProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "test_sources:",
+ " java/com/google/tests/*",
+ " java/com/google/moretests");
+
+ workspace.createDirectory(new WorkspacePath("java/com/google"));
+
+ VirtualFile testFile =
+ workspace.createFile(new WorkspacePath("java/com/google/tests/ExampleTest.java"));
+ VirtualFile moreTestsFile =
+ workspace.createFile(
+ new WorkspacePath("java/com/google/moretests/AnotherExampleTest.java"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ImmutableList<ContentEntry> contentEntries = getWorkspaceContentEntries();
+ assertThat(contentEntries).hasSize(1);
+
+ SourceFolder testRoot = findSourceFolder(contentEntries.get(0), testFile.getParent());
+ SourceFolder moreTestsRoot = findSourceFolder(contentEntries.get(0), moreTestsFile.getParent());
+
+ assertThat(testRoot).isNotNull();
+ assertThat(testRoot.isTestSource()).isTrue();
+ assertThat(testRoot.getPackagePrefix()).isEqualTo("com.google.tests");
+
+ assertThat(moreTestsRoot).isNotNull();
+ assertThat(moreTestsRoot.isTestSource()).isTrue();
+ assertThat(moreTestsRoot.getPackagePrefix()).isEqualTo("com.google.moretests");
+ }
+
+ @Test
+ public void testTestSourcesUpdateCorrectlyOnSubsequentSync() {
+ setProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "test_sources:",
+ " java/com/google/tests/*");
+
+ VirtualFile root = workspace.createDirectory(new WorkspacePath("java/com/google"));
+
+ VirtualFile testsDir = workspace.createDirectory(new WorkspacePath("java/com/google/tests"));
+ VirtualFile moreTestsDir =
+ workspace.createDirectory(new WorkspacePath("java/com/google/moretests"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ContentEntry contentEntry = findContentEntry(root);
+ assertThat(findSourceFolder(contentEntry, testsDir).isTestSource()).isTrue();
+ assertThat(findSourceFolder(contentEntry, moreTestsDir)).isNull();
+
+ // unmark one test source, mark another.
+ setProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "test_sources:",
+ " java/com/google/moretests/*");
+
+ runBlazeSync(syncParams);
+
+ contentEntry = findContentEntry(root);
+ assertThat(findSourceFolder(contentEntry, testsDir)).isNull();
+ assertThat(findSourceFolder(contentEntry, moreTestsDir).isTestSource()).isTrue();
+ }
+
+ @Test
+ public void testExistingPackagePrefixRetainedForTestSources() {
+ setProjectView(
+ "directories:",
+ " java/com/google",
+ " javatests/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "test_sources:",
+ " javatests/*");
+
+ workspace.createDirectory(new WorkspacePath("java/com/google"));
+ VirtualFile javatestsRoot =
+ workspace.createDirectory(new WorkspacePath("javatests/com/google"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ContentEntry testRoot = findContentEntry(javatestsRoot);
+ SourceFolder testRootSource = findSourceFolder(testRoot, javatestsRoot);
+ assertThat(testRootSource.isTestSource()).isTrue();
+ assertThat(testRootSource.getPackagePrefix()).isEqualTo("com.google");
+ }
+
+ @Test
+ public void testTestSourceRelativePackagePrefixCalculation() {
+ setProjectView(
+ "directories:",
+ " java/com/google",
+ "targets:",
+ " //java/com/google:lib",
+ "test_sources:",
+ " java/com/google/tests/*");
+
+ VirtualFile javatestsRoot =
+ workspace.createDirectory(new WorkspacePath("java/com/google/tests"));
+
+ BlazeSyncParams syncParams =
+ new BlazeSyncParams.Builder("Full Sync", BlazeSyncParams.SyncMode.FULL)
+ .addProjectViewTargets(true)
+ .build();
+ runBlazeSync(syncParams);
+
+ errorCollector.assertNoIssues();
+
+ ContentEntry root = findContentEntry(javatestsRoot.getParent());
+ SourceFolder rootSource = findSourceFolder(root, javatestsRoot.getParent());
+ assertThat(rootSource.isTestSource()).isFalse();
+ assertThat(rootSource.getPackagePrefix()).isEqualTo("com.google");
+
+ SourceFolder childTestSource = findSourceFolder(root, javatestsRoot);
+ assertThat(childTestSource.isTestSource()).isTrue();
+ assertThat(childTestSource.getPackagePrefix()).isEqualTo("com.google.tests");
+ }
+
+ @Nullable
+ private static SourceFolder findSourceFolder(ContentEntry entry, VirtualFile file) {
+ for (SourceFolder sourceFolder : entry.getSourceFolders()) {
+ if (file.equals(sourceFolder.getFile())) {
+ return sourceFolder;
+ }
+ }
+ return null;
+ }
}
diff --git a/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java b/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java
new file mode 100644
index 0000000..73e67a3
--- /dev/null
+++ b/java/tests/integrationtests/com/google/idea/blaze/java/sync/projectstructure/JavaSourceFolderProviderTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.java.sync.projectstructure;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.BlazeIntegrationTestCase;
+import com.google.idea.blaze.base.model.primitives.WorkspacePath;
+import com.google.idea.blaze.base.projectview.section.Glob.GlobSet;
+import com.google.idea.blaze.java.sync.model.BlazeContentEntry;
+import com.google.idea.blaze.java.sync.model.BlazeJavaImportResult;
+import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
+import com.google.idea.blaze.java.sync.model.BlazeSourceDirectory;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link JavaSourceFolderProvider} */
+@RunWith(JUnit4.class)
+public class JavaSourceFolderProviderTest extends BlazeIntegrationTestCase {
+
+ @Test
+ public void testInitializeSourceFolders() {
+ ImmutableList<BlazeContentEntry> contentEntries =
+ ImmutableList.of(
+ BlazeContentEntry.builder("/src/workspace/java/apps")
+ .addSource(
+ BlazeSourceDirectory.builder("/src/workspace/java/apps")
+ .setPackagePrefix("apps")
+ .build())
+ .addSource(
+ BlazeSourceDirectory.builder("/src/workspace/java/apps/gen")
+ .setPackagePrefix("apps.gen")
+ .setGenerated(true)
+ .build())
+ .addSource(
+ BlazeSourceDirectory.builder("/src/workspace/java/apps/resources")
+ .setPackagePrefix("apps.resources")
+ .setResource(true)
+ .build())
+ .build(),
+ BlazeContentEntry.builder("/src/workspace/javatests/apps/example")
+ .addSource(
+ BlazeSourceDirectory.builder("/src/workspace/javatests/apps/example")
+ .setPackagePrefix("apps.example")
+ .build())
+ .build());
+
+ JavaSourceFolderProvider provider =
+ new JavaSourceFolderProvider(
+ new BlazeJavaSyncData(
+ new BlazeJavaImportResult(
+ contentEntries, ImmutableMap.of(), ImmutableList.of(), ImmutableSet.of(), null),
+ new GlobSet(ImmutableList.of())));
+
+ VirtualFile root = workspace.createDirectory(new WorkspacePath("java/apps"));
+ VirtualFile gen = workspace.createDirectory(new WorkspacePath("java/apps/gen"));
+ VirtualFile res = workspace.createDirectory(new WorkspacePath("java/apps/resources"));
+
+ ImmutableMap<VirtualFile, SourceFolder> sourceFolders =
+ provider.initializeSourceFolders(getContentEntry(root));
+ assertThat(sourceFolders).hasSize(3);
+
+ SourceFolder rootSource = sourceFolders.get(root);
+ assertThat(rootSource.getPackagePrefix()).isEqualTo("apps");
+ assertThat(JavaSourceFolderProvider.isGenerated(rootSource)).isFalse();
+ assertThat(JavaSourceFolderProvider.isResource(rootSource)).isFalse();
+
+ SourceFolder genSource = sourceFolders.get(gen);
+ assertThat(genSource.getPackagePrefix()).isEqualTo("apps.gen");
+ assertThat(JavaSourceFolderProvider.isGenerated(genSource)).isTrue();
+ assertThat(JavaSourceFolderProvider.isResource(genSource)).isFalse();
+
+ SourceFolder resSource = sourceFolders.get(res);
+ assertThat(JavaSourceFolderProvider.isGenerated(resSource)).isFalse();
+ assertThat(JavaSourceFolderProvider.isResource(resSource)).isTrue();
+
+ VirtualFile testRoot = workspace.createDirectory(new WorkspacePath("javatests/apps/example"));
+ sourceFolders = provider.initializeSourceFolders(getContentEntry(testRoot));
+
+ assertThat(sourceFolders).hasSize(1);
+ assertThat(sourceFolders.get(testRoot).getPackagePrefix()).isEqualTo("apps.example");
+ }
+
+ @Test
+ public void testRelativePackagePrefix() {
+ ImmutableList<BlazeContentEntry> contentEntries =
+ ImmutableList.of(
+ BlazeContentEntry.builder("/src/workspace/java/apps")
+ .addSource(
+ BlazeSourceDirectory.builder("/src/workspace/java/apps")
+ .setPackagePrefix("apps")
+ .build())
+ .build());
+
+ JavaSourceFolderProvider provider =
+ new JavaSourceFolderProvider(
+ new BlazeJavaSyncData(
+ new BlazeJavaImportResult(
+ contentEntries, ImmutableMap.of(), ImmutableList.of(), ImmutableSet.of(), null),
+ new GlobSet(ImmutableList.of())));
+
+ VirtualFile root = workspace.createDirectory(new WorkspacePath("java/apps"));
+ ContentEntry contentEntry = getContentEntry(root);
+
+ ImmutableMap<VirtualFile, SourceFolder> sourceFolders =
+ provider.initializeSourceFolders(contentEntry);
+ assertThat(sourceFolders).hasSize(1);
+
+ VirtualFile testRoot = workspace.createDirectory(new WorkspacePath("java/apps/tests"));
+
+ SourceFolder testSourceChild =
+ provider.setSourceFolderForLocation(contentEntry, sourceFolders.get(root), testRoot, true);
+ assertThat(testSourceChild.isTestSource()).isTrue();
+ assertThat(testSourceChild.getPackagePrefix()).isEqualTo("apps.tests");
+ }
+
+ private ContentEntry getContentEntry(VirtualFile root) {
+ return ModuleRootManager.getInstance(testFixture.getModule())
+ .getModifiableModel()
+ .addContentEntry(root);
+ }
+}
diff --git a/java/tests/unittests/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporterTest.java b/java/tests/unittests/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporterTest.java
index 76d49ac..ad731d7 100644
--- a/java/tests/unittests/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporterTest.java
+++ b/java/tests/unittests/com/google/idea/blaze/java/sync/importer/BlazeJavaWorkspaceImporterTest.java
@@ -441,7 +441,6 @@
.addSource(
BlazeSourceDirectory.builder("/root/javatests/apps/example")
.setPackagePrefix("apps.example")
- .setTest(true)
.build())
.build());
}
@@ -562,7 +561,6 @@
.addSource(
BlazeSourceDirectory.builder("/root/javatests/apps/example")
.setPackagePrefix("apps.example")
- .setTest(true)
.build())
.build());
}
@@ -1330,6 +1328,7 @@
@Override
public void addJarsForSourceTarget(
WorkspaceLanguageSettings workspaceLanguageSettings,
+ ProjectViewSet projectViewSet,
TargetIdeInfo target,
Collection<BlazeJarLibrary> jars,
Collection<BlazeJarLibrary> genJars) {
diff --git a/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java b/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java
index ccd035a..cd5b051 100644
--- a/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java
+++ b/java/tests/unittests/com/google/idea/blaze/java/sync/source/SourceDirectoryCalculatorTest.java
@@ -34,11 +34,9 @@
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.prefetch.MockPrefetchService;
import com.google.idea.blaze.base.prefetch.PrefetchService;
-import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.ErrorCollector;
import com.google.idea.blaze.base.scope.output.IssueOutput;
-import com.google.idea.blaze.base.sync.projectview.SourceTestConfig;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
@@ -82,20 +80,6 @@
(ArtifactLocationDecoder)
artifactLocation -> new File("/root", artifactLocation.getRelativePath());
- static final class TestSourceImportConfig extends SourceTestConfig {
- final boolean isTest;
-
- public TestSourceImportConfig(boolean isTest) {
- super(ProjectViewSet.builder().build());
- this.isTest = isTest;
- }
-
- @Override
- public boolean isTestSource(String relativePath) {
- return isTest;
- }
- }
-
@Override
protected void initTest(
@NotNull Container applicationServices, @NotNull Container projectServices) {
@@ -127,7 +111,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false /* isTest */),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google/app")),
sourceArtifacts,
@@ -160,7 +143,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -193,7 +175,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(true),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -205,7 +186,6 @@
.addSource(
BlazeSourceDirectory.builder("/root/java/com/google")
.setPackagePrefix("com.google")
- .setTest(true)
.build())
.build());
}
@@ -236,7 +216,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -289,7 +268,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("")),
sourceArtifacts,
@@ -340,7 +318,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("")),
sourceArtifacts,
@@ -389,7 +366,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -435,7 +411,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -481,7 +456,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -518,7 +492,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -551,7 +524,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -584,7 +556,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/org")),
sourceArtifacts,
@@ -620,7 +591,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -646,7 +616,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google/my")),
sourceArtifacts,
@@ -669,7 +638,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -717,7 +685,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -769,7 +736,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -817,7 +783,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
@@ -872,7 +837,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
decoder,
ImmutableList.of(new WorkspacePath("java/com/google/android")),
sourceArtifacts,
@@ -1050,7 +1014,6 @@
project,
context,
workspaceRoot,
- new TestSourceImportConfig(false),
getDecoder("/root"),
ImmutableList.of(new WorkspacePath("java/com/google")),
sourceArtifacts,
diff --git a/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java b/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java
index 33ba239..34b06df 100644
--- a/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java
+++ b/plugin_dev/src/com/google/idea/blaze/plugin/sync/IntellijPluginSyncPlugin.java
@@ -23,7 +23,10 @@
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
+import com.google.idea.blaze.base.sync.SourceFolderProvider;
import com.google.idea.blaze.java.sync.JavaLanguageLevelHelper;
+import com.google.idea.blaze.java.sync.model.BlazeJavaSyncData;
+import com.google.idea.blaze.java.sync.projectstructure.JavaSourceFolderProvider;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
@@ -62,6 +65,15 @@
return ImmutableSet.of();
}
+ @Nullable
+ @Override
+ public SourceFolderProvider getSourceFolderProvider(BlazeProjectData projectData) {
+ if (!projectData.workspaceLanguageSettings.isWorkspaceType(WorkspaceType.INTELLIJ_PLUGIN)) {
+ return null;
+ }
+ return new JavaSourceFolderProvider(projectData.syncState.get(BlazeJavaSyncData.class));
+ }
+
@Override
public void updateProjectSdk(
Project project,
diff --git a/testing/src/com/google/idea/testing/ServiceHelper.java b/testing/src/com/google/idea/testing/ServiceHelper.java
index 7a8d2ce..2e5457b 100644
--- a/testing/src/com/google/idea/testing/ServiceHelper.java
+++ b/testing/src/com/google/idea/testing/ServiceHelper.java
@@ -15,6 +15,8 @@
*/
package com.google.idea.testing;
+
+import com.intellij.lang.LanguageExtensionPoint;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
@@ -36,6 +38,20 @@
Disposer.register(parentDisposable, () -> ep.unregisterExtension(instance));
}
+ /** Unregister all extensions of the given class, for the given extension point. */
+ public static <T> void unregisterLanguageExtensionPoint(
+ String extensionPointKey, Class<T> clazz, Disposable parentDisposable) {
+ ExtensionPoint<LanguageExtensionPoint<T>> ep =
+ Extensions.getRootArea().getExtensionPoint(extensionPointKey);
+ LanguageExtensionPoint<T>[] existingExtensions = ep.getExtensions();
+ for (LanguageExtensionPoint<T> ext : existingExtensions) {
+ if (clazz.getName().equals(ext.implementationClass)) {
+ ep.unregisterExtension(ext);
+ Disposer.register(parentDisposable, () -> ep.registerExtension(ext));
+ }
+ }
+ }
+
public static <T> void registerApplicationService(
Class<T> key, T implementation, Disposable parentDisposable) {
registerComponentInstance(
diff --git a/third_party/BUILD b/third_party/BUILD
index 04f13a6..283fe42 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -8,5 +8,5 @@
sh_binary(
name = "zip",
- srcs = ["zip/zip.sh"],
+ srcs = ["zip-wrap/zip.sh"],
)
diff --git a/third_party/zip/zip.sh b/third_party/zip-wrap/zip.sh
similarity index 100%
rename from third_party/zip/zip.sh
rename to third_party/zip-wrap/zip.sh
diff --git a/version.bzl b/version.bzl
index ec651ae..0f144e5 100644
--- a/version.bzl
+++ b/version.bzl
@@ -1,3 +1,3 @@
"""Version of the blaze plugin."""
-VERSION = "1.12.6"
+VERSION = "2016.12.05.6"