Python framework for building VS Code extensions
Project description
pyxend
⚡️ No JavaScript required for extension logic — write VS Code extensions in pure Python.
⚠️ Note: Preview was recorded when the project was called pyvscode. Now project renamed to pyxend.
✨ Features
- 🧠 Simple Python API for defining commands
- ⚙️ CLI tool to scaffold, sync, build, and publish extensions
- 🧩 Template-based generation of
extension.jsandpackage.json - 🔁 Context-aware Python execution with editor data (selected text, cursor, file)
- 📦 Easy packaging using
vsce
📦 Installation
pip install pyxend
Or using git repository:
git clone https://github.com/codeflane/pyxend
cd pyxend
pip install -e .
Make sure Node.js and vsce are installed:
npm install -g vsce
🚀 Getting Started
1. Create a new extension
pyxend init "My Extension Name" myextension
2. Add logic in Python
Edit main.py:
from pyxend import Extension, ModalType
ext = Extension()
@ext.command('hello')
def say_hello(ctx):
ext.show_modal("Hello from Python!", type=ModalType.INFO)
ext.run()
3. Sync the metadata
pyxend sync
4. Build and install the extension
pyxend build
code --install-extension your-extension.vsix
📚 CLI Options
All CLI commands accept a --target (or -t) option to specify the working directory (defaults to current folder).
Init
pyxend init "Display Name" extension_name
Init new project.
Arguments:
- Display Name: extension display name (that showing in extension hub)
- Extension Name: extension name (defaults to display name)
Creates:
main.py(logic)extension.js(bridge)package.json(extension metadata).vscodeignore
Sync
pyxend sync
Sync Python decorators in main.py with extension.js and package.json
Metadata
pyxend metadata -v 0.0.1 -e 1.70.0 -d desc -t title -n name -g git
Update package.json metadata
Options:
| Option | Description |
|---|---|
--engine / -e |
VS Code engine version |
--description / -d |
Description of your extension |
--git / -g |
GitHub repo URL |
--name / -n |
Display name |
--version / -v |
Extension version |
License
pyxend license author
Create LICENSE file (now only MIT support). License is required for creating extensions
🧩 Extension API
The core API is exposed via the Extension class.
Command decorator
Decorator to register a command that can be invoked from VS Code.
Arguments:
name- The command name (e.g.,"sayHello").title- Title to display in the Command Palette. Defaults toname
Context:
When the command is invoked, it receives a context dictionary with useful metadata:
{
"selected_text": "Hello", // Currently selected text
"language": "python", // Opened file language
"cursor_pos": {"line": 3, "character": 15}, // Current cursor position
"file_path": "D:/projects/example.py", // Opened file path
"all_text": "Hello World", // File content
"cursor_word": "Hello", // the word under the cursor
"lines": 3, // Lines count in file
"file_size": 12, // File size in bytes
"opened_files": ["D:/projects/example.py", "D:/test.txt"], // Currently opened files
"is_saved": false, //Is file saved
"workspace": "D:/projects" //Opened workspace folder
}
Example:
@ext.command("sayHello", title="Say Hello")
def say_hello(context):
ext.show_modal(f"Hi! You selected: {context['selected_text']}")
Event decorator
Decorator to register an event that can be invoked from VS Code.
Arguments:
event- Event type (Eventenum).
Context:
Context same as Command decorator. See pyxend -> Extension API -> Command decorator -> Context
Example:
@ext.command("sayHello", title="Say Hello")
def say_hello(context):
ext.show_modal(f"Hi! You selected: {context['selected_text']}")
Show modal
Show modal popup
Arguments:
- message - The message to display.
- type - Must be one of the ModalType values:
- ModalType.INFO - modal with an blue informational (i) icon (default)
- ModalType.WARNING - modal with an yellow warning /!\ icon
- ModalType.ERROR - modal with an red error (x) icon
Example:
ext.show_modal("This is an error", type=ModalType.ERROR) #Show error modal with text "This is an error"
Replace selected text
Replace the currently selected text in the editor. (deprecated)
Arguments:
text- The text that will replace the current selection.
Example:
ext.replace_selected_text("Replaced content.") #Replace currently selected text to "Replace content."
Insert text
Insert new text.
Arguments:
-
text- The text to insert. -
preset- Position preset. Must be one of the InsertTextPreset values:- InsertTextPreset.START - Insert text at the start of file
- InsertTextPreset.CURSOR - Insert text after cursor position
- InsertTextPreset.CUSTOM - Insert text at custom position (use
lineandcharacterto provide it) (default) - InsertTextPreset.END - Insert text at the end of file
-
line- Line number to insert text. Requires only when preset isCUSTOM -
character- Character number to insert text. Requires only when preset isCUSTOM
Example:
ext.insert_text("Inserted text.", preset=InsertTextPreset.CURSOR) #Insert text "Inserted text." after cursor position
Replace text
Replace text.
Arguments:
-
text- The text to replace. -
preset- Position preset. Must be one of the ReplaceTextPreset values:- ReplaceTextPreset.SELECTED - Replace selected text
- ReplaceTextPreset.CUSTOM - Replace text at custom position (use
start_line,start_character,end_lineandend_characterto provide it) (default) - ReplaceTextPreset.ALL - Replace whole file
-
start_line- Start line number to replace text. Requires only when preset isCUSTOM -
start_character- Start character number to replace text. Requires only when preset isCUSTOM -
end_line- End line number to replace text. Requires only when preset isCUSTOM -
end_character- End character number to replace text. Requires only when preset isCUSTOM
Example:
ext.replace_text("Replaceed text.", preset=InsertTextPreset.ALL) #Replace all file text to "Replaced text."
Open file
Open a file in the editor by its path.
Arguments:
path- Full path to the file.
Example:
ext.open_file("D:/projects/example.py") #open "D:/projects/example.py" in editor
Set cursor position
Move the editor’s cursor to the specified position.
Arguments:
line- Line number.character- Character number.
Example:
ext.set_cursor_pos(5, 10) #move cursor to line 5, character 10
Save file
Save the current file.
Example:
ext.save_file() #save current file
Replace all text
Replace the entire content of the file. (deprecated)
Arguments:
text- The new content for the whole file.
Example:
ext.replace_all_text("print('Hello, World!')\n") #replace all file text to "print('Hello, World!')"
Run terminal command
Execute a command in a new or existing terminal.
Arguments:
command- The terminal command to execute.name(optional) - Name of the terminal instance. Default is "pyxend terminal"
Example:
ext.run_terminal_command("echo 'Hello World'") #create new terminal and echo "Hello World"
Delete selected text
Delete currently selected text
Example
ext.delete_selected_text() #delete selected text
Delete file
Delete currently opened file (Do not recommend to use)
Example
ext.delete_file() #delete file
Status message
Show status message in a bottom of screen.
Arguments:
message- The message to show.timeout(optional) - How long message will show in ms. Defaults to 3000ms (3s)
Example:
ext.status_message("Hello from pyxend", 1000) #show status message "Hello from pyxend" for 1 second
⚠️ Important Notes about Command Execution
- All actions (
ext.show_modal,ext.insert_text, etc.) are collected into a list during Python execution and returned as a single JSON batch to VS Code. - This means VS Code does not execute Python actions one-by-one. It only runs Python once, receives all the actions at once, and then executes them sequentially in JavaScript.
- Any delay (
time.sleep) or conditional logic in Python will happen before any action is performed in the editor.
Example:
ext.show_modal("First")
time.sleep(1)
ext.show_modal("Second")
This will result in both modals appearing immediately, one after the other — not with a delay between them.
🚧 Future Plans
We are planning to add streaming or asynchronous action execution in a future version (v1.0 or v2.0) so that:
actions like ext.show_modal() can be executed live, one at a time, and delays or dynamic logic will reflect in real-time in the editor.
We already have some ideas on how to implement this — stay tuned!
🧠 How it Works
pyxend bridges VS Code and Python.
When a command is executed:
-
VS Code (JavaScript)
- Collects the file context (selected text, full code, cursor, file path, etc.)
- Launches the Python script (
main.py) and passes the command name and context as JSON.
-
Python (pyxend)
- Receives the command and context.
- Executes the matching
@ext.command(...)function. - Generates actions list from executed commands.
- Return actions list to the JS.
-
VS Code (JavaScript)
- Parses the returned actions.
- Executes them one by one using the VS Code API (editing text, opening files, setting cursor position, etc.)
This allows Python to control editor behavior dynamically.
🔧 How to Integrate a New Custom Action
To add a new custom action that isn’t supported yet:
1. Define it in Python
Append an action manually:
ext.actions.append({
"action": "highlight_range",
"line_start": 5,
"line_end": 8
})
2. Extend the extension.js
Open extension.js and add a new case to handle it:
case "highlight_range":
if (!editor) return;
const start = new vscode.Position(action.line_start, 0);
const end = new vscode.Position(action.line_end, 0);
const decorationType = vscode.window.createTextEditorDecorationType({
backgroundColor: "rgba(255,255,0,0.2)"
});
editor.setDecorations(decorationType, [new vscode.Range(start, end)]);
break;
This allows pyxend to be fully extensible.
Note: after syncing (
pyxend sync), all JS will overwrite. It will be fixed in future versions.
📄 Changelog
See full change log in CHANGELOG.md
0.3.1 (Latest)
Added 1 new action
0.3.0
Added events and 2 new actions
0.2.0
Added 2 new actions and reworked replace/insert text methods
0.1.2
Added 3 new values in context, renamed manifest → metadata
0.1.1
Fixed packaging bug, improved error modals, typo fixes
⭐ Contributing
Thank you for checking out pyxend! If you find it useful, please consider:
- ⭐ Starring the GitHub repository — it really helps the project grow.
- 🛠 Opening an issue or PR with your suggestions or improvements.
- 📢 Sharing the project with fellow Python developers who want to build VS Code extensions without touching JavaScript.
Let’s make Python-powered VS Code extensions mainstream!
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyxend-0.3.1.tar.gz.
File metadata
- Download URL: pyxend-0.3.1.tar.gz
- Upload date:
- Size: 19.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26ac6dd8ecc3d248f54da326a12bc7db8669e7147b20cf96603f97128383dd8a
|
|
| MD5 |
0fd30bce68316a0b663ca41486f9f85e
|
|
| BLAKE2b-256 |
7e189e8f473a1f25165591f869746ac273d6c69b77d15056036507019c1ea2c6
|
File details
Details for the file pyxend-0.3.1-py3-none-any.whl.
File metadata
- Download URL: pyxend-0.3.1-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46d14e23d4ceacd082cb523a5287381b019d5444f91a9145b3c7d5e69b0549a2
|
|
| MD5 |
2ed8722843f8e8e8b706626ee2773e9e
|
|
| BLAKE2b-256 |
7dabe88bb1625464de979eb65bbd8a83b189a20210d8d43f2463d0adb28856bf
|