Python REPL inside Neovim powered by Jupyter console!
- Neovim 0.11+
- Python 3.10+
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,
image_max_history = 10,
image_width_ratio = 0.5,
image_height_ratio = 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.open_image_history)
vim.keymap.set({ "n", "t" }, "<C-j>", pyrepl.toggle_repl_focus)
-- 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.step_cell_backward)
vim.keymap.set("n", "<leader>jn", pyrepl.step_cell_forward)
vim.keymap.set("n", "<leader>je", pyrepl.export_to_notebook)
vim.keymap.set("n", "<leader>js", ":PyreplInstall")Then install REPL runtime packages with uv or pip directly from Neovim:
:PyreplInstall pip
:PyreplInstall uvTo use jupytext integration, make sure jupytext is available in neovim:
# pipx install jupytext
uv tool install jupytextFor mason users:
:MasonInstall jupytextdemo.mp4
This plugin aims to provide a sensible workflow to work with Python REPL.
Features pyrepl.nvim currently provides:
- Convert notebook files from and to python with
jupytext; - Install all Jupyter deps required with a Neovim command;
- Start
jupyter-consolein Neovim built-in terminal; - Prompt the user to choose Jupyter kernel on REPL start;
- Send code to the REPL from current buffer;
- Automatically display output images;
- Neovim theme integration for
jupyter-console; - Jupytext cell navigation;
- Toggle focus to REPL window in active terminal mode.
Use placeholders provider for ghostty and kitty terminal.
This allows image display in hard cases (for example, when nvim 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 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" })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 supportThen, in init.lua:
require("pyrepl").setup({ python_path = "~/.venv_nvim/bin/python" })To use arbitrary kernel in that case, you need to install it globally:
# from kernel virtual environment
python -m ipykernel install --user --name {kernel_name}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"
})Combine send and step commands:
vim.keymap.set("n", "<leader>jl", function()
vim.cmd("PyreplSendCell")
vim.cmd("PyreplStepCellForward")
end)You can use 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:
:PyreplOpen- select a kernel and open the REPL;: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 cell around the cursor (cells are separated bycell_pattern);:PyreplStepCellForward- move cursor to the start of the next cell separated bycell_pattern;:PyreplStepCellBackward- move cursor to the start of the previous cell separated bycell_pattern;:PyreplOpenImageHistory- open the image manager; usej/hfor previous,k/lfor next,ddto delete,qor<Esc>to close;:PyreplExport- export current buffer to notebook (jupytextshould be installed);:PyreplConvert- prompt to convert current notebook buffer to python (jupytextshould be installed);:PyreplInstall {tool}- install required packages intopython_path(withpiporuvtool).
Highlight groups:
PyreplImageBorder(link toFloatBorderby default);PyreplImageTitle(link toFloatTitleby default);PyreplImageNormal(link toNormalFloatby default).
Lua API:
require("pyrepl").setup(opts)
require("pyrepl").open_repl()
require("pyrepl").hide_repl()
require("pyrepl").close_repl()
require("pyrepl").toggle_repl()
require("pyrepl").toggle_repl_focus()
require("pyrepl").send_visual()
require("pyrepl").send_buffer()
require("pyrepl").send_cell()
require("pyrepl").step_cell_forward()
require("pyrepl").step_cell_backward()
require("pyrepl").open_image_history()
require("pyrepl").export_to_notebook()
require("pyrepl").convert_to_python()
require("pyrepl").install_packages(tool)