Python plugins¶
Modular Text Editor loads Python plugins alongside native C++ ones through
the same plugin management surface. A Python plugin is a folder containing
one plugin.json and at least one .py file with a top-level
register(ctx) function.
The editor spawns a small worker subprocess per plugin, runs
register(ctx) inside it, and marshals commands / menus / events across
an RPC channel. Your plugin never touches Qt or Scintilla — the ctx
object is a plain Python service locator.
What works today (Phases 1–5: feature-complete)
Phase 1: commands, menu items, event subscriptions (open / close / save / active document changed / selection changed / app quit), the log, the status bar.
Phase 2: editor buffer / selection / caret /
search / indentation / file ops via ctx.editor.*, persistent
settings via ctx.settings.* and read-only app settings via
ctx.app_settings.*, completion providers (with a small
keystroke deadline) via @ctx.completions.provider(...), and
ctx.data_dir() for the per-plugin writable directory.
Phase 3: declarative dock panels via ctx.docks.add_panel(...),
Preferences pages via ctx.settings_page.register(...), both
built from mte.ui form builders, and runtime control
access via ctx.ui.get / ctx.ui.set.
Phase 4: .mteplugin package installer — zip your plugin
folder, install it from Preferences → Plugins
(see Packaging).
Phase 5: per-plugin virtual environments — declare pip
dependencies in pyRequires and the editor provisions a venv for
your plugin on first launch (see
Dependencies); a
permissions declaration shown in the Plugins page; and a
streaming/cancellation RPC substrate (no plugin-facing API yet —
it underpins future AI services).
Minimal plugin¶
Create <any-folder>/hello/ with these two files.
{
"id": "org.example.hello",
"name": "Hello (Python)",
"version": "1.0.0",
"apiVersion": 1,
"entry": "python",
"module": "main",
"menus": [
{ "title": "Plugins", "barPriority": 570, "itemPriority": 100 }
]
}
import mte # noqa: F401
def register(ctx):
@ctx.commands.command(id="hello.sayHi",
title="Say Hi",
shortcut="Ctrl+Alt+H")
def _say_hi():
ctx.status.show("Hello from Python!", timeout_ms=3000)
ctx.log.info("hello: Say Hi invoked")
ctx.menus.add_item("Plugins/Hello/Say Hi", "hello.sayHi")
Zip the folder as hello.mteplugin and install it via
Preferences → Plugins → Install… — or drop the folder into your
per-user plugin directory by hand (see Packaging).
Restart the editor and Plugins → Hello → Say Hi appears in the
menu bar.
What you need on the machine¶
- A Python 3.9+ interpreter reachable one of two ways:
- On
PATH(py.exe,python.exe, orpython3.exeon Windows;python3on macOS/Linux), or - Pointed at by the
MTE_PYTHONenvironment variable (either an absolute path to apython.exe, or a bare name to look up onPATH).
- On
- No pip packages: the Phase 1 client library
mteships with the editor and is added tosys.pathfor you.
The editor logs which interpreter it selected on startup:
PluginHost: MTE_PYTHON is not set; falling back to PATH.
PluginHost: located Python on PATH: py.exe -> C:/Windows/py.exe
PluginHost: Python plugin backend enabled; interpreter=..., worker=.../mte_python_host.py
If you see Python plugins disabled, jump to
Troubleshooting.
What your plugin can do¶
Each item below links to its API section:
| Capability | Section |
|---|---|
| Register commands (with optional keyboard shortcut) | ctx.commands |
| Contribute menu items and separators | ctx.menus |
| Subscribe to editor events (documents, selection, app quit) | ctx.events and Events reference |
| Log to the editor's diagnostics stream | ctx.log |
| Post transient status-bar messages | ctx.status |
| Read/modify the active buffer, selection, caret, indentation | ctx.editor |
| Persist per-plugin config, read app-wide settings | ctx.settings / ctx.app_settings |
| Register a completion provider (with a keystroke deadline) | ctx.completions |
| Locate your writable per-plugin directory | ctx.data_dir() |
| Add a declarative dock panel | ctx.docks and mte.ui |
| Add a page in Preferences | ctx.settings_page |
| Read / write panel controls at runtime | ctx.ui |
| Toggle a persistent check mark on a menu item | ctx.menus.set_item_checked |
What's next¶
plugin.jsonreference — every field and what it does.ctxAPI reference — method-by-method reference for the five Phase 1 services.- Events reference — the topic ↔ dataclass table.
- Packaging — where plugin folders belong on disk.
- Troubleshooting — decoding the host's log lines.
- Authoring with Claude — a compact guide you can hand to an AI assistant to have it produce a plugin for you.