REPL experience for Python in Neovim.
pyrepl.nvim
Python REPL inside Neovim powered by Jupyter console!
Requirements
- Neovim 0.11+
- Python 3.10+
Preface
This plugin aims to provide a sensible workflow for working with Python REPL.
Features pyrepl.nvim currently provides:
- Hook to convert a notebook to a Python script, command to convert a Python script to a notebook;
- Command to install required Python packages in a configured Python environment;
- UI commands to integrate
jupyter-consolein Neovim: open, close, toggle, etc; - Commands to send code from Neovim buffers to an open REPL: send visual, configurable block and buffer;
- Hook to display all images in a Neovim floating window;
- Console theme integration: syntax, matched brackets, prompt and autocomplete colors;
- QoL commands: jupytext cell navigation, toggling REPL focus in insert mode for fast testing.
Quickstart
Minimal vim.pack setup with the default config and example keymaps:
vim.pack.add({
"https://github.com/dangooddd/pyrepl.nvim",
"https://github.com/nvim-treesitter/nvim-treesitter",
}, {
confirm = false,
load = true,
})
local pyrepl = require("pyrepl")
-- default config pyrepl.setup({ split_horizontal = false, split_ratio = 0.5, style = "default", -- generate jupyter-console theme from neovim theme style_integration = true, imagemaxhistory = 10, imagewidthratio = 0.5, imageheightratio = 0.5, -- built-in provider, works best for ghostty and kitty -- for other terminals use "image" provider image_provider = "placeholders", -- can also be a function for advanced use cases cell_pattern = "^# %%%%.*$", python_path = "python", preferred_kernel = "python3", -- automatically prompt to convert notebook files into python scripts jupytext_hook = true, })
-- repl ui-related commands vim.keymap.set("n", "<leader>jo", pyrepl.open_repl) vim.keymap.set("n", "<leader>jh", pyrepl.hide_repl) vim.keymap.set("n", "<leader>jc", pyrepl.close_repl) vim.keymap.set("n", "<leader>jt", pyrepl.toggle_repl) vim.keymap.set("n", "<leader>ji", pyrepl.openimagehistory) vim.keymap.set({ "n", "t" }, "<C-j>", pyrepl.togglereplfocus)
-- send commands vim.keymap.set("n", "<leader>jb", pyrepl.send_buffer) vim.keymap.set("n", "<leader>jl", pyrepl.send_cell) vim.keymap.set("v", "<leader>jv", pyrepl.send_visual)
-- QoL commands vim.keymap.set("n", "<leader>jp", pyrepl.stepcellbackward) vim.keymap.set("n", "<leader>jn", pyrepl.stepcellforward) vim.keymap.set("n", "<leader>je", pyrepl.exporttonotebook) vim.keymap.set("n", "<leader>js", ":PyreplInstall")
Then install REPL runtime packages with uv or pip directly from Neovim:
:PyreplInstall pip
:PyreplInstall uv
To use jupytext integration, make sure jupytext is available in Neovim:
# pipx install jupytext
uv tool install jupytext
For Mason users:
:MasonInstall jupytext
Demo
https://github.com/user-attachments/assets/233494d7-c02b-4320-b3c9-f6dfef096fc0
Tips & Tricks
Advanced console usage
Pyrepl supports all features of jupyter-console, as it is a wrapper around it. You can pass any flags supported by jupyter-console to pyrepl with the user command:
:PyreplOpen {args}
:PyreplOpen! forcefully prompts you to choose from local kernels, ignoring args
or from Lua:
-- pass nil instead of a table to achieve PyreplOpen! behavior: require("pyrepl").open_repl()
-- args is a vim.system-style list: { "--kernel", "python3" }
require("pyrepl").open_repl(args)
For example, you can open remote kernel like this:
:PyreplOpen --existing /path/to/connection/file.json --ssh user@remote --sshkey ~/.ssh/id_example
You will also get completions for jupyter-console CLI arguments.
Image display
Use placeholders provider for ghostty and kitty terminals. This allows image display in hard cases (for example, when Neovim is started in nested ssh, tmux, and docker).
For other terminals, change provider to image - image.nvim will be used to display images. For example, to display images in a terminal with sixel protocol support:
vim.pack.add({
"https://github.com/dangooddd/pyrepl.nvim",
"https://github.com/nvim-treesitter/nvim-treesitter",
"https://github.com/3rd/image.nvim",
}, {
confirm = false,
load = true,
})
require("image").setup({ backend = "sixel" }) require("pyrepl").setup({ image_provider = "image" })
Use a dedicated Python environment for runtime packages
By default, pyrepl.nvim uses python executable (python_path = "python"). If Neovim is started inside a venv, that venv will be used.
You can also install all required packages once in a dedicated Python interpreter and then point to it via python_path:
uv venv ~/.venv_nvim
source ~/.venv_nvim/bin/activate
uv pip install pynvim jupyter-console ipykernel
uv pip install pillow cairosvg # optional, for jpg and svg support
Then, in init.lua:
require("pyrepl").setup({ pythonpath = "~/.venvnvim/bin/python" })
To use an arbitrary kernel in that case, you need to install it globally:
# from kernel virtual environment
python -m ipykernel install --user --name {kernel_name}
Use a built-in Pygments style
If you do not want pyrepl.nvim to derive REPL colors from your Neovim theme, pick a built-in Pygments style instead:
require("pyrepl").setup({
style_integration = false,
style = "default", -- or another Pygments style, e.g. "gruvbox-dark"
})
Send cell and move forward
Combine send and step commands:
vim.keymap.set("n", "<leader>jl", function()
vim.cmd("PyreplSendCell")
vim.cmd("PyreplStepCellForward")
end)
Advanced cell pattern options
You can use a function in place of cell_pattern config option. Example with filetype specific patterns:
require("pyrepl").setup({
cell_pattern = function()
local ft = vim.bo.filetype
if ft == "markdown" or ft == "quarto" then
return "^.*$"
end
return "^# %%%%.*$"
end,
})
## Commands and API
Commands:
:PyreplOpen {args} - open the REPL with given args, use :PyreplOpen! to pick kernel interactively;
:PyreplHide - hide the REPL window (kernel stays alive);
:PyreplClose - close the REPL and shut down the kernel;
:PyreplToggle - toggle REPL between hidden and opened state;
:PyreplToggleFocus - focus REPL in terminal mode or switch back to previous window;
:PyreplSendVisual - send the last visual selection;
:PyreplSendBuffer - send the entire buffer;
:PyreplSendCell - send the cell around the cursor (cells are separated by cell_pattern);
:PyreplStepCellForward - move cursor to the start of the next cell separated by cell_pattern;
:PyreplStepCellBackward - move cursor to the start of the previous cell separated by cell_pattern;
:PyreplOpenImageHistory - open the image manager; use j/h for previous, k/l for next, dd to delete, q or <Esc> to close;
:PyreplExport - export current buffer to notebook (jupytext should be installed);
:PyreplConvert - prompt to convert current notebook buffer to Python (jupytext should be installed);
:PyreplInstall {tool} - install required packages into python_path (with pip or uv tool).
Highlight groups:
PyreplImageBorder (link to FloatBorder by default);
PyreplImageTitle (link to FloatTitle by default);
PyreplImageNormal (link to NormalFloat by default).
Lua API:lua
require("pyrepl").setup(opts)
require("pyrepl").open_repl([args])
require("pyrepl").hide_repl()
require("pyrepl").close_repl()
require("pyrepl").toggle_repl()
require("pyrepl").togglereplfocus()
require("pyrepl").send_visual()
require("pyrepl").send_buffer()
require("pyrepl").send_cell()
require("pyrepl").stepcellforward()
require("pyrepl").stepcellbackward()
require("pyrepl").openimagehistory()
require("pyrepl").exporttonotebook()
require("pyrepl").converttopython()
require("pyrepl").install_packages(tool)
```