Skip to content

Instantly share code, notes, and snippets.

Last active January 11, 2023 13:16
What would you like to do?
IDE Scripting

Here are my attempts to script an IntelliJ-based IDE using javax.script.* API (ex-JSR-223).

The list of available scripting languages and engines:

  1. Groovy - built-in, via Groovy jars and <app>/lib/groovy-jsr223-xxx.jar
  2. JavaScript (Nashorn) - built-in, via Java Runtime <app>/jbr/... (deprecated and will be removed soon)
  3. JavaScript (GraalJS) -
  4. JPython -
  5. JRuby -
  6. Clojure -
  7. Kotlin - (bundled in IntelliJ IDEA)

Open IDE Scripting Console, type a statement, hit Ctrl-Enter to execute the current line or selection.

.profile.language-extension file in the same directory will be executed along with it if present.

CAUTION: Scripts exert full control over IDE VM so any damage is possible, e.g. System.exit(0).

(write #(. (document (firstFile "")) (setText "Rollback!")))
(pool #(do (Thread/sleep 5000) (. IDE print "123")))
(action "TryMe!" "alt shift P" (fn [e] (let
[e (event-data e 'PSI_ELEMENT)
res (and (-> e nil? not) (.canFindUsages finds e)) ]
(. IDE print (str "can find usages of '" e "'? " (if res "Yes!" "Nope"))))))
(timer "bump" 500 #(. IDE (print (str (System/currentTimeMillis) ": bump!"))))
write { "".firstFile().document().setText("Rollback!") }
pool { Thread.sleep(5000); IDE.print("123") }
action("TryMe!", "alt shift P") { e->
e = PSI_ELEMENT.from(e)
res = e != null && finds.canFindUsages(e)
IDE.print("can find usages of '" + e +"'? " + (res ? "Yes!" : "Nope"))
timer("bump", 500) {
IDE.print(System.currentTimeMillis() + ": bump!")
def ep = "com.intellij.iconLayerProvider".ep()
ep.registerExtension(new com.intellij.ide.IconLayerProvider() {
String getLayerDescription() { return "123" }
javax.swing.Icon getLayerIcon(com.intellij.openapi.util.Iconable element, boolean isLocked) {
"config".equals(element.getName()) ? com.intellij.icons.AllIcons.Nodes.FinalMark : null
(doseq [x (list (.project IDE) (.application IDE))]
(let [pico (.getPicoContainer x)
field (.getDeclaredField (.getClass pico) "componentKeyToAdapterCache")
services (do (.setAccessible field true) (.get field pico))]
(doseq [key (filter #(instance? String %) (.keySet services))]
(let [match (re-find (re-matcher #"[\.]([^\.]+?)(Service|Manager|Helper|Factory)?$" key))
groups (rest match)
singular (empty? (rest groups))
words (seq (com.intellij.psi.codeStyle.NameUtil/nameToWords (first groups)))
short0 (clojure.string/join (flatten (list (.toLowerCase (first words)) (rest words))))
shortName (if (false? singular) (com.intellij.openapi.util.text.StringUtil/pluralize short0) short0)]
(intern *ns* (symbol shortName) (.getComponentInstance pico key))
(catch Exception e ()))
(defn file [^String x] (.findFileByUrl virtualFiles (com.intellij.openapi.vfs.VfsUtil/pathToUrl x)))
(defn file2 [^String x] (first (filter #( = x (.getName %)) (.getOpenFiles fileEditors))))
(defn all-scope [] ( (.project IDE)))
(defn findPsi [^String x] ( (.project IDE), x, (all-scope)))
(defn findFile [^String x] ( (.project IDE) x, (all-scope)) )
(defn firstPsi [^String x] (first (findPsi x)))
(defn firstFile [^String x] (first (findFile x)))
(defn ep [^String x] (.getExtensionPoint (com.intellij.openapi.extensions.Extensions/getArea nil) x))
(defn psi [x] (.findFile psis x))
(defn document [x] (.getDocument fileDocuments x))
(defn editor ([x] (.getEditor (.getSelectedEditor fileEditors x)))
([] (try (.. windows (getFocusedComponent (. IDE project)) (getEditor)) (catch Exception e ()) )) )
(defn #^Runnable runnable [x] (proxy [Runnable] [] (run [] (x))))
(defn write [x] (.. IDE application (runWriteAction (runnable x))))
(defn read [x] (.. IDE application (runReadAction (runnable x))))
(defn pool [x] (.. IDE application (executeOnPooledThread (runnable x))))
(defn swing [x] (com.intellij.util.ui.UIUtil/invokeLaterIfNeeded (runnable x)))
(defn dumpe [e] (.print IDE (str e (clojure.string/replace (str (seq (.getStackTrace e))) #"#<StackTraceElement " "\n at "))))
(defn safe [x] (try (x) (catch Exception e (dumpe e))))
(defn data-key [e] (let [c ['com.intellij.openapi.actionSystem.LangDataKeys]]
(some #(try (. (. (Class/forName (str %)) (getField (str e))) (get nil)) (catch Exception e nil) ) c)))
(defn event-data [e key] (.getData (data-key key) (.getDataContext e)))
(defn action [name & x]
(.unregisterAction actions name)
(.. keymaps getActiveKeymap (removeAllActionShortcuts name))
(if (nil? (second x)) nil
(let [[shortcut, perform] x]
(. actions (registerAction name (proxy [com.intellij.openapi.actionSystem.AnAction] [name, name, nil]
(actionPerformed [e] (perform e)) )))
(if (= shortcut nil) nil
(.. keymaps (getActiveKeymap) (addShortcut name
(new com.intellij.openapi.actionSystem.KeyboardShortcut (javax.swing.KeyStroke/getKeyStroke shortcut) nil)) )
(defn dispose [x] (let [t (if (instance? com.intellij.openapi.Disposable x) (x) (.put IDE x nil))]
(if (nil? t) nil (com.intellij.openapi.util.Disposer/dispose t)) ) )
(defn timer [name & x] (do
(dispose name)
(if (nil? (second x)) nil
(let [[delay, perform] x
h (new com.intellij.util.Alarm (.project IDE))
r (runnable (fn time_fun [] (do (perform) (. h (addRequest (runnable time_fun) delay)) ) ))]
(. h (addRequest r delay))
(. IDE (put name h))
) ))
import static com.intellij.openapi.actionSystem.LangDataKeys.*
metaClass.propertyMissing = {name ->
switch (name) {
case "application": return com.intellij.openapi.application.ApplicationManager.getApplication()
case "project": return com.intellij.openapi.project.ProjectManager.getInstance().getOpenProjects()[0]
case "INDEX": return
case "GSS": return
case "EXT": return com.intellij.openapi.extensions.Extensions
def variants = []
for (t in [IDE.project, IDE.application]) {
for (obj in t.picoContainer.componentKeyToAdapterCache.keySet()) {
def key = obj instanceof String ? obj : obj instanceof Class ? obj.getName() : null
if (key == null) continue
def match = key =~ /[\.]([^\.]+?)(Service|Manager|Helper|Factory)?$/
def groups = match.size()? match[0][1..-1] : [key, null]
def singular = groups[1..-1][0] == null
def words = com.intellij.psi.codeStyle.NameUtil.nameToWords(groups[0])
def short0 = [words[0].toLowerCase(), words.length==1? "" : words[1..-1]].flatten().join()
def shortName = singular? short0 : com.intellij.openapi.util.text.StringUtil.pluralize(short0)
if (shortName.equals(name)) return t.picoContainer.getComponentInstance(obj);
if (com.intellij.openapi.util.text.StringUtil.containsIgnoreCase(groups[0], name)) variants.add(shortName)
throw new MissingPropertyException("Service or Component '$name' not found. Variants: $variants")
String.class.metaClass.file = {-> virtualFiles.findFileByUrl(com.intellij.openapi.vfs.VfsUtil.pathToUrl(delegate))}
String.class.metaClass.file2 = {-> def name = delegate; fileEditors.getOpenFiles().find {file -> file.getName().equals(name) }}
String.class.metaClass.findPsi = {-> INDEX.getFilesByName(project, delegate, GSS.allScope(project)) }
String.class.metaClass.findFile = {-> INDEX.getVirtualFilesByName(project, delegate, GSS.allScope(project)) }
String.class.metaClass.firstPsi = {-> delegate.findPsi()[0] }
String.class.metaClass.firstFile = {-> delegate.findFile()[0] }
String.class.metaClass.ep = {-> EXT.getArea(null).getExtensionPoint(delegate) }
com.intellij.openapi.actionSystem.DataKey.class.metaClass.from = {e -> delegate.getData(e.getDataContext()) }
def virtualFileMetaClass = com.intellij.openapi.vfs.VirtualFile.class.metaClass
virtualFileMetaClass.psi = {-> psiManager.findFile(delegate)}
virtualFileMetaClass.document = {-> fileDocuments.getDocument(delegate)}
virtualFileMetaClass.editor = {-> fileEditors.getSelectedEditor(delegate)?.getEditor()}
def psiMetaClass = com.intellij.psi.PsiElement.class.metaClass
psiMetaClass.document = {-> psiDocuments.getDocument(delegate)}
psiMetaClass.file = {-> delegate.getContainingFile().getVirtualFile()}
psiMetaClass.editor = {-> fileEditors.getSelectedEditor(delegate.file())?.getEditor()}
write = { c -> application.runWriteAction(c)}
read = { c -> application.runReadAction(c)}
pool = { c -> application.executeOnPooledThread(c)}
swing = { c -> com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(c)}
action = { name, shortcut = null, perform = null ->
if (perform == null) return
actions.registerAction(name, new com.intellij.openapi.actionSystem.AnAction(name, name, null) {
void actionPerformed(com.intellij.openapi.actionSystem.AnActionEvent e) {
} })
if (shortcut != null) {
keymaps.getActiveKeymap().addShortcut(name, new com.intellij.openapi.actionSystem.KeyboardShortcut(
javax.swing.KeyStroke.getKeyStroke(shortcut), null))
timer = {name, delay = 1, perform = null ->
if (perform == null) return
def h = new com.intellij.util.Alarm(project)
def r = new Runnable() { public void run() {perform(); h.addRequest(this, delay); }}
h.addRequest(r, delay)
IDE.put(name, h)
dispose = { h ->
t = h instanceof com.intellij.openapi.Disposable ? h : IDE.put(h, null)
if (t != null) com.intellij.openapi.util.Disposer.dispose(t)
editor = { -> try {windows.getFocusedComponent(project).getEditor()} catch(e){}}
import com.intellij.openapi.ui.Messages
val b = bindings as Map<String, Any>
val IDE = b["IDE"] as com.intellij.ide.script.IDE
val name = System.getProperty("")
Messages.showInfoMessage("Hi, $name",
import com.intellij.notification.*
// Change the `pattern` and the `replacement` as needed
def pattern = ~/(.*)XXX\.(.*)/
def replacement = "\$1_xxx_.\$2"
// `Rename Multiple Files` action:
// 1. select some files in `Project` view
// 2. invoke it via Search Everywhere/Goto Action or a shortcut
// 3. see `Event Log` for the report
// Note: `action`, `write`, etc. are defined in `.profile.groovy`
action("Rename Multiple Files", "control alt R") { event ->
def files = VIRTUAL_FILE_ARRAY.getData(event.getDataContext())
def report = new StringBuilder()
for (def file : files) {
def curName =
def matcher = curName =~ pattern
if (!matcher.matches()) {
report.append("$curName skipped\n")
else {
def newName = matcher.replaceFirst(replacement)
report.append("$curName -> $newName\n")
write {
file.rename(this, newName)
Notifications.Bus.notify(new Notification(
"${event.presentation.text}: ${files ? files.length : 0} file(s) processed",
report.toString(), NotificationType.INFORMATION))
Copy link

glockbender commented Mar 4, 2019

Kotlin examples wanted!

Kotlin example with output

import com.intellij.openapi.ui.Messages.showInfoMessage
var sum: Long = 0L
val arr = "35907 77134 453661 175096 23673 29350"
    .split(" ")
arr.forEach { sum+=it.length }

showInfoMessage((sum.toFloat() / arr.size).toString(), "test")

Copy link

Getting current project and editor in Kotlin:

val dataContext = DataManager.getInstance().dataContextFromFocusAsync.blockingGet(1000)!!
val project = CommonDataKeys.PROJECT.getData(dataContext)!!
val editor = BaseCodeInsightAction.getInjectedEditor(project, CommonDataKeys.EDITOR.getData(dataContext))!!

Copy link

smaudet commented Jun 4, 2019

IDE.print is no longer functional. It does not display to the run toolwindow and no console appears. Additionally the static method is private and constructing the IDE instance manually yields no results.

Fallback, although not pretty, is to open up File I/O directly and e.g. tail -f <yourfile>

Copy link

smaudet commented Jun 4, 2019

I will also say from what I could tell it is not possible to gain console output from the scripting console into the run dialog, not without either:
a) Discovering the runtime console during run and injecting something into it
b) Collecting and dumping the console output internal to the script
c) Building a new Tool Window and manually outputting console output to it.

Copy link

ice1000 commented Jul 24, 2019

Kotlin example request

Copy link

Anybody know if there's a way to run an IDE script from the command line somehow? I'd like to trigger a script I've written from an external script.

Copy link

mgroth0 commented Jun 15, 2020

I'd like to echo @shawkinaw 's question. Also, can we script the embedded terminal? It would be great if I could automatically open a terminal tab, name it, and run a command

Copy link

gregsh commented Jun 16, 2020

Also, can we script the embedded terminal?

An IDE scripting console script, like the following:

com.intellij.openapi.components.ServiceManager.getService(IDE.project,"ls", "/", "title")

UPD Updated to address @skissane note below.

Copy link

gregsh commented Jun 16, 2020

I've just added a Kotlin *.kts example to show the external bindings access which is tricky at the moment,
and updated IDE with the list of available languages/engines.

Copy link

@gregsh I thought @shawkinaw's question was, how do I use the IntelliJ command line interface to run an IDE Console script from a shell script? You seem to be answering the opposite question of how I run a shell script from the IDE Console script?

Copy link

gregsh commented Jul 13, 2020

Anybody know if there's a way to run an IDE script from the command line somehow?

No such thing now, but a simple plugin with a com.intellij.appStarter extension shall do the trick.

UPD I'll do that, watch for IDEA-245847.

Copy link

sesm commented Oct 27, 2020

I've tried .project.clj but I get RuntimeException: Unable to resolve symbol: fileEditors in this context.
I guess this symbol should be added to namespace in the first loop. I checked which symbols are added and the closest I see is fileEditorProviders. Should I use that one to get fileEditors somehow?

Copy link

gregsh commented Feb 5, 2021

Anybody know if there's a way to run an IDE script from the command line somehow?

IDEA-245847 is now included in 2021 EAP.
Usage: idea ideScript <files>...

Copy link

mvastola commented Mar 3, 2021

Any starter code for ruby? I.e. files we need to require

Copy link

mgroth0 commented Mar 11, 2021


I want to get the editor object you referenced but my code is blocking

val dataContext = getInstance().dataContextFromFocusAsync.blockingGet(1000)!!
val project = CommonDataKeys.PROJECT.getData(dataContext)!!
val editor_data = CommonDataKeys.EDITOR.getData(dataContext)
val editor = BaseCodeInsightAction.getInjectedEditor(project, editor_data)!!



I never see here2, nor any exceptions

Copy link


Just checked, works without a problem on IDEA 2020.3

val dataContext = DataManager.getInstance().dataContextFromFocusAsync.blockingGet(10)!!
val project = CommonDataKeys.PROJECT.getData(dataContext)!!
val editor = BaseCodeInsightAction.getInjectedEditor(project, CommonDataKeys.EDITOR.getData(dataContext))!!
val ctx = SearchContext(project)

Copy link

mgroth0 commented Mar 11, 2021

I've discovered part of my problem. If I run this code while focused on the editor:

val dataContext = DataManager.getInstance().dataContextFromFocusAsync.blockingGet(10)!!

I get this output:

component=EditorComponent file=file:///Users/matt/Library/Application Support/JetBrains/IntelliJIdea2020.3/consoles/ide/ide-scripting.kts

But for my use case, I need this to work when I'm not focused on the editor. So I'm actually focused on the embedded terminal. So as you might expect from the name of the function dataContextFromFocusAsync, I'm getting:


Copy link

mgroth0 commented Mar 11, 2021

To get the editor, I use

val projectMan = com.intellij.openapi.project.ProjectManager.getInstance()
val projects = projectMan.openProjects
val project = projects.first { == "projectname" }
val editor = FileEditorManager.getInstance(project)

Then I can use editor.openFile(f, true) to open files. This works perfectly. However, I can not scroll to a specific line. Then I came across the code you posted @mnowotnik, and found that the editor class you demonstrated does have a scrollingModel.scrollTo method that works well. However, as I stated in my previous message, I just can't seem to get a reference to the com.intellij.openapi.editor.Editor (for a specific file, in a specific project, and without being focused on any particular component) yet, in the same way I could get a FileEditorManager instance.

Copy link

mgroth0 commented Mar 11, 2021

Figured it out!
Using the project and editor values from my previous comment:

BaseCodeInsightAction.getInjectedEditor(project, editor.selectedTextEditor).scrollingModel.scrollTo(LogicalPosition(50,0), ScrollType.CENTER)

Copy link

Copy link

I occasionally accidentally close my pinned tabs and wanted a quick and dirty way to re-pin them.
After reading this page, finally cobbled together a few lines to do just that.
Pasted below in case someone else has similar need.

import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx

val b = bindings as Map<String, Any>
val IDE = b["IDE"] as com.intellij.ide.script.IDE

val editor = FileEditorManagerEx.getInstance(IDE.project) as FileEditorManagerEx
val filesToPin = editor.openFiles.filter {"Scratch") }
filesToPin.forEach { f -> editor.currentWindow.setFilePinned(f, true) }

Copy link

@gregsh A huge thank you for IDEA-245847, I was really looking for that. I created a related PR to teach the launcher Python script how to invoke it. However, looking at the current implementation of IdeScriptStarter I can see a couple of gaps:

  • There is no way to pass arguments to scripts. If I call ideScript foo.groovy bar it tries to run bar as a second script (which it can't find) instead of passing it as an argument (somehow) to foo.groovy
  • There is zero feedback to the launcher about whether the script ran successfully. If the script doesn't exist, or contains a syntax error, the launcher still returns successfully, although the error does get logged in idea.log. If the script compiles successfully, but throws an exception during execution, you don't even get any error logged in idea.log

I can cook up some hacky workarounds to (mostly) solve the above two issues for my own purposes, but it would be great if one day there was some solution for them out of the box.

Copy link

Dear Mr Shrago:
I have installed Intellij 2021.1.3 and 2021.2.3 for testing IDE Scripting.
Samples.groovy and .profile.groovy ran well under Intellij 2021.1.3, but got the following errors under Intelli 2021.2.3:
MissingPropertyException: No such property: componentKeyToAdapterCache for class: com.intellij.openapi.project.impl.ProjectExImpl

Could you help me?
Many thanks to you!

Copy link

atorr0 commented Jun 30, 2022

@fuxin-liu , you'll need the Kotlin plugin activated in your IDE.

Copy link

AfzalivE commented Oct 5, 2022

Wrote a little script to create a text file based on the name of the current Git branch. We add changelogs this way and it was very tedious to copy over the branch name all the time.

Thanks to all of you sharing your scripts as examples!

import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.vcs.LocalFilePath
import git4idea.GitUtil

val b = bindings as Map<String, Any>
val IDE = b["IDE"] as com.intellij.ide.script.IDE


fun main() {
    val baseProjectPath = requireNotNull(IDE.project.basePath)
    val isVcs = GitUtil.isGitRoot(baseProjectPath)
    if (!isVcs) {
        IDE.print("File not under VCS")

    val repo = GitUtil.getRepositoryForFile(IDE.project, LocalFilePath(baseProjectPath, true))

    val currentBranch = requireNotNull(repo.currentBranch)
    val filename =
    val file = createChangelogFile(baseProjectPath, filename)

    val editor = FileEditorManager.getInstance(IDE.project)
    val virtualFile = requireNotNull(file.toVirtualFile(true))

    editor.openFile(virtualFile, true, true)

fun createChangelogFile(baseProjectPath: String, filename: String): File {
    val sanitizedFilename = filename.replace("/", ".").plus(".txt")
    IDE.print("Creating changelog file: $filename")
    val changelogFolder = "$baseProjectPath/changelog"
    IDE.print("In folder: $changelogFolder")
    val file = File(changelogFolder, sanitizedFilename)
    val created = file.createNewFile()
    IDE.print("File created: $created")
    return file

Copy link

Is there a way to execute ide script from console / using run configurations or something similar?

Copy link

Hi, so many examples and I'm still looking for one on how to get the filename of the currently open file in the Editor. Can someone please help me with that?

Copy link

I think I got a way to get the current file path. Can someone please help me with how to handle a "keyboard shortcut pressed" action in our kotlin script?

Copy link

@AfzalivE : Nice script, but how do you execute it quickly - (I mean other than opening the script itself and running it manually). Can you add a keyboard shortcut for the same, or add it to 'External Tools'? I tried to do that but failed.

Copy link

@piyushsoni Nice script, but how do you execute it quickly - (I mean other than opening the script itself and running it manually). Can you add a keyboard shortcut for the same, or add it to 'External Tools'? I tried to do that but failed.

Thanks. Yeah I have to open it, select all, and then cmd+enter :( Haven't explored any other way yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment