### Groovy API Example with `PluginUtil` Source: https://context7.com/dkandalov/live-plugin/llms.txt Demonstrates using the static `PluginUtil` class in Groovy plugins for registering actions, showing popups, and performing background tasks. Requires importing static methods from `liveplugin.PluginUtil`. ```groovy // plugin.groovy import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.ide.BrowserUtil import com.intellij.openapi.progress.ProgressIndicator import groovy.json.JsonSlurper import static liveplugin.PluginUtil.* // Register a Google quick-search popup action registerAction("GoogleSearchAction", "ctrl alt shift G") { AnActionEvent event -> def itemProvider = { String pattern, ProgressIndicator indicator -> def encoded = URLEncoder.encode(pattern, "UTF-8") def json = "http://suggestqueries.google.com/complete/search?client=firefox&q=$encoded".toURL().text new JsonSlurper().parseText(json)[1].toList() } showPopupSearch("Google quick search", event.project, itemProvider) { String item -> BrowserUtil.browse("http://www.google.com/search?q=${URLEncoder.encode(item, 'UTF-8')}") } } // Collect project file stats in background registerAction("FileStats", "ctrl alt S") { AnActionEvent event -> doInBackground("Collecting file stats") { runReadAction { def fileStats = com.intellij.openapi.fileTypes.FileTypeManager.instance .registeredFileTypes.inject([:]) { stats, fileType -> def scope = com.intellij.psi.search.GlobalSearchScope.projectScope(project) int count = com.intellij.psi.search.FileTypeIndex.getFiles(fileType, scope).size() if (count > 0) stats["'$fileType.defaultExtension'" ] = count stats }.sort { -it.value } show("File count by type:
" + fileStats.entrySet().join("
")) } } } if (!isIdeStartup) show("Loaded Google Search and File Stats actions") ``` -------------------------------- ### HTTP POST Request Example Source: https://github.com/dkandalov/live-plugin/blob/master/src/test/liveplugin/implementation/actions/git/recorded_traffic/post-zgOK3DbuCdeHtIwqF43Agg+vumo=/request.txt This snippet shows a sample HTTP POST request to the Live Plugin API. It includes common headers like Accept and Authorization, along with a JSON body for creating a resource. ```http POST HTTP/1.1 Accept: application/vnd.github.v3+json Authorization: Bearer dummy-token {"description":"test","public":false} ``` -------------------------------- ### Show Hello World Notification in Groovy Source: https://github.com/dkandalov/live-plugin/blob/master/README.md A simple Groovy script that displays a balloon notification popup with the text 'Hello world'. This is a basic example of using the `show` utility function. ```groovy import static liveplugin.PluginUtil.show show("Hello world") // Shows balloon notification popup with "Hello world" text ``` -------------------------------- ### Automate NullPointerException Handling Source: https://github.com/dkandalov/live-plugin/wiki/Scripting-a-macros This script demonstrates a non-linear macro that automatically stops a running process if a NullPointerException is detected in the console output. It registers a console listener, starts a run configuration, and invokes the 'Stop' action if the exception occurs. Ensure you have the necessary imports from liveplugin.PluginUtil. ```groovy import static liveplugin.PluginUtil.* if (isIdeStartup) return doInBackground { registerConsoleListener("NPEConsoleListener") { consoleText -> if (consoleText.contains("NullPointerException")) { invokeOnEDT { actionById("Stop").actionPerformed(anActionEvent()) } show("The process was stopped because of NullPointerException") unregisterConsoleListener("NPEConsoleListener") } } invokeOnEDT { executeRunConfiguration("VeryEnterpriseProjectMain", project) } } ``` -------------------------------- ### Register IDE or Project Tool Window Source: https://context7.com/dkandalov/live-plugin/llms.txt Use `registerIdeToolWindow` for global registration or `registerProjectToolWindow` for project-specific registration. Both create a persistent tool window panel in the IDE sidebar. ```kotlin // plugin.kts import javax.swing.* import liveplugin.registerIdeToolWindow import liveplugin.show registerIdeToolWindow("My Tool Window") { val panel = JPanel() val button = JButton("Click Me") button.addActionListener { show("Button clicked in project: ${project.name}") } panel.add(button) panel } if (!isIdeStartup) show("Loaded 'My Tool Window' in the right sidebar") ``` ```groovy // plugin.groovy (Groovy equivalent) import javax.swing.* import static liveplugin.PluginUtil.registerToolWindow registerToolWindow("HelloToolWindow", pluginDisposable) { def panel = new JPanel() def button = new JButton("Hello") button.addActionListener({ panel.add(new JButton("World")); panel.revalidate() } as AbstractAction) panel.add(button) panel } ``` -------------------------------- ### Create and Show Action Popup Menu Source: https://context7.com/dkandalov/live-plugin/llms.txt Use `PopupActionGroup` to construct a group of `AnAction` instances and `createPopup` to build a `ListPopup`. The popup can then be displayed in the current window. ```kotlin // plugin.kts import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.Separator import com.intellij.openapi.ui.Messages import liveplugin.* registerAction(id = "Show Actions Popup", keyStroke = "ctrl alt shift P") { event: AnActionEvent -> PopupActionGroup("Developer Actions", AnAction("Run Shell Command") { val cmd = Messages.showInputDialog("Command:", "Shell", null) if (cmd != null) show(runShellCommand(cmd).stdout) }, AnAction("Show Project Name") { e -> show("Project: ${e.project?.name}") }, Separator.getInstance(), PopupActionGroup("Open Docs", AnAction("IntelliJ SDK Docs") { openInBrowser("https://plugins.jetbrains.com/docs/intellij/") }, AnAction("LivePlugin README") { openInBrowser("https://github.com/dkandalov/live-plugin") } ) ).createPopup(event.dataContext) .showCenteredInCurrentWindow(event.project!!) } if (!isIdeStartup) show("Loaded popup — use ctrl+alt+shift+P") ``` -------------------------------- ### Navigation Helpers: `openInBrowser`, `Project.openInIdeBrowser`, `Project.openInEditor` Source: https://context7.com/dkandalov/live-plugin/llms.txt Helper functions to open URLs in the system browser, an internal IDE browser tab, or navigate to a file in the IDE editor. Ensure the project is available when calling `openInIdeBrowser` or `openInEditor`. ```kotlin // plugin.kts import liveplugin.* import com.intellij.openapi.actionSystem.AnActionEvent registerAction(id = "Open Docs", keyStroke = "ctrl alt shift D") { event: AnActionEvent -> // Open in system browser openInBrowser("https://plugins.jetbrains.com/docs/intellij/fundamentals.html") // Open in IDE's built-in browser panel event.project?.openInIdeBrowser("https://github.com/dkandalov/live-plugin", "LivePlugin Docs") // Open a file in the code editor event.project?.openInEditor("$pluginPath/plugin.kts") } if (!isIdeStartup) show("Docs actions loaded") ``` -------------------------------- ### Register Project Open Listener Source: https://context7.com/dkandalov/live-plugin/llms.txt Subscribes to project lifecycle events. The callback fires immediately for all currently open projects and then for each newly opened project. Automatically unsubscribed when the `disposable` is disposed. ```kotlin import liveplugin.registerProjectOpenListener import liveplugin.show registerProjectOpenListener(pluginDisposable) { project -> show("Project opened: ${project.name}") // Set up project-specific tooling here, e.g. register actions or tool windows scoped to this project } ``` -------------------------------- ### Console Output with `Project.showInConsole` Source: https://context7.com/dkandalov/live-plugin/llms.txt Prints messages to a dedicated Run console tab, supporting different content types like normal output and error output. The console tab is identified by a title. ```kotlin // plugin.kts import com.intellij.execution.ui.ConsoleViewContentType import liveplugin.showInConsole import liveplugin.show registerAction(id = "Run Analysis", keyStroke = "ctrl alt R") { event -> val proj = event.project ?: return@registerAction val console = proj.showInConsole("Starting analysis...\n", consoleTitle = "My Plugin") console.print("Found 42 issues\n", ConsoleViewContentType.ERROR_OUTPUT) console.print("Done.\n", ConsoleViewContentType.NORMAL_OUTPUT) } ``` -------------------------------- ### Create Repository Source: https://github.com/dkandalov/live-plugin/blob/master/src/test/liveplugin/implementation/actions/git/recorded_traffic/post-zgOK3DbuCdeHtIwqF43Agg+vumo=/request.txt This endpoint allows you to create a new repository. You can specify details such as the repository name, description, and privacy settings. ```APIDOC ## POST /user/repos ### Description Creates a new repository for the authenticated user. ### Method POST ### Endpoint /user/repos ### Request Body - **description** (string) - Required - The description of the repository. - **public** (boolean) - Required - Whether the repository is public or private. ``` -------------------------------- ### Threading Utilities: runOnEdtWithWriteLock, runWithReadLock, runLaterOnEdt Source: https://context7.com/dkandalov/live-plugin/llms.txt Helpers for IntelliJ's threading model. `runWithReadLock` safely reads PSI off-EDT. `runBackgroundTask` does heavy work in the background. `runLaterOnEdt` schedules UI updates on the EDT. `runOnEdtWithWriteLock` modifies documents synchronously on the EDT with a write lock. ```kotlin import liveplugin.* registerAction(id = "Threading Demo", keyStroke = "ctrl alt T") { event -> // Read PSI off-EDT safely val className = runWithReadLock { event.psiFile?.name ?: "unknown" } // Do heavy work in background runBackgroundTask("Processing") { _ -> Thread.sleep(500) // simulate work "Result for $className" }.thenAccept { result -> // Update UI on EDT runLaterOnEdt { show(result) } } // Synchronous EDT + write lock: modify a document runOnEdtWithWriteLock { event.document?.insertString(0, "// processed\n") } } ``` -------------------------------- ### registerIdeToolWindow / registerProjectToolWindow Source: https://context7.com/dkandalov/live-plugin/llms.txt Creates a persistent tool window panel in the IDE sidebar. `registerIdeToolWindow` registers the window for all open and future projects; `registerProjectToolWindow` registers it only in the current project. ```APIDOC ## `registerIdeToolWindow` / `registerProjectToolWindow` — Register a custom tool window ### Description Creates a persistent tool window panel in the IDE sidebar. `registerIdeToolWindow` registers the window for all open and future projects; `registerProjectToolWindow` registers it only in the current project. ### Usage ```kotlin // plugin.kts import javax.swing.* import liveplugin.registerIdeToolWindow import liveplugin.show registerIdeToolWindow("My Tool Window") { project -> val panel = JPanel() val button = JButton("Click Me") button.addActionListener { show("Button clicked in project: ${project.name}") } panel.add(button) panel } if (!isIdeStartup) show("Loaded 'My Tool Window' in the right sidebar") ``` ```groovy // plugin.groovy (Groovy equivalent) import javax.swing.* import static liveplugin.PluginUtil.registerToolWindow registerToolWindow("HelloToolWindow", pluginDisposable) { def panel = new JPanel() def button = new JButton("Hello") button.addActionListener({ panel.add(new JButton("World")); panel.revalidate() } as AbstractAction) panel.add(button) panel } ``` ``` -------------------------------- ### registerProjectOpenListener Source: https://context7.com/dkandalov/live-plugin/llms.txt Subscribes to project open and close events. The callback is triggered for all currently open projects and for each newly opened project. The listener is automatically unsubscribed when the provided `disposable` is disposed. ```APIDOC ## Project Lifecycle Events ### Description Subscribes to project lifecycle events. The callback fires immediately for all currently open projects, and then again for each newly opened project. Automatically unsubscribed when the `disposable` is disposed. ### Method `registerProjectOpenListener(disposable: Disposable, callback: (project: Project) -> Unit)` ### Parameters * **disposable** (Disposable) - Required - The disposable object that will manage the lifecycle of the listener. * **callback** (Function) - Required - A function that takes a `Project` object as input and is executed when a project is opened. ``` -------------------------------- ### Find and Invoke IntelliJ Actions Source: https://github.com/dkandalov/live-plugin/wiki/Scripting-a-macros Use this script to find actions by name or ID and then invoke them. Ensure you have the necessary imports from liveplugin.PluginUtil and liveplugin.implementation.ActionSearch. ```groovy import static liveplugin.PluginUtil.* import static liveplugin.implementation.ActionSearch.allActionIds if (isIdeStartup) return show(allActions().findAll { it.class.simpleName.contains("ToolWindow") }) show(allActionIds().findAll { it.contains("ToolWindow") }) def action = actionById("ActivateProjectToolWindow") // or ActionManager.instance.getAction("ActivateProjectToolWindow") // Once you have instance of an action, you can invoke it like this action.actionPerformed(anActionEvent()) ``` -------------------------------- ### Use functions from multiple files in plugin.kts Source: https://context7.com/dkandalov/live-plugin/llms.txt The main `plugin.kts` file can import and use functions defined in other `.kt` files, including those in sub-packages. ```kotlin import bar.bar import liveplugin.show show(foo()) // "Hello from foo" show(bar()) // "Hello from bar" ``` -------------------------------- ### Threading Utilities: runOnEdtWithWriteLock, runWithReadLock, runLaterOnEdt Source: https://context7.com/dkandalov/live-plugin/llms.txt Provides essential helpers for managing threads within IntelliJ's threading model. Safely reads PSI off-EDT, performs heavy work in the background, and updates the UI on the Event Dispatch Thread (EDT). ```APIDOC ## Threading Utilities ### Description Helpers for IntelliJ's threading model: reads require a read lock (or EDT), writes require a write lock on EDT. `runLaterOnEdt` schedules work asynchronously on the Event Dispatch Thread. ### Methods * **`runWithReadLock(task: () -> T): T`**: Executes a task under a read lock. Suitable for reading PSI or other data structures that require a read lock. * **`runOnEdtWithWriteLock(task: () -> Unit)`**: Executes a task on the EDT under a write lock. Use for modifying documents or other mutable states that require a write lock on the EDT. * **`runLaterOnEdt(task: () -> Unit)`**: Schedules a task to be executed asynchronously on the Event Dispatch Thread. ### Parameters * **task** (Function) - The code block to execute on the appropriate thread with the required lock. ``` -------------------------------- ### Define function in a subpackage bar/bar.kt Source: https://context7.com/dkandalov/live-plugin/llms.txt Functions within sub-packages are accessible by importing the package. Ensure the file is placed in the correct directory structure. ```kotlin package bar fun bar() = "Hello from bar" ``` -------------------------------- ### PopupActionGroup / ActionGroup.createPopup Source: https://context7.com/dkandalov/live-plugin/llms.txt Creates a popup menu from a group of `AnAction` instances. `PopupActionGroup` constructs the group; `createPopup` builds a `ListPopup` that can be shown in the current window. ```APIDOC ## `PopupActionGroup` / `ActionGroup.createPopup` — Show an action popup menu ### Description Creates a popup menu from a group of `AnAction` instances. `PopupActionGroup` constructs the group; `createPopup` builds a `ListPopup` that can be shown in the current window. ### Usage ```kotlin // plugin.kts import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.Separator import com.intellij.openapi.ui.Messages import liveplugin.* registerAction(id = "Show Actions Popup", keyStroke = "ctrl alt shift P") { event: AnActionEvent -> PopupActionGroup("Developer Actions", AnAction("Run Shell Command") { val cmd = Messages.showInputDialog("Command:", "Shell", null) if (cmd != null) show(runShellCommand(cmd).stdout) }, AnAction("Show Project Name") { e -> show("Project: ${e.project?.name}") }, Separator.getInstance(), PopupActionGroup("Open Docs", AnAction("IntelliJ SDK Docs") { openInBrowser("https://plugins.jetbrains.com/docs/intellij/") }, AnAction("LivePlugin README") { openInBrowser("https://github.com/dkandalov/live-plugin") } ) ).createPopup(event.dataContext) .showCenteredInCurrentWindow(event.project!!) } if (!isIdeStartup) show("Loaded popup — use ctrl+alt+shift+P") ``` ``` -------------------------------- ### Clone IntelliJ Community Repository Source: https://github.com/dkandalov/live-plugin/blob/master/IntellijApiCheatSheet.md Clone the IntelliJ Community Edition source code from GitHub to explore the API in detail. ```bash git clone https://github.com/JetBrains/intellij-community.git ``` -------------------------------- ### Run Background Task with Progress Indicator Source: https://context7.com/dkandalov/live-plugin/llms.txt Executes a task on a background thread, showing a progress indicator in the IDE status bar. The callback runs off-EDT; use `runLaterOnEdt` to update the UI. ```kotlin import liveplugin.runBackgroundTask import liveplugin.show import liveplugin.runLaterOnEdt registerAction(id = "Analyze Project Files", keyStroke = "ctrl alt A") { runBackgroundTask(taskTitle = "Counting project files", canBeCancelledInUI = true) { indicator -> indicator.text = "Scanning..." val path = java.io.File(project?.basePath ?: return@runBackgroundTask 0) val count = path.walkTopDown() .onEach { indicator.checkCanceled() } .count { it.extension == "kt" } count }.thenAccept { count -> runLaterOnEdt { show("Found $count Kotlin files") } } } if (!isIdeStartup) show("Loaded background task action") ``` -------------------------------- ### Define helper function in utils.kt Source: https://context7.com/dkandalov/live-plugin/llms.txt Helper functions can be defined in separate `.kt` files and are accessible from `plugin.kts`. ```kotlin fun foo() = "Hello from foo" ``` -------------------------------- ### Execute Shell Commands or Scripts Source: https://context7.com/dkandalov/live-plugin/llms.txt Use `runShellCommand` for external commands or `runShellScript` for inline scripts. Both return a `CommandResult` object containing stdout, stderr, and exit code. Handle potential errors by checking `exitCode`. ```kotlin // plugin.kts import liveplugin.runShellCommand import liveplugin.runShellScript import liveplugin.show registerAction(id = "Git Log Summary", keyStroke = "ctrl alt G") { val result = runShellCommand("git", "-C", project?.basePath ?: ".", "log", "--oneline", "-10") if (result.exitCode == 0) { show(result.stdout.replace("\n", "
")) } else { show("Error: ${result.stderr}", title = "Git Error") } } registerAction(id = "Count Source Files", keyStroke = "ctrl alt F") { val result = runShellScript(""" find ${project?.basePath} -name "*.kt" | wc -l """.trimIndent()) show("Kotlin files: ${result.stdout.trim()}") } if (!isIdeStartup) show("Git and file actions loaded") ``` -------------------------------- ### Display Balloon Notifications with `show` Source: https://context7.com/dkandalov/live-plugin/llms.txt Use the `show` function to display popup notifications in the IDE. It supports plain text, HTML messages, custom titles, notification types, and group IDs. Implicit script variables like `isIdeStartup`, `project`, and `pluginPath` can be included in messages. ```kotlin // plugin.kts import liveplugin.show import com.intellij.notification.NotificationType // Simple message show("Hello world") // With HTML and title show( message = "Build succeeded in 3.2s", title = "Build Result", notificationType = NotificationType.INFORMATION, groupDisplayId = "Live Plugin" ) // Show implicit script variables show("isIdeStartup: $isIdeStartup") show("project: ${project?.name}") show("pluginPath: $pluginPath") ``` -------------------------------- ### runBackgroundTask Source: https://context7.com/dkandalov/live-plugin/llms.txt Executes a task on a background thread, displaying a progress indicator in the IDE status bar. The task can be cancelled via the UI. Returns a CompletableFuture, and UI updates should be performed using `runLaterOnEdt`. ```APIDOC ## runBackgroundTask ### Description Executes a task on a background thread with a progress indicator shown in the IDE status bar. Returns a `CompletableFuture`. The callback runs off-EDT; update UI inside `runLaterOnEdt`. ### Method `runBackgroundTask(taskTitle: String, canBeCancelledInUI: Boolean = false, task: (indicator: ProgressIndicator) -> T): CompletableFuture` ### Parameters * **taskTitle** (String) - Required - The title to display for the background task. * **canBeCancelledInUI** (Boolean) - Optional - Whether the task can be cancelled by the user through the UI. Defaults to false. * **task** (Function) - Required - The background task to execute. It receives a `ProgressIndicator` and should return a result of type `T`. ### Response * **CompletableFuture** - A future that will complete with the result of the background task. ``` -------------------------------- ### Modify Editor Document with Undo/Redo Support Source: https://context7.com/dkandalov/live-plugin/llms.txt Wraps document modifications in a CommandProcessor command for undo/redo support. Must be called from within an action running on EDT. Use for inserting, replacing, or deleting text. ```kotlin import com.intellij.openapi.actionSystem.AnActionEvent import liveplugin.* registerAction(id = "Insert Hello World", keyStroke = "ctrl shift W") { event: AnActionEvent -> val project = event.project ?: return@registerAction val editor = event.editor ?: return@registerAction editor.document.executeCommand(project, description = "Insert Hello World") { // 'this' is the Document inside the lambda insertString(editor.caretModel.offset, "/* Hello world */") } } if (!isIdeStartup) show("Loaded 'Insert Hello World' — use ctrl+shift+W") ``` -------------------------------- ### Reorder New Kotlin File Action in IntelliJ IDEA Source: https://github.com/dkandalov/live-plugin/blob/master/src/test/liveplugin/implementation/actions/git/recorded_traffic/d51de2311dfd92dfa56feb3e3f9f96a6/get-PBpuSBYgsBooJLVO2y4HQ2xv0Xs=/response.txt Use this Kotlin code to reorder the 'New Kotlin File' action to the first position in the 'New' action group. This requires access to the ActionManager and specific action IDs. ```Kotlin import com.intellij.openapi.actionSystem.* val newElementActionGroup = ActionManager.getInstance().getAction(IdeActions.GROUP_NEW) as DefaultActionGroup val newKotlinFileAction = ActionManager.getInstance().getAction("Kotlin.NewFile") as AnAction newElementActionGroup.remove(newKotlinFileAction) newElementActionGroup.add(newKotlinFileAction, Constraints.FIRST) ``` -------------------------------- ### registerWidget Source: https://context7.com/dkandalov/live-plugin/llms.txt Adds a custom widget to the IDE status bar. Accepts a `StatusBarWidget.WidgetPresentation` (e.g. `TextPresentation`) and is automatically removed when `pluginDisposable` is disposed. ```APIDOC ## `registerWidget` — Register a status bar widget ### Description Adds a custom widget to the IDE status bar. Accepts a `StatusBarWidget.WidgetPresentation` (e.g. `TextPresentation`) and is automatically removed when `pluginDisposable` is disposed. ### Usage ```kotlin // plugin.kts import com.intellij.openapi.wm.StatusBarWidget import liveplugin.registerWidget import liveplugin.show import java.awt.Component var counter = 0 registerWidget( widgetId = "MyCounterWidget", disposable = pluginDisposable, presentation = object : StatusBarWidget.TextPresentation { override fun getText() = "Calls: $counter" override fun getAlignment() = Component.CENTER_ALIGNMENT override fun getTooltipText() = "Number of registered actions called" override fun getClickConsumer() = null } ) registerAction(id = "Increment Counter", keyStroke = "ctrl alt I") { counter++ // Call updateWidget("MyCounterWidget") to refresh display (Groovy API) show("Counter is now $counter") } if (!isIdeStartup) show("Loaded counter widget and action") ``` ``` -------------------------------- ### Groovy Script for Reloading Java Plugins with Liveplugin Source: https://github.com/dkandalov/live-plugin/wiki/Liveplugin-as-an-entry-point-for-standard-plugins This Groovy script is used with Liveplugin to manage the lifecycle of a Java-based plugin. It ensures proper disposal of the previous plugin instance before initializing a new one. The `add-to-classpath` directive is crucial for including the compiled plugin JAR. ```groovy import net.vektah.codeglance.CodeGlancePlugin import static liveplugin.PluginUtil.changeGlobalVar import static liveplugin.PluginUtil.show // The path below should point to the jar file generated by IDEA // add-to-classpath $HOME/Library/Application Support/IntelliJIdea13/live-plugins/CodeGlance/classes/artifacts/CodeGlance_jar/*.jar if (isIdeStartup) return changeGlobalVar("codeGlance") { previousPlugin -> if (previousPlugin != null) { previousPlugin.disposeComponent() show("Disposed previous CodeGlancePlugin") } def plugin = new CodeGlancePlugin(project) plugin.initComponent() plugin } show("Reloaded CodeGlancePlugin") ``` -------------------------------- ### Register Keyboard-Triggered Actions with `registerAction` Source: https://context7.com/dkandalov/live-plugin/llms.txt Register custom IDE actions that can be triggered by keyboard shortcuts. Actions are automatically unregistered on plugin reload. Supports lambda-based actions and AnAction subclasses, with options for shortcuts, menu group placement, and dynamic enablement via `update()`. ```kotlin // plugin.kts import com.intellij.openapi.actionSystem.AnActionEvent import liveplugin.* // Lambda form — simplest usage registerAction(id = "Insert New Line Above", keyStroke = "ctrl alt shift ENTER") { event: AnActionEvent -> val project = event.project ?: return@registerAction val editor = event.editor ?: return@registerAction editor.document.executeCommand(project, "Insert New Line Above") { val lineStart = getLineStartOffset(editor.caretModel.logicalPosition.line) insertString(lineStart, "\n") editor.caretModel.moveToOffset(editor.caretModel.offset + 1) } } // AnAction subclass form — allows implementing update() for dynamic enablement import com.intellij.openapi.project.DumbAware class ShowProjectPathAction : AnAction("Show Project Path"), DumbAware { override fun actionPerformed(event: AnActionEvent) = show("Project path: ${event.project?.basePath}") } registerAction(id = "Show Project Path", keyStroke = "ctrl shift H", action = ShowProjectPathAction()) // Add action to a menu group (e.g., Tools menu) registerAction( id = "My Tools Action", keyStroke = "ctrl alt M", actionGroupId = ActionGroupIds.Menu.Tools ) { show("Running from Tools menu!") } if (!isIdeStartup) show("Actions loaded") ``` -------------------------------- ### Registering Custom Console Input Filters Source: https://github.com/dkandalov/live-plugin/wiki/Console-filtering This Groovy script registers custom input filters to modify console output. It defines filters to 'beautify' exceptions and hide lines containing 'INFO - '. The filters are then registered via the `INPUT_FILTER_PROVIDERS` extension point. ```groovy import com.intellij.execution.filters.ConsoleInputFilterProvider import com.intellij.execution.filters.InputFilter import com.intellij.execution.ui.ConsoleViewContentType as ContentType import com.intellij.openapi.project.Project import com.intellij.openapi.util.Pair import org.jetbrains.annotations.NotNull import static liveplugin.PluginUtil.* import com.intellij.openapi.extensions.Extensions import static com.intellij.execution.filters.ConsoleInputFilterProvider.* import static com.intellij.execution.ui.ConsoleViewContentType.NORMAL_OUTPUT if (isIdeStartup) return def beautifyExceptions() { new InputFilter() { @Override List> applyFilter(String consoleText, ContentType contentType) { if (!consoleText.contains("Exception")) null else [new Pair(consoleText.replace("Exception", "Usualness"), contentType)] } } } def linesWithout(String text) { new InputFilter() { @Override List> applyFilter(String consoleText, ContentType contentType) { consoleText.contains(text) ? [] : null } } } def createInputFilterProvider() { new ConsoleInputFilterProvider() { @Override InputFilter[] getDefaultFilters(@NotNull Project project) { [beautifyExceptions(), linesWithout("INFO - ")] } } } def extensionPoint = Extensions.rootArea.getExtensionPoint(INPUT_FILTER_PROVIDERS) def inputFilterProvider = changeGlobalVar("myConsoleFilter") { if (lastInputFilterProvider != null && extensionPoint.hasExtension(lastInputFilterProvider)) { extensionPoint.unregisterExtension(lastInputFilterProvider) } createInputFilterProvider() } extensionPoint.registerExtension(inputFilterProvider) def console = showInConsole("-----\\n", project) console.print("INFO - which will be invisible\\n", NORMAL_OUTPUT) console.print("ERROR - beautified java.lang.NullPointerException\\n", NORMAL_OUTPUT) console.print("-----\\n", NORMAL_OUTPUT) ``` -------------------------------- ### Register Google Auto-Complete Contributor in Kotlin Source: https://github.com/dkandalov/live-plugin/wiki/Google-auto-complete Registers a custom auto-completion contributor for Kotlin language and basic completion type. It fetches Google suggestions based on the current prefix and adds them to the result set. Ensure the plugin is not running during IDE startup to avoid issues. ```kotlin import com.google.gson.Gson import com.google.gson.JsonArray import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.completion.CompletionType.BASIC import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.lang.Language import com.intellij.lang.LanguageExtension import com.intellij.openapi.Disposable import com.intellij.patterns.ElementPattern import com.intellij.patterns.PlatformPatterns.psiElement import com.intellij.psi.PsiElement import com.intellij.util.ProcessingContext import liveplugin.PluginUtil.show import liveplugin.getStaticField import java.net.URL import java.net.URLEncoder if (!isIdeStartup) { val language = Language.findLanguageByID("kotlin")!! registerCompletionContributor(language, BASIC, psiElement(), pluginDisposable) { _, _, result -> result.addAllElements( findSuggestionsFor(result.prefixMatcher.prefix) .map { LookupElementBuilder.create(it.toCamelCase()) } ) } show("Loaded auto-completion contributor") } fun registerCompletionContributor( language: Language, completionType: CompletionType, elementPattern: ElementPattern, disposable: Disposable, callback: (CompletionParameters, ProcessingContext, CompletionResultSet) -> Unit ) { val languageExtension = CompletionContributor::class.java.getStaticField>("INSTANCE") // languageExtension.clearCache() val contributor = object : CompletionContributor() {} contributor.extend(completionType, elementPattern, object : CompletionProvider() { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { callback(parameters, context, result) } }) languageExtension.addExplicitExtension(language, contributor, disposable) } val gson = Gson() fun findSuggestionsFor(text: String): List { val encodedText = URLEncoder.encode(text, "UTF-8") val json = URL("https://suggestqueries.google.com/complete/search?client=firefox&q=$encodedText").readText() return (gson.fromJson(json, JsonArray::class.java).get(1) as JsonArray).map { it.asString } .filter { !it.equals(text, ignoreCase = true) } } fun String.toCamelCase() = split(" ").joinToString("") { word -> word.replaceFirstChar { it.titlecase() } } ``` -------------------------------- ### registerConsoleFilter / registerConsoleListener Source: https://context7.com/dkandalov/live-plugin/llms.txt Allows interception and manipulation of console output in real time. `registerConsoleFilter` can transform or suppress lines, while `registerConsoleListener` observes lines without modification. ```APIDOC ## Console Output Interception ### Description Intercepts console output in real time. `registerConsoleFilter` can transform or suppress console lines; `registerConsoleListener` observes lines without modifying them. ### Methods * **`registerConsoleFilter(disposable: Disposable, filter: (text: String) -> String?)`**: Registers a filter for console output. The filter function receives a line of text and can return a modified string, an empty string to suppress the line, or null to leave it unchanged. * **`registerConsoleListener(disposable: Disposable, listener: (text: String) -> Unit)`**: Registers a listener for console output. The listener function receives a line of text and can perform actions based on it, but cannot modify the output. ### Parameters * **disposable** (Disposable) - Required - The disposable object that will manage the lifecycle of the listener/filter. * **filter** (Function) - Required for `registerConsoleFilter` - A function that takes console output text and returns a modified string, an empty string, or null. * **listener** (Function) - Required for `registerConsoleListener` - A function that takes console output text and performs an action. ``` -------------------------------- ### Register 'Insert New Line Above' Action in Kotlin Source: https://github.com/dkandalov/live-plugin/blob/master/README.md A Kotlin script that registers a custom action to insert a new line above the current cursor position. It utilizes LivePlugin's `registerAction` and `executeCommand` utilities. The action is assigned a hotkey and provides feedback upon successful loading. ```kotlin import com.intellij.openapi.actionSystem.AnActionEvent import liveplugin.* // Action to insert a new line above the current line. // Based on this post https://martinfowler.com/bliki/InternalReprogrammability.html // Note that there is also built-in "Start New Line Before Current" action (ctrl+alt+enter). registerAction(id = "Insert New Line Above", keyStroke = "ctrl alt shift ENTER") { event: AnActionEvent -> val project = event.project ?: return@registerAction val editor = event.editor ?: return@registerAction executeCommand(editor.document, project) { document -> val caretModel = editor.caretModel val lineStartOffset = document.getLineStartOffset(caretModel.logicalPosition.line) document.insertString(lineStartOffset, "\n") caretModel.moveToOffset(caretModel.offset + 1) } } show("Loaded 'Insert New Line Above' action
Use 'ctrl+alt+shift+Enter' to run it") ``` -------------------------------- ### Extend Classloader with `add-to-classpath` and `depends-on-plugin` Source: https://context7.com/dkandalov/live-plugin/llms.txt Use special comment directives to add external JARs to the plugin's classloader or depend on other IDE plugin APIs. Paths support glob wildcards and environment variables. ```kotlin // plugin.kts // add-to-classpath $HOME/.gradle/caches/modules-2/files-2.1/org.http4k/http4k-core/4.17.0.0/*/*.jar // depends-on-plugin Git4Idea // depends-on-plugin org.jetbrains.kotlin import liveplugin.show import org.http4k.core.Request import org.http4k.core.Method import git4idea.GitUtil registerAction(id = "HTTP and Git Demo", keyStroke = "ctrl alt D") { // Use http4k from external classpath val request = Request(Method.GET, "https://example.com") show("Request created: $request") // Use Git4Idea bundled plugin API val repos = GitUtil.getRepositories(project!!) show("Git repos: ${repos.map { it.root.name }}") } if (!isIdeStartup) show("Loaded HTTP + Git demo") ``` -------------------------------- ### Reflection Access: getProperty, getField, invoke Source: https://context7.com/dkandalov/live-plugin/llms.txt Utility functions for accessing private fields, properties, and methods in IntelliJ Platform classes. Useful for exploratory hacking when no public API exists. ```kotlin import liveplugin.getProperty import liveplugin.getField import liveplugin.invoke import com.intellij.openapi.actionSystem.ActionManager import liveplugin.show // Access a private field by name and type val actionManager = ActionManager.getInstance() val actionMap = actionManager.getField>("myActions") show("Registered action count: ${actionMap.size}") // Call a private method by name val result: String = actionManager.invoke("somePrivateMethod", "arg1") show("Method returned: $result") ``` -------------------------------- ### Register Status Bar Widget Source: https://context7.com/dkandalov/live-plugin/llms.txt Adds a custom widget to the IDE status bar using `registerWidget`. The widget is automatically removed when `pluginDisposable` is disposed. Ensure `updateWidget` is called to refresh the display. ```kotlin // plugin.kts import com.intellij.openapi.wm.StatusBarWidget import liveplugin.registerWidget import liveplugin.show import java.awt.Component var counter = 0 registerWidget( widgetId = "MyCounterWidget", disposable = pluginDisposable, presentation = object : StatusBarWidget.TextPresentation { override fun getText() = "Calls: $counter" override fun getAlignment() = Component.CENTER_ALIGNMENT override fun getTooltipText() = "Number of registered actions called" override fun getClickConsumer() = null } ) registerAction(id = "Increment Counter", keyStroke = "ctrl alt I") { counter++ // Call updateWidget("MyCounterWidget") to refresh display (Groovy API) show("Counter is now $counter") } if (!isIdeStartup) show("Loaded counter widget and action") ``` -------------------------------- ### runShellCommand / runShellScript Source: https://context7.com/dkandalov/live-plugin/llms.txt Runs an external shell command or an inline shell script from within a plugin, returning stdout, stderr, and exit code as a `CommandResult`. ```APIDOC ## `runShellCommand` / `runShellScript` — Execute shell commands ### Description Runs an external shell command or an inline shell script from within a plugin, returning stdout, stderr, and exit code as a `CommandResult`. ### Usage ```kotlin // plugin.kts import liveplugin.runShellCommand import liveplugin.runShellScript import liveplugin.show registerAction(id = "Git Log Summary", keyStroke = "ctrl alt G") { val result = runShellCommand("git", "-C", project?.basePath ?: ".", "log", "--oneline", "-10") if (result.exitCode == 0) { show(result.stdout.replace("\n", "
")) } else { show("Error: ${result.stderr}", title = "Git Error") } } registerAction(id = "Count Source Files", keyStroke = "ctrl alt F") { val result = runShellScript(""" find ${project?.basePath} -name "*.kt" | wc -l """.trimIndent()) show("Kotlin files: ${result.stdout.trim()}") } if (!isIdeStartup) show("Git and file actions loaded") ``` ``` -------------------------------- ### Execute EDT Write Action Source: https://github.com/dkandalov/live-plugin/blob/master/IntellijApiCheatSheet.md Use `runWriteAction` to perform write operations on the Event Dispatch Thread (EDT). ```java runWriteAction { ... } ``` -------------------------------- ### Register Editor-Modifying Actions with `registerEditorAction` Source: https://context7.com/dkandalov/live-plugin/llms.txt Register actions that directly modify editor content. These actions run within a write action and operate on each caret, simplifying text manipulation without manual `executeCommand` calls. Supports defining actions via lambda expressions. ```kotlin // plugin.kts import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Editor import com.intellij.openapi.actionSystem.DataContext import liveplugin.registerEditorAction import liveplugin.show registerEditorAction(id = "Wrap Selection In Quotes", keyStroke = "ctrl alt QUOTE") { editor: Editor, caret: Caret, _: DataContext -> val doc = editor.document val start = caret.selectionStart val end = caret.selectionEnd val selected = doc.getText(com.intellij.openapi.util.TextRange(start, end)) doc.replaceString(start, end, "\"$selected\"") } if (!isIdeStartup) show("Loaded 'Wrap Selection In Quotes' — use ctrl+alt+'") ``` -------------------------------- ### Document.executeCommand Source: https://context7.com/dkandalov/live-plugin/llms.txt Wraps a document modification in a CommandProcessor command, making the change undoable. This method should be used for inserting, replacing, or deleting text in the editor and must be called from within an action. ```APIDOC ## Document.executeCommand ### Description Wraps a document modification in a `CommandProcessor` command, which makes the change undoable. Must be called from within an action (which runs on EDT). Use this whenever inserting, replacing, or deleting text in the editor. ### Method Signature ```kotlin fun Document.executeCommand(project: Project, description: String, modification: Document.() -> Unit) ``` ### Parameters - **project** (Project) - The project context. - **description** (String) - A description for the undo/redo history. - **modification** (Document.() -> Unit) - A lambda function that performs the document modifications. 'this' inside the lambda refers to the Document. ### Request Example ```kotlin // plugin.kts import com.intellij.openapi.actionSystem.AnActionEvent import liveplugin.* registerAction(id = "Insert Hello World", keyStroke = "ctrl shift W") { event: AnActionEvent -> val project = event.project ?: return@registerAction val editor = event.editor ?: return@registerAction editor.document.executeCommand(project, description = "Insert Hello World") { // 'this' is the Document inside the lambda insertString(editor.caretModel.offset, "/* Hello world */") } } if (!isIdeStartup) show("Loaded 'Insert Hello World' — use ctrl+shift+W") ``` ``` -------------------------------- ### Register Alt-Enter Intention Action Source: https://context7.com/dkandalov/live-plugin/llms.txt Registers an IntentionAction that appears in the Alt-Enter popup for applicable code elements. Automatically unregistered on plugin reload. Requires com.intellij.java plugin. ```kotlin // depends-on-plugin com.intellij.java import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.fileTypes.LanguageFileType import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiField import com.intellij.psi.util.parentOfType import liveplugin.registerIntention import liveplugin.show registerIntention(MakeJavaFieldFinalIntention()) class MakeJavaFieldFinalIntention : PsiElementBaseIntentionAction() { override fun isAvailable(project: Project, editor: Editor, element: PsiElement) = element.isInJavaFile() && element.parentOfType()?.hasModifierProperty("final") == false override fun invoke(project: Project, editor: Editor, element: PsiElement) { element.parentOfType()?.modifierList?.setModifierProperty("final", true) } override fun getText() = "Make 'final'" override fun getFamilyName() = "Make Java field (non-)final" } fun PsiElement.isInJavaFile(): Boolean { val fileType = containingFile?.fileType ?: return false return fileType is LanguageFileType && fileType.language.id == "JAVA" } if (!isIdeStartup) show("Loaded MakeJavaFieldFinalIntention") ``` -------------------------------- ### show Source: https://context7.com/dkandalov/live-plugin/llms.txt Displays a balloon notification in the IDE, which is also added to the Event Log. It can display any object as a message and supports HTML tags. Optional parameters allow for setting a title, notification type, and group ID. ```APIDOC ## show ### Description Displays a popup balloon notification in the IDE (also added to the Event Log). Accepts any object as `message`; supports HTML tags. Optional parameters allow setting a title, notification type, and group ID. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Method Implicitly called within script execution context. ### Endpoint N/A (Scripting API) ### Request Example ```kotlin // Simple message show("Hello world") // With HTML and title show( message = "Build succeeded in 3.2s", title = "Build Result", notificationType = NotificationType.INFORMATION, groupDisplayId = "Live Plugin" ) // Show implicit script variables show("isIdeStartup: $isIdeStartup") show("project: ${project?.name}") show("pluginPath: $pluginPath") ``` ### Response #### Success Response (void) No explicit return value, notification is displayed. #### Response Example None ``` -------------------------------- ### getProperty / getField / invoke Source: https://context7.com/dkandalov/live-plugin/llms.txt Utility functions for accessing private fields, properties, and methods in IntelliJ Platform classes. These are useful for exploratory programming when no public API is available. ```APIDOC ## Reflection Utilities ### Description Utility functions for accessing private fields, properties, and methods in IntelliJ Platform classes — useful for exploratory hacking when no public API exists. ### Methods * **`getField(instance: Any, fieldName: String): T`**: Retrieves the value of a private field from an object. * **`getProperty(instance: Any, propertyName: String): T`**: Retrieves the value of a private property from an object. * **`invoke(instance: Any, methodName: String, vararg args: Any?): Any?`**: Invokes a private method on an object with the given arguments. ### Parameters * **instance** (Any) - Required - The object from which to access the field, property, or method. * **fieldName** (String) - Required for `getField` - The name of the private field. * **propertyName** (String) - Required for `getProperty` - The name of the private property. * **methodName** (String) - Required for `invoke` - The name of the private method. * **args** (Any?) - Optional for `invoke` - The arguments to pass to the method. ```