Skip to content

Commit

Permalink
Add initial skeleton of the new intellij-ext service.
Browse files Browse the repository at this point in the history
This adds the new external grpc server that will isolate
google3 from the IDE and that will eventually replace
the current grpc services.

PiperOrigin-RevId: 554330557
  • Loading branch information
Googler authored and copybara-github committed Aug 14, 2023
1 parent 516aab6 commit 985dca8
Show file tree
Hide file tree
Showing 15 changed files with 934 additions and 0 deletions.
1 change: 1 addition & 0 deletions base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ java_library(
"//common/util:concurrency",
"//common/util:platform",
"//common/util:transactions",
"//ext:intellijext",
"//intellij_platform_sdk:jsr305", # unuseddeps: keep for @Nullable
"//intellij_platform_sdk:plugin_api",
"//proto:proto_deps",
Expand Down
7 changes: 7 additions & 0 deletions base/src/META-INF/blaze-base.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@
<keyboard-shortcut first-keystroke="ctrl alt shift D" keymap="$default"/>
<keyboard-shortcut first-keystroke="meta alt shift D" keymap="macOS System Shortcuts" replace-all="true"/>
</action>
<action id="Blaze.IntelliJExt.Status" class="com.google.idea.blaze.base.ext.IntelliJExtStatusAction" text="Status" >
</action>

<group id="Blaze.MainMenuActionGroup" class="com.google.idea.blaze.base.actions.BlazeMenuGroup">
<add-to-group group-id="MainMenu" anchor="before" relative-to-action="HelpMenu"/>
Expand All @@ -148,6 +150,10 @@
<reference id="MakeBlazeProject"/>
<reference id="MakeBlazeModule"/>
</group>
<group id="Blaze.IntelliJExtMenuGroup" class="com.google.idea.blaze.base.ext.IntelliJExtMenu"
text="_Extensions" popup="true">
<reference id="Blaze.IntelliJExt.Status"/>
</group>
<group id="Blaze.Project" text="_Project" popup="true">
<reference id="Blaze.EditProjectView"/>
<reference id="Blaze.AddDirectoryToProjectView"/>
Expand Down Expand Up @@ -226,6 +232,7 @@
<projectService serviceInterface="com.google.idea.blaze.base.sync.libraries.LibraryFilesProviderFactory"
serviceImplementation="com.google.idea.blaze.base.sync.libraries.DefaultLibraryFilesProviderFactory"/>

<applicationService serviceImplementation="com.google.idea.blaze.base.ext.IntelliJExtManager"/>
<applicationService serviceInterface="com.google.idea.blaze.base.async.executor.BlazeExecutor"
serviceImplementation="com.google.idea.blaze.base.async.executor.BlazeExecutorImpl"/>
<fileDocumentManagerListener implementation="com.google.idea.blaze.base.buildmodifier.BuildFileFormatOnSaveHandler" order="first"/>
Expand Down
73 changes: 73 additions & 0 deletions base/src/com/google/idea/blaze/base/ext/IntelliJExtManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2023 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.ext;

import com.google.idea.blaze.ext.IntelliJExtService;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.jetbrains.annotations.Nullable;

/**
* An application level manager that creates the IntelliJExtService.
*
* <p>To enable this service the system property `intellij.ext.binary` must be set to be pointing to
* the executable that provides the extended services. This executable must implement the
* intellij-ext grpc interface for the IDE to communicate with it.
*/
public class IntelliJExtManager {

public static final String INTELLIJ_EXT_BINARY = "intellij.ext.binary";

private IntelliJExtService service;

private static final Logger logger = Logger.getInstance(IntelliJExtManager.class);

public static IntelliJExtManager getInstance() {
return ApplicationManager.getApplication().getService(IntelliJExtManager.class);
}

public synchronized IntelliJExtService getService() {
if (service == null) {
Path path = getBinaryPath();
if (path == null) {
throw new IllegalStateException("No intellij-ext binary found");
}
service = new IntelliJExtService(path);
}
return service;
}

@Nullable
private Path getBinaryPath() {
String binary = System.getProperty(INTELLIJ_EXT_BINARY);
if (binary == null) {
return null;
}
Path path = Paths.get(binary);
if (!Files.exists(path)) {
logger.warn(String.format("intellij-ext binary path %s does not exist", path));
return null;
}
return path;
}

boolean isEnabled() {
return getBinaryPath() != null;
}
}
35 changes: 35 additions & 0 deletions base/src/com/google/idea/blaze/base/ext/IntelliJExtMenu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2023 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.ext;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import org.jetbrains.annotations.NotNull;

/** The menu for all intellij-ext actions */
public class IntelliJExtMenu extends DefaultActionGroup {

@Override
public void update(@NotNull AnActionEvent e) {
boolean enabled = IntelliJExtManager.getInstance().isEnabled();
e.getPresentation().setEnabledAndVisible(enabled);
}

@Override
public boolean isDumbAware() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2023 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.ext;

import com.google.idea.blaze.ext.IntelliJExtService;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAwareAction;
import org.jetbrains.annotations.NotNull;

/** An action to show the connection status against the intellij-ext server. */
public class IntelliJExtStatusAction extends DumbAwareAction {

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
IntelliJExtService service = IntelliJExtManager.getInstance().getService();
new StatusDialog(e.getProject(), service).show();
}

@Override
public void update(@NotNull AnActionEvent e) {
boolean enabled = IntelliJExtManager.getInstance().isEnabled();
e.getPresentation().setEnabledAndVisible(enabled);
}
}
136 changes: 136 additions & 0 deletions base/src/com/google/idea/blaze/base/ext/StatusDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright 2023 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.ext;

import com.google.idea.blaze.ext.IntelliJExtService;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.AppUIUtil;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.scale.ScaleContext;
import com.intellij.util.ui.JBFont;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import org.jetbrains.annotations.Nullable;

/** The status dialog for the connection to the intellij-ext service. */
public class StatusDialog extends DialogWrapper {

private final Box box;

private String content;

public StatusDialog(@Nullable Project project, IntelliJExtService service) {
super(project, false);
this.box = Box.createVerticalBox();
setResizable(false);
setTitle("Extended IntelliJ Services");
init();
setContent("Loading...", null);
new Thread(
() -> {
String message;
Map<String, String> status = null;
try {
status = service.getStatus();
message = service.getVersion();
} catch (Exception e) {
message = "Error\nCannot fetch status, see logs.";
}
String finalMessage = message;
Map<String, String> finalStatus = status;
UIUtil.invokeLaterIfNeeded(() -> setContent(finalMessage, finalStatus));
})
.start();
}

@Override
protected @Nullable JComponent createCenterPanel() {
Icon appIcon = AppUIUtil.loadApplicationIcon(ScaleContext.create(), 60);
JLabel icon = new JLabel(appIcon);
icon.setVerticalAlignment(SwingConstants.TOP);
icon.setBorder(JBUI.Borders.empty(20, 12, 0, 24));
box.setBorder(JBUI.Borders.empty(20, 0, 0, 20));

return JBUI.Panels.simplePanel().addToLeft(icon).addToCenter(box);
}

@Override
protected void createDefaultActions() {
super.createDefaultActions();
myOKAction =
new OkAction() {
@Override
protected void doAction(ActionEvent e) {
copyAboutInfoToClipboard();
close(OK_EXIT_CODE);
}
};
myOKAction.putValue(Action.NAME, "Copy and Close");
myCancelAction.putValue(Action.NAME, "Close");
}

private void setContent(String message, Map<String, String> status) {
String content = message;
box.removeAll();
String[] lines = message.split("\n");
box.add(label(lines[0], JBFont.regular().asBold()));
for (int i = 1; i < lines.length; i++) {
box.add(label(lines[i], JBFont.small()));
}
box.add(Box.createVerticalStrut(10));
if (status != null) {
Box line = Box.createHorizontalBox();
line.setAlignmentX(0.0f);
for (Entry<String, String> e : status.entrySet()) {
line.add(label(e.getKey(), JBFont.regular().asBold()));
line.add(Box.createHorizontalStrut(5));
line.add(label(e.getValue(), JBFont.small()));
box.add(line);
content += "\n" + e.getKey() + ": " + e.getValue();
}
}
this.content = content;
this.getContentPane().revalidate();
}

private static JLabel label(String text, JBFont font) {
return new JBLabel(text).withFont(font);
}

private String getPlainText() {
return content;
}

private void copyAboutInfoToClipboard() {
try {
CopyPasteManager.getInstance().setContents(new StringSelection(getPlainText()));
} catch (Exception ignore) {
// Ignore
}
}
}
57 changes: 57 additions & 0 deletions ext/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
java_binary(
name = "client",
main_class = "com.google.idea.blaze.ext.IntelliJExtClientCli",
runtime_deps = [
":intellijext",
],
)

java_library(
name = "intellijext",
srcs = glob(["src/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//ext/proto:intellijext_java_grpc",
"//ext/proto:intellijext_java_proto",
"//third_party/java/grpc:core",
"//third_party/java/grpc:netty",
"//third_party/java/netty4:common",
"//third_party/java/netty4:transport",
"//third_party/java/netty4:transport_native_epoll",
"//third_party/java/netty4:transport_native_unix_common",
"@com_google_guava_guava//jar",
],
)

java_binary(
name = "IntelliJExtTestServer",
srcs = ["tests/com/google/idea/blaze/ext/IntelliJExtTestServer.java"],
main_class = "com.google.idea.blaze.ext.IntelliJExtTestServer",
deps = [
"//ext/proto:intellijext_java_grpc",
"//ext/proto:intellijext_java_proto",
"//third_party/java/grpc:core",
"//third_party/java/grpc:netty",
"//third_party/java/grpc:stub",
"//third_party/java/netty4:common",
"//third_party/java/netty4:transport",
"//third_party/java/netty4:transport_native_epoll",
"//third_party/java/netty4:transport_native_unix_common",
],
)

java_test(
name = "IntelliJExtServiceTest",
srcs = ["tests/com/google/idea/blaze/ext/IntelliJExtServiceTest.java"],
data = [
"//ext:IntelliJExtTestServer_deploy.jar",
],
test_class = "com.google.idea.blaze.ext.IntelliJExtServiceTest",
deps = [
":intellijext",
"//ext/proto:intellijext_java_proto",
"//third_party/java/grpc:core",
"@junit//jar",
"@truth//jar",
],
)
Loading

0 comments on commit 985dca8

Please sign in to comment.