Skip to content

Instantly share code, notes, and snippets.

@exupero
Created April 17, 2019 15:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save exupero/32abdd4e881d40bb4b16afa7f39a7a7c to your computer and use it in GitHub Desktop.
Save exupero/32abdd4e881d40bb4b16afa7f39a7a7c to your computer and use it in GitHub Desktop.
Launching a Clojure nREPL from IntelliJ

Adding a Clojure nREPL to an IntelliJ plugin

  • Add the JARs for Clojure and tools.nrepl. I had to use clojure-1.6.0 and tools.nrepl-0.2.6, but it may be possible to use more recent versions.
  • IntellijNrepl.java has the bulk of the necessary logic.
  • Add the action defined in IntellijNrepl.java to the plugin.xml file.
  • Install the plugin, relaunch IntelliJ, and jack in! You can now script IntelliJ with Clojure. Refer to the open source Community Edition of IntelliJ for clues on whatever you're trying to do.
package squij;
import clojure.lang.*;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.io.StringWriter;
public class LaunchNrepl extends AnAction {
private static final Logger LOG = Logger.getLogger(LaunchNrepl.class);
private static int port = (int) Math.round(50000 + Math.random() * 10000);
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
boolean pushed = false;
try {
ClassLoader loader = LaunchNrepl.class.getClassLoader();
Thread.currentThread().setContextClassLoader(loader);
StringWriter writer = new StringWriter();
Class.forName("clojure.lang.RT");
Var.pushThreadBindings(RT.map(clojure.lang.Compiler.LOADER, loader, RT.var("clojure.core", "*warn-on-reflection*"), true, RT.ERR, writer));
pushed = true;
IFn require = RT.var("clojure.core", "require");
require.invoke(Symbol.intern("clojure.tools.nrepl.server"));
IFn startServer = RT.var("clojure.tools.nrepl.server", "start-server");
startServer.invoke(Keyword.intern("port"), port);
System.out.println("started nREPL on port " + LaunchNrepl.port);
String result = writer.toString();
if (result.length() > 0) {
LOG.error("Reflection warnings:\n"+result);
}
} catch (Exception e) {
Messages.showMessageDialog(event.getProject(), e.getMessage(), "Error",
Messages.getErrorIcon());
return;
} finally {
if (pushed) Var.popThreadBindings();
Thread.currentThread().setContextClassLoader(oldLoader);
}
LaunchNrepl.showPort(event.getProject());
}
static void showPort(Project project) {
Messages.showMessageDialog(project, "nREPL launched on port " + LaunchNrepl.port, "nREPL launched",
Messages.getInformationIcon());
}
}
<idea-plugin>
<id>squij</id>
<name>Squij</name>
<version>1.0</version>
<description><![CDATA[
]]></description>
<change-notes><![CDATA[
]]>
</change-notes>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
<idea-version since-build="173.0"/>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->
<extensions defaultExtensionNs="com.intellij">
</extensions>
<actions>
<action id="nrepl" class="squij.LaunchNrepl" text="Launch nREPL" />
</actions>
</idea-plugin>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment