IntelliJ Platform Plugin SDK Help

Embedded Terminal

IntelliJ Platform provides several terminal implementations:

  • Classic Terminal — the initial implementation that was the default option for a long time.

  • Reworked Terminal — the default option since the 2025.2 release.

  • Experimental Terminal — the deprecated experimental implementation.

All three implementations expose different APIs, and using Reworked Terminal API (described on this page) is recommended. Starting from this point, the Terminal term will always refer to the Reworked Terminal.

Adding Dependency to the Terminal Plugin

The Terminal API is provided by the Terminal plugin bundled into the IntelliJ Platform-based IDEs. To use the API, add the dependency on the Terminal plugin (ID: org.jetbrains.plugins.terminal).

Getting a Terminal Instance

The Terminal instance is represented by TerminalView. Currently, the only place where the Reworked Terminal is available is the Terminal tool window whose tabs are managed by the TerminalToolWindowTabsManager.

  • Use TerminalToolWindowTabsManager.getTabs() to access already opened terminal tabs.

  • Use TerminalToolWindowTabsManager.createTabBuilder() to create a new terminal tab.

  • Use TerminalView.DATA_KEY to get the TerminalView from action's DataContext.

Accessing the Terminal Output

The terminal has two output buffers:

  • regular — used for executing commands and displaying their output

  • alternative — usually used by "fullscreen" terminal applications like vim, nano, mc, and similar

Both buffers are represented by TerminalOutputModel, stored in TerminalOutputModelsSet, and can be accessed via TerminalView.outputModels.

TerminalOutputModel is a read-only view of the terminal screen and the output history. It can be thought of as a string that contains the currently displayed text and some previous history that is removed ("trimmed") from time to time to avoid consuming too much memory.

Because of trimming, this model uses absolute offsets to navigate in it. They are represented by TerminalOffset and TerminalLineIndex. The currently available "window" has the length TerminalOutputModel.textLength and is located between TerminalOutputModel.startOffset and TerminalOutputModel.endOffset.

Sending Input to the Shell

Text can be sent to the shell using TerminalView.sendText(). It will asynchronously send the text to the input stream of the process as is.

Some additional options are provided via TerminalSendTextBuilder created with TerminalView.createSendTextBuilder():

  • shouldExecute() - inserts the line wrap after the provided text to execute the command. Prefer using this option rather than adding a line wrap manually to the text.

  • useBracketedPasteMode() - wraps the provided text into a bracketed paste mode escape sequences (if it is supported by the shell). It makes the shell treat the text like it was pasted from the clipboard. And also ensure that the text won't be interpreted as a shell key binding.

Adding Actions to the Terminal

To provide some custom handling for a shortcut in the terminal, AnAction should be implemented. But key events handling in the terminal are different compared to other places of the IDE, so just registering an action in the plugin.xml won't be enough.

Terminal has an option Override IDE shortcuts that limits the list of actions that can be executed by shortcuts in the terminal to avoid conflicts of the IDE actions with the shell key bindings. For example, it allows handling the Ctrl+R shortcut by the shell (and invoke search in commands history) instead of starting a Run Configuration in the IDE.

To make an action available by shortcut in the terminal, its ID should be provided to the terminal by implementing TerminalAllowedActionsProvider and registering it in org.jetbrains.plugins.terminal.allowedActionsProvider extension point.

Consider the following example.

MyTerminalAction.kt

class MyTerminalAction : DumbAwareAction() { override fun actionPerformed(e: AnActionEvent) { val terminal = e.getData(TerminalView.DATA_KEY) ?: return // perform the action in the terminal } } class MyTerminalAllowedActionsProvider : TerminalAllowedActionsProvider { override fun getActionIds(): List<String> { return listOf("MyPlugin.MyTerminalAction") } }

plugin.xml

<actions> <action id="MyPlugin.MyTerminalAction" class="com.example.MyTerminalAction"> <keyboard-shortcut first-keystroke="shift ENTER" keymap="$default"/> </action> </actions> <extensions defaultExtensionNs="org.jetbrains.plugins.terminal"> <allowedActionsProvider implementation="com.example.MyTerminalAllowedActionsProvider"/> </extensions>

Shell Integration

When the shell process is started, the Terminal plugin injects shell scripts into its startup to get information about the environment and subscribe to events. For example, this allows tracking the positions of the prompt, command, and command output in the shell output.

All APIs that rely on the shell integration are available in TerminalShellIntegration. It can be accessed via TerminalView.shellIntegrationDeferred. It is not available until the shell process is started and the shell integration is initialized. To wait for the shell integration initialization, use shellIntegrationDeferred.await().

Exploring Terminal Output Structure

Information about previously executed commands and the current one is stored in TerminalBlocksModel. This model is built of Terminal Blocks.

Terminal block represents a range of text in the regular TerminalOutputModel and some additional information about the content and meaning of this text. Currently, there is a single type of the block: TerminalCommandBlock.

TerminalCommandBlock represents the range of the shell output that can contain prompt, command and the command output. Also, it provides additional metadata about the command, such as working directory, executed command, and exit code. Note that not every command block is an executed terminal command. Generally, it is just a range of text between the start of the shell prompt until the next prompt.

Listening for Command Execution

To get notified when a command is executed in the shell, add a listener using TerminalShellIntegration.addCommandExecutionListener(). Note that this listener is called only if the shell interprets the typed text as a valid command. For example, it may not be called if the typed command text was blank and the user pressed Enter.

To know the current state of the shell, for example, whether a command is executing or not, use TerminalShellIntegration.outputStatus. Also, it allows listening for changes of the shell output state.

07 November 2025