diff --git a/WORKSPACE b/WORKSPACE
index 5bf3e0e..ab336bb 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -4,6 +4,7 @@ workspace(
)
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:jvm.bzl", "jvm_maven_import_external")
http_archive(
name = "build_bazel_rules_nodejs",
@@ -28,3 +29,18 @@ yarn_install(
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
install_bazel_dependencies()
+
+jvm_maven_import_external(
+ name = "error_prone_annotations",
+ artifact = "com.google.errorprone:error_prone_annotations:2.3.0",
+ artifact_sha256 = "524b43ea15ca97c68f10d5f417c4068dc88144b620d2203f0910441a769fd42f",
+ licenses = ["notice"], # Apache 2.0
+ server_urls = ["https://repo1.maven.org/maven2"],
+)
+
+http_archive(
+ name = "intellij_ce_2019_3",
+ build_file = "@//third_party/intellij_sdk:BUILD.idea193",
+ sha256 = "fb347c3c681328d11e87846950e8c5af6ac2c8d6a7e56946d3a10e6121d322f9",
+ url = "https://www.jetbrains.com/intellij-repository/releases/com/jetbrains/intellij/idea/ideaIC/2019.3.2/ideaIC-2019.3.2.zip",
+)
diff --git a/plugin/BUILD b/plugin/BUILD
new file mode 100644
index 0000000..bb14614
--- /dev/null
+++ b/plugin/BUILD
@@ -0,0 +1,16 @@
+java_library(
+ name = "ij_sdk",
+ neverlink = True,
+ exports = [
+ "@intellij_ce_2019_3//:sdk",
+ ],
+)
+
+java_binary(
+ name = "bzlgen_plugin",
+ srcs = glob(["src/**/*.java"]),
+ create_executable = False,
+ resource_strip_prefix = "plugin/resources",
+ resources = glob(["resources/**"]),
+ deps = [":ij_sdk"],
+)
diff --git a/plugin/plugin.iml b/plugin/plugin.iml
new file mode 100644
index 0000000..b152f53
--- /dev/null
+++ b/plugin/plugin.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugin/resources/META-INF/plugin.xml b/plugin/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..7300eb2
--- /dev/null
+++ b/plugin/resources/META-INF/plugin.xml
@@ -0,0 +1,53 @@
+
+ com.evertz.devtools.bzlgen.ij
+ Bzlgen
+ 0.1.0
+ Evertz Microsystems
+
+
+
+
+
+
+
+
+ com.intellij.modules.platform
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/BzlgenUtil.java b/plugin/src/com/evertz/devtools/bzlgen/ij/BzlgenUtil.java
new file mode 100644
index 0000000..2091fa1
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/BzlgenUtil.java
@@ -0,0 +1,75 @@
+package com.evertz.devtools.bzlgen.ij;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypes;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.search.FileTypeIndex;
+import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.indexing.FileBasedIndex;
+
+import java.io.File;
+import java.util.Set;
+
+public final class BzlgenUtil {
+ private BzlgenUtil() {}
+
+ public static final String BZLGEN_BINARY = "bzlgen";
+
+ public static final String MESSAGE_TITLE = "bzlgen";
+
+ public static File findBzlgenBinaryOnPath() {
+ String path = EnvironmentUtil.getValue("PATH");
+
+ if (path == null) {
+ return null;
+ }
+
+ for (String entry : path.split(File.pathSeparator)) {
+ File file = new File(entry, BZLGEN_BINARY);
+ if (file.exists() && file.isFile() && file.canExecute()) {
+ return file;
+ }
+ }
+
+ return null;
+ }
+
+ public static boolean directoryContainsFileWithExtension(VirtualFile directory, String type) {
+ if (!directory.isDirectory()) {
+ return false;
+ }
+
+ for (VirtualFile child : VfsUtil.getChildren(directory)) {
+ if (type.equals(child.getExtension())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean directoryContainsFileWithAnyExtension(VirtualFile directory, Set types) {
+ if (!directory.isDirectory()) {
+ return false;
+ }
+
+ if (types.isEmpty()) {
+ // empty set is considered that all files are supported
+ return true;
+ }
+
+ for (String type : types) {
+ if (directoryContainsFileWithExtension(directory, type)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean isInEv() {
+ // pathetic check to try and see if we are inside ev, or somewhere else
+ return EnvironmentUtil.getEnvironmentMap().containsKey("build_tools_dir");
+ }
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/actions/AbstractBzlgenAction.java b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/AbstractBzlgenAction.java
new file mode 100644
index 0000000..a9a37ea
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/AbstractBzlgenAction.java
@@ -0,0 +1,91 @@
+package com.evertz.devtools.bzlgen.ij.actions;
+
+import com.evertz.devtools.bzlgen.ij.BzlgenUtil;
+import com.evertz.devtools.bzlgen.ij.process.BzlgenCommand;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public abstract class AbstractBzlgenAction extends AnAction {
+ private final static ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ @Override
+ public void update(AnActionEvent event) {
+ VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE);
+
+ if (vf.isDirectory() && !supportsDirectories()) {
+ event.getPresentation().setEnabledAndVisible(false);
+
+ // no point carrying on
+ return;
+ }
+
+ String extension = vf.getExtension();
+ Set supportedExtensions = getSupportedFileExtensions();
+
+ boolean containsSupportedFiles = BzlgenUtil.directoryContainsFileWithAnyExtension(vf, supportedExtensions);
+
+ if (containsSupportedFiles || supportedExtensions.contains(extension)) {
+ setEnabledForRule(event.getPresentation(), vf);
+ } else {
+ event.getPresentation().setEnabledAndVisible(false);
+ }
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent event) {
+ VirtualFile selected = event.getData(CommonDataKeys.VIRTUAL_FILE);
+ VirtualFile contentRoot = getContentRoot(event.getProject(), selected);
+
+ BzlgenCommand.Builder builder = new BzlgenCommand.Builder()
+ .type(getRuleType())
+ .path(VfsUtilCore.getRelativePath(selected, contentRoot))
+ .workingDirectory(contentRoot.getPath())
+ .executor(executor);
+
+ decorateBzlgenCommandBuilder(builder);
+
+ builder.build().run();
+
+ if (selected.isDirectory()) {
+ selected.refresh(false, false);
+ } else {
+ selected.getParent().refresh(false, false);
+ }
+ }
+
+ protected void setEnabledForRule(Presentation presentation, VirtualFile vf) {
+ String type = getRuleType();
+ presentation.setText("Generate " + type + " for " + vf.getName(), false);
+ presentation.setEnabledAndVisible(true);
+ }
+
+ protected boolean supportsDirectories() {
+ return true;
+ }
+
+ protected Set getSupportedFileExtensions() {
+ return Collections.emptySet();
+ }
+
+ protected VirtualFile getContentRoot(Project project, VirtualFile file) {
+ ProjectFileIndex index = ProjectFileIndex.getInstance(project);
+ return index.getContentRootForFile(file);
+ }
+
+ protected void decorateBzlgenCommandBuilder(BzlgenCommand.Builder builder) {
+ // allow subclass to override this and add to the builder
+ };
+
+ protected abstract String getRuleType();
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateContainerLayerRuleAction.java b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateContainerLayerRuleAction.java
new file mode 100644
index 0000000..735abba
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateContainerLayerRuleAction.java
@@ -0,0 +1,9 @@
+package com.evertz.devtools.bzlgen.ij.actions;
+
+public class GenerateContainerLayerRuleAction extends AbstractBzlgenAction {
+
+ @Override
+ protected String getRuleType() {
+ return "container_layer";
+ }
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateFilegroupRuleAction.java b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateFilegroupRuleAction.java
new file mode 100644
index 0000000..6b768dc
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateFilegroupRuleAction.java
@@ -0,0 +1,9 @@
+package com.evertz.devtools.bzlgen.ij.actions;
+
+public class GenerateFilegroupRuleAction extends AbstractBzlgenAction {
+
+ @Override
+ protected String getRuleType() {
+ return "filegroup";
+ }
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateNgModuleRuleAction.java b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateNgModuleRuleAction.java
new file mode 100644
index 0000000..db00dab
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateNgModuleRuleAction.java
@@ -0,0 +1,42 @@
+package com.evertz.devtools.bzlgen.ij.actions;
+
+import com.evertz.devtools.bzlgen.ij.BzlgenUtil;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.Arrays;
+
+public class GenerateNgModuleRuleAction extends AbstractBzlgenAction {
+
+ @Override
+ public void update(AnActionEvent event) {
+ VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE);
+
+ // ng_module is only supported on directories
+ if (!vf.isDirectory()) {
+ event.getPresentation().setEnabledAndVisible(false);
+ return;
+ }
+
+ // look for a .module.ts, and .component.ts, although this is a convention, it's well followed within ev
+ // and this somewhat enforces it ;)
+ boolean containsNgFiles = Arrays.stream(VfsUtil.getChildren(vf))
+ .filter(child -> !child.isDirectory()
+ && (child.getPath().endsWith(".component.ts") || child.getPath().endsWith(".module.ts")))
+ .count() == 2;
+
+ if (!containsNgFiles) {
+ event.getPresentation().setEnabledAndVisible(false);
+ return;
+ }
+
+ setEnabledForRule(event.getPresentation(), vf);
+ }
+
+ @Override
+ protected String getRuleType() {
+ return BzlgenUtil.isInEv() ? "ng_bundle" : "ng_module";
+ }
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateNodeJsBinaryRuleAction.java b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateNodeJsBinaryRuleAction.java
new file mode 100644
index 0000000..efd9424
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateNodeJsBinaryRuleAction.java
@@ -0,0 +1,24 @@
+package com.evertz.devtools.bzlgen.ij.actions;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class GenerateNodeJsBinaryRuleAction extends AbstractBzlgenAction {
+ private static final Set SUPPORTED_EXTS = new HashSet(Arrays.asList("ts", "js"));
+
+ @Override
+ protected boolean supportsDirectories() {
+ return false;
+ }
+
+ @Override
+ protected Set getSupportedFileExtensions() {
+ return SUPPORTED_EXTS;
+ }
+
+ @Override
+ protected String getRuleType() {
+ return "js_binary";
+ }
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateTsLibraryRuleAction.java b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateTsLibraryRuleAction.java
new file mode 100644
index 0000000..e500a9a
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/actions/GenerateTsLibraryRuleAction.java
@@ -0,0 +1,19 @@
+package com.evertz.devtools.bzlgen.ij.actions;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class GenerateTsLibraryRuleAction extends AbstractBzlgenAction {
+ private static final Set SUPPORTED_EXTS = new HashSet(Collections.singleton("ts"));
+
+ @Override
+ protected Set getSupportedFileExtensions() {
+ return SUPPORTED_EXTS;
+ }
+
+ @Override
+ protected String getRuleType() {
+ return "ts_library";
+ }
+}
diff --git a/plugin/src/com/evertz/devtools/bzlgen/ij/process/BzlgenCommand.java b/plugin/src/com/evertz/devtools/bzlgen/ij/process/BzlgenCommand.java
new file mode 100644
index 0000000..8a270e6
--- /dev/null
+++ b/plugin/src/com/evertz/devtools/bzlgen/ij/process/BzlgenCommand.java
@@ -0,0 +1,123 @@
+package com.evertz.devtools.bzlgen.ij.process;
+
+import com.evertz.devtools.bzlgen.ij.BzlgenUtil;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationType;
+import com.intellij.notification.Notifications;
+import com.intellij.util.EnvironmentUtil;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public final class BzlgenCommand {
+ final ProcessBuilder processBuilder;
+ final ExecutorService executor;
+
+ protected BzlgenCommand(ProcessBuilder processBuilder, ExecutorService executor) {
+ this.processBuilder = processBuilder;
+ this.executor = executor;
+ }
+
+ public Future run() {
+ if (executor.isShutdown() || executor.isTerminated()) {
+ return CompletableFuture.completedFuture(1);
+ }
+
+ return executor.submit(() -> {
+ try {
+ Process process = processBuilder.start();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ StringBuilder builder = new StringBuilder();
+ String line = null;
+
+ while ((line = reader.readLine()) != null) {
+ builder.append(line);
+ builder.append(System.getProperty("line.separator"));
+ }
+
+ String result = builder.toString();
+
+ int code = process.waitFor();
+
+ NotificationType type = code == 0 ? NotificationType.INFORMATION : NotificationType.ERROR;
+ String message = code == 0 ? "Generated BUILD file" : "Error generating BUILD file";
+
+ Notifications.Bus.notify(new Notification("bzlgen", BzlgenUtil.MESSAGE_TITLE, message, type));
+
+ if (code != 0) {
+ Notifications.Bus.notify(new Notification("bzlgen", BzlgenUtil.MESSAGE_TITLE, result, type));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return 1;
+ });
+ }
+
+ public static class Builder {
+ private String type;
+ private String path;
+ private Map flags = new HashMap<>();
+
+ private String workingDirectory;
+
+ private ExecutorService executor;
+
+ public Builder type(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public Builder path(String path) {
+ this.path = path;
+ return this;
+ }
+
+ public Builder workingDirectory(String workingDirectory) {
+ this.workingDirectory = workingDirectory;
+ return this;
+ }
+
+ public Builder executor(ExecutorService executor) {
+ this.executor = executor;
+ return this;
+ }
+
+ public Builder flag(String flag, String value) {
+ this.flags.put("--" + flag, value);
+ return this;
+ }
+
+ public BzlgenCommand build() {
+ File binary = BzlgenUtil.findBzlgenBinaryOnPath();
+ if (binary == null) {
+ throw new RuntimeException("bzlgen not found on PATH");
+ }
+
+ ArrayList commands = new ArrayList<>();
+ commands.add(binary.getAbsolutePath());
+ commands.add(type);
+ commands.add(path);
+ flags.forEach((flag, value) -> commands.add(flag + "=" + value));
+
+ ProcessBuilder processBuilder = new ProcessBuilder(commands)
+ .directory(new File(workingDirectory))
+ .redirectErrorStream(true);
+
+ processBuilder.environment()
+ .putAll(EnvironmentUtil.getEnvironmentMap());
+
+ return new BzlgenCommand(processBuilder, executor);
+ }
+ }
+}
diff --git a/third_party/BUILD b/third_party/BUILD
new file mode 100644
index 0000000..e69de29
diff --git a/third_party/intellij_sdk/BUILD b/third_party/intellij_sdk/BUILD
new file mode 100644
index 0000000..e69de29
diff --git a/third_party/intellij_sdk/BUILD.idea193 b/third_party/intellij_sdk/BUILD.idea193
new file mode 100644
index 0000000..d1a92b8
--- /dev/null
+++ b/third_party/intellij_sdk/BUILD.idea193
@@ -0,0 +1,72 @@
+# Adapted from https://github.com/bazelbuild/intellij/blob/master/intellij_platform_sdk/BUILD.idea193
+package(default_visibility = ["//visibility:public"])
+
+java_import(
+ name = "sdk",
+ jars = glob(
+ # temporarily include the newly extracted java plugin in the core SDK
+ # see https://blog.jetbrains.com/platform/2019/06/java-functionality-extracted-as-a-plugin/
+ # api#191: expose the java plugin as a separate target
+ [
+ "lib/*.jar",
+ "plugins/java/lib/*.jar",
+ ],
+ ),
+ deps = ["@error_prone_annotations//jar"],
+)
+
+java_import(
+ name = "devkit",
+ jars = glob(["plugins/devkit/lib/devkit.jar"]),
+)
+
+java_import(
+ name = "guava",
+ jars = glob([
+ "lib/failureaccess-*.jar",
+ "lib/guava-*.jar",
+ ]),
+)
+
+java_import(
+ name = "coverage",
+ jars = glob(["plugins/coverage/lib/*.jar"]),
+)
+
+java_import(
+ name = "hg4idea",
+ jars = glob(["plugins/hg4idea/lib/hg4idea.jar"]),
+)
+
+java_import(
+ name = "kotlin",
+ jars = glob(["plugins/Kotlin/lib/*.jar"]),
+)
+
+java_import(
+ name = "junit",
+ jars = glob(["plugins/junit/lib/*.jar"]),
+)
+
+java_import(
+ name = "tasks",
+ jars = glob([
+ "plugins/tasks/lib/tasks-api.jar",
+ "plugins/tasks/lib/tasks-core.jar",
+ ]),
+)
+
+java_import(
+ name = "terminal",
+ jars = glob(["plugins/terminal/lib/terminal.jar"]),
+)
+
+java_import(
+ name = "forms_rt",
+ jars = ["lib/forms_rt.jar"],
+)
+
+filegroup(
+ name = "application_info_jar",
+ srcs = glob(["lib/resources.jar"]),
+)