/* * 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 liveplugin; import com.intellij.notification.NotificationGroup; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VirtualFile; import liveplugin.pluginrunner.*; import liveplugin.toolwindow.PluginToolWindowManager; import liveplugin.toolwindow.util.ExamplePluginInstaller; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.intellij.openapi.project.Project.DIRECTORY_STORE_FOLDER; import static java.util.Arrays.asList; import static liveplugin.IDEUtil.askIfUserWantsToRestartIde; import static liveplugin.IDEUtil.downloadFile; public class LivePluginAppComponent implements ApplicationComponent, DumbAware { public static final String LIVE_PLUGIN_ID = "LivePlugin"; public static final String PLUGIN_EXAMPLES_PATH = "/liveplugin/pluginexamples/"; public static final String LIVEPLUGIN_LIBS_PATH = PathManager.getPluginsPath() + "/LivePlugin/lib/"; public static final NotificationGroup livePluginNotificationGroup = NotificationGroup.balloonGroup("Live Plugin"); private static final Logger LOG = Logger.getInstance(LivePluginAppComponent.class); private static final String DEFAULT_PLUGIN_PATH = PLUGIN_EXAMPLES_PATH; private static final String DEFAULT_PLUGIN_SCRIPT = "default-plugin.groovy"; private static final String DEFAULT_PLUGIN_TEST_SCRIPT = "default-plugin-test.groovy"; private static final String DEFAULT_IDEA_OUTPUT_FOLDER = "out"; private static final String COMPONENT_NAME = "LivePluginComponent"; public static String pluginsRootPath() { return FileUtilRt.toSystemIndependentName(PathManager.getPluginsPath() + "/live-plugins"); } public static Map<String, String> pluginIdToPathMap() { final boolean containsIdeaProjectFolder = new File(pluginsRootPath() + "/" + DIRECTORY_STORE_FOLDER).exists(); File[] files = new File(pluginsRootPath()).listFiles(file -> file.isDirectory() && !file.getName().equals(DIRECTORY_STORE_FOLDER) && !(containsIdeaProjectFolder && file.getName().equals(DEFAULT_IDEA_OUTPUT_FOLDER)) ); if (files == null) return new HashMap<>(); HashMap<String, String> result = new HashMap<>(); for (File file : files) { result.put(file.getName(), FileUtilRt.toSystemIndependentName(file.getAbsolutePath())); } return result; } public static boolean isInvalidPluginFolder(VirtualFile virtualFile) { List<String> scriptFile = asList( GroovyPluginRunner.MAIN_SCRIPT, ClojurePluginRunner.MAIN_SCRIPT, ScalaPluginRunner.MAIN_SCRIPT, KotlinPluginRunner.MAIN_SCRIPT ); for (String file : scriptFile) { if (MyFileUtil.findScriptFilesIn(virtualFile.getPath(), file).size() > 0) { return false; } } return true; } public static String defaultPluginScript() { return readSampleScriptFile(DEFAULT_PLUGIN_PATH, DEFAULT_PLUGIN_SCRIPT); } public static String defaultPluginTestScript() { return readSampleScriptFile(DEFAULT_PLUGIN_PATH, DEFAULT_PLUGIN_TEST_SCRIPT); } public static String readSampleScriptFile(String pluginPath, String file) { try { String path = pluginPath + file; return FileUtil.loadTextAndClose(LivePluginAppComponent.class.getClassLoader().getResourceAsStream(path)); } catch (IOException e) { LOG.error(e); return ""; } } public static boolean pluginExists(String pluginId) { return pluginIdToPathMap().keySet().contains(pluginId); } private static boolean isGroovyOnClasspath() { return IDEUtil.isOnClasspath("org.codehaus.groovy.runtime.DefaultGroovyMethods"); } public static boolean scalaIsOnClassPath() { return IDEUtil.isOnClasspath("scala.Some"); } public static boolean kotlinCompilerIsOnClassPath() { // TODO in case of IJ update, remove old and download new version of compiler jar return IDEUtil.isOnClasspath("org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler"); } public static boolean clojureIsOnClassPath() { return IDEUtil.isOnClasspath("clojure.core.Vec"); } private static void runAllPlugins() { ApplicationManager.getApplication().invokeLater(() -> { AnActionEvent event = new AnActionEvent( null, IDEUtil.DUMMY_DATA_CONTEXT, PluginRunner.IDE_STARTUP, new Presentation(), ActionManager.getInstance(), 0 ); ErrorReporter errorReporter = new ErrorReporter(); RunPluginAction.runPlugins(pluginIdToPathMap().keySet(), event, errorReporter, RunPluginAction.createPluginRunners(errorReporter)); }); } public static void checkThatGroovyIsOnClasspath() { if (isGroovyOnClasspath()) return; // this can be useful for non-java IDEs because they don't have bundled groovy libs NotificationListener listener = (notification, event) -> { boolean downloaded = downloadFile("http://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/2.3.9/", "groovy-all-2.3.9.jar", LIVEPLUGIN_LIBS_PATH); if (downloaded) { notification.expire(); askIfUserWantsToRestartIde("For Groovy libraries to be loaded IDE restart is required. Restart now?"); } else { livePluginNotificationGroup .createNotification("Failed to download Groovy libraries", NotificationType.WARNING); } }; livePluginNotificationGroup.createNotification( "LivePlugin didn't find Groovy libraries on classpath", "Without it plugins won't work. <a href=\"\">Download Groovy libraries</a> (~7Mb)", NotificationType.ERROR, listener ).notify(null); } @Override public void initComponent() { checkThatGroovyIsOnClasspath(); Settings settings = Settings.getInstance(); if (settings.justInstalled) { installHelloWorldPlugins(); settings.justInstalled = false; } if (settings.runAllPluginsOnIDEStartup) { runAllPlugins(); } new PluginToolWindowManager().init(); } private static void installHelloWorldPlugins() { ExamplePluginInstaller.Listener loggingListener = (e, pluginPath) -> LOG.warn("Failed to install plugin: " + pluginPath, e); new ExamplePluginInstaller(PLUGIN_EXAMPLES_PATH + "helloWorld/", asList("plugin.groovy")).installPlugin(loggingListener); new ExamplePluginInstaller(PLUGIN_EXAMPLES_PATH + "registerAction/", asList("plugin.groovy")).installPlugin(loggingListener); new ExamplePluginInstaller(PLUGIN_EXAMPLES_PATH + "popupMenu/", asList("plugin.groovy")).installPlugin(loggingListener); } @Override public void disposeComponent() { } @Override @NotNull public String getComponentName() { return COMPONENT_NAME; } }