GNU ELPA — termint

termint Atom Feed

Description
Run REPLs in a terminal backend
Latest
termint-0.2.2.tar (.sig), 2026-May-17, 100 KiB
Maintainer
Milan Glacier <dev@milanglacier.com>
Website
https://github.com/milanglacier/termint.el
Browse ELPA's repository
CGit or Gitweb
Badge

To install this package from Emacs, use package-install or list-packages.

Full description

GNU ELPA badge MELPA badge

Run REPLs in a terminal emulator backend

termint.el provides a flexible way to define and manage interactions with Read-Eval-Print Loops (REPLs) running inside a terminal emulator backend within Emacs. It allows you to easily configure how Emacs communicates with different REPLs, leveraging the capabilities of fully-featured terminal emulators like term, vterm, or eat.

Instead of relying on Emacs’s built-in “dumb” terminal (comint-mode), termint runs REPLs in a full terminal emulator, enabling features like bracketed paste mode, proper rendering, and potentially advanced interaction for complex terminal applications.

The core of the package is the termint-define macro, which generates a set of functions and keybindings tailored to a specific REPL, making it easy to start sessions, send code snippets, source files, and manage the REPL window.

Features

Installation

termint is available on the ELPA and MELPA package repository. You can install it using your preferred package manager.

(package-install 'termint)

;; Alternatively, using straight.el
(straight-use-package 'termint)

Configuration

Choose your preferred terminal backend by customizing the termint-backend variable:

;; Choose one: 'term, 'vterm, or 'eat
(setq termint-backend 'eat)

Make sure the corresponding backend package (vterm or eat) is installed if you choose it. term is built-in.

We recommend using eat or vterm due to their superior performance compared to the built-in term.

Usage

The primary way to use termint is by defining REPL configurations in your Emacs init file using the termint-define macro.

This approach takes inspiration from the reformatter package, which similarly provides a macro to define distinct formatters. Each formatter is accompanied by its own command, allowing you to apply different configurations across various languages.

This macro defines a schema for interacting with a specific REPL.

(termint-define REPL-NAME REPL-CMD &rest ARGS)
Helper Functions for Project-Local Binaries

Generated Components

For a (termint-define "myrepl" ...) definition, the macro generates several components prefixed with termint-myrepl-:

Functions: A set of interactive functions are created for common REPL interactions:

Region Operations:

Paragraph Operations:

Buffer Operations:

Defun Operations:

Evil Operators: If Evil mode is loaded, the following operators are defined, mirroring the region operations:

Keymap:

Customizable Variables: Several variables are generated to allow customization of the REPL’s behavior:

Global Customization Options: Configure these before calling termint-define to ensure all schemas inherit the same extensions to avoid per-REPL repetition:

Examples

As termint-define is a macro, it cannot automatically generate autoloads for the derived commands. To address this, we recommend configuring termint using use-package, which provides advanced lazy-loading functionality out of the box.

We also recommend declare separate use-package forms for each REPL schema. This structure allows you to activate specific REPL commands as long as their corresponding programming language mode loads.

In the below example, we created two REPL schemas:

  1. ipython - loads with Python mode
  2. claude-code - lazy-loaded with global keybindings
(use-package termint
  :demand t
  :after python
  :bind
  (:map python-ts-mode-map
   ("C-c s" . termint-ipython-start))

  :config

  ;; Uses project-local ipython from .venv if available
  (termint-define "ipython" #'termint-ipython-cmd-function
                  :bracketed-paste-p t
                  :source-syntax termint-ipython-source-syntax-template)

  ;; C-c m s: `termint-ipython-start'
  ;; C-c m e: `termint-ipython-send-string'
  ;; C-c m r: `termint-ipython-send-region' (or `termint-ipython-send-region-operator' if evil is installed.)
  ;; C-c m p: `termint-ipython-send-paragraph'
  ;; C-c m b: `termint-ipython-send-buffer'
  ;; C-c m f: `termint-ipython-send-defun'
  ;; C-c m R: `termint-ipython-source-region' (or `termint-ipython-source-region-operator' if evil is installed.)
  ;; C-c m P: `termint-ipython-source-paragraph'
  ;; C-c m B: `termint-ipython-source-buffer'
  ;; C-c m F: `termint-ipython-source-defun'
  ;; C-c m h: `termint-ipython-hide-window'
  (define-key python-ts-mode-map (kbd "C-c m") termint-ipython-map)

  ;; for evil user
  (evil-define-key '(normal visual)
    python-ts-mode-map (kbd "SPC r") #'termint-ipython-send-region-operator)
  (evil-define-key '(normal visual)
    python-ts-mode-map (kbd "SPC R") #'termint-ipython-source-region-operator))

(use-package termint
  :bind (("C-c C" . termint-claude-code-start))
  :bind-keymap ("C-c c" . termint-claude-code-map)

  :config
  (setq termint-backend 'eat)
  (termint-define "claude-code" "claude" :bracketed-paste-p t
                  ;; In most cases, there is no need to configure
                  ;; :send-delayed-final-ret; the default value suffices.
                  ;; However, for claude-code, it should be explicitly set to t.
                  :send-delayed-final-ret t
                  :source-syntax "@{{file}}"))

;; Codex is also required to enable :send-delayed-final-ret
(termint-define "codex" "codex" :bracketed-paste-p t
                :source-syntax "Read the instruction from {{file}}"
                :end-pattern ""
                :send-delayed-final-ret t)

When using use-package to lazy-load termint, keep in mind the following:

  1. To bind a termint-repl-map to a prefix key globally, utilize the :bind-keymap block within use-package.
  2. To bind a termint-repl-map to a prefix key within a major mode (e.g., Python mode), use define-key manually inside the :config block within use-package.

Window Management

The display behavior of REPL buffers created by termint can be customized by defining window popup rules in display-buffer-alist.

For example, to configure the window layout for ipython buffers, use the following configuration:


(add-to-list 'display-buffer-alist
             '("\\*ipython\\*"
               (display-buffer-in-side-window display-buffer-reuse-window)
               (window-width . 0.5)
               (window-height 0.5)
               (side . bottom)
               (slot . -1)))

FAQ

Difference between “sourcing” and “sending”

termint differentiates between “sourcing” and simply “sending” code to the REPL.

The primary advantage of source lies in handling large code content. Instead of sending huge code chunks directly to the REPL, it prevents cluttering your interaction history, maintaining a cleaner session. Additionally, on Windows, sourcing from a file mitigates potential issues with stdin processing when the REPL must read substantial input, as the file-based approach significantly reduces the data read from the stdin.

However, one notable drawback involves the security implications of temporary file creation. Since the REPL executes code from this file, any vulnerability in temporary file handling—such as exposure to malicious attack—could pose security risks. Thus, while beneficial in certain scenarios, this method requires careful consideration of its potential drawbacks.

you may consider enabling :show-source-command-hint as t, which provides useful hint about the underlying command being executed.

Bracketed-Paste Mode

Bracketed-paste mode wraps pasted text in escape sequences, letting terminals distinguish it from typed input. In termint, control it via :bracketed-paste-p in termint-define. This prevents REPLs from misinterpreting pasted newlines or special characters. Most modern REPLs support it, making :bracketed-paste-p t a generally recommended setting, but some may not work perfectly. Test with your setup to confirm compatibility.

Comparison with Other Approaches

Emacs Built-in comint

comint-mode is Emacs’s standard way of interacting with inferior processes, including shells and REPLs.

comint runs the process in a “dumb” process. This means it doesn’t fully support complex terminal escape sequences for things like advanced color rendering, or features like bracketed paste mode (though some workarounds exist). This can lead to display glitches or less robust interaction with modern REPLs that expect a capable terminal.

termint runs the REPL inside a real terminal emulator backend (term, vterm, eat). This enables the REPL to function as it would in a standalone terminal, taking advantage of features such as accurate rendering, bracketed paste support, and complex interaction capabilities, leading to a potentially smoother and more reliable experience.

Language/Backend-Specific Packages (py-vterm-interaction, vterm-julia, etc.)

Packages like py-vterm-interaction (for Python in vterm) or vterm-julia (for Julia in vterm) provide tight integration between a specific language and a specific terminal backend.

termint offers a general and flexible definition mechanism that is backend-agnostic (works withterm, vterm, eat) and language-agnostic. You can use the same framework to define interactions for Python, R, Julia, shell scripting, or any other tool with a command-line REPL, simply by providing the appropriate command and interaction parameters. This provides a consistent approach across different tools and backends. The trade-off might be less deep language-specific integration compared to a dedicated package, but it offers much broader applicability and user choice regarding the terminal backend.

Contributing

Contributions, bug reports, and feature requests are welcome. Please open an issue or pull request on the project repository.

Old versions