Skip to content

Commit

Permalink
feat: add IntelliJ IDEA plugin for generating with bzlgen
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Mackay committed May 18, 2020
1 parent 4c3f910 commit 061b005
Show file tree
Hide file tree
Showing 14 changed files with 562 additions and 0 deletions.
16 changes: 16 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
)
16 changes: 16 additions & 0 deletions plugin/BUILD
Original file line number Diff line number Diff line change
@@ -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"],
)
13 changes: 13 additions & 0 deletions plugin/plugin.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/resources/META-INF/plugin.xml" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
</content>
<orderEntry type="jdk" jdkName="IntelliJ IDEA IU-193.6494.35" jdkType="IDEA JDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
53 changes: 53 additions & 0 deletions plugin/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<idea-plugin>
<id>com.evertz.devtools.bzlgen.ij</id>
<name>Bzlgen</name>
<version>0.1.0</version>
<vendor email="opensource@evertz.com" url="https://www.evertz.com">Evertz Microsystems</vendor>

<description><![CDATA[
Bazel build file generator for various rules, driven by bzlgen
]]></description>

<change-notes><![CDATA[
Initial release
]]>
</change-notes>

<idea-version since-build="173.0"/>

<depends>com.intellij.modules.platform</depends>

<extensions defaultExtensionNs="com.intellij"></extensions>

<actions>
<group id="Bglgen.Generate"
text="Bzlgen"
description="Bzlgen BUILD file generation"
popup="true"
compact="true">

<action id="Bglgen.Generate.container_layer"
class="com.evertz.devtools.bzlgen.ij.actions.GenerateContainerLayerRuleAction">
</action>

<action id="Bglgen.Generate.filegroup"
class="com.evertz.devtools.bzlgen.ij.actions.GenerateFilegroupRuleAction">
</action>

<action id="Bglgen.Generate.ng_module"
class="com.evertz.devtools.bzlgen.ij.actions.GenerateNgModuleRuleAction">
</action>

<action id="Bglgen.Generate.nodejs_binary"
class="com.evertz.devtools.bzlgen.ij.actions.GenerateNodeJsBinaryRuleAction">
</action>

<action id="Bglgen.Generate.ts_library"
class="com.evertz.devtools.bzlgen.ij.actions.GenerateTsLibraryRuleAction">
</action>

<add-to-group group-id="ProjectViewPopupMenu" />
</group>
</actions>

</idea-plugin>
75 changes: 75 additions & 0 deletions plugin/src/com/evertz/devtools/bzlgen/ij/BzlgenUtil.java
Original file line number Diff line number Diff line change
@@ -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<String> 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");
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.evertz.devtools.bzlgen.ij.actions;

public class GenerateContainerLayerRuleAction extends AbstractBzlgenAction {

@Override
protected String getRuleType() {
return "container_layer";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.evertz.devtools.bzlgen.ij.actions;

public class GenerateFilegroupRuleAction extends AbstractBzlgenAction {

@Override
protected String getRuleType() {
return "filegroup";
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
}
Original file line number Diff line number Diff line change
@@ -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<String> SUPPORTED_EXTS = new HashSet<String>(Arrays.asList("ts", "js"));

@Override
protected boolean supportsDirectories() {
return false;
}

@Override
protected Set<String> getSupportedFileExtensions() {
return SUPPORTED_EXTS;
}

@Override
protected String getRuleType() {
return "js_binary";
}
}
Original file line number Diff line number Diff line change
@@ -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<String> SUPPORTED_EXTS = new HashSet<String>(Collections.singleton("ts"));

@Override
protected Set<String> getSupportedFileExtensions() {
return SUPPORTED_EXTS;
}

@Override
protected String getRuleType() {
return "ts_library";
}
}
Loading

0 comments on commit 061b005

Please sign in to comment.