Mission
What this is
scintilla-py provides permissively-licensed (MIT) Python bindings for the
Scintilla source code editing component and its
lexer library, Lexilla, as two
packages:
- pyside6-scintilla -- the Scintilla editor widget for
PySide6 (the official Qt for Python
project), as a
QWidgetthat drops into any PySide6 application. - lexilla -- Lexilla's lexers: the
ILexer5objects that give a Scintilla editor syntax highlighting and folding for ~125 languages.
Why it exists
The established option for using Scintilla from Python with Qt is
QScintilla by Riverbank
Computing, with Python bindings via PyQt6-QScintilla. However:
- QScintilla is licensed under GPLv3 or a paid commercial license, which prevents use in closed-source or permissively-licensed projects without buying a commercial license.
- QScintilla targets PyQt6, not PySide6; there are no official PySide6 bindings.
Scintilla and Lexilla themselves are licensed under the Historical Permission Notice and Disclaimer (HPND) -- permissive, no copyleft, compatible with MIT, BSD, commercial and closed-source use. This project exposes that permissive core directly to PySide6 users, without the GPL encumbrance.
Why two packages
Since Scintilla 5.0, lexers live in a separate library, Lexilla: a Qt-free C++
library that creates ILexer5 objects by name (CreateLexer), which a
Scintilla editor then uses via SCI_SETILEXER. The packages follow that
split:
- Separate release cadences. Each package's version follows its vendored upstream (Scintilla 5.6.3 → pyside6-scintilla 5.6.3.N, Lexilla 5.5.0 → lexilla 5.5.0.N), and Scintilla and Lexilla release independently.
- No Qt in lexilla. Lexilla has no Qt dependency, so its binding needs none either -- a much smaller, simpler package (see How the packages are built).
- Optional. An editor that doesn't need Lexilla's lexers (e.g. one styled with Pygments or tree-sitter instead, as in the examples) doesn't install them.
In practice pyside6-scintilla is lexilla's only consumer -- nothing else
implements ILexer5, and Python code wanting syntax highlighting without a
Scintilla editor has better-fitting tools. The two packages connect through
the lexer's raw ILexer5* pointer (zero coupling), plus an optional
lexilla.pyside6_scintilla.set_lexer() convenience.
Why one repo
The packages were developed as two repos (pyside6-scintilla and
lexilla-py) and merged into this monorepo (issue #19): they're maintained
together, occasionally, and lexilla doesn't make sense on its own. One repo
means one CI, one docs site, one dev environment in which lexilla is always
tested against the pyside6-scintilla it ships with, and Scintilla interface
headers that can't drift apart -- while each package still releases on its
own.
Scope
The goal is faithful, low-level bindings:
- pyside6-scintilla exposes the Scintilla API -- one typed method per Scintilla
message, text as
str, colours asQColor-- not a re-implementation of QScintilla's high-level API (auto-completion helpers, lexer configuration wrappers, etc.). That higher-level layer can be built on top by consumers. - lexilla exposes
CreateLexer/ILexer5as-is, not a redesign of Lexilla's API.
What this is NOT
- Not a port of QScintilla's high-level API.
- Not affiliated with Riverbank Computing, the QScintilla project, or the Scintilla/Lexilla project.
- Not trying to upstream into Scintilla or Lexilla: upstream uses Mercurial on SourceForge with an informal, maintainer-driven patch process not suited to Python/Qt bindings, and staying separate lets these packages evolve on their own schedule.
Licensing
| Component | License |
|---|---|
| Scintilla / Lexilla C++ source (vendored) | HPND (permissive, no copyleft) |
| This repo's own code (binding glue, Python packages) | MIT |
The packages must not depend on or bundle any GPL or LGPL code at runtime in a way that would require users to open-source their applications. (PySide6 itself is LGPL, used as a separately installed dependency.)
Upstream sources
Both upstreams are vendored from release tarballs, not the Mercurial tip (no hg toolchain needed). Updating either is a deliberate, tested action -- not automatic. See Verifying vendored Scintilla and Verifying vendored Lexilla.
Target platforms
| Platform | Architecture | Notes |
|---|---|---|
| Linux | x86_64, aarch64 | manylinux wheels via cibuildwheel |
| Windows | x86_64 | MSVC build |
| macOS | arm64 (Apple Silicon) | Primary test machine: M1 Mac Mini |
| macOS | x86_64 (Intel) | Cross-built on Apple Silicon CI runners |
Development environment
- Primary dev machine: Windows 11 PC with WSL (Debian/Ubuntu)
- Secondary machine: macOS M1 Mac Mini
- Package manager:
uv(a workspace with both packages) - CI: GitHub Actions; wheels via
cibuildwheel; PyPI trusted publishing (TestPyPI first)
Prior work by the same author
bscintillaedit-- on PyPI, Windows-only wheel, wrapsScintillaEditBaseinside aQScrollArea-derived widget.pyside6-scintilla-- on GitLab, archived, a simpler binding experiment.
This project is the clean, properly packaged successor to both.
Design decisions
Each package keeps its own decision log: pyside6-scintilla and lexilla.