A modern, lightweight, and extensible code editor built with Python and PySide6.
JEDITOR is a complete rewrite of the original JEditor project, rebuilt from the ground up with a focus on speed, usability, and extensibility. Powered by PySide6 (Qt for Python), it delivers a modern desktop editing experience with syntax highlighting, auto-completion, an integrated Git client, an AI assistant, an embedded browser, an IPython console, and a plugin system.
JEDITOR achieves up to 1000% faster performance compared to the original JEditor while providing a significantly richer feature set.
Every screenshot below is a capture of the running application, not a mockup.
Press F5 and the current file runs with the interpreter you selected; stdout and stderr stream back
into the Code Result pane as they arrive, with errors in red. Shift+F5 stops it, and the
Debugger, Terminal, Variable Inspector and Git panes sit beside it as tabs.
Ctrl+Shift+A fuzzy-searches every menu command by name or menu path, ranked by word boundaries,
consecutive characters and prefixes, and shows each command’s own shortcut on the right.
Ctrl+P does the same for files. Indexing runs on a background thread, skipping VCS, cache,
virtualenv and build directories along with binary file types; typing > at the start switches the
same picker back into command mode.
ruff runs on the buffer rather than the file on disk, on a worker thread once typing pauses, so
unsaved edits are covered and a stale result from a superseded run is discarded. Findings are
underlined in place and listed in the Problems panel, where Apply Fixes applies everything ruff
can fix by itself. A language server’s diagnostics land in the same list, and the panel filters both
by severity (Error, Warning, Information, Hint) and by source.
The Outline panel lists the current file’s classes, methods, functions and module variables. Python
is parsed with ast, so no code runs; other languages are asked of their language server, which
means a TypeScript or Rust file gets an outline too.
The TODO panel scans the whole project for TODO, FIXME, HACK, XXX, BUG, NOTE and
OPTIMIZE comments across Python, C-like, HTML, SQL and other comment styles. Tags are only reported
when they follow a comment marker, so ordinary strings are never misreported.
Run pytest from a panel and read the results with failures first. Selecting a failing test shows its traceback below the list, double-clicking opens that test at the failing line, and you can re-run everything, only the selection, or only what failed last time. Ticking With coverage adds the total beside the summary.
The gutter shows how the file differs from its last commit — green for added lines, orange for
modified, a thin red line where lines were deleted. F7 / Shift+F7 jump between changes,
Ctrl+Alt+Z reverts the one under the caret in a single undo step, and the right-click menu stages
just that change. Ctrl+Alt+B toggles inline blame. The committed version is read on a background
thread and the comparison is a pure in-memory diff, so editing never waits on git.
The full client handles branches, staging, commits, stash, conflicts, clone and push:
Any change can be opened as a side-by-side comparison, against HEAD or against what is staged:
Search the current file, a folder, or the whole project, with regex and case options. Long searches run on a worker thread so the window stays responsive, and double-clicking a hit opens that file at that line.
Ctrl+Alt+M opens a minimap of the whole file, drawn as bars following each line’s length and
indentation, with marks for lint diagnostics, git changes and search hits. Ctrl+Alt+\ splits the
view: both halves share one document, so an edit on either side appears in the other at once, while
scrolling and the caret stay independent.
The Terminal tab is a real interactive shell (cmd, PowerShell, bash or sh) with command history and its own working directory.
The browser tab keeps documentation and Stack Overflow one click away, with tabs, an address bar and in-page search.
Every command’s keys are listed in one editable table. Two commands claiming the same keys cannot be saved, because Qt runs neither of them when that happens; a change takes effect immediately, and only what differs from a default is stored.
Snippets use the usual $1 / ${2:default} / $0 notation, with per-language sets on top of the
shared ones, edited from Tab > Edit Snippets rather than by hand.
Themes come from Qt Material, and the editor’s own colours follow whichever one you pick — a light window never leaves you with dark-theme syntax colours.
| Category | Features |
|---|---|
| Editor | Multi-tab editing, syntax highlighting for twelve languages, auto-completion (Jedi and language servers), multiple carets with selections, snippets with mirrored placeholders, split view, minimap, code folding (indentation and braces), bookmarks, occurrence highlighting, line operations |
| Navigation | Command palette, quick open (go to file), go to symbol, document outline, navigation history (back/forward), TODO/FIXME task panel |
| Execution | Run Python scripts (F5), debug mode (F9), shell commands, virtual environment detection |
| Code Quality | YAPF formatting, format on save, PEP8 checking, Ruff linting with a problems panel, language-server diagnostics and quick fixes, pytest panel with tracebacks and coverage, JSON reformatting |
| Git | Branch management, commit history, side-by-side diff viewer, gutter change markers, per-change staging and revert, inline blame, stash, conflict resolution, audit logging |
| AI | Chat panel with interchangeable providers: OpenAI-compatible endpoints (LangChain) and Anthropic (streamed), per-provider models, keys & prompts |
| Console | Interactive shell, Jupyter/IPython console, command history, multi-shell support |
| Browser | Embedded web browser, URL navigation, in-page search |
| Plugins | Custom syntax highlighting, UI translations, run configurations, auto-discovery |
| UI | Dark/light themes (Qt Material) with matching editor colours, configurable keyboard shortcuts, font customization, dockable panels, system tray, toolbar, status bar |
| i18n | English, Traditional Chinese, Simplified Chinese, Japanese; follows the system language, switches without restarting, extensible via plugins |
| Files | Auto-save, multi-encoding support (UTF-8, GBK, Latin-1, etc.), recent files, multi-file session restore |
| Platform | Version |
|---|---|
| Windows | Windows 10 / 11 |
| macOS | 10.5 ~ 11 Big Sur |
| Linux | Ubuntu 20.04+ |
| Raspberry Pi | 3B+ |
| Python | 3.10+ (tested on 3.10, 3.11, 3.12) |
pip install je_editor
git clone https://github.com/Integration-Automation/JEDITOR.git
cd je_editor
pip install .
Core dependencies are installed automatically:
| Package | Purpose |
|---|---|
| PySide6 | GUI framework (Qt for Python) |
| qt-material | Dark/light material themes |
| yapf | Python code formatting (Google style) |
| jedi | Python auto-completion & analysis |
| ruff | Fast Python linter |
| gitpython | Git repository operations |
| langchain_openai + langchain_core | OpenAI-compatible AI provider |
| anthropic | Anthropic AI provider |
| tree-sitter + tree-sitter-python / -javascript / -json | Syntax parsing for highlighting |
| debugpy | Python debug adapter (Debug Adapter Protocol) |
| watchdog | File system monitoring |
| pycodestyle | PEP8 style checking |
| qtconsole | Jupyter/IPython console widget |
python -m je_editor
from je_editor import start_editor
start_editor()
The editor launches maximized with a dark amber theme by default.
The parts of the editor that are not widgets — the workspace, open documents and diagnostics, plus
the interfaces for language services, debugging, task execution, remote sessions and AI providers
— live in je_editor.core and need no window:
from je_editor.core import Diagnostic, EditorServices, Severity, TextRange, Workspace, to_uri
services = EditorServices(Workspace.single_root("my_project"))
uri = to_uri("my_project/main.py")
services.diagnostics.publish("ruff", uri, [
Diagnostic("`os` imported but unused", TextRange.from_lines(1, 8), Severity.WARNING, code="F401"),
])
print(services.diagnostics.counts()[Severity.WARNING]) # 1
services.shutdown()
This layer is the foundation of the next-generation editor; the editor window does not consume it yet. See the Core Services page of the documentation.
QTextCursor), so they follow their code when lines are inserted or removed above them instead of drifting.Ctrl+Shift+L puts a caret at the end of every selected line, Ctrl+Alt+N adds one at the next occurrence of the word under the caret, Ctrl+Alt+Shift+Up / Down adds one on the line above or below, and Alt-click adds or removes one anywhere. All of them move together with the arrow keys, Home and End, and Shift with any of those extends a selection at every caret. Typing, Backspace and Delete apply at all of them as a single undo step, replacing each selection where there is one; Ctrl+Shift+Esc or a plain click returns to one caret.Ctrl+Alt+\) – A second view of the same file, sharing one document: an edit in either side shows up in the other at once, while scrolling and the caret stay independent.Ctrl+Alt+M) – An overview of the whole file drawn as bars following each line’s length and indentation, with a band marking what is on screen. Marks down the sides show lint diagnostics, git changes, and whatever you are currently looking for – the search box’s hits while a search is open, and otherwise the other occurrences of the word under the caret. Click or drag to jump. Large files are sampled rather than drawn line by line.$1 / ${2:default} / $0 notation, so existing snippets drop straight into snippets.json, and there are per-language sets on top of the shared ones. Edit them from Tab > Edit Snippets rather than by hand; a missing or broken file falls back to the built-in Python set.pytest-cov in the project being tested). Run everything, only the selection, or only what failed last time; double-click a row to open that test at the failing line.ruff are underlined in the editor and listed in a Problems dock panel (rule, message, line), with double-click to jump. The buffer is checked, not the file on disk, so unsaved edits are covered; the run happens on a worker thread after typing pauses, and a stale result from a superseded run is discarded. If ruff is not installed, or a run fails, the editor simply shows no diagnostics rather than reporting an error.F7 / Shift+F7, revert the change under the caret back to its committed form with Ctrl+Alt+Z (one undo step), and stage just that change from the right-click menu. Ctrl+Alt+B toggles inline blame, showing the commit, author and summary that last touched each line. The Git menu opens a side-by-side diff of the whole file against HEAD, or against what is staged – after staging change by change, that second one is what shows which parts actually went into the index. The right-click menu also unstages the file and commits what is staged. The committed version is read on a background thread when the file opens, and the comparison itself is a pure in-memory diff, recomputed only after typing pauses – so editing never waits on git. Files outside a repository, or not yet committed, simply show no markers.Ctrl+Shift+D), sort selected lines (Ctrl+Alt+S), join selected lines into one (Ctrl+Shift+J), and (from the Text menu) natural sort, remove duplicate lines, remove blank lines, reverse line order, or align lines on a delimiter (e.g. =). Each is a single undo step.Ctrl+D) – Duplicates the selection when there is one (selecting the new copy), or the whole line when there isn’t.Ctrl+Alt+Right), and shrink it back (Ctrl+Alt+Left). Shrinking only retraces expansions, and a manual selection change resets the history.Ctrl+Alt+Up / Ctrl+Alt+Down), handling negative signs and growing widths.F2) – Rename every whole-word occurrence of the identifier under the caret across the file as a single undo step. Word boundaries protect partial matches (renaming val never touches value).Alt+Left / Alt+Right). A jump records both where you came from and where you went, so “back” returns to where you started.ast, so no code runs; other languages are asked of their language server, so a TypeScript or Rust file gets an outline too. Double-click to jump to a definition.> at the start switches the same picker into command mode.ast module, so no user code is ever executed, and a file that does not parse simply yields no symbols instead of erroring while you type.TODO, FIXME, HACK, XXX, BUG, NOTE and OPTIMIZE comments across Python, C-like, HTML, SQL and other comment styles. Filter by tag and double-click a row to open that file at that line. Tags are only reported when they follow a comment marker, so ordinary strings are not misreported.Ctrl+F9) and can carry a condition, and a program started with debugpy --listen can be attached to.Text > Indent Size), and the indent width is auto-detected from a file’s own content when it is opened.snake_case / camelCase / PascalCase / kebab-case), number-base conversion (hex / decimal / binary), and encode/decode helpers (Base64, URL, HTML entities, JSON string escaping). Decoders that fail leave the text untouched.restore_session to false in .jeditor/user_setting.json.HEAD or the index.| Type | Purpose |
|---|---|
| Programming Language | Add syntax highlighting for new languages |
| Natural Language | Add UI translations for new locales |
| Run Configuration | Define custom execution environments |
| Plugin Metadata | Provide plugin version and author info |
Plugins are discovered automatically from the jeditor_plugins/ directory, and can also be browsed
and installed from within the editor. See Plugin Development.
zh-Hant and the Taiwan, Hong Kong and Macau regions get traditional characters, and anything else simplified. What is detected is recorded, so from then on it is simply the chosen language.| Shortcut | Action |
|---|---|
Ctrl+N |
New file |
Ctrl+O |
Open file |
Ctrl+K |
Open folder |
Ctrl+S |
Save file |
Ctrl+Shift+S |
Save every modified tab |
Ctrl+Shift+A |
Command palette |
Ctrl+P |
Quick open (go to file) |
Ctrl+Shift+O |
Go to symbol |
Ctrl+Shift+[ |
Toggle fold at cursor |
Ctrl+Alt+[ |
Fold all |
Ctrl+Alt+] |
Unfold all |
Ctrl+Alt+K |
Toggle bookmark |
Ctrl+Alt+L |
Next bookmark |
Ctrl+Alt+J |
Previous bookmark |
Alt+Left |
Navigate back |
Alt+Right |
Navigate forward |
Ctrl+Shift+D |
Delete current line / selection |
Ctrl+Alt+S |
Sort selected lines |
Ctrl+Shift+J |
Join selected lines |
Ctrl+Alt+Right |
Expand selection |
Ctrl+Alt+Left |
Shrink selection |
Ctrl+Alt+Up |
Increment number under caret |
Ctrl+Alt+Down |
Decrement number under caret |
F2 |
Rename occurrences in file |
Ctrl+Shift+L |
Caret at the end of every selected line |
Ctrl+Alt+N |
Add caret at next occurrence |
Ctrl+Alt+Shift+Up / Ctrl+Alt+Shift+Down |
Add caret above / below |
Ctrl+Shift+Esc |
Back to a single caret |
Ctrl+Shift+R |
Start / stop macro recording |
Ctrl+Shift+G |
Play macro |
Ctrl+Alt+E |
Recent locations |
Ctrl+Alt+\ |
Toggle split view |
Ctrl+Alt+M |
Toggle minimap |
F7 / Shift+F7 |
Next / previous change |
Ctrl+Alt+Z |
Revert the change at the caret |
Ctrl+Alt+B |
Toggle inline blame |
Ctrl+J |
Reformat JSON |
Ctrl+Shift+Y |
YAPF Python format |
Ctrl+Alt+P |
PEP8 format checker |
Ctrl+F |
Find text (editor, browser) |
Ctrl+Shift+F |
Search across files |
Alt+W |
Word wrap |
Ctrl+Shift+P |
Install a package with pip |
Ctrl+Shift+U |
Upgrade and install packages |
Ctrl+Shift+V |
Change the Python interpreter |
Ctrl+H |
Find and replace |
Ctrl+G |
Go to line |
F5 |
Run program |
F9 |
Debug |
Shift+F5 |
Stop program |
Ctrl+F9 |
Toggle breakpoint |
Ctrl+F5 |
Debugger: continue |
F10 / F11 / Shift+F11 |
Debugger: step over / into / out |
Ctrl+D |
Duplicate line / selection |
Ctrl+/ |
Toggle comment |
Alt+Up / Alt+Down |
Move line up / down |
Ctrl+B |
Jump to the definition under the caret |
Ctrl+Shift+\ |
Jump to the matching bracket |
Ctrl++ or Ctrl+= / Ctrl+- |
Zoom the editor font in / out |
Every shortcut above can be reassigned from Style > Keyboard Shortcuts; Ctrl++ and Ctrl+=
are two separate zoom-in commands, so each can be changed on its own. The keys below are handled
by the editing area or the console themselves, so they are fixed:
| Shortcut | Action |
|---|---|
Tab / Shift+Tab |
Indent / unindent the line or selection |
Up/Down |
Command history (console) |
je_editor/
├── pyside_ui/ GUI layer (PySide6)
│ ├── browser/ Embedded web browser
│ ├── code/ The editor itself: syntax, folding, lint, LSP, git markers,
│ │ multiple carets, snippets, minimap, process execution
│ ├── dialog/ Search & replace, shortcuts, snippets, file dialogs
│ ├── git_ui/ Git client, commit graph, diff viewers
│ └── main_ui/ Main window, menus, toolbar, panels, settings, AI, console
├── core/ Service layer, no Qt: workspace, documents, diagnostics, and the
│ interfaces for language services, debugging, tasks, remote and AI
├── adapters/ Implementations of those interfaces, no Qt: the AI providers, the
│ Tree-sitter syntax engine, the local task runner and the DAP
│ debug session
├── code_scan/ Ruff execution and watchdog file monitoring
├── git_client/ Git operations (GitPython + git CLI)
├── plugins/ Plugin registry and loader
└── utils/ Pure logic, no Qt: diffing, folding, fuzzy matching, symbols,
LSP protocol, encodings, shortcuts, translations
Features are built in two halves: the algorithm lives in utils/ with no Qt import, and a thin
manager in pyside_ui/ wires it to widgets. Folding, for example, is utils/code_folding/ plus
pyside_ui/code/folding/. That is why most of the behaviour above can be tested without opening a
window.
core/ sits between the two: it composes that logic into services a host application can use
without the JEditor window. A test walks its import graph and fails on any Qt import beneath it.
A module-by-module reference — what every file does, the threading model, the global singletons and
the settings layout — is kept in architecture_explore.md.
Create plugins in a jeditor_plugins/ directory inside your working directory. Each plugin is a
Python module that registers what it provides on import.
Add syntax highlighting for a new language:
from je_editor.plugins import register_programming_language
register_programming_language(
suffix=".rs",
syntax_words={"keywords": ["fn", "let", "mut", "struct", "impl", "enum"]},
syntax_rules={"keyword_color": "#FF6600"},
)
Add a new UI translation:
from je_editor.plugins import register_natural_language
register_natural_language(
language_key="ja",
display_name="Japanese",
word_dict={"file": "ファイル", "edit": "編集", "run": "実行"},
)
Teach the Run with… menu how to run another language. Interpreted languages just need a compiler and its arguments:
from je_editor.plugins import register_plugin_run_config
register_plugin_run_config({
"name": "Go", # shown in the menu
"suffixes": (".go",), # file types this applies to
"compiler": "go", # executable
"args": ("run",), # arguments before the file path
})
# runs: go run file.go
Compiled languages add compile_then_run and the flag that names the output binary:
register_plugin_run_config({
"name": "C (GCC)",
"suffixes": (".c",),
"compiler": "gcc",
"args": (),
"compile_then_run": True,
"output_flag": "-o",
})
# compiles: gcc file.c -o file then runs the result
For the full guide, including plugin metadata and packaging, see
PLUGIN_GUIDE.md.
JEDITOR stores user settings in a .jeditor/ directory inside the working directory:
| File | Content |
|---|---|
user_setting.json |
General preferences (font, theme, language, recent files, open tabs, reassigned shortcuts) |
user_color_setting.json |
Editor and output colours, including syntax highlighting |
snippets.json |
Your own snippets, merged over the built-in sets |
ai_config.json |
AI assistant settings, grouped by provider — written only when you tick saving in the AI settings dialog, since it holds the key as plain text |
Each file is backed up to <name>.bak before it is rewritten.
Full documentation is available at https://je-editor.readthedocs.io/en/latest/.
Contributions are welcome. Please feel free to submit issues and pull requests on GitHub.
This project is licensed under the MIT License. See LICENSE for details.
Copyright (c) 2021 ~ Now JE-Chen